fix: harden Responses API tool call conversion

This commit is contained in:
Rip&Tear
2026-07-13 08:28:38 +08:00
parent 6450d67b9c
commit f6640b1cd8
3 changed files with 39 additions and 3 deletions

View File

@@ -683,14 +683,17 @@ class OpenAICompletion(BaseLLM):
if role == "assistant" and message.get("tool_calls"):
items: list[dict[str, Any] | LLMMessage] = []
if message.get("content"):
items.append({"role": "assistant", "content": message["content"]})
for tool_call in message["tool_calls"]:
function = tool_call.get("function", {})
args = function.get("arguments", "")
items.append(
{
"type": "function_call",
"call_id": tool_call.get("id", ""),
"call_id": tool_call.get("id") or f"call_{id(tool_call)}",
"name": function.get("name", ""),
"arguments": function.get("arguments", ""),
"arguments": args if isinstance(args, str) else json.dumps(args),
}
)
return items

View File

@@ -1277,7 +1277,9 @@ def is_tool_call_list(response: list[Any]) -> bool:
# Bedrock-style
if isinstance(first_item, dict) and "name" in first_item and "input" in first_item:
return True
# OpenAI Responses API-style (flat dict, no nested "function" key)
# OpenAI Responses API-style (flat dict, no nested "function" key). This
# intentionally accepts the same broad shape as the Bedrock check above;
# only provider paths that return lists reach this classifier.
if (
isinstance(first_item, dict)
and "name" in first_item

View File

@@ -1006,6 +1006,37 @@ def test_openai_responses_api_converts_assistant_tool_calls_message():
}
def test_openai_responses_api_preserves_assistant_content_with_tool_calls():
"""Assistant text must be retained when it accompanies tool calls."""
llm = OpenAICompletion(model="gpt-4o-mini", api="responses")
messages = [
{
"role": "assistant",
"content": "I'll fetch that page now.",
"tool_calls": [
{
"type": "function",
"function": {
"name": "fetch_page",
"arguments": {"url": "https://example.com"},
},
}
],
}
]
params = llm._prepare_responses_params(messages)
assert params["input"][0] == {
"role": "assistant",
"content": "I'll fetch that page now.",
}
assert params["input"][1]["type"] == "function_call"
assert params["input"][1]["call_id"].startswith("call_")
assert params["input"][1]["arguments"] == '{"url": "https://example.com"}'
def test_openai_responses_api_converts_tool_result_message():
"""Regression: tool-role messages (Chat-Completions shape) must become
function_call_output input items for the Responses API.