Skip to content

Commit 63422e5

Browse files
authored
fix!: Enhance response API support to not fail with tool calling (#3385)
# What does this PR do? Introduces two main fixes to enhance the stability of Responses API when dealing with tool calling responses and structured outputs. ### Changes Made 1. It added OpenAIResponseOutputMessageMCPCall and ListTools to OpenAIResponseInput but #3810 got merge that did the same in a different way. Still this PR does it in a way that keep the sync between OpenAIResponsesOutput and the allowed objects in OpenAIResponseInput. 2. Add protection in case self.ctx.response_format does not have type attribute BREAKING CHANGE: OpenAIResponseInput now uses OpenAIResponseOutput union type. This is semantically equivalent - all previously accepted types are still supported via the OpenAIResponseOutput union. This improves type consistency and maintainability.
1 parent f18b5eb commit 63422e5

File tree

10 files changed

+84
-79
lines changed

10 files changed

+84
-79
lines changed

client-sdks/stainless/openapi.yml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6735,14 +6735,9 @@ components:
67356735
Error details for failed OpenAI response requests.
67366736
OpenAIResponseInput:
67376737
oneOf:
6738-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall'
6739-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall'
6740-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall'
6738+
- $ref: '#/components/schemas/OpenAIResponseOutput'
67416739
- $ref: '#/components/schemas/OpenAIResponseInputFunctionToolCallOutput'
6742-
- $ref: '#/components/schemas/OpenAIResponseMCPApprovalRequest'
67436740
- $ref: '#/components/schemas/OpenAIResponseMCPApprovalResponse'
6744-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageMCPCall'
6745-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageMCPListTools'
67466741
- $ref: '#/components/schemas/OpenAIResponseMessage'
67476742
OpenAIResponseInputToolFileSearch:
67486743
type: object

docs/static/deprecated-llama-stack-spec.html

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8526,29 +8526,14 @@
85268526
"OpenAIResponseInput": {
85278527
"oneOf": [
85288528
{
8529-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall"
8530-
},
8531-
{
8532-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall"
8533-
},
8534-
{
8535-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall"
8529+
"$ref": "#/components/schemas/OpenAIResponseOutput"
85368530
},
85378531
{
85388532
"$ref": "#/components/schemas/OpenAIResponseInputFunctionToolCallOutput"
85398533
},
8540-
{
8541-
"$ref": "#/components/schemas/OpenAIResponseMCPApprovalRequest"
8542-
},
85438534
{
85448535
"$ref": "#/components/schemas/OpenAIResponseMCPApprovalResponse"
85458536
},
8546-
{
8547-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageMCPCall"
8548-
},
8549-
{
8550-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageMCPListTools"
8551-
},
85528537
{
85538538
"$ref": "#/components/schemas/OpenAIResponseMessage"
85548539
}

docs/static/deprecated-llama-stack-spec.yaml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6369,14 +6369,9 @@ components:
63696369
Error details for failed OpenAI response requests.
63706370
OpenAIResponseInput:
63716371
oneOf:
6372-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall'
6373-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall'
6374-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall'
6372+
- $ref: '#/components/schemas/OpenAIResponseOutput'
63756373
- $ref: '#/components/schemas/OpenAIResponseInputFunctionToolCallOutput'
6376-
- $ref: '#/components/schemas/OpenAIResponseMCPApprovalRequest'
63776374
- $ref: '#/components/schemas/OpenAIResponseMCPApprovalResponse'
6378-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageMCPCall'
6379-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageMCPListTools'
63806375
- $ref: '#/components/schemas/OpenAIResponseMessage'
63816376
"OpenAIResponseInputFunctionToolCallOutput":
63826377
type: object

docs/static/llama-stack-spec.html

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7305,29 +7305,14 @@
73057305
"OpenAIResponseInput": {
73067306
"oneOf": [
73077307
{
7308-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall"
7309-
},
7310-
{
7311-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall"
7312-
},
7313-
{
7314-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall"
7308+
"$ref": "#/components/schemas/OpenAIResponseOutput"
73157309
},
73167310
{
73177311
"$ref": "#/components/schemas/OpenAIResponseInputFunctionToolCallOutput"
73187312
},
7319-
{
7320-
"$ref": "#/components/schemas/OpenAIResponseMCPApprovalRequest"
7321-
},
73227313
{
73237314
"$ref": "#/components/schemas/OpenAIResponseMCPApprovalResponse"
73247315
},
7325-
{
7326-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageMCPCall"
7327-
},
7328-
{
7329-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageMCPListTools"
7330-
},
73317316
{
73327317
"$ref": "#/components/schemas/OpenAIResponseMessage"
73337318
}

docs/static/llama-stack-spec.yaml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5522,14 +5522,9 @@ components:
55225522
Error details for failed OpenAI response requests.
55235523
OpenAIResponseInput:
55245524
oneOf:
5525-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall'
5526-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall'
5527-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall'
5525+
- $ref: '#/components/schemas/OpenAIResponseOutput'
55285526
- $ref: '#/components/schemas/OpenAIResponseInputFunctionToolCallOutput'
5529-
- $ref: '#/components/schemas/OpenAIResponseMCPApprovalRequest'
55305527
- $ref: '#/components/schemas/OpenAIResponseMCPApprovalResponse'
5531-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageMCPCall'
5532-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageMCPListTools'
55335528
- $ref: '#/components/schemas/OpenAIResponseMessage'
55345529
OpenAIResponseInputToolFileSearch:
55355530
type: object

docs/static/stainless-llama-stack-spec.html

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8977,29 +8977,14 @@
89778977
"OpenAIResponseInput": {
89788978
"oneOf": [
89798979
{
8980-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall"
8981-
},
8982-
{
8983-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall"
8984-
},
8985-
{
8986-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall"
8980+
"$ref": "#/components/schemas/OpenAIResponseOutput"
89878981
},
89888982
{
89898983
"$ref": "#/components/schemas/OpenAIResponseInputFunctionToolCallOutput"
89908984
},
8991-
{
8992-
"$ref": "#/components/schemas/OpenAIResponseMCPApprovalRequest"
8993-
},
89948985
{
89958986
"$ref": "#/components/schemas/OpenAIResponseMCPApprovalResponse"
89968987
},
8997-
{
8998-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageMCPCall"
8999-
},
9000-
{
9001-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageMCPListTools"
9002-
},
90038988
{
90048989
"$ref": "#/components/schemas/OpenAIResponseMessage"
90058990
}

docs/static/stainless-llama-stack-spec.yaml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6735,14 +6735,9 @@ components:
67356735
Error details for failed OpenAI response requests.
67366736
OpenAIResponseInput:
67376737
oneOf:
6738-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall'
6739-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall'
6740-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall'
6738+
- $ref: '#/components/schemas/OpenAIResponseOutput'
67416739
- $ref: '#/components/schemas/OpenAIResponseInputFunctionToolCallOutput'
6742-
- $ref: '#/components/schemas/OpenAIResponseMCPApprovalRequest'
67436740
- $ref: '#/components/schemas/OpenAIResponseMCPApprovalResponse'
6744-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageMCPCall'
6745-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageMCPListTools'
67466741
- $ref: '#/components/schemas/OpenAIResponseMessage'
67476742
OpenAIResponseInputToolFileSearch:
67486743
type: object

llama_stack/apis/agents/openai_responses.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,14 +1254,9 @@ class OpenAIResponseInputFunctionToolCallOutput(BaseModel):
12541254

12551255
OpenAIResponseInput = Annotated[
12561256
# Responses API allows output messages to be passed in as input
1257-
OpenAIResponseOutputMessageWebSearchToolCall
1258-
| OpenAIResponseOutputMessageFileSearchToolCall
1259-
| OpenAIResponseOutputMessageFunctionToolCall
1257+
OpenAIResponseOutput
12601258
| OpenAIResponseInputFunctionToolCallOutput
1261-
| OpenAIResponseMCPApprovalRequest
12621259
| OpenAIResponseMCPApprovalResponse
1263-
| OpenAIResponseOutputMessageMCPCall
1264-
| OpenAIResponseOutputMessageMCPListTools
12651260
| OpenAIResponseMessage,
12661261
Field(union_mode="left_to_right"),
12671262
]

llama_stack/providers/inline/agents/meta_reference/responses/streaming.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,9 @@ async def create_response(self) -> AsyncIterator[OpenAIResponseObjectStream]:
217217
while True:
218218
# Text is the default response format for chat completion so don't need to pass it
219219
# (some providers don't support non-empty response_format when tools are present)
220-
response_format = None if self.ctx.response_format.type == "text" else self.ctx.response_format
220+
response_format = (
221+
None if getattr(self.ctx.response_format, "type", None) == "text" else self.ctx.response_format
222+
)
221223
logger.debug(f"calling openai_chat_completion with tools: {self.ctx.chat_tools}")
222224

223225
params = OpenAIChatCompletionRequestWithExtraBody(

tests/unit/providers/agents/meta_reference/test_openai_responses.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
OpenAIResponseInputToolWebSearch,
2525
OpenAIResponseMessage,
2626
OpenAIResponseOutputMessageContentOutputText,
27+
OpenAIResponseOutputMessageFunctionToolCall,
2728
OpenAIResponseOutputMessageMCPCall,
2829
OpenAIResponseOutputMessageWebSearchToolCall,
2930
OpenAIResponseText,
@@ -1169,3 +1170,75 @@ async def test_create_openai_response_with_invalid_text_format(openai_responses_
11691170
model=model,
11701171
text=OpenAIResponseText(format={"type": "invalid"}),
11711172
)
1173+
1174+
1175+
async def test_create_openai_response_with_output_types_as_input(
1176+
openai_responses_impl, mock_inference_api, mock_responses_store
1177+
):
1178+
"""Test that response outputs can be used as inputs in multi-turn conversations.
1179+
1180+
Before adding OpenAIResponseOutput types to OpenAIResponseInput,
1181+
creating a _OpenAIResponseObjectWithInputAndMessages with some output types
1182+
in the input field would fail with a Pydantic ValidationError.
1183+
1184+
This test simulates storing a response where the input contains output message
1185+
types (MCP calls, function calls), which happens in multi-turn conversations.
1186+
"""
1187+
model = "meta-llama/Llama-3.1-8B-Instruct"
1188+
1189+
# Mock the inference response
1190+
mock_inference_api.openai_chat_completion.return_value = fake_stream()
1191+
1192+
# Create a response with store=True to trigger the storage path
1193+
result = await openai_responses_impl.create_openai_response(
1194+
input="What's the weather?",
1195+
model=model,
1196+
stream=True,
1197+
temperature=0.1,
1198+
store=True,
1199+
)
1200+
1201+
# Consume the stream
1202+
_ = [chunk async for chunk in result]
1203+
1204+
# Verify store was called
1205+
assert mock_responses_store.store_response_object.called
1206+
1207+
# Get the stored data
1208+
store_call_args = mock_responses_store.store_response_object.call_args
1209+
stored_response = store_call_args.kwargs["response_object"]
1210+
1211+
# Now simulate a multi-turn conversation where outputs become inputs
1212+
input_with_output_types = [
1213+
OpenAIResponseMessage(role="user", content="What's the weather?", name=None),
1214+
# These output types need to be valid OpenAIResponseInput
1215+
OpenAIResponseOutputMessageFunctionToolCall(
1216+
call_id="call_123",
1217+
name="get_weather",
1218+
arguments='{"city": "Tokyo"}',
1219+
type="function_call",
1220+
),
1221+
OpenAIResponseOutputMessageMCPCall(
1222+
id="mcp_456",
1223+
type="mcp_call",
1224+
server_label="weather_server",
1225+
name="get_temperature",
1226+
arguments='{"location": "Tokyo"}',
1227+
output="25°C",
1228+
),
1229+
]
1230+
1231+
# This simulates storing a response in a multi-turn conversation
1232+
# where previous outputs are included in the input.
1233+
stored_with_outputs = _OpenAIResponseObjectWithInputAndMessages(
1234+
id=stored_response.id,
1235+
created_at=stored_response.created_at,
1236+
model=stored_response.model,
1237+
status=stored_response.status,
1238+
output=stored_response.output,
1239+
input=input_with_output_types, # This will trigger Pydantic validation
1240+
messages=None,
1241+
)
1242+
1243+
assert stored_with_outputs.input == input_with_output_types
1244+
assert len(stored_with_outputs.input) == 3

0 commit comments

Comments
 (0)