mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Add search tool content for OpenAI responses (#5302)
* Add OpenAI search tool content parsing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix typing * simplified oai image test * same for azure * skip az responses api test --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
60af59ba8b
commit
3e54a689fc
@@ -549,6 +549,7 @@ class RawOpenAIChatClient( # type: ignore[misc]
|
||||
chunk,
|
||||
options=validated_options,
|
||||
function_call_ids=function_call_ids,
|
||||
seen_reasoning_delta_item_ids=seen_reasoning_delta_item_ids,
|
||||
)
|
||||
else:
|
||||
async for chunk in await client.responses.create(stream=True, **run_options):
|
||||
@@ -556,6 +557,7 @@ class RawOpenAIChatClient( # type: ignore[misc]
|
||||
chunk,
|
||||
options=validated_options,
|
||||
function_call_ids=function_call_ids,
|
||||
seen_reasoning_delta_item_ids=seen_reasoning_delta_item_ids,
|
||||
)
|
||||
except Exception as ex:
|
||||
self._handle_request_error(ex)
|
||||
@@ -1587,6 +1589,54 @@ class RawOpenAIChatClient( # type: ignore[misc]
|
||||
"""Join shell commands into a single executable command string."""
|
||||
return "\n".join(command for command in commands if command).strip()
|
||||
|
||||
@staticmethod
|
||||
def _serialize_provider_payload(value: Any) -> Any:
|
||||
"""Convert OpenAI SDK objects into JSON-serializable Python values."""
|
||||
if isinstance(value, BaseModel):
|
||||
return value.model_dump(mode="json", exclude_none=True)
|
||||
if isinstance(value, Mapping):
|
||||
return {str(key): RawOpenAIChatClient._serialize_provider_payload(item) for key, item in value.items()} # type: ignore[reportUnknownVariableType]
|
||||
if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
|
||||
return [RawOpenAIChatClient._serialize_provider_payload(item) for item in value] # type: ignore[reportUnknownVariableType]
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def _get_search_tool_name(item_type: str) -> str:
|
||||
"""Map OpenAI search output item types to unified content tool names."""
|
||||
return "web_search" if item_type == "web_search_call" else "file_search"
|
||||
|
||||
def _parse_search_tool_call_content(self, item: Any) -> Content:
|
||||
"""Create unified search tool call content from an OpenAI search output item."""
|
||||
item_type = getattr(item, "type", "")
|
||||
call_id = getattr(item, "id", None) or getattr(item, "call_id", None) or ""
|
||||
if item_type == "web_search_call":
|
||||
arguments = self._serialize_provider_payload(getattr(item, "action", None))
|
||||
else:
|
||||
arguments = {"queries": list(getattr(item, "queries", []) or [])}
|
||||
return Content.from_search_tool_call(
|
||||
call_id=call_id,
|
||||
tool_name=self._get_search_tool_name(item_type),
|
||||
arguments=arguments,
|
||||
status=getattr(item, "status", None),
|
||||
raw_representation=item,
|
||||
)
|
||||
|
||||
def _parse_search_tool_result_content(self, item: Any) -> Content:
|
||||
"""Create unified search tool result content from an OpenAI search output item."""
|
||||
item_type = getattr(item, "type", "")
|
||||
call_id = getattr(item, "id", None) or getattr(item, "call_id", None) or ""
|
||||
if item_type == "web_search_call":
|
||||
result = {"action": self._serialize_provider_payload(getattr(item, "action", None))}
|
||||
else:
|
||||
result = {"results": self._serialize_provider_payload(getattr(item, "results", None))}
|
||||
return Content.from_search_tool_result(
|
||||
call_id=call_id,
|
||||
tool_name=self._get_search_tool_name(item_type),
|
||||
result=result,
|
||||
status=getattr(item, "status", None),
|
||||
raw_representation=item,
|
||||
)
|
||||
|
||||
# region Parse methods
|
||||
def _parse_response_from_openai(
|
||||
self,
|
||||
@@ -1788,6 +1838,9 @@ class RawOpenAIChatClient( # type: ignore[misc]
|
||||
raw_representation=item,
|
||||
)
|
||||
)
|
||||
case "web_search_call" | "file_search_call":
|
||||
contents.append(self._parse_search_tool_call_content(item))
|
||||
contents.append(self._parse_search_tool_result_content(item))
|
||||
case "mcp_approval_request": # ResponseOutputMcpApprovalRequest
|
||||
contents.append(
|
||||
Content.from_function_approval_request(
|
||||
@@ -2377,8 +2430,19 @@ class RawOpenAIChatClient( # type: ignore[misc]
|
||||
additional_properties=additional_properties_empty or None,
|
||||
)
|
||||
)
|
||||
case "web_search_call" | "file_search_call":
|
||||
contents.append(self._parse_search_tool_call_content(event_item))
|
||||
case _:
|
||||
logger.debug("Unparsed event of type: %s: %s", event.type, event)
|
||||
case (
|
||||
"response.web_search_call.in_progress"
|
||||
| "response.web_search_call.searching"
|
||||
| "response.web_search_call.completed"
|
||||
| "response.file_search_call.in_progress"
|
||||
| "response.file_search_call.searching"
|
||||
| "response.file_search_call.completed"
|
||||
):
|
||||
pass
|
||||
case "response.function_call_arguments.delta":
|
||||
call_id, name = function_call_ids.get(event.output_index, (None, None))
|
||||
if call_id and name:
|
||||
@@ -2514,6 +2578,8 @@ class RawOpenAIChatClient( # type: ignore[misc]
|
||||
raw_representation=done_item,
|
||||
)
|
||||
)
|
||||
elif getattr(done_item, "type", None) in ("web_search_call", "file_search_call"):
|
||||
contents.append(self._parse_search_tool_result_content(done_item))
|
||||
case _:
|
||||
logger.debug("Unparsed event of type: %s: %s", event.type, event)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user