Files
Agentic-OS/backend/app/api/llm.py
nearxos 6185b9b85a Initial commit: Agentic OS troubleshooting platform
Self-hosted, Docker-based agentic troubleshooting platform: FastAPI backend +
LangGraph agent, Next.js UI, tiered LLM routing (local Ollama -> Gemini ->
DeepSeek -> OpenRouter), MCP server manager, encrypted device credentials,
RBAC, audit log, project-memory + Obsidian integrations, and editable
troubleshooting decision rules tuned for the GeneseasX vessel stack.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 22:11:07 +03:00

86 lines
2.9 KiB
Python

"""LLM routing configuration API."""
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException
from app.core.deps import require_admin, require_readonly
from app.models.user import User
from app.schemas import LlmRoutingConfigIn, LlmRoutingConfigOut, ProviderModelsOut
from app.services.model_catalog import catalog_by_tier, get_entry
from app.services.model_config import (
LlmRoutingConfig,
available_backends,
get_routing_config,
save_routing_config,
)
from app.services.model_discovery import list_all_provider_models
router = APIRouter(prefix="/api/llm", tags=["llm"])
ROUTING_STRATEGY = (
"Local Ollama first for triage and reasoning. On escalation, cloud providers are tried "
"in order: Gemini API → DeepSeek → OpenRouter. Within each cost tier (economy, premium), "
"each provider is attempted before moving to the next tier."
)
_MODEL_ID_FIELDS = (
"local_model_id",
"economy_gemini_id",
"economy_deepseek_id",
"economy_openrouter_id",
"premium_gemini_id",
"premium_deepseek_id",
"premium_openrouter_id",
)
@router.get("/routing", response_model=LlmRoutingConfigOut)
async def get_llm_routing(_: User = Depends(require_readonly)):
cfg = await get_routing_config()
return LlmRoutingConfigOut(
config=cfg.to_dict(),
catalog=catalog_by_tier(),
available_backends=available_backends(),
strategy=ROUTING_STRATEGY,
)
@router.get("/models/available", response_model=ProviderModelsOut)
async def get_available_models(_: User = Depends(require_readonly)):
"""Live model lists from Ollama, Gemini, DeepSeek, and OpenRouter APIs."""
data = await list_all_provider_models()
return ProviderModelsOut(providers=data["providers"], routing_order=data["routing_order"])
@router.put("/routing", response_model=LlmRoutingConfigOut)
async def update_llm_routing(body: LlmRoutingConfigIn, _: User = Depends(require_admin)):
current = await get_routing_config()
data = body.model_dump(exclude_unset=True)
for field in (
*_MODEL_ID_FIELDS,
"auto_escalate",
"max_tier",
):
if field in data and data[field] is not None:
setattr(current, field, data[field])
if body.cloud_provider_order is not None:
current.cloud_provider_order = body.cloud_provider_order
for fid in _MODEL_ID_FIELDS:
val = getattr(current, fid)
if val and not get_entry(val):
raise HTTPException(status_code=400, detail=f"Unknown model id: {val}")
if current.max_tier not in ("local", "economy", "premium"):
raise HTTPException(status_code=400, detail="max_tier must be local, economy, or premium")
await save_routing_config(current)
return LlmRoutingConfigOut(
config=current.to_dict(),
catalog=catalog_by_tier(),
available_backends=available_backends(),
strategy=ROUTING_STRATEGY,
)