fix: make the integration work against a Claude proxy
validate / hassfest (push) Failing after 32s
validate / hacs (push) Failing after 1m50s
test / pytest (push) Successful in 3m56s

- Treat an empty tool argument string as no parameters instead of raising
  JSONDecodeError; the proxy sends "" for tools like the timer status
  (upstream issue #3).
- Do not replay earlier web searches in the history. The chat log keeps only
  their status, and a search without results makes the model disown the
  answers it based on it and search again (upstream issue #4).
- Look up the home location for web search with the subentry's model instead
  of gpt-4o-mini, and save the options without city/region if the lookup
  fails (upstream issue #5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
pc-heiniandClaude Opus 5.5 committed 2026-10-22 08:38:01 +02:00
1 parent 8737e5d4ec
commit 235c536cab
6 files changed
+224 -70

No files matched your search

+14 -32
View File
@@ -22,7 +22,6 @@ from openai.types.responses import (
ResponseFunctionToolCall,
ResponseFunctionToolCallParam,
ResponseFunctionWebSearch,
ResponseFunctionWebSearchParam,
ResponseIncompleteEvent,
ResponseInputFileParam,
ResponseInputImageParam,
@@ -181,20 +180,13 @@ def _convert_content_to_param(
"""Convert any native chat message for this agent to the native format."""
messages: ResponseInputParam = []
reasoning_summary: list[str] = []
web_search_calls: dict[str, ResponseFunctionWebSearchParam] = {}
for content in chat_content:
if isinstance(content, conversation.ToolResultContent):
if (
content.tool_name == "web_search_call"
and content.tool_call_id in web_search_calls
):
web_search_call = web_search_calls.pop(content.tool_call_id)
web_search_call["status"] = content.tool_result.get( # type: ignore[typeddict-item]
"status", "completed"
)
messages.append(web_search_call)
else:
# Earlier web searches are not replayed: the chat log only keeps
# their status, not their results, and a search without results
# makes the model disown the answer it based on that search.
if content.tool_name != "web_search_call":
messages.append(
FunctionCallOutput(
type="function_call_output",
@@ -217,26 +209,16 @@ def _convert_content_to_param(
if isinstance(content, conversation.AssistantContent):
if content.tool_calls:
for tool_call in content.tool_calls:
if (
tool_call.external
and tool_call.tool_name == "web_search_call"
and "action" in tool_call.tool_args
):
web_search_calls[tool_call.id] = ResponseFunctionWebSearchParam(
type="web_search_call",
id=tool_call.id,
action=tool_call.tool_args["action"],
status="completed",
)
else:
messages.append(
ResponseFunctionToolCallParam(
type="function_call",
name=tool_call.tool_name,
arguments=json_dumps(tool_call.tool_args),
call_id=tool_call.id,
)
if tool_call.external and tool_call.tool_name == "web_search_call":
continue
messages.append(
ResponseFunctionToolCallParam(
type="function_call",
name=tool_call.tool_name,
arguments=json_dumps(tool_call.tool_args),
call_id=tool_call.id,
)
)
if content.thinking_content:
reasoning_summary.append(content.thinking_content)
@@ -418,7 +400,7 @@ async def _transform_stream( # noqa: C901 - This is complex, but better to have
llm.ToolInput(
id=current_tool_call.call_id,
tool_name=current_tool_call.name,
tool_args=json.loads(current_tool_call.arguments),
tool_args=json.loads(current_tool_call.arguments or "{}"),
)
]
}