Skip to content

vLLM chat_template_kwargs hint is injected for non-vLLM providers — HTTP 400 on Fireworks-served deepseek-v4 targets #121

Description

@himorishige

Summary

Routing deepseek-v4 models served by Fireworks AI through the Python deterministic (LLM-classifier) profile fails on every request with:

HTTP 400 {"error":{"message":"Extra inputs are not permitted, field: 'chat_template_kwargs'", ...}}

chat_template_kwargs.enable_thinking=false is a vLLM-side hint. Fireworks' OpenAI-compatible API validates request bodies strictly and rejects the unknown field. Two Python-side code paths inject the hint for provider-agnostic targets:

  1. apply_deepseek_overrides() (switchyard/lib/profiles/tier_target_builders.py) injects it into tier calls for any target whose model id contains deepseek-v4, regardless of provider. It does not consult model_accepts_reasoning_hint(), the model-id compatibility check the LLM classifier already uses for the same hint.
  2. model_accepts_reasoning_hint() (switchyard/lib/processors/reasoning_hint.py) deny-lists only anthropic / bedrock / claude, so the classifier's disable_reasoning auto-detect injects the hint for Fireworks-hosted classifier models (accounts/fireworks/models/...).

Reproduction

profiles.yaml:

endpoints:
  fireworks:
    base_url: https://api.fireworks.ai/inference/v1
    api_key: ${FIREWORKS_API_KEY}
targets:
  weak:
    endpoint: fireworks
    model: accounts/fireworks/models/deepseek-v4-flash
    format: openai

Any profile that routes a tier call through build_tier_backend() (deterministic / escalation router) to the weak target returns the 400 above for every request.

Expected

Provider-specific defaults should not be sent to providers that reject them; at minimum the existing compatibility check should be honored consistently.

Workaround

Declaring an explicit empty extra_body: {} on the target suppresses the tier-call injection (caller-supplied extras win). The classifier-side injection has no config-level escape in the v2 profile config path.

Proposed fixes (PRs to follow)

  • Gate the apply_deepseek_overrides() body default on model_accepts_reasoning_hint() — the same check the classifier applies.
  • Add fireworks to the reasoning-hint deny list (Fireworks ids always carry the accounts/fireworks/models/ namespace).

Related observation (out of scope here)

The Rust-side llm-routing profile's classifier also injects the hint with its own logic; if the components-v2 removal in #119 lands this may be moot, so this issue only covers the Python paths that survive it.

Verified on main @ 060ad758 against Fireworks serverless (deepseek-v4-flash / deepseek-v4-pro).

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions