feat(fireworks_ai): full chat_template_kwargs parity with the gateway

Map the remaining gateway-documented effort keys: thinking as an alias
for enable_thinking (enable_thinking wins when both are present),
reasoning_budget to an integer reasoning_effort (skipped when thinking
is explicitly off), and low_effort=true to reasoning_effort=low (budget
wins when both are set). guided_json and guided_choice response_format
wrappers now include the name field (response and choice) to match the
gateway wire shape.
This commit is contained in:
Miles Adkins 2026-08-06 10:46:48 -05:00
parent 431f61b4f7
commit 4a601c49a6
2 changed files with 104 additions and 15 deletions

View file

@ -61,8 +61,32 @@ def _extract_fireworks_hidden_params(payload: dict) -> dict:
return {**top_level, **per_choice}
def _json_schema_response_format(schema: object) -> Mapping[str, object]:
return {"type": "json_schema", "json_schema": {"schema": schema}} # mutable-ok: JSON request body
def _json_schema_response_format(schema: object, name: str) -> Mapping[str, object]:
return {"type": "json_schema", "json_schema": {"name": name, "schema": schema}} # mutable-ok: JSON request body
_EFFORT_KWARG_KEYS: Final = frozenset({"enable_thinking", "thinking", "reasoning_budget", "low_effort"})
def _bool_from_kwargs(kwargs: Mapping[str, object], keys: tuple[str, ...]) -> bool | None:
for key in keys:
value = kwargs.get(key)
if isinstance(value, bool):
return value
return None
def _effort_from_chat_template_kwargs(kwargs: Mapping[str, object]) -> object:
enable_thinking: Final = _bool_from_kwargs(kwargs, ("enable_thinking", "thinking"))
if enable_thinking is False:
return "none"
budget: Final = kwargs.get("reasoning_budget")
if isinstance(budget, (int, float)) and not isinstance(budget, bool) and budget > 0:
return int(budget)
low_effort: Final = _bool_from_kwargs(kwargs, ("low_effort",))
if low_effort is True:
return "low"
return None
_NIM_VLLM_STRIP_PARAMS: Final = frozenset(
@ -357,29 +381,28 @@ class FireworksAIConfig(FireworksAIMixin, OpenAIGPTConfig):
type(chat_template_kwargs).__name__,
)
return ()
other_keys: Final = tuple(sorted(k for k in chat_template_kwargs if k != "enable_thinking"))
other_keys: Final = tuple(sorted(k for k in chat_template_kwargs if k not in _EFFORT_KWARG_KEYS))
if other_keys:
verbose_logger.debug(
"fireworks_ai does not support chat_template_kwargs keys %s for model=%s; dropping them.",
other_keys,
model,
)
if "enable_thinking" not in chat_template_kwargs:
return ()
if "reasoning_effort" in optional_params or "thinking" in optional_params:
verbose_logger.debug(
"fireworks_ai ignoring chat_template_kwargs.enable_thinking; explicit reasoning_effort/thinking takes precedence."
"fireworks_ai ignoring chat_template_kwargs; explicit reasoning_effort/thinking takes precedence."
)
return ()
effort: Final = _effort_from_chat_template_kwargs(chat_template_kwargs)
if effort is None:
return ()
if not supports_reasoning(model=model, custom_llm_provider="fireworks_ai"):
verbose_logger.debug(
"fireworks_ai model %r does not support reasoning; dropping chat_template_kwargs.enable_thinking.",
"fireworks_ai model %r does not support reasoning; dropping chat_template_kwargs effort keys.",
model,
)
return ()
if chat_template_kwargs["enable_thinking"]:
return ()
return (("reasoning_effort", "none"),)
return (("reasoning_effort", effort),)
@staticmethod
def _translate_guided_params(
@ -396,7 +419,7 @@ class FireworksAIConfig(FireworksAIMixin, OpenAIGPTConfig):
)
return ()
if extra_body.get("guided_json") is not None:
return (("response_format", _json_schema_response_format(extra_body["guided_json"])),)
return (("response_format", _json_schema_response_format(extra_body["guided_json"], "response")),)
if extra_body.get("guided_grammar") is not None:
grammar_response_format: Final = { # mutable-ok: JSON request body
"type": "grammar",
@ -407,7 +430,7 @@ class FireworksAIConfig(FireworksAIMixin, OpenAIGPTConfig):
"type": "string",
"enum": extra_body["guided_choice"],
}
return (("response_format", _json_schema_response_format(choice_schema)),)
return (("response_format", _json_schema_response_format(choice_schema, "choice")),)
def _transform_tools(self, tools: list[OpenAIChatCompletionToolParam]) -> list[OpenAIChatCompletionToolParam]:
for tool in tools:

View file

@ -1338,6 +1338,66 @@ def test_map_extra_body_params_chat_template_kwargs_enable_thinking():
assert enabled == {}
def test_map_extra_body_params_chat_template_kwargs_thinking_alias():
config = FireworksAIConfig()
result = config.map_extra_body_params(
{"extra_body": {"chat_template_kwargs": {"thinking": False}}},
_REASONING_MODEL,
)
assert result == {"reasoning_effort": "none"}
def test_map_extra_body_params_chat_template_kwargs_enable_thinking_wins_over_thinking():
config = FireworksAIConfig()
result = config.map_extra_body_params(
{"extra_body": {"chat_template_kwargs": {"enable_thinking": True, "thinking": False}}},
_REASONING_MODEL,
)
assert result == {}
def test_map_extra_body_params_chat_template_kwargs_reasoning_budget():
config = FireworksAIConfig()
result = config.map_extra_body_params(
{"extra_body": {"chat_template_kwargs": {"reasoning_budget": 512}}},
_REASONING_MODEL,
)
assert result == {"reasoning_effort": 512}
def test_map_extra_body_params_chat_template_kwargs_budget_ignored_when_thinking_off():
config = FireworksAIConfig()
result = config.map_extra_body_params(
{"extra_body": {"chat_template_kwargs": {"enable_thinking": False, "reasoning_budget": 512}}},
_REASONING_MODEL,
)
assert result == {"reasoning_effort": "none"}
def test_map_extra_body_params_chat_template_kwargs_low_effort():
config = FireworksAIConfig()
result = config.map_extra_body_params(
{"extra_body": {"chat_template_kwargs": {"low_effort": True}}},
_REASONING_MODEL,
)
assert result == {"reasoning_effort": "low"}
budget_wins = config.map_extra_body_params(
{"extra_body": {"chat_template_kwargs": {"low_effort": True, "reasoning_budget": 256}}},
_REASONING_MODEL,
)
assert budget_wins == {"reasoning_effort": 256}
def test_map_extra_body_params_chat_template_kwargs_effort_keys_dropped_for_non_reasoning_model():
config = FireworksAIConfig()
result = config.map_extra_body_params(
{"extra_body": {"chat_template_kwargs": {"reasoning_budget": 512, "low_effort": True}}},
_NON_REASONING_MODEL,
)
assert result == {}
def test_map_extra_body_params_chat_template_kwargs_native_reasoning_effort_wins():
config = FireworksAIConfig()
result = config.map_extra_body_params(
@ -1388,7 +1448,10 @@ def test_map_extra_body_params_guided_json():
{"extra_body": {"guided_json": schema}}, _REASONING_MODEL
)
assert result == {
"response_format": {"type": "json_schema", "json_schema": {"schema": schema}}
"response_format": {
"type": "json_schema",
"json_schema": {"name": "response", "schema": schema},
}
}
@ -1407,7 +1470,10 @@ def test_map_extra_body_params_guided_grammar_and_choice():
assert choice == {
"response_format": {
"type": "json_schema",
"json_schema": {"schema": {"type": "string", "enum": ["yes", "no"]}},
"json_schema": {
"name": "choice",
"schema": {"type": "string", "enum": ["yes", "no"]},
},
}
}
@ -1440,7 +1506,7 @@ def test_map_extra_body_params_multiple_guided_params_priority_order():
assert result == {
"response_format": {
"type": "json_schema",
"json_schema": {"schema": {"type": "object"}},
"json_schema": {"name": "response", "schema": {"type": "object"}},
}
}