Skip to content

fix(processors): treat Fireworks model ids as reasoning-hint incompatible - #123

Open
himorishige wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
himorishige:fix/reasoning-hint-fireworks-tag
Open

fix(processors): treat Fireworks model ids as reasoning-hint incompatible#123
himorishige wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
himorishige:fix/reasoning-hint-fireworks-tag

Conversation

@himorishige

@himorishige himorishige commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Fixes the classifier half of #121.

What

Fireworks AI's OpenAI-compatible API validates request bodies strictly and rejects the vLLM chat_template_kwargs hint with HTTP 400 (Extra inputs are not permitted, field: 'chat_template_kwargs'). Fireworks model ids always carry the provider namespace (accounts/fireworks/models/...), so this PR adds fireworks to _NO_REASONING_HINT_TAGS.

With this, the LLM classifier's disable_reasoning auto-detect stops injecting the hint for Fireworks-served classifier models, and — combined with #122 — deterministic tier calls stop injecting it as well.

Validation

  • Parametrized deny-list cases extended with three Fireworks ids (deepseek-v4-flash, deepseek-v4-pro, glm-5p2); tests/test_reasoning_hint.py 13 passed.
  • Real-world check: with the deny list patched this way, a deterministic profile with a Fireworks-hosted classifier (accounts/fireworks/models/deepseek-v4-flash) classifies successfully end to end (main @ 060ad758, Fireworks serverless).

Summary by CodeRabbit

  • Bug Fixes
    • Improved compatibility with Fireworks-served models by preventing unsupported reasoning hints that could cause request failures.
    • Added coverage for additional DeepSeek and GLM model variants.

…ible

Fireworks AI's OpenAI-compatible API validates request bodies strictly
and rejects the vLLM chat_template_kwargs hint with HTTP 400 ("Extra
inputs are not permitted, field: 'chat_template_kwargs'"). Fireworks
model ids always carry the provider namespace
(accounts/fireworks/models/...), so extend the deny list used by
model_accepts_reasoning_hint() to match them.

This stops the LLM classifier's disable_reasoning auto-detect from
injecting the hint at Fireworks-served classifier models, and — once
the DeepSeek tier overrides consult the same check — deterministic
tier calls as well.

Signed-off-by: Hiroshi Morishige <hiroshi.morishige@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The reasoning hint compatibility check now rejects model IDs containing "fireworks", documents the associated HTTP 400 behavior, and tests DeepSeek and GLM Fireworks model identifiers.

Changes

Reasoning Hint Compatibility

Layer / File(s) Summary
Fireworks model rejection and coverage
switchyard/lib/processors/reasoning_hint.py, tests/test_reasoning_hint.py
The reasoning-hint blocklist includes "fireworks", and parameterized tests verify rejection for three Fireworks-served model IDs.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit found a hint in flight,
Then Fireworks made the answer “no” just right.
DeepSeek and GLM joined the test,
The blocklist now knows them best.
Hop, hop—HTTP four-hundred rests!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: marking Fireworks model IDs as incompatible with the reasoning hint.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/test_reasoning_hint.py (1)

21-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rename the parametrized test to cover Fireworks models.

The test now covers more than the Claude family, so test_claude_family_rejects_hint is misleading. Rename it to a provider-neutral name such as test_models_rejecting_reasoning_hint.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_reasoning_hint.py` around lines 21 - 23, Rename the parametrized
test function test_claude_family_rejects_hint to a provider-neutral name such as
test_models_rejecting_reasoning_hint, keeping its existing parameters and
assertions unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/test_reasoning_hint.py`:
- Around line 21-23: Rename the parametrized test function
test_claude_family_rejects_hint to a provider-neutral name such as
test_models_rejecting_reasoning_hint, keeping its existing parameters and
assertions unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ecfbcbb0-16db-4487-b250-1d290351914a

📥 Commits

Reviewing files that changed from the base of the PR and between 060ad75 and ab29944.

📒 Files selected for processing (2)
  • switchyard/lib/processors/reasoning_hint.py
  • tests/test_reasoning_hint.py

# 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")

@elyasmnvidian elyasmnvidian Jul 28, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds fireworks to the deny list so we stop sending chat_template_kwargs to Fireworks. Recap for anyone reading: chat_template_kwargs is 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_hint decides 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-pro runs 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:

Provider Accepts chat_template_kwargs? How you actually turn reasoning off
vLLM yes (unknown fields are ignored, not rejected) chat_template_kwargs.enable_thinking=false for Qwen3 — DeepSeek-V3.1 uses the key thinking with the opposite default
Fireworks no — 400 top-level reasoning_effort: "none"
Azure AI (DeepSeek) no — 400 no request parameter; the model decides
OpenAI no reasoning_effort
Anthropic no thinking: {"type": "disabled"}
DeepSeek API no choose the model: deepseek-chat (off) vs deepseek-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_off is 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:

# provider / model-family -> per-provider request settings
# reasoning_off is one key here; future per-provider quirks live alongside it
PROVIDER_SETTINGS = {
    "vllm-qwen":     {"reasoning_off": {"chat_template_kwargs": {"enable_thinking": False}}},
    "vllm-deepseek": {"reasoning_off": {}},                        # DeepSeek-V3.1 default is off (key is `thinking`)
    "fireworks":     {"reasoning_off": {"reasoning_effort": "none"}},
    "openai":        {"reasoning_off": {"reasoning_effort": "minimal"}},
    "anthropic":     {"reasoning_off": {"thinking": {"type": "disabled"}}},
    # azure deepseek / deepseek-api: no per-request reasoning switch
}
# lookup: PROVIDER_SETTINGS.get(provider, {}).get("reasoning_off")

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_thinking vs DeepSeek-V3.1 thinking), 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).

— 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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. disable_reasoning controls whether we send enable_thinking=false. That hint matters because DeepSeek V4 is a reasoning model: without it, the model can put its JSON decision in the reasoning_content field and leave the normal content field empty. The classifier only reads content, so an empty content means it can't parse a decision and falls back to the default tier.

This change makes model_accepts_reasoning_hint return False for Fireworks, so the classifier stops sending the hint to a Fireworks classifier model. My question: does a Fireworks-served DeepSeek V4 still put its answer in content when we don't send the hint? If it behaves like the vLLM version and leaves content empty, we've replaced a clear error (the 400) with a silent one — the classifier gets nothing back and falls back on every request, with no error to notice.

Concrete suggestion: worth one real call to a Fireworks DeepSeek V4 with a classifier-style prompt and no hint, then check whether content is filled in. If it comes back empty, note that Fireworks disables reasoning with the top-level reasoning_effort: "none" field, not chat_template_kwargs (docs) — so the fix would be to send that for Fireworks rather than nothing, which is what the map on the line above would let us do. The two places that read disable_reasoning are llm_classifier/request_processor.py and stage_router/classifier.py.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants