feat(2.0): Phase 1 — Engine + Routing (Herzstueck)
Backend-Services: fit/caps/sources (portiert), discover (live HF + Fit + Caps + ranked recommendation), llama-swap write/register + groups (Ko- Residenz swap:false), LiteLLM-Gateway-Config + gateway-Service (model:auto + Fallbacks). Router: discover/fit/register/groups/routing; health zeigt gateway_reachable. Frontend: Modelle&Routing mit Caps-Chips, Fit-Badges, Discover-Tab (live), Routing-View. Lokal verifiziert: Backend-Smoke (alle Endpunkte) + Frontend-Build + Browser (Shell, Discover, Caps/Fit). Box-Verifikation offen. Docs: README + docs/STATUS.md (Phasen-Tracker + Resume-Guide). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
Routing-Gateway-Service: liest/schreibt die LiteLLM-Config und prüft die
|
||||
Erreichbarkeit. MC verwaltet damit die Modell-Zuordnung (welcher llama-swap-Alias
|
||||
ist fast/heavy/vision/coder) und die Routing-Regeln.
|
||||
"""
|
||||
|
||||
import httpx
|
||||
|
||||
from config import GATEWAY_CONFIG_PATH, GATEWAY_URL, yaml
|
||||
|
||||
|
||||
def read_gateway_config() -> dict:
|
||||
if not GATEWAY_CONFIG_PATH.exists():
|
||||
return {"model_list": [], "litellm_settings": {}, "router_settings": {}}
|
||||
with GATEWAY_CONFIG_PATH.open("r", encoding="utf-8") as f:
|
||||
return yaml.load(f) or {}
|
||||
|
||||
|
||||
def write_gateway_config(cfg: dict) -> None:
|
||||
import os
|
||||
GATEWAY_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
tmp = GATEWAY_CONFIG_PATH.with_name(GATEWAY_CONFIG_PATH.name + ".tmp")
|
||||
with tmp.open("w", encoding="utf-8") as f:
|
||||
yaml.dump(cfg, f)
|
||||
os.replace(tmp, GATEWAY_CONFIG_PATH)
|
||||
|
||||
|
||||
def routing_summary() -> dict:
|
||||
"""Kompakte Sicht für die UI: welcher Backend-Alias steckt hinter welchem
|
||||
Gateway-Modellnamen + die Fallback-Ketten."""
|
||||
cfg = read_gateway_config()
|
||||
routes = []
|
||||
for entry in cfg.get("model_list") or []:
|
||||
params = entry.get("litellm_params") or {}
|
||||
routes.append({
|
||||
"name": entry.get("model_name"),
|
||||
"target": str(params.get("model", "")),
|
||||
"api_base": params.get("api_base"),
|
||||
})
|
||||
settings = cfg.get("litellm_settings") or {}
|
||||
return {
|
||||
"routes": routes,
|
||||
"fallbacks": settings.get("fallbacks") or [],
|
||||
"context_window_fallbacks": settings.get("context_window_fallbacks") or [],
|
||||
}
|
||||
|
||||
|
||||
def set_route(name: str, target_alias: str, api_base: str = "http://127.0.0.1:8080/v1") -> None:
|
||||
"""Einen Gateway-Modellnamen (z.B. 'fast') auf einen llama-swap-Alias mappen."""
|
||||
cfg = read_gateway_config()
|
||||
ml = cfg.setdefault("model_list", [])
|
||||
params = {"model": f"openai/{target_alias}", "api_base": api_base, "api_key": "sk-noauth"}
|
||||
for entry in ml:
|
||||
if entry.get("model_name") == name:
|
||||
entry["litellm_params"] = params
|
||||
break
|
||||
else:
|
||||
ml.append({"model_name": name, "litellm_params": params})
|
||||
write_gateway_config(cfg)
|
||||
|
||||
|
||||
def gateway_reachable() -> bool:
|
||||
try:
|
||||
with httpx.Client(timeout=3.0) as c:
|
||||
# LiteLLM hat /health/liveliness; /v1/models tut's auch.
|
||||
r = c.get(f"{GATEWAY_URL}/v1/models")
|
||||
return r.status_code in (200, 401)
|
||||
except Exception:
|
||||
return False
|
||||
Reference in New Issue
Block a user