import time from fastapi import APIRouter, HTTPException from pydantic import BaseModel from services import storage from config import DEFAULT_SYSTEM_PROMPT from ai import get_provider router = APIRouter(prefix="/api/settings", tags=["settings"]) class SettingsPatch(BaseModel): system_prompt: str | None = None active_provider: str | None = None providers: dict | None = None class TestProviderRequest(BaseModel): provider: str @router.get("") async def get_settings(): settings = storage.load_settings() return storage.settings_masked(settings) @router.patch("") async def patch_settings(body: SettingsPatch): settings = storage.load_settings() if body.system_prompt is not None: settings["system_prompt"] = body.system_prompt if body.active_provider is not None: valid = {"anthropic", "openai", "ollama", "lmstudio"} if body.active_provider not in valid: raise HTTPException(400, f"Invalid provider. Must be one of: {valid}") settings["active_provider"] = body.active_provider if body.providers is not None: # Deep merge per-provider config for prov_name, prov_cfg in body.providers.items(): if prov_name not in settings.get("providers", {}): settings.setdefault("providers", {})[prov_name] = {} existing = settings["providers"][prov_name] for key, val in prov_cfg.items(): # Don't overwrite api_key if it comes in masked (contains ****) if key == "api_key" and val and "****" in str(val): continue existing[key] = val storage.save_settings(settings) return storage.settings_masked(settings) @router.post("/test-provider") async def test_provider(body: TestProviderRequest): settings = storage.load_settings() # Temporarily switch active provider for the test test_settings = dict(settings) test_settings["active_provider"] = body.provider try: provider = get_provider(test_settings) except ValueError as e: raise HTTPException(400, str(e)) start = time.monotonic() try: ok = await provider.health_check() except Exception as e: return {"ok": False, "message": str(e), "latency_ms": 0} latency_ms = int((time.monotonic() - start) * 1000) return { "ok": ok, "message": "Connection successful" if ok else "Health check failed", "latency_ms": latency_ms, } @router.get("/default-prompt") async def get_default_prompt(): return {"system_prompt": DEFAULT_SYSTEM_PROMPT}