my_voice_assistant_v3_jamulix/app/model_presets.py
dschlueter 18ad442c94 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>
2026-06-26 20:02:44 +02:00

43 lines
1.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Kuratierte „erprobte" KI-Modell-Presets fürs Admin-Dropdown (System Status).
Geladen aus ``config/models.yaml``. Jede Auswahl stellt Provider + Modell als
Laufzeit-Override um (siehe RUNTIME_SETTABLE) — ohne Neustart. Ein Preset mit
``default: true`` steht für den Config-Standard: seine Auswahl ENTFERNT die
LLM-Overrides, sodass wieder Profil/TOML greift.
"""
from pathlib import Path
import yaml
from app.runtime_config import RUNTIME_SETTABLE
_PATH = Path(__file__).resolve().parent.parent / "config" / "models.yaml"
# LLM-Schlüssel, die ein Preset/Reset berührt (Schnittmenge mit RUNTIME_SETTABLE).
LLM_KEYS = ("default_llm_provider", "openrouter_llm_model", "local_llm_model")
def load_presets() -> list[dict]:
"""Liest die Presets aus der YAML. Unbekannte/leere Datei -> leere Liste.
Nur Keys aus RUNTIME_SETTABLE werden im `set` übernommen (Sicherheit)."""
try:
data = yaml.safe_load(_PATH.read_text(encoding="utf-8")) or {}
except (OSError, yaml.YAMLError):
return []
out: list[dict] = []
for i, p in enumerate(data.get("presets", []) or []):
if not isinstance(p, dict):
continue
sets = {
k: str(v)
for k, v in (p.get("set") or {}).items()
if k in RUNTIME_SETTABLE
}
out.append({
"index": i,
"label": str(p.get("label") or f"Preset {i}"),
"note": str(p.get("note") or ""),
"is_default": bool(p.get("default")),
"set": sets,
})
return out