fix(fireworks_ai): map developer items after pydantic input items are dumped

This commit is contained in:
mateo-berri 2026-09-04 22:16:14 -07:00
parent 525d9fb14c
commit e9a40ad4d2
2 changed files with 45 additions and 3 deletions

View file

@ -68,6 +68,9 @@ class FireworksAIResponsesAPIConfig(OpenAIResponsesAPIConfig):
base: Final = (api_base or get_secret_str("FIREWORKS_API_BASE") or FIREWORKS_AI_DEFAULT_API_BASE).rstrip("/")
return f"{base}/responses"
def _validate_input_param(self, input: str | ResponseInputParam) -> str | ResponseInputParam:
return _developer_items_as_system(super()._validate_input_param(input))
def transform_responses_api_request(
self,
model: str,
@ -78,7 +81,7 @@ class FireworksAIResponsesAPIConfig(OpenAIResponsesAPIConfig):
) -> dict: # mutable-ok: overrides the base class signature
return super().transform_responses_api_request(
model=resolve_fireworks_resource_name(model),
input=_developer_items_as_system(input),
input=input,
response_api_optional_request_params=response_api_optional_request_params,
litellm_params=litellm_params,
headers=headers,

View file

@ -1,13 +1,14 @@
import json
from collections.abc import Mapping
from types import MappingProxyType
from typing import Final, TypedDict
from typing import Final, TypedDict, cast
from unittest.mock import MagicMock, patch
from urllib.parse import quote
import httpx
import pytest
from openai.types.responses import (
EasyInputMessage,
ResponseFunctionToolCall,
ResponseOutputMessage,
ResponseOutputText,
@ -20,7 +21,7 @@ from typing_extensions import ReadOnly
import litellm
from litellm.llms.fireworks_ai.responses.transformation import FireworksAIResponsesAPIConfig
from litellm.responses.file_search.emulated_handler import should_use_emulated_file_search
from litellm.types.llms.openai import InputTokensDetails, ResponseAPIUsage, ResponsesAPIResponse
from litellm.types.llms.openai import InputTokensDetails, ResponseAPIUsage, ResponseInputParam, ResponsesAPIResponse
from litellm.types.router import GenericLiteLLMParams
from litellm.types.utils import LlmProviders
from litellm.utils import ProviderConfigManager
@ -181,6 +182,44 @@ def test_responses_call_sends_developer_items_as_system_messages() -> None:
)
def test_responses_call_maps_pydantic_developer_items_and_replays_pydantic_output_items() -> None:
client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/kimi-k3"))
pydantic_input: Final = cast(
ResponseInputParam,
[ # mutable-ok: the Responses API takes input as a JSON list
EasyInputMessage(role="developer", content="Answer with exactly one word.", type="message"),
ResponseReasoningItem(id="rs_1", summary=(), type="reasoning"),
ResponseFunctionToolCall(
id="fc_1",
call_id="call_abc123",
name="get_weather",
arguments='{"city": "Paris"}',
status="completed",
type="function_call",
),
FunctionCallOutput(type="function_call_output", call_id="call_abc123", output="21C"),
],
)
with patch(HTTPX_CLIENT_FACTORY, return_value=client):
litellm.responses(
model="fireworks_ai/accounts/fireworks/models/kimi-k3", input=pydantic_input, api_key="fw-test-key"
)
_, _, body = _sent_request(client)
assert tuple(body["input"]) == (
{"role": "system", "content": "Answer with exactly one word.", "type": "message"},
{"id": "rs_1", "summary": [], "type": "reasoning"},
{
"id": "fc_1",
"call_id": "call_abc123",
"name": "get_weather",
"arguments": '{"city": "Paris"}',
"status": "completed",
"type": "function_call",
},
{"type": "function_call_output", "call_id": "call_abc123", "output": "21C"},
)
def test_file_search_tools_take_litellm_emulated_search_not_fireworks() -> None:
config: Final = FireworksAIResponsesAPIConfig()
file_search: Final = ({"type": "file_search", "vector_store_ids": ("vs_kb",)},)