Repository navigation
Raise an error when a model spends its whole output budget before replying - #163
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Two silent failures, found while verifying the 4.0.0 stack on 2026-11-06.
1. An exhausted output budget came back as an empty reply
Reasoning tokens count against
max_tokens. A local Gemma 4 model with a 300-token budget, the size the settings panel offers, spent the whole budget reasoning. LiteLLM reportedfinish_reason: lengthwith empty content and 1,375 characters of reasoning. Ragbot returned""with no error, so it looked like a model with nothing to say.synthesis_engine.llm.base.require_reply()now raisesOutputBudgetExhaustedErrorwhen a completion has no text and stopped at its output limit. The error names the model and the budget, and says to raisemax_tokensor lowerthinking_effort. It covers OpenAI and LiteLLMlength, Anthropicmax_tokensand google-genaiFinishReason.MAX_TOKENS. Every path calls it: LiteLLM complete and stream, and direct-backend complete and stream for Anthropic, OpenAI and Google. A partial reply cut at the limit is still returned; only an empty one raises.2.
chat_streamdropped failures from its streaming threadchat_streambridges the backend's callback stream to a generator through a worker thread. That thread'sfinallyposted the end-of-stream sentinel, but its exception went nowhere. A streamed chat that failed, for any reason, simply ended. The thread now hands its exception back, and the generator re-raises it asChatErrorafter delivering any text that arrived first. The API already turns that into the SSEerrorevent, and the web UI shows it in the reply bubble.Verification
tests/test_output_budget.py, 21 tests:require_replyacross finish-reason shapes, including partial replies and normal stops;chat_streampropagating a failure after delivering earlier text.main'score.py, bothchat_streamtests fail withDID NOT RAISE ChatError.make test-fast: 1152 passed, 14 skipped.gemma4:26bthrough the API image:chat(max_tokens=300)now raisesChatError: Chat failed: ollama_chat/gemma4:26b used its whole 300-token output budget without writing a reply; reasoning counts against that budget. Raise max_tokens or lower thinking_effort.max_tokens=4096returns the full answer.🤖 Generated with Claude Code