Skip to content

Commit d33043f

Browse files
committed
Enhance response API support to not fail with tool calling
1 parent 2c0ca82 commit d33043f

File tree

6 files changed

+24
-32
lines changed

6 files changed

+24
-32
lines changed

docs/static/llama-stack-spec.html

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6170,25 +6170,13 @@
61706170
"OpenAIResponseInput": {
61716171
"oneOf": [
61726172
{
6173-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall"
6174-
},
6175-
{
6176-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall"
6177-
},
6178-
{
6179-
"$ref": "#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall"
6173+
"$ref": "#/components/schemas/OpenAIResponseOutput"
61806174
},
61816175
{
61826176
"$ref": "#/components/schemas/OpenAIResponseInputFunctionToolCallOutput"
61836177
},
6184-
{
6185-
"$ref": "#/components/schemas/OpenAIResponseMCPApprovalRequest"
6186-
},
61876178
{
61886179
"$ref": "#/components/schemas/OpenAIResponseMCPApprovalResponse"
6189-
},
6190-
{
6191-
"$ref": "#/components/schemas/OpenAIResponseMessage"
61926180
}
61936181
]
61946182
},

docs/static/llama-stack-spec.yaml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4588,13 +4588,9 @@ components:
45884588
Error details for failed OpenAI response requests.
45894589
OpenAIResponseInput:
45904590
oneOf:
4591-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall'
4592-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall'
4593-
- $ref: '#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall'
4591+
- $ref: '#/components/schemas/OpenAIResponseOutput'
45944592
- $ref: '#/components/schemas/OpenAIResponseInputFunctionToolCallOutput'
4595-
- $ref: '#/components/schemas/OpenAIResponseMCPApprovalRequest'
45964593
- $ref: '#/components/schemas/OpenAIResponseMCPApprovalResponse'
4597-
- $ref: '#/components/schemas/OpenAIResponseMessage'
45984594
"OpenAIResponseInputFunctionToolCallOutput":
45994595
type: object
46004596
properties:

llama_stack/apis/agents/openai_responses.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -746,15 +746,7 @@ class OpenAIResponseInputFunctionToolCallOutput(BaseModel):
746746

747747
OpenAIResponseInput = Annotated[
748748
# Responses API allows output messages to be passed in as input
749-
OpenAIResponseOutputMessageWebSearchToolCall
750-
| OpenAIResponseOutputMessageFileSearchToolCall
751-
| OpenAIResponseOutputMessageFunctionToolCall
752-
| OpenAIResponseInputFunctionToolCallOutput
753-
| OpenAIResponseMCPApprovalRequest
754-
| OpenAIResponseMCPApprovalResponse
755-
|
756-
# Fallback to the generic message type as a last resort
757-
OpenAIResponseMessage,
749+
OpenAIResponseOutput | OpenAIResponseInputFunctionToolCallOutput | OpenAIResponseMCPApprovalResponse,
758750
Field(union_mode="left_to_right"),
759751
]
760752
register_schema(OpenAIResponseInput, name="OpenAIResponseInput")

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,10 @@ async def create_response(self) -> AsyncIterator[OpenAIResponseObjectStream]:
125125
while True:
126126
# Text is the default response format for chat completion so don't need to pass it
127127
# (some providers don't support non-empty response_format when tools are present)
128-
response_format = None if self.ctx.response_format.type == "text" else self.ctx.response_format
128+
129+
response_format = (
130+
None if getattr(self.ctx.response_format, "type", None) == "text" else self.ctx.response_format
131+
)
129132
completion_result = await self.inference_api.openai_chat_completion(
130133
model=self.ctx.model,
131134
messages=messages,

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,8 @@ async def _build_result_messages(
371371
content.append(part)
372372
else:
373373
raise ValueError(f"Unknown result content type: {type(result.content)}")
374-
input_message = OpenAIToolMessageParam(content=content, tool_call_id=tool_call_id)
374+
# OpenAI tool messages must have simple string content, not complex content structures
375+
input_message = OpenAIToolMessageParam(content=str(content), tool_call_id=tool_call_id)
375376
else:
376377
text = str(error_exc) if error_exc else "Tool execution failed"
377378
input_message = OpenAIToolMessageParam(content=text, tool_call_id=tool_call_id)

llama_stack/providers/utils/inference/openai_compat.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -861,10 +861,11 @@ def _convert_openai_request_response_format(
861861

862862

863863
def _convert_openai_tool_calls(
864-
tool_calls: list[OpenAIChatCompletionMessageFunctionToolCall],
864+
tool_calls: list[OpenAIChatCompletionMessageFunctionToolCall] | list[ToolCall],
865865
) -> list[ToolCall]:
866866
"""
867867
Convert an OpenAI ChatCompletionMessageToolCall list into a list of ToolCall.
868+
If already ToolCall objects, return them as-is.
868869
869870
OpenAI ChatCompletionMessageToolCall:
870871
id: str
@@ -885,12 +886,15 @@ def _convert_openai_tool_calls(
885886
if not tool_calls:
886887
return [] # CompletionMessage tool_calls is not optional
887888

889+
# Convert from OpenAI format to ToolCall format if not already ToolCall objects
888890
return [
889891
ToolCall(
890892
call_id=call.id,
891893
tool_name=call.function.name,
892894
arguments=call.function.arguments,
893895
)
896+
if not isinstance(call, ToolCall)
897+
else call
894898
for call in tool_calls
895899
]
896900

@@ -955,13 +959,19 @@ def _convert_openai_sampling_params(
955959

956960

957961
def openai_messages_to_messages(
958-
messages: list[OpenAIMessageParam],
962+
messages: list[OpenAIMessageParam] | list[Message],
959963
) -> list[Message]:
960964
"""
961965
Convert a list of OpenAIChatCompletionMessage into a list of Message.
966+
If already Message objects, return them as-is.
962967
"""
963968
converted_messages = []
964969
for message in messages:
970+
# Check if this individual message is already a Llama Stack Message
971+
if isinstance(message, (UserMessage | SystemMessage | ToolResponseMessage | CompletionMessage)):
972+
# Already a Llama Stack Message, use as-is
973+
converted_messages.append(message)
974+
continue
965975
if message.role == "system":
966976
converted_message = SystemMessage(content=openai_content_to_content(message.content))
967977
elif message.role == "user":
@@ -973,9 +983,11 @@ def openai_messages_to_messages(
973983
stop_reason=StopReason.end_of_turn,
974984
)
975985
elif message.role == "tool":
986+
# Handle both OpenAI format (tool_call_id) and Llama Stack format (call_id)
987+
tool_call_id = getattr(message, "tool_call_id", None) or getattr(message, "call_id", None)
976988
converted_message = ToolResponseMessage(
977989
role="tool",
978-
call_id=message.tool_call_id,
990+
call_id=tool_call_id,
979991
content=openai_content_to_content(message.content),
980992
)
981993
else:

0 commit comments

Comments
 (0)