Python: [BREAKING] Simplify API: ChatAgent -> Agent, ChatMessage -> Message (#3747)

* [BREAKING] Rename ChatAgent -> Agent, ChatMessage -> Message, ChatClientProtocol -> SupportsChatGetResponse

Simplify the public API by removing redundant 'Chat' prefix from core types:
- ChatAgent -> Agent
- RawChatAgent -> RawAgent
- ChatMessage -> Message
- ChatClientProtocol -> SupportsChatGetResponse

Also renamed internal WorkflowMessage (was Message in _runner_context) to avoid collision.

No backward compatibility aliases - this is a clean breaking change.

* [BREAKING] Rename Agent chat_client parameter to client

* Fix rebase issues: WorkflowMessage references and broken markdown links

* Fix formatting and lint issues from code quality checks

* Fix import ordering in workflow sample files

* fixed rebase

* Fix test failures: use WorkflowMessage and A2AMessage after ChatMessage→Message rename

- Replace Message(data=..., source_id=...) with WorkflowMessage(...) in workflow tests
- Fix isinstance check in A2A agent to use A2AMessage instead of Message
- Fix import in test_workflow_observability.py (Message→WorkflowMessage)

* Fix lint, fmt, and sample errors after ChatMessage→Message rename

- Auto-fix 70+ ruff lint issues across samples (ChatMessage→Message refs)
- Fix HostedVectorStoreContent→Content.from_hosted_vector_store in file search sample
- Fix _normalize_messages→normalize_messages in custom agent sample
- Fix context.terminate→raise MiddlewareTermination in middleware samples
- Fix with_update_hook→with_transform_hook in override middleware sample
- Add TOptions_co import back to custom_chat_client sample
- Add noqa for FastAPI File() default in chatkit sample
- Fix B023 loop variable capture in weather agent sample

* fix: update Agent constructor calls from chat_client to client in declaration-only tool tests

* fix: add register_cleanup to devui lazy-loading proxy and type stub

* fixed tests and updated new pieces

* fix agui typevar

* fix merge errors

* fix merge conflicts

* fiux merge

* Remove unused links

---------

Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
This commit is contained in:
Eduard van Valkenburg
2026-02-10 23:04:32 +00:00
committed by GitHub
co-authored by Evan Mattson
parent a4c9e43afb
commit 0521f5bed8
418 changed files with 5385 additions and 5389 deletions
@@ -4,11 +4,11 @@ from __future__ import annotations
import sys
from collections.abc import Callable, MutableMapping, Sequence
from typing import TYPE_CHECKING, Any, Generic, cast
from typing import Any, Generic, cast
from agent_framework import (
AGENT_FRAMEWORK_USER_AGENT,
ChatAgent,
Agent,
ContextProvider,
FunctionTool,
MiddlewareTypes,
@@ -18,16 +18,14 @@ from agent_framework import (
from agent_framework._mcp import MCPTool
from agent_framework.exceptions import ServiceInitializationError
from azure.ai.agents.aio import AgentsClient
from azure.ai.agents.models import Agent, ResponseFormatJsonSchema, ResponseFormatJsonSchemaType
from azure.ai.agents.models import Agent as AzureAgent
from azure.ai.agents.models import ResponseFormatJsonSchema, ResponseFormatJsonSchemaType
from azure.core.credentials_async import AsyncTokenCredential
from pydantic import BaseModel, ValidationError
from ._chat_client import AzureAIAgentClient
from ._chat_client import AzureAIAgentClient, AzureAIAgentOptions
from ._shared import AzureAISettings, from_azure_ai_agent_tools, to_azure_ai_agent_tools
if TYPE_CHECKING:
from ._chat_client import AzureAIAgentOptions
if sys.version_info >= (3, 13):
from typing import Self, TypeVar # type: ignore # pragma: no cover
else:
@@ -38,7 +36,7 @@ else:
from typing_extensions import TypedDict # type: ignore # pragma: no cover
# Type variable for options - allows typed ChatAgent[OptionsCoT] returns
# Type variable for options - allows typed Agent[TOptions] returns
# Default matches AzureAIAgentClient's default options type
OptionsCoT = TypeVar(
"OptionsCoT",
@@ -51,7 +49,7 @@ OptionsCoT = TypeVar(
class AzureAIAgentsProvider(Generic[OptionsCoT]):
"""Provider for Azure AI Agent Service V1 (Persistent Agents API).
This provider enables creating, retrieving, and wrapping Azure AI agents as ChatAgent
This provider enables creating, retrieving, and wrapping Azure AI agents as Agent
instances. It manages the underlying AgentsClient lifecycle and provides a high-level
interface for agent operations.
@@ -179,11 +177,11 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
default_options: OptionsCoT | None = None,
middleware: Sequence[MiddlewareTypes] | None = None,
context_provider: ContextProvider | None = None,
) -> ChatAgent[OptionsCoT]:
"""Create a new agent on the Azure AI service and return a ChatAgent.
) -> Agent[OptionsCoT]:
"""Create a new agent on the Azure AI service and return a Agent.
This method creates a persistent agent on the Azure AI service with the specified
configuration and returns a local ChatAgent instance for interaction.
configuration and returns a local Agent instance for interaction.
Args:
name: The name for the agent.
@@ -200,7 +198,7 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
context_provider: Context provider to include during agent invocation.
Returns:
ChatAgent: A ChatAgent instance configured with the created agent.
Agent: A Agent instance configured with the created agent.
Raises:
ServiceInitializationError: If model deployment name is not available.
@@ -240,7 +238,7 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
args["response_format"] = self._create_response_format_config(response_format)
# Normalize and convert tools
# Local MCP tools (MCPTool) are handled by ChatAgent at runtime, not stored on the Azure agent
# Local MCP tools (MCPTool) are handled by Agent at runtime, not stored on the Azure agent
normalized_tools = normalize_tools(tools)
if normalized_tools:
# Only convert non-MCP tools to Azure AI format
@@ -255,7 +253,7 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
# Create the agent on the service
created_agent = await self._agents_client.create_agent(**args)
# Create ChatAgent wrapper
# Create Agent wrapper
return self._to_chat_agent_from_agent(
created_agent,
normalized_tools,
@@ -276,11 +274,11 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
default_options: OptionsCoT | None = None,
middleware: Sequence[MiddlewareTypes] | None = None,
context_provider: ContextProvider | None = None,
) -> ChatAgent[OptionsCoT]:
"""Retrieve an existing agent from the service and return a ChatAgent.
) -> Agent[OptionsCoT]:
"""Retrieve an existing agent from the service and return a Agent.
This method fetches an agent by ID from the Azure AI service
and returns a local ChatAgent instance for interaction.
and returns a local Agent instance for interaction.
Args:
id: The ID of the agent to retrieve from the service.
@@ -294,7 +292,7 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
context_provider: Context provider to include during agent invocation.
Returns:
ChatAgent: A ChatAgent instance configured with the retrieved agent.
Agent: A Agent instance configured with the retrieved agent.
Raises:
ServiceInitializationError: If required function tools are not provided.
@@ -323,7 +321,7 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
def as_agent(
self,
agent: Agent,
agent: AzureAgent,
tools: ToolProtocol
| Callable[..., Any]
| MutableMapping[str, Any]
@@ -332,8 +330,8 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
default_options: OptionsCoT | None = None,
middleware: Sequence[MiddlewareTypes] | None = None,
context_provider: ContextProvider | None = None,
) -> ChatAgent[OptionsCoT]:
"""Wrap an existing Agent SDK object as a ChatAgent without making HTTP calls.
) -> Agent[OptionsCoT]:
"""Wrap an existing Agent SDK object as a Agent without making HTTP calls.
Use this method when you already have an Agent object from a previous
SDK operation and want to use it with the Agent Framework.
@@ -348,7 +346,7 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
context_provider: Context provider to include during agent invocation.
Returns:
ChatAgent: A ChatAgent instance configured with the agent.
Agent: A Agent instance configured with the agent.
Raises:
ServiceInitializationError: If required function tools are not provided.
@@ -363,7 +361,7 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
instructions="...",
)
# Wrap as ChatAgent
# Wrap as Agent
chat_agent = provider.as_agent(sdk_agent)
"""
# Validate function tools
@@ -380,13 +378,13 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
def _to_chat_agent_from_agent(
self,
agent: Agent,
agent: AzureAgent,
provided_tools: Sequence[ToolProtocol | MutableMapping[str, Any]] | None = None,
default_options: OptionsCoT | None = None,
middleware: Sequence[MiddlewareTypes] | None = None,
context_provider: ContextProvider | None = None,
) -> ChatAgent[OptionsCoT]:
"""Create a ChatAgent from an Agent SDK object.
) -> Agent[OptionsCoT]:
"""Create a Agent from an Agent SDK object.
Args:
agent: The Agent SDK object.
@@ -408,8 +406,8 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
# Merge tools: convert agent's hosted tools + user-provided function tools
merged_tools = self._merge_tools(agent.tools, provided_tools)
return ChatAgent( # type: ignore[return-value]
chat_client=client,
return Agent( # type: ignore[return-value]
client=client,
id=agent.id,
name=agent.name,
description=agent.description,
@@ -433,7 +431,7 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
provided_tools: User-provided tools (Agent Framework format).
Returns:
Combined list of tools for the ChatAgent.
Combined list of tools for the Agent.
"""
merged: list[ToolProtocol | dict[str, Any]] = []
@@ -452,7 +450,7 @@ class AzureAIAgentsProvider(Generic[OptionsCoT]):
if provided_tools:
for provided_tool in provided_tools:
# FunctionTool - has implementation for function calling
# MCPTool - ChatAgent handles MCP connection and tool discovery at runtime
# MCPTool - Agent handles MCP connection and tool discovery at runtime
if isinstance(provided_tool, (FunctionTool, MCPTool)):
merged.append(provided_tool) # type: ignore[reportUnknownArgumentType]
@@ -12,11 +12,10 @@ from typing import Any, ClassVar, Generic, TypedDict
from agent_framework import (
AGENT_FRAMEWORK_USER_AGENT,
Agent,
Annotation,
BaseChatClient,
ChatAgent,
ChatAndFunctionMiddlewareTypes,
ChatMessage,
ChatMessageStoreProtocol,
ChatMiddlewareLayer,
ChatOptions,
@@ -31,6 +30,7 @@ from agent_framework import (
HostedFileSearchTool,
HostedMCPTool,
HostedWebSearchTool,
Message,
MiddlewareTypes,
ResponseStream,
Role,
@@ -44,7 +44,9 @@ from agent_framework.exceptions import ServiceInitializationError, ServiceInvali
from agent_framework.observability import ChatTelemetryLayer
from azure.ai.agents.aio import AgentsClient
from azure.ai.agents.models import (
Agent,
Agent as AzureAgent,
)
from azure.ai.agents.models import (
AgentsNamedToolChoice,
AgentsNamedToolChoiceType,
AgentsToolChoiceOptionMode,
@@ -346,7 +348,7 @@ class AzureAIAgentClient(
self.should_cleanup_agent = should_cleanup_agent # Track whether we should delete the agent
self._agent_created = False # Track whether agent was created inside this class
self._should_close_client = should_close_client # Track whether we should close client connection
self._agent_definition: Agent | None = None # Cached definition for existing agent
self._agent_definition: AzureAgent | None = None # Cached definition for existing agent
async def __aenter__(self) -> Self:
"""Async context manager entry."""
@@ -365,7 +367,7 @@ class AzureAIAgentClient(
def _inner_get_response(
self,
*,
messages: Sequence[ChatMessage],
messages: Sequence[Message],
options: Mapping[str, Any],
stream: bool = False,
**kwargs: Any,
@@ -898,7 +900,7 @@ class AzureAIAgentClient(
self.agent_id = None
self._agent_created = False
async def _load_agent_definition_if_needed(self) -> Agent | None:
async def _load_agent_definition_if_needed(self) -> AzureAgent | None:
"""Load and cache agent details if not already loaded."""
if self._agent_definition is None and self.agent_id is not None:
self._agent_definition = await self.agents_client.get_agent(self.agent_id)
@@ -906,7 +908,7 @@ class AzureAIAgentClient(
async def _prepare_options(
self,
messages: Sequence[ChatMessage],
messages: Sequence[Message],
options: Mapping[str, Any],
**kwargs: Any,
) -> tuple[dict[str, Any], list[Content] | None]:
@@ -1020,7 +1022,7 @@ class AzureAIAgentClient(
async def _prepare_tool_definitions_and_resources(
self,
options: Mapping[str, Any],
agent_definition: Agent | None,
agent_definition: AzureAgent | None,
run_options: dict[str, Any],
) -> list[ToolDefinition | dict[str, Any]]:
"""Prepare tool definitions and resources for the run options."""
@@ -1084,7 +1086,7 @@ class AzureAIAgentClient(
return mcp_resources
def _prepare_messages(
self, messages: Sequence[ChatMessage]
self, messages: Sequence[Message]
) -> tuple[
list[ThreadMessageOptions] | None,
list[str],
@@ -1301,10 +1303,10 @@ class AzureAIAgentClient(
context_provider: ContextProvider | None = None,
middleware: Sequence[MiddlewareTypes] | None = None,
**kwargs: Any,
) -> ChatAgent[AzureAIAgentOptionsT]:
"""Convert this chat client to a ChatAgent.
) -> Agent[AzureAIAgentOptionsT]:
"""Convert this chat client to a Agent.
This method creates a ChatAgent instance with this client pre-configured.
This method creates a Agent instance with this client pre-configured.
It does NOT create an agent on the Azure AI service - the actual agent
will be created on the server during the first invocation (run).
@@ -1324,7 +1326,7 @@ class AzureAIAgentClient(
kwargs: Any additional keyword arguments.
Returns:
A ChatAgent instance configured with this chat client.
A Agent instance configured with this chat client.
"""
return super().as_agent(
id=id,
@@ -8,15 +8,15 @@ from typing import Any, ClassVar, Generic, TypedDict, TypeVar, cast
from agent_framework import (
AGENT_FRAMEWORK_USER_AGENT,
ChatAgent,
Agent,
ChatAndFunctionMiddlewareTypes,
ChatMessage,
ChatMessageStoreProtocol,
ChatMiddlewareLayer,
ContextProvider,
FunctionInvocationConfiguration,
FunctionInvocationLayer,
HostedMCPTool,
Message,
MiddlewareTypes,
ToolProtocol,
get_logger,
@@ -329,7 +329,7 @@ class RawAzureAIClient(RawOpenAIResponsesClient[AzureAIClientOptionsT], Generic[
if self.agent_name is None:
raise ServiceInitializationError(
"Agent name is required. Provide 'agent_name' when initializing AzureAIClient "
"or 'name' when initializing ChatAgent."
"or 'name' when initializing Agent."
)
# If no agent_version is provided, either use latest version or create a new agent:
@@ -396,7 +396,7 @@ class RawAzureAIClient(RawOpenAIResponsesClient[AzureAIClientOptionsT], Generic[
@override
async def _prepare_options(
self,
messages: Sequence[ChatMessage],
messages: Sequence[Message],
options: Mapping[str, Any],
**kwargs: Any,
) -> dict[str, Any]:
@@ -489,9 +489,9 @@ class RawAzureAIClient(RawOpenAIResponsesClient[AzureAIClientOptionsT], Generic[
"""Get the current conversation ID from chat options or kwargs."""
return options.get("conversation_id") or kwargs.get("conversation_id") or self.conversation_id
def _prepare_messages_for_azure_ai(self, messages: Sequence[ChatMessage]) -> tuple[list[ChatMessage], str | None]:
def _prepare_messages_for_azure_ai(self, messages: Sequence[Message]) -> tuple[list[Message], str | None]:
"""Prepare input from messages and convert system/developer messages to instructions."""
result: list[ChatMessage] = []
result: list[Message] = []
instructions_list: list[str] = []
instructions: str | None = None
@@ -575,10 +575,10 @@ class RawAzureAIClient(RawOpenAIResponsesClient[AzureAIClientOptionsT], Generic[
context_provider: ContextProvider | None = None,
middleware: Sequence[MiddlewareTypes] | None = None,
**kwargs: Any,
) -> ChatAgent[AzureAIClientOptionsT]:
"""Convert this chat client to a ChatAgent.
) -> Agent[AzureAIClientOptionsT]:
"""Convert this chat client to a Agent.
This method creates a ChatAgent instance with this client pre-configured.
This method creates a Agent instance with this client pre-configured.
It does NOT create an agent on the Azure AI service - the actual agent
will be created on the server during the first invocation (run).
@@ -598,7 +598,7 @@ class RawAzureAIClient(RawOpenAIResponsesClient[AzureAIClientOptionsT], Generic[
kwargs: Any additional keyword arguments.
Returns:
A ChatAgent instance configured with this chat client.
A Agent instance configured with this chat client.
"""
return super().as_agent(
id=id,
@@ -8,7 +8,7 @@ from typing import Any, Generic
from agent_framework import (
AGENT_FRAMEWORK_USER_AGENT,
ChatAgent,
Agent,
ContextProvider,
FunctionTool,
MiddlewareTypes,
@@ -47,7 +47,7 @@ else:
logger = get_logger("agent_framework.azure")
# Type variable for options - allows typed ChatAgent[OptionsT] returns
# Type variable for options - allows typed Agent[OptionsT] returns
# Default matches AzureAIClient's default options type
OptionsCoT = TypeVar(
"OptionsCoT",
@@ -170,8 +170,8 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
default_options: OptionsCoT | None = None,
middleware: Sequence[MiddlewareTypes] | None = None,
context_provider: ContextProvider | None = None,
) -> ChatAgent[OptionsCoT]:
"""Create a new agent on the Azure AI service and return a local ChatAgent wrapper.
) -> Agent[OptionsCoT]:
"""Create a new agent on the Azure AI service and return a local Agent wrapper.
Args:
name: The name of the agent to create.
@@ -186,7 +186,7 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
context_provider: Context provider to include during agent invocation.
Returns:
ChatAgent: A ChatAgent instance configured with the created agent.
Agent: A Agent instance configured with the created agent.
Raises:
ServiceInitializationError: If required parameters are missing.
@@ -272,8 +272,8 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
default_options: OptionsCoT | None = None,
middleware: Sequence[MiddlewareTypes] | None = None,
context_provider: ContextProvider | None = None,
) -> ChatAgent[OptionsCoT]:
"""Retrieve an existing agent from the Azure AI service and return a local ChatAgent wrapper.
) -> Agent[OptionsCoT]:
"""Retrieve an existing agent from the Azure AI service and return a local Agent wrapper.
You must provide either name or reference. Use `as_agent()` if you already have
AgentVersionDetails and want to avoid an async call.
@@ -288,7 +288,7 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
context_provider: Context provider to include during agent invocation.
Returns:
ChatAgent: A ChatAgent instance configured with the retrieved agent.
Agent: A Agent instance configured with the retrieved agent.
Raises:
ValueError: If no identifier is provided or required tools are missing.
@@ -308,7 +308,7 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
raise ValueError("Either name or reference must be provided to get an agent.")
if not isinstance(existing_agent.definition, PromptAgentDefinition):
raise ValueError("Agent definition must be PromptAgentDefinition to get a ChatAgent.")
raise ValueError("Agent definition must be PromptAgentDefinition to get a Agent.")
# Validate that required function tools are provided
self._validate_function_tools(existing_agent.definition.tools, tools)
@@ -332,8 +332,8 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
default_options: OptionsCoT | None = None,
middleware: Sequence[MiddlewareTypes] | None = None,
context_provider: ContextProvider | None = None,
) -> ChatAgent[OptionsCoT]:
"""Wrap an SDK agent version object into a ChatAgent without making HTTP calls.
) -> Agent[OptionsCoT]:
"""Wrap an SDK agent version object into a Agent without making HTTP calls.
Use this when you already have an AgentVersionDetails from a previous API call.
@@ -346,13 +346,13 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
context_provider: Context provider to include during agent invocation.
Returns:
ChatAgent: A ChatAgent instance configured with the agent version.
Agent: A Agent instance configured with the agent version.
Raises:
ValueError: If the agent definition is not a PromptAgentDefinition or required tools are missing.
"""
if not isinstance(details.definition, PromptAgentDefinition):
raise ValueError("Agent definition must be PromptAgentDefinition to create a ChatAgent.")
raise ValueError("Agent definition must be PromptAgentDefinition to create a Agent.")
# Validate that required function tools are provided
self._validate_function_tools(details.definition.tools, tools)
@@ -372,8 +372,8 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
default_options: OptionsCoT | None = None,
middleware: Sequence[MiddlewareTypes] | None = None,
context_provider: ContextProvider | None = None,
) -> ChatAgent[OptionsCoT]:
"""Create a ChatAgent from an AgentVersionDetails.
) -> Agent[OptionsCoT]:
"""Create a Agent from an AgentVersionDetails.
Args:
details: The AgentVersionDetails containing the agent definition.
@@ -385,7 +385,7 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
context_provider: Context provider to include during agent invocation.
"""
if not isinstance(details.definition, PromptAgentDefinition):
raise ValueError("Agent definition must be PromptAgentDefinition to get a ChatAgent.")
raise ValueError("Agent definition must be PromptAgentDefinition to get a Agent.")
client = AzureAIClient(
project_client=self._project_client,
@@ -400,8 +400,8 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
# but function tools need the actual implementations from provided_tools
merged_tools = self._merge_tools(details.definition.tools, provided_tools)
return ChatAgent( # type: ignore[return-value]
chat_client=client,
return Agent( # type: ignore[return-value]
client=client,
id=details.id,
name=details.name,
description=details.description,
@@ -425,7 +425,7 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
provided_tools: User-provided tools (Agent Framework format), including function implementations.
Returns:
Combined list of tools for the ChatAgent.
Combined list of tools for the Agent.
"""
merged: list[ToolProtocol | dict[str, Any]] = []
@@ -442,7 +442,7 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
if provided_tools:
for provided_tool in provided_tools:
# FunctionTool - has implementation for function calling
# MCPTool - ChatAgent handles MCP connection and tool discovery at runtime
# MCPTool - Agent handles MCP connection and tool discovery at runtime
if isinstance(provided_tool, (FunctionTool, MCPTool)):
merged.append(provided_tool) # type: ignore[reportUnknownArgumentType]
@@ -6,7 +6,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from agent_framework import (
ChatAgent,
Agent,
Content,
HostedCodeInterpreterTool,
HostedFileSearchTool,
@@ -16,7 +16,9 @@ from agent_framework import (
)
from agent_framework.exceptions import ServiceInitializationError
from azure.ai.agents.models import (
Agent,
Agent as AzureAgent,
)
from azure.ai.agents.models import (
CodeInterpreterToolDefinition,
)
from azure.identity.aio import AzureCliCredential
@@ -156,7 +158,7 @@ async def test_create_agent_basic(
mock_agents_client: MagicMock,
) -> None:
"""Test creating a basic agent."""
mock_agent = MagicMock(spec=Agent)
mock_agent = MagicMock(spec=AzureAgent)
mock_agent.id = "test-agent-id"
mock_agent.name = "TestAgent"
mock_agent.description = "A test agent"
@@ -175,7 +177,7 @@ async def test_create_agent_basic(
description="A test agent",
)
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
assert agent.name == "TestAgent"
assert agent.id == "test-agent-id"
mock_agents_client.create_agent.assert_called_once()
@@ -186,7 +188,7 @@ async def test_create_agent_with_model(
mock_agents_client: MagicMock,
) -> None:
"""Test creating an agent with explicit model."""
mock_agent = MagicMock(spec=Agent)
mock_agent = MagicMock(spec=AzureAgent)
mock_agent.id = "test-agent-id"
mock_agent.name = "TestAgent"
mock_agent.description = None
@@ -210,7 +212,7 @@ async def test_create_agent_with_tools(
mock_agents_client: MagicMock,
) -> None:
"""Test creating an agent with tools."""
mock_agent = MagicMock(spec=Agent)
mock_agent = MagicMock(spec=AzureAgent)
mock_agent.id = "test-agent-id"
mock_agent.name = "TestAgent"
mock_agent.description = None
@@ -245,7 +247,7 @@ async def test_create_agent_with_response_format(
temperature: float
description: str
mock_agent = MagicMock(spec=Agent)
mock_agent = MagicMock(spec=AzureAgent)
mock_agent.id = "test-agent-id"
mock_agent.name = "TestAgent"
mock_agent.description = None
@@ -297,7 +299,7 @@ async def test_get_agent_by_id(
mock_agents_client: MagicMock,
) -> None:
"""Test getting an agent by ID."""
mock_agent = MagicMock(spec=Agent)
mock_agent = MagicMock(spec=AzureAgent)
mock_agent.id = "existing-agent-id"
mock_agent.name = "ExistingAgent"
mock_agent.description = "An existing agent"
@@ -312,7 +314,7 @@ async def test_get_agent_by_id(
agent = await provider.get_agent("existing-agent-id")
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
assert agent.id == "existing-agent-id"
mock_agents_client.get_agent.assert_called_once_with("existing-agent-id")
@@ -327,7 +329,7 @@ async def test_get_agent_with_function_tools(
mock_function_tool.function = MagicMock()
mock_function_tool.function.name = "get_weather"
mock_agent = MagicMock(spec=Agent)
mock_agent = MagicMock(spec=AzureAgent)
mock_agent.id = "agent-with-tools"
mock_agent.name = "AgentWithTools"
mock_agent.description = None
@@ -356,7 +358,7 @@ async def test_get_agent_with_provided_function_tools(
mock_function_tool.function = MagicMock()
mock_function_tool.function.name = "get_weather"
mock_agent = MagicMock(spec=Agent)
mock_agent = MagicMock(spec=AzureAgent)
mock_agent.id = "agent-with-tools"
mock_agent.name = "AgentWithTools"
mock_agent.description = None
@@ -376,7 +378,7 @@ async def test_get_agent_with_provided_function_tools(
agent = await provider.get_agent("agent-with-tools", tools=get_weather)
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
assert agent.id == "agent-with-tools"
@@ -391,7 +393,7 @@ def test_as_agent_wraps_without_http(
mock_agents_client: MagicMock,
) -> None:
"""Test as_agent wraps Agent object without making HTTP calls."""
mock_agent = MagicMock(spec=Agent)
mock_agent = MagicMock(spec=AzureAgent)
mock_agent.id = "wrap-agent-id"
mock_agent.name = "WrapAgent"
mock_agent.description = "Wrapped agent"
@@ -405,7 +407,7 @@ def test_as_agent_wraps_without_http(
agent = provider.as_agent(mock_agent)
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
assert agent.id == "wrap-agent-id"
assert agent.name == "WrapAgent"
# Ensure no HTTP calls were made
@@ -423,7 +425,7 @@ def test_as_agent_with_function_tools_validates(
mock_function_tool.function = MagicMock()
mock_function_tool.function.name = "my_function"
mock_agent = MagicMock(spec=Agent)
mock_agent = MagicMock(spec=AzureAgent)
mock_agent.id = "agent-id"
mock_agent.name = "Agent"
mock_agent.description = None
@@ -449,7 +451,7 @@ def test_as_agent_with_hosted_tools(
mock_code_interpreter = MagicMock()
mock_code_interpreter.type = "code_interpreter"
mock_agent = MagicMock(spec=Agent)
mock_agent = MagicMock(spec=AzureAgent)
mock_agent.id = "agent-id"
mock_agent.name = "Agent"
mock_agent.description = None
@@ -463,7 +465,7 @@ def test_as_agent_with_hosted_tools(
agent = provider.as_agent(mock_agent)
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
# Should have HostedCodeInterpreterTool in the default_options tools
assert any(isinstance(t, HostedCodeInterpreterTool) for t in (agent.default_options.get("tools") or [])) # type: ignore
@@ -483,7 +485,7 @@ def test_as_agent_with_dict_function_tools_validates(
},
}
mock_agent = MagicMock(spec=Agent)
mock_agent = MagicMock(spec=AzureAgent)
mock_agent.id = "agent-id"
mock_agent.name = "Agent"
mock_agent.description = None
@@ -515,7 +517,7 @@ def test_as_agent_with_dict_function_tools_provided(
},
}
mock_agent = MagicMock(spec=Agent)
mock_agent = MagicMock(spec=AzureAgent)
mock_agent.id = "agent-id"
mock_agent.name = "Agent"
mock_agent.description = None
@@ -534,7 +536,7 @@ def test_as_agent_with_dict_function_tools_provided(
agent = provider.as_agent(mock_agent, tools=dict_based_function)
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
assert agent.id == "agent-id"
@@ -810,7 +812,7 @@ async def test_integration_create_agent() -> None:
)
try:
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
assert agent.name == "IntegrationTestAgent"
assert agent.id is not None
finally:
@@ -837,7 +839,7 @@ async def test_integration_get_agent() -> None:
# Then get it using the provider
agent = await provider.get_agent(created.id)
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
assert agent.id == created.id
finally:
await provider._agents_client.delete_agent(created.id) # type: ignore
File diff suppressed because it is too large Load Diff
@@ -11,10 +11,8 @@ from uuid import uuid4
import pytest
from agent_framework import (
Agent,
AgentResponse,
ChatAgent,
ChatClientProtocol,
ChatMessage,
ChatOptions,
ChatResponse,
Content,
@@ -22,6 +20,8 @@ from agent_framework import (
HostedFileSearchTool,
HostedMCPTool,
HostedWebSearchTool,
Message,
SupportsChatGetResponse,
tool,
)
from agent_framework.exceptions import ServiceInitializationError
@@ -88,19 +88,19 @@ async def temporary_chat_client(agent_name: str) -> AsyncIterator[AzureAIClient]
"""Async context manager that creates an Azure AI agent and yields an `AzureAIClient`.
The underlying agent version is cleaned up automatically after use.
Tests can construct their own `ChatAgent` instances from the yielded client.
Tests can construct their own `Agent` instances from the yielded client.
"""
endpoint = os.environ["AZURE_AI_PROJECT_ENDPOINT"]
async with (
AzureCliCredential() as credential,
AIProjectClient(endpoint=endpoint, credential=credential) as project_client,
):
chat_client = AzureAIClient(
client = AzureAIClient(
project_client=project_client,
agent_name=agent_name,
)
try:
yield chat_client
yield client
finally:
await project_client.agents.delete(agent_name=agent_name)
@@ -179,7 +179,7 @@ def test_init_with_project_client(mock_project_client: MagicMock) -> None:
assert client.agent_name == "test-agent"
assert client.agent_version == "1.0"
assert not client._should_close_client # type: ignore
assert isinstance(client, ChatClientProtocol)
assert isinstance(client, SupportsChatGetResponse)
def test_init_auto_create_client(
@@ -298,9 +298,9 @@ async def test_prepare_messages_for_azure_ai_with_system_messages(
client = create_test_azure_ai_client(mock_project_client)
messages = [
ChatMessage(role="system", contents=[Content.from_text(text="You are a helpful assistant.")]),
ChatMessage(role="user", contents=[Content.from_text(text="Hello")]),
ChatMessage(role="assistant", contents=[Content.from_text(text="System response")]),
Message(role="system", contents=[Content.from_text(text="You are a helpful assistant.")]),
Message(role="user", contents=[Content.from_text(text="Hello")]),
Message(role="assistant", contents=[Content.from_text(text="System response")]),
]
result_messages, instructions = client._prepare_messages_for_azure_ai(messages) # type: ignore
@@ -318,8 +318,8 @@ async def test_prepare_messages_for_azure_ai_no_system_messages(
client = create_test_azure_ai_client(mock_project_client)
messages = [
ChatMessage(role="user", contents=[Content.from_text(text="Hello")]),
ChatMessage(role="assistant", contents=[Content.from_text(text="Hi there!")]),
Message(role="user", contents=[Content.from_text(text="Hello")]),
Message(role="assistant", contents=[Content.from_text(text="Hi there!")]),
]
result_messages, instructions = client._prepare_messages_for_azure_ai(messages) # type: ignore
@@ -419,7 +419,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="user", contents=[Content.from_text(text="Hello")])]
messages = [Message(role="user", contents=[Content.from_text(text="Hello")])]
with (
patch(
@@ -456,7 +456,7 @@ async def test_prepare_options_with_application_endpoint(
agent_version="1",
)
messages = [ChatMessage(role="user", contents=[Content.from_text(text="Hello")])]
messages = [Message(role="user", contents=[Content.from_text(text="Hello")])]
with (
patch(
@@ -498,7 +498,7 @@ async def test_prepare_options_with_application_project_client(
agent_version="1",
)
messages = [ChatMessage(role="user", contents=[Content.from_text(text="Hello")])]
messages = [Message(role="user", contents=[Content.from_text(text="Hello")])]
with (
patch(
@@ -977,7 +977,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="user", contents=[Content.from_text(text="Hello")])]
messages = [Message(role="user", contents=[Content.from_text(text="Hello")])]
chat_options: ChatOptions = {}
with (
@@ -1363,10 +1363,10 @@ async def test_integration_options(
# Prepare test message
if option_name.startswith("tool_choice"):
# Use weather-related prompt for tool tests
messages = [ChatMessage(role="user", text="What is the weather in Seattle?")]
messages = [Message(role="user", text="What is the weather in Seattle?")]
else:
# Generic prompt for simple options
messages = [ChatMessage(role="user", text="Say 'Hello World' briefly.")]
messages = [Message(role="user", text="Say 'Hello World' briefly.")]
# Build options dict
options: dict[str, Any] = {option_name: option_value, "tools": [get_weather]}
@@ -1480,11 +1480,11 @@ async def test_integration_agent_options(
# Prepare test message
if option_name.startswith("response_format"):
# Use prompt that works well with structured output
messages = [ChatMessage(role="user", text="The weather in Seattle is sunny")]
messages.append(ChatMessage(role="user", text="What is the weather in Seattle?"))
messages = [Message(role="user", text="The weather in Seattle is sunny")]
messages.append(Message(role="user", text="What is the weather in Seattle?"))
else:
# Generic prompt for simple options
messages = [ChatMessage(role="user", text="Say 'Hello World' briefly.")]
messages = [Message(role="user", text="Say 'Hello World' briefly.")]
# Build options dict
options = {option_name: option_value}
@@ -1621,8 +1621,8 @@ async def test_integration_agent_existing_thread():
async with (
temporary_chat_client(agent_name="af-int-test-existing-thread") as client,
ChatAgent(
chat_client=client,
Agent(
client=client,
instructions="You are a helpful assistant with good memory.",
) as first_agent,
):
@@ -1640,8 +1640,8 @@ async def test_integration_agent_existing_thread():
if preserved_thread:
async with (
temporary_chat_client(agent_name="af-int-test-existing-thread-2") as client,
ChatAgent(
chat_client=client,
Agent(
client=client,
instructions="You are a helpful assistant with good memory.",
) as second_agent,
):
@@ -4,7 +4,7 @@ import os
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from agent_framework import ChatAgent, FunctionTool
from agent_framework import Agent, FunctionTool
from agent_framework._mcp import MCPTool
from agent_framework.exceptions import ServiceInitializationError
from azure.ai.projects.aio import AIProjectClient
@@ -158,7 +158,7 @@ async def test_provider_create_agent(
description="Test Agent",
)
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
assert agent.name == "test-agent"
mock_project_client.agents.create_version.assert_called_once()
@@ -192,7 +192,7 @@ async def test_provider_create_agent_with_env_model(
# Call without model parameter - should use env var
agent = await provider.create_agent(name="test-agent")
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
# Verify the model from env var was used
call_args = mock_project_client.agents.create_version.call_args
assert call_args[1]["definition"].model == azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"]
@@ -322,7 +322,7 @@ async def test_provider_get_agent_with_name(mock_project_client: MagicMock) -> N
agent = await provider.get_agent(name="test-agent")
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
assert agent.name == "test-agent"
mock_project_client.agents.get.assert_called_with(agent_name="test-agent")
@@ -350,7 +350,7 @@ async def test_provider_get_agent_with_reference(mock_project_client: MagicMock)
agent_reference = AgentReference(name="test-agent", version="1.0")
agent = await provider.get_agent(reference=agent_reference)
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
assert agent.name == "test-agent"
mock_project_client.agents.get_version.assert_called_with(agent_name="test-agent", agent_version="1.0")
@@ -410,7 +410,7 @@ def test_provider_as_agent(mock_project_client: MagicMock) -> None:
with patch("agent_framework_azure_ai._project_provider.AzureAIClient") as mock_azure_ai_client:
agent = provider.as_agent(mock_agent_version)
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
assert agent.name == "test-agent"
assert agent.description == "Test Agent"
@@ -709,7 +709,7 @@ async def test_provider_create_and_get_agent_integration() -> None:
instructions="You are a helpful assistant. Always respond with 'Hello from provider!'",
)
assert isinstance(agent, ChatAgent)
assert isinstance(agent, Agent)
assert agent.name == "ProviderTestAgent"
# Run the agent