KI: Modellauswahl als Dropdown + Abgleich mit Produktionsstand

Einstellungen -> KI: statt freiem Modellnamen ein Dropdown mit den
Chat-Modellen, die der hinterlegte OpenAI-Schluessel nutzen darf.

- Backend: listAiModels() fragt OpenAI GET /v1/models, filtert auf
  Chat-Modelle (gpt-*, o*, chatgpt-*; ohne Embedding/Audio/Bild/Instruct
  usw.), neueste zuerst, 10 min im Speicher (Schluesselwechsel laedt neu)
- Neuer Endpunkt GET /api/ai/models (admin)
- Frontend: aktives Modell bleibt waehlbar, auch wenn nicht mehr gelistet;
  Rueckfall auf Textfeld, wenn die Liste nicht abrufbar ist
- Doku: 05-ki (Modellauswahl mit Ablaufdiagramm), 10-api, 08-anleitung

Zieht ausserdem den bereits live laufenden, bisher nicht eingecheckten
Stand nach (perinco-Kontaktabgleich/MergeDialog, Projektmargen,
Whisper-Anpassungen u. a.). Repo entspricht damit icrm.icon-experts.de.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
schakal00783
2026-09-23 10:26:30 +02:00
co-authored by Claude Opus 5.5
parent 3f25355a6d
commit cbac02a96a
40 changed files with 1476 additions and 80 deletions
+34 -1
View File
@@ -22,12 +22,45 @@ Dienst neu starten müssen.
| Anzeige | nur verkürzt (`sk-proj…a1b2`) – vollständig verlässt der Schlüssel den Server nie |
| Eingabe | Passwortfeld, schreibt nur; ein bestehender Wert wird ersetzt |
| Entfernen | löscht den Wert in der Datenbank; steht in der `.env` einer, gilt wieder dieser |
| Modell | frei setzbar, Vorgabe `gpt-5.6-luna` |
| Modell | Auswahlliste der Chat-Modelle, die der Schlüssel nutzen darf (siehe unten); Vorgabe `gpt-5.6-luna` |
| Test | ein Miniaufruf gegen das Modell – sagt, ob Schlüssel, Modellname und Kontingent zusammenpassen |
In der Historie landet, **dass** jemand den Schlüssel gewechselt hat, nie der
Wert. Ein stiller Wechsel wäre später nicht mehr nachvollziehbar.
### Modellauswahl
Statt eines freien Textfelds zeigt der Reiter ein Dropdown. Die Liste kommt von
`GET /api/ai/models`, das Backend fragt dafür OpenAI `GET /v1/models` mit dem
hinterlegten Schlüssel (`listAiModels()` in `ai.js`) – angezeigt wird also genau
das, was dieser Schlüssel darf.
```mermaid
sequenceDiagram
participant B as Browser (AiSettings)
participant S as Backend /api/ai/models
participant O as OpenAI /v1/models
B->>S: GET (admin)
alt Liste im Speicher und < 10 min alt, gleicher Schlüssel
S-->>B: { models }
else
S->>O: GET mit Bearer-Schlüssel
O-->>S: alle Modelle
S->>S: nur Chat-Modelle, neueste zuerst
S-->>B: { models }
end
```
| Regel | Umsetzung |
| --- | --- |
| Nur Chat-Modelle | ID beginnt mit `gpt-`, `o<Ziffer>` oder `chatgpt-`; raus fallen Embedding, Whisper, TTS, Bild, Audio, Realtime, Transcribe, Moderation, Search, Instruct, Codex, Computer-Use, Sora |
| Reihenfolge | nach `created` absteigend – neueste oben |
| Zwischenspeicher | 10 Minuten im Speicher; ein anderer Schlüssel lädt sofort neu |
| Aktives Modell nicht mehr gelistet | bleibt im Dropdown, markiert „– nicht verfügbar" |
| Vorgabemodell | markiert „(Vorgabe)" |
| Liste nicht abrufbar (kein Schlüssel, OpenAI down) | Rückfall auf das Textfeld mit Hinweis und *Erneut versuchen* |
| Schlüssel gespeichert/entfernt | Liste wird neu geladen bzw. verworfen |
> **Fallstrick:** `gpt-5.6-luna` akzeptiert **kein eigenes `temperature`** und
> weist jeden Wert außer dem Standard mit HTTP 400 ab. `ai.js` schickt deshalb
> keines. `response_format: {type:'json_object'}` funktioniert dagegen.