-
Notifications
You must be signed in to change notification settings - Fork 36
fix(processors): treat Fireworks model ids as reasoning-hint incompatible #123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,16 +5,21 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from __future__ import annotations | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Anthropic-served (Claude) ids reject the vLLM ``chat_template_kwargs`` field; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # vLLM-served reasoning models that need the hint never carry these tokens. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _NO_REASONING_HINT_TAGS = ("anthropic", "bedrock", "claude") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Anthropic-served (Claude) ids reject the vLLM ``chat_template_kwargs`` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # field, and Fireworks AI's OpenAI-compatible API validates request bodies | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # strictly and 400s on it as well ("Extra inputs are not permitted"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Fireworks ids always carry the provider namespace | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # (``accounts/fireworks/models/...``). vLLM-served reasoning models that | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # need the hint never carry these tokens. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _NO_REASONING_HINT_TAGS = ("anthropic", "bedrock", "claude", "fireworks") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def model_accepts_reasoning_hint(model: str) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Whether ``model`` tolerates the vLLM ``enable_thinking=False`` hint. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``False`` for Anthropic/Bedrock/Claude ids (they 400 on it), else ``True`` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| — usable directly as a classifier ``disable_reasoning`` default. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``False`` for Anthropic/Bedrock/Claude and Fireworks-served ids (they | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 400 on it), else ``True`` — usable directly as a classifier | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``disable_reasoning`` default. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some context for this line: the LLM classifier is a small model that reads each incoming request and returns a JSON decision about which tier (weak or strong) should handle it. This change makes Concrete suggestion: worth one real call to a Fireworks DeepSeek V4 with a classifier-style prompt and no hint, then check whether
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ran the experiment you asked for — real calls to Fireworks-served DeepSeek V4 Flash (
So: no silent failure. Without the hint, Fireworks still returns the JSON decision in The honest summary of this PR, then: it converts a hard 400 into a working-but-taxed classifier (~5–10× completion tokens, ~3.5× wall time per classifier call). The right endgame for Fireworks is exactly what you suggest — send |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lowered = model.lower() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return not any(tag in lowered for tag in _NO_REASONING_HINT_TAGS) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds
fireworksto the deny list so we stop sendingchat_template_kwargsto Fireworks. Recap for anyone reading:chat_template_kwargsis a vLLM-specific request field — it passes keyword arguments into the model's chat template and isn't part of the OpenAI API.model_accepts_reasoning_hintdecides whether we send it, answering "no" only when the model name contains one of these tags.Two things I looked into that make me wonder if a different structure would hold up better:
1. This list has to grow one provider at a time, and it already misses one.
azure/deepseek-ai/deepseek-v4-proruns on the same gateway, its name matches none of these tags, so we'd still send it the field — and Azure also validates the body strictly. Every new strict provider needs another tag added here by hand.2. Each provider turns reasoning off with a different knob, so a yes/no "does it accept the hint?" can only ever suppress the field — it can't send the right one. From the docs and source:
chat_template_kwargs?chat_template_kwargs.enable_thinking=falsefor Qwen3 — DeepSeek-V3.1 uses the keythinkingwith the opposite defaultreasoning_effort: "none"reasoning_effortthinking: {"type": "disabled"}deepseek-chat(off) vsdeepseek-reasoner(on)Sources: vLLM defines the field and allows (ignores) unknown ones rather than rejecting them —
protocol.py,engine/protocol.py, reasoning outputs; Fireworks reasoning guide; OpenAI reasoning; Anthropic extended thinking; DeepSeek thinking mode; Azure chat reasoning.Concrete suggestion: instead of a deny-list, a map keyed by provider / served-model family → a small dict of per-provider request settings.
reasoning_offis just one entry in that dict, so the next provider-specific quirk we hit (a required header, another body field, a format tweak) becomes another key alongside it — not a whole new parallel map to keep in sync:One honest caveat so we don't over-build it: the hard part is the key, not the map — vLLM's own knob differs by model family (Qwen3
enable_thinkingvs DeepSeek-V3.1thinking), so keying on the model-id string still needs care. If we'd rather not maintain provider detection here at all, the other option is to let each target/endpoint declare its reasoning-off body in config (I left that note on #122).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed the deny-list doesn't scale — your Azure example is a concrete miss today, and the deeper point stands: a yes/no check can only suppress the vLLM field, it can never send the provider's own knob. The measurements on the other thread back the map shape: Fireworks-served DeepSeek V4 accepts
reasoning_effort: "none"and it collapses the classifier call from ~300–640 completion tokens down to a flat 63, with identical decisions.Two observations for the map design, from reading current
main:ReasoningEffortNormalizeralready gives the inbound side areasoning_effortvocabulary; a provider map on the outbound side would meet it in the middle nicely.disable_reasoningon the escalation judge (route-bundle key). The deterministic classifier has no config path at all —make_classifier_config()is called withoutdisable_reasoning, so the model-name check is the only decision point. Whichever shape wins (provider map here, or the per-target flag you suggested on fix(profiles): gate DeepSeek thinking-off default on reasoning-hint compatibility #122), also exposing an explicit classifier override would give strict backends an escape hatch that doesn't depend on name detection.Scope-wise I'd keep this PR as the minimal stop-the-400 fix and do the map as a follow-up — happy to take that on if useful.