mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: [Breaking] Simplified Content types to a single class with classmethod constructors. (#3252)
* ported Content to a new model * fixed linting * fixes * fixed data format handling * fix for 3.10 mypy * fix * fix int test
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import ast
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections.abc import AsyncIterable, Callable, Mapping, MutableMapping, MutableSequence, Sequence
|
||||
@@ -9,6 +10,8 @@ from typing import Any, ClassVar, Generic, TypedDict
|
||||
|
||||
from agent_framework import (
|
||||
AGENT_FRAMEWORK_USER_AGENT,
|
||||
AIFunction,
|
||||
Annotation,
|
||||
BaseChatClient,
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
@@ -16,23 +19,16 @@ from agent_framework import (
|
||||
ChatOptions,
|
||||
ChatResponse,
|
||||
ChatResponseUpdate,
|
||||
CitationAnnotation,
|
||||
Contents,
|
||||
Content,
|
||||
ContextProvider,
|
||||
DataContent,
|
||||
FunctionApprovalRequestContent,
|
||||
FunctionApprovalResponseContent,
|
||||
FunctionCallContent,
|
||||
FunctionResultContent,
|
||||
HostedFileContent,
|
||||
HostedCodeInterpreterTool,
|
||||
HostedFileSearchTool,
|
||||
HostedMCPTool,
|
||||
HostedWebSearchTool,
|
||||
Middleware,
|
||||
Role,
|
||||
TextContent,
|
||||
TextSpanRegion,
|
||||
ToolProtocol,
|
||||
UriContent,
|
||||
UsageContent,
|
||||
UsageDetails,
|
||||
get_logger,
|
||||
prepare_function_call_results,
|
||||
@@ -50,9 +46,14 @@ from azure.ai.agents.models import (
|
||||
AgentStreamEvent,
|
||||
AsyncAgentEventHandler,
|
||||
AsyncAgentRunStream,
|
||||
BingCustomSearchTool,
|
||||
BingGroundingTool,
|
||||
CodeInterpreterToolDefinition,
|
||||
FileSearchTool,
|
||||
FunctionName,
|
||||
FunctionToolDefinition,
|
||||
ListSortOrder,
|
||||
McpTool,
|
||||
MessageDeltaChunk,
|
||||
MessageDeltaTextContent,
|
||||
MessageDeltaTextFileCitationAnnotation,
|
||||
@@ -422,7 +423,7 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
self,
|
||||
agent_id: str,
|
||||
run_options: dict[str, Any],
|
||||
required_action_results: list[FunctionResultContent | FunctionApprovalResponseContent] | None,
|
||||
required_action_results: list[Content] | None,
|
||||
) -> tuple[AsyncAgentRunStream[AsyncAgentEventHandler[Any]] | AsyncAgentEventHandler[Any], str]:
|
||||
"""Create the agent stream for processing.
|
||||
|
||||
@@ -506,9 +507,9 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
|
||||
def _extract_url_citations(
|
||||
self, message_delta_chunk: MessageDeltaChunk, azure_search_tool_calls: list[dict[str, Any]]
|
||||
) -> list[CitationAnnotation]:
|
||||
) -> list[Annotation]:
|
||||
"""Extract URL citations from MessageDeltaChunk."""
|
||||
url_citations: list[CitationAnnotation] = []
|
||||
url_citations: list[Annotation] = []
|
||||
|
||||
# Process each content item in the delta to find citations
|
||||
for content in message_delta_chunk.delta.content:
|
||||
@@ -520,6 +521,7 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
if annotation.start_index and annotation.end_index:
|
||||
annotated_regions = [
|
||||
TextSpanRegion(
|
||||
type="text_span",
|
||||
start_index=annotation.start_index,
|
||||
end_index=annotation.end_index,
|
||||
)
|
||||
@@ -530,11 +532,12 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
annotation.url_citation.url, azure_search_tool_calls
|
||||
)
|
||||
|
||||
# Create CitationAnnotation with real URL
|
||||
citation = CitationAnnotation(
|
||||
title=getattr(annotation.url_citation, "title", None),
|
||||
# Create Annotation with real URL
|
||||
citation = Annotation(
|
||||
type="citation",
|
||||
title=annotation.url_citation.title, # type: ignore[typeddict-item]
|
||||
url=real_url,
|
||||
snippet=None,
|
||||
snippet=None, # type: ignore[typeddict-item]
|
||||
annotated_regions=annotated_regions,
|
||||
raw_representation=annotation,
|
||||
)
|
||||
@@ -542,7 +545,7 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
|
||||
return url_citations
|
||||
|
||||
def _extract_file_path_contents(self, message_delta_chunk: MessageDeltaChunk) -> list[HostedFileContent]:
|
||||
def _extract_file_path_contents(self, message_delta_chunk: MessageDeltaChunk) -> list[Content]:
|
||||
"""Extract file references from MessageDeltaChunk annotations.
|
||||
|
||||
Code interpreter generates files that are referenced via file path or file citation
|
||||
@@ -559,7 +562,7 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
Returns:
|
||||
List of HostedFileContent objects for any files referenced in annotations
|
||||
"""
|
||||
file_contents: list[HostedFileContent] = []
|
||||
file_contents: list[Content] = []
|
||||
|
||||
for content in message_delta_chunk.delta.content:
|
||||
if isinstance(content, MessageDeltaTextContent) and content.text and content.text.annotations:
|
||||
@@ -570,14 +573,14 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
if file_path is not None:
|
||||
file_id = getattr(file_path, "file_id", None)
|
||||
if file_id:
|
||||
file_contents.append(HostedFileContent(file_id=file_id))
|
||||
file_contents.append(Content.from_hosted_file(file_id=file_id))
|
||||
elif isinstance(annotation, MessageDeltaTextFileCitationAnnotation):
|
||||
# Extract file_id from the file_citation annotation
|
||||
file_citation = getattr(annotation, "file_citation", None)
|
||||
if file_citation is not None:
|
||||
file_id = getattr(file_citation, "file_id", None)
|
||||
if file_id:
|
||||
file_contents.append(HostedFileContent(file_id=file_id))
|
||||
file_contents.append(Content.from_hosted_file(file_id=file_id))
|
||||
|
||||
return file_contents
|
||||
|
||||
@@ -644,9 +647,9 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
file_contents = self._extract_file_path_contents(event_data)
|
||||
|
||||
# Create contents with citations if any exist
|
||||
citation_content: list[Contents] = []
|
||||
citation_content: list[Content] = []
|
||||
if event_data.text or url_citations:
|
||||
text_content_obj = TextContent(text=event_data.text or "")
|
||||
text_content_obj = Content.from_text(text=event_data.text or "")
|
||||
if url_citations:
|
||||
text_content_obj.annotations = url_citations
|
||||
citation_content.append(text_content_obj)
|
||||
@@ -722,7 +725,7 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
self._capture_azure_search_tool_calls(event_data, azure_search_tool_calls)
|
||||
|
||||
if event_data.usage:
|
||||
usage_content = UsageContent(
|
||||
usage_content = Content.from_usage(
|
||||
UsageDetails(
|
||||
input_token_count=event_data.usage.prompt_tokens,
|
||||
output_token_count=event_data.usage.completion_tokens,
|
||||
@@ -757,19 +760,21 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
tool_call.code_interpreter,
|
||||
RunStepDeltaCodeInterpreterDetailItemObject,
|
||||
):
|
||||
code_contents: list[Contents] = []
|
||||
code_contents: list[Content] = []
|
||||
if tool_call.code_interpreter.input is not None:
|
||||
logger.debug(f"Code Interpreter Input: {tool_call.code_interpreter.input}")
|
||||
if tool_call.code_interpreter.outputs is not None:
|
||||
for output in tool_call.code_interpreter.outputs:
|
||||
if isinstance(output, RunStepDeltaCodeInterpreterLogOutput) and output.logs:
|
||||
code_contents.append(TextContent(text=output.logs))
|
||||
code_contents.append(Content.from_text(text=output.logs))
|
||||
if (
|
||||
isinstance(output, RunStepDeltaCodeInterpreterImageOutput)
|
||||
and output.image is not None
|
||||
and output.image.file_id is not None
|
||||
):
|
||||
code_contents.append(HostedFileContent(file_id=output.image.file_id))
|
||||
code_contents.append(
|
||||
Content.from_hosted_file(file_id=output.image.file_id)
|
||||
)
|
||||
yield ChatResponseUpdate(
|
||||
role=Role.ASSISTANT,
|
||||
contents=code_contents,
|
||||
@@ -822,12 +827,12 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
except Exception as ex:
|
||||
logger.debug(f"Failed to capture Azure AI Search tool call: {ex}")
|
||||
|
||||
def _parse_function_calls_from_azure_ai(self, event_data: ThreadRun, response_id: str | None) -> list[Contents]:
|
||||
def _parse_function_calls_from_azure_ai(self, event_data: ThreadRun, response_id: str | None) -> list[Content]:
|
||||
"""Parse function call contents from an Azure AI tool action event."""
|
||||
if isinstance(event_data, ThreadRun) and event_data.required_action is not None:
|
||||
if isinstance(event_data.required_action, SubmitToolOutputsAction):
|
||||
return [
|
||||
FunctionCallContent(
|
||||
Content.from_function_call(
|
||||
call_id=f'["{response_id}", "{tool.id}"]',
|
||||
name=tool.function.name,
|
||||
arguments=tool.function.arguments,
|
||||
@@ -837,9 +842,9 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
]
|
||||
if isinstance(event_data.required_action, SubmitToolApprovalAction):
|
||||
return [
|
||||
FunctionApprovalRequestContent(
|
||||
Content.from_function_approval_request(
|
||||
id=f'["{response_id}", "{tool.id}"]',
|
||||
function_call=FunctionCallContent(
|
||||
function_call=Content.from_function_call(
|
||||
call_id=f'["{response_id}", "{tool.id}"]',
|
||||
name=tool.name,
|
||||
arguments=tool.arguments,
|
||||
@@ -875,7 +880,7 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
messages: MutableSequence[ChatMessage],
|
||||
options: Mapping[str, Any],
|
||||
**kwargs: Any,
|
||||
) -> tuple[dict[str, Any], list[FunctionResultContent | FunctionApprovalResponseContent] | None]:
|
||||
) -> tuple[dict[str, Any], list[Content] | None]:
|
||||
agent_definition = await self._load_agent_definition_if_needed()
|
||||
|
||||
# Build run_options from options dict, excluding specific keys
|
||||
@@ -1052,7 +1057,7 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
) -> tuple[
|
||||
list[ThreadMessageOptions] | None,
|
||||
list[str],
|
||||
list[FunctionResultContent | FunctionApprovalResponseContent] | None,
|
||||
list[Content] | None,
|
||||
]:
|
||||
"""Prepare messages for Azure AI Agents API.
|
||||
|
||||
@@ -1064,28 +1069,34 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
Tuple of (additional_messages, instructions, required_action_results)
|
||||
"""
|
||||
instructions: list[str] = []
|
||||
required_action_results: list[FunctionResultContent | FunctionApprovalResponseContent] | None = None
|
||||
required_action_results: list[Content] | None = None
|
||||
additional_messages: list[ThreadMessageOptions] | None = None
|
||||
|
||||
for chat_message in messages:
|
||||
if chat_message.role.value in ["system", "developer"]:
|
||||
for text_content in [content for content in chat_message.contents if isinstance(content, TextContent)]:
|
||||
instructions.append(text_content.text)
|
||||
for text_content in [content for content in chat_message.contents if content.type == "text"]:
|
||||
instructions.append(text_content.text) # type: ignore[arg-type]
|
||||
continue
|
||||
|
||||
message_contents: list[MessageInputContentBlock] = []
|
||||
|
||||
for content in chat_message.contents:
|
||||
if isinstance(content, TextContent):
|
||||
message_contents.append(MessageInputTextBlock(text=content.text))
|
||||
elif isinstance(content, (DataContent, UriContent)) and content.has_top_level_media_type("image"):
|
||||
message_contents.append(MessageInputImageUrlBlock(image_url=MessageImageUrlParam(url=content.uri)))
|
||||
elif isinstance(content, (FunctionResultContent, FunctionApprovalResponseContent)):
|
||||
if required_action_results is None:
|
||||
required_action_results = []
|
||||
required_action_results.append(content)
|
||||
elif isinstance(content.raw_representation, MessageInputContentBlock):
|
||||
message_contents.append(content.raw_representation)
|
||||
match content.type:
|
||||
case "text":
|
||||
message_contents.append(MessageInputTextBlock(text=content.text)) # type: ignore[arg-type]
|
||||
case "data" | "uri":
|
||||
if content.has_top_level_media_type("image"):
|
||||
message_contents.append(
|
||||
MessageInputImageUrlBlock(image_url=MessageImageUrlParam(url=content.uri)) # type: ignore[arg-type]
|
||||
)
|
||||
# Only images are supported. Other media types are ignored.
|
||||
case "function_result" | "function_approval_response":
|
||||
if required_action_results is None:
|
||||
required_action_results = []
|
||||
required_action_results.append(content)
|
||||
case _:
|
||||
if isinstance(content.raw_representation, MessageInputContentBlock):
|
||||
message_contents.append(content.raw_representation)
|
||||
|
||||
if message_contents:
|
||||
if additional_messages is None:
|
||||
@@ -1099,9 +1110,85 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
|
||||
return additional_messages, instructions, required_action_results
|
||||
|
||||
async def _prepare_tools_for_azure_ai(
|
||||
self, tools: Sequence["ToolProtocol | MutableMapping[str, Any]"], run_options: dict[str, Any] | None = None
|
||||
) -> list[ToolDefinition | dict[str, Any]]:
|
||||
"""Prepare tool definitions for the Azure AI Agents API."""
|
||||
tool_definitions: list[ToolDefinition | dict[str, Any]] = []
|
||||
for tool in tools:
|
||||
match tool:
|
||||
case AIFunction():
|
||||
tool_definitions.append(tool.to_json_schema_spec()) # type: ignore[reportUnknownArgumentType]
|
||||
case HostedWebSearchTool():
|
||||
additional_props = tool.additional_properties or {}
|
||||
config_args: dict[str, Any] = {}
|
||||
if count := additional_props.get("count"):
|
||||
config_args["count"] = count
|
||||
if freshness := additional_props.get("freshness"):
|
||||
config_args["freshness"] = freshness
|
||||
if market := additional_props.get("market"):
|
||||
config_args["market"] = market
|
||||
if set_lang := additional_props.get("set_lang"):
|
||||
config_args["set_lang"] = set_lang
|
||||
# Bing Grounding
|
||||
connection_id = additional_props.get("connection_id") or os.getenv("BING_CONNECTION_ID")
|
||||
# Custom Bing Search
|
||||
custom_connection_id = additional_props.get("custom_connection_id") or os.getenv(
|
||||
"BING_CUSTOM_CONNECTION_ID"
|
||||
)
|
||||
custom_instance_name = additional_props.get("custom_instance_name") or os.getenv(
|
||||
"BING_CUSTOM_INSTANCE_NAME"
|
||||
)
|
||||
bing_search: BingGroundingTool | BingCustomSearchTool | None = None
|
||||
if (connection_id) and not custom_connection_id and not custom_instance_name:
|
||||
if connection_id:
|
||||
conn_id = connection_id
|
||||
else:
|
||||
raise ServiceInitializationError("Parameter connection_id is not provided.")
|
||||
bing_search = BingGroundingTool(connection_id=conn_id, **config_args)
|
||||
if custom_connection_id and custom_instance_name:
|
||||
bing_search = BingCustomSearchTool(
|
||||
connection_id=custom_connection_id,
|
||||
instance_name=custom_instance_name,
|
||||
**config_args,
|
||||
)
|
||||
if not bing_search:
|
||||
raise ServiceInitializationError(
|
||||
"Bing search tool requires either 'connection_id' for Bing Grounding "
|
||||
"or both 'custom_connection_id' and 'custom_instance_name' for Custom Bing Search. "
|
||||
"These can be provided via additional_properties or environment variables: "
|
||||
"'BING_CONNECTION_ID', 'BING_CUSTOM_CONNECTION_ID', "
|
||||
"'BING_CUSTOM_INSTANCE_NAME'"
|
||||
)
|
||||
tool_definitions.extend(bing_search.definitions)
|
||||
case HostedCodeInterpreterTool():
|
||||
tool_definitions.append(CodeInterpreterToolDefinition())
|
||||
case HostedMCPTool():
|
||||
mcp_tool = McpTool(
|
||||
server_label=tool.name.replace(" ", "_"),
|
||||
server_url=str(tool.url),
|
||||
allowed_tools=list(tool.allowed_tools) if tool.allowed_tools else [],
|
||||
)
|
||||
tool_definitions.extend(mcp_tool.definitions)
|
||||
case HostedFileSearchTool():
|
||||
vector_stores = [inp for inp in tool.inputs or [] if inp.type == "hosted_vector_store"]
|
||||
if vector_stores:
|
||||
file_search = FileSearchTool(vector_store_ids=[vs.vector_store_id for vs in vector_stores]) # type: ignore[misc]
|
||||
tool_definitions.extend(file_search.definitions)
|
||||
# Set tool_resources for file search to work properly with Azure AI
|
||||
if run_options is not None and "tool_resources" not in run_options:
|
||||
run_options["tool_resources"] = file_search.resources
|
||||
case ToolDefinition():
|
||||
tool_definitions.append(tool)
|
||||
case dict():
|
||||
tool_definitions.append(tool)
|
||||
case _:
|
||||
raise ServiceInitializationError(f"Unsupported tool type: {type(tool)}")
|
||||
return tool_definitions
|
||||
|
||||
def _prepare_tool_outputs_for_azure_ai(
|
||||
self,
|
||||
required_action_results: list[FunctionResultContent | FunctionApprovalResponseContent] | None,
|
||||
required_action_results: list[Content] | None,
|
||||
) -> tuple[str | None, list[ToolOutput] | None, list[ToolApproval] | None]:
|
||||
"""Prepare function results and approvals for submission to the Azure AI API."""
|
||||
run_id: str | None = None
|
||||
@@ -1115,9 +1202,7 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
# We need to extract the run ID and ensure that the Output/Approval we send back to Azure
|
||||
# is only the call ID.
|
||||
run_and_call_ids: list[str] = (
|
||||
json.loads(content.call_id)
|
||||
if isinstance(content, FunctionResultContent)
|
||||
else json.loads(content.id)
|
||||
json.loads(content.call_id) if content.type == "function_result" else json.loads(content.id) # type: ignore[arg-type]
|
||||
)
|
||||
|
||||
if (
|
||||
@@ -1132,16 +1217,16 @@ class AzureAIAgentClient(BaseChatClient[TAzureAIAgentOptions], Generic[TAzureAIA
|
||||
run_id = run_and_call_ids[0]
|
||||
call_id = run_and_call_ids[1]
|
||||
|
||||
if isinstance(content, FunctionResultContent):
|
||||
if content.type == "function_result":
|
||||
if tool_outputs is None:
|
||||
tool_outputs = []
|
||||
tool_outputs.append(
|
||||
ToolOutput(tool_call_id=call_id, output=prepare_function_call_results(content.result))
|
||||
)
|
||||
elif isinstance(content, FunctionApprovalResponseContent):
|
||||
elif content.type == "function_approval_response":
|
||||
if tool_approvals is None:
|
||||
tool_approvals = []
|
||||
tool_approvals.append(ToolApproval(tool_call_id=call_id, approve=content.approved))
|
||||
tool_approvals.append(ToolApproval(tool_call_id=call_id, approve=content.approved)) # type: ignore[arg-type]
|
||||
|
||||
return run_id, tool_outputs, tool_approvals
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ from agent_framework import (
|
||||
ContextProvider,
|
||||
HostedMCPTool,
|
||||
Middleware,
|
||||
TextContent,
|
||||
ToolProtocol,
|
||||
get_logger,
|
||||
use_chat_middleware,
|
||||
@@ -477,8 +476,8 @@ class AzureAIClient(OpenAIBaseResponsesClient[TAzureAIClientOptions], Generic[TA
|
||||
# System/developer messages are turned into instructions, since there is no such message roles in Azure AI.
|
||||
for message in messages:
|
||||
if message.role.value in ["system", "developer"]:
|
||||
for text_content in [content for content in message.contents if isinstance(content, TextContent)]:
|
||||
instructions_list.append(text_content.text)
|
||||
for text_content in [content for content in message.contents if content.type == "text"]:
|
||||
instructions_list.append(text_content.text) # type: ignore[arg-type]
|
||||
else:
|
||||
result.append(message)
|
||||
|
||||
|
||||
@@ -6,12 +6,10 @@ from typing import Any, ClassVar, Literal, cast
|
||||
|
||||
from agent_framework import (
|
||||
AIFunction,
|
||||
Contents,
|
||||
Content,
|
||||
HostedCodeInterpreterTool,
|
||||
HostedFileContent,
|
||||
HostedFileSearchTool,
|
||||
HostedMCPTool,
|
||||
HostedVectorStoreContent,
|
||||
HostedWebSearchTool,
|
||||
ToolProtocol,
|
||||
get_logger,
|
||||
@@ -189,9 +187,9 @@ def to_azure_ai_agent_tools(
|
||||
)
|
||||
tool_definitions.extend(mcp_tool.definitions)
|
||||
case HostedFileSearchTool():
|
||||
vector_stores = [inp for inp in tool.inputs or [] if isinstance(inp, HostedVectorStoreContent)]
|
||||
vector_stores = [inp for inp in tool.inputs or [] if inp.type == "hosted_vector_store"]
|
||||
if vector_stores:
|
||||
file_search = AgentsFileSearchTool(vector_store_ids=[vs.vector_store_id for vs in vector_stores])
|
||||
file_search = AgentsFileSearchTool(vector_store_ids=[vs.vector_store_id for vs in vector_stores]) # type: ignore[misc]
|
||||
tool_definitions.extend(file_search.definitions)
|
||||
# Set tool_resources for file search to work properly with Azure AI
|
||||
if run_options is not None and "tool_resources" not in run_options:
|
||||
@@ -247,7 +245,7 @@ def _convert_dict_tool(tool: dict[str, Any]) -> ToolProtocol | dict[str, Any] |
|
||||
if tool_type == "file_search":
|
||||
file_search_config = tool.get("file_search", {})
|
||||
vector_store_ids = file_search_config.get("vector_store_ids", [])
|
||||
inputs = [HostedVectorStoreContent(vector_store_id=vs_id) for vs_id in vector_store_ids]
|
||||
inputs = [Content.from_hosted_vector_store(vector_store_id=vs_id) for vs_id in vector_store_ids]
|
||||
return HostedFileSearchTool(inputs=inputs if inputs else None) # type: ignore
|
||||
|
||||
if tool_type == "bing_grounding":
|
||||
@@ -287,7 +285,7 @@ def _convert_sdk_tool(tool: ToolDefinition) -> ToolProtocol | dict[str, Any] | N
|
||||
if tool_type == "file_search":
|
||||
file_search_config = getattr(tool, "file_search", None)
|
||||
vector_store_ids = getattr(file_search_config, "vector_store_ids", []) if file_search_config else []
|
||||
inputs = [HostedVectorStoreContent(vector_store_id=vs_id) for vs_id in vector_store_ids]
|
||||
inputs = [Content.from_hosted_vector_store(vector_store_id=vs_id) for vs_id in vector_store_ids]
|
||||
return HostedFileSearchTool(inputs=inputs if inputs else None) # type: ignore
|
||||
|
||||
if tool_type == "bing_grounding":
|
||||
@@ -372,18 +370,18 @@ def from_azure_ai_tools(tools: Sequence[Tool | dict[str, Any]] | None) -> list[T
|
||||
elif tool_type == "code_interpreter":
|
||||
ci_tool = cast(CodeInterpreterTool, tool_dict)
|
||||
container = ci_tool.get("container", {})
|
||||
ci_inputs: list[Contents] = []
|
||||
ci_inputs: list[Content] = []
|
||||
if "file_ids" in container:
|
||||
for file_id in container["file_ids"]:
|
||||
ci_inputs.append(HostedFileContent(file_id=file_id))
|
||||
ci_inputs.append(Content.from_hosted_file(file_id=file_id))
|
||||
|
||||
agent_tools.append(HostedCodeInterpreterTool(inputs=ci_inputs if ci_inputs else None)) # type: ignore
|
||||
elif tool_type == "file_search":
|
||||
fs_tool = cast(ProjectsFileSearchTool, tool_dict)
|
||||
fs_inputs: list[Contents] = []
|
||||
fs_inputs: list[Content] = []
|
||||
if "vector_store_ids" in fs_tool:
|
||||
for vs_id in fs_tool["vector_store_ids"]:
|
||||
fs_inputs.append(HostedVectorStoreContent(vector_store_id=vs_id))
|
||||
fs_inputs.append(Content.from_hosted_vector_store(vector_store_id=vs_id))
|
||||
|
||||
agent_tools.append(
|
||||
HostedFileSearchTool(
|
||||
@@ -433,8 +431,8 @@ def to_azure_ai_tools(
|
||||
file_ids: list[str] = []
|
||||
if tool.inputs:
|
||||
for tool_input in tool.inputs:
|
||||
if isinstance(tool_input, HostedFileContent):
|
||||
file_ids.append(tool_input.file_id)
|
||||
if tool_input.type == "hosted_file":
|
||||
file_ids.append(tool_input.file_id) # type: ignore[misc, arg-type]
|
||||
container = CodeInterpreterToolAuto(file_ids=file_ids if file_ids else None)
|
||||
ci_tool: CodeInterpreterTool = CodeInterpreterTool(container=container)
|
||||
azure_tools.append(ci_tool)
|
||||
@@ -453,11 +451,14 @@ def to_azure_ai_tools(
|
||||
if not tool.inputs:
|
||||
raise ValueError("HostedFileSearchTool requires inputs to be specified.")
|
||||
vector_store_ids: list[str] = [
|
||||
inp.vector_store_id for inp in tool.inputs if isinstance(inp, HostedVectorStoreContent)
|
||||
inp.vector_store_id # type: ignore[misc]
|
||||
for inp in tool.inputs
|
||||
if inp.type == "hosted_vector_store"
|
||||
]
|
||||
if not vector_store_ids:
|
||||
raise ValueError(
|
||||
"HostedFileSearchTool requires inputs to be of type `HostedVectorStoreContent`."
|
||||
"HostedFileSearchTool requires inputs to be of type `Content` with "
|
||||
"type 'hosted_vector_store'."
|
||||
)
|
||||
fs_tool: ProjectsFileSearchTool = ProjectsFileSearchTool(vector_store_ids=vector_store_ids)
|
||||
if tool.max_results:
|
||||
|
||||
@@ -7,10 +7,10 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
import pytest
|
||||
from agent_framework import (
|
||||
ChatAgent,
|
||||
Content,
|
||||
HostedCodeInterpreterTool,
|
||||
HostedFileSearchTool,
|
||||
HostedMCPTool,
|
||||
HostedVectorStoreContent,
|
||||
HostedWebSearchTool,
|
||||
ai_function,
|
||||
)
|
||||
@@ -509,7 +509,7 @@ def test_to_azure_ai_agent_tools_code_interpreter() -> None:
|
||||
|
||||
def test_to_azure_ai_agent_tools_file_search() -> None:
|
||||
"""Test converting HostedFileSearchTool with vector stores."""
|
||||
tool = HostedFileSearchTool(inputs=[HostedVectorStoreContent(vector_store_id="vs-123")])
|
||||
tool = HostedFileSearchTool(inputs=[Content.from_hosted_vector_store(vector_store_id="vs-123")])
|
||||
run_options: dict[str, Any] = {}
|
||||
|
||||
result = to_azure_ai_agent_tools([tool], run_options)
|
||||
|
||||
@@ -17,19 +17,12 @@ from agent_framework import (
|
||||
ChatOptions,
|
||||
ChatResponse,
|
||||
ChatResponseUpdate,
|
||||
CitationAnnotation,
|
||||
FunctionApprovalRequestContent,
|
||||
FunctionApprovalResponseContent,
|
||||
FunctionCallContent,
|
||||
FunctionResultContent,
|
||||
Content,
|
||||
HostedCodeInterpreterTool,
|
||||
HostedFileContent,
|
||||
HostedFileSearchTool,
|
||||
HostedMCPTool,
|
||||
HostedVectorStoreContent,
|
||||
HostedWebSearchTool,
|
||||
Role,
|
||||
TextContent,
|
||||
UriContent,
|
||||
)
|
||||
from agent_framework._serialization import SerializationMixin
|
||||
from agent_framework.exceptions import ServiceInitializationError
|
||||
@@ -368,7 +361,7 @@ async def test_azure_ai_chat_client_prepare_options_with_image_content(mock_agen
|
||||
# Mock get_agent
|
||||
mock_agents_client.get_agent = AsyncMock(return_value=None)
|
||||
|
||||
image_content = UriContent(uri="https://example.com/image.jpg", media_type="image/jpeg")
|
||||
image_content = Content.from_uri(uri="https://example.com/image.jpg", media_type="image/jpeg")
|
||||
messages = [ChatMessage(role=Role.USER, contents=[image_content])]
|
||||
|
||||
run_options, _ = await chat_client._prepare_options(messages, {}) # type: ignore
|
||||
@@ -551,7 +544,7 @@ def test_azure_ai_chat_client_parse_function_calls_from_azure_ai_basic(mock_agen
|
||||
result = chat_client._parse_function_calls_from_azure_ai(mock_event_data, "response_123") # type: ignore
|
||||
|
||||
assert len(result) == 1
|
||||
assert isinstance(result[0], FunctionCallContent)
|
||||
assert result[0].type == "function_call"
|
||||
assert result[0].name == "get_weather"
|
||||
assert result[0].call_id == '["response_123", "call_123"]'
|
||||
|
||||
@@ -728,6 +721,121 @@ async def test_azure_ai_chat_client_prepare_options_mcp_with_headers(mock_agents
|
||||
assert mcp_resource["headers"] == headers
|
||||
|
||||
|
||||
async def test_azure_ai_chat_client_prepare_tools_for_azure_ai_web_search_bing_grounding(
|
||||
mock_agents_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test _prepare_tools_for_azure_ai with HostedWebSearchTool using Bing Grounding."""
|
||||
|
||||
chat_client = create_test_azure_ai_chat_client(mock_agents_client, agent_id="test-agent")
|
||||
|
||||
web_search_tool = HostedWebSearchTool(
|
||||
additional_properties={
|
||||
"connection_id": "test-connection-id",
|
||||
"count": 5,
|
||||
"freshness": "Day",
|
||||
"market": "en-US",
|
||||
"set_lang": "en",
|
||||
}
|
||||
)
|
||||
|
||||
# Mock BingGroundingTool
|
||||
with patch("agent_framework_azure_ai._chat_client.BingGroundingTool") as mock_bing_grounding:
|
||||
mock_bing_tool = MagicMock()
|
||||
mock_bing_tool.definitions = [{"type": "bing_grounding"}]
|
||||
mock_bing_grounding.return_value = mock_bing_tool
|
||||
|
||||
result = await chat_client._prepare_tools_for_azure_ai([web_search_tool]) # type: ignore
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0] == {"type": "bing_grounding"}
|
||||
call_args = mock_bing_grounding.call_args[1]
|
||||
assert call_args["count"] == 5
|
||||
assert call_args["freshness"] == "Day"
|
||||
assert call_args["market"] == "en-US"
|
||||
assert call_args["set_lang"] == "en"
|
||||
assert "connection_id" in call_args
|
||||
|
||||
|
||||
async def test_azure_ai_chat_client_prepare_tools_for_azure_ai_web_search_bing_grounding_with_connection_id(
|
||||
mock_agents_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test _prepare_tools_... with HostedWebSearchTool using Bing Grounding with connection_id (no HTTP call)."""
|
||||
|
||||
chat_client = create_test_azure_ai_chat_client(mock_agents_client, agent_id="test-agent")
|
||||
|
||||
web_search_tool = HostedWebSearchTool(
|
||||
additional_properties={
|
||||
"connection_id": "direct-connection-id",
|
||||
"count": 3,
|
||||
}
|
||||
)
|
||||
|
||||
# Mock BingGroundingTool
|
||||
with patch("agent_framework_azure_ai._chat_client.BingGroundingTool") as mock_bing_grounding:
|
||||
mock_bing_tool = MagicMock()
|
||||
mock_bing_tool.definitions = [{"type": "bing_grounding"}]
|
||||
mock_bing_grounding.return_value = mock_bing_tool
|
||||
|
||||
result = await chat_client._prepare_tools_for_azure_ai([web_search_tool]) # type: ignore
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0] == {"type": "bing_grounding"}
|
||||
mock_bing_grounding.assert_called_once_with(connection_id="direct-connection-id", count=3)
|
||||
|
||||
|
||||
async def test_azure_ai_chat_client_prepare_tools_for_azure_ai_web_search_custom_bing(
|
||||
mock_agents_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test _prepare_tools_for_azure_ai with HostedWebSearchTool using Custom Bing Search."""
|
||||
|
||||
chat_client = create_test_azure_ai_chat_client(mock_agents_client, agent_id="test-agent")
|
||||
|
||||
web_search_tool = HostedWebSearchTool(
|
||||
additional_properties={
|
||||
"custom_connection_id": "custom-connection-id",
|
||||
"custom_instance_name": "custom-instance",
|
||||
"count": 10,
|
||||
}
|
||||
)
|
||||
|
||||
# Mock BingCustomSearchTool
|
||||
with patch("agent_framework_azure_ai._chat_client.BingCustomSearchTool") as mock_custom_bing:
|
||||
mock_custom_tool = MagicMock()
|
||||
mock_custom_tool.definitions = [{"type": "bing_custom_search"}]
|
||||
mock_custom_bing.return_value = mock_custom_tool
|
||||
|
||||
result = await chat_client._prepare_tools_for_azure_ai([web_search_tool]) # type: ignore
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0] == {"type": "bing_custom_search"}
|
||||
|
||||
|
||||
async def test_azure_ai_chat_client_prepare_tools_for_azure_ai_file_search_with_vector_stores(
|
||||
mock_agents_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test _prepare_tools_for_azure_ai with HostedFileSearchTool using vector stores."""
|
||||
|
||||
chat_client = create_test_azure_ai_chat_client(mock_agents_client, agent_id="test-agent")
|
||||
|
||||
vector_store_input = Content.from_hosted_vector_store(vector_store_id="vs-123")
|
||||
file_search_tool = HostedFileSearchTool(inputs=[vector_store_input])
|
||||
|
||||
# Mock FileSearchTool
|
||||
with patch("agent_framework_azure_ai._chat_client.FileSearchTool") as mock_file_search:
|
||||
mock_file_tool = MagicMock()
|
||||
mock_file_tool.definitions = [{"type": "file_search"}]
|
||||
mock_file_tool.resources = {"vector_store_ids": ["vs-123"]}
|
||||
mock_file_search.return_value = mock_file_tool
|
||||
|
||||
run_options = {}
|
||||
result = await chat_client._prepare_tools_for_azure_ai([file_search_tool], run_options) # type: ignore
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0] == {"type": "file_search"}
|
||||
assert run_options["tool_resources"] == {"vector_store_ids": ["vs-123"]}
|
||||
mock_file_search.assert_called_once_with(vector_store_ids=["vs-123"])
|
||||
|
||||
|
||||
async def test_azure_ai_chat_client_create_agent_stream_submit_tool_approvals(
|
||||
mock_agents_client: MagicMock,
|
||||
) -> None:
|
||||
@@ -741,9 +849,9 @@ async def test_azure_ai_chat_client_create_agent_stream_submit_tool_approvals(
|
||||
chat_client._get_active_thread_run = AsyncMock(return_value=mock_thread_run) # type: ignore
|
||||
|
||||
# Mock required action results with approval response that matches run ID
|
||||
approval_response = FunctionApprovalResponseContent(
|
||||
approval_response = Content.from_function_approval_response(
|
||||
id='["test-run-id", "test-call-id"]',
|
||||
function_call=FunctionCallContent(
|
||||
function_call=Content.from_function_call(
|
||||
call_id='["test-run-id", "test-call-id"]', name="test_function", arguments="{}"
|
||||
),
|
||||
approved=True,
|
||||
@@ -839,7 +947,7 @@ async def test_azure_ai_chat_client_prepare_tool_outputs_for_azure_ai_function_r
|
||||
chat_client = create_test_azure_ai_chat_client(mock_agents_client, agent_id="test-agent")
|
||||
|
||||
# Test with simple result
|
||||
function_result = FunctionResultContent(call_id='["run_123", "call_456"]', result="Simple result")
|
||||
function_result = Content.from_function_result(call_id='["run_123", "call_456"]', result="Simple result")
|
||||
|
||||
run_id, tool_outputs, tool_approvals = chat_client._prepare_tool_outputs_for_azure_ai([function_result]) # type: ignore
|
||||
|
||||
@@ -857,7 +965,7 @@ async def test_azure_ai_chat_client_convert_required_action_invalid_call_id(mock
|
||||
chat_client = create_test_azure_ai_chat_client(mock_agents_client, agent_id="test-agent")
|
||||
|
||||
# Invalid call_id format - should raise JSONDecodeError
|
||||
function_result = FunctionResultContent(call_id="invalid_json", result="result")
|
||||
function_result = Content.from_function_result(call_id="invalid_json", result="result")
|
||||
|
||||
with pytest.raises(json.JSONDecodeError):
|
||||
chat_client._prepare_tool_outputs_for_azure_ai([function_result]) # type: ignore
|
||||
@@ -870,7 +978,7 @@ async def test_azure_ai_chat_client_convert_required_action_invalid_structure(
|
||||
chat_client = create_test_azure_ai_chat_client(mock_agents_client, agent_id="test-agent")
|
||||
|
||||
# Valid JSON but invalid structure (missing second element)
|
||||
function_result = FunctionResultContent(call_id='["run_123"]', result="result")
|
||||
function_result = Content.from_function_result(call_id='["run_123"]', result="result")
|
||||
|
||||
run_id, tool_outputs, tool_approvals = chat_client._prepare_tool_outputs_for_azure_ai([function_result]) # type: ignore
|
||||
|
||||
@@ -894,7 +1002,7 @@ async def test_azure_ai_chat_client_convert_required_action_serde_model_results(
|
||||
|
||||
# Test with BaseModel result
|
||||
mock_result = MockResult(name="test", value=42)
|
||||
function_result = FunctionResultContent(call_id='["run_123", "call_456"]', result=mock_result)
|
||||
function_result = Content.from_function_result(call_id='["run_123", "call_456"]', result=mock_result)
|
||||
|
||||
run_id, tool_outputs, tool_approvals = chat_client._prepare_tool_outputs_for_azure_ai([function_result]) # type: ignore
|
||||
|
||||
@@ -922,7 +1030,7 @@ async def test_azure_ai_chat_client_convert_required_action_multiple_results(
|
||||
# Test with multiple results - mix of BaseModel and regular objects
|
||||
mock_basemodel = MockResult(data="model_data")
|
||||
results_list = [mock_basemodel, {"key": "value"}, "string_result"]
|
||||
function_result = FunctionResultContent(call_id='["run_123", "call_456"]', result=results_list)
|
||||
function_result = Content.from_function_result(call_id='["run_123", "call_456"]', result=results_list)
|
||||
|
||||
run_id, tool_outputs, tool_approvals = chat_client._prepare_tool_outputs_for_azure_ai([function_result]) # type: ignore
|
||||
|
||||
@@ -948,9 +1056,11 @@ async def test_azure_ai_chat_client_convert_required_action_approval_response(
|
||||
chat_client = create_test_azure_ai_chat_client(mock_agents_client, agent_id="test-agent")
|
||||
|
||||
# Test with approval response - need to provide required fields
|
||||
approval_response = FunctionApprovalResponseContent(
|
||||
approval_response = Content.from_function_approval_response(
|
||||
id='["run_123", "call_456"]',
|
||||
function_call=FunctionCallContent(call_id='["run_123", "call_456"]', name="test_function", arguments="{}"),
|
||||
function_call=Content.from_function_call(
|
||||
call_id='["run_123", "call_456"]', name="test_function", arguments="{}"
|
||||
),
|
||||
approved=True,
|
||||
)
|
||||
|
||||
@@ -985,7 +1095,7 @@ async def test_azure_ai_chat_client_parse_function_calls_from_azure_ai_approval_
|
||||
result = chat_client._parse_function_calls_from_azure_ai(mock_event_data, "response_123") # type: ignore
|
||||
|
||||
assert len(result) == 1
|
||||
assert isinstance(result[0], FunctionApprovalRequestContent)
|
||||
assert result[0].type == "function_approval_request"
|
||||
assert result[0].id == '["response_123", "approval_call_123"]'
|
||||
assert result[0].function_call.name == "approve_action"
|
||||
assert result[0].function_call.call_id == '["response_123", "approval_call_123"]'
|
||||
@@ -1064,7 +1174,7 @@ async def test_azure_ai_chat_client_create_agent_stream_submit_tool_outputs(
|
||||
chat_client._get_active_thread_run = AsyncMock(return_value=mock_thread_run) # type: ignore
|
||||
|
||||
# Mock required action results with matching run ID
|
||||
function_result = FunctionResultContent(call_id='["test-run-id", "test-call-id"]', result="test result")
|
||||
function_result = Content.from_function_result(call_id='["test-run-id", "test-call-id"]', result="test result")
|
||||
|
||||
# Mock submit_tool_outputs_stream
|
||||
mock_handler = MagicMock()
|
||||
@@ -1115,14 +1225,13 @@ def test_azure_ai_chat_client_extract_url_citations_with_citations(mock_agents_c
|
||||
# Verify results
|
||||
assert len(citations) == 1
|
||||
citation = citations[0]
|
||||
assert isinstance(citation, CitationAnnotation)
|
||||
assert citation.url == "https://example.com/test"
|
||||
assert citation.title == "Test Title"
|
||||
assert citation.snippet is None
|
||||
assert citation.annotated_regions is not None
|
||||
assert len(citation.annotated_regions) == 1
|
||||
assert citation.annotated_regions[0].start_index == 10
|
||||
assert citation.annotated_regions[0].end_index == 20
|
||||
assert citation["url"] == "https://example.com/test"
|
||||
assert citation["title"] == "Test Title"
|
||||
assert citation["snippet"] is None
|
||||
assert citation["annotated_regions"] is not None
|
||||
assert len(citation["annotated_regions"]) == 1
|
||||
assert citation["annotated_regions"][0]["start_index"] == 10
|
||||
assert citation["annotated_regions"][0]["end_index"] == 20
|
||||
|
||||
|
||||
def test_azure_ai_chat_client_extract_file_path_contents_with_file_path_annotation(
|
||||
@@ -1158,7 +1267,7 @@ def test_azure_ai_chat_client_extract_file_path_contents_with_file_path_annotati
|
||||
|
||||
# Verify results
|
||||
assert len(file_contents) == 1
|
||||
assert isinstance(file_contents[0], HostedFileContent)
|
||||
assert file_contents[0].type == "hosted_file"
|
||||
assert file_contents[0].file_id == "assistant-test-file-123"
|
||||
|
||||
|
||||
@@ -1195,7 +1304,7 @@ def test_azure_ai_chat_client_extract_file_path_contents_with_file_citation_anno
|
||||
|
||||
# Verify results
|
||||
assert len(file_contents) == 1
|
||||
assert isinstance(file_contents[0], HostedFileContent)
|
||||
assert file_contents[0].type == "hosted_file"
|
||||
assert file_contents[0].file_id == "cfile_test-citation-456"
|
||||
|
||||
|
||||
@@ -1305,7 +1414,7 @@ async def test_azure_ai_chat_client_streaming() -> None:
|
||||
assert chunk is not None
|
||||
assert isinstance(chunk, ChatResponseUpdate)
|
||||
for content in chunk.contents:
|
||||
if isinstance(content, TextContent) and content.text:
|
||||
if content.type == "text" and content.text:
|
||||
full_message += content.text
|
||||
|
||||
assert any(word in full_message.lower() for word in ["sunny", "25"])
|
||||
@@ -1331,7 +1440,7 @@ async def test_azure_ai_chat_client_streaming_tools() -> None:
|
||||
assert chunk is not None
|
||||
assert isinstance(chunk, ChatResponseUpdate)
|
||||
for content in chunk.contents:
|
||||
if isinstance(content, TextContent) and content.text:
|
||||
if content.type == "text" and content.text:
|
||||
full_message += content.text
|
||||
|
||||
assert any(word in full_message.lower() for word in ["sunny", "25"])
|
||||
@@ -1476,7 +1585,9 @@ async def test_azure_ai_chat_client_agent_file_search():
|
||||
)
|
||||
|
||||
# 2. Create file search tool with uploaded resources
|
||||
file_search_tool = HostedFileSearchTool(inputs=[HostedVectorStoreContent(vector_store_id=vector_store.id)])
|
||||
file_search_tool = HostedFileSearchTool(
|
||||
inputs=[Content.from_hosted_vector_store(vector_store_id=vector_store.id)]
|
||||
)
|
||||
|
||||
async with ChatAgent(
|
||||
chat_client=client,
|
||||
@@ -1795,7 +1906,7 @@ def test_azure_ai_chat_client_extract_url_citations_with_azure_search_enhanced_u
|
||||
# Verify real URL was used
|
||||
assert len(citations) == 1
|
||||
citation = citations[0]
|
||||
assert citation.url == "https://real-example.com/doc2" # doc_1 maps to index 1
|
||||
assert citation["url"] == "https://real-example.com/doc2" # doc_1 maps to index 1
|
||||
|
||||
|
||||
def test_azure_ai_chat_client_init_with_auto_created_agents_client(
|
||||
|
||||
@@ -16,14 +16,12 @@ from agent_framework import (
|
||||
ChatMessage,
|
||||
ChatOptions,
|
||||
ChatResponse,
|
||||
Content,
|
||||
HostedCodeInterpreterTool,
|
||||
HostedFileContent,
|
||||
HostedFileSearchTool,
|
||||
HostedMCPTool,
|
||||
HostedVectorStoreContent,
|
||||
HostedWebSearchTool,
|
||||
Role,
|
||||
TextContent,
|
||||
)
|
||||
from agent_framework.exceptions import ServiceInitializationError
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
@@ -298,9 +296,9 @@ async def test_prepare_messages_for_azure_ai_with_system_messages(
|
||||
client = create_test_azure_ai_client(mock_project_client)
|
||||
|
||||
messages = [
|
||||
ChatMessage(role=Role.SYSTEM, contents=[TextContent(text="You are a helpful assistant.")]),
|
||||
ChatMessage(role=Role.USER, contents=[TextContent(text="Hello")]),
|
||||
ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text="System response")]),
|
||||
ChatMessage(role=Role.SYSTEM, contents=[Content.from_text(text="You are a helpful assistant.")]),
|
||||
ChatMessage(role=Role.USER, contents=[Content.from_text(text="Hello")]),
|
||||
ChatMessage(role=Role.ASSISTANT, contents=[Content.from_text(text="System response")]),
|
||||
]
|
||||
|
||||
result_messages, instructions = client._prepare_messages_for_azure_ai(messages) # type: ignore
|
||||
@@ -318,8 +316,8 @@ async def test_prepare_messages_for_azure_ai_no_system_messages(
|
||||
client = create_test_azure_ai_client(mock_project_client)
|
||||
|
||||
messages = [
|
||||
ChatMessage(role=Role.USER, contents=[TextContent(text="Hello")]),
|
||||
ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text="Hi there!")]),
|
||||
ChatMessage(role=Role.USER, contents=[Content.from_text(text="Hello")]),
|
||||
ChatMessage(role=Role.ASSISTANT, contents=[Content.from_text(text="Hi there!")]),
|
||||
]
|
||||
|
||||
result_messages, instructions = client._prepare_messages_for_azure_ai(messages) # type: ignore
|
||||
@@ -419,7 +417,7 @@ async def test_prepare_options_basic(mock_project_client: MagicMock) -> None:
|
||||
"""Test prepare_options basic functionality."""
|
||||
client = create_test_azure_ai_client(mock_project_client, agent_name="test-agent", agent_version="1.0")
|
||||
|
||||
messages = [ChatMessage(role=Role.USER, contents=[TextContent(text="Hello")])]
|
||||
messages = [ChatMessage(role=Role.USER, contents=[Content.from_text(text="Hello")])]
|
||||
|
||||
with (
|
||||
patch.object(client.__class__.__bases__[0], "_prepare_options", return_value={"model": "test-model"}),
|
||||
@@ -453,7 +451,7 @@ async def test_prepare_options_with_application_endpoint(
|
||||
agent_version="1",
|
||||
)
|
||||
|
||||
messages = [ChatMessage(role=Role.USER, contents=[TextContent(text="Hello")])]
|
||||
messages = [ChatMessage(role=Role.USER, contents=[Content.from_text(text="Hello")])]
|
||||
|
||||
with (
|
||||
patch.object(client.__class__.__bases__[0], "_prepare_options", return_value={"model": "test-model"}),
|
||||
@@ -492,7 +490,7 @@ async def test_prepare_options_with_application_project_client(
|
||||
agent_version="1",
|
||||
)
|
||||
|
||||
messages = [ChatMessage(role=Role.USER, contents=[TextContent(text="Hello")])]
|
||||
messages = [ChatMessage(role=Role.USER, contents=[Content.from_text(text="Hello")])]
|
||||
|
||||
with (
|
||||
patch.object(client.__class__.__bases__[0], "_prepare_options", return_value={"model": "test-model"}),
|
||||
@@ -848,7 +846,7 @@ async def test_prepare_options_excludes_response_format(
|
||||
"""Test that prepare_options excludes response_format, text, and text_format from final run options."""
|
||||
client = create_test_azure_ai_client(mock_project_client, agent_name="test-agent", agent_version="1.0")
|
||||
|
||||
messages = [ChatMessage(role=Role.USER, contents=[TextContent(text="Hello")])]
|
||||
messages = [ChatMessage(role=Role.USER, contents=[Content.from_text(text="Hello")])]
|
||||
chat_options: ChatOptions = {}
|
||||
|
||||
with (
|
||||
@@ -992,7 +990,7 @@ def test_from_azure_ai_tools() -> None:
|
||||
|
||||
tool_input = parsed_tools[0].inputs[0]
|
||||
|
||||
assert tool_input and isinstance(tool_input, HostedFileContent) and tool_input.file_id == "file-1"
|
||||
assert tool_input and tool_input.type == "hosted_file" and tool_input.file_id == "file-1"
|
||||
|
||||
# Test File Search tool
|
||||
fs_tool = FileSearchTool(vector_store_ids=["vs-1"], max_num_results=5)
|
||||
@@ -1004,7 +1002,7 @@ def test_from_azure_ai_tools() -> None:
|
||||
|
||||
tool_input = parsed_tools[0].inputs[0]
|
||||
|
||||
assert tool_input and isinstance(tool_input, HostedVectorStoreContent) and tool_input.vector_store_id == "vs-1"
|
||||
assert tool_input and tool_input.type == "hosted_vector_store" and tool_input.vector_store_id == "vs-1"
|
||||
assert parsed_tools[0].max_results == 5
|
||||
|
||||
# Test Web Search tool
|
||||
|
||||
Reference in New Issue
Block a user