Files
flowdeck/app/services/llm_config.py
T
bruno 3cb9edc1f3
FlowDeck CI / test (push) Failing after 7s
FlowDeck CI / docker (push) Skipped
feat(agent): v4.10.1 - config LLM dans l'UI (sélecteur provider/modèle, config runtime, admin + test connexion)
- Panneau agent : selects provider/modèles connus persistés par conversation
  (colonnes agent_conversations.provider/model, migration idempotente)
- GET /api/agent/providers enrichi (liste providers + modèles + requires_key)
- PATCH /api/agent/providers (admin) : config runtime DB-backed (table llm_config)
- POST /api/agent/providers/test : test connexion via LLMClient.ping() sans fallback mock
- Settings > Admin > Agent & IA : provider, modèle, clé API, URL API, save + test
- VERSION -> 4.10.1, CHANGELOG + ROADMAP mis à jour, 232 tests verts
2026-09-05 10:58:09 -04:00

82 lines
2.9 KiB
Python

"""FlowDeck — Runtime LLM configuration store (v4.10.1).
Precedence: a saved `llm_config` DB row (id=1) overrides the .env settings.
The row is created only once an admin saves the LLM settings from the UI;
until then the app falls back to `settings.llm_*` (default: offline mock).
"""
from __future__ import annotations
from app.config import settings
from app.services.llm_client import PROVIDERS, PROVIDER_MODELS
__all__ = ["get_llm_config", "set_llm_config", "provider_info"]
def get_llm_config() -> dict:
"""Return the effective LLM config — DB overrides .env when present."""
cfg = {
"provider": settings.llm_provider or "offline",
"model": settings.llm_model or "gpt-4o",
"api_key": settings.llm_api_key or "",
"api_base": settings.llm_api_base or "",
}
try:
from app.db import get_conn
with get_conn() as conn:
row = conn.execute(
"SELECT provider, model, api_key, api_base FROM llm_config WHERE id=1"
).fetchone()
except Exception: # noqa: BLE001 — DB not ready yet → env defaults
return cfg
if row:
for key in ("provider", "model", "api_key", "api_base"):
if row[key]:
cfg[key] = row[key]
return cfg
def set_llm_config(*, provider: str | None = None, model: str | None = None,
api_key: str | None = None, api_base: str | None = None) -> dict:
"""Upsert the runtime LLM config row (id=1) and return the new effective config."""
from app.db import get_conn
cfg = get_llm_config()
if provider is not None:
cfg["provider"] = provider
if model is not None:
cfg["model"] = model
if api_key is not None:
cfg["api_key"] = api_key
if api_base is not None:
cfg["api_base"] = api_base
with get_conn() as conn:
conn.execute(
"""INSERT INTO llm_config (id, provider, model, api_key, api_base, updated_at)
VALUES (1, ?, ?, ?, ?, CURRENT_TIMESTAMP)
ON CONFLICT(id) DO UPDATE SET
provider=excluded.provider, model=excluded.model,
api_key=excluded.api_key, api_base=excluded.api_base,
updated_at=CURRENT_TIMESTAMP""",
(cfg["provider"], cfg["model"], cfg["api_key"], cfg["api_base"]),
)
conn.commit()
return cfg
def provider_info() -> list[dict]:
"""Providers list for the UI: known models + whether an API key is required."""
out: list[dict] = []
for name, (_base, default_model) in PROVIDERS.items():
models = list(PROVIDER_MODELS.get(name) or ())
if default_model and default_model not in models:
models.insert(0, default_model)
out.append({
"id": name,
"name": name.capitalize(),
"default_model": default_model,
"models": models,
"requires_key": name not in ("offline", "ollama"),
})
return out