Skip to content

Commit eacbcdd

Browse files
authored
Fix the issue of response_format for responses api (#8911)
* Use proper format argument for responses api * comment * comment
1 parent 8bb47c5 commit eacbcdd

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

dspy/clients/lm.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,13 @@ def _convert_chat_request_to_responses_request(request: dict[str, Any]):
470470
elif isinstance(c, list):
471471
content_blocks.extend(c)
472472
request["input"] = [{"role": msg.get("role", "user"), "content": content_blocks}]
473+
474+
# Convert `response_format` to `text.format` for Responses API
475+
if "response_format" in request:
476+
response_format = request.pop("response_format")
477+
text = request.pop("text", {})
478+
request["text"] = {**text, "format": response_format}
479+
473480
return request
474481

475482
def _get_headers(headers: dict[str, Any] | None = None):

tests/adapters/test_json_adapter.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import pydantic
44
import pytest
5+
from litellm.types.llms.openai import ResponseAPIUsage, ResponsesAPIResponse
56
from litellm.utils import ChatCompletionMessageToolCall, Choices, Function, Message, ModelResponse
7+
from openai.types.responses import ResponseOutputMessage
68

79
import dspy
810

@@ -866,3 +868,62 @@ def get_weather(city: str) -> str:
866868
mock_completion.assert_called_once()
867869
_, call_kwargs = mock_completion.call_args
868870
assert call_kwargs["response_format"] == {"type": "json_object"}
871+
872+
873+
def test_json_adapter_with_responses_api():
874+
class TestSignature(dspy.Signature):
875+
question: str = dspy.InputField()
876+
answer: str = dspy.OutputField()
877+
878+
api_response = ResponsesAPIResponse(
879+
id="resp_1",
880+
created_at=0.0,
881+
error=None,
882+
incomplete_details=None,
883+
instructions=None,
884+
model="openai/gpt-4o",
885+
object="response",
886+
output=[
887+
ResponseOutputMessage(
888+
**{
889+
"id": "msg_1",
890+
"type": "message",
891+
"role": "assistant",
892+
"status": "completed",
893+
"content": [
894+
{"type": "output_text", "text": '{"answer": "Washington, D.C."}', "annotations": []}
895+
],
896+
},
897+
),
898+
],
899+
metadata={},
900+
parallel_tool_calls=False,
901+
temperature=1.0,
902+
tool_choice="auto",
903+
tools=[],
904+
top_p=1.0,
905+
max_output_tokens=None,
906+
previous_response_id=None,
907+
reasoning=None,
908+
status="completed",
909+
text=None,
910+
truncation="disabled",
911+
usage=ResponseAPIUsage(input_tokens=10, output_tokens=5, total_tokens=15),
912+
user=None,
913+
)
914+
915+
lm = dspy.LM(model="openai/gpt-4o", model_type="responses", cache=False)
916+
dspy.configure(lm=lm, adapter=dspy.JSONAdapter())
917+
918+
program = dspy.Predict(TestSignature)
919+
with mock.patch("litellm.responses", autospec=True, return_value=api_response) as mock_responses:
920+
result = program(question="What is the capital of the USA?")
921+
922+
assert result.answer == "Washington, D.C."
923+
mock_responses.assert_called_once()
924+
# Verify that response_format was converted to text.format
925+
call_kwargs = mock_responses.call_args.kwargs
926+
assert "response_format" not in call_kwargs
927+
assert "text" in call_kwargs
928+
assert isinstance(call_kwargs["text"]["format"], type)
929+
assert issubclass(call_kwargs["text"]["format"], pydantic.BaseModel)

0 commit comments

Comments
 (0)