mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
OpenAI Responses Agent Completeness
This commit is contained in:
@@ -14,6 +14,9 @@ from agent_framework import (
|
||||
ChatResponse,
|
||||
ChatResponseUpdate,
|
||||
HostedCodeInterpreterTool,
|
||||
HostedFileSearchTool,
|
||||
HostedMCPTool,
|
||||
HostedVectorStoreContent,
|
||||
TextContent,
|
||||
ai_function,
|
||||
)
|
||||
@@ -45,6 +48,29 @@ async def get_weather(location: Annotated[str, "The location as a city name"]) -
|
||||
return f"The weather in {location} is sunny and 72°F."
|
||||
|
||||
|
||||
async def create_vector_store(client: AzureResponsesClient) -> tuple[str, HostedVectorStoreContent]:
|
||||
"""Create a vector store with sample documents for testing."""
|
||||
file = await client.client.files.create(
|
||||
file=("todays_weather.txt", b"The weather today is sunny with a high of 75F."), purpose="assistants"
|
||||
)
|
||||
vector_store = await client.client.vector_stores.create(
|
||||
name="knowledge_base",
|
||||
expires_after={"anchor": "last_active_at", "days": 1},
|
||||
)
|
||||
result = await client.client.vector_stores.files.create_and_poll(vector_store_id=vector_store.id, file_id=file.id)
|
||||
if result.last_error is not None:
|
||||
raise Exception(f"Vector store file processing failed with status: {result.last_error.message}")
|
||||
|
||||
return file.id, HostedVectorStoreContent(vector_store_id=vector_store.id)
|
||||
|
||||
|
||||
async def delete_vector_store(client: AzureResponsesClient, file_id: str, vector_store_id: str) -> None:
|
||||
"""Delete the vector store after tests."""
|
||||
|
||||
await client.client.vector_stores.delete(vector_store_id=vector_store_id)
|
||||
await client.client.files.delete(file_id=file_id)
|
||||
|
||||
|
||||
def test_init(azure_openai_unit_test_env: dict[str, str]) -> None:
|
||||
# Test successful initialization
|
||||
azure_responses_client = AzureResponsesClient()
|
||||
@@ -459,3 +485,138 @@ async def test_azure_responses_client_agent_level_tool_persistence():
|
||||
assert second_response.text is not None
|
||||
# Should use the agent-level weather tool again
|
||||
assert any(term in second_response.text.lower() for term in ["miami", "sunny", "72"])
|
||||
|
||||
|
||||
@skip_if_azure_integration_tests_disabled
|
||||
async def test_azure_responses_client_agent_chat_options_run_level() -> None:
|
||||
"""Integration test for comprehensive ChatOptions parameter coverage with Azure Response Agent."""
|
||||
async with ChatAgent(
|
||||
chat_client=AzureResponsesClient(),
|
||||
instructions="You are a helpful assistant.",
|
||||
) as agent:
|
||||
response = await agent.run(
|
||||
"Provide a brief, helpful response.",
|
||||
max_tokens=100,
|
||||
temperature=0.7,
|
||||
top_p=0.9,
|
||||
seed=123,
|
||||
user="comprehensive-test-user",
|
||||
tools=[get_weather],
|
||||
tool_choice="auto",
|
||||
)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
|
||||
|
||||
@skip_if_azure_integration_tests_disabled
|
||||
async def test_azure_responses_client_agent_chat_options_agent_level() -> None:
|
||||
"""Integration test for comprehensive ChatOptions parameter coverage with Azure Response Agent."""
|
||||
async with ChatAgent(
|
||||
chat_client=AzureResponsesClient(),
|
||||
instructions="You are a helpful assistant.",
|
||||
max_tokens=100,
|
||||
temperature=0.7,
|
||||
top_p=0.9,
|
||||
seed=123,
|
||||
user="comprehensive-test-user",
|
||||
tools=[get_weather],
|
||||
tool_choice="auto",
|
||||
) as agent:
|
||||
response = await agent.run(
|
||||
"Provide a brief, helpful response.",
|
||||
)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
|
||||
|
||||
@skip_if_azure_integration_tests_disabled
|
||||
async def test_azure_responses_client_agent_hosted_mcp_tool() -> None:
|
||||
"""Integration test for HostedMCPTool with Azure Response Agent using Microsoft Learn MCP."""
|
||||
# Use the same MCP server as the Foundry example
|
||||
mcp_tool = HostedMCPTool(
|
||||
name="Microsoft Learn MCP",
|
||||
url="https://learn.microsoft.com/api/mcp",
|
||||
description="A Microsoft Learn MCP server for documentation questions",
|
||||
approval_mode="never_require",
|
||||
)
|
||||
|
||||
async with ChatAgent(
|
||||
chat_client=AzureResponsesClient(),
|
||||
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
|
||||
tools=[mcp_tool],
|
||||
) as agent:
|
||||
# Use the same query as the Foundry example
|
||||
response = await agent.run(
|
||||
"How to create an Azure storage account using az cli?",
|
||||
max_tokens=200,
|
||||
)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
# Should contain Azure-related content since it's asking about Azure CLI
|
||||
assert any(term in response.text.lower() for term in ["azure", "storage", "account", "cli"])
|
||||
|
||||
|
||||
@skip_if_azure_integration_tests_disabled
|
||||
async def test_azure_responses_client_file_search() -> None:
|
||||
"""Test Azure responses client with file search tool."""
|
||||
azure_responses_client = AzureResponsesClient()
|
||||
|
||||
assert isinstance(azure_responses_client, ChatClientProtocol)
|
||||
|
||||
file_id, vector_store = await create_vector_store(azure_responses_client)
|
||||
# Test that the client will use the web search tool
|
||||
response = await azure_responses_client.get_response(
|
||||
messages=[
|
||||
ChatMessage(
|
||||
role="user",
|
||||
text="What is the weather today? Do a file search to find the answer.",
|
||||
)
|
||||
],
|
||||
tools=[HostedFileSearchTool(inputs=vector_store)],
|
||||
tool_choice="auto",
|
||||
)
|
||||
|
||||
await delete_vector_store(azure_responses_client, file_id, vector_store.vector_store_id)
|
||||
assert "sunny" in response.text.lower()
|
||||
assert "75" in response.text
|
||||
|
||||
|
||||
@skip_if_azure_integration_tests_disabled
|
||||
async def test_azure_responses_client_file_search_streaming() -> None:
|
||||
"""Test Azure responses client with file search tool and streaming."""
|
||||
azure_responses_client = AzureResponsesClient()
|
||||
|
||||
assert isinstance(azure_responses_client, ChatClientProtocol)
|
||||
|
||||
file_id, vector_store = await create_vector_store(azure_responses_client)
|
||||
# Test that the client will use the web search tool
|
||||
response = azure_responses_client.get_streaming_response(
|
||||
messages=[
|
||||
ChatMessage(
|
||||
role="user",
|
||||
text="What is the weather today? Do a file search to find the answer.",
|
||||
)
|
||||
],
|
||||
tools=[HostedFileSearchTool(inputs=vector_store)],
|
||||
tool_choice="auto",
|
||||
)
|
||||
|
||||
assert response is not None
|
||||
full_message: str = ""
|
||||
async for chunk in response:
|
||||
assert chunk is not None
|
||||
assert isinstance(chunk, ChatResponseUpdate)
|
||||
for content in chunk.contents:
|
||||
if isinstance(content, TextContent) and content.text:
|
||||
full_message += content.text
|
||||
|
||||
await delete_vector_store(azure_responses_client, file_id, vector_store.vector_store_id)
|
||||
|
||||
assert "sunny" in full_message.lower()
|
||||
assert "75" in full_message
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import sys
|
||||
from collections.abc import AsyncIterable, Callable, Mapping, MutableMapping, MutableSequence, Sequence
|
||||
from collections.abc import AsyncIterable, Mapping, MutableMapping, MutableSequence, Sequence
|
||||
from datetime import datetime
|
||||
from itertools import chain
|
||||
from typing import TYPE_CHECKING, Any, Literal, TypeVar
|
||||
from typing import TYPE_CHECKING, Any, TypeVar
|
||||
|
||||
from openai import AsyncOpenAI, BadRequestError
|
||||
from openai.types.responses.file_search_tool_param import FileSearchToolParam
|
||||
@@ -68,14 +68,12 @@ from ._exceptions import OpenAIContentFilterException
|
||||
from ._shared import OpenAIBase, OpenAIConfigMixin, OpenAISettings, prepare_function_call_results
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
from typing import override # type: ignore # pragma: no cover
|
||||
pass # type: ignore # pragma: no cover
|
||||
else:
|
||||
from typing_extensions import override # type: ignore[import] # pragma: no cover
|
||||
pass # type: ignore[import] # pragma: no cover
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from openai.types.responses.response_includable import ResponseIncludable
|
||||
|
||||
from .._types import ChatToolMode
|
||||
pass
|
||||
|
||||
|
||||
logger = get_logger("agent_framework.openai")
|
||||
@@ -90,192 +88,6 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient):
|
||||
|
||||
FILE_SEARCH_MAX_RESULTS: int = 50
|
||||
|
||||
def _filter_options(self, **kwargs: Any) -> dict[str, Any]:
|
||||
"""Filter options for the responses call."""
|
||||
# The responses call does not support all the options that the chat completion call does.
|
||||
# We filter out the unsupported options.
|
||||
return {key: value for key, value in kwargs.items() if value is not None}
|
||||
|
||||
@override
|
||||
async def get_response(
|
||||
self,
|
||||
messages: str | ChatMessage | list[str] | list[ChatMessage],
|
||||
*,
|
||||
include: list["ResponseIncludable"] | None = None,
|
||||
instructions: str | None = None,
|
||||
max_tokens: int | None = None,
|
||||
parallel_tool_calls: bool | None = None,
|
||||
model: str | None = None,
|
||||
previous_response_id: str | None = None,
|
||||
reasoning: dict[str, str] | None = None,
|
||||
service_tier: str | None = None,
|
||||
response_format: type[BaseModel] | None = None,
|
||||
seed: int | None = None,
|
||||
store: bool | None = None,
|
||||
temperature: float | None = None,
|
||||
tool_choice: "ChatToolMode" | Literal["auto", "required", "none"] | dict[str, Any] | None = "auto",
|
||||
tools: ToolProtocol
|
||||
| Callable[..., Any]
|
||||
| MutableMapping[str, Any]
|
||||
| list[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]]
|
||||
| None = None,
|
||||
top_p: float | None = None,
|
||||
user: str | None = None,
|
||||
truncation: str | None = None,
|
||||
timeout: float | None = None,
|
||||
additional_properties: dict[str, Any] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> ChatResponse:
|
||||
"""Get a response from the OpenAI API.
|
||||
|
||||
Args:
|
||||
messages: the message or messages to send to the model
|
||||
include: additional output data to include in the model response.
|
||||
instructions: a system (or developer) message inserted into the model's context.
|
||||
max_tokens: The maximum number of tokens to generate.
|
||||
parallel_tool_calls: Whether to enable parallel tool calls.
|
||||
model: The model to use for the agent.
|
||||
previous_response_id: The ID of the previous response.
|
||||
reasoning: The reasoning to use for the response.
|
||||
service_tier: The service tier to use for the response.
|
||||
response_format: The format of the response.
|
||||
seed: The random seed to use for the response.
|
||||
store: whether to store the response.
|
||||
temperature: the sampling temperature to use.
|
||||
tool_choice: the tool choice for the request.
|
||||
tools: the tools to use for the request.
|
||||
top_p: the nucleus sampling probability to use.
|
||||
user: the user to associate with the request.
|
||||
truncation: the truncation strategy to use.
|
||||
timeout: the timeout for the request.
|
||||
additional_properties: additional properties to include in the request.
|
||||
kwargs: any additional keyword arguments,
|
||||
will only be passed to functions that are called.
|
||||
|
||||
Returns:
|
||||
A chat response from the model.
|
||||
"""
|
||||
additional_properties = additional_properties or {}
|
||||
additional_properties.update(
|
||||
self._filter_options(
|
||||
include=include,
|
||||
instructions=instructions,
|
||||
parallel_tool_calls=parallel_tool_calls,
|
||||
model=model,
|
||||
previous_response_id=previous_response_id,
|
||||
reasoning=reasoning,
|
||||
service_tier=service_tier,
|
||||
truncation=truncation,
|
||||
timeout=timeout,
|
||||
)
|
||||
)
|
||||
|
||||
return await super().get_response(
|
||||
messages=messages,
|
||||
max_tokens=max_tokens,
|
||||
response_format=response_format,
|
||||
seed=seed,
|
||||
store=store,
|
||||
temperature=temperature,
|
||||
tool_choice=tool_choice,
|
||||
tools=tools, # type: ignore
|
||||
top_p=top_p,
|
||||
user=user,
|
||||
additional_properties=additional_properties,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@override
|
||||
async def get_streaming_response(
|
||||
self,
|
||||
messages: str | ChatMessage | list[str] | list[ChatMessage],
|
||||
*,
|
||||
# TODO(peterychang): enable this option. background: bool | None = None,
|
||||
include: list["ResponseIncludable"] | None = None,
|
||||
instructions: str | None = None,
|
||||
max_tokens: int | None = None,
|
||||
parallel_tool_calls: bool | None = None,
|
||||
model: str | None = None,
|
||||
previous_response_id: str | None = None,
|
||||
reasoning: dict[str, str] | None = None,
|
||||
service_tier: str | None = None,
|
||||
response_format: type[BaseModel] | None = None,
|
||||
seed: int | None = None,
|
||||
store: bool | None = None,
|
||||
temperature: float | None = None,
|
||||
tool_choice: "ChatToolMode" | Literal["auto", "required", "none"] | dict[str, Any] | None = "auto",
|
||||
tools: ToolProtocol
|
||||
| Callable[..., Any]
|
||||
| MutableMapping[str, Any]
|
||||
| list[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]]
|
||||
| None = None,
|
||||
top_p: float | None = None,
|
||||
user: str | None = None,
|
||||
truncation: str | None = None,
|
||||
timeout: float | None = None,
|
||||
additional_properties: dict[str, Any] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[ChatResponseUpdate]:
|
||||
"""Get a streaming response from the OpenAI API.
|
||||
|
||||
Args:
|
||||
messages: the message or messages to send to the model
|
||||
include: additional output data to include in the model response.
|
||||
instructions: a system (or developer) message inserted into the model's context.
|
||||
max_tokens: The maximum number of tokens to generate.
|
||||
parallel_tool_calls: Whether to enable parallel tool calls.
|
||||
model: The model to use for the agent.
|
||||
previous_response_id: The ID of the previous response.
|
||||
reasoning: The reasoning to use for the response.
|
||||
service_tier: The service tier to use for the response.
|
||||
response_format: The format of the response.
|
||||
seed: The random seed to use for the response.
|
||||
store: whether to store the response.
|
||||
temperature: the sampling temperature to use.
|
||||
tool_choice: the tool choice for the request.
|
||||
tools: the tools to use for the request.
|
||||
top_p: the nucleus sampling probability to use.
|
||||
user: the user to associate with the request.
|
||||
truncation: the truncation strategy to use.
|
||||
timeout: the timeout for the request.
|
||||
additional_properties: additional properties to include in the request.
|
||||
kwargs: any additional keyword arguments,
|
||||
will only be passed to functions that are called.
|
||||
|
||||
Returns:
|
||||
A stream representing the response(s) from the LLM.
|
||||
"""
|
||||
additional_properties = additional_properties or {}
|
||||
additional_properties.update(
|
||||
self._filter_options(
|
||||
include=include,
|
||||
instructions=instructions,
|
||||
parallel_tool_calls=parallel_tool_calls,
|
||||
model=model,
|
||||
previous_response_id=previous_response_id,
|
||||
reasoning=reasoning,
|
||||
service_tier=service_tier,
|
||||
truncation=truncation,
|
||||
timeout=timeout,
|
||||
)
|
||||
)
|
||||
|
||||
async for update in super().get_streaming_response(
|
||||
messages=messages,
|
||||
max_tokens=max_tokens,
|
||||
response_format=response_format,
|
||||
seed=seed,
|
||||
store=store,
|
||||
temperature=temperature,
|
||||
tool_choice=tool_choice,
|
||||
tools=tools, # type: ignore
|
||||
top_p=top_p,
|
||||
user=user,
|
||||
additional_properties=additional_properties,
|
||||
**kwargs,
|
||||
):
|
||||
yield update
|
||||
|
||||
# region Inner Methods
|
||||
|
||||
async def _inner_get_response(
|
||||
@@ -474,18 +286,33 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient):
|
||||
return response_tools
|
||||
|
||||
def _prepare_options(self, messages: MutableSequence[ChatMessage], chat_options: ChatOptions) -> dict[str, Any]:
|
||||
"""Take ChatOptions and create the specific options for Responses."""
|
||||
options_dict = chat_options.to_provider_settings(exclude={"response_format"})
|
||||
"""Take ChatOptions and create the specific options for Responses API."""
|
||||
options_dict: dict[str, Any] = {}
|
||||
|
||||
if chat_options.max_tokens is not None:
|
||||
options_dict["max_output_tokens"] = chat_options.max_tokens
|
||||
|
||||
if chat_options.temperature is not None:
|
||||
options_dict["temperature"] = chat_options.temperature
|
||||
|
||||
if chat_options.top_p is not None:
|
||||
options_dict["top_p"] = chat_options.top_p
|
||||
|
||||
if chat_options.user is not None:
|
||||
options_dict["user"] = chat_options.user
|
||||
|
||||
# messages
|
||||
request_input = self._prepare_chat_messages_for_request(messages)
|
||||
if not request_input:
|
||||
raise ServiceInvalidRequestError("Messages are required for chat completions")
|
||||
options_dict["input"] = request_input
|
||||
|
||||
# tools
|
||||
if chat_options.tools is None:
|
||||
options_dict.pop("parallel_tool_calls", None)
|
||||
else:
|
||||
options_dict["tools"] = self._tools_to_response_tools(chat_options.tools)
|
||||
|
||||
# other settings
|
||||
if "store" not in options_dict:
|
||||
options_dict["store"] = False
|
||||
|
||||
@@ -181,37 +181,6 @@ def test_serialize_with_org_id(openai_unit_test_env: dict[str, str]) -> None:
|
||||
assert "User-Agent" not in dumped_settings["default_headers"]
|
||||
|
||||
|
||||
def test_filter_options_method(openai_unit_test_env: dict[str, str]) -> None:
|
||||
"""Test that the _filter_options method filters out None values correctly."""
|
||||
client = OpenAIResponsesClient()
|
||||
|
||||
# Test with a mix of None and non-None values
|
||||
filtered = client._filter_options( # type: ignore
|
||||
include=["usage"],
|
||||
instructions="Test instruction",
|
||||
max_tokens=None,
|
||||
temperature=0.7,
|
||||
seed=None,
|
||||
model="test-model",
|
||||
store=True,
|
||||
top_p=None,
|
||||
)
|
||||
|
||||
# Should only contain non-None values
|
||||
expected = {
|
||||
"include": ["usage"],
|
||||
"instructions": "Test instruction",
|
||||
"temperature": 0.7,
|
||||
"model": "test-model",
|
||||
"store": True,
|
||||
}
|
||||
|
||||
assert filtered == expected
|
||||
assert "max_tokens" not in filtered
|
||||
assert "seed" not in filtered
|
||||
assert "top_p" not in filtered
|
||||
|
||||
|
||||
def test_get_response_with_invalid_input() -> None:
|
||||
"""Test get_response with invalid inputs to trigger exception handling."""
|
||||
|
||||
@@ -972,7 +941,7 @@ async def test_openai_responses_client_response_tools() -> None:
|
||||
|
||||
@skip_if_openai_integration_tests_disabled
|
||||
async def test_openai_responses_client_streaming() -> None:
|
||||
"""Test Azure OpenAI chat completion responses."""
|
||||
"""Test OpenAI chat completion responses."""
|
||||
openai_responses_client = OpenAIResponsesClient()
|
||||
|
||||
assert isinstance(openai_responses_client, ChatClientProtocol)
|
||||
@@ -1156,7 +1125,6 @@ async def test_openai_responses_client_web_search_streaming() -> None:
|
||||
|
||||
|
||||
@skip_if_openai_integration_tests_disabled
|
||||
@pytest.mark.skip(reason="OpenAI file search functionality is currently broken - tracked in GitHub issue")
|
||||
async def test_openai_responses_client_file_search() -> None:
|
||||
openai_responses_client = OpenAIResponsesClient()
|
||||
|
||||
@@ -1181,7 +1149,6 @@ async def test_openai_responses_client_file_search() -> None:
|
||||
|
||||
|
||||
@skip_if_openai_integration_tests_disabled
|
||||
@pytest.mark.skip(reason="OpenAI file search functionality is currently broken - tracked in GitHub issue")
|
||||
async def test_openai_responses_client_streaming_file_search() -> None:
|
||||
openai_responses_client = OpenAIResponsesClient()
|
||||
|
||||
@@ -1422,6 +1389,81 @@ async def test_openai_responses_client_run_level_tool_isolation():
|
||||
assert call_count == 1
|
||||
|
||||
|
||||
@skip_if_openai_integration_tests_disabled
|
||||
async def test_openai_responses_client_agent_chat_options_run_level() -> None:
|
||||
"""Integration test for comprehensive ChatOptions parameter coverage with OpenAI Response Agent."""
|
||||
async with ChatAgent(
|
||||
chat_client=OpenAIResponsesClient(),
|
||||
instructions="You are a helpful assistant.",
|
||||
) as agent:
|
||||
response = await agent.run(
|
||||
"Provide a brief, helpful response.",
|
||||
max_tokens=100,
|
||||
temperature=0.7,
|
||||
top_p=0.9,
|
||||
seed=123,
|
||||
user="comprehensive-test-user",
|
||||
tools=[get_weather],
|
||||
tool_choice="auto",
|
||||
)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
|
||||
|
||||
@skip_if_openai_integration_tests_disabled
|
||||
async def test_openai_responses_client_agent_chat_options_agent_level() -> None:
|
||||
"""Integration test for comprehensive ChatOptions parameter coverage with OpenAI Response Agent."""
|
||||
async with ChatAgent(
|
||||
chat_client=OpenAIResponsesClient(),
|
||||
instructions="You are a helpful assistant.",
|
||||
max_tokens=100,
|
||||
temperature=0.7,
|
||||
top_p=0.9,
|
||||
seed=123,
|
||||
user="comprehensive-test-user",
|
||||
tools=[get_weather],
|
||||
tool_choice="auto",
|
||||
) as agent:
|
||||
response = await agent.run(
|
||||
"Provide a brief, helpful response.",
|
||||
)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
|
||||
|
||||
@skip_if_openai_integration_tests_disabled
|
||||
async def test_openai_responses_client_agent_hosted_mcp_tool() -> None:
|
||||
"""Integration test for HostedMCPTool with OpenAI Response Agent using Microsoft Learn MCP."""
|
||||
# Use the same MCP server as the Foundry example
|
||||
mcp_tool = HostedMCPTool(
|
||||
name="Microsoft Learn MCP",
|
||||
url="https://learn.microsoft.com/api/mcp",
|
||||
description="A Microsoft Learn MCP server for documentation questions",
|
||||
approval_mode="never_require",
|
||||
)
|
||||
|
||||
async with ChatAgent(
|
||||
chat_client=OpenAIResponsesClient(),
|
||||
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
|
||||
tools=[mcp_tool],
|
||||
) as agent:
|
||||
# Use the same query as the Foundry example
|
||||
response = await agent.run(
|
||||
"How to create an Azure storage account using az cli?",
|
||||
max_tokens=200,
|
||||
)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
# Should contain Azure-related content since it's asking about Azure CLI
|
||||
assert any(term in response.text.lower() for term in ["azure", "storage", "account", "cli"])
|
||||
|
||||
|
||||
def test_service_response_exception_includes_original_error_details() -> None:
|
||||
"""Test that ServiceResponseException messages include original error details in the new format."""
|
||||
client = OpenAIResponsesClient(ai_model_id="test-model", api_key="test-key")
|
||||
|
||||
Reference in New Issue
Block a user