fix(llm): model_map params erreichen die Wire-Ebene + Dream-Degenerations-Fixes - #39
Closed
W4-NERF wants to merge 8 commits into
Closed
fix(llm): model_map params erreichen die Wire-Ebene + Dream-Degenerations-Fixes#39W4-NERF wants to merge 8 commits into
W4-NERF wants to merge 8 commits into
Conversation
applyModelParams verschluckte unbekannte Params aus model_map.<role>.params (chat_template_kwargs etc.), chatOpenAI sendete stattdessen das OpenRouter-only reasoning-Feld. Nicht-OpenRouter-Provider (vLLM/LiteLLM, Nemotron 3.5 Lightning) denken dadurch weiter -> Output laeuft in den Token-Cap, strukturiertes JSON kommt abgeschnitten an (dream-eval links_parsed:0, query-temporal invalid JSON). Fix: Options.Extra (json:"-", Ollama-options bleiben sauber), Sammeln in applyModelParams, Merge in chatOpenAI VOR applyOpenAIBodyExtras (Backend- extra_body behaelt Vorrang). TestModelMapExtraPassthrough pinnt den Vertrag.
…-Anpassung Nemotron 3.5 Lightning ignoriert 'Maximum 5 entries' und listet alle Kandidaten (~40 Tokens/Eintrag). Bei 10+ Kandidaten riss der 600er-Cap das JSON mitten im Array (prod: Block 01a0120d, 2x output cap). 1200 deckt ~25 Eintraege ab, weit unter den 12000 der Reasoning-Poison-Aera; model_map kann weiter uebersteuern.
…rd-Fehlschlag Zwei strukturelle Fixes gegen den BRADES-Loop (prod 2026-08-22): 1. CapLocked: dream-keywords nutzt RoleDream, daher ueberschrieb das model_map dream max_tokens (1200, fuer Eval) auch die Keyword-Phase (Default 200). Nemotron degenerierte bei Backslash-lastigem Content bis zum Cap (9x completion_tokens=1200, 'keywords too few (1)') und blockierte den Zyklus (workers=1). CapLocked haelt das Extraktions- Budget hart; applyModelParams respektiert es bei num_predict/max_tokens. 2. CooldownKeywordFailMinutes=12h: nach 3 fehlgeschlagenen Keyword- Versuchen parkt der Block 12h statt 5 Min, statt alle ~6 Min neu gepickt zu werden (Endlos-Retry). Transient-Pfad (Preempt etc.) bleibt bei 5 Min. Tests: TestCapLockedResistsModelMapOverride pinnt den Vertrag.
…eywords + Escaping)
Prod 2026-08-23: CapLocked 200 war zu knapp. 38/49 (78%) der
dream-keywords-Extraktionen liefen in den 200er-Cap (signatur:
completion_tokens=200, response abgeschnitten wie {"rclone serve sftp"),
-> 'keywords too few (1)'. Nemotron 3.5 Lightning benoetigt bei langem
Content + Windows-Pfad-Escaping >200 Tokens fuer das 5-8er-JSON.
400 deckt ~8 Keywords inkl. Escaping ab, bleibt weit unter der
1200-Degenerations-Schwelle. CapLocked bleibt aktiv (kein model_map
Override).
Nemotron 3.5 Lightning degeneriert bei dichten technischen Blaettern
(Windows-Pfade, lange Schemata) unabhaengig vom Budget: 200 UND 400
Token-Caps laufen leer ab ('no valid strings found', prod 2026-08-23,
Block 01a02d3e Transfer-Dashboard). Weiteres Budget-Anheben wuerde die
alte 1200-Degenerations-Schwelle zurueckbringen.
Fix: Nach KeywordsMaxRetries fehlgeschlagenen LLM-Versuchen faellt
GenerateKeywords auf den deterministischen ExtractKeywords-Tokenizer
zurueck (Stopwort-Filter, laengste/rarest Terms, Titel-Bonus). Der Block
wird dann trotzdem in diesem Zyklus gesucht/evaluiert statt geparkt, und
der 12h-Cooldown greift nur noch, wenn selbst der Tokenizer nichts findet.
Test: TestGenerateKeywords_FallbackAfterRetries pinnt den Vertrag (3
LLM-Calls, dann Fallback, >= MinKeywords).
…uchbar)
Der deterministische Fallback (d4ba54e) lieferte fuer dichte technische
Bloecke weiterhin unbrauchbare Keywords: tokenize behandelte
'C:\Users\Brades\.ssh\authorized_keys' als EINEN Riesen-Token
('authorizedkeysfile=c:\users\brades...'), und 'server->streaming-pc'
blieb ein Token. Damit fand die RRF-Suche nichts, der Block wurde
trotzdem geparkt (prod 2026-08-23, Bloecke 01a03062/01a02b24/01a03040).
Fix: tokenize spaltet jetzt an ALLEN Nicht-Buchstaben-/Ziffern-Zeichen
(ausser Binde-strich): Pfade, key=value, user@host, -> werden saubere
Suchbegriffe. Verifiziert: BRADES-Content liefert jetzt
[authorizedkeysfile streaming-pc admin-user-falle pipeline-server
doppel-session] statt Pfad-Muell.
Test: TestExtractKeywords_BradesContent pinnt den Fallback auf dem
Prod-Content.
Prod-Audit 2026-08-24: 3 Cap-Hits in 24h auf dream-eval mit 21-28
Kandidaten (prompt_tokens 5719-7256) - 1200 truncatete das JSON
mitten im Array ('parse links: unexpected end of JSON input'). 1600
deckt ~30 Eintraege ab (~40 Tokens/Eintrag + Struktur), bleibt weit
unter der 12000-Degenerations-Schwelle. Bloecke mit >30 Kandidaten
bleiben selten; die bekommen Cooldown + Retry.
Prod 2026-08-25: Ornith 1.5 35B antwortet auf die Array-Anforderung
konsistent mit einem JSON-OBJEKT ({"Phrase":"description",...})
statt eines Arrays. parseKeywords scheiterte (Erfolgsquote ~16%,
5/30). Fix:
1. Prompt expliziter: 'NO object, NO keys', Antwort muss mit [ starten
und mit ] enden.
2. Parser: Objekt-Drift erkennen ({...}) und die WERTE als Keywords
nehmen (die Schluessel sind oft lange Satzfragmente; die Werte die
Konzepte). Fallback bleibt fuer kaputte Escapes.
Tests: TestParseKeywords_ObjectDrift (Werte statt Schluessel),
TestParseKeywords_ArrayStillWorks (Array-Pfad unberuehrt).
GottZ
pushed a commit
that referenced
this pull request
Aug 25, 2026
…ions-Fixes (#39) Squash der PR-Strecke fix/model-params-wire-passthrough (8 Commits, W4-NERF). llm: applyModelParams verarbeitete aus model_map.<role>.params nur die festen Options-Keys und verschluckte alle unbekannten stillschweigend — insbesondere chat_template_kwargs, bei vLLM/LiteLLM-served Modellen (Nemotron 3.5 Lightning) der einzig wirksame Thinking-Schalter; chatOpenAI übersetzt think:false nur in das OpenRouter-spezifische reasoning-Feld. Fix: Options.Extra (json:"-") sammelt unbekannte Params; chatOpenAI merged sie nach dem Marshal und VOR applyOpenAIBodyExtras in den Body — die Backend-extra_body behält bei Kollision Vorrang, die zdr-Enforcement bleibt das letzte Wort. Ollamas options-Block bekommt bewusst keine fremden Keys (Extra wird auf dem Ollama-Pfad nie gelesen). llm: Options.CapLocked markiert Phasen mit hartem Budget — num_predict/max_tokens-Overrides aus der Serving-Row greifen dort nicht. dream: Keyword-Phase nutzt CapLocked (Budget 200→400, von der Eval-Row nicht mehr aufblähbar); nach erschöpften LLM-Retries deterministischer ExtractKeywords-Fallback statt Parken; erst wenn auch der nichts findet, 12h-Cooldown (CooldownKeywordFailMinutes) statt 5-min-Spin (prod: BRADES- Block blockierte den Zyklus alle ~6 min bei workers=1). tokenize trennt Pfad-/Symbol-Cluster (\ / . = @ -> :), damit dichte Windows-Pfad-Blöcke suchbare Terme liefern; Objekt-Drift-Parser nimmt bei {"k":"v"}-Antworten die Values als Konzepte (Ornith 1.5); Keyword-Systemprompt verschärft (Array-Pflicht). Eval: DefaultNumPredict-Kommentar dokumentiert die Nemotron-Empfehlung dream.num_predict=1600; Cap-Hit bleibt reine Observability (Parse-Fehler + Cooldown unverändert, Test gepinnt). Prod-Evidenz 2026-08-22/24: dream-eval + query-temporal "unexpected end of JSON input" seit v5.0.0-Pool-Migration; 9× completion_tokens=1200 "keywords too few (1)"; Ø 12.000 statt 4.700 Tokens/Eval.
GottZ
added a commit
that referenced
this pull request
Aug 25, 2026
Der ModelMapEditor zeigte params nur als passives "+params (edit via CLI)"-Badge — seit dem generischen Wire-Passthrough (#39) ist jeder params-Key bedeutungstragend (chat_template_kwargs, think, max_tokens, Provider-Knobs), also editiert das Web sie jetzt vollständig: pro Row ein params-Toggle mit JSON-Objekt-Textarea (Mono, Platzhalter zeigt die Nemotron-Empfehlung aus #39). Nur valides JSON-Objekt erreicht onchange — invalide Drafts bleiben in der Textarea stehen (roter Rand + Hinweis), der letzte valide Stand gilt; leer/{} entfernt die params der Row. Die Projektion lebt als exportiertes parseParamsDraft im module-script (mount() im Test-Env nicht verfügbar — VaultForm-Muster: pure Funktion + Server-Render-Assertion). Datenkette (rowsToModelMap, ModelSpec.params, Server map[string]any ohne Key-Restriktion) trug params bereits. Doku: development.md Backends-Sub-Route-Beschreibung erweitert. FE-Gates: vitest 978/978, stylelint+inline-gate+svelte-check+tsc clean.
GottZ
added a commit
that referenced
this pull request
Aug 25, 2026
Owner
|
Merged in v5.3.0 — danke für die saubere Analyse-Strecke, die Prod-Evidenz im PR-Body war Gold wert. Deine 8 Commits sind als ein Squash-Commit mit deiner Autorschaft gelandet: 621b0cd (
Credit steht in CONTRIBUTORS.md (f3acfde). Verifikation vor dem Merge: volle go-short-Suite, Integration dream+llm, vitest 978/978, e2e-visual 447/447. Closing als merged — der Squash taucht für GitHub nicht als Branch-Merge auf, die Commits sind seit dem v5.3.0-Push auf |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(llm): model_map params erreichen die Wire-Ebene + Dream-Degenerations-Fixes (v5.1.0-Basis)
Problem (v5.0.0-Regression + Nemotron-Degeneration, prod-beobachtet 2026-08-22/24)
Seit der v5.0.0-Backend-Pool-Migration schlugen dream-eval und query-temporal
(translate-Rolle) mit
unexpected end of JSON inputfehl. Zusätzlich degenerierteNemotron 3.5 Lightning (vLLM/LiteLLM) auf dichten technischen Blöcken. Symptome:
dream: response hit the output cap — truncated JSONmitnum_predict: 600,completion_tokens: 12000→links_parsed: 0(stiller Fehlschlag, kein WARN)temporal LLM fallback failed, no temporal expansion available→temporal: invalid JSON: unexpected end of JSON inputkeywords too few (1)/no valid strings foundaufBackslash-lastigem Content (BRADES-Migrations-Blöcke), 9×
completion_tokens=1200,Endlos-Retry alle ~6 Min (workers=1 blockierte den Zyklus)
parse links: unexpected end)dream.think=false): Ø ~4.700 Tokens/Eval,keine Fehler. Nach Migration: Ø ~12.000 Tokens, fast jeder Eval am Limit.
Root Cause
applyModelParams(llm/chain.go) verarbeitet ausmodel_map.<role>.paramsnur diefesten Options-Keys (temperature, top_p, num_predict/max_tokens, think, …) und
verschluckt alle unbekannten Keys stillschweigend — insbesondere
chat_template_kwargs, das bei vLLM/LiteLLM-servierten Modellen (Nemotron 3.5Lightning) der einzig wirksame Thinking-Schalter ist.
chatOpenAI(llm/client.go) übersetztthink:falsestattdessen in dasOpenRouter-spezifische Feld
reasoning:{enabled:false,exclude:true}. Dasignorieren nicht-OpenRouter-Provider (vLLM/LiteLLM) → das Modell denkt weiter,
der Output läuft in den Token-Cap, strukturiertes JSON kommt abgeschnitten an.
Der dokumentierte Vertrag — "per-backend tuning belongs in the serving row's
model_map params … which override this default at dispatch" (Kommentar in
dream/evaluate.go, DreamOptions) — galt damit nur für die festen Keys.Fix
Generic Passthrough statt Config-Workaround:
Optionsbekommt einExtra map[string]anyFeld (json:"-"— Ollamas/api/chatoptions-Block bekommt bewusst keine fremden Keys).applyModelParamssammelt unbekannte Params inOptions.Extra, statt siezu droppen.
chat_template_kwargswird explizit als bekannter Passthrough-Keygeführt (dokumentiert, dass er nur auf dem OpenAI-Wire wirkt).
chatOpenAImergedOptions.Extranach dem Marshal in den Request-Body —vor
applyOpenAIBodyExtras, sodass die Backend-extra_bodybeiKey-Kollision weiter Vorrang behält (last write wins, "extra_body only
tightens").
Damit funktioniert die bestehende
model_map-Konfiguration wie dokumentiert:{ "dream": { "model": "DGX_nemotron_3_5_lightning", "params": { "max_tokens": 600, "think": false, "chat_template_kwargs": { "enable_thinking": false } } } }Dream-Budget:
dream.num_predict(v5.1 hot-Setting) statt Code-KonstanteZweites, unabhängiges Problem: Nemotron 3.5 Lightning ignoriert die
"Maximum 5 entries"-Instruktion und listet alle Kandidaten (~40 Tokens pro
pretty-printed Array-Eintrag). Bei 10+ Kandidaten riss der 600er-Cap das JSON
mitten im Array ab (prod: Block
01a0120d, 2× "output cap — truncated JSON"),bei 21–28 Kandidaten auch der 1200er-Cap (
parse links: unexpected end of JSON input, prod 2026-08-24). v5.1.0 bringtdream.num_predictals hot-Setting —dieser PR dokumentiert die Prod-Erkenntnisse im Code-Kommentar und empfiehlt
dream.num_predict=1600für Nemotron-Installationen (~30 Einträge, weit unterder 12000-Degenerations-Schwelle). Modelle, die die 5-Einträge-Instruktion
befolgen, bleiben weit darunter (gemessen 61–315 Tokens).
Keyword-Phase: Cap hart (
CapLocked) + 12h-Cooldown bei FehlschlagDrittes, wieder anderes Problem (prod 2026-08-22, Block
01a02b24"MigrationBRADES-SERVER"): Die Keyword-Phase nutzt dieselbe Rolle
RoleDreamwie dieEval-Phase — das model_map
dream.max_tokens-Override (1200, für Evalgedacht) blähte dadurch auch die Keyword-Extraktion auf (Code-Default 200).
Bei einem Block mit vielen Backslash-Pfaden (
E:\brades-export\etc.)degenerierte Nemotron und generierte 9× deterministisch bis zum Cap
(
completion_tokens: 1200,"keywords too few (1)"). Da der Fehlschlag nureinen 5-Min-Cooldown auslöste, wurde der Block alle ~6 Min neu gepickt —
ein Endlos-Retry, der den Dream-Zyklus (workers=1) blockierte.
Zwei Fixes:
Options.CapLocked: markiert eine Phase mit hartem Budget. DieKeyword-Phase setzt es (
keywordOptions: NumPredict 200, CapLocked) —applyModelParamswendetnum_predict/max_tokens-Overrides dann nichtan. Eine Extraktions-Phase behält damit immer ihr Budget, egal was die
Serving-Row für andere Phasen derselben Rolle konfiguriert.
CooldownKeywordFailMinutes = 12h: nach 3 fehlgeschlagenenKeyword-Versuchen parkt der Block 12h statt 5 Min. Transiente Fehler
(Preempt, GPU-Outage) bleiben beim 5-Min-Pfad.
Deterministischer Keyword-Fallback + Tokenize-Fix
Viertes Problem (prod 2026-08-23): Auch mit CapLocked-400 degenerierte Nemotron
auf dichten technischen Blöcken (Windows-Pfade) bis zum Cap →
no valid strings found. Fixes:GenerateKeywordsfällt nachKeywordsMaxRetriesauf den deterministischenExtractKeywords-Tokenizer zurück — der Block wird trotzdem gesucht/evaluiertstatt geparkt; der 12h-Cooldown greift nur, wenn selbst der Tokenizer nichts findet.
tokenizespaltet an allen Nicht-Buchstaben-/Ziffern-Zeichen (außerBindestrich):
C:\Users\Brades\.ssh\authorized_keyswird zu suchbarenBegriffen statt einem Riesen-Token. Verifiziert: BRADES-Content liefert
[authorizedkeysfile streaming-pc admin-user-falle pipeline-server ...].Tests:
TestCapLockedResistsModelMapOverridepinnt den CapLocked-Vertrag,TestGenerateKeywords_FallbackAfterRetriesden Fallback,TestExtractKeywords_BradesContentden Tokenize-Fix auf Prod-Content.
Die
think:false/chat_template_kwargs.enable_thinking:false-Konfiguration istnur erforderlich, wenn das Serving-Modell per Default Reasoning/Thinking
emittiert — konkret nicht für Qwen 3.6/3.8 (27b/35b), die ohne Thinking
antworten. Betroffen war Nemotron 3.5 Lightning (30B A3B NVFP4) über
vLLM/LiteLLM, das standardmäßig Chain-of-Thought produziert.
Für Qwen-Modelle kann die Konfiguration entfallen bzw. wäre nur eine
Optimierung; für Nemotron und andere Default-Thinking-Modelle ist sie
zwingend, sonst fressen Reasoning-Tokens das Output-Budget und JSON-Antworten
werden abgeschnitten.
Warum KEIN neues Settings-System (bewusste Entscheidung)
Die v5.0.0-Migration (133) hat die alten Tuple-Settings (
dream.think,chat.*etc.) bewusst retired — die Architektur ist jetzt Backend-zentriert(
context_backendsmitmodel_map/extra_body). Diese Konfiguration alsglobale Settings wieder einzuführen wäre ein Schritt zurück und würde die
Per-Rolle-/Per-Backend-Differenzierung (chat vs. dream vs. translate) verlieren.
Der Passthrough macht das vorhandene
model_map-Params-System vollständigfunktionsfähig — der richtige Ort für Backend-spezifische Knöpfe.
Tests
TestModelMapExtraPassthrough(neu, llm/wire_test.go): pinnt den Vertrag —chat_template_kwargslandet inOptions.Extra, geht auf die Wire-Ebene,Backend-
extra_bodygewinnt bei Kollision,think:falsewird gesetzt.TestCapLockedResistsModelMapOverride(neu): CapLocked-Phase behält ihrBudget trotz model_map-Override; Kontrolle ohne Lock übersteuerbar.
TestGenerateKeywords_FallbackAfterRetries(neu): 3 LLM-Calls, danndeterministischer Fallback, >= MinKeywords.
TestExtractKeywords_BradesContent(neu): Tokenize-Fix auf Prod-Content.TestEvaluateRelationships_CapHitStillErrors(dream): Cap-hit-Pfad bleibtObservability-only.
(auf v5.1.0-Basis rebased).
go vet+golangci-lint: 0 Issues.Verifikation (prod, 2026-08-22/24, v5.1.0 + Fixes)
links_evaluated/links_written5/5, 5/5, 6/6 — keine stillen0-Fehlschläge. Tokens 12.000 → 162–865.
12h-Cooldown; kein Endlos-Retry mehr. 24h: 790/835 eval-Erfolg (94,6%),
keyword-Fehler auf ~6 Blöcke begrenzt (Retry-Versuche, Fallback greift).
dream.num_predict=1600adressiert.kein Fallback-Fehler mehr.
ok.Beobachtungszeitraum
Erneuter Beobachtungszeitraum nach Merge dieses PRs: 48 h (mindestens
10 Dream-Zyklen + mehrere temporal-Queries), danach Abschluss-Urteil.