[Haik]: minor cleanup focussed around naming (especially the names starting with an underscore as this just nerfs all type checking/specing) also some logger removes, pulling out constants, etc. Now gonna pull out some helpers then maybe its singelton time again?

This commit is contained in:
haikdc
2026-04-05 20:33:40 -07:00
parent 961941bd38
commit aa250ce203
5 changed files with 67 additions and 88 deletions
@@ -1,31 +1,11 @@
"""HTTP client for 9Router's REST API."""
import logging
import httpx
from typeguard import typechecked
from backend.apps.subscriptions.NineRouter.constants import NINE_ROUTER_API, NINE_ROUTER_V1
from backend.ports import NINE_ROUTER_PORT
logger = logging.getLogger(__name__)
NINE_ROUTER_URL: str = f"http://localhost:{NINE_ROUTER_PORT}"
NINE_ROUTER_API: str = f"{NINE_ROUTER_URL}/api"
NINE_ROUTER_V1: str = f"{NINE_ROUTER_URL}/v1"
@typechecked
async def get_usage_stats(period: str = "all") -> dict | None:
try:
async with httpx.AsyncClient(timeout=5.0) as client:
r = await client.get(f"{NINE_ROUTER_API}/usage/stats", params={"period": period})
if r.status_code == 200:
return r.json()
except Exception as e:
logger.debug(f"9Router usage stats fetch failed: {e}")
return None
@typechecked
async def get_providers() -> list[dict] | dict:
try:
@@ -34,7 +14,7 @@ async def get_providers() -> list[dict] | dict:
if r.status_code == 200:
return r.json()
except Exception as e:
logger.debug(f"9Router providers fetch failed: {e}")
print(f"9Router providers fetch failed: {e}")
return []
@@ -110,10 +90,10 @@ async def exchange_oauth(
"codeVerifier": code_verifier,
"state": state,
}
logger.info(f"exchange_oauth: provider={provider} redirect_uri={redirect_uri}")
print(f"exchange_oauth: provider={provider} redirect_uri={redirect_uri}")
async with httpx.AsyncClient(timeout=15.0) as client:
r = await client.post(f"{NINE_ROUTER_API}/oauth/{provider}/exchange", json=payload)
logger.info(f"exchange_oauth: status={r.status_code}")
print(f"exchange_oauth: status={r.status_code}")
r.raise_for_status()
return r.json()
@@ -136,5 +116,5 @@ async def get_models() -> list[dict]:
for m in models
]
except Exception as e:
logger.debug(f"9Router models fetch failed: {e}")
print(f"9Router models fetch failed: {e}")
return []
@@ -5,7 +5,6 @@ Claude/ChatGPT/Gemini subscriptions to OpenSwarm without API keys.
"""
import asyncio
import logging
import os
import shutil
import subprocess
@@ -15,14 +14,10 @@ import httpx
from typeguard import typechecked
from backend.ports import NINE_ROUTER_PORT
from backend.apps.subscriptions.NineRouter.constants import NINE_ROUTER_V1, NINE_ROUTER_URL
logger = logging.getLogger(__name__)
NINE_ROUTER_URL: str = f"http://localhost:{NINE_ROUTER_PORT}"
NINE_ROUTER_V1: str = f"{NINE_ROUTER_URL}/v1"
_process: subprocess.Popen | None = None
_THIS_DIR: str = os.path.dirname(os.path.abspath(__file__))
P_PROCESS: subprocess.Popen | None = None
P_THIS_DIR: str = os.path.dirname(os.path.abspath(__file__))
def _forward_output(pipe) -> None:
@@ -55,12 +50,12 @@ def _find_9router_dir() -> str | None:
_is_packaged: bool = os.environ.get("OPENSWARM_PACKAGED") == "1"
if _is_packaged:
_resources: str = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(_THIS_DIR))))
_resources: str = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(P_THIS_DIR))))
_candidate: str = os.path.join(_resources, "9router")
if os.path.isdir(_candidate):
return _candidate
else:
_backend_dir: str = os.path.dirname(os.path.dirname(os.path.dirname(_THIS_DIR)))
_backend_dir: str = os.path.dirname(os.path.dirname(os.path.dirname(P_THIS_DIR)))
_project_root: str = os.path.dirname(_backend_dir)
_candidate = os.path.join(_project_root, "9router")
if os.path.isdir(_candidate):
@@ -83,7 +78,7 @@ def _find_node() -> str | None:
@typechecked
async def ensure_running() -> None:
"""Start 9Router if not already running."""
global _process
global P_PROCESS
_is_packaged: bool = os.environ.get("OPENSWARM_PACKAGED") == "1"
if is_running():
@@ -173,14 +168,14 @@ async def ensure_running() -> None:
}
try:
_process = subprocess.Popen(
P_PROCESS = subprocess.Popen(
cmd, cwd=cwd,
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
env=env,
)
threading.Thread(target=_forward_output, args=(_process.stdout,), daemon=True).start()
threading.Thread(target=_forward_output, args=(P_PROCESS.stdout,), daemon=True).start()
timeout: int = 20 if _is_packaged else 30
for _ in range(timeout * 2):
@@ -196,15 +191,15 @@ async def ensure_running() -> None:
@typechecked
def stop() -> None:
global _process
if _process:
global P_PROCESS
if P_PROCESS:
try:
_process.terminate()
_process.wait(timeout=5)
P_PROCESS.terminate()
P_PROCESS.wait(timeout=5)
except Exception:
try:
_process.kill()
P_PROCESS.kill()
except Exception:
pass
_process = None
logger.info("9Router stopped")
P_PROCESS = None
print("9Router stopped")
@@ -0,0 +1,5 @@
from backend.ports import NINE_ROUTER_PORT
NINE_ROUTER_URL: str = f"http://localhost:{NINE_ROUTER_PORT}"
NINE_ROUTER_API: str = f"{NINE_ROUTER_URL}/api"
NINE_ROUTER_V1: str = f"{NINE_ROUTER_URL}/v1"
@@ -0,0 +1,23 @@
SUCCESS_HTML: str = (
'<html><body style="background:#1a1a1a;color:#fff;display:flex;align-items:center;'
'justify-content:center;height:100vh;font-family:sans-serif">'
'<div style="text-align:center">'
'<div style="width:64px;height:64px;border-radius:50%;background:#22c55e20;'
'display:flex;align-items:center;justify-content:center;margin:0 auto 16px;font-size:32px">&#10003;</div>'
'<h2 style="margin:0 0 8px">Connected!</h2>'
'<p style="color:#888;margin:0">You can close this window</p>'
'</div>'
'<script>'
'try{if(window.opener)window.opener.postMessage({type:"oauth_callback",data:{connected:true}},"*")}catch(e){}'
'setTimeout(()=>window.close(),1500)'
'</script>'
'</body></html>'
)
ERROR_STYLE: str = (
'style="background:#1a1a1a;color:#fff;display:flex;align-items:center;'
'justify-content:center;height:100vh;font-family:sans-serif"'
)
+19 -43
View File
@@ -6,7 +6,6 @@ for the frontend to connect/disconnect subscription providers
"""
import asyncio
import logging
import httpx
from contextlib import asynccontextmanager
@@ -23,12 +22,11 @@ from backend.apps.subscriptions.NineRouter.NineRouterClient import (
get_providers, get_models, start_oauth, poll_oauth, exchange_oauth,
NINE_ROUTER_API,
)
from backend.apps.subscriptions.html_constants import SUCCESS_HTML, ERROR_STYLE
from typing import Dict
logger = logging.getLogger(__name__)
_pending_oauth: dict[str, dict] = {}
_ensure_task: Optional[asyncio.Task] = None
P_PENDING_OAUTH: Dict[str, dict] = {}
P_ENSURE_TASK: Optional[asyncio.Task] = None
# ---------------------------------------------------------------------------
# Lifespan
@@ -39,11 +37,11 @@ async def subscriptions_lifespan():
try:
await ensure_running()
except Exception as e:
logger.warning(f"9Router auto-start failed: {e}")
print(f"9Router auto-start failed: {e}")
yield
global _ensure_task
if _ensure_task and not _ensure_task.done():
_ensure_task.cancel()
global P_ENSURE_TASK
if P_ENSURE_TASK and not P_ENSURE_TASK.done():
P_ENSURE_TASK.cancel()
try:
stop()
except Exception:
@@ -59,10 +57,10 @@ subscriptions = SubApp("subscriptions", subscriptions_lifespan)
@subscriptions.router.get("/status")
async def subscriptions_status() -> dict:
global _ensure_task
global P_ENSURE_TASK
if not is_running():
if _ensure_task is None or _ensure_task.done():
_ensure_task = asyncio.create_task(ensure_running())
if P_ENSURE_TASK is None or P_ENSURE_TASK.done():
P_ENSURE_TASK = asyncio.create_task(ensure_running())
return {"running": False, "providers": [], "models": []}
providers = await get_providers()
models = await get_models()
@@ -83,7 +81,7 @@ async def subscriptions_connect(body: dict) -> dict:
try:
result: dict = await start_oauth(provider)
if result.get("flow") == "authorization_code" and result.get("state"):
_pending_oauth[result["state"]] = {
P_PENDING_OAUTH[result["state"]] = {
"provider": provider,
"code_verifier": result.get("code_verifier", ""),
"redirect_uri": result.get("redirect_uri", ""),
@@ -132,7 +130,7 @@ async def subscriptions_disconnect(body: dict) -> dict:
@subscriptions.router.get("/pending/{state}")
async def subscriptions_pending(state: str):
pending: Optional[dict] = _pending_oauth.get(state)
pending: Optional[dict] = P_PENDING_OAUTH.get(state)
if not pending:
return JSONResponse(
{"error": "not found"}, status_code=404,
@@ -145,28 +143,6 @@ async def subscriptions_pending(state: str):
}, headers={"Access-Control-Allow-Origin": "*"})
_SUCCESS_HTML: str = (
'<html><body style="background:#1a1a1a;color:#fff;display:flex;align-items:center;'
'justify-content:center;height:100vh;font-family:sans-serif">'
'<div style="text-align:center">'
'<div style="width:64px;height:64px;border-radius:50%;background:#22c55e20;'
'display:flex;align-items:center;justify-content:center;margin:0 auto 16px;font-size:32px">&#10003;</div>'
'<h2 style="margin:0 0 8px">Connected!</h2>'
'<p style="color:#888;margin:0">You can close this window</p>'
'</div>'
'<script>'
'try{if(window.opener)window.opener.postMessage({type:"oauth_callback",data:{connected:true}},"*")}catch(e){}'
'setTimeout(()=>window.close(),1500)'
'</script>'
'</body></html>'
)
_ERROR_STYLE: str = (
'style="background:#1a1a1a;color:#fff;display:flex;align-items:center;'
'justify-content:center;height:100vh;font-family:sans-serif"'
)
@subscriptions.router.get("/callback")
async def subscriptions_callback(request: Request):
code: str = request.query_params.get("code", "")
@@ -176,14 +152,14 @@ async def subscriptions_callback(request: Request):
if error:
desc: str = request.query_params.get("error_description", error)
return HTMLResponse(
f'<html><body {_ERROR_STYLE}><div style="text-align:center">'
f'<html><body {ERROR_STYLE}><div style="text-align:center">'
f'<h2>Authorization failed</h2><p style="color:#888">{desc}</p></div></body></html>'
)
pending: Optional[dict] = _pending_oauth.pop(state, None)
pending: Optional[dict] = P_PENDING_OAUTH.pop(state, None)
if not pending:
return HTMLResponse(
f'<html><body {_ERROR_STYLE}><div style="text-align:center">'
f'<html><body {ERROR_STYLE}><div style="text-align:center">'
f'<h2>Session expired</h2><p style="color:#888">Please try connecting again.</p></div></body></html>'
)
@@ -193,10 +169,10 @@ async def subscriptions_callback(request: Request):
pending["redirect_uri"], pending["code_verifier"], state,
)
except Exception as e:
logger.error(f"OAuth callback: exchange failed for provider={pending['provider']}: {e}")
print(f"OAuth callback: exchange failed for provider={pending['provider']}: {e}")
return HTMLResponse(
f'<html><body {_ERROR_STYLE}><div style="text-align:center">'
f'<html><body {ERROR_STYLE}><div style="text-align:center">'
f'<h2>Connection failed</h2><p style="color:#888">{e}</p></div></body></html>'
)
return HTMLResponse(_SUCCESS_HTML)
return HTMLResponse(SUCCESS_HTML)