Python: [Feature Branch] Renamed Azure AI agent and small fixes (#1919)

* Renaming

* Small fixes

* Update python/packages/core/agent_framework/openai/_shared.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Dmytro Struk
2025-11-05 07:53:55 -08:00
committed by GitHub
Unverified
parent 39d3111734
commit 8135a99f9e
26 changed files with 316 additions and 331 deletions
@@ -3,7 +3,7 @@
import importlib.metadata
from ._chat_client import AzureAIAgentClient, AzureAISettings
from ._chat_client_v2 import AzureAIAgentClientV2
from ._client import AzureAIClient
try:
__version__ = importlib.metadata.version(__name__)
@@ -12,7 +12,7 @@ except importlib.metadata.PackageNotFoundError:
__all__ = [
"AzureAIAgentClient",
"AzureAIAgentClientV2",
"AzureAIClient",
"AzureAISettings",
"__version__",
]
@@ -37,14 +37,14 @@ else:
logger = get_logger("agent_framework.azure")
TAzureAIAgentClient = TypeVar("TAzureAIAgentClient", bound="AzureAIAgentClientV2")
TAzureAIClient = TypeVar("TAzureAIClient", bound="AzureAIClient")
@use_function_invocation
@use_observability
@use_chat_middleware
class AzureAIAgentClientV2(OpenAIBaseResponsesClient):
"""Azure AI Agent Chat client."""
class AzureAIClient(OpenAIBaseResponsesClient):
"""Azure AI Agent client."""
OTEL_PROVIDER_NAME: ClassVar[str] = "azure.ai" # type: ignore[reportIncompatibleVariableOverride, misc]
@@ -121,12 +121,6 @@ class AzureAIAgentClientV2(OpenAIBaseResponsesClient):
"or 'AZURE_AI_PROJECT_ENDPOINT' environment variable."
)
if not azure_ai_settings.model_deployment_name:
raise ServiceInitializationError(
"Azure AI model deployment name is required. Set via 'model_deployment_name' parameter "
"or 'AZURE_AI_MODEL_DEPLOYMENT_NAME' environment variable."
)
# Use provided credential
if not async_credential:
raise ServiceInitializationError("Azure credential is required when project_client is not provided.")
@@ -139,7 +133,6 @@ class AzureAIAgentClientV2(OpenAIBaseResponsesClient):
# Initialize parent
super().__init__(
model_id=azure_ai_settings.model_deployment_name, # type: ignore
**kwargs,
)
@@ -13,23 +13,23 @@ from agent_framework import (
from agent_framework.exceptions import ServiceInitializationError
from pydantic import ValidationError
from agent_framework_azure_ai import AzureAIAgentClientV2, AzureAISettings
from agent_framework_azure_ai import AzureAIClient, AzureAISettings
def create_test_azure_ai_client_v2(
def create_test_azure_ai_client(
mock_project_client: MagicMock,
agent_name: str | None = None,
agent_version: str | None = None,
conversation_id: str | None = None,
azure_ai_settings: AzureAISettings | None = None,
should_close_client: bool = False,
) -> AzureAIAgentClientV2:
"""Helper function to create AzureAIAgentClientV2 instances for testing, bypassing normal validation."""
) -> AzureAIClient:
"""Helper function to create AzureAIClient instances for testing, bypassing normal validation."""
if azure_ai_settings is None:
azure_ai_settings = AzureAISettings(env_file_path="test.env")
# Create client instance directly
client = object.__new__(AzureAIAgentClientV2)
client = object.__new__(AzureAIClient)
# Set attributes directly
client.project_client = mock_project_client
@@ -70,13 +70,13 @@ def test_azure_ai_settings_init_with_explicit_values() -> None:
assert settings.model_deployment_name == "custom-model"
def test_azure_ai_client_v2_init_with_project_client(mock_project_client: MagicMock) -> None:
"""Test AzureAIAgentClientV2 initialization with existing project_client."""
with patch("agent_framework_azure_ai._chat_client_v2.AzureAISettings") as mock_settings:
def test_azure_ai_client_init_with_project_client(mock_project_client: MagicMock) -> None:
"""Test AzureAIClient initialization with existing project_client."""
with patch("agent_framework_azure_ai._client.AzureAISettings") as mock_settings:
mock_settings.return_value.project_endpoint = None
mock_settings.return_value.model_deployment_name = "test-model"
client = AzureAIAgentClientV2(
client = AzureAIClient(
project_client=mock_project_client,
agent_name="test-agent",
agent_version="1.0",
@@ -89,16 +89,16 @@ def test_azure_ai_client_v2_init_with_project_client(mock_project_client: MagicM
assert isinstance(client, ChatClientProtocol)
def test_azure_ai_client_v2_init_auto_create_client(
def test_azure_ai_client_init_auto_create_client(
azure_ai_unit_test_env: dict[str, str],
mock_azure_credential: MagicMock,
) -> None:
"""Test AzureAIAgentClientV2 initialization with auto-created project_client."""
with patch("agent_framework_azure_ai._chat_client_v2.AIProjectClient") as mock_ai_project_client:
"""Test AzureAIClient initialization with auto-created project_client."""
with patch("agent_framework_azure_ai._client.AIProjectClient") as mock_ai_project_client:
mock_project_client = MagicMock()
mock_ai_project_client.return_value = mock_project_client
client = AzureAIAgentClientV2(
client = AzureAIClient(
project_endpoint=azure_ai_unit_test_env["AZURE_AI_PROJECT_ENDPOINT"],
model_deployment_name=azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
async_credential=mock_azure_credential,
@@ -113,64 +113,54 @@ def test_azure_ai_client_v2_init_auto_create_client(
mock_ai_project_client.assert_called_once()
def test_azure_ai_client_v2_init_missing_project_endpoint() -> None:
"""Test AzureAIAgentClientV2 initialization when project_endpoint is missing and no project_client provided."""
with patch("agent_framework_azure_ai._chat_client_v2.AzureAISettings") as mock_settings:
def test_azure_ai_client_init_missing_project_endpoint() -> None:
"""Test AzureAIClient initialization when project_endpoint is missing and no project_client provided."""
with patch("agent_framework_azure_ai._client.AzureAISettings") as mock_settings:
mock_settings.return_value.project_endpoint = None
mock_settings.return_value.model_deployment_name = "test-model"
with pytest.raises(ServiceInitializationError, match="Azure AI project endpoint is required"):
AzureAIAgentClientV2(async_credential=MagicMock())
AzureAIClient(async_credential=MagicMock())
def test_azure_ai_client_v2_init_missing_model_deployment() -> None:
"""Test AzureAIAgentClientV2 initialization when model deployment is missing for agent creation."""
with patch("agent_framework_azure_ai._chat_client_v2.AzureAISettings") as mock_settings:
mock_settings.return_value.project_endpoint = "https://test.com"
mock_settings.return_value.model_deployment_name = None
with pytest.raises(ServiceInitializationError, match="Azure AI model deployment name is required"):
AzureAIAgentClientV2(async_credential=MagicMock())
def test_azure_ai_client_v2_init_missing_credential(azure_ai_unit_test_env: dict[str, str]) -> None:
"""Test AzureAIAgentClientV2.__init__ when async_credential is missing and no project_client provided."""
def test_azure_ai_client_init_missing_credential(azure_ai_unit_test_env: dict[str, str]) -> None:
"""Test AzureAIClient.__init__ when async_credential is missing and no project_client provided."""
with pytest.raises(
ServiceInitializationError, match="Azure credential is required when project_client is not provided"
):
AzureAIAgentClientV2(
AzureAIClient(
project_endpoint=azure_ai_unit_test_env["AZURE_AI_PROJECT_ENDPOINT"],
model_deployment_name=azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
)
def test_azure_ai_client_v2_init_validation_error(mock_azure_credential: MagicMock) -> None:
def test_azure_ai_client_init_validation_error(mock_azure_credential: MagicMock) -> None:
"""Test that ValidationError in AzureAISettings is properly handled."""
with patch("agent_framework_azure_ai._chat_client_v2.AzureAISettings") as mock_settings:
with patch("agent_framework_azure_ai._client.AzureAISettings") as mock_settings:
mock_settings.side_effect = ValidationError.from_exception_data("test", [])
with pytest.raises(ServiceInitializationError, match="Failed to create Azure AI settings"):
AzureAIAgentClientV2(async_credential=mock_azure_credential)
AzureAIClient(async_credential=mock_azure_credential)
async def test_azure_ai_client_v2_get_agent_reference_or_create_existing_version(
async def test_azure_ai_client_get_agent_reference_or_create_existing_version(
mock_project_client: MagicMock,
) -> None:
"""Test _get_agent_reference_or_create when agent_version is already provided."""
client = create_test_azure_ai_client_v2(mock_project_client, agent_name="existing-agent", agent_version="1.0")
client = create_test_azure_ai_client(mock_project_client, agent_name="existing-agent", agent_version="1.0")
agent_ref = await client._get_agent_reference_or_create({}, None) # type: ignore
assert agent_ref == {"name": "existing-agent", "version": "1.0", "type": "agent_reference"}
async def test_azure_ai_client_v2_get_agent_reference_or_create_new_agent(
async def test_azure_ai_client_get_agent_reference_or_create_new_agent(
mock_project_client: MagicMock,
azure_ai_unit_test_env: dict[str, str],
) -> None:
"""Test _get_agent_reference_or_create when creating a new agent."""
azure_ai_settings = AzureAISettings(model_deployment_name=azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"])
client = create_test_azure_ai_client_v2(
client = create_test_azure_ai_client(
mock_project_client, agent_name="new-agent", azure_ai_settings=azure_ai_settings
)
@@ -188,32 +178,32 @@ async def test_azure_ai_client_v2_get_agent_reference_or_create_new_agent(
assert client.agent_version == "1.0"
async def test_azure_ai_client_v2_get_agent_reference_missing_model(
async def test_azure_ai_client_get_agent_reference_missing_model(
mock_project_client: MagicMock,
) -> None:
"""Test _get_agent_reference_or_create when model is missing for agent creation."""
client = create_test_azure_ai_client_v2(mock_project_client, agent_name="test-agent")
client = create_test_azure_ai_client(mock_project_client, agent_name="test-agent")
with pytest.raises(ServiceInitializationError, match="Model deployment name is required for agent creation"):
await client._get_agent_reference_or_create({}, None) # type: ignore
async def test_azure_ai_client_v2_get_conversation_id_or_create_existing(
async def test_azure_ai_client_get_conversation_id_or_create_existing(
mock_project_client: MagicMock,
) -> None:
"""Test _get_conversation_id_or_create when conversation_id is already provided."""
client = create_test_azure_ai_client_v2(mock_project_client, conversation_id="existing-conversation")
client = create_test_azure_ai_client(mock_project_client, conversation_id="existing-conversation")
conversation_id = await client._get_conversation_id_or_create({}) # type: ignore
assert conversation_id == "existing-conversation"
async def test_azure_ai_client_v2_get_conversation_id_or_create_new(
async def test_azure_ai_client_get_conversation_id_or_create_new(
mock_project_client: MagicMock,
) -> None:
"""Test _get_conversation_id_or_create when creating a new conversation."""
client = create_test_azure_ai_client_v2(mock_project_client)
client = create_test_azure_ai_client(mock_project_client)
# Mock conversation creation response
mock_conversation = MagicMock()
@@ -226,11 +216,11 @@ async def test_azure_ai_client_v2_get_conversation_id_or_create_new(
client.client.conversations.create.assert_called_once()
async def test_azure_ai_client_v2_prepare_input_with_system_messages(
async def test_azure_ai_client_prepare_input_with_system_messages(
mock_project_client: MagicMock,
) -> None:
"""Test _prepare_input converts system/developer messages to instructions."""
client = create_test_azure_ai_client_v2(mock_project_client)
client = create_test_azure_ai_client(mock_project_client)
messages = [
ChatMessage(role=Role.SYSTEM, contents=[TextContent(text="You are a helpful assistant.")]),
@@ -246,11 +236,11 @@ async def test_azure_ai_client_v2_prepare_input_with_system_messages(
assert instructions == "You are a helpful assistant."
async def test_azure_ai_client_v2_prepare_input_no_system_messages(
async def test_azure_ai_client_prepare_input_no_system_messages(
mock_project_client: MagicMock,
) -> None:
"""Test _prepare_input with no system/developer messages."""
client = create_test_azure_ai_client_v2(mock_project_client)
client = create_test_azure_ai_client(mock_project_client)
messages = [
ChatMessage(role=Role.USER, contents=[TextContent(text="Hello")]),
@@ -263,9 +253,9 @@ async def test_azure_ai_client_v2_prepare_input_no_system_messages(
assert instructions is None
async def test_azure_ai_client_v2_prepare_options_basic(mock_project_client: MagicMock) -> None:
async def test_azure_ai_client_prepare_options_basic(mock_project_client: MagicMock) -> None:
"""Test prepare_options basic functionality."""
client = create_test_azure_ai_client_v2(mock_project_client, agent_name="test-agent", agent_version="1.0")
client = create_test_azure_ai_client(mock_project_client, agent_name="test-agent", agent_version="1.0")
messages = [ChatMessage(role=Role.USER, contents=[TextContent(text="Hello")])]
chat_options = ChatOptions()
@@ -284,9 +274,9 @@ async def test_azure_ai_client_v2_prepare_options_basic(mock_project_client: Mag
assert run_options["extra_body"]["agent"]["name"] == "test-agent"
async def test_azure_ai_client_v2_prepare_options_with_store(mock_project_client: MagicMock) -> None:
async def test_azure_ai_client_prepare_options_with_store(mock_project_client: MagicMock) -> None:
"""Test prepare_options with store=True creates conversation."""
client = create_test_azure_ai_client_v2(mock_project_client, agent_name="test-agent", agent_version="1.0")
client = create_test_azure_ai_client(mock_project_client, agent_name="test-agent", agent_version="1.0")
# Mock conversation creation
mock_conversation = MagicMock()
@@ -312,9 +302,9 @@ async def test_azure_ai_client_v2_prepare_options_with_store(mock_project_client
assert run_options["conversation"] == "new-conversation-456"
async def test_azure_ai_client_v2_initialize_client(mock_project_client: MagicMock) -> None:
async def test_azure_ai_client_initialize_client(mock_project_client: MagicMock) -> None:
"""Test initialize_client method."""
client = create_test_azure_ai_client_v2(mock_project_client)
client = create_test_azure_ai_client(mock_project_client)
mock_openai_client = MagicMock()
mock_project_client.get_openai_client = AsyncMock(return_value=mock_openai_client)
@@ -325,9 +315,9 @@ async def test_azure_ai_client_v2_initialize_client(mock_project_client: MagicMo
mock_project_client.get_openai_client.assert_called_once()
def test_azure_ai_client_v2_get_conversation_id_from_response(mock_project_client: MagicMock) -> None:
def test_azure_ai_client_get_conversation_id_from_response(mock_project_client: MagicMock) -> None:
"""Test get_conversation_id method."""
client = create_test_azure_ai_client_v2(mock_project_client)
client = create_test_azure_ai_client(mock_project_client)
# Test with conversation and store=True
mock_response = MagicMock()
@@ -346,9 +336,9 @@ def test_azure_ai_client_v2_get_conversation_id_from_response(mock_project_clien
assert conversation_id is None
def test_azure_ai_client_v2_update_agent_name(mock_project_client: MagicMock) -> None:
def test_azure_ai_client_update_agent_name(mock_project_client: MagicMock) -> None:
"""Test _update_agent_name method."""
client = create_test_azure_ai_client_v2(mock_project_client)
client = create_test_azure_ai_client(mock_project_client)
# Test updating agent name when current is None
with patch.object(client, "_update_agent_name") as mock_update:
@@ -367,9 +357,9 @@ def test_azure_ai_client_v2_update_agent_name(mock_project_client: MagicMock) ->
mock_update.assert_called_once_with(None)
async def test_azure_ai_client_v2_async_context_manager(mock_project_client: MagicMock) -> None:
async def test_azure_ai_client_async_context_manager(mock_project_client: MagicMock) -> None:
"""Test async context manager functionality."""
client = create_test_azure_ai_client_v2(mock_project_client, should_close_client=True)
client = create_test_azure_ai_client(mock_project_client, should_close_client=True)
mock_project_client.close = AsyncMock()
@@ -380,9 +370,9 @@ async def test_azure_ai_client_v2_async_context_manager(mock_project_client: Mag
mock_project_client.close.assert_called_once()
async def test_azure_ai_client_v2_close_method(mock_project_client: MagicMock) -> None:
async def test_azure_ai_client_close_method(mock_project_client: MagicMock) -> None:
"""Test close method."""
client = create_test_azure_ai_client_v2(mock_project_client, should_close_client=True)
client = create_test_azure_ai_client(mock_project_client, should_close_client=True)
mock_project_client.close = AsyncMock()
@@ -391,9 +381,9 @@ async def test_azure_ai_client_v2_close_method(mock_project_client: MagicMock) -
mock_project_client.close.assert_called_once()
async def test_azure_ai_client_v2_close_client_when_should_close_false(mock_project_client: MagicMock) -> None:
async def test_azure_ai_client_close_client_when_should_close_false(mock_project_client: MagicMock) -> None:
"""Test _close_client_if_needed when should_close_client is False."""
client = create_test_azure_ai_client_v2(mock_project_client, should_close_client=False)
client = create_test_azure_ai_client(mock_project_client, should_close_client=False)
mock_project_client.close = AsyncMock()
@@ -403,11 +393,11 @@ async def test_azure_ai_client_v2_close_client_when_should_close_false(mock_proj
mock_project_client.close.assert_not_called()
async def test_azure_ai_client_v2_agent_creation_with_instructions(
async def test_azure_ai_client_agent_creation_with_instructions(
mock_project_client: MagicMock,
) -> None:
"""Test agent creation with combined instructions."""
client = create_test_azure_ai_client_v2(mock_project_client, agent_name="test-agent")
client = create_test_azure_ai_client(mock_project_client, agent_name="test-agent")
# Mock agent creation response
mock_agent = MagicMock()
@@ -425,11 +415,11 @@ async def test_azure_ai_client_v2_agent_creation_with_instructions(
assert call_args[1]["definition"].instructions == "Message instructions. Option instructions. "
async def test_azure_ai_client_v2_agent_creation_with_tools(
async def test_azure_ai_client_agent_creation_with_tools(
mock_project_client: MagicMock,
) -> None:
"""Test agent creation with tools."""
client = create_test_azure_ai_client_v2(mock_project_client, agent_name="test-agent")
client = create_test_azure_ai_client(mock_project_client, agent_name="test-agent")
# Mock agent creation response
mock_agent = MagicMock()
@@ -6,7 +6,7 @@ from typing import Any
_IMPORTS: dict[str, tuple[str, str]] = {
"AzureAIAgentClient": ("agent_framework_azure_ai", "azure-ai"),
"AzureAIAgentClientV2": ("agent_framework_azure_ai", "azure-ai"),
"AzureAIClient": ("agent_framework_azure_ai", "azure-ai"),
"AzureOpenAIAssistantsClient": ("agent_framework.azure._assistants_client", "core"),
"AzureOpenAIChatClient": ("agent_framework.azure._chat_client", "core"),
"AzureAISettings": ("agent_framework_azure_ai", "azure-ai"),
@@ -1,6 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
from agent_framework_azure_ai import AzureAIAgentClient, AzureAIAgentClientV2, AzureAISettings
from agent_framework_azure_ai import AzureAIAgentClient, AzureAIClient, AzureAISettings
from agent_framework.azure._assistants_client import AzureOpenAIAssistantsClient
from agent_framework.azure._chat_client import AzureOpenAIChatClient
@@ -10,7 +10,7 @@ from agent_framework.azure._shared import AzureOpenAISettings
__all__ = [
"AzureAIAgentClient",
"AzureAIAgentClientV2",
"AzureAIClient",
"AzureAISettings",
"AzureOpenAIAssistantsClient",
"AzureOpenAIChatClient",
@@ -338,6 +338,8 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient):
# model id
if not run_options.get("model"):
if not self.model_id:
raise ValueError("model_id must be a non-empty string")
run_options["model"] = self.model_id
# messages
@@ -127,18 +127,18 @@ class OpenAIBase(SerializationMixin):
INJECTABLE: ClassVar[set[str]] = {"client"}
def __init__(self, *, model_id: str, client: AsyncOpenAI | None = None, **kwargs: Any) -> None:
def __init__(self, *, model_id: str | None = None, client: AsyncOpenAI | None = None, **kwargs: Any) -> None:
"""Initialize OpenAIBase.
Keyword Args:
client: The AsyncOpenAI client instance.
model_id: The AI model ID to use (non-empty, whitespace stripped).
model_id: The AI model ID to use.
**kwargs: Additional keyword arguments.
"""
if not model_id or not model_id.strip():
raise ValueError("model_id must be a non-empty string")
self.client = client
self.model_id = model_id.strip()
self.model_id = None
if model_id:
self.model_id = model_id.strip()
# Call super().__init__() to continue MRO chain (e.g., BaseChatClient)
# Extract known kwargs that belong to other base classes
@@ -2075,27 +2075,27 @@ def test_create_response_content_image_generation_fallback():
assert f"data:image/png;base64,{unrecognized_base64}" == content.uri
def test_prepare_options_store_parameter_handling() -> None:
async def test_prepare_options_store_parameter_handling() -> None:
client = OpenAIResponsesClient(model_id="test-model", api_key="test-key")
messages = [ChatMessage(role="user", text="Test message")]
test_conversation_id = "test-conversation-123"
chat_options = ChatOptions(store=True, conversation_id=test_conversation_id)
options = client._prepare_options(messages, chat_options) # type: ignore
options = await client.prepare_options(messages, chat_options) # type: ignore
assert options["store"] is True
assert options["previous_response_id"] == test_conversation_id
chat_options = ChatOptions(store=False, conversation_id="")
options = client._prepare_options(messages, chat_options) # type: ignore
options = await client.prepare_options(messages, chat_options) # type: ignore
assert options["store"] is False
chat_options = ChatOptions(store=None, conversation_id=None)
options = client._prepare_options(messages, chat_options) # type: ignore
options = await client.prepare_options(messages, chat_options) # type: ignore
assert options["store"] is False
assert "previous_response_id" not in options
chat_options = ChatOptions()
options = client._prepare_options(messages, chat_options) # type: ignore
options = await client.prepare_options(messages, chat_options) # type: ignore
assert options["store"] is False
assert "previous_response_id" not in options
@@ -4,15 +4,15 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework.azure import AzureAIAgentClient
from agent_framework.azure import AzureAIClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
"""
Azure AI Agent Basic Example
This sample demonstrates basic usage of AzureAIAgentClient to create agents with automatic
lifecycle management. Shows both streaming and non-streaming responses with function tools.
This sample demonstrates basic usage of AzureAIAgentClient.
Shows both streaming and non-streaming responses with function tools.
"""
@@ -28,14 +28,13 @@ async def non_streaming_example() -> None:
"""Example of non-streaming response (get the complete result at once)."""
print("=== Non-streaming Response Example ===")
# Since no Agent ID is provided, the agent will be automatically created
# and deleted after getting a response
# Since no Agent ID is provided, the agent will be automatically created.
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
AzureAIAgentClient(async_credential=credential).create_agent(
name="WeatherAgent",
AzureAIClient(async_credential=credential).create_agent(
name="BasicWeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
@@ -50,14 +49,13 @@ async def streaming_example() -> None:
"""Example of streaming response (get results as they are generated)."""
print("=== Streaming Response Example ===")
# Since no Agent ID is provided, the agent will be automatically created
# and deleted after getting a response
# Since no Agent ID is provided, the agent will be automatically created.
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
AzureAIAgentClient(async_credential=credential).create_agent(
name="WeatherAgent",
AzureAIClient(async_credential=credential).create_agent(
name="BasicWeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
@@ -4,16 +4,16 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent
from agent_framework.azure import AzureAIAgentClient
from agent_framework import AgentThread
from agent_framework.azure import AzureAIClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
"""
Azure AI Agent with Thread Management Example
This sample demonstrates thread management with Azure AI Agents, comparing
automatic thread creation with explicit thread management for persistent context.
This sample demonstrates thread management with Azure AI Agent, showing
persistent conversation context and simplified response handling.
"""
@@ -26,44 +26,42 @@ def get_weather(
async def example_with_automatic_thread_creation() -> None:
"""Example showing automatic thread creation (service-managed thread)."""
"""Example showing automatic thread creation."""
print("=== Automatic Thread Creation Example ===")
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=AzureAIAgentClient(async_credential=credential),
AzureAIClient(async_credential=credential).create_agent(
name="BasicWeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
):
# First conversation - no thread provided, will be created automatically
first_query = "What's the weather like in Seattle?"
print(f"User: {first_query}")
first_result = await agent.run(first_query)
print(f"Agent: {first_result.text}")
query1 = "What's the weather like in Seattle?"
print(f"User: {query1}")
result1 = await agent.run(query1)
print(f"Agent: {result1.text}")
# Second conversation - still no thread provided, will create another new thread
second_query = "What was the last city I asked about?"
print(f"\nUser: {second_query}")
second_result = await agent.run(second_query)
print(f"Agent: {second_result.text}")
query2 = "What was the last city I asked about?"
print(f"\nUser: {query2}")
result2 = await agent.run(query2)
print(f"Agent: {result2.text}")
print("Note: Each call creates a separate thread, so the agent doesn't remember previous context.\n")
async def example_with_thread_persistence() -> None:
"""Example showing thread persistence across multiple conversations."""
print("=== Thread Persistence Example ===")
print("Using the same thread across multiple conversations to maintain context.\n")
async def example_with_thread_persistence_in_memory() -> None:
"""
Example showing thread persistence across multiple conversations.
In this example, messages are stored in-memory.
"""
print("=== Thread Persistence Example (In-Memory) ===")
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=AzureAIAgentClient(async_credential=credential),
AzureAIClient(async_credential=credential).create_agent(
name="BasicWeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
@@ -72,81 +70,81 @@ async def example_with_thread_persistence() -> None:
thread = agent.get_new_thread()
# First conversation
first_query = "What's the weather like in Tokyo?"
print(f"User: {first_query}")
first_result = await agent.run(first_query, thread=thread)
print(f"Agent: {first_result.text}")
query1 = "What's the weather like in Tokyo?"
print(f"User: {query1}")
result1 = await agent.run(query1, thread=thread)
print(f"Agent: {result1.text}")
# Second conversation using the same thread - maintains context
second_query = "How about London?"
print(f"\nUser: {second_query}")
second_result = await agent.run(second_query, thread=thread)
print(f"Agent: {second_result.text}")
query2 = "How about London?"
print(f"\nUser: {query2}")
result2 = await agent.run(query2, thread=thread)
print(f"Agent: {result2.text}")
# Third conversation - agent should remember both previous cities
third_query = "Which of the cities I asked about has better weather?"
print(f"\nUser: {third_query}")
third_result = await agent.run(third_query, thread=thread)
print(f"Agent: {third_result.text}")
query3 = "Which of the cities I asked about has better weather?"
print(f"\nUser: {query3}")
result3 = await agent.run(query3, thread=thread)
print(f"Agent: {result3.text}")
print("Note: The agent remembers context from previous messages in the same thread.\n")
async def example_with_existing_thread_id() -> None:
"""Example showing how to work with an existing thread ID from the service."""
"""
Example showing how to work with an existing thread ID from the service.
In this example, messages are stored on the server using Azure AI conversation state.
"""
print("=== Existing Thread ID Example ===")
print("Using a specific thread ID to continue an existing conversation.\n")
# First, create a conversation and capture the thread ID
existing_thread_id = None
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=AzureAIAgentClient(async_credential=credential),
AzureAIClient(async_credential=credential).create_agent(
name="BasicWeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
):
# Start a conversation and get the thread ID
thread = agent.get_new_thread()
first_query = "What's the weather in Paris?"
print(f"User: {first_query}")
first_result = await agent.run(first_query, thread=thread)
print(f"Agent: {first_result.text}")
query1 = "What's the weather in Paris?"
print(f"User: {query1}")
# Enable Azure AI conversation state by setting `store` parameter to True
result1 = await agent.run(query1, thread=thread, store=True)
print(f"Agent: {result1.text}")
# The thread ID is set after the first response
existing_thread_id = thread.service_thread_id
print(f"Thread ID: {existing_thread_id}")
if existing_thread_id:
print("\n--- Continuing with the same thread ID in a new agent instance ---")
if existing_thread_id:
print("\n--- Continuing with the same thread ID in a new agent instance ---")
# Create a new agent instance but use the existing thread ID
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=AzureAIAgentClient(thread_id=existing_thread_id, async_credential=credential),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
):
# Create a thread with the existing ID
thread = AgentThread(service_thread_id=existing_thread_id)
async with (
AzureAIClient(async_credential=credential).create_agent(
name="BasicWeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
):
# Create a thread with the existing ID
thread = AgentThread(service_thread_id=existing_thread_id)
second_query = "What was the last city I asked about?"
print(f"User: {second_query}")
second_result = await agent.run(second_query, thread=thread)
print(f"Agent: {second_result.text}")
print("Note: The agent continues the conversation from the previous thread.\n")
query2 = "What was the last city I asked about?"
print(f"User: {query2}")
result2 = await agent.run(query2, thread=thread, store=True)
print(f"Agent: {result2.text}")
print("Note: The agent continues the conversation from the previous thread by using thread ID.\n")
async def main() -> None:
print("=== Azure AI Chat Client Agent Thread Management Examples ===\n")
print("=== Azure AI Agent Thread Management Examples ===\n")
await example_with_automatic_thread_creation()
await example_with_thread_persistence()
await example_with_thread_persistence_in_memory()
await example_with_existing_thread_id()
@@ -4,15 +4,15 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework.azure import AzureAIAgentClientV2
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
"""
Azure AI Agent Basic Example
This sample demonstrates basic usage of AzureAIAgentClient.
Shows both streaming and non-streaming responses with function tools.
This sample demonstrates basic usage of AzureAIAgentClient to create agents with automatic
lifecycle management. Shows both streaming and non-streaming responses with function tools.
"""
@@ -28,13 +28,14 @@ async def non_streaming_example() -> None:
"""Example of non-streaming response (get the complete result at once)."""
print("=== Non-streaming Response Example ===")
# Since no Agent ID is provided, the agent will be automatically created.
# Since no Agent ID is provided, the agent will be automatically created
# and deleted after getting a response
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
AzureAIAgentClientV2(async_credential=credential).create_agent(
name="BasicWeatherAgent",
AzureAIAgentClient(async_credential=credential).create_agent(
name="WeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
@@ -49,13 +50,14 @@ async def streaming_example() -> None:
"""Example of streaming response (get results as they are generated)."""
print("=== Streaming Response Example ===")
# Since no Agent ID is provided, the agent will be automatically created.
# Since no Agent ID is provided, the agent will be automatically created
# and deleted after getting a response
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
AzureAIAgentClientV2(async_credential=credential).create_agent(
name="BasicWeatherAgent",
AzureAIAgentClient(async_credential=credential).create_agent(
name="WeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
@@ -0,0 +1,154 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
"""
Azure AI Agent with Thread Management Example
This sample demonstrates thread management with Azure AI Agents, comparing
automatic thread creation with explicit thread management for persistent context.
"""
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
"""Get the weather for a given location."""
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
async def example_with_automatic_thread_creation() -> None:
"""Example showing automatic thread creation (service-managed thread)."""
print("=== Automatic Thread Creation Example ===")
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=AzureAIAgentClient(async_credential=credential),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
):
# First conversation - no thread provided, will be created automatically
first_query = "What's the weather like in Seattle?"
print(f"User: {first_query}")
first_result = await agent.run(first_query)
print(f"Agent: {first_result.text}")
# Second conversation - still no thread provided, will create another new thread
second_query = "What was the last city I asked about?"
print(f"\nUser: {second_query}")
second_result = await agent.run(second_query)
print(f"Agent: {second_result.text}")
print("Note: Each call creates a separate thread, so the agent doesn't remember previous context.\n")
async def example_with_thread_persistence() -> None:
"""Example showing thread persistence across multiple conversations."""
print("=== Thread Persistence Example ===")
print("Using the same thread across multiple conversations to maintain context.\n")
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=AzureAIAgentClient(async_credential=credential),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
):
# Create a new thread that will be reused
thread = agent.get_new_thread()
# First conversation
first_query = "What's the weather like in Tokyo?"
print(f"User: {first_query}")
first_result = await agent.run(first_query, thread=thread)
print(f"Agent: {first_result.text}")
# Second conversation using the same thread - maintains context
second_query = "How about London?"
print(f"\nUser: {second_query}")
second_result = await agent.run(second_query, thread=thread)
print(f"Agent: {second_result.text}")
# Third conversation - agent should remember both previous cities
third_query = "Which of the cities I asked about has better weather?"
print(f"\nUser: {third_query}")
third_result = await agent.run(third_query, thread=thread)
print(f"Agent: {third_result.text}")
print("Note: The agent remembers context from previous messages in the same thread.\n")
async def example_with_existing_thread_id() -> None:
"""Example showing how to work with an existing thread ID from the service."""
print("=== Existing Thread ID Example ===")
print("Using a specific thread ID to continue an existing conversation.\n")
# First, create a conversation and capture the thread ID
existing_thread_id = None
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=AzureAIAgentClient(async_credential=credential),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
):
# Start a conversation and get the thread ID
thread = agent.get_new_thread()
first_query = "What's the weather in Paris?"
print(f"User: {first_query}")
first_result = await agent.run(first_query, thread=thread)
print(f"Agent: {first_result.text}")
# The thread ID is set after the first response
existing_thread_id = thread.service_thread_id
print(f"Thread ID: {existing_thread_id}")
if existing_thread_id:
print("\n--- Continuing with the same thread ID in a new agent instance ---")
# Create a new agent instance but use the existing thread ID
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=AzureAIAgentClient(thread_id=existing_thread_id, async_credential=credential),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
):
# Create a thread with the existing ID
thread = AgentThread(service_thread_id=existing_thread_id)
second_query = "What was the last city I asked about?"
print(f"User: {second_query}")
second_result = await agent.run(second_query, thread=thread)
print(f"Agent: {second_result.text}")
print("Note: The agent continues the conversation from the previous thread.\n")
async def main() -> None:
print("=== Azure AI Chat Client Agent Thread Management Examples ===\n")
await example_with_automatic_thread_creation()
await example_with_thread_persistence()
await example_with_existing_thread_id()
if __name__ == "__main__":
asyncio.run(main())
@@ -1,152 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from random import randint
from typing import Annotated
from agent_framework import AgentThread
from agent_framework.azure import AzureAIAgentClientV2
from azure.identity.aio import AzureCliCredential
from pydantic import Field
"""
Azure AI Agent with Thread Management Example
This sample demonstrates thread management with Azure AI Agent, showing
persistent conversation context and simplified response handling.
"""
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
"""Get the weather for a given location."""
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
async def example_with_automatic_thread_creation() -> None:
"""Example showing automatic thread creation."""
print("=== Automatic Thread Creation Example ===")
async with (
AzureCliCredential() as credential,
AzureAIAgentClientV2(async_credential=credential).create_agent(
name="BasicWeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
):
# First conversation - no thread provided, will be created automatically
query1 = "What's the weather like in Seattle?"
print(f"User: {query1}")
result1 = await agent.run(query1)
print(f"Agent: {result1.text}")
# Second conversation - still no thread provided, will create another new thread
query2 = "What was the last city I asked about?"
print(f"\nUser: {query2}")
result2 = await agent.run(query2)
print(f"Agent: {result2.text}")
print("Note: Each call creates a separate thread, so the agent doesn't remember previous context.\n")
async def example_with_thread_persistence_in_memory() -> None:
"""
Example showing thread persistence across multiple conversations.
In this example, messages are stored in-memory.
"""
print("=== Thread Persistence Example (In-Memory) ===")
async with (
AzureCliCredential() as credential,
AzureAIAgentClientV2(async_credential=credential).create_agent(
name="BasicWeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
):
# Create a new thread that will be reused
thread = agent.get_new_thread()
# First conversation
query1 = "What's the weather like in Tokyo?"
print(f"User: {query1}")
result1 = await agent.run(query1, thread=thread)
print(f"Agent: {result1.text}")
# Second conversation using the same thread - maintains context
query2 = "How about London?"
print(f"\nUser: {query2}")
result2 = await agent.run(query2, thread=thread)
print(f"Agent: {result2.text}")
# Third conversation - agent should remember both previous cities
query3 = "Which of the cities I asked about has better weather?"
print(f"\nUser: {query3}")
result3 = await agent.run(query3, thread=thread)
print(f"Agent: {result3.text}")
print("Note: The agent remembers context from previous messages in the same thread.\n")
async def example_with_existing_thread_id() -> None:
"""
Example showing how to work with an existing thread ID from the service.
In this example, messages are stored on the server using Azure AI conversation state.
"""
print("=== Existing Thread ID Example ===")
# First, create a conversation and capture the thread ID
existing_thread_id = None
async with (
AzureCliCredential() as credential,
AzureAIAgentClientV2(async_credential=credential).create_agent(
name="BasicWeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
):
# Start a conversation and get the thread ID
thread = agent.get_new_thread()
query1 = "What's the weather in Paris?"
print(f"User: {query1}")
# Enable Azure AI conversation state by setting `store` parameter to True
result1 = await agent.run(query1, thread=thread, store=True)
print(f"Agent: {result1.text}")
# The thread ID is set after the first response
existing_thread_id = thread.service_thread_id
print(f"Thread ID: {existing_thread_id}")
if existing_thread_id:
print("\n--- Continuing with the same thread ID in a new agent instance ---")
async with (
AzureAIAgentClientV2(async_credential=credential).create_agent(
name="BasicWeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
):
# Create a thread with the existing ID
thread = AgentThread(service_thread_id=existing_thread_id)
query2 = "What was the last city I asked about?"
print(f"User: {query2}")
result2 = await agent.run(query2, thread=thread, store=True)
print(f"Agent: {result2.text}")
print("Note: The agent continues the conversation from the previous thread by using thread ID.\n")
async def main() -> None:
print("=== Azure AI Agent Thread Management Examples ===\n")
await example_with_automatic_thread_creation()
await example_with_thread_persistence_in_memory()
await example_with_existing_thread_id()
if __name__ == "__main__":
asyncio.run(main())