Provider-Failover bei hartem Fehlschlag + Einsatz-Prozentfeld im Dashboard
1) LLM-Failover (core/agent.py): ein hart gescheiterter Provider wird zeitweise uebersprungen, analyze() nimmt den naechsten aus der Kette. Hart = 401/402/403/ 404 (Key/Guthaben/Modell) -> 30 min, nicht erreichbar -> 5 min. Timeout/429/5xx loesen BEWUSST keinen Wechsel aus (voruebergehend; sonst kostet jede Lastspitze die volle Timeout-Summe aller Anbieter). Neu im Snapshot: agent.provider_dead mit Restminuten - der DeepSeek-Ausfall stand vorher nur im Log und blieb deshalb 18 h unbemerkt. 9 Szenarien getestet. 2) Einsatz-Prozentfeld (web, v=137): margin_buffer_pct hatte bisher keine UI. Neues Feld "Einsatz %" neben "Einsatz EUR", POST /api/marginpct -> engine.set_margin_pct -> config.set_margin_buffer, Snapshot margin_pct, neustart-fest. Der feste EUR-Betrag hat Vorrang; das Prozentfeld wird dann ausgegraut, damit nicht unklar bleibt was gilt. Auf 1-99 % geklemmt. Ende-zu-Ende getestet (50, 150->99, 0 und -5 abgelehnt, 95, persistiert). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7e9b0196a5
commit
cc4f1a0863
@@ -91,14 +91,25 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
|
||||
(`glm-4.5-flash`) ist kostenlos**, antwortet in ~2,3 s (DeepSeek ~6 s) und wird im
|
||||
Projekt ohnehin für `daily_levels` genutzt; live verifiziert („[zai] NEUTRAL (30%)").
|
||||
Kimi (`kimi-k2.6`) antwortet ebenfalls (7,6 s), ist aber kostenpflichtig.
|
||||
⚠⚠ **Der eigentliche Befund: die Fallback-Kette hat NICHT gegriffen.** `_active_
|
||||
provider` prüft in `avail` nur das **Key-FORMAT** (`startswith("sk-")`), nicht ob der
|
||||
Key noch funktioniert. Ein Provider ohne Guthaben gilt damit als „verfügbar" und
|
||||
**blockiert die gesamte Kette** (`deepseek→kimi→zai→local`) — der Copilot lief
|
||||
⚠⚠ **Der eigentliche Befund: die Fallback-Kette hatte NICHT gegriffen.** `_active_
|
||||
provider` prüfte in `avail` nur das **Key-FORMAT** (`startswith("sk-")`), nicht ob der
|
||||
Key noch funktioniert. Ein Provider ohne Guthaben galt damit als „verfügbar" und
|
||||
**blockierte die gesamte Kette** (`deepseek→kimi→zai→local`) — der Copilot lief
|
||||
**~18 h still ins Leere** (29 Fehlversuche ab 04.08. 14:52), ohne dass irgendetwas
|
||||
außer einer WARNING-Zeile passierte. **Offen, noch nicht gebaut:** bei hartem
|
||||
Fehlschlag (401/402) den Provider für X Minuten als tot markieren und den nächsten
|
||||
nehmen. Ohne das wiederholt sich dasselbe, sobald das z.ai-Kontingent endet.
|
||||
außer einer WARNING-Zeile passierte.
|
||||
✅ **BEHOBEN am selben Tag (`agent._mark_dead`/`_provider_chain`/`_chain_raw`):** ein
|
||||
hart gescheiterter Provider wird zeitweise übersprungen und `analyze()` nimmt den
|
||||
nächsten aus der Kette. **Hart = 401/402/403/404** (Key/Guthaben/Modell) → **30 min**;
|
||||
**nicht erreichbar** (z. B. Ollama aus) → 5 min. **Bewusst NICHT hart: Timeout, 429,
|
||||
5xx** — das sind vorübergehende Zustände; wer darauf wechselt, verliert bei jeder
|
||||
Lastspitze den bevorzugten Anbieter und zahlt pro Zyklus die volle Timeout-Summe
|
||||
ALLER Anbieter (90–120 s je Stück). Sichtbar im Snapshot als
|
||||
**`agent.provider_dead {provider: Restminuten}`** — der Ausfall stand vorher nur im
|
||||
Log, genau deshalb blieb er 18 h unbemerkt. `_dead` läuft absichtlich **ohne**
|
||||
`self._lock` (`snapshot()` hält ihn bereits, nicht reentrant → Deadlock; dict-Zugriffe
|
||||
sind unter dem GIL atomar). Mit **9 Szenarien getestet** (402→Fallback · zweiter Zyklus
|
||||
überspringt direkt · zwei tote Anbieter · Timeout/429 lösen KEINEN Wechsel aus ·
|
||||
ConnectionError 5 min · alle tot → sauberer Fehler · Erholung nach Ablauf · Snapshot).
|
||||
Zurück auf DeepSeek nach Aufladen: `provider = deepseek`.
|
||||
- **LLM-Provider (Historie):** **DeepSeek (`[deepseek]`) war der aktive
|
||||
Copilot-Provider** (`[agent] provider=deepseek`, 2026-07-20 bis 2026-08-05). `api.deepseek.com`,
|
||||
@@ -3218,6 +3229,19 @@ Enter blurrt nur, `change` sendet einmal (gleicher Fix wie beim Mindestgewinn).
|
||||
· `POST /api/manualmargin {value}` · Snapshot `manual_margin` · neustart-fest über
|
||||
`runtime_state.json`. Ende-zu-Ende getestet (250 → Snapshot → 0 → persistiert).
|
||||
|
||||
**Einsatz in PROZENT (2026-08-05, v=137):** das Gegenstück zum festen Betrag —
|
||||
`margin_buffer_pct` (live 95) hatte bisher **keine UI** und war nur in der ini
|
||||
änderbar. Neu: Feld **„Einsatz %"** (`#marginpct-input`) direkt neben „Einsatz €" in
|
||||
der Order-Leiste · `POST /api/marginpct {value}` → `engine.set_margin_pct` →
|
||||
`config.set_margin_buffer` · Snapshot `margin_pct` · neustart-fest.
|
||||
⚠ **VORRANG ist sichtbar gemacht:** steht links ein fester €-Betrag, wird das
|
||||
Prozent-Feld **ausgegraut** (`.mg-field.muted`, gestrichelter Rand) und der Tooltip
|
||||
sagt, dass der Prozentsatz dann nur noch die Obergrenze ist — zwei scheinbar
|
||||
gleichrangige Felder ohne Hinweis wären die schlechtere Lösung.
|
||||
⚠ Geklemmt auf **1–99 %** (`set_margin_buffer`); 100 % ließe keinen Puffer für
|
||||
Spread/Swap. Ende-zu-Ende getestet: 50 → 50 · 150 → auf 99 geklemmt · 0 und −5
|
||||
abgelehnt · 95 → 95, in `runtime_state.json` persistiert.
|
||||
|
||||
**Sprachausgabe: Piper, lokal** (`tools/speak.py`, `tools/piper/`). Anlass: die
|
||||
Windows-Sprachausgabe funktioniert zwar, aber auf diesem Rechner ist **keine
|
||||
deutsche Stimme installiert** — weder SAPI5 noch OneCore (nur David/Zira/Mark,
|
||||
|
||||
Reference in New Issue
Block a user