cff3f0b1a8
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>
82 lines
2.2 KiB
Python
82 lines
2.2 KiB
Python
"""Modelle-Endpoints: Liste (mit Caps), Discover, Fit, Register, Groups."""
|
|
|
|
import psutil
|
|
from fastapi import APIRouter, HTTPException
|
|
from pydantic import BaseModel
|
|
|
|
from services import discover, llamaswap
|
|
from services.fit import evaluate_fit, max_ctx_for
|
|
|
|
router = APIRouter(prefix="/api")
|
|
|
|
|
|
def _ram_gb() -> float:
|
|
return psutil.virtual_memory().total / (1024 ** 3)
|
|
|
|
|
|
@router.get("/models")
|
|
def models() -> dict:
|
|
items = llamaswap.list_models()
|
|
return {"models": items, "count": len(items)}
|
|
|
|
|
|
@router.get("/discover")
|
|
def discover_models(force: bool = False) -> dict:
|
|
ram = _ram_gb()
|
|
data = discover.refresh_discover(ram) if force else discover.safe_discover(ram)
|
|
if not data:
|
|
raise HTTPException(502, "Modell-Quellen gerade nicht erreichbar — später erneut.")
|
|
return {**data, "sys_ram_gb": round(ram, 1)}
|
|
|
|
|
|
@router.get("/fit")
|
|
def fit(params_b: float, quant: str = "Q4_K_M", ctx: int = 8192, name: str = "") -> dict:
|
|
ram = _ram_gb()
|
|
return {
|
|
"fit": evaluate_fit(params_b, quant, ctx, ram, name=name),
|
|
"optimal_ctx": max_ctx_for(params_b, quant, ram),
|
|
"sys_ram_gb": round(ram, 1),
|
|
}
|
|
|
|
|
|
class RegisterReq(BaseModel):
|
|
model_path: str
|
|
role: str | None = None
|
|
ctx: int = 8192
|
|
ttl: int | None = None
|
|
mmproj_path: str | None = None
|
|
jinja: bool = False
|
|
|
|
|
|
@router.post("/models/register")
|
|
def register(req: RegisterReq) -> dict:
|
|
try:
|
|
model_id = llamaswap.register_model(
|
|
req.model_path, role=req.role, ctx=req.ctx, ttl=req.ttl,
|
|
mmproj_path=req.mmproj_path, jinja=req.jinja,
|
|
)
|
|
except PermissionError as exc:
|
|
raise HTTPException(500, str(exc))
|
|
return {"ok": True, "model_id": model_id}
|
|
|
|
|
|
@router.get("/groups")
|
|
def groups() -> dict:
|
|
return {"groups": llamaswap.list_groups()}
|
|
|
|
|
|
class GroupReq(BaseModel):
|
|
group: str
|
|
members: list[str]
|
|
swap: bool = False
|
|
persist: bool = False
|
|
|
|
|
|
@router.put("/groups")
|
|
def set_group(req: GroupReq) -> dict:
|
|
try:
|
|
llamaswap.set_group(req.group, req.members, swap=req.swap, persist=req.persist)
|
|
except PermissionError as exc:
|
|
raise HTTPException(500, str(exc))
|
|
return {"ok": True}
|