feat(admin): Dropdown „Zentrales KI-Modell" — erprobte Presets ruck-zuck umstellen
Neue Karte in Admin › System › Status: ein Dropdown kuratierter, erprobter
Modelle, das Provider + Modell in einem Klick als Laufzeit-Override umstellt
(wirkt sofort, kein Neustart).
- Presets in config/models.yaml (Label, optional note, set:{key:value} aus
RUNTIME_SETTABLE, default:true = Config-Standard). Loader app/model_presets.py
(nur erlaubte LLM-Keys: default_llm_provider, openrouter_llm_model,
local_llm_model).
- Endpunkte: GET /api/admin/models (Presets + aktuell aktives LLM + active_index),
POST /api/admin/models/select (Default -> Overrides löschen, sonst Override-Keys
setzen) — nutzt set_/delete_config_override + invalidate_cache, mit Audit-Log.
- Frontend: Karte zeigt Dropdown + „Aktiv: <provider> · <modell>"; Auswahl wirkt
sofort und lädt den Status neu. Bump app.js v=52 -> v=53.
- Startliste: Mistral Small 24B (Standard), DeepSeek V4 Flash/Pro, Ollama Gemma 3.
Tests: Laden/Default-aktiv/Select-setzt-Override/Default-löscht/404/Auth.
263 Offline-Tests grün. Live via Headless-Chrome verifiziert (Auswahl ->
Override gesetzt, Provider-Karte spiegelt das Modell).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2ccfd7f6d6
commit
18ad442c94
6 changed files with 243 additions and 2 deletions
|
|
@ -19,6 +19,7 @@ from app.audit import log_admin_action
|
|||
from app.auth import CAPABILITY_COOKIE, is_admin_user, require_admin, require_admin_or_user
|
||||
from app.config import settings
|
||||
from app.dependencies import get_store
|
||||
from app.model_presets import LLM_KEYS, load_presets
|
||||
from app.runtime_config import RUNTIME_SETTABLE, invalidate_cache, runtime_settings
|
||||
from app.schemas import AdminUserPrefsUpdate, MemoryCreate, MemoryOut, UserCreate, UserCreated, UserUpdate, UserAdminUpdate
|
||||
|
||||
|
|
@ -451,6 +452,50 @@ async def delete_runtime_config(key: str, request: Request):
|
|||
return {"deleted": key}
|
||||
|
||||
|
||||
# ── Erprobte KI-Modelle (Dropdown) ─────────────────────────────────────────
|
||||
|
||||
class ModelSelect(BaseModel):
|
||||
index: int
|
||||
|
||||
|
||||
@router.get("/admin/models", dependencies=[Depends(require_admin)])
|
||||
async def list_model_presets():
|
||||
"""Kuratierte Modell-Presets + aktuell aktives LLM (für das Admin-Dropdown)."""
|
||||
presets = load_presets()
|
||||
effective = {k: str(getattr(runtime_settings, k, "") or "") for k in LLM_KEYS}
|
||||
overrides = get_store().get_config_overrides()
|
||||
no_llm_override = not any(k in overrides for k in LLM_KEYS)
|
||||
active = None
|
||||
for p in presets:
|
||||
if p["is_default"]:
|
||||
if no_llm_override:
|
||||
active = p["index"]
|
||||
elif p["set"] and all(effective.get(k) == v for k, v in p["set"].items()):
|
||||
if active is None:
|
||||
active = p["index"]
|
||||
return {"presets": presets, "current": effective, "active_index": active}
|
||||
|
||||
|
||||
@router.post("/admin/models/select", dependencies=[Depends(require_admin)])
|
||||
async def select_model_preset(body: ModelSelect, request: Request):
|
||||
"""Wendet ein Preset an: Default -> LLM-Overrides löschen (Config-Standard);
|
||||
sonst die Override-Keys des Presets setzen. Wirkt sofort (kein Neustart)."""
|
||||
preset = next((p for p in load_presets() if p["index"] == body.index), None)
|
||||
if preset is None:
|
||||
raise HTTPException(status_code=404, detail=f"Preset {body.index} nicht gefunden.")
|
||||
store = get_store()
|
||||
if preset["is_default"]:
|
||||
for key in LLM_KEYS:
|
||||
store.delete_config_override(key)
|
||||
log_admin_action(request, "model_preset_reset", label=preset["label"])
|
||||
else:
|
||||
for key, value in preset["set"].items():
|
||||
store.set_config_override(key, value)
|
||||
log_admin_action(request, "model_preset_set", label=preset["label"], set=preset["set"])
|
||||
invalidate_cache()
|
||||
return {"selected": preset["label"], "set": preset["set"], "is_default": preset["is_default"]}
|
||||
|
||||
|
||||
# ── Live-Log (journalctl → WebSocket) ──────────────────────────────────────
|
||||
|
||||
@router.websocket("/admin/log")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue