fix(complexity_router): frame the classifier prompt around the context window it was given - #35508
Closed
tin-berri wants to merge 1 commit into
Closed
Conversation
… it was given The classifier's system role was built from the rubric override alone, so the same bytes shipped whether classifier_context_window_size was 0, 3 or 5. One static string cannot describe both payloads, and it was wrong in both directions. At 0 nothing about the conversation reaches the payload, yet the boundary paragraph still promised quoted prior turns and asked the model to rate "the work the current message asks for, judged in the context of the conversation it continues", which on a bare "yes" is a demand to weigh an exchange it cannot see. Above 0 the window was quoted but the model was never told it existed or that its view was bounded, so the operator's choice of N never reached it. The system role is now tier definitions, then the trust boundary, then a framing sentence selected by the window: above 0 it names the configured count and keeps the short-reply guidance, at 0 it says plainly that no earlier turns are quoted. Not a revival of "Classify only the current message", which told the model to disregard context that was present; this describes a payload that has none. The prior-turn half of the boundary moved into that framing, since the caller's system prompt is quoted at every setting and prior turns are not. The framing keys on configuration and never on the individual request, so the system role stays prompt-cacheable across a session's classifier calls; "may be quoted" already covers a single-turn request with nothing to quote.
Contributor
Greptile SummaryThis PR makes the complexity classifier’s system prompt describe the configured conversation-context window while preserving the existing trust boundary and custom tier rubric.
Confidence Score: 5/5The PR appears safe to merge with no actionable defects identified. The classifier prompt and user payload derive context behavior from the same validated configuration value, and the added tests cover both disabled and enabled context-window paths.
|
| Filename | Overview |
|---|---|
| litellm/router_strategy/complexity_router/complexity_router.py | Splits context guidance from the trust boundary and consistently selects framing from the validated context-window configuration. |
| tests/test_litellm/router_strategy/test_complexity_router.py | Updates helper callers and adds focused tests for framing composition, pluralization, configured counts, and classifier payload consistency. |
Reviews (1): Last reviewed commit: "fix(complexity_router): frame the classi..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
tin-berri
added a commit
that referenced
this pull request
Aug 1, 2026
… rubric on the window it was given Two changes to the classifier's system role, both narrowing it rather than adding to it classifier_tier_rubric let an operator replace the tier definitions. It shipped in #35471 alongside the assistant-turn context window, but the two answer different halves of the same report and only the context window was asked for. The override carried a composed prompt, an overridable and a non-overridable half, a blank-is-unset rule, a length-warning validator and a pair of dashboard controls. All of it goes The rubric then closes on one of two lines, chosen by classifier_context_window_size. At 0 no conversation is quoted, so the line is the original one, byte for byte: a deployment that sends no context is told to classify the current message and nothing else, which is what it could see all along. Above 0 the turns are quoted, and the original line told the model to disregard them, which is how a request whose difficulty was established in an earlier turn came back SIMPLE on the word "yes". There the line instead says to classify the current message using the quoted turns as context, and to rate what a short reply approves rather than the reply The choice keys on the window and not on classifier_context_include_assistant_turns. Whether the quoted turns are the user's alone or include the assistant's replies does not change what the model needs told, and whose turn is whose is already on the turns. Keying it on the assistant toggle would put the default deployment back on the original line, which is the configuration the report was raised against Folds in #35508, which built the window-dependent framing on top of the override this removes; that PR is closed in favour of this one
5 tasks
Contributor
tin-berri
added a commit
that referenced
this pull request
Aug 1, 2026
… rubric on the window it was given (#35504) Two changes to the classifier's system role, both narrowing it rather than adding to it classifier_tier_rubric let an operator replace the tier definitions. It shipped in #35471 alongside the assistant-turn context window, but the two answer different halves of the same report and only the context window was asked for. The override carried a composed prompt, an overridable and a non-overridable half, a blank-is-unset rule, a length-warning validator and a pair of dashboard controls. All of it goes The rubric then closes on one of two lines, chosen by classifier_context_window_size. At 0 no conversation is quoted, so the line is the original one, byte for byte: a deployment that sends no context is told to classify the current message and nothing else, which is what it could see all along. Above 0 the turns are quoted, and the original line told the model to disregard them, which is how a request whose difficulty was established in an earlier turn came back SIMPLE on the word "yes". There the line instead says to classify the current message using the quoted turns as context, and to rate what a short reply approves rather than the reply The choice keys on the window and not on classifier_context_include_assistant_turns. Whether the quoted turns are the user's alone or include the assistant's replies does not change what the model needs told, and whose turn is whose is already on the turns. Keying it on the assistant toggle would put the default deployment back on the original line, which is the configuration the report was raised against Folds in #35508, which built the window-dependent framing on top of the override this removes; that PR is closed in favour of this one
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.
TLDR
Problem this solves:
classifier_context_window_sizeis 0, 3 or 5, so the operator's opt-in never reaches the model that has to act on itHow it solves it:
Relevant issues
classifier_context_window_sizeinstead of shipping one static string for every settingLinear ticket
Resolves LIT-5099
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Screenshots / Proof of Fix
Two proxies, same config file, same request. Port 4101 runs base staging
b1fd20f4cd, port 4102 runs this branch. Both routers classify withbedrock/us.anthropic.claude-haiku-4-5-20251001-v1:0and route SIMPLE/MEDIUM to Haiku, COMPLEX/REASONING to Sonnet; they differ only inclassifier_context_window_size(0 vs 5, assistant turns on for the second).The upstream was the sandbox gateway rather than api.anthropic.com, because both provider keys in
.envare out of credit; the calls are real Bedrock traffic and cost real money. A direct-to-Anthropic re-run is owed if anyone wants it.The classifier system role and payload below are lifted from each proxy's
--detailed_debuglog for the request above.Read the two BEFORE blocks first: the system role is byte-identical across the two settings, which is the defect. The AFTER blocks differ, and each one now describes the payload printed directly under it. Tiers are unchanged in both settings, which is the intent here: window 0 still routes the bare "yes" to Haiku, and the #35471 behaviour at window 5 still routes it to Sonnet on the strength of the plan it approves. What changed is that neither setting is now instructed against the payload it actually receives.
Type
🐛 Bug Fix
Changes
_classification_system_prompttakes the configured window alongside the rubric override and composes three parts: the operator's tier definitions, the trust boundary, and a context framing chosen by the window. The prior-turn half of the old boundary sentence moved into the framing, since the caller's system prompt is quoted at every setting and prior turns are not.The window-0 wording is not a revival of "Classify only the current message", which #35471 removed for telling the model to disregard context that was present. This describes a payload that genuinely has none.
Tests cover both branches, the configured count reaching the prompt (singular and plural), the exact composition order, and the wiring through
aclassifywhere the system role and the user payload have to agree about whether a conversation was quoted. Mutation testing on the new logic killed 12 of 12: gate flipped to>= 0, either branch forced, the count hardcoded at the call site and inside the framing, each of the three sections dropped, framing and boundary swapped, the paragraph separator collapsed, both plural forms pinned, and the rubric override ignored.QA runbook
classifier_type: llm,classifier_context_window_size: 0, and a second identical one withclassifier_context_window_size: 5plusclassifier_context_include_assistant_turns: true.complexity_router_configgoes as a direct sibling ofmodelunderlitellm_params--detailed_debugand send the three-turn conversation above to each routerRecent conversationblockclassifier_tier_rubricon either router and confirm the operator's tiers replace the built-in ones while both the trust boundary and the context framing survive