You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix HuggingFace providers reserving the entire context window as output when `max_tokens` is unset (causing spurious ~1M output-window reports and rate limiting), handle invocation errors adaptively, and stop rendering backend errors as chat bubbles in the frontend. Steps 1-4 done; Step 5 (frontend) pending.
56
+
57
+
## Completed
58
+
59
+
1.**models.dev catalog client** (`models_catalog.py`): lazy fetch of the models.dev api.json with in-memory + 1-day on-disk cache, offline fallback, graceful `None` for providers/models missing from the catalog.
60
+
2.**Invocation error classification**: `LLMInvocationErrorCategory` enum (errors.py) + `classify_llm_invocation_error()` (llm.py) -- tolerant regex classifiers with cause-chain unwrapping. `looks_like_structured_output_error` folded into the `STRUCTURED_OUTPUT_REJECTED` category.
61
+
3.**Always-bounded max output tokens**: every invoke now gets a finite, clamped max-output param. `resolve_output_token_limit()` resolves explicit param -> generic `max_output_tokens` -> per-role fallback, then clamps to catalog `limit.output` and to `context - estimated input` (chars//4), since HF applies a **total budget** (input + output <= context). `LLMModel.provider_defaults` (Layer 4) + optional `providers` config section; `_provider_defaults_for_role` shared on `BaseLangGraph`. Per-node `max_output_tokens` added to RAG/shared node `model_defaults`; code-pkg deferred to the `plan` role default.
62
+
4.**Adaptive retry** in `BaseLLMNode`: `CONTEXT_OVERFLOW` shrinks the output window and retries (<=3x); `finish_reason="length"` grows it and retries (<=2x); rate-limit and other errors re-raise immediately. Bails when the window cannot move.
63
+
64
+
## Errors and Lessons
65
+
66
+
-**Do not reuse tenacity for this retry.** Tenacity (api, mcp tools) retries the *identical* call with backoff; our retry must change parameters between attempts, which tenacity's decorator cannot do cleanly. Backoff is also most useful for rate-limit retries, which we deliberately surface.
67
+
-**`ChatHuggingFace` uses `max_tokens`** (mapped internally to `max_new_tokens`), not `max_new_tokens` -- only ollama is `num_predict`. Using `max_new_tokens` gets stripped by provider field filtering. Verify against the installed provider class.
68
+
-**Resolver "explicit wins" precedence bit the retry**: setting the generic `max_output_tokens` key while a stale explicit `max_tokens` was present meant the window never changed; fix is to set the provider token param directly, then re-clamp.
69
+
- Async tests must NOT inherit `unittest.TestCase` (pytest-asyncio auto mode silently skips those coroutines).
0 commit comments