Waehlt jemand (Admin/Preset) ein Modell ohne Tool-faehigen Endpoint, lieferte
OpenRouter ein 404 "No endpoints found that support tool use" -> bisher 4x Retry
mit Backoff und danach stille, konfident-veraltete Fallback-Antwort.
- ToolCallingLLM erkennt das strukturelle 404 (_looks_tool_unsupported), merkt
es fuer den Turn (_tools_unsupported), deaktiviert Tools und antwortet SOFORT
plain (kein Haenger). Log-Warnung + Metrik tool_unsupported_total.
- Backstop-Inject jetzt als SYSTEM-Kontext statt synthetischem tool_call: das
funktioniert mit tool-faehigen UND tool-unfaehigen Modellen (letztere lehnen
tool-Messages mit 405 ab). Folge: heikle Kategorien bleiben auch auf einem
tool-unfaehigen Modell korrekt; nur nicht-heikle Volatil-Fragen fallen plain.
Live verifiziert gegen mistral-2501 (tool-unfaehig): 3.7s statt Haenger,
Backstop liefert weiter "Merz"; gegen mistral-3.2 kein Regress (1 Suche).
Tests: 312 gruen. Doc §5.7/§5.8/§8.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sonar liefert Citation-URLs; bisher verworfen, jetzt bis in die UI gereicht.
- ToolCallingLLM: _run_tool/_inject_forced_search geben Citations zurueck;
stream() sammelt sie ueber einen on_citations-Callback.
- Orchestrator: chat_stream reicht on_citations durch (via _stream_supports)
und legt die gesammelten URLs in PipelineTrace.citations.
- schemas: PipelineTrace.citations.
- ws.py: Citations im semantic-Event.
- Frontend (app.js): renderCitations() zeigt bis zu 4 Quellen-Links
(Hostname, neuer Tab) unter der Antwort-Bubble.
Nur ueber den Streaming-Pfad (= Web-UI). Live verifiziert: "Wer ist
Bundeskanzler?" -> 15 Quellen gesammelt (Bundeskanzler.de, Wikipedia, ...).
Tests: 312 gruen; JS-Syntax geprueft. Doc §8.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bisher wurde der Filler im "Im Gerät"-Modus (Web Speech API) nur angezeigt,
aber nicht gesprochen (Server-Audio ist dort aus). Jetzt eigener Kanal:
- Orchestrator: chat_stream bekommt on_filler; _emit_filler bevorzugt den
dedizierten Filler-Kanal (sonst Fallback on_token). Ephemer wie bisher.
- ws.py: on_filler -> {type:"filler", text}.
- Frontend (app.js): {type:"filler"} -> transient in der STATUS-Zeile (nicht
in der Antwort-Bubble) und im Geräte-Modus sofort speechSynthesis.speak()
OHNE cancel (mehrere Filler -> Queue; die Antwort cancelt spaeter und
uebernimmt). Abgesichert ueber deviceVoicesReady()/deviceVoiceReady(lang) —
keine Verschaerfung der bekannten deviceVoiceReady-Regression.
Server-TTS-Modus unveraendert (Filler zusaetzlich als "Satz null" gesprochen),
zeigt den Filler nun ebenfalls in der Status-Zeile statt in der Bubble.
Tests: 312 gruen; JS-Syntax geprueft. Doc §5.4.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Schliesst die ~5-15 % Erst-Miss bei Fragen, deren veraltete Antwort
konfident-falsch waere (Amtstraeger, Wohnort lebender Personen, "lebt X noch").
- app/pipeline/search_backstop.py: should_force_search() erkennt solche
Phrasierungen per mehrsprachiger Regex (de/en/nl + etwas fr/es) mit
Gegenwarts-Schutz ("wer ist", nicht "wer war"). 22 Offline-Tests.
- ToolCallingLLM: bei Treffer wird die Suche DETERMINISTISCH erzwungen.
Wichtig: NICHT per tool_choice-Forcen (das honoriert das Modell nicht
zuverlaessig -> beobachtet: ignoriert, veraltete Antwort). Stattdessen
fuehrt der Wrapper die Suche selbst aus und speist das Ergebnis als
synthetischen Tool-Turn ein (_inject_forced_search). Metrik
search_forced_total. Filler/Geduld feuern auch hier.
- Haertung: _chat faengt non-JSON-200 ab (war ein Crash-/Hänger-Ausloeser,
betrifft auch Produktion).
Live verifiziert: "Wer ist Bundeskanzler?" sucht jetzt 3/3 -> Merz.
Tests: 312 gruen. Doc: §5.7.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ersetzt die statischen 3-Satz-Filler durch einen kreativen, weniger
roboterhaften Mechanismus für schnelles Feedback waehrend der Web-Suche.
- app/pipeline/fillers.py: EINZIGE Pflege-Stelle. Englische Master-Listen
(12 OPENING + 16 PATIENCE). Zur Laufzeit einmal je Sprache uebersetzt und
gecacht (durchgaengig hoefliche Anrede "Sie"/"vous"/"usted"...), Fallback
Englisch. Zufaellige Auswahl statt fester Reihenfolge.
- Geduld-Schleife: bei laengerer Recherche schiebt ein Hintergrund-Task alle
FILLER_PATIENCE_INTERVAL Sekunden einen zufaelligen PATIENCE-Satz nach;
Abbruch beim ersten Antwort-Delta und im finally (keine verwaisten Tasks).
- Ephemer wie bisher: laeuft ueber on_token/_dispatch, nie in
semantic_response/History.
- Standardsprache wird beim Start vorgewaermt (warmup.py) -> erster Such-Turn
ohne Uebersetzungs-Latenz. dependencies: get_fillers()-Singleton.
Doc: Docs/weg2-tool-calling.md §5.4. Tests: 290 gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Frische-/Web-Such-Funktion: Das zentrale Modell entscheidet selbst via
web_search-Tool, ob es tagesaktuelle Fakten braucht, holt sie über
perplexity/sonar und formuliert die Antwort in Persona (Augment).
- SonarTool (app/tools/web_search.py): Fakten via perplexity/sonar,
Citations als Metadaten, honest-punt-Sentinel bei Fehler/Timeout.
- ToolCallingLLM (app/providers/llm/tool_calling.py): agentischer Loop als
LLMProvider; complete() + gestreamtes stream() mit SSE-Tool-Assembler;
Persona- + Trigger- + Vorrang-Prompt (Tool-Ergebnis schlaegt Gedaechtnis).
- Verdrahtung: Registry-Eintrag openrouter-tools; web_search_enabled
(global an, pro Nutzer/Profil abschaltbar) via Route-Layering;
build_orchestrator waehlt tool-faehig vs. plain, Fallback-Kette erhalten.
- Filler: ephemerer Beruhigungssatz beim Tool-Start (sofort angezeigt UND
gesprochen als Satz null), nie in semantic_response/History; on_tool_start
defensiv durch die stream()-Kette gefaedelt (kein Bruch bestehender Provider).
- Modellwechsel: Standard auf mistralai/mistral-small-3.2-24b-instruct
(tool-faehig; im Eval einziger Recall-Gate-Passer). 2501 ist tool-unfaehig.
- Eval-Harness (eval/tool_calling/): Datensatz + Runner zur Modellauswahl.
Doc: Docs/weg2-tool-calling.md. Tests: 290 gruen.
Offen (Schritt 5): Koreferenz-Vorstufe (nl-Pronomen) + Metrik-Zaehler.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>