This commit is contained in:
devin-ai-integration[bot] 2026-08-27 17:59:49 -05:00 committed by GitHub
commit 2dba2cd283
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 64 additions and 2 deletions

View file

@ -725,10 +725,13 @@ class FireworksAIConfig(FireworksAIMixin, OpenAIGPTConfig):
## FIREWORKS AI sends tool calls in the content field instead of tool_calls
for choice in response.choices:
cast(Choices, choice).message = self._handle_message_content_with_tool_calls(
message=cast(Choices, choice).message,
typed_choice = cast(Choices, choice)
typed_choice.message = self._handle_message_content_with_tool_calls(
message=typed_choice.message,
tool_calls=optional_params.get("tools", None),
)
if typed_choice.finish_reason == "stop" and typed_choice.message.tool_calls:
typed_choice.finish_reason = "tool_calls"
response._hidden_params = {
"additional_headers": additional_headers,

View file

@ -1236,6 +1236,65 @@ def test_transform_response_captures_token_ids():
assert result._hidden_params["fireworks_token_ids"] == [[4, 5, 6]]
def test_transform_response_sets_tool_calls_finish_reason_for_content_embedded_call():
"""
Fireworks returns content-embedded tool calls with finish_reason="stop".
After litellm lifts the JSON call into message.tool_calls, finish_reason
must be upgraded to "tool_calls" so OpenAI-style agent loops execute the
tool instead of treating the turn as finished (issue #33036).
"""
config = FireworksAIConfig()
body = {
"id": "resp-tool",
"object": "chat.completion",
"created": 1,
"model": "accounts/fireworks/models/llama",
"choices": [
{
"index": 0,
"finish_reason": "stop",
"message": {
"role": "assistant",
"content": json.dumps(
{"name": "get_weather", "arguments": {"city": "SF"}}
),
},
}
],
"usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2},
}
result = config.transform_response(
model="llama",
raw_response=_make_fireworks_raw_response(body),
model_response=ModelResponse(),
logging_obj=MagicMock(),
request_data={},
messages=[],
optional_params={
"tools": [
{
"type": "function",
"function": {"name": "get_weather", "parameters": {}},
}
]
},
litellm_params={},
encoding=None,
)
choice = result.choices[0]
assert choice.message.tool_calls is not None
assert choice.message.tool_calls[0].function.name == "get_weather"
assert choice.finish_reason == "tool_calls"
def test_transform_response_keeps_stop_finish_reason_for_plain_text():
"""A plain-text Fireworks response must keep finish_reason="stop"."""
result = _run_transform_response(dict(_BASE_CHAT_COMPLETION_RESPONSE))
choice = result.choices[0]
assert choice.message.tool_calls is None
assert choice.finish_reason == "stop"
def test_streaming_surfaces_fireworks_response_fields():
"""
The Fireworks-specific response fields captured into _hidden_params for