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:
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.
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).
Summary
Routing deepseek-v4 models served by Fireworks AI through the Python deterministic (LLM-classifier) profile fails on every request with:
chat_template_kwargs.enable_thinking=falseis 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:apply_deepseek_overrides()(switchyard/lib/profiles/tier_target_builders.py) injects it into tier calls for any target whose model id containsdeepseek-v4, regardless of provider. It does not consultmodel_accepts_reasoning_hint(), the model-id compatibility check the LLM classifier already uses for the same hint.model_accepts_reasoning_hint()(switchyard/lib/processors/reasoning_hint.py) deny-lists onlyanthropic/bedrock/claude, so the classifier'sdisable_reasoningauto-detect injects the hint for Fireworks-hosted classifier models (accounts/fireworks/models/...).Reproduction
profiles.yaml:Any profile that routes a tier call through
build_tier_backend()(deterministic / escalation router) to theweaktarget 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)
apply_deepseek_overrides()body default onmodel_accepts_reasoning_hint()— the same check the classifier applies.fireworksto the reasoning-hint deny list (Fireworks ids always carry theaccounts/fireworks/models/namespace).Related observation (out of scope here)
The Rust-side
llm-routingprofile'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 @
060ad758against Fireworks serverless (deepseek-v4-flash / deepseek-v4-pro).