chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1,152 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from collections.abc import AsyncIterator, Awaitable, Callable
from typing import Any, Generic, Protocol, TypeVar
from semantic_kernel.agents.agent import Agent, AgentResponseItem, AgentThread
from semantic_kernel.contents import ChatMessageContent
DEFAULT_MAX_ATTEMPTS = 3
DEFAULT_BACKOFF_SECONDS = 1
class ChatResponseProtocol(Protocol):
"""Represents a single response item returned by the agent."""
@property
def message(self) -> ChatMessageContent: ...
@property
def thread(self) -> AgentThread | None: ...
class ChatAgentProtocol(Protocol):
"""Protocol describing the common agent interface used by the tests."""
async def get_response(
self, messages: str | list[str] | None, thread: object | None = None
) -> ChatResponseProtocol: ...
def invoke(
self, messages: str | list[str] | None, thread: object | None = None
) -> AsyncIterator[ChatResponseProtocol]: ...
def invoke_stream(
self, messages: str | list[str] | None, thread: object | None = None
) -> AsyncIterator[ChatResponseProtocol]: ...
TAgent = TypeVar("TAgent", bound=ChatAgentProtocol)
async def run_with_retry(
coro: Callable[..., Awaitable[Any]],
*args,
attempts: int = DEFAULT_MAX_ATTEMPTS,
backoff_seconds: float = DEFAULT_BACKOFF_SECONDS,
**kwargs,
) -> AgentResponseItem[ChatMessageContent]:
"""
Execute an async callable with retry/backoff logic.
Args:
coro: The async function to call
args: Positional args to pass to the function
attempts: How many times to attempt before giving up
backoff_seconds: The initial backoff in seconds, doubled after each failure
kwargs: Keyword args to pass to the function
Returns:
Whatever the async function returns
Raises:
Exception: If the function fails after the specified number of attempts
"""
delay = backoff_seconds
for attempt in range(1, attempts + 1):
try:
return await coro(*args, **kwargs)
except Exception:
if attempt == attempts:
raise
await asyncio.sleep(delay)
delay *= 2
raise RuntimeError("Unexpected error: run_with_retry exit.")
class AgentTestBase(Generic[TAgent]):
"""Common test base that wraps all agent invocation patterns with retry logic.
Each integration test can inherit from this or use its methods directly.
"""
async def get_response_with_retry(
self,
agent: Agent,
messages: str | list[str] | None,
thread: Any | None = None,
attempts: int = DEFAULT_MAX_ATTEMPTS,
backoff_seconds: float = DEFAULT_BACKOFF_SECONDS,
) -> AgentResponseItem[ChatMessageContent]:
"""Wraps agent.get_response(...) in run_with_retry."""
return await run_with_retry(
agent.get_response, messages=messages, thread=thread, attempts=attempts, backoff_seconds=backoff_seconds
)
async def get_invoke_with_retry(
self,
agent: Any,
messages: str | list[str] | None,
thread: Any | None = None,
attempts: int = DEFAULT_MAX_ATTEMPTS,
backoff_seconds: float = DEFAULT_BACKOFF_SECONDS,
) -> list[AgentResponseItem[ChatMessageContent]]:
"""Wraps agent.invoke(...) in run_with_retry.
Collects generator results in a list before returning them.
"""
return await run_with_retry(
self._collect_from_invoke,
agent,
messages,
thread=thread,
attempts=attempts,
backoff_seconds=backoff_seconds,
)
async def get_invoke_stream_with_retry(
self,
agent: Any,
messages: str | list[str] | None,
thread: Any | None = None,
attempts: int = DEFAULT_MAX_ATTEMPTS,
backoff_seconds: float = DEFAULT_BACKOFF_SECONDS,
) -> list[AgentResponseItem[ChatMessageContent]]:
"""Wraps agent.invoke_stream(...) in run_with_retry.
Collects streaming results in a list before returning them."""
return await run_with_retry(
self._collect_from_invoke_stream,
agent,
messages,
thread=thread,
attempts=attempts,
backoff_seconds=backoff_seconds,
)
async def _collect_from_invoke(
self, agent: Agent, messages: str | list[str] | None, thread: Any | None = None
) -> list[AgentResponseItem[ChatMessageContent]]:
results: list[AgentResponseItem[ChatMessageContent]] = []
async for response in agent.invoke(messages=messages, thread=thread):
results.append(response)
return results
async def _collect_from_invoke_stream(
self, agent: Agent, messages: str | list[str] | None, thread: Any | None = None
) -> list[AgentResponseItem[ChatMessageContent]]:
results: list[AgentResponseItem[ChatMessageContent]] = []
async for response in agent.invoke_stream(messages=messages, thread=thread):
results.append(response)
return results
@@ -0,0 +1,278 @@
# Copyright (c) Microsoft. All rights reserved.
import os
from typing import Annotated
import pytest
from azure.ai.agents.models import CodeInterpreterTool, FileInfo, FileSearchTool
from azure.identity.aio import AzureCliCredential
from semantic_kernel.agents import AzureAIAgent, AzureAIAgentSettings
from semantic_kernel.contents import AuthorRole, ChatMessageContent, StreamingChatMessageContent
from semantic_kernel.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.contents.text_content import TextContent
from semantic_kernel.functions import kernel_function
from tests.integration.agents.agent_test_base import AgentTestBase
class WeatherPlugin:
"""Mock weather plugin."""
@kernel_function(description="Get real-time weather information.")
def current_weather(self, location: Annotated[str, "The location to get the weather"]) -> str:
"""Returns the current weather."""
return f"The weather in {location} is sunny."
class TestAzureAIAgentIntegration:
@pytest.fixture
async def azureai_agent(self, request):
ai_agent_settings = AzureAIAgentSettings()
async with (
AzureCliCredential() as creds,
AzureAIAgent.create_client(credential=creds) as client,
):
tools, tool_resources, plugins = [], {}, []
params = getattr(request, "param", {})
if params.get("enable_code_interpreter"):
ci_tool = CodeInterpreterTool()
tools.extend(ci_tool.definitions)
tool_resources.update(ci_tool.resources)
if params.get("enable_file_search"):
pdf_file_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
"resources",
"employees.pdf",
)
file: FileInfo = await client.agents.files.upload_and_poll(
file_path=pdf_file_path, purpose="assistants"
)
vector_store = await client.agents.vector_stores.create_and_poll(
file_ids=[file.id], name="my_vectorstore"
)
fs_tool = FileSearchTool(vector_store_ids=[vector_store.id])
tools.extend(fs_tool.definitions)
tool_resources.update(fs_tool.resources)
if params.get("enable_kernel_function"):
plugins.append(WeatherPlugin())
agent_definition = await client.agents.create_agent(
model=ai_agent_settings.model_deployment_name,
tools=tools,
tool_resources=tool_resources,
name="SKPythonIntegrationTestAgent",
instructions="You are a helpful assistant that help users with their questions.",
)
azureai_agent = AzureAIAgent(
client=client,
definition=agent_definition,
plugins=plugins,
)
yield azureai_agent # yield agent for test method to use
# cleanup
await azureai_agent.client.agents.delete_agent(azureai_agent.id)
async def test_get_response(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test get response of the agent."""
response = await agent_test_base.get_response_with_retry(azureai_agent, messages="Hello")
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
assert "thread_id" in response.message.metadata
assert "run_id" in response.message.metadata
async def test_get_response_with_thread(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test get response of the agent with a thread."""
thread = None
user_messages = ["Hello, I am John Doe.", "What is my name?"]
for user_message in user_messages:
response = await agent_test_base.get_response_with_retry(
azureai_agent, messages=user_message, thread=thread
)
thread = response.thread
assert thread is not None
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
await thread.delete() if thread else None
async def test_invoke(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test invoke of the agent."""
responses = await agent_test_base.get_invoke_with_retry(azureai_agent, messages="Hello")
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
async def test_invoke_with_thread(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test invoke of the agent with a thread."""
thread = None
user_messages = ["Hello, I am John Doe.", "What is my name?"]
for user_message in user_messages:
responses = await agent_test_base.get_invoke_with_retry(azureai_agent, messages=user_message, thread=thread)
assert len(responses) > 0
for response in responses:
thread = response.thread
assert thread is not None
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
await thread.delete() if thread else None
async def test_invoke_stream(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test invoke stream of the agent."""
responses = await agent_test_base.get_invoke_stream_with_retry(azureai_agent, messages="Hello")
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.parametrize("azureai_agent", [{"enable_code_interpreter": True}], indirect=True)
async def test_invoke_stream_with_thread(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test invoke stream of the agent with a thread."""
thread = None
user_messages = ["Hello, I am John Doe.", "What is my name?"]
for user_message in user_messages:
responses = await agent_test_base.get_invoke_stream_with_retry(
azureai_agent, messages=user_message, thread=thread
)
assert len(responses) > 0
for response in responses:
thread = response.thread
assert thread is not None
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
await thread.delete() if thread else None
@pytest.mark.parametrize("azureai_agent", [{"enable_code_interpreter": True}], indirect=True)
async def test_code_interpreter_get_response(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test code interpreter."""
input_text = """
Using Python, sum the number of animals for the following data:
Panda 5
Tiger 8
Lion 3
Monkey 6
Dolphin 2
"""
response = await agent_test_base.get_response_with_retry(azureai_agent, messages=input_text)
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.parametrize("azureai_agent", [{"enable_code_interpreter": True}], indirect=True)
async def test_code_interpreter_invoke(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test code interpreter."""
input_text = """
Using Python, sum the number of animals for the following data:
Panda 5
Tiger 8
Lion 3
Monkey 6
Dolphin 2
"""
responses = await agent_test_base.get_invoke_with_retry(azureai_agent, messages=input_text)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.parametrize("azureai_agent", [{"enable_code_interpreter": True}], indirect=True)
async def test_code_interpreter_invoke_stream(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test code interpreter streaming."""
input_text = """
Using Python, sum the number of animals for the following data:
Panda 5
Tiger 8
Lion 3
Monkey 6
Dolphin 2
"""
responses = await agent_test_base.get_invoke_stream_with_retry(azureai_agent, messages=input_text)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.parametrize("azureai_agent", [{"enable_file_search": True}], indirect=True)
async def test_file_search_get_response(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test code interpreter."""
input_text = "Who is the youngest employee?"
response = await agent_test_base.get_response_with_retry(azureai_agent, messages=input_text)
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
@pytest.mark.parametrize("azureai_agent", [{"enable_file_search": True}], indirect=True)
async def test_file_search_invoke(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test code interpreter."""
input_text = "Who is the youngest employee?"
responses = await agent_test_base.get_invoke_with_retry(azureai_agent, messages=input_text)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
@pytest.mark.parametrize("azureai_agent", [{"enable_file_search": True}], indirect=True)
async def test_file_search_invoke_stream(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test code interpreter streaming."""
input_text = "Who is the youngest employee?"
responses = await agent_test_base.get_invoke_stream_with_retry(azureai_agent, messages=input_text)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
@pytest.mark.parametrize("azureai_agent", [{"enable_kernel_function": True}], indirect=True)
async def test_function_calling_get_response(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test function calling."""
response = await agent_test_base.get_response_with_retry(
azureai_agent,
messages="What is the weather in Seattle?",
)
assert isinstance(response.message, ChatMessageContent)
assert all(isinstance(item, TextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
assert "sunny" in response.message.content
@pytest.mark.parametrize("azureai_agent", [{"enable_kernel_function": True}], indirect=True)
async def test_function_calling_invoke(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test function calling."""
responses = await agent_test_base.get_invoke_with_retry(
azureai_agent,
messages="What is the weather in Seattle?",
)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert all(isinstance(item, TextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
assert "sunny" in response.message.content
@pytest.mark.parametrize("azureai_agent", [{"enable_kernel_function": True}], indirect=True)
async def test_function_calling_stream(self, azureai_agent: AzureAIAgent, agent_test_base: AgentTestBase):
"""Test function calling streaming."""
full_message: str = ""
responses = await agent_test_base.get_invoke_stream_with_retry(
azureai_agent, messages="What is the weather in Seattle?"
)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert all(isinstance(item, StreamingTextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
full_message += response.message.content
assert "sunny" in full_message
@@ -0,0 +1,25 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Annotated
import pytest
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.kernel import Kernel
class WeatherPlugin:
"""Mock weather plugin."""
@kernel_function(description="Get real-time weather information.")
def current(self, location: Annotated[str, "The location to get the weather"]) -> str:
"""Returns the current weather."""
return f"The weather in {location} is sunny."
@pytest.fixture
def kernel_with_dummy_function() -> Kernel:
kernel = Kernel()
kernel.add_plugin(WeatherPlugin(), plugin_name="weather")
return kernel
@@ -0,0 +1,141 @@
# Copyright (c) Microsoft. All rights reserved.
import uuid
import pytest
from semantic_kernel.agents.bedrock.bedrock_agent import BedrockAgent
from semantic_kernel.contents.binary_content import BinaryContent
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.streaming_chat_message_content import StreamingChatMessageContent
from semantic_kernel.contents.utils.author_role import AuthorRole
class TestBedrockAgentIntegration:
@pytest.fixture(autouse=True)
async def setup_and_teardown(self, request):
"""Setup and teardown for the test.
This is run for each test function, i.e. each test function will have its own instance of the agent.
"""
try:
self.bedrock_agent = await BedrockAgent.create_and_prepare_agent(
f"semantic-kernel-integration-test-agent-{uuid.uuid4()}",
"You are a helpful assistant that help users with their questions.",
)
if hasattr(request, "param"):
if "enable_code_interpreter" in request.param:
await self.bedrock_agent.create_code_interpreter_action_group()
if "kernel" in request.param:
self.bedrock_agent.kernel = request.getfixturevalue(request.param.get("kernel"))
if "enable_kernel_function" in request.param:
await self.bedrock_agent.create_kernel_function_action_group()
except Exception as e:
pytest.fail("Failed to create agent")
raise e
# Yield control to the test
yield
# Clean up
try:
await self.bedrock_agent.delete_agent()
except Exception as e:
pytest.fail(f"Failed to delete agent: {e}")
raise e
@pytest.mark.asyncio
async def test_invoke(self):
"""Test invoke of the agent."""
async for response in self.bedrock_agent.invoke(messages="Hello"):
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.asyncio
async def test_invoke_stream(self):
"""Test invoke stream of the agent."""
async for response in self.bedrock_agent.invoke_stream(messages="Hello"):
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.asyncio
@pytest.mark.parametrize("setup_and_teardown", [{"enable_code_interpreter": True}], indirect=True)
async def test_code_interpreter(self):
"""Test code interpreter."""
input_text = """
Create a bar chart for the following data:
Panda 5
Tiger 8
Lion 3
Monkey 6
Dolphin 2
"""
binary_item: BinaryContent | None = None
async for response in self.bedrock_agent.invoke(messages=input_text):
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
if not binary_item:
binary_item = next((item for item in response.message.items if isinstance(item, BinaryContent)), None)
assert binary_item
@pytest.mark.asyncio
@pytest.mark.parametrize("setup_and_teardown", [{"enable_code_interpreter": True}], indirect=True)
async def test_code_interpreter_stream(self):
"""Test code interpreter streaming."""
input_text = """
Create a bar chart for the following data:
Panda 5
Tiger 8
Lion 3
Monkey 6
Dolphin 2
"""
binary_item: BinaryContent | None = None
async for response in self.bedrock_agent.invoke_stream(messages=input_text):
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
binary_item = next((item for item in response.message.items if isinstance(item, BinaryContent)), None)
assert binary_item
@pytest.mark.asyncio
@pytest.mark.parametrize(
"setup_and_teardown",
[
{
"enable_kernel_function": True,
"kernel": "kernel_with_dummy_function",
},
],
indirect=True,
)
async def test_function_calling(self):
"""Test function calling."""
async for response in self.bedrock_agent.invoke(
messages="What is the weather in Seattle?",
):
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert "sunny" in response.message.content
@pytest.mark.asyncio
@pytest.mark.parametrize(
"setup_and_teardown",
[
{
"enable_kernel_function": True,
"kernel": "kernel_with_dummy_function",
},
],
indirect=True,
)
async def test_function_calling_stream(self):
"""Test function calling streaming."""
full_message: str = ""
async for response in self.bedrock_agent.invoke_stream(
messages="What is the weather in Seattle?",
):
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
full_message += response.message.content
assert "sunny" in full_message
@@ -0,0 +1,272 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Annotated
import pytest
from azure.identity import AzureCliCredential
from semantic_kernel.agents import ChatCompletionAgent
from semantic_kernel.connectors.ai.completion_usage import CompletionUsage
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, OpenAIChatCompletion
from semantic_kernel.contents import AuthorRole, ChatMessageContent, StreamingChatMessageContent
from semantic_kernel.contents.image_content import ImageContent
from semantic_kernel.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.contents.text_content import TextContent
from semantic_kernel.functions import kernel_function
from tests.integration.agents.agent_test_base import AgentTestBase
class WeatherPlugin:
"""A sample Mock weather plugin."""
@kernel_function(description="Get real-time weather information.")
def current_weather(self, location: Annotated[str, "The location to get the weather"]) -> str:
"""Returns the current weather."""
return f"The weather in {location} is sunny."
class TestChatCompletionAgentIntegration:
@pytest.fixture(params=["azure", "openai"])
async def chat_completion_agent(self, request):
raw_param = request.param
if isinstance(raw_param, str):
agent_service, params = raw_param, {}
elif isinstance(raw_param, tuple) and len(raw_param) == 2:
agent_service, params = raw_param
else:
raise ValueError(f"Unsupported param format: {raw_param}")
plugins = []
service = (
AzureChatCompletion(credential=AzureCliCredential()) if agent_service == "azure" else OpenAIChatCompletion()
)
if params.get("enable_kernel_function"):
plugins.append(WeatherPlugin())
agent = ChatCompletionAgent(
service=service,
name="SKPythonIntegrationTestChatCompletionAgent",
instructions="You are a helpful assistant that help users with their questions.",
plugins=plugins,
)
yield agent # yield agent for test method to use
# region Simple 'Hello' messages tests
@pytest.mark.parametrize("chat_completion_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_get_response(self, chat_completion_agent: ChatCompletionAgent, agent_test_base: AgentTestBase):
"""Test get response of the agent."""
response = await agent_test_base.get_response_with_retry(chat_completion_agent, messages="Hello")
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.parametrize("chat_completion_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_get_response_with_thread(
self, chat_completion_agent: ChatCompletionAgent, agent_test_base: AgentTestBase
):
"""Test get response of the agent with a thread."""
thread = None
user_messages = ["Hello, I am John Doe.", "What is my name?"]
for user_message in user_messages:
response = await agent_test_base.get_response_with_retry(
chat_completion_agent,
messages=user_message,
thread=thread,
)
thread = response.thread
assert thread is not None
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
await thread.delete() if thread else None
@pytest.mark.parametrize("chat_completion_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_invoke(self, chat_completion_agent: ChatCompletionAgent, agent_test_base: AgentTestBase):
"""Test invoke of the agent."""
responses = await agent_test_base.get_invoke_with_retry(chat_completion_agent, messages="Hello")
assert len(responses) > 0
usage: CompletionUsage = CompletionUsage()
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
if response.metadata.get("usage"):
usage += response.metadata["usage"]
assert usage.prompt_tokens > 0
assert usage.completion_tokens > 0
@pytest.mark.parametrize("chat_completion_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_invoke_with_thread(self, chat_completion_agent: ChatCompletionAgent, agent_test_base: AgentTestBase):
"""Test invoke of the agent with a thread."""
thread = None
user_messages = ["Hello, I am John Doe.", "What is my name?"]
for user_message in user_messages:
responses = await agent_test_base.get_invoke_with_retry(
chat_completion_agent, messages=user_message, thread=thread
)
assert len(responses) > 0
for response in responses:
thread = response.thread
assert thread is not None
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
await thread.delete() if thread else None
@pytest.mark.parametrize("chat_completion_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_invoke_stream(self, chat_completion_agent: ChatCompletionAgent, agent_test_base: AgentTestBase):
"""Test invoke stream of the agent."""
responses = await agent_test_base.get_invoke_stream_with_retry(chat_completion_agent, messages="Hello")
assert len(responses) > 0
usage: CompletionUsage = CompletionUsage()
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
if response.metadata.get("usage"):
usage += response.metadata["usage"]
assert usage.prompt_tokens > 0
assert usage.completion_tokens > 0
@pytest.mark.parametrize("chat_completion_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_invoke_stream_with_thread(
self, chat_completion_agent: ChatCompletionAgent, agent_test_base: AgentTestBase
):
"""Test invoke stream of the agent with a thread."""
thread = None
user_messages = ["Hello, I am John Doe.", "What is my name?"]
for user_message in user_messages:
responses = await agent_test_base.get_invoke_stream_with_retry(
chat_completion_agent, messages=user_message, thread=thread
)
assert len(responses) > 0
for response in responses:
thread = response.thread
assert thread is not None
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
await thread.delete() if thread else None
# endregion
# region Function calling tests
@pytest.mark.parametrize(
"chat_completion_agent",
[
("azure", {"enable_kernel_function": True}),
("openai", {"enable_kernel_function": True}),
],
indirect=["chat_completion_agent"],
ids=["azure-function-calling", "openai-function-calling"],
)
async def test_function_calling_get_response(
self, chat_completion_agent: ChatCompletionAgent, agent_test_base: AgentTestBase
):
"""Test function calling."""
response = await agent_test_base.get_response_with_retry(
chat_completion_agent,
messages="What is the weather in Seattle?",
)
assert isinstance(response.message, ChatMessageContent)
assert all(isinstance(item, TextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
assert "sunny" in response.message.content
@pytest.mark.parametrize(
"chat_completion_agent",
[
("azure", {"enable_kernel_function": True}),
("openai", {"enable_kernel_function": True}),
],
indirect=["chat_completion_agent"],
ids=["azure-function-calling", "openai-function-calling"],
)
async def test_function_calling_invoke(
self, chat_completion_agent: ChatCompletionAgent, agent_test_base: AgentTestBase
):
"""Test function calling."""
responses = await agent_test_base.get_invoke_with_retry(
chat_completion_agent,
messages="What is the weather in Seattle?",
)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert all(isinstance(item, TextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
assert "sunny" in response.message.content
@pytest.mark.parametrize(
"chat_completion_agent",
[
("azure", {"enable_kernel_function": True}),
("openai", {"enable_kernel_function": True}),
],
indirect=["chat_completion_agent"],
ids=["azure-function-calling", "openai-function-calling"],
)
async def test_function_calling_stream(
self, chat_completion_agent: ChatCompletionAgent, agent_test_base: AgentTestBase
):
"""Test function calling streaming."""
full_message: str = ""
responses = await agent_test_base.get_invoke_stream_with_retry(
chat_completion_agent, messages="What is the weather in Seattle?"
)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert all(isinstance(item, StreamingTextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
full_message += response.message.content
assert "sunny" in full_message
# endregion
# region Image Content tests
@pytest.mark.parametrize(
"chat_completion_agent",
[
("azure", {"enable_kernel_function": True}),
pytest.param(
("openai", {"enable_kernel_function": True}),
marks=pytest.mark.xfail(reason="OpenAI service raise error for downloading image from URL"),
),
],
indirect=["chat_completion_agent"],
ids=["azure-image-content-streaming", "openai-image-content-streaming"],
)
async def test_image_content_stream(
self, chat_completion_agent: ChatCompletionAgent, agent_test_base: AgentTestBase
):
"""Test function calling streaming."""
IMAGE_URI = (
"https://raw.githubusercontent.com/microsoft/semantic-kernel/main/python/tests/assets/sample_image.jpg"
)
image_content_remote = ImageContent(uri=IMAGE_URI)
full_message: str = ""
responses = await agent_test_base.get_invoke_stream_with_retry(
chat_completion_agent,
messages=ChatMessageContent(
role=AuthorRole.USER,
items=[TextContent(text="What is in this image?"), image_content_remote],
),
)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert all(isinstance(item, StreamingTextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
full_message += response.message.content
assert full_message is not None
@@ -0,0 +1,14 @@
# Copyright (c) Microsoft. All rights reserved.
import pytest
from tests.integration.agents.agent_test_base import AgentTestBase, ChatAgentProtocol
@pytest.fixture
def agent_test_base() -> AgentTestBase[ChatAgentProtocol]:
"""Provides a single AgentTestBase instance that all tests can use.
Typed as a Generic over any ChatAgentProtocol.
"""
return AgentTestBase()
@@ -0,0 +1,417 @@
# Copyright (c) Microsoft. All rights reserved.
import os
from typing import Annotated
import pytest
from azure.identity import AzureCliCredential
from semantic_kernel.agents import AzureAssistantAgent, OpenAIAssistantAgent
from semantic_kernel.connectors.ai.open_ai import AzureOpenAISettings, OpenAISettings
from semantic_kernel.contents import AuthorRole, ChatMessageContent, StreamingChatMessageContent
from semantic_kernel.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.contents.text_content import TextContent
from semantic_kernel.functions import kernel_function
from tests.integration.agents.agent_test_base import AgentTestBase
class WeatherPlugin:
"""A sample Mock weather plugin."""
@kernel_function(description="Get real-time weather information.")
def current_weather(self, location: Annotated[str, "The location to get the weather"]) -> str:
"""Returns the current weather."""
return f"The weather in {location} is sunny."
class TestOpenAIAssistantAgentIntegration:
@pytest.fixture(params=["azure", "openai"])
async def assistant_agent(self, request):
raw_param = request.param
if isinstance(raw_param, str):
agent_type, params = raw_param, {}
elif isinstance(raw_param, tuple) and len(raw_param) == 2:
agent_type, params = raw_param
else:
raise ValueError(f"Unsupported param format: {raw_param}")
tools, tool_resources, plugins = [], {}, []
if agent_type == "azure":
client = AzureAssistantAgent.create_client(credential=AzureCliCredential())
model = AzureOpenAISettings().chat_deployment_name
AgentClass = AzureAssistantAgent
else: # agent_type == "openai"
client = OpenAIAssistantAgent.create_client()
model = OpenAISettings().chat_model_id
AgentClass = OpenAIAssistantAgent
if params.get("enable_code_interpreter"):
code_interpreter_tool, code_interpreter_tool_resources = (
AzureAssistantAgent.configure_code_interpreter_tool()
if agent_type == "azure"
else OpenAIAssistantAgent.configure_code_interpreter_tool()
)
tools.extend(code_interpreter_tool)
tool_resources.update(code_interpreter_tool_resources)
if params.get("enable_file_search"):
pdf_file_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "resources", "employees.pdf"
)
with open(pdf_file_path, "rb") as file:
file = await client.files.create(file=file, purpose="assistants")
vector_store = await client.vector_stores.create(
name="assistant_file_search_int_tests",
file_ids=[file.id],
)
file_search_tool, file_search_tool_resources = (
AzureAssistantAgent.configure_file_search_tool(vector_store.id)
if agent_type == "azure"
else OpenAIAssistantAgent.configure_file_search_tool(vector_store.id)
)
tools.extend(file_search_tool)
tool_resources.update(file_search_tool_resources)
if params.get("enable_kernel_function"):
plugins.append(WeatherPlugin())
definition = await client.beta.assistants.create(
model=model,
tools=tools,
tool_resources=tool_resources,
name="SKPythonIntegrationTestAssistantAgent",
instructions="You are a helpful assistant that help users with their questions.",
)
agent = AgentClass(
client=client,
definition=definition,
plugins=plugins,
)
yield agent # yield agent for test method to use
# cleanup
await client.beta.assistants.delete(agent.id)
# region Simple 'Hello' messages tests
@pytest.mark.parametrize("assistant_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_get_response(self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase):
"""Test get response of the agent."""
response = await agent_test_base.get_response_with_retry(assistant_agent, messages="Hello")
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
assert "thread_id" in response.message.metadata
assert "run_id" in response.message.metadata
@pytest.mark.parametrize("assistant_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_get_response_with_thread(
self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase
):
"""Test get response of the agent with a thread."""
thread = None
user_messages = ["Hello, I am John Doe.", "What is my name?"]
for user_message in user_messages:
response = await agent_test_base.get_response_with_retry(
assistant_agent, messages=user_message, thread=thread
)
thread = response.thread
assert thread is not None
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
await thread.delete() if thread else None
@pytest.mark.parametrize("assistant_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_invoke(self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase):
"""Test invoke of the agent."""
responses = await agent_test_base.get_invoke_with_retry(assistant_agent, messages="Hello")
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.parametrize("assistant_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_invoke_with_thread(self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase):
"""Test invoke of the agent with a thread."""
thread = None
user_messages = ["Hello, I am John Doe.", "What is my name?"]
for user_message in user_messages:
responses = await agent_test_base.get_invoke_with_retry(
assistant_agent, messages=user_message, thread=thread
)
assert len(responses) > 0
for response in responses:
thread = response.thread
assert thread is not None
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
await thread.delete() if thread else None
@pytest.mark.parametrize("assistant_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_invoke_stream(self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase):
"""Test invoke stream of the agent."""
responses = await agent_test_base.get_invoke_stream_with_retry(assistant_agent, messages="Hello")
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.parametrize("assistant_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_invoke_stream_with_thread(
self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase
):
"""Test invoke stream of the agent with a thread."""
thread = None
user_messages = ["Hello, I am John Doe.", "What is my name?"]
for user_message in user_messages:
responses = await agent_test_base.get_invoke_stream_with_retry(
assistant_agent, messages=user_message, thread=thread
)
assert len(responses) > 0
for response in responses:
thread = response.thread
assert thread is not None
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
await thread.delete() if thread else None
# endregion
# region Code interpreter tests
@pytest.mark.parametrize(
"assistant_agent",
[
pytest.param(
("azure", {"enable_code_interpreter": True}), marks=pytest.mark.xfail(reason="Service outage")
),
pytest.param(
("openai", {"enable_code_interpreter": True}),
),
],
indirect=["assistant_agent"],
ids=["azure-code-interpreter", "openai-code-interpreter"],
)
async def test_code_interpreter_get_response(
self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase
):
"""Test code interpreter."""
input_text = """
Create a bar chart for the following data:
Panda 5
Tiger 8
Lion 3
Monkey 6
Dolphin 2
"""
response = await agent_test_base.get_response_with_retry(assistant_agent, messages=input_text)
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.parametrize(
"assistant_agent",
[
pytest.param(
("azure", {"enable_code_interpreter": True}), marks=pytest.mark.xfail(reason="Service outage")
),
pytest.param(
("openai", {"enable_code_interpreter": True}),
),
],
indirect=["assistant_agent"],
ids=["azure-code-interpreter", "openai-code-interpreter"],
)
async def test_code_interpreter_invoke(self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase):
"""Test code interpreter."""
input_text = """
Create a bar chart for the following data:
Panda 5
Tiger 8
Lion 3
Monkey 6
Dolphin 2
"""
responses = await agent_test_base.get_invoke_with_retry(assistant_agent, messages=input_text)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.parametrize(
"assistant_agent",
[
pytest.param(
("azure", {"enable_code_interpreter": True}), marks=pytest.mark.xfail(reason="Service outage")
),
pytest.param(
("openai", {"enable_code_interpreter": True}),
),
],
indirect=["assistant_agent"],
ids=["azure-code-interpreter", "openai-code-interpreter"],
)
async def test_code_interpreter_invoke_stream(
self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase
):
"""Test code interpreter streaming."""
input_text = """
Create a bar chart for the following data:
Panda 5
Tiger 8
Lion 3
Monkey 6
Dolphin 2
"""
responses = await agent_test_base.get_invoke_stream_with_retry(assistant_agent, messages=input_text)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
# endregion
# region File search tests
@pytest.mark.parametrize(
"assistant_agent",
[
("azure", {"enable_file_search": True}),
("openai", {"enable_file_search": True}),
],
indirect=["assistant_agent"],
ids=["azure-file-search", "openai-file-search"],
)
async def test_file_search_get_response(
self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase
):
"""Test code interpreter."""
input_text = "Who is the youngest employee?"
response = await agent_test_base.get_response_with_retry(assistant_agent, messages=input_text)
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
@pytest.mark.parametrize(
"assistant_agent",
[
("azure", {"enable_file_search": True}),
("openai", {"enable_file_search": True}),
],
indirect=["assistant_agent"],
ids=["azure-file-search", "openai-file-search"],
)
async def test_file_search_invoke(self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase):
"""Test code interpreter."""
input_text = "Who is the youngest employee?"
responses = await agent_test_base.get_invoke_with_retry(assistant_agent, messages=input_text)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
@pytest.mark.parametrize(
"assistant_agent",
[
("azure", {"enable_file_search": True}),
("openai", {"enable_file_search": True}),
],
indirect=["assistant_agent"],
ids=["azure-file-search", "openai-file-search"],
)
async def test_file_search_invoke_stream(
self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase
):
"""Test code interpreter streaming."""
input_text = "Who is the youngest employee?"
responses = await agent_test_base.get_invoke_stream_with_retry(assistant_agent, messages=input_text)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
# endregion
# region Function calling tests
@pytest.mark.parametrize(
"assistant_agent",
[
("azure", {"enable_kernel_function": True}),
("openai", {"enable_kernel_function": True}),
],
indirect=["assistant_agent"],
ids=["azure-function-calling", "openai-function-calling"],
)
async def test_function_calling_get_response(
self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase
):
"""Test function calling."""
response = await agent_test_base.get_response_with_retry(
assistant_agent,
messages="What is the weather in Seattle?",
)
assert isinstance(response.message, ChatMessageContent)
assert all(isinstance(item, TextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
assert "sunny" in response.message.content
@pytest.mark.parametrize(
"assistant_agent",
[
("azure", {"enable_kernel_function": True}),
("openai", {"enable_kernel_function": True}),
],
indirect=["assistant_agent"],
ids=["azure-function-calling", "openai-function-calling"],
)
async def test_function_calling_invoke(self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase):
"""Test function calling."""
responses = await agent_test_base.get_invoke_with_retry(
assistant_agent,
messages="What is the weather in Seattle?",
)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert all(isinstance(item, TextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
assert "sunny" in response.message.content
@pytest.mark.parametrize(
"assistant_agent",
[
("azure", {"enable_kernel_function": True}),
("openai", {"enable_kernel_function": True}),
],
indirect=["assistant_agent"],
ids=["azure-function-calling", "openai-function-calling"],
)
async def test_function_calling_stream(self, assistant_agent: OpenAIAssistantAgent, agent_test_base: AgentTestBase):
"""Test function calling streaming."""
full_message: str = ""
responses = await agent_test_base.get_invoke_stream_with_retry(
assistant_agent, messages="What is the weather in Seattle?"
)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert all(isinstance(item, StreamingTextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
full_message += response.message.content
assert "sunny" in full_message
# endregion
@@ -0,0 +1,420 @@
# Copyright (c) Microsoft. All rights reserved.
import os
from typing import Annotated
import pytest
from azure.identity import AzureCliCredential
from pydantic import BaseModel
from semantic_kernel.agents import AzureResponsesAgent, OpenAIResponsesAgent
from semantic_kernel.connectors.ai.open_ai import AzureOpenAISettings, OpenAISettings
from semantic_kernel.contents import AuthorRole, ChatMessageContent, StreamingChatMessageContent
from semantic_kernel.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.contents.text_content import TextContent
from semantic_kernel.functions import kernel_function
from tests.integration.agents.agent_test_base import AgentTestBase
class WeatherPlugin:
"""A sample Mock weather plugin."""
@kernel_function(description="Get real-time weather information.")
def current_weather(self, location: Annotated[str, "The location to get the weather"]) -> str:
"""Returns the current weather."""
return f"The weather in {location} is sunny."
class Step(BaseModel):
explanation: str
output: str
class Reasoning(BaseModel):
steps: list[Step]
final_answer: str
class TestOpenAIResponsesAgentIntegration:
@pytest.fixture(params=["azure", "openai"])
async def responses_agent(self, request):
raw_param = request.param
if isinstance(raw_param, str):
agent_type, params = raw_param, {}
elif isinstance(raw_param, tuple) and len(raw_param) == 2:
agent_type, params = raw_param
else:
raise ValueError(f"Unsupported param format: {raw_param}")
tools, plugins, text = [], [], None
if agent_type == "azure":
client = AzureResponsesAgent.create_client(credential=AzureCliCredential())
model = AzureOpenAISettings().chat_deployment_name
AgentClass = AzureResponsesAgent
else: # agent_type == "openai"
client = OpenAIResponsesAgent.create_client()
model = OpenAISettings().chat_model_id
AgentClass = OpenAIResponsesAgent
if params.get("enable_web_search"):
web_search_tool = OpenAIResponsesAgent.configure_web_search_tool()
tools.append(web_search_tool)
if params.get("enable_structured_outputs"):
text = OpenAIResponsesAgent.configure_response_format(Reasoning)
if params.get("enable_file_search"):
pdf_file_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "resources", "employees.pdf"
)
with open(pdf_file_path, "rb") as file:
file = await client.files.create(file=file, purpose="assistants")
vector_store = await client.vector_stores.create(
name="responses_file_search_int_tests",
file_ids=[file.id],
)
file_search_tool = (
AzureResponsesAgent.configure_file_search_tool(vector_store.id)
if agent_type == "azure"
else OpenAIResponsesAgent.configure_file_search_tool(vector_store.id)
)
tools.append(file_search_tool)
if params.get("enable_kernel_function"):
plugins.append(WeatherPlugin())
agent = AgentClass(
ai_model_id=model,
client=client,
name="SKPythonIntegrationTestResponsesAgent",
instructions="You are a helpful agent that help users with their questions.",
plugins=plugins,
tools=tools,
text=text,
)
yield agent # yield agent for test method to use
# region Simple 'Hello' messages tests
@pytest.mark.parametrize("responses_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_get_response(self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase):
"""Test get response of the agent."""
response = await agent_test_base.get_response_with_retry(responses_agent, messages="Hello")
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
assert "thread_id" in response.message.metadata
@pytest.mark.parametrize("responses_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_get_response_with_thread(
self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase
):
"""Test get response of the agent with a thread."""
thread = None
user_messages = ["Hello, I am John Doe.", "What is my name?"]
for user_message in user_messages:
response = await agent_test_base.get_response_with_retry(
responses_agent, messages=user_message, thread=thread
)
thread = response.thread
assert thread is not None
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
await thread.delete() if thread else None
@pytest.mark.parametrize("responses_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_invoke(self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase):
"""Test invoke of the agent."""
async for response in responses_agent.invoke(messages="Hello"):
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.parametrize("responses_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_invoke_with_thread(self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase):
"""Test invoke of the agent with a thread."""
thread = None
user_messages = ["Hello, I am John Doe.", "What is my name?"]
for user_message in user_messages:
responses = await agent_test_base.get_invoke_with_retry(
responses_agent, messages=user_message, thread=thread
)
assert len(responses) > 0
for response in responses:
thread = response.thread
assert thread is not None
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
await thread.delete() if thread else None
@pytest.mark.parametrize("responses_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_invoke_stream(self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase):
"""Test invoke stream of the agent."""
responses = await agent_test_base.get_invoke_stream_with_retry(responses_agent, messages="Hello")
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
@pytest.mark.parametrize("responses_agent", ["azure", "openai"], indirect=True, ids=["azure", "openai"])
async def test_invoke_stream_with_thread(
self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase
):
"""Test invoke stream of the agent with a thread."""
thread = None
user_messages = ["Hello, I am John Doe.", "What is my name?"]
for user_message in user_messages:
responses = await agent_test_base.get_invoke_stream_with_retry(
responses_agent, messages=user_message, thread=thread
)
assert len(responses) > 0
for response in responses:
thread = response.thread
assert thread is not None
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
await thread.delete() if thread else None
# endregion
# region Web Search tests
@pytest.mark.parametrize(
"responses_agent",
[
# Azure OpenAI Responses API doesn't yet support the web search tool
("openai", {"enable_web_search": True}),
],
indirect=["responses_agent"],
ids=["openai-web-search-get-response"],
)
@pytest.mark.xfail(reason="The Responses API is unstable when using the web search tool.")
async def test_web_search_get_response(self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase):
"""Test code interpreter."""
input_text = "Find articles about the latest AI trends."
response = await responses_agent.get_response(messages=input_text)
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert response.message.content is not None
# endregion
# region File search tests
@pytest.mark.parametrize(
"responses_agent",
[
("azure", {"enable_file_search": True}),
("openai", {"enable_file_search": True}),
],
indirect=["responses_agent"],
ids=["azure-file-search-get-response", "openai-file-search-get-response"],
)
@pytest.mark.xfail(reason="The Responses API is unstable and is throwing 500s.")
async def test_file_search_get_response(
self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase
):
"""Test code interpreter."""
input_text = "Who is the youngest employee?"
response = await agent_test_base.get_response_with_retry(responses_agent, messages=input_text)
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
@pytest.mark.parametrize(
"responses_agent",
[
("azure", {"enable_file_search": True}),
("openai", {"enable_file_search": True}),
],
indirect=["responses_agent"],
ids=["azure-file-search-invoke", "openai-file-search-invoke"],
)
@pytest.mark.xfail(reason="The Responses API is unstable and is throwing 500s.")
async def test_file_search_invoke(self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase):
"""Test code interpreter."""
input_text = "Who is the youngest employee?"
responses = await agent_test_base.get_invoke_with_retry(responses_agent, messages=input_text)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
@pytest.mark.parametrize(
"responses_agent",
[
("azure", {"enable_file_search": True}),
("openai", {"enable_file_search": True}),
],
indirect=["responses_agent"],
ids=["azure-file-search-invoke-stream", "openai-file-search-invoke-stream"],
)
@pytest.mark.xfail(reason="The Responses API is unstable and is throwing 500s.")
async def test_file_search_invoke_stream(
self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase
):
"""Test code interpreter streaming."""
input_text = "Who is the youngest employee?"
responses = await agent_test_base.get_invoke_stream_with_retry(responses_agent, messages=input_text)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
# endregion
# region Function calling tests
@pytest.mark.parametrize(
"responses_agent",
[
("azure", {"enable_kernel_function": True}),
("openai", {"enable_kernel_function": True}),
],
indirect=["responses_agent"],
ids=["azure-function-calling-get-response", "openai-function-calling-get-response"],
)
async def test_function_calling_get_response(
self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase
):
"""Test function calling."""
response = await agent_test_base.get_response_with_retry(
responses_agent,
messages="What is the weather in Seattle?",
)
assert isinstance(response.message, ChatMessageContent)
assert all(isinstance(item, TextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
assert "sunny" in response.message.content
@pytest.mark.parametrize(
"responses_agent",
[
("azure", {"enable_kernel_function": True}),
("openai", {"enable_kernel_function": True}),
],
indirect=["responses_agent"],
ids=["azure-function-calling-invoke", "openai-function-calling-invoke"],
)
async def test_function_calling_invoke(self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase):
"""Test function calling."""
responses = await agent_test_base.get_invoke_with_retry(
responses_agent,
messages="What is the weather in Seattle?",
)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert all(isinstance(item, TextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
assert "sunny" in response.message.content
@pytest.mark.parametrize(
"responses_agent",
[
("azure", {"enable_kernel_function": True}),
("openai", {"enable_kernel_function": True}),
],
indirect=["responses_agent"],
ids=["azure-function-calling-invoke-stream", "openai-function-calling-invoke-stream"],
)
async def test_function_calling_stream(self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase):
"""Test function calling streaming."""
full_message: str = ""
responses = await agent_test_base.get_invoke_stream_with_retry(
responses_agent, messages="What is the weather in Seattle?"
)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert all(isinstance(item, StreamingTextContent) for item in response.items)
assert response.message.role == AuthorRole.ASSISTANT
full_message += response.message.content
assert "sunny" in full_message
# endregion
# region Structured Outputs
@pytest.mark.parametrize(
"responses_agent",
[
("azure", {"enable_structured_outputs": True}),
("openai", {"enable_structured_outputs": True}),
],
indirect=["responses_agent"],
ids=["azure-structured-outputs-get-response", "openai-structured-outputs-get-response"],
)
@pytest.mark.xfail(reason="The Responses API is unstable when configuring structured outputs.")
async def test_structured_outputs_get_response(
self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase
):
"""Test structured outputs get response."""
response = await agent_test_base.get_response_with_retry(
responses_agent,
messages="how can I solve 8x + 7y = -23, and 4x=12?",
)
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert Reasoning.model_validate_json(response.message.content)
@pytest.mark.parametrize(
"responses_agent",
[
("azure", {"enable_structured_outputs": True}),
("openai", {"enable_structured_outputs": True}),
],
indirect=["responses_agent"],
ids=["azure-structured-outputs-invoke", "openai-structured-outputs-invoke"],
)
@pytest.mark.xfail(reason="The Responses API is unstable when configuring structured outputs.")
async def test_structured_outputs_invoke(
self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase
):
"""Test structured outputs invoke."""
responses = await agent_test_base.get_invoke_with_retry(
responses_agent,
messages="how can I solve 8x + 7y = -23, and 4x=12?",
)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, ChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
assert Reasoning.model_validate_json(response.message.content)
@pytest.mark.parametrize(
"responses_agent",
[
("azure", {"enable_structured_outputs": True}),
("openai", {"enable_structured_outputs": True}),
],
indirect=["responses_agent"],
ids=["azure-structured-outputs-invoke-stream", "openai-structured-outputs-invoke-stream"],
)
@pytest.mark.xfail(reason="The Responses API is unstable when configuring structured outputs.")
async def test_structured_outputs_stream(
self, responses_agent: OpenAIResponsesAgent, agent_test_base: AgentTestBase
):
"""Test structured outputs streaming."""
full_message: str = ""
responses = await agent_test_base.get_invoke_stream_with_retry(
responses_agent,
messages="how can I solve 8x + 7y = -23, and 4x=12?",
)
assert len(responses) > 0
for response in responses:
assert isinstance(response.message, StreamingChatMessageContent)
assert response.message.role == AuthorRole.ASSISTANT
full_message += response.message.content
assert Reasoning.model_validate_json(full_message)
# endregion