mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-22 01:24:52 +02:00
[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:
@@ -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">✓</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"'
|
||||
)
|
||||
|
||||
@@ -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">✓</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)
|
||||
|
||||
Reference in New Issue
Block a user