Python: [BREAKING] updated structure and samples (#875)

* updated structure and samples

* updated names and removed cross tests

* updated projects etc

* updated tests

* updated test

* test fixes

* removed devui for now

* updated all-tests task

* removed old style configs

* remove coverage from tests

* updated to unit tests with all-tests

* updated foundry everywhere

* fix azure ai tests

* fix merge tests

* fix mypy
This commit is contained in:
Eduard van Valkenburg
2025-09-25 09:02:53 +02:00
committed by GitHub
Unverified
parent 366a7f7d47
commit 9355329dfd
169 changed files with 1159 additions and 1761 deletions
+3 -3
View File
@@ -1,6 +1,6 @@
# Foundry
FOUNDRY_PROJECT_ENDPOINT=""
FOUNDRY_MODEL_DEPLOYMENT_NAME=""
# Azure AI
AZURE_AI_PROJECT_ENDPOINT=""
AZURE_AI_MODEL_DEPLOYMENT_NAME=""
# OpenAI
OPENAI_API_KEY=""
OPENAI_CHAT_MODEL_ID=""
+1 -1
View File
@@ -275,7 +275,7 @@ The package follows a flat import structure:
- **Connectors**: Import from `agent_framework.<vendor/platform>`
```python
from agent_framework.openai import OpenAIChatClient
from agent_framework.azure import AzureChatClient
from agent_framework.azure import AzureOpenAIChatClient
```
## Testing
+13 -11
View File
@@ -5,12 +5,14 @@
```bash
# Base package including workflow support
pip install agent-framework
# Optional: Add Azure integration
pip install agent-framework[azure]
# Optional: Add Foundry integration
pip install agent-framework[foundry]
# Optional: Add Azure AI integration (Foundry)
pip install agent-framework[azure-ai]
# Optional: Add Microsoft integrations, currently only CopilotStudioAgents
pip install agent-framework[microsoft]
# Optional: Both
pip install agent-framework[azure,foundry]
pip install agent-framework[microsoft,azure-ai]
# All-in-one
pip install agent-framework[all]
```
Supported Platforms:
@@ -29,16 +31,16 @@ AZURE_OPENAI_API_KEY=...
AZURE_OPENAI_ENDPOINT=...
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME=...
...
FOUNDRY_PROJECT_ENDPOINT=...
FOUNDRY_MODEL_DEPLOYMENT_NAME=...
AZURE_AI_PROJECT_ENDPOINT=...
AZURE_AI_MODEL_DEPLOYMENT_NAME=...
```
You can also override environment variables by explicitly passing configuration parameters to the chat client constructor:
```python
from agent_framework.azure import AzureChatClient
from agent_framework.azure import AzureOpenAIChatClient
chat_client = AzureChatClient(
chat_client = AzureOpenAIChatClient(
api_key='',
endpoint='',
deployment_name='',
@@ -215,8 +217,8 @@ if __name__ == "__main__":
- [Getting Started with Agents](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/agents): Basic agent creation and tool usage
- [Chat Client Examples](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/chat_client): Direct chat client usage patterns
- [Azure Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure): Azure OpenAI and AI Foundry integration
- [.NET Orchestration Samples](https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/GettingStarted/Orchestration): Advanced multi-agent patterns (.NET)
- [Azure AI Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure-ai): Azure AI integration
- [Workflow Samples](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/workflow): Advanced multi-agent patterns
## Agent Framework Documentation
@@ -1,9 +1,9 @@
# Get Started with Microsoft Agent Framework Azure
# Get Started with Microsoft Agent Framework Azure AI
Please install this package as the extra for `agent-framework`:
```bash
pip install agent-framework[azure]
pip install agent-framework[azure_ai]
```
and see the [README](https://github.com/microsoft/agent-framework/tree/main/python/README.md) for more information.
@@ -2,7 +2,7 @@
import importlib.metadata
from ._chat_client import FoundryChatClient, FoundrySettings
from ._chat_client import AzureAIAgentClient, AzureAISettings
try:
__version__ = importlib.metadata.version(__name__)
@@ -10,7 +10,7 @@ except importlib.metadata.PackageNotFoundError:
__version__ = "0.0.0" # Fallback for development mode
__all__ = [
"FoundryChatClient",
"FoundrySettings",
"AzureAIAgentClient",
"AzureAISettings",
"__version__",
]
@@ -92,46 +92,42 @@ else:
from typing_extensions import Self # pragma: no cover
logger = get_logger("agent_framework.foundry")
logger = get_logger("agent_framework.azure")
class FoundrySettings(AFBaseSettings):
"""Foundry model settings.
class AzureAISettings(AFBaseSettings):
"""Azure AI Project settings.
The settings are first loaded from environment variables with the prefix 'FOUNDRY_'.
The settings are first loaded from environment variables with the prefix 'AZURE_AI_'.
If the environment variables are not found, the settings can be loaded from a .env file
with the encoding 'utf-8'. If the settings are not found in the .env file, the settings
are ignored; however, validation will fail alerting that the settings are missing.
Attributes:
project_endpoint: The Azure AI Foundry project endpoint URL.
(Env var FOUNDRY_PROJECT_ENDPOINT)
Args:
project_endpoint: The Azure AI Project endpoint URL.
(Env var AZURE_AI_PROJECT_ENDPOINT)
model_deployment_name: The name of the model deployment to use.
(Env var FOUNDRY_MODEL_DEPLOYMENT_NAME)
agent_name: Default name for automatically created agents.
(Env var FOUNDRY_AGENT_NAME)
Parameters:
(Env var AZURE_AI_MODEL_DEPLOYMENT_NAME)
env_file_path: If provided, the .env settings are read from this file path location.
env_file_encoding: The encoding of the .env file, defaults to 'utf-8'.
"""
env_prefix: ClassVar[str] = "FOUNDRY_"
env_prefix: ClassVar[str] = "AZURE_AI_"
project_endpoint: str | None = None
model_deployment_name: str | None = None
agent_name: str | None = "UnnamedAgent"
TFoundryChatClient = TypeVar("TFoundryChatClient", bound="FoundryChatClient")
TAzureAIAgentClient = TypeVar("TAzureAIAgentClient", bound="AzureAIAgentClient")
@use_function_invocation
@use_observability
class FoundryChatClient(BaseChatClient):
"""Azure AI Foundry Chat client."""
class AzureAIAgentClient(BaseChatClient):
"""Azure AI Agent Chat client."""
OTEL_PROVIDER_NAME: ClassVar[str] = "azure.ai.foundry" # type: ignore[reportIncompatibleVariableOverride, misc]
client: AIProjectClient = Field(...)
OTEL_PROVIDER_NAME: ClassVar[str] = "azure.ai" # type: ignore[reportIncompatibleVariableOverride, misc]
project_client: AIProjectClient = Field(...)
credential: AsyncTokenCredential | None = Field(...)
agent_id: str | None = Field(default=None)
agent_name: str | None = Field(default=None)
@@ -143,7 +139,7 @@ class FoundryChatClient(BaseChatClient):
def __init__(
self,
*,
client: AIProjectClient | None = None,
project_client: AIProjectClient | None = None,
agent_id: str | None = None,
agent_name: str | None = None,
thread_id: str | None = None,
@@ -154,82 +150,83 @@ class FoundryChatClient(BaseChatClient):
env_file_encoding: str | None = None,
**kwargs: Any,
) -> None:
"""Initialize a FoundryChatClient.
"""Initialize a AzureAIAgentClient.
Args:
client: An existing AIProjectClient to use. If not provided, one will be created.
agent_id: The ID of an existing agent to use. If not provided and client is provided,
a new agent will be created (and deleted after the request). If neither client
project_client: An existing AIProjectClient to use. If not provided, one will be created.
agent_id: The ID of an existing agent to use. If not provided and project_client is provided,
a new agent will be created (and deleted after the request). If neither project_client
nor agent_id is provided, both will be created and managed automatically.
agent_name: The name to use when creating new agents.
thread_id: Default thread ID to use for conversations. Can be overridden by
conversation_id property, when making a request.
project_endpoint: The Azure AI Foundry project endpoint URL. Used if client is not provided.
project_endpoint: The Azure AI Project endpoint URL, can also be set via
'AZURE_AI_PROJECT_ENDPOINT' environment variable. Is ignored when a project_client is passed.
model_deployment_name: The model deployment name to use for agent creation.
Can also be set via 'AZURE_AI_MODEL_DEPLOYMENT_NAME' environment variable.
async_credential: Azure async credential to use for authentication.
setup_tracing: Whether to setup tracing for the client. Defaults to True.
setup_tracing: Whether to setup tracing for the project_client. Defaults to True.
env_file_path: Path to environment file for loading settings.
env_file_encoding: Encoding of the environment file.
**kwargs: Additional keyword arguments passed to the parent class.
"""
try:
foundry_settings = FoundrySettings(
azure_ai_settings = AzureAISettings(
project_endpoint=project_endpoint,
model_deployment_name=model_deployment_name,
agent_name=agent_name,
env_file_path=env_file_path,
env_file_encoding=env_file_encoding,
)
except ValidationError as ex:
raise ServiceInitializationError("Failed to create Foundry settings.", ex) from ex
raise ServiceInitializationError("Failed to create Azure AI settings.", ex) from ex
# If no client is provided, create one
# If no project_client is provided, create one
should_close_client = False
if client is None:
if not foundry_settings.project_endpoint:
if project_client is None:
if not azure_ai_settings.project_endpoint:
raise ServiceInitializationError(
"Foundry project endpoint is required. Set via 'project_endpoint' parameter "
"or 'FOUNDRY_PROJECT_ENDPOINT' environment variable."
"Azure AI project endpoint is required. Set via 'project_endpoint' parameter "
"or 'AZURE_AI_PROJECT_ENDPOINT' environment variable."
)
if agent_id is None and not foundry_settings.model_deployment_name:
if agent_id is None and not azure_ai_settings.model_deployment_name:
raise ServiceInitializationError(
"Foundry model deployment name is required. Set via 'model_deployment_name' parameter "
"or 'FOUNDRY_MODEL_DEPLOYMENT_NAME' environment variable."
"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 client is not provided.")
client = AIProjectClient(
endpoint=foundry_settings.project_endpoint,
raise ServiceInitializationError("Azure credential is required when project_client is not provided.")
project_client = AIProjectClient(
endpoint=azure_ai_settings.project_endpoint,
credential=async_credential,
user_agent=AGENT_FRAMEWORK_USER_AGENT,
)
should_close_client = True
super().__init__(
client=client, # type: ignore[reportCallIssue]
project_client=project_client, # type: ignore[reportCallIssue]
credential=async_credential, # type: ignore[reportCallIssue]
agent_id=agent_id, # type: ignore[reportCallIssue]
thread_id=thread_id, # type: ignore[reportCallIssue]
agent_name=foundry_settings.agent_name, # type: ignore[reportCallIssue]
ai_model_id=foundry_settings.model_deployment_name, # type: ignore[reportCallIssue]
agent_name=agent_name, # type: ignore[reportCallIssue]
ai_model_id=azure_ai_settings.model_deployment_name, # type: ignore[reportCallIssue]
**kwargs,
)
self._should_close_client = should_close_client
async def setup_foundry_observability(self, enable_live_metrics: bool = False) -> None:
"""Call this method to setup tracing with Foundry.
async def setup_observability(self, enable_live_metrics: bool = False) -> None:
"""Use this method to setup tracing in your Azure AI Project.
This will take the connection string from the project client.
This will take the connection string from the project project_client.
It will override any connection string that is set in the environment variables.
It will disable any OTLP endpoint that might have been set.
"""
from agent_framework.observability import setup_observability
setup_observability(
applicationinsights_connection_string=await self.client.telemetry.get_application_insights_connection_string(), # noqa: E501
applicationinsights_connection_string=await self.project_client.telemetry.get_application_insights_connection_string(), # noqa: E501
enable_live_metrics=enable_live_metrics,
)
@@ -242,19 +239,19 @@ class FoundryChatClient(BaseChatClient):
await self.close()
async def close(self) -> None:
"""Close the client and clean up any agents we created."""
"""Close the project_client and clean up any agents we created."""
await self._cleanup_agent_if_needed()
await self._close_client_if_needed()
@classmethod
def from_dict(cls: type[TFoundryChatClient], settings: dict[str, Any]) -> TFoundryChatClient:
"""Initialize a FoundryChatClient from a dictionary of settings.
def from_dict(cls: type[TAzureAIAgentClient], settings: dict[str, Any]) -> TAzureAIAgentClient:
"""Initialize a AzureAIAgentClient from a dictionary of settings.
Args:
settings: A dictionary of settings for the service.
"""
return cls(
client=settings.get("client"),
project_client=settings.get("project_client"),
agent_id=settings.get("agent_id"),
thread_id=settings.get("thread_id"),
project_endpoint=settings.get("project_endpoint"),
@@ -315,8 +312,8 @@ class FoundryChatClient(BaseChatClient):
if not self.ai_model_id:
raise ServiceInitializationError("Model deployment name is required for agent creation.")
agent_name = self.agent_name
args = {"model": self.ai_model_id, "name": agent_name}
agent_name: str = self.agent_name or "UnnamedAgent"
args: dict[str, Any] = {"model": self.ai_model_id, "name": agent_name}
if run_options:
if "tools" in run_options:
args["tools"] = run_options["tools"]
@@ -324,8 +321,8 @@ class FoundryChatClient(BaseChatClient):
args["instructions"] = run_options["instructions"]
if "response_format" in run_options:
args["response_format"] = run_options["response_format"]
created_agent = await self.client.agents.create_agent(**args) # type: ignore[arg-type]
self.agent_id = created_agent.id
created_agent = await self.project_client.agents.create_agent(**args)
self.agent_id = str(created_agent.id)
self._should_delete_agent = True
return self.agent_id
@@ -367,7 +364,7 @@ class FoundryChatClient(BaseChatClient):
args["tool_outputs"] = tool_outputs
if tool_approvals:
args["tool_approvals"] = tool_approvals
await self.client.agents.runs.submit_tool_outputs_stream(**args) # type: ignore[reportUnknownMemberType]
await self.project_client.agents.runs.submit_tool_outputs_stream(**args) # type: ignore[reportUnknownMemberType]
# Pass the handler to the stream to continue processing
stream = handler # type: ignore
final_thread_id = thread_run.thread_id
@@ -377,7 +374,7 @@ class FoundryChatClient(BaseChatClient):
# Now create a new run and stream the results.
run_options.pop("conversation_id", None)
stream = await self.client.agents.runs.stream( # type: ignore[reportUnknownMemberType]
stream = await self.project_client.agents.runs.stream( # type: ignore[reportUnknownMemberType]
final_thread_id, agent_id=agent_id, **run_options
)
@@ -388,7 +385,9 @@ class FoundryChatClient(BaseChatClient):
if thread_id is None:
return None
async for run in self.client.agents.runs.list(thread_id=thread_id, limit=1, order=ListSortOrder.DESCENDING): # type: ignore[reportUnknownMemberType]
async for run in self.project_client.agents.runs.list(
thread_id=thread_id, limit=1, order=ListSortOrder.DESCENDING
): # type: ignore[reportUnknownMemberType]
if run.status not in [
RunStatus.COMPLETED,
RunStatus.CANCELLED,
@@ -405,12 +404,12 @@ class FoundryChatClient(BaseChatClient):
if thread_id is not None:
if thread_run is not None:
# There was an active run; we need to cancel it before starting a new run.
await self.client.agents.runs.cancel(thread_id, thread_run.id)
await self.project_client.agents.runs.cancel(thread_id, thread_run.id)
return thread_id
# No thread ID was provided, so create a new thread.
thread = await self.client.agents.threads.create(
thread = await self.project_client.agents.threads.create(
tool_resources=run_options.get("tool_resources"), metadata=run_options.get("metadata")
)
thread_id = thread.id
@@ -419,7 +418,7 @@ class FoundryChatClient(BaseChatClient):
# once fixed, in the function above, readd:
# `messages=run_options.pop("additional_messages")`
for msg in run_options.pop("additional_messages", []):
await self.client.agents.messages.create(
await self.project_client.agents.messages.create(
thread_id=thread_id, role=msg.role, content=msg.content, metadata=msg.metadata
)
# and remove until here.
@@ -607,14 +606,14 @@ class FoundryChatClient(BaseChatClient):
return []
async def _close_client_if_needed(self) -> None:
"""Close client session if we created it."""
"""Close project_client session if we created it."""
if self._should_close_client:
await self.client.close()
await self.project_client.close()
async def _cleanup_agent_if_needed(self) -> None:
"""Clean up the agent if we created it."""
if self._should_delete_agent and self.agent_id is not None:
await self.client.agents.delete_agent(self.agent_id)
await self.project_client.agents.delete_agent(self.agent_id)
self.agent_id = None
self._should_delete_agent = False
@@ -666,7 +665,7 @@ class FoundryChatClient(BaseChatClient):
additional_messages: list[ThreadMessageOptions] | None = None
# System/developer messages are turned into instructions, since there is no such message roles in Foundry.
# System/developer messages are turned into instructions, since there is no such message roles in Azure AI.
# All other messages are added 1:1, treating assistant messages as agent messages
# and everything else as user messages.
for chat_message in messages:
@@ -742,10 +741,13 @@ class FoundryChatClient(BaseChatClient):
bing_search = BingGroundingTool(connection_id=connection_id, **config_args)
if custom_connection_name and custom_configuration_name:
try:
bing_custom_connection = await self.client.connections.get(name=custom_connection_name)
bing_custom_connection = await self.project_client.connections.get(
name=custom_connection_name
)
except HttpResponseError as err:
raise ServiceInitializationError(
f"Bing custom connection '{custom_connection_name}' not found in Foundry.", err
f"Bing custom connection '{custom_connection_name}' not found in the Azure AI Project.",
err,
) from err
else:
bing_search = BingCustomSearchTool(
@@ -781,15 +783,18 @@ class FoundryChatClient(BaseChatClient):
index_name = additional_props.get("index_name") or os.getenv("AZURE_AI_SEARCH_INDEX_NAME")
if not index_name:
raise ServiceInitializationError(
"File search tool requires at least one vector store input, for file search in Foundry "
"File search tool requires at least one vector store input, "
"for file search in the Azure AI Project "
"or an 'index_name' to use Azure AI Search, "
"in additional_properties or environment variable 'AZURE_AI_SEARCH_INDEX_NAME'."
)
try:
azs_conn_id = await self.client.connections.get_default(ConnectionType.AZURE_AI_SEARCH)
azs_conn_id = await self.project_client.connections.get_default(
ConnectionType.AZURE_AI_SEARCH
)
except HttpResponseError as err:
raise ServiceInitializationError(
"No default Azure AI Search connection found in Foundry. "
"No default Azure AI Search connection found in the Azure AI Project. "
"Please create one or provide vector store inputs for the file search tool.",
err,
) from err
@@ -890,4 +895,4 @@ class FoundryChatClient(BaseChatClient):
Returns:
The service URL for the chat client, or None if not set.
"""
return self.client._config.endpoint
return self.project_client._config.endpoint
@@ -1,7 +1,7 @@
[project]
name = "agent-framework-foundry"
name = "agent-framework-azure-ai"
description = "Azure AI Foundry integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
@@ -74,15 +74,15 @@ disallow_incomplete_defs = true
disallow_untyped_decorators = true
[tool.bandit]
targets = ["agent_framework_foundry"]
targets = ["agent_framework_azure_ai"]
exclude_dirs = ["tests"]
[tool.poe]
executor.type = "uv"
include = "../../shared_tasks.toml"
[tool.poe.tasks]
mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_foundry"
test = "pytest --cov=agent_framework_foundry --cov-report=term-missing:skip-covered tests"
mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_azure_ai"
test = "pytest --cov=agent_framework_azure_ai --cov-report=term-missing:skip-covered tests"
[build-system]
requires = ["flit-core >= 3.9,<4.0"]
@@ -18,8 +18,8 @@ def override_env_param_dict(request: Any) -> dict[str, str]:
@fixture()
def foundry_unit_test_env(monkeypatch, exclude_list, override_env_param_dict): # type: ignore
"""Fixture to set environment variables for FoundrySettings."""
def azure_ai_unit_test_env(monkeypatch, exclude_list, override_env_param_dict): # type: ignore
"""Fixture to set environment variables for AzureAISettings."""
if exclude_list is None:
exclude_list = []
@@ -28,9 +28,8 @@ def foundry_unit_test_env(monkeypatch, exclude_list, override_env_param_dict):
override_env_param_dict = {}
env_vars = {
"FOUNDRY_PROJECT_ENDPOINT": "https://test-project.cognitiveservices.azure.com/",
"FOUNDRY_MODEL_DEPLOYMENT_NAME": "test-gpt-4o",
"FOUNDRY_AGENT_NAME": "TestAgent",
"AZURE_AI_PROJECT_ENDPOINT": "https://test-project.cognitiveservices.azure.com/",
"AZURE_AI_MODEL_DEPLOYMENT_NAME": "test-gpt-4o",
}
env_vars.update(override_env_param_dict) # type: ignore
@@ -23,8 +23,8 @@ from agent_framework import (
TextContent,
UriContent,
)
from agent_framework.azure import AzureAIAgentClient, AzureAISettings
from agent_framework.exceptions import ServiceInitializationError
from agent_framework.foundry import FoundryChatClient, FoundrySettings
from azure.ai.agents.models import (
RequiredFunctionToolCall,
SubmitToolOutputsAction,
@@ -34,95 +34,91 @@ from azure.core.credentials_async import AsyncTokenCredential
from azure.identity.aio import AzureCliCredential
from pydantic import Field, ValidationError
skip_if_foundry_integration_tests_disabled = pytest.mark.skipif(
skip_if_azure_ai_integration_tests_disabled = pytest.mark.skipif(
os.getenv("RUN_INTEGRATION_TESTS", "false").lower() != "true"
or os.getenv("FOUNDRY_PROJECT_ENDPOINT", "") in ("", "https://test-project.cognitiveservices.azure.com/"),
reason="No real FOUNDRY_PROJECT_ENDPOINT provided; skipping integration tests."
or os.getenv("AZURE_AI_PROJECT_ENDPOINT", "") in ("", "https://test-project.cognitiveservices.azure.com/"),
reason="No real AZURE_AI_PROJECT_ENDPOINT provided; skipping integration tests."
if os.getenv("RUN_INTEGRATION_TESTS", "false").lower() == "true"
else "Integration tests are disabled.",
)
def create_test_foundry_chat_client(
def create_test_azure_ai_chat_client(
mock_ai_project_client: MagicMock,
agent_id: str | None = None,
thread_id: str | None = None,
foundry_settings: FoundrySettings | None = None,
azure_ai_settings: AzureAISettings | None = None,
should_delete_agent: bool = False,
) -> FoundryChatClient:
"""Helper function to create FoundryChatClient instances for testing, bypassing Pydantic validation."""
if foundry_settings is None:
foundry_settings = FoundrySettings(env_file_path="test.env")
) -> AzureAIAgentClient:
"""Helper function to create AzureAIAgentClient instances for testing, bypassing Pydantic validation."""
if azure_ai_settings is None:
azure_ai_settings = AzureAISettings(env_file_path="test.env")
return FoundryChatClient.model_construct(
client=mock_ai_project_client,
return AzureAIAgentClient.model_construct(
project_client=mock_ai_project_client,
agent_id=agent_id,
thread_id=thread_id,
_should_delete_agent=should_delete_agent,
agent_name=foundry_settings.agent_name, # type: ignore[reportCallIssue]
ai_model_id=foundry_settings.model_deployment_name,
ai_model_id=azure_ai_settings.model_deployment_name,
)
def test_foundry_settings_init(foundry_unit_test_env: dict[str, str]) -> None:
"""Test FoundrySettings initialization."""
settings = FoundrySettings()
def test_azure_ai_settings_init(azure_ai_unit_test_env: dict[str, str]) -> None:
"""Test AzureAISettings initialization."""
settings = AzureAISettings()
assert settings.project_endpoint == foundry_unit_test_env["FOUNDRY_PROJECT_ENDPOINT"]
assert settings.model_deployment_name == foundry_unit_test_env["FOUNDRY_MODEL_DEPLOYMENT_NAME"]
assert settings.agent_name == foundry_unit_test_env["FOUNDRY_AGENT_NAME"]
assert settings.project_endpoint == azure_ai_unit_test_env["AZURE_AI_PROJECT_ENDPOINT"]
assert settings.model_deployment_name == azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"]
def test_foundry_settings_init_with_explicit_values() -> None:
"""Test FoundrySettings initialization with explicit values."""
settings = FoundrySettings(
def test_azure_ai_settings_init_with_explicit_values() -> None:
"""Test AzureAISettings initialization with explicit values."""
settings = AzureAISettings(
project_endpoint="https://custom-endpoint.com/",
model_deployment_name="custom-model",
agent_name="CustomAgent",
)
assert settings.project_endpoint == "https://custom-endpoint.com/"
assert settings.model_deployment_name == "custom-model"
assert settings.agent_name == "CustomAgent"
def test_foundry_chat_client_init_with_client(mock_ai_project_client: MagicMock) -> None:
"""Test FoundryChatClient initialization with existing client."""
chat_client = create_test_foundry_chat_client(
def test_azure_ai_chat_client_init_with_client(mock_ai_project_client: MagicMock) -> None:
"""Test AzureAIAgentClient initialization with existing project_client."""
chat_client = create_test_azure_ai_chat_client(
mock_ai_project_client, agent_id="existing-agent-id", thread_id="test-thread-id"
)
assert chat_client.client is mock_ai_project_client
assert chat_client.project_client is mock_ai_project_client
assert chat_client.agent_id == "existing-agent-id"
assert chat_client.thread_id == "test-thread-id"
assert not chat_client._should_delete_agent # type: ignore
assert isinstance(chat_client, ChatClientProtocol)
def test_foundry_chat_client_init_auto_create_client(
foundry_unit_test_env: dict[str, str],
def test_azure_ai_chat_client_init_auto_create_client(
azure_ai_unit_test_env: dict[str, str],
mock_ai_project_client: MagicMock,
) -> None:
"""Test FoundryChatClient initialization with auto-created client."""
foundry_settings = FoundrySettings(**foundry_unit_test_env) # type: ignore
chat_client = FoundryChatClient.model_construct(
client=mock_ai_project_client,
"""Test AzureAIAgentClient initialization with auto-created project_client."""
azure_ai_settings = AzureAISettings(**azure_ai_unit_test_env) # type: ignore
chat_client = AzureAIAgentClient.model_construct(
project_client=mock_ai_project_client,
agent_id=None,
thread_id=None,
_should_delete_agent=False,
_foundry_settings=foundry_settings,
_azure_ai_settings=azure_ai_settings,
credential=None,
)
assert chat_client.client is mock_ai_project_client
assert chat_client.project_client is mock_ai_project_client
assert chat_client.agent_id is None
assert not chat_client._should_delete_agent # type: ignore
def test_foundry_chat_client_init_missing_project_endpoint() -> None:
"""Test FoundryChatClient initialization when project_endpoint is missing and no client provided."""
# Mock FoundrySettings to return settings with None project_endpoint
with patch("agent_framework_foundry._chat_client.FoundrySettings") as mock_settings:
def test_azure_ai_chat_client_init_missing_project_endpoint() -> None:
"""Test AzureAIAgentClient initialization when project_endpoint is missing and no project_client provided."""
# Mock AzureAISettings to return settings with None project_endpoint
with patch("agent_framework_azure_ai._chat_client.AzureAISettings") as mock_settings:
mock_settings_instance = MagicMock()
mock_settings_instance.project_endpoint = None # This should trigger the error
mock_settings_instance.model_deployment_name = "test-model"
@@ -130,8 +126,8 @@ def test_foundry_chat_client_init_missing_project_endpoint() -> None:
mock_settings.return_value = mock_settings_instance
with pytest.raises(ServiceInitializationError, match="project endpoint is required"):
FoundryChatClient(
client=None,
AzureAIAgentClient(
project_client=None,
agent_id=None,
project_endpoint=None, # Missing endpoint
model_deployment_name="test-model",
@@ -139,10 +135,10 @@ def test_foundry_chat_client_init_missing_project_endpoint() -> None:
)
def test_foundry_chat_client_init_missing_model_deployment_for_agent_creation() -> None:
"""Test FoundryChatClient initialization when model deployment is missing for agent creation."""
# Mock FoundrySettings to return settings with None model_deployment_name
with patch("agent_framework_foundry._chat_client.FoundrySettings") as mock_settings:
def test_azure_ai_chat_client_init_missing_model_deployment_for_agent_creation() -> None:
"""Test AzureAIAgentClient initialization when model deployment is missing for agent creation."""
# Mock AzureAISettings to return settings with None model_deployment_name
with patch("agent_framework_azure_ai._chat_client.AzureAISettings") as mock_settings:
mock_settings_instance = MagicMock()
mock_settings_instance.project_endpoint = "https://test.com"
mock_settings_instance.model_deployment_name = None # This should trigger the error
@@ -150,8 +146,8 @@ def test_foundry_chat_client_init_missing_model_deployment_for_agent_creation()
mock_settings.return_value = mock_settings_instance
with pytest.raises(ServiceInitializationError, match="model deployment name is required"):
FoundryChatClient(
client=None,
AzureAIAgentClient(
project_client=None,
agent_id=None, # No existing agent
project_endpoint="https://test.com",
model_deployment_name=None, # Missing for agent creation
@@ -159,10 +155,10 @@ def test_foundry_chat_client_init_missing_model_deployment_for_agent_creation()
)
def test_foundry_chat_client_from_dict(mock_ai_project_client: MagicMock) -> None:
"""Test FoundryChatClient.from_dict method."""
def test_azure_ai_chat_client_from_dict(mock_ai_project_client: MagicMock) -> None:
"""Test AzureAIAgentClient.from_dict method."""
settings = {
"client": mock_ai_project_client,
"project_client": mock_ai_project_client,
"agent_id": "test-agent-id",
"thread_id": "test-thread-id",
"project_endpoint": "https://test-endpoint.com/",
@@ -170,55 +166,57 @@ def test_foundry_chat_client_from_dict(mock_ai_project_client: MagicMock) -> Non
"agent_name": "TestAgent",
}
foundry_settings = FoundrySettings(
azure_ai_settings = AzureAISettings(
project_endpoint=settings["project_endpoint"],
model_deployment_name=settings["model_deployment_name"],
agent_name=settings["agent_name"],
)
chat_client: FoundryChatClient = create_test_foundry_chat_client(
chat_client: AzureAIAgentClient = create_test_azure_ai_chat_client(
mock_ai_project_client,
agent_id=settings["agent_id"], # type: ignore
thread_id=settings["thread_id"], # type: ignore
foundry_settings=foundry_settings,
azure_ai_settings=azure_ai_settings,
)
assert chat_client.client is mock_ai_project_client
assert chat_client.project_client is mock_ai_project_client
assert chat_client.agent_id == "test-agent-id"
assert chat_client.thread_id == "test-thread-id"
def test_foundry_chat_client_init_missing_credential(foundry_unit_test_env: dict[str, str]) -> None:
"""Test FoundryChatClient.__init__ when async_credential is missing and no client provided."""
with pytest.raises(ServiceInitializationError, match="Azure credential is required when client is not provided"):
FoundryChatClient(
client=None,
def test_azure_ai_chat_client_init_missing_credential(azure_ai_unit_test_env: dict[str, str]) -> None:
"""Test AzureAIAgentClient.__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"
):
AzureAIAgentClient(
project_client=None,
agent_id="existing-agent",
project_endpoint=foundry_unit_test_env["FOUNDRY_PROJECT_ENDPOINT"],
model_deployment_name=foundry_unit_test_env["FOUNDRY_MODEL_DEPLOYMENT_NAME"],
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=None, # Missing credential
)
def test_foundry_chat_client_init_validation_error(mock_azure_credential: MagicMock) -> None:
"""Test that ValidationError in FoundrySettings is properly handled."""
with patch("agent_framework_foundry._chat_client.FoundrySettings") as mock_settings:
def test_azure_ai_chat_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.AzureAISettings") as mock_settings:
# Create a proper ValidationError with empty errors list and model dict
mock_settings.side_effect = ValidationError.from_exception_data("FoundrySettings", [])
mock_settings.side_effect = ValidationError.from_exception_data("AzureAISettings", [])
with pytest.raises(ServiceInitializationError, match="Failed to create Foundry settings"):
FoundryChatClient(
with pytest.raises(ServiceInitializationError, match="Failed to create Azure AI settings."):
AzureAIAgentClient(
project_endpoint="https://test.com",
model_deployment_name="test-model",
async_credential=mock_azure_credential,
)
async def test_foundry_chat_client_get_agent_id_or_create_existing_agent(
async def test_azure_ai_chat_client_get_agent_id_or_create_existing_agent(
mock_ai_project_client: MagicMock,
) -> None:
"""Test _get_agent_id_or_create when agent_id is already provided."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client, agent_id="existing-agent-id")
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="existing-agent-id")
agent_id = await chat_client._get_agent_id_or_create() # type: ignore
@@ -226,15 +224,15 @@ async def test_foundry_chat_client_get_agent_id_or_create_existing_agent(
assert not chat_client._should_delete_agent # type: ignore
async def test_foundry_chat_client_get_agent_id_or_create_create_new(
async def test_azure_ai_chat_client_get_agent_id_or_create_create_new(
mock_ai_project_client: MagicMock,
foundry_unit_test_env: dict[str, str],
azure_ai_unit_test_env: dict[str, str],
) -> None:
"""Test _get_agent_id_or_create when creating a new agent."""
foundry_settings = FoundrySettings(
model_deployment_name=foundry_unit_test_env["FOUNDRY_MODEL_DEPLOYMENT_NAME"], agent_name="TestAgent"
azure_ai_settings = AzureAISettings(
model_deployment_name=azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"], agent_name="TestAgent"
)
chat_client = create_test_foundry_chat_client(mock_ai_project_client, foundry_settings=foundry_settings)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, azure_ai_settings=azure_ai_settings)
agent_id = await chat_client._get_agent_id_or_create() # type: ignore
@@ -242,11 +240,11 @@ async def test_foundry_chat_client_get_agent_id_or_create_create_new(
assert chat_client._should_delete_agent # type: ignore
async def test_foundry_chat_client_tool_results_without_thread_error_via_public_api(
async def test_azure_ai_chat_client_tool_results_without_thread_error_via_public_api(
mock_ai_project_client: MagicMock,
) -> None:
"""Test that tool results without thread ID raise error through public API."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client, agent_id="test-agent")
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
# Create messages with tool results but no thread/conversation ID
messages = [
@@ -262,9 +260,9 @@ async def test_foundry_chat_client_tool_results_without_thread_error_via_public_
pass
async def test_foundry_chat_client_thread_management_through_public_api(mock_ai_project_client: MagicMock) -> None:
async def test_azure_ai_chat_client_thread_management_through_public_api(mock_ai_project_client: MagicMock) -> None:
"""Test thread creation and management through public API."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client, agent_id="test-agent")
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
mock_thread = MagicMock()
mock_thread.id = "new-thread-456"
@@ -293,22 +291,22 @@ async def test_foundry_chat_client_thread_management_through_public_api(mock_ai_
mock_ai_project_client.agents.threads.create.assert_called_once()
@pytest.mark.parametrize("exclude_list", [["FOUNDRY_MODEL_DEPLOYMENT_NAME"]], indirect=True)
async def test_foundry_chat_client_get_agent_id_or_create_missing_model(
mock_ai_project_client: MagicMock, foundry_unit_test_env: dict[str, str]
@pytest.mark.parametrize("exclude_list", [["AZURE_AI_MODEL_DEPLOYMENT_NAME"]], indirect=True)
async def test_azure_ai_chat_client_get_agent_id_or_create_missing_model(
mock_ai_project_client: MagicMock, azure_ai_unit_test_env: dict[str, str]
) -> None:
"""Test _get_agent_id_or_create when model_deployment_name is missing."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
with pytest.raises(ServiceInitializationError, match="Model deployment name is required"):
await chat_client._get_agent_id_or_create() # type: ignore
async def test_foundry_chat_client_cleanup_agent_if_needed_should_delete(
async def test_azure_ai_chat_client_cleanup_agent_if_needed_should_delete(
mock_ai_project_client: MagicMock,
) -> None:
"""Test _cleanup_agent_if_needed when agent should be deleted."""
chat_client = create_test_foundry_chat_client(
chat_client = create_test_azure_ai_chat_client(
mock_ai_project_client, agent_id="agent-to-delete", should_delete_agent=True
)
@@ -318,11 +316,11 @@ async def test_foundry_chat_client_cleanup_agent_if_needed_should_delete(
assert not chat_client._should_delete_agent # type: ignore
async def test_foundry_chat_client_cleanup_agent_if_needed_should_not_delete(
async def test_azure_ai_chat_client_cleanup_agent_if_needed_should_not_delete(
mock_ai_project_client: MagicMock,
) -> None:
"""Test _cleanup_agent_if_needed when agent should not be deleted."""
chat_client = create_test_foundry_chat_client(
chat_client = create_test_azure_ai_chat_client(
mock_ai_project_client, agent_id="agent-to-keep", should_delete_agent=False
)
@@ -333,11 +331,11 @@ async def test_foundry_chat_client_cleanup_agent_if_needed_should_not_delete(
assert not chat_client._should_delete_agent # type: ignore
async def test_foundry_chat_client_cleanup_agent_if_needed_exception_handling(
async def test_azure_ai_chat_client_cleanup_agent_if_needed_exception_handling(
mock_ai_project_client: MagicMock,
) -> None:
"""Test _cleanup_agent_if_needed propagates exceptions (it doesn't handle them)."""
chat_client = create_test_foundry_chat_client(
chat_client = create_test_azure_ai_chat_client(
mock_ai_project_client, agent_id="agent-to-delete", should_delete_agent=True
)
mock_ai_project_client.agents.delete_agent.side_effect = Exception("Deletion failed")
@@ -346,9 +344,9 @@ async def test_foundry_chat_client_cleanup_agent_if_needed_exception_handling(
await chat_client._cleanup_agent_if_needed() # type: ignore
async def test_foundry_chat_client_aclose(mock_ai_project_client: MagicMock) -> None:
async def test_azure_ai_chat_client_aclose(mock_ai_project_client: MagicMock) -> None:
"""Test aclose method calls cleanup."""
chat_client = create_test_foundry_chat_client(
chat_client = create_test_azure_ai_chat_client(
mock_ai_project_client, agent_id="agent-to-delete", should_delete_agent=True
)
@@ -358,9 +356,9 @@ async def test_foundry_chat_client_aclose(mock_ai_project_client: MagicMock) ->
mock_ai_project_client.agents.delete_agent.assert_called_once_with("agent-to-delete")
async def test_foundry_chat_client_async_context_manager(mock_ai_project_client: MagicMock) -> None:
async def test_azure_ai_chat_client_async_context_manager(mock_ai_project_client: MagicMock) -> None:
"""Test async context manager functionality."""
chat_client = create_test_foundry_chat_client(
chat_client = create_test_azure_ai_chat_client(
mock_ai_project_client, agent_id="agent-to-delete", should_delete_agent=True
)
@@ -372,9 +370,9 @@ async def test_foundry_chat_client_async_context_manager(mock_ai_project_client:
mock_ai_project_client.agents.delete_agent.assert_called_once_with("agent-to-delete")
async def test_foundry_chat_client_create_run_options_basic(mock_ai_project_client: MagicMock) -> None:
async def test_azure_ai_chat_client_create_run_options_basic(mock_ai_project_client: MagicMock) -> None:
"""Test _create_run_options with basic ChatOptions."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
messages = [ChatMessage(role=Role.USER, text="Hello")]
chat_options = ChatOptions(max_tokens=100, temperature=0.7)
@@ -385,9 +383,9 @@ async def test_foundry_chat_client_create_run_options_basic(mock_ai_project_clie
assert tool_results is None
async def test_foundry_chat_client_create_run_options_no_chat_options(mock_ai_project_client: MagicMock) -> None:
async def test_azure_ai_chat_client_create_run_options_no_chat_options(mock_ai_project_client: MagicMock) -> None:
"""Test _create_run_options with no ChatOptions."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
messages = [ChatMessage(role=Role.USER, text="Hello")]
@@ -397,10 +395,10 @@ async def test_foundry_chat_client_create_run_options_no_chat_options(mock_ai_pr
assert tool_results is None
async def test_foundry_chat_client_create_run_options_with_image_content(mock_ai_project_client: MagicMock) -> None:
async def test_azure_ai_chat_client_create_run_options_with_image_content(mock_ai_project_client: MagicMock) -> None:
"""Test _create_run_options with image content."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client, agent_id="test-agent")
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
image_content = UriContent(uri="https://example.com/image.jpg", media_type="image/jpeg")
messages = [ChatMessage(role=Role.USER, contents=[image_content])]
@@ -414,9 +412,9 @@ async def test_foundry_chat_client_create_run_options_with_image_content(mock_ai
assert len(message.content) == 1
def test_foundry_chat_client_convert_function_results_to_tool_output_none(mock_ai_project_client: MagicMock) -> None:
def test_azure_ai_chat_client_convert_function_results_to_tool_output_none(mock_ai_project_client: MagicMock) -> None:
"""Test _convert_required_action_to_tool_output with None input."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
run_id, tool_outputs, tool_approvals = chat_client._convert_required_action_to_tool_output(None) # type: ignore
@@ -425,9 +423,9 @@ def test_foundry_chat_client_convert_function_results_to_tool_output_none(mock_a
assert tool_approvals is None
async def test_foundry_chat_client_close_client_when_should_close_true(mock_ai_project_client: MagicMock) -> None:
"""Test _close_client_if_needed closes client when should_close_client is True."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
async def test_azure_ai_chat_client_close_client_when_should_close_true(mock_ai_project_client: MagicMock) -> None:
"""Test _close_client_if_needed closes project_client when should_close_client is True."""
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
chat_client._should_close_client = True # type: ignore
mock_ai_project_client.close = AsyncMock()
@@ -437,9 +435,9 @@ async def test_foundry_chat_client_close_client_when_should_close_true(mock_ai_p
mock_ai_project_client.close.assert_called_once()
async def test_foundry_chat_client_close_client_when_should_close_false(mock_ai_project_client: MagicMock) -> None:
"""Test _close_client_if_needed does not close client when should_close_client is False."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
async def test_azure_ai_chat_client_close_client_when_should_close_false(mock_ai_project_client: MagicMock) -> None:
"""Test _close_client_if_needed does not close project_client when should_close_client is False."""
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
chat_client._should_close_client = False # type: ignore
await chat_client._close_client_if_needed() # type: ignore
@@ -447,9 +445,9 @@ async def test_foundry_chat_client_close_client_when_should_close_false(mock_ai_
mock_ai_project_client.close.assert_not_called()
def test_foundry_chat_client_update_agent_name_when_current_is_none(mock_ai_project_client: MagicMock) -> None:
def test_azure_ai_chat_client_update_agent_name_when_current_is_none(mock_ai_project_client: MagicMock) -> None:
"""Test _update_agent_name updates name when current agent_name is None."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
chat_client.agent_name = None # type: ignore
chat_client._update_agent_name("NewAgentName") # type: ignore
@@ -457,9 +455,9 @@ def test_foundry_chat_client_update_agent_name_when_current_is_none(mock_ai_proj
assert chat_client.agent_name == "NewAgentName"
def test_foundry_chat_client_update_agent_name_when_current_exists(mock_ai_project_client: MagicMock) -> None:
def test_azure_ai_chat_client_update_agent_name_when_current_exists(mock_ai_project_client: MagicMock) -> None:
"""Test _update_agent_name does not update when current agent_name exists."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
chat_client.agent_name = "ExistingName" # type: ignore
chat_client._update_agent_name("NewAgentName") # type: ignore
@@ -467,9 +465,9 @@ def test_foundry_chat_client_update_agent_name_when_current_exists(mock_ai_proje
assert chat_client.agent_name == "ExistingName"
def test_foundry_chat_client_update_agent_name_with_none_input(mock_ai_project_client: MagicMock) -> None:
def test_azure_ai_chat_client_update_agent_name_with_none_input(mock_ai_project_client: MagicMock) -> None:
"""Test _update_agent_name with None input."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
chat_client.agent_name = None # type: ignore
chat_client._update_agent_name(None) # type: ignore
@@ -477,9 +475,9 @@ def test_foundry_chat_client_update_agent_name_with_none_input(mock_ai_project_c
assert chat_client.agent_name is None
async def test_foundry_chat_client_create_run_options_with_messages(mock_ai_project_client: MagicMock) -> None:
async def test_azure_ai_chat_client_create_run_options_with_messages(mock_ai_project_client: MagicMock) -> None:
"""Test _create_run_options with different message types."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
# Test with system message (becomes instruction)
messages = [
@@ -495,9 +493,9 @@ async def test_foundry_chat_client_create_run_options_with_messages(mock_ai_proj
assert len(run_options["additional_messages"]) == 1 # Only user message
async def test_foundry_chat_client_inner_get_response(mock_ai_project_client: MagicMock) -> None:
async def test_azure_ai_chat_client_inner_get_response(mock_ai_project_client: MagicMock) -> None:
"""Test _inner_get_response method."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client, agent_id="test-agent")
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
messages = [ChatMessage(role=Role.USER, text="Hello")]
chat_options = ChatOptions()
@@ -517,14 +515,14 @@ async def test_foundry_chat_client_inner_get_response(mock_ai_project_client: Ma
mock_from_generator.assert_called_once()
async def test_foundry_chat_client_get_agent_id_or_create_with_run_options(
mock_ai_project_client: MagicMock, foundry_unit_test_env: dict[str, str]
async def test_azure_ai_chat_client_get_agent_id_or_create_with_run_options(
mock_ai_project_client: MagicMock, azure_ai_unit_test_env: dict[str, str]
) -> None:
"""Test _get_agent_id_or_create with run_options containing tools and instructions."""
foundry_settings = FoundrySettings(
model_deployment_name=foundry_unit_test_env["FOUNDRY_MODEL_DEPLOYMENT_NAME"], agent_name="TestAgent"
azure_ai_settings = AzureAISettings(
model_deployment_name=azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"], agent_name="TestAgent"
)
chat_client = create_test_foundry_chat_client(mock_ai_project_client, foundry_settings=foundry_settings)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, azure_ai_settings=azure_ai_settings)
run_options = {
"tools": [{"type": "function", "function": {"name": "test_tool"}}],
@@ -543,9 +541,9 @@ async def test_foundry_chat_client_get_agent_id_or_create_with_run_options(
assert "response_format" in call_args
async def test_foundry_chat_client_prepare_thread_cancels_active_run(mock_ai_project_client: MagicMock) -> None:
async def test_azure_ai_chat_client_prepare_thread_cancels_active_run(mock_ai_project_client: MagicMock) -> None:
"""Test _prepare_thread cancels active thread run when provided."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client, agent_id="test-agent")
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
mock_thread_run = MagicMock()
mock_thread_run.id = "run_123"
@@ -559,9 +557,9 @@ async def test_foundry_chat_client_prepare_thread_cancels_active_run(mock_ai_pro
mock_ai_project_client.agents.runs.cancel.assert_called_once_with("test-thread", "run_123")
def test_foundry_chat_client_create_function_call_contents_basic(mock_ai_project_client: MagicMock) -> None:
def test_azure_ai_chat_client_create_function_call_contents_basic(mock_ai_project_client: MagicMock) -> None:
"""Test _create_function_call_contents with basic function call."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
mock_tool_call = MagicMock(spec=RequiredFunctionToolCall)
mock_tool_call.id = "call_123"
@@ -582,9 +580,9 @@ def test_foundry_chat_client_create_function_call_contents_basic(mock_ai_project
assert result[0].call_id == '["response_123", "call_123"]'
def test_foundry_chat_client_create_function_call_contents_no_submit_action(mock_ai_project_client: MagicMock) -> None:
def test_azure_ai_chat_client_create_function_call_contents_no_submit_action(mock_ai_project_client: MagicMock) -> None:
"""Test _create_function_call_contents when required_action is not SubmitToolOutputsAction."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
mock_event_data = MagicMock(spec=ThreadRun)
mock_event_data.required_action = MagicMock()
@@ -594,11 +592,11 @@ def test_foundry_chat_client_create_function_call_contents_no_submit_action(mock
assert result == []
def test_foundry_chat_client_create_function_call_contents_non_function_tool_call(
def test_azure_ai_chat_client_create_function_call_contents_non_function_tool_call(
mock_ai_project_client: MagicMock,
) -> None:
"""Test _create_function_call_contents with non-function tool call."""
chat_client = create_test_foundry_chat_client(mock_ai_project_client)
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client)
mock_tool_call = MagicMock()
@@ -620,11 +618,11 @@ def get_weather(
return f"The weather in {location} is sunny with a high of 25°C."
@skip_if_foundry_integration_tests_disabled
async def test_foundry_chat_client_get_response() -> None:
"""Test Foundry Chat Client response."""
async with FoundryChatClient(async_credential=AzureCliCredential()) as foundry_chat_client:
assert isinstance(foundry_chat_client, ChatClientProtocol)
@skip_if_azure_ai_integration_tests_disabled
async def test_azure_ai_chat_client_get_response() -> None:
"""Test Azure AI Chat Client response."""
async with AzureAIAgentClient(async_credential=AzureCliCredential()) as azure_ai_chat_client:
assert isinstance(azure_ai_chat_client, ChatClientProtocol)
messages: list[ChatMessage] = []
messages.append(
@@ -636,25 +634,25 @@ async def test_foundry_chat_client_get_response() -> None:
)
messages.append(ChatMessage(role="user", text="What's the weather like today?"))
# Test that the client can be used to get a response
response = await foundry_chat_client.get_response(messages=messages)
# Test that the project_client can be used to get a response
response = await azure_ai_chat_client.get_response(messages=messages)
assert response is not None
assert isinstance(response, ChatResponse)
assert any(word in response.text.lower() for word in ["sunny", "25"])
@skip_if_foundry_integration_tests_disabled
async def test_foundry_chat_client_get_response_tools() -> None:
"""Test Foundry Chat Client response with tools."""
async with FoundryChatClient(async_credential=AzureCliCredential()) as foundry_chat_client:
assert isinstance(foundry_chat_client, ChatClientProtocol)
@skip_if_azure_ai_integration_tests_disabled
async def test_azure_ai_chat_client_get_response_tools() -> None:
"""Test Azure AI Chat Client response with tools."""
async with AzureAIAgentClient(async_credential=AzureCliCredential()) as azure_ai_chat_client:
assert isinstance(azure_ai_chat_client, ChatClientProtocol)
messages: list[ChatMessage] = []
messages.append(ChatMessage(role="user", text="What's the weather like in Seattle?"))
# Test that the client can be used to get a response
response = await foundry_chat_client.get_response(
# Test that the project_client can be used to get a response
response = await azure_ai_chat_client.get_response(
messages=messages,
tools=[get_weather],
tool_choice="auto",
@@ -665,11 +663,11 @@ async def test_foundry_chat_client_get_response_tools() -> None:
assert any(word in response.text.lower() for word in ["sunny", "25"])
@skip_if_foundry_integration_tests_disabled
async def test_foundry_chat_client_streaming() -> None:
"""Test Foundry Chat Client streaming response."""
async with FoundryChatClient(async_credential=AzureCliCredential()) as foundry_chat_client:
assert isinstance(foundry_chat_client, ChatClientProtocol)
@skip_if_azure_ai_integration_tests_disabled
async def test_azure_ai_chat_client_streaming() -> None:
"""Test Azure AI Chat Client streaming response."""
async with AzureAIAgentClient(async_credential=AzureCliCredential()) as azure_ai_chat_client:
assert isinstance(azure_ai_chat_client, ChatClientProtocol)
messages: list[ChatMessage] = []
messages.append(
@@ -681,8 +679,8 @@ async def test_foundry_chat_client_streaming() -> None:
)
messages.append(ChatMessage(role="user", text="What's the weather like today?"))
# Test that the client can be used to get a response
response = foundry_chat_client.get_streaming_response(messages=messages)
# Test that the project_client can be used to get a response
response = azure_ai_chat_client.get_streaming_response(messages=messages)
full_message: str = ""
async for chunk in response:
@@ -695,17 +693,17 @@ async def test_foundry_chat_client_streaming() -> None:
assert any(word in full_message.lower() for word in ["sunny", "25"])
@skip_if_foundry_integration_tests_disabled
async def test_foundry_chat_client_streaming_tools() -> None:
"""Test Foundry Chat Client streaming response with tools."""
async with FoundryChatClient(async_credential=AzureCliCredential()) as foundry_chat_client:
assert isinstance(foundry_chat_client, ChatClientProtocol)
@skip_if_azure_ai_integration_tests_disabled
async def test_azure_ai_chat_client_streaming_tools() -> None:
"""Test Azure AI Chat Client streaming response with tools."""
async with AzureAIAgentClient(async_credential=AzureCliCredential()) as azure_ai_chat_client:
assert isinstance(azure_ai_chat_client, ChatClientProtocol)
messages: list[ChatMessage] = []
messages.append(ChatMessage(role="user", text="What's the weather like in Seattle?"))
# Test that the client can be used to get a response
response = foundry_chat_client.get_streaming_response(
# Test that the project_client can be used to get a response
response = azure_ai_chat_client.get_streaming_response(
messages=messages,
tools=[get_weather],
tool_choice="auto",
@@ -721,11 +719,11 @@ async def test_foundry_chat_client_streaming_tools() -> None:
assert any(word in full_message.lower() for word in ["sunny", "25"])
@skip_if_foundry_integration_tests_disabled
async def test_foundry_chat_client_agent_basic_run() -> None:
"""Test ChatAgent basic run functionality with FoundryChatClient."""
@skip_if_azure_ai_integration_tests_disabled
async def test_azure_ai_chat_client_agent_basic_run() -> None:
"""Test ChatAgent basic run functionality with AzureAIAgentClient."""
async with ChatAgent(
chat_client=FoundryChatClient(async_credential=AzureCliCredential()),
chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()),
) as agent:
# Run a simple query
response = await agent.run("Hello! Please respond with 'Hello World' exactly.")
@@ -737,11 +735,11 @@ async def test_foundry_chat_client_agent_basic_run() -> None:
assert "Hello World" in response.text
@skip_if_foundry_integration_tests_disabled
async def test_foundry_chat_client_agent_basic_run_streaming() -> None:
"""Test ChatAgent basic streaming functionality with FoundryChatClient."""
@skip_if_azure_ai_integration_tests_disabled
async def test_azure_ai_chat_client_agent_basic_run_streaming() -> None:
"""Test ChatAgent basic streaming functionality with AzureAIAgentClient."""
async with ChatAgent(
chat_client=FoundryChatClient(async_credential=AzureCliCredential()),
chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()),
) as agent:
# Run streaming query
full_message: str = ""
@@ -756,11 +754,11 @@ async def test_foundry_chat_client_agent_basic_run_streaming() -> None:
assert "streaming response test" in full_message.lower()
@skip_if_foundry_integration_tests_disabled
async def test_foundry_chat_client_agent_thread_persistence() -> None:
"""Test ChatAgent thread persistence across runs with FoundryChatClient."""
@skip_if_azure_ai_integration_tests_disabled
async def test_azure_ai_chat_client_agent_thread_persistence() -> None:
"""Test ChatAgent thread persistence across runs with AzureAIAgentClient."""
async with ChatAgent(
chat_client=FoundryChatClient(async_credential=AzureCliCredential()),
chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()),
instructions="You are a helpful assistant with good memory.",
) as agent:
# Create a new thread that will be reused
@@ -781,11 +779,11 @@ async def test_foundry_chat_client_agent_thread_persistence() -> None:
assert "42" in second_response.text
@skip_if_foundry_integration_tests_disabled
async def test_foundry_chat_client_agent_existing_thread_id() -> None:
"""Test ChatAgent existing thread ID functionality with FoundryChatClient."""
@skip_if_azure_ai_integration_tests_disabled
async def test_azure_ai_chat_client_agent_existing_thread_id() -> None:
"""Test ChatAgent existing thread ID functionality with AzureAIAgentClient."""
async with ChatAgent(
chat_client=FoundryChatClient(async_credential=AzureCliCredential()),
chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()),
instructions="You are a helpful assistant with good memory.",
) as first_agent:
# Start a conversation and get the thread ID
@@ -802,7 +800,7 @@ async def test_foundry_chat_client_agent_existing_thread_id() -> None:
# Now continue with the same thread ID in a new agent instance
async with ChatAgent(
chat_client=FoundryChatClient(thread_id=existing_thread_id, async_credential=AzureCliCredential()),
chat_client=AzureAIAgentClient(thread_id=existing_thread_id, async_credential=AzureCliCredential()),
instructions="You are a helpful assistant with good memory.",
) as second_agent:
# Create a thread with the existing ID
@@ -818,12 +816,12 @@ async def test_foundry_chat_client_agent_existing_thread_id() -> None:
assert "alice" in response2.text.lower()
@skip_if_foundry_integration_tests_disabled
async def test_foundry_chat_client_agent_code_interpreter():
"""Test ChatAgent with code interpreter through FoundryChatClient."""
@skip_if_azure_ai_integration_tests_disabled
async def test_azure_ai_chat_client_agent_code_interpreter():
"""Test ChatAgent with code interpreter through AzureAIAgentClient."""
async with ChatAgent(
chat_client=FoundryChatClient(async_credential=AzureCliCredential()),
chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()),
instructions="You are a helpful assistant that can write and execute Python code.",
tools=[HostedCodeInterpreterTool()],
) as agent:
@@ -837,11 +835,11 @@ async def test_foundry_chat_client_agent_code_interpreter():
assert "120" in response.text or "factorial" in response.text.lower()
@skip_if_foundry_integration_tests_disabled
async def test_foundry_chat_client_agent_with_mcp_tools() -> None:
"""Test MCP tools defined at agent creation with FoundryChatClient."""
@skip_if_azure_ai_integration_tests_disabled
async def test_azure_ai_chat_client_agent_with_mcp_tools() -> None:
"""Test MCP tools defined at agent creation with AzureAIAgentClient."""
async with ChatAgent(
chat_client=FoundryChatClient(async_credential=AzureCliCredential()),
chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()),
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
tools=MCPStreamableHTTPTool(
@@ -858,11 +856,11 @@ async def test_foundry_chat_client_agent_with_mcp_tools() -> None:
assert any(term in response.text.lower() for term in ["app service", "azure", "web", "application"])
@skip_if_foundry_integration_tests_disabled
async def test_foundry_chat_client_agent_level_tool_persistence():
"""Test that agent-level tools persist across multiple runs with FoundryChatClient."""
@skip_if_azure_ai_integration_tests_disabled
async def test_azure_ai_chat_client_agent_level_tool_persistence():
"""Test that agent-level tools persist across multiple runs with AzureAIAgentClient."""
async with ChatAgent(
chat_client=FoundryChatClient(async_credential=AzureCliCredential()),
chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()),
instructions="You are a helpful assistant that uses available tools.",
tools=[get_weather],
) as agent:
@@ -1,23 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
import importlib.metadata
from ._assistants_client import AzureAssistantsClient
from ._chat_client import AzureChatClient
from ._entra_id_authentication import get_entra_auth_token
from ._responses_client import AzureResponsesClient
from ._shared import AzureOpenAISettings
try:
__version__ = importlib.metadata.version(__name__)
except importlib.metadata.PackageNotFoundError:
__version__ = "0.0.0" # Fallback for development mode
__all__ = [
"AzureAssistantsClient",
"AzureChatClient",
"AzureOpenAISettings",
"AzureResponsesClient",
"__version__",
"get_entra_auth_token",
]
-87
View File
@@ -1,87 +0,0 @@
[project]
name = "agent-framework-azure"
description = "Azure integrations for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
license-files = ["LICENSE"]
urls.homepage = "https://learn.microsoft.com/en-us/semantic-kernel/overview/"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
urls.release_notes = "https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true"
urls.issues = "https://github.com/microsoft/agent-framework/issues"
classifiers = [
"License :: OSI Approved :: MIT License",
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Framework :: Pydantic :: 2",
"Typing :: Typed",
]
dependencies = [
"agent-framework",
"azure-identity >= 1.13"
]
[tool.uv]
prerelease = "if-necessary-or-explicit"
environments = [
"sys_platform == 'darwin'",
"sys_platform == 'linux'",
"sys_platform == 'win32'"
]
[tool.uv-dynamic-versioning]
fallback-version = "0.0.0"
[tool.pytest.ini_options]
testpaths = 'tests'
addopts = "-ra -q -r fEX"
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
filterwarnings = []
timeout = 120
[tool.ruff]
extend = "../../pyproject.toml"
[tool.coverage.run]
omit = [
"**/__init__.py"
]
[tool.pyright]
extend = "../../pyproject.toml"
exclude = ['tests']
[tool.mypy]
plugins = ['pydantic.mypy']
strict = true
python_version = "3.10"
ignore_missing_imports = true
disallow_untyped_defs = true
no_implicit_optional = true
check_untyped_defs = true
warn_return_any = true
show_error_codes = true
warn_unused_ignores = false
disallow_incomplete_defs = true
disallow_untyped_decorators = true
[tool.bandit]
targets = ["agent_framework_azure"]
exclude_dirs = ["tests"]
[tool.poe]
executor.type = "uv"
include = "../../shared_tasks.toml"
[tool.poe.tasks]
mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_azure"
test = "pytest --cov=agent_framework_azure --cov-report=term-missing:skip-covered tests"
[build-system]
requires = ["flit-core >= 3.9,<4.0"]
build-backend = "flit_core.buildapi"
@@ -1,28 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
def test_self_through_main():
try:
from agent_framework.azure import __version__
except ImportError:
__version__ = None
assert __version__ is not None
def test_self():
try:
from agent_framework_azure import __version__
except ImportError:
__version__ = None
assert __version__ is not None
def test_agent_framework():
try:
from agent_framework import __version__
except ImportError:
__version__ = None
assert __version__ is not None
+2 -2
View File
@@ -31,7 +31,7 @@ The following environment variables are used for configuration:
```python
import asyncio
from agent_framework.copilotstudio import CopilotStudioAgent
from agent_framework.microsoft import CopilotStudioAgent
async def main():
# Create agent using environment variables
@@ -49,7 +49,7 @@ asyncio.run(main())
```python
import asyncio
import os
from agent_framework.copilotstudio import CopilotStudioAgent, acquire_token
from agent_framework.microsoft import CopilotStudioAgent, acquire_token
from microsoft_agents.copilotstudio.client import ConnectionSettings, CopilotClient, PowerPlatformCloud, AgentType
async def main():
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "agent-framework-copilotstudio"
description = "Copilot Studio integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
@@ -1,28 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
def test_self_through_main() -> None:
try:
from agent_framework.copilotstudio import __version__
except ImportError:
__version__ = None
assert __version__ is not None
def test_self() -> None:
try:
from agent_framework_copilotstudio import __version__
except ImportError:
__version__ = None
assert __version__ is not None
def test_agent_framework() -> None:
try:
from agent_framework import __version__
except ImportError:
__version__ = None
assert __version__ is not None
@@ -8,7 +8,7 @@ to support Agent Framework specific features like workflows, traces, and functio
from typing import Any, Literal
from pydantic import BaseModel
from pydantic import BaseModel, ConfigDict
# Custom Agent Framework OpenAI event types for structured data
@@ -111,8 +111,7 @@ class AgentFrameworkExtraBody(BaseModel):
thread_id: str | None = None
input_data: dict[str, Any] | None = None
class Config:
extra = "allow" # Allow additional fields
model_config = ConfigDict(extra="allow")
# Agent Framework Request Model - Extending real OpenAI types
@@ -138,12 +137,10 @@ class AgentFrameworkRequest(BaseModel):
# Agent Framework extension - strongly typed
extra_body: AgentFrameworkExtraBody | None = None
class Config:
# Allow extra fields from OpenAI spec
extra = "allow"
entity_id: str | None = None # Allow entity_id as top-level field
model_config = ConfigDict(extra="allow")
def get_entity_id(self) -> str | None:
"""Get entity_id from either top-level field or extra_body."""
# Priority 1: Top-level entity_id field
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "agent-framework-devui"
description = "Debug UI for Microsoft Agent Framework with OpenAI-compatible API server."
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
@@ -5,7 +5,7 @@ import os
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework.azure import AzureChatClient
from agent_framework.azure import AzureOpenAIChatClient
def get_weather(
@@ -42,7 +42,7 @@ agent = ChatAgent(
and forecasts for any location. Always be helpful and provide detailed
weather information when asked.
""",
chat_client=AzureChatClient(
chat_client=AzureOpenAIChatClient(
api_key=os.environ.get("AZURE_OPENAI_API_KEY", ""),
),
tools=[get_weather, get_forecast],
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
-9
View File
@@ -1,9 +0,0 @@
# Get Started with Microsoft Agent Framework Foundry
Please install this package as the extra for `agent-framework`:
```bash
pip install agent-framework[foundry]
```
and see the [README](https://github.com/microsoft/agent-framework/tree/main/python/README.md) for more information.
@@ -1,28 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
def test_self_through_main():
try:
from agent_framework.foundry import __version__
except ImportError:
__version__ = None
assert __version__ is not None
def test_self():
try:
from agent_framework_foundry import __version__
except ImportError:
__version__ = None
assert __version__ is not None
def test_agent_framework():
try:
from agent_framework import __version__
except ImportError:
__version__ = None
assert __version__ is not None
@@ -16,7 +16,7 @@ You will be prompted for the following information:
- **package_description**: Brief description of the package (auto-generated from display name)
- **version**: Starting version (default: 0.1.0b1)
- **author_name**: Author name (default: Microsoft)
- **author_email**: Author email (default: SK-Support@microsoft.com)
- **author_email**: Author email (default: af-support@microsoft.com)
- **include_cli_script**: Whether to include a CLI script (y/n)
- **cli_script_name**: Name of CLI script if included
@@ -55,4 +55,4 @@ Don't forget to add your new package to the workspace:
1. Add to `python/pyproject.toml` dependencies
2. Add to `[tool.uv.sources]` section
3. Test installation with `uv run python -c "from agent_framework.lab.{name} import *"`
3. Test installation with `uv run python -c "from agent_framework.lab.{name} import *"`
@@ -4,7 +4,7 @@
"package_description": "{{ cookiecutter.package_display_name }} module for Microsoft Agent Framework.",
"version": "0.1.0b1",
"author_name": "Microsoft",
"author_email": "SK-Support@microsoft.com",
"author_email": "af-support@microsoft.com",
"license": "MIT",
"include_cli_script": ["y", "n"],
"cli_script_name": "{{ cookiecutter.package_name }}_cli",
@@ -17,4 +17,4 @@
"_copy_without_render": [
"*.py.typed"
]
}
}
@@ -92,4 +92,4 @@ addopts = "--strict-markers --strict-config"
markers = [
"unit: marks tests as unit tests",
"integration: marks tests as integration tests",
]
]
+2 -2
View File
@@ -1,7 +1,7 @@
[project]
name = "agent-framework-lab-gaia"
description = "GAIA benchmark module for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
@@ -83,4 +83,4 @@ addopts = "--strict-markers --strict-config"
markers = [
"unit: marks tests as unit tests",
"integration: marks tests as integration tests",
]
]
@@ -10,7 +10,7 @@ To run this sample, execute it from the root directory of the agent-framework re
This avoids namespace package conflicts that occur when running from within the gaia package directory.
"""
from agent_framework.foundry import FoundryChatClient
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from agent_framework.lab.gaia import GAIA, Evaluation, GAIATelemetryConfig, Prediction, Task
@@ -39,7 +39,7 @@ async def main() -> None:
# Create a single agent once and reuse it for all tasks
async with (
AzureCliCredential() as credential,
FoundryChatClient(async_credential=credential).create_agent(
AzureAIAgentClient(async_credential=credential).create_agent(
name="GaiaAgent",
instructions="Solve tasks to your best ability.",
) as agent,
+2 -2
View File
@@ -1,7 +1,7 @@
[project]
name = "agent-framework-lab-lightning"
description = "RL Module for Microsoft Agent Framework"
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
@@ -84,4 +84,4 @@ addopts = "--strict-markers --strict-config"
markers = [
"unit: marks tests as unit tests",
"integration: marks tests as integration tests",
]
]
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "agent-framework-lab-tau2"
description = "Tau2 Benchmark for Agent Framework."
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
+10 -12
View File
@@ -5,7 +5,7 @@ Highlights
- Flexible Agent Framework: build, orchestrate, and deploy AI agents and multi-agent systems
- Multi-Agent Orchestration: Group chat, sequential, concurrent, and handoff patterns
- Plugin Ecosystem: Extend with native functions, OpenAPI, Model Context Protocol (MCP), and more
- LLM Support: OpenAI, Azure OpenAI, Azure AI Foundry, and more
- LLM Support: OpenAI, Azure OpenAI, Azure AI, and more
- Runtime Support: In-process and distributed agent execution
- Multimodal: Text, vision, and function calling
- Cross-Platform: .NET and Python implementations
@@ -13,13 +13,11 @@ Highlights
## Quick Install
```bash
pip install agent-framework
# Optional: Add Azure integration
pip install agent-framework[azure]
# Optional: Add Foundry integration
pip install agent-framework[foundry]
pip install agent-framework[all]
# Optional: Add Azure AI integration
pip install agent-framework-azure-ai
# Optional: Both
pip install agent-framework[azure,foundry]
pip install agent-framework-azure-ai agent-framework-copilotstudio
```
Supported Platforms:
@@ -40,16 +38,16 @@ AZURE_OPENAI_API_KEY=...
AZURE_OPENAI_ENDPOINT=...
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME=...
...
FOUNDRY_PROJECT_ENDPOINT=...
FOUNDRY_MODEL_DEPLOYMENT_NAME=...
AZURE_AI_PROJECT_ENDPOINT=...
AZURE_AI_MODEL_DEPLOYMENT_NAME=...
```
You can also override environment variables by explicitly passing configuration parameters to the chat client constructor:
```python
from agent_framework.azure import AzureChatClient
from agent_framework.azure import AzureOpenAIChatClient
chat_client = AzureChatClient(
chat_client = AzureOpenAIChatClient(
api_key="",
endpoint="",
deployment_name="",
@@ -223,7 +221,7 @@ if __name__ == "__main__":
- [Getting Started with Agents](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/agents): Basic agent creation and tool usage
- [Chat Client Examples](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/chat_client): Direct chat client usage patterns
- [Azure Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure): Azure OpenAI and AI Foundry integration
- [Azure AI Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure-ai): Azure AI integration
- [.NET Orchestration Samples](https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/GettingStarted/Orchestration): Advanced multi-agent patterns (.NET)
## Agent Framework Documentation
@@ -1,31 +1,33 @@
# Copyright (c) Microsoft. All rights reserved.
import importlib
from typing import Any
PACKAGE_NAME = "agent_framework_azure"
PACKAGE_EXTRA = "azure"
_IMPORTS = [
"AzureAssistantsClient",
"AzureChatClient",
"AzureOpenAISettings",
"AzureResponsesClient",
"get_entra_auth_token",
"__version__",
]
_IMPORTS: dict[str, tuple[str, list[str]]] = {
"AzureAIAgentClient": ("agent_framework_azure_ai", ["azure_ai", "azure"]),
"AzureOpenAIAssistantsClient": ("agent_framework.azure._assistants_client", []),
"AzureOpenAIChatClient": ("agent_framework.azure._chat_client", []),
"AzureAISettings": ("agent_framework_azure_ai", ["azure_ai", "azure"]),
"AzureOpenAISettings": ("agent_framework.azure._shared", []),
"AzureOpenAIResponsesClient": ("agent_framework.azure._responses_client", []),
"get_entra_auth_token": ("agent_framework.azure._entra_id_authentication", []),
}
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
package_name, package_extra = _IMPORTS[name]
try:
return getattr(importlib.import_module(PACKAGE_NAME), name)
return getattr(importlib.import_module(package_name), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"The '{PACKAGE_EXTRA}' extra is not installed, "
f"please do `pip install agent-framework[{PACKAGE_EXTRA}]`"
f"The {' or '.join(package_extra)} extra is not installed, "
f"please use `pip install agent-framework[{package_extra[0]}]`, "
"or update your requirements.txt or pyproject.toml file."
) from exc
raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.")
raise AttributeError(f"Module `azure` has no attribute {name}.")
def __dir__() -> list[str]:
return _IMPORTS
return list(_IMPORTS.keys())
@@ -1,19 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
from agent_framework_azure import (
AzureAssistantsClient,
AzureChatClient,
AzureOpenAISettings,
AzureResponsesClient,
__version__,
get_entra_auth_token,
)
__all__ = [
"AzureAssistantsClient",
"AzureChatClient",
"AzureOpenAISettings",
"AzureResponsesClient",
"__version__",
"get_entra_auth_token",
]
@@ -3,21 +3,21 @@
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, ClassVar
from agent_framework.exceptions import ServiceInitializationError
from agent_framework.openai import OpenAIAssistantsClient
from openai.lib.azure import AsyncAzureADTokenProvider, AsyncAzureOpenAI
from pydantic import SecretStr, ValidationError
from pydantic.networks import AnyUrl
from ..exceptions import ServiceInitializationError
from ..openai import OpenAIAssistantsClient
from ._shared import AzureOpenAISettings
if TYPE_CHECKING:
from azure.core.credentials import TokenCredential
__all__ = ["AzureAssistantsClient"]
__all__ = ["AzureOpenAIAssistantsClient"]
class AzureAssistantsClient(OpenAIAssistantsClient):
class AzureOpenAIAssistantsClient(OpenAIAssistantsClient):
"""Azure OpenAI Assistants client."""
DEFAULT_AZURE_API_VERSION: ClassVar[str] = "2024-05-01-preview"
@@ -6,16 +6,6 @@ import sys
from collections.abc import Mapping
from typing import Any, TypeVar
from agent_framework import (
ChatResponse,
ChatResponseUpdate,
CitationAnnotation,
TextContent,
use_function_invocation,
)
from agent_framework.exceptions import ServiceInitializationError
from agent_framework.observability import use_observability
from agent_framework.openai._chat_client import OpenAIBaseChatClient
from azure.core.credentials import TokenCredential
from openai.lib.azure import AsyncAzureADTokenProvider, AsyncAzureOpenAI
from openai.types.chat.chat_completion import Choice
@@ -23,6 +13,16 @@ from openai.types.chat.chat_completion_chunk import Choice as ChunkChoice
from pydantic import SecretStr, ValidationError
from pydantic.networks import AnyUrl
from .._tools import use_function_invocation
from .._types import (
ChatResponse,
ChatResponseUpdate,
CitationAnnotation,
TextContent,
)
from ..exceptions import ServiceInitializationError
from ..observability import use_observability
from ..openai._chat_client import OpenAIBaseChatClient
from ._shared import (
AzureOpenAIConfigMixin,
AzureOpenAISettings,
@@ -36,13 +36,13 @@ else:
logger: logging.Logger = logging.getLogger(__name__)
TChatResponse = TypeVar("TChatResponse", ChatResponse, ChatResponseUpdate)
TAzureChatClient = TypeVar("TAzureChatClient", bound="AzureChatClient")
TAzureOpenAIChatClient = TypeVar("TAzureOpenAIChatClient", bound="AzureOpenAIChatClient")
@use_function_invocation
@use_observability
class AzureChatClient(AzureOpenAIConfigMixin, OpenAIBaseChatClient):
"""Azure Chat completion class."""
class AzureOpenAIChatClient(AzureOpenAIConfigMixin, OpenAIBaseChatClient):
"""Azure OpenAI Chat completion class."""
def __init__(
self,
@@ -123,7 +123,7 @@ class AzureChatClient(AzureOpenAIConfigMixin, OpenAIBaseChatClient):
)
@classmethod
def from_dict(cls: type[TAzureChatClient], settings: dict[str, Any]) -> TAzureChatClient:
def from_dict(cls: type[TAzureOpenAIChatClient], settings: dict[str, Any]) -> TAzureOpenAIChatClient:
"""Initialize an Azure OpenAI service from a dictionary of settings.
Args:
@@ -3,9 +3,10 @@
import logging
from typing import TYPE_CHECKING, Any
from agent_framework.exceptions import ServiceInvalidAuthError
from azure.core.exceptions import ClientAuthenticationError
from ..exceptions import ServiceInvalidAuthError
if TYPE_CHECKING:
from azure.core.credentials import TokenCredential
from azure.core.credentials_async import AsyncTokenCredential
@@ -4,26 +4,26 @@ from collections.abc import Mapping
from typing import Any, TypeVar
from urllib.parse import urljoin
from agent_framework import use_function_invocation
from agent_framework.exceptions import ServiceInitializationError
from agent_framework.observability import use_observability
from agent_framework.openai._responses_client import OpenAIBaseResponsesClient
from azure.core.credentials import TokenCredential
from openai.lib.azure import AsyncAzureADTokenProvider, AsyncAzureOpenAI
from pydantic import SecretStr, ValidationError
from pydantic.networks import AnyUrl
from .._tools import use_function_invocation
from ..exceptions import ServiceInitializationError
from ..observability import use_observability
from ..openai._responses_client import OpenAIBaseResponsesClient
from ._shared import (
AzureOpenAIConfigMixin,
AzureOpenAISettings,
)
TAzureResponsesClient = TypeVar("TAzureResponsesClient", bound="AzureResponsesClient")
TAzureOpenAIResponsesClient = TypeVar("TAzureOpenAIResponsesClient", bound="AzureOpenAIResponsesClient")
@use_observability
@use_function_invocation
class AzureResponsesClient(AzureOpenAIConfigMixin, OpenAIBaseResponsesClient):
class AzureOpenAIResponsesClient(AzureOpenAIConfigMixin, OpenAIBaseResponsesClient):
"""Azure Responses completion class."""
def __init__(
@@ -115,7 +115,7 @@ class AzureResponsesClient(AzureOpenAIConfigMixin, OpenAIBaseResponsesClient):
)
@classmethod
def from_dict(cls: type[TAzureResponsesClient], settings: dict[str, Any]) -> TAzureResponsesClient:
def from_dict(cls: type[TAzureOpenAIResponsesClient], settings: dict[str, Any]) -> TAzureOpenAIResponsesClient:
"""Initialize an Open AI service from a dictionary of settings.
Args:
@@ -6,14 +6,14 @@ from collections.abc import Awaitable, Callable, Mapping
from copy import copy
from typing import Any, ClassVar, Final
from agent_framework._pydantic import AFBaseSettings, HTTPsUrl
from agent_framework._telemetry import APP_INFO, USER_AGENT_KEY, prepend_agent_framework_to_user_agent
from agent_framework.exceptions import ServiceInitializationError
from agent_framework.openai._shared import OpenAIBase
from azure.core.credentials import TokenCredential
from openai.lib.azure import AsyncAzureOpenAI
from pydantic import ConfigDict, SecretStr, model_validator, validate_call
from .._pydantic import AFBaseSettings, HTTPsUrl
from .._telemetry import APP_INFO, USER_AGENT_KEY, prepend_agent_framework_to_user_agent
from ..exceptions import ServiceInitializationError
from ..openai._shared import OpenAIBase
from ._entra_id_authentication import get_entra_auth_token
if sys.version_info >= (3, 11):
@@ -37,7 +37,12 @@ class AzureOpenAISettings(AFBaseSettings):
with the encoding 'utf-8'. If the settings are not found in the .env file, the settings
are ignored; however, validation will fail alerting that the settings are missing.
Attributes:
Args:
endpoint: The endpoint of the Azure deployment. This value
can be found in the Keys & Endpoint section when examining
your resource from the Azure portal, the endpoint should end in openai.azure.com.
If both base_url and endpoint are supplied, base_url will be used.
(Env var AZURE_OPENAI_ENDPOINT)
chat_deployment_name: The name of the Azure Chat deployment. This value
will correspond to the custom name you chose for your deployment
when you deployed a model. This value can be found under
@@ -50,59 +55,18 @@ class AzureOpenAISettings(AFBaseSettings):
Resource Management > Deployments in the Azure portal or, alternatively,
under Management > Deployments in Azure AI Foundry.
(Env var AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME)
text_deployment_name: The name of the Azure Text deployment. This value
will correspond to the custom name you chose for your deployment
when you deployed a model. This value can be found under
Resource Management > Deployments in the Azure portal or, alternatively,
under Management > Deployments in Azure AI Foundry.
(Env var AZURE_OPENAI_TEXT_DEPLOYMENT_NAME)
embedding_deployment_name: The name of the Azure Embedding deployment. This value
will correspond to the custom name you chose for your deployment
when you deployed a model. This value can be found under
Resource Management > Deployments in the Azure portal or, alternatively,
under Management > Deployments in Azure AI Foundry.
(Env var AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME)
text_to_image_deployment_name: The name of the Azure Text to Image deployment. This
value will correspond to the custom name you chose for your deployment
when you deployed a model. This value can be found under
Resource Management > Deployments in the Azure portal or, alternatively,
under Management > Deployments in Azure AI Foundry.
(Env var AZURE_OPENAI_TEXT_TO_IMAGE_DEPLOYMENT_NAME)
audio_to_text_deployment_name: The name of the Azure Audio to Text deployment. This
value will correspond to the custom name you chose for your deployment
when you deployed a model. This value can be found under
Resource Management > Deployments in the Azure portal or, alternatively,
under Management > Deployments in Azure AI Foundry.
(Env var AZURE_OPENAI_AUDIO_TO_TEXT_DEPLOYMENT_NAME)
text_to_audio_deployment_name: The name of the Azure Text to Audio deployment. This
value will correspond to the custom name you chose for your deployment
when you deployed a model. This value can be found under
Resource Management > Deployments in the Azure portal or, alternatively,
under Management > Deployments in Azure AI Foundry.
(Env var AZURE_OPENAI_TEXT_TO_AUDIO_DEPLOYMENT_NAME)
realtime_deployment_name: The name of the Azure Realtime deployment. This value
will correspond to the custom name you chose for your deployment
when you deployed a model. This value can be found under
Resource Management > Deployments in the Azure portal or, alternatively,
under Management > Deployments in Azure AI Foundry.
(Env var AZURE_OPENAI_REALTIME_DEPLOYMENT_NAME)
api_key: The API key for the Azure deployment. This value can be
found in the Keys & Endpoint section when examining your resource in
the Azure portal. You can use either KEY1 or KEY2.
(Env var AZURE_OPENAI_API_KEY)
api_version: The API version to use. The default value is `default_api_version`.
(Env var AZURE_OPENAI_API_VERSION)
base_url: The url of the Azure deployment. This value
can be found in the Keys & Endpoint section when examining
your resource from the Azure portal, the base_url consists of the endpoint,
followed by /openai/deployments/{deployment_name}/,
use endpoint if you only want to supply the endpoint.
(Env var AZURE_OPENAI_BASE_URL)
endpoint: The endpoint of the Azure deployment. This value
can be found in the Keys & Endpoint section when examining
your resource from the Azure portal, the endpoint should end in openai.azure.com.
If both base_url and endpoint are supplied, base_url will be used.
(Env var AZURE_OPENAI_ENDPOINT)
api_version: The API version to use. The default value is `default_api_version`.
(Env var AZURE_OPENAI_API_VERSION)
token_endpoint: The token endpoint to use to retrieve the authentication token.
The default value is `default_token_endpoint`.
(Env var AZURE_OPENAI_TOKEN_ENDPOINT)
@@ -110,8 +74,6 @@ class AzureOpenAISettings(AFBaseSettings):
The default value is "2024-10-21".
default_token_endpoint: The default token endpoint to use if not specified.
The default value is "https://cognitiveservices.azure.com/.default".
Parameters:
env_file_path: The path to the .env file to load settings from.
env_file_encoding: The encoding of the .env file, defaults to 'utf-8'.
"""
@@ -120,12 +82,6 @@ class AzureOpenAISettings(AFBaseSettings):
chat_deployment_name: str | None = None
responses_deployment_name: str | None = None
text_deployment_name: str | None = None
embedding_deployment_name: str | None = None
text_to_image_deployment_name: str | None = None
audio_to_text_deployment_name: str | None = None
text_to_audio_deployment_name: str | None = None
realtime_deployment_name: str | None = None
endpoint: HTTPsUrl | None = None
base_url: HTTPsUrl | None = None
api_key: SecretStr | None = None
@@ -1,24 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
import importlib
from typing import Any
PACKAGE_NAME = "agent_framework_copilotstudio"
PACKAGE_EXTRA = "copilotstudio"
_IMPORTS = ["CopilotStudioAgent", "__version__", "acquire_token"]
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
try:
return getattr(importlib.import_module(PACKAGE_NAME), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"The '{PACKAGE_EXTRA}' extra is not installed, "
f"please do `pip install agent-framework[{PACKAGE_EXTRA}]`"
) from exc
raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.")
def __dir__() -> list[str]:
return _IMPORTS
@@ -1,24 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
import importlib
from typing import Any
PACKAGE_NAME = "agent_framework_foundry"
PACKAGE_EXTRA = "foundry"
_IMPORTS = ["__version__", "FoundryChatClient", "FoundrySettings"]
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
try:
return getattr(importlib.import_module(PACKAGE_NAME), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"The '{PACKAGE_EXTRA}' extra is not installed, "
f"please do `pip install agent-framework[{PACKAGE_EXTRA}]`"
) from exc
raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.")
def __dir__() -> list[str]:
return _IMPORTS
@@ -1,5 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
from agent_framework_foundry import FoundryChatClient, FoundrySettings, __version__
__all__ = ["FoundryChatClient", "FoundrySettings", "__version__"]
@@ -0,0 +1,30 @@
# Copyright (c) Microsoft. All rights reserved.
import importlib
from typing import Any
PACKAGE_NAME = "agent_framework_copilotstudio"
PACKAGE_EXTRA = ["microsoft-copilotstudio", "copilotstudio"]
_IMPORTS: dict[str, tuple[str, list[str]]] = {
"CopilotStudioAgent": ("agent_framework_copilotstudio", ["microsoft-copilotstudio", "copilotstudio"]),
"__version__": ("agent_framework_copilotstudio", ["microsoft-copilotstudio", "copilotstudio"]),
"acquire_token": ("agent_framework_copilotstudio", ["microsoft-copilotstudio", "copilotstudio"]),
}
def __getattr__(name: str) -> Any:
if name in _IMPORTS:
package_name, package_extra = _IMPORTS[name]
try:
return getattr(importlib.import_module(package_name), name)
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
f"The {' or '.join(package_extra)} extra is not installed, "
f"please use `pip install agent-framework[{package_extra[0]}]`, "
"or update your requirements.txt or pyproject.toml file."
) from exc
raise AttributeError(f"Module `azure` has no attribute {name}.")
def __dir__() -> list[str]:
return list(_IMPORTS.keys())
@@ -82,7 +82,7 @@ class OpenAISettings(AFBaseSettings):
encoding 'utf-8'. If the settings are not found in the .env file, the settings are ignored;
however, validation will fail alerting that the settings are missing.
Attributes:
Args:
api_key: OpenAI API key, see https://platform.openai.com/account/api-keys
(Env var OPENAI_API_KEY)
base_url: The base URL for the OpenAI API.
@@ -93,21 +93,6 @@ class OpenAISettings(AFBaseSettings):
(Env var OPENAI_CHAT_MODEL_ID)
responses_model_id: The OpenAI responses model ID to use, for example, gpt-4o or o1.
(Env var OPENAI_RESPONSES_MODEL_ID)
text_model_id: The OpenAI text model ID to use, for example, gpt-3.5-turbo-instruct.
(Env var OPENAI_TEXT_MODEL_ID)
embedding_model_id: The OpenAI embedding model ID to use, for example, text-embedding-ada-002.
(Env var OPENAI_EMBEDDING_MODEL_ID)
text_to_image_model_id: The OpenAI text to image model ID to use, for example, dall-e-3.
(Env var OPENAI_TEXT_TO_IMAGE_MODEL_ID)
audio_to_text_model_id: The OpenAI audio to text model ID to use, for example, whisper-1.
(Env var OPENAI_AUDIO_TO_TEXT_MODEL_ID)
text_to_audio_model_id: The OpenAI text to audio model ID to use, for example, jukebox-1.
(Env var OPENAI_TEXT_TO_AUDIO_MODEL_ID)
realtime_model_id: The OpenAI realtime model ID to use,
for example, gpt-4o-realtime-preview-2024-12-17.
(Env var OPENAI_REALTIME_MODEL_ID)
Parameters:
env_file_path: The path to the .env file to load settings from.
env_file_encoding: The encoding of the .env file, defaults to 'utf-8'.
"""
@@ -119,12 +104,6 @@ class OpenAISettings(AFBaseSettings):
org_id: str | None = None
chat_model_id: str | None = None
responses_model_id: str | None = None
text_model_id: str | None = None
embedding_model_id: str | None = None
text_to_image_model_id: str | None = None
audio_to_text_model_id: str | None = None
text_to_audio_model_id: str | None = None
realtime_model_id: str | None = None
class OpenAIBase(AFBaseModel):
+26 -19
View File
@@ -1,10 +1,10 @@
[project]
name = "agent-framework"
description = "Microsoft Agent Framework for building AI Agents with Python."
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
version = "0.1.0b1" # TODO: decide on initial version and versioning strategy
license-files = ["LICENSE"]
urls.homepage = "https://learn.microsoft.com/en-us/semantic-kernel/overview/"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
@@ -24,31 +24,38 @@ classifiers = [
]
dependencies = [
"openai>=1.99.0",
"pydantic>=2.11.7",
"pydantic-settings>=2.10.1",
"typing-extensions>=4.14.0",
"opentelemetry-api ~= 1.24",
"opentelemetry-sdk ~= 1.24",
"pydantic>=2,<3",
"pydantic-settings>=2,<3",
"typing-extensions",
"opentelemetry-api>=1.24",
"opentelemetry-sdk>=1.24",
"mcp[ws]>=1.13",
"azure-monitor-opentelemetry>=1.7.0",
"azure-monitor-opentelemetry-exporter>=1.0.0b41",
"opentelemetry-exporter-otlp-proto-grpc>=1.36.0",
"opentelemetry-semantic-conventions-ai>=0.4.13",
"aiofiles>=24.1.0"
"aiofiles>=24.1.0",
"azure-identity>=1,<2"
]
[project.optional-dependencies]
azure = [
"agent-framework-azure"
azure-ai = [
"agent-framework-azure-ai"
]
foundry = [
"agent-framework-foundry"
azure = [
"agent-framework-azure-ai"
]
microsoft-copilotstudio = [
"agent-framework-copilotstudio"
]
microsoft = [
"agent-framework-copilotstudio"
]
redis = [
"agent-framework-redis"
]
viz = [
"graphviz>=0.20.0",
"graphviz>=0.20.0"
]
runtime = [
"agent-framework-runtime"
@@ -60,12 +67,13 @@ devui = [
"agent-framework-devui"
]
all = [
"agent-framework-azure",
"agent-framework-foundry",
"agent_framework_copilotstudio",
"agent-framework-azure-ai",
"agent-framework-runtime",
"agent-framework-mem0",
"agent-framework-redis",
"agent-framework-devui"
"agent-framework-devui",
"graphviz>=0.20.0"
]
[tool.uv]
@@ -81,10 +89,9 @@ fallback-version = "0.0.0"
[tool.pytest.ini_options]
testpaths = [
'tests',
'tests',
'packages/main/tests',
'packages/azure/tests',
'packages/foundry/tests',
'packages/azure-ai/tests',
'packages/copilotstudio/tests',
'packages/mem0/tests',
'packages/runtime/tests'
@@ -1,9 +1,10 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import Any
from agent_framework import ChatMessage
from pytest import fixture
from agent_framework import ChatMessage
# region: Connector Settings fixtures
@fixture
@@ -5,6 +5,9 @@ from typing import Annotated
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from azure.identity import AzureCliCredential
from pydantic import Field
from agent_framework import (
AgentRunResponse,
AgentRunResponseUpdate,
@@ -17,11 +20,8 @@ from agent_framework import (
HostedCodeInterpreterTool,
TextContent,
)
from agent_framework.azure import AzureOpenAIAssistantsClient
from agent_framework.exceptions import ServiceInitializationError
from azure.identity import AzureCliCredential
from pydantic import Field
from agent_framework_azure import AzureAssistantsClient
skip_if_azure_integration_tests_disabled = pytest.mark.skipif(
os.getenv("RUN_INTEGRATION_TESTS", "false").lower() != "true"
@@ -39,9 +39,9 @@ def create_test_azure_assistants_client(
assistant_name: str | None = None,
thread_id: str | None = None,
should_delete_assistant: bool = False,
) -> AzureAssistantsClient:
"""Helper function to create AzureAssistantsClient instances for testing, bypassing Pydantic validation."""
return AzureAssistantsClient.model_construct(
) -> AzureOpenAIAssistantsClient:
"""Helper function to create AzureOpenAIAssistantsClient instances for testing, bypassing Pydantic validation."""
return AzureOpenAIAssistantsClient.model_construct(
ai_model_id=deployment_name or "test_chat_deployment",
assistant_id=assistant_id,
assistant_name=assistant_name,
@@ -79,7 +79,7 @@ def mock_async_azure_openai() -> MagicMock:
def test_azure_assistants_client_init_with_client(mock_async_azure_openai: MagicMock) -> None:
"""Test AzureAssistantsClient initialization with existing client."""
"""Test AzureOpenAIAssistantsClient initialization with existing client."""
chat_client = create_test_azure_assistants_client(
mock_async_azure_openai,
deployment_name="test_chat_deployment",
@@ -99,8 +99,8 @@ def test_azure_assistants_client_init_auto_create_client(
azure_openai_unit_test_env: dict[str, str],
mock_async_azure_openai: MagicMock,
) -> None:
"""Test AzureAssistantsClient initialization with auto-created client."""
chat_client = AzureAssistantsClient.model_construct(
"""Test AzureOpenAIAssistantsClient initialization with auto-created client."""
chat_client = AzureOpenAIAssistantsClient.model_construct(
ai_model_id=azure_openai_unit_test_env["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"],
assistant_id=None,
assistant_name="TestAssistant",
@@ -119,26 +119,26 @@ def test_azure_assistants_client_init_auto_create_client(
def test_azure_assistants_client_init_validation_fail() -> None:
"""Test AzureAssistantsClient initialization with validation failure."""
"""Test AzureOpenAIAssistantsClient initialization with validation failure."""
with pytest.raises(ServiceInitializationError):
# Force failure by providing invalid deployment name type - this should cause validation to fail
AzureAssistantsClient(deployment_name=123, api_key="valid-key") # type: ignore
AzureOpenAIAssistantsClient(deployment_name=123, api_key="valid-key") # type: ignore
@pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"]], indirect=True)
def test_azure_assistants_client_init_missing_deployment_name(azure_openai_unit_test_env: dict[str, str]) -> None:
"""Test AzureAssistantsClient initialization with missing deployment name."""
"""Test AzureOpenAIAssistantsClient initialization with missing deployment name."""
with pytest.raises(ServiceInitializationError):
AzureAssistantsClient(
AzureOpenAIAssistantsClient(
api_key=azure_openai_unit_test_env.get("AZURE_OPENAI_API_KEY", "test-key"), env_file_path="nonexistent.env"
)
def test_azure_assistants_client_init_with_default_headers(azure_openai_unit_test_env: dict[str, str]) -> None:
"""Test AzureAssistantsClient initialization with default headers."""
"""Test AzureOpenAIAssistantsClient initialization with default headers."""
default_headers = {"X-Unit-Test": "test-guid"}
chat_client = AzureAssistantsClient(
chat_client = AzureOpenAIAssistantsClient(
deployment_name="test_chat_deployment",
api_key=azure_openai_unit_test_env["AZURE_OPENAI_API_KEY"],
endpoint=azure_openai_unit_test_env["AZURE_OPENAI_ENDPOINT"],
@@ -225,11 +225,11 @@ async def test_azure_assistants_client_async_context_manager(mock_async_azure_op
def test_azure_assistants_client_serialize(azure_openai_unit_test_env: dict[str, str]) -> None:
"""Test serialization of AzureAssistantsClient."""
"""Test serialization of AzureOpenAIAssistantsClient."""
default_headers = {"X-Unit-Test": "test-guid"}
# Test basic initialization and to_dict
chat_client = AzureAssistantsClient(
chat_client = AzureOpenAIAssistantsClient(
deployment_name="test_chat_deployment",
assistant_id="test-assistant-id",
assistant_name="TestAssistant",
@@ -265,7 +265,7 @@ def get_weather(
@skip_if_azure_integration_tests_disabled
async def test_azure_assistants_client_get_response() -> None:
"""Test Azure Assistants Client response."""
async with AzureAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client:
async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client:
assert isinstance(azure_assistants_client, ChatClientProtocol)
messages: list[ChatMessage] = []
@@ -289,7 +289,7 @@ async def test_azure_assistants_client_get_response() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_assistants_client_get_response_tools() -> None:
"""Test Azure Assistants Client response with tools."""
async with AzureAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client:
async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client:
assert isinstance(azure_assistants_client, ChatClientProtocol)
messages: list[ChatMessage] = []
@@ -310,7 +310,7 @@ async def test_azure_assistants_client_get_response_tools() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_assistants_client_streaming() -> None:
"""Test Azure Assistants Client streaming response."""
async with AzureAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client:
async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client:
assert isinstance(azure_assistants_client, ChatClientProtocol)
messages: list[ChatMessage] = []
@@ -340,7 +340,7 @@ async def test_azure_assistants_client_streaming() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_assistants_client_streaming_tools() -> None:
"""Test Azure Assistants Client streaming response with tools."""
async with AzureAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client:
async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client:
assert isinstance(azure_assistants_client, ChatClientProtocol)
messages: list[ChatMessage] = []
@@ -367,14 +367,14 @@ async def test_azure_assistants_client_streaming_tools() -> None:
async def test_azure_assistants_client_with_existing_assistant() -> None:
"""Test Azure Assistants Client with existing assistant ID."""
# First create an assistant to use in the test
async with AzureAssistantsClient(credential=AzureCliCredential()) as temp_client:
async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()) as temp_client:
# Get the assistant ID by triggering assistant creation
messages = [ChatMessage(role="user", text="Hello")]
await temp_client.get_response(messages=messages)
assistant_id = temp_client.assistant_id
# Now test using the existing assistant
async with AzureAssistantsClient(
async with AzureOpenAIAssistantsClient(
assistant_id=assistant_id, credential=AzureCliCredential()
) as azure_assistants_client:
assert isinstance(azure_assistants_client, ChatClientProtocol)
@@ -392,9 +392,9 @@ async def test_azure_assistants_client_with_existing_assistant() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_assistants_agent_basic_run():
"""Test ChatAgent basic run functionality with AzureAssistantsClient."""
"""Test ChatAgent basic run functionality with AzureOpenAIAssistantsClient."""
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
) as agent:
# Run a simple query
response = await agent.run("Hello! Please respond with 'Hello World' exactly.")
@@ -408,9 +408,9 @@ async def test_azure_assistants_agent_basic_run():
@skip_if_azure_integration_tests_disabled
async def test_azure_assistants_agent_basic_run_streaming():
"""Test ChatAgent basic streaming functionality with AzureAssistantsClient."""
"""Test ChatAgent basic streaming functionality with AzureOpenAIAssistantsClient."""
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
) as agent:
# Run streaming query
full_message: str = ""
@@ -427,9 +427,9 @@ async def test_azure_assistants_agent_basic_run_streaming():
@skip_if_azure_integration_tests_disabled
async def test_azure_assistants_agent_thread_persistence():
"""Test ChatAgent thread persistence across runs with AzureAssistantsClient."""
"""Test ChatAgent thread persistence across runs with AzureOpenAIAssistantsClient."""
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant with good memory.",
) as agent:
# Create a new thread that will be reused
@@ -460,7 +460,7 @@ async def test_azure_assistants_agent_existing_thread_id():
existing_thread_id = None
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=[get_weather],
) as agent:
@@ -480,7 +480,7 @@ async def test_azure_assistants_agent_existing_thread_id():
# Now continue with the same thread ID in a new agent instance
async with ChatAgent(
chat_client=AzureAssistantsClient(thread_id=existing_thread_id, credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(thread_id=existing_thread_id, credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=[get_weather],
) as agent:
@@ -499,10 +499,10 @@ async def test_azure_assistants_agent_existing_thread_id():
@skip_if_azure_integration_tests_disabled
async def test_azure_assistants_agent_code_interpreter():
"""Test ChatAgent with code interpreter through AzureAssistantsClient."""
"""Test ChatAgent with code interpreter through AzureOpenAIAssistantsClient."""
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can write and execute Python code.",
tools=[HostedCodeInterpreterTool()],
) as agent:
@@ -521,7 +521,7 @@ async def test_azure_assistants_client_agent_level_tool_persistence():
"""Test that agent-level tools persist across multiple runs with Azure Assistants Client."""
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that uses available tools.",
tools=[get_weather], # Agent-level tool
) as agent:
@@ -547,8 +547,8 @@ def test_azure_assistants_client_entra_id_authentication() -> None:
mock_credential = MagicMock()
with (
patch("agent_framework_azure._assistants_client.AzureOpenAISettings") as mock_settings_class,
patch("agent_framework_azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client,
patch("agent_framework.azure._assistants_client.AzureOpenAISettings") as mock_settings_class,
patch("agent_framework.azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client,
patch("agent_framework.openai.OpenAIAssistantsClient.__init__", return_value=None),
):
mock_settings = MagicMock()
@@ -561,7 +561,7 @@ def test_azure_assistants_client_entra_id_authentication() -> None:
mock_settings.base_url = None
mock_settings_class.return_value = mock_settings
client = AzureAssistantsClient(
client = AzureOpenAIAssistantsClient(
deployment_name="test-deployment",
api_key="placeholder-key",
endpoint="https://test-endpoint.openai.azure.com",
@@ -578,12 +578,12 @@ def test_azure_assistants_client_entra_id_authentication() -> None:
assert call_args["azure_ad_token"] == "entra-token-12345"
assert client is not None
assert isinstance(client, AzureAssistantsClient)
assert isinstance(client, AzureOpenAIAssistantsClient)
def test_azure_assistants_client_no_authentication_error() -> None:
"""Test authentication validation error when no auth provided."""
with patch("agent_framework_azure._assistants_client.AzureOpenAISettings") as mock_settings_class:
with patch("agent_framework.azure._assistants_client.AzureOpenAISettings") as mock_settings_class:
mock_settings = MagicMock()
mock_settings.chat_deployment_name = "test-deployment"
mock_settings.api_key = None # No API key
@@ -592,7 +592,7 @@ def test_azure_assistants_client_no_authentication_error() -> None:
# Test missing authentication raises error
with pytest.raises(ServiceInitializationError, match="API key, ad_token, or ad_token_provider is required"):
AzureAssistantsClient(
AzureOpenAIAssistantsClient(
deployment_name="test-deployment",
endpoint="https://test-endpoint.openai.azure.com",
# No authentication provided at all
@@ -602,8 +602,8 @@ def test_azure_assistants_client_no_authentication_error() -> None:
def test_azure_assistants_client_ad_token_authentication() -> None:
"""Test ad_token authentication client parameter path."""
with (
patch("agent_framework_azure._assistants_client.AzureOpenAISettings") as mock_settings_class,
patch("agent_framework_azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client,
patch("agent_framework.azure._assistants_client.AzureOpenAISettings") as mock_settings_class,
patch("agent_framework.azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client,
patch("agent_framework.openai.OpenAIAssistantsClient.__init__", return_value=None),
):
mock_settings = MagicMock()
@@ -614,7 +614,7 @@ def test_azure_assistants_client_ad_token_authentication() -> None:
mock_settings.base_url = None
mock_settings_class.return_value = mock_settings
client = AzureAssistantsClient(
client = AzureOpenAIAssistantsClient(
deployment_name="test-deployment",
endpoint="https://test-endpoint.openai.azure.com",
ad_token="test-ad-token-12345",
@@ -626,7 +626,7 @@ def test_azure_assistants_client_ad_token_authentication() -> None:
assert call_args["azure_ad_token"] == "test-ad-token-12345"
assert client is not None
assert isinstance(client, AzureAssistantsClient)
assert isinstance(client, AzureOpenAIAssistantsClient)
def test_azure_assistants_client_ad_token_provider_authentication() -> None:
@@ -636,8 +636,8 @@ def test_azure_assistants_client_ad_token_provider_authentication() -> None:
mock_token_provider = MagicMock(spec=AsyncAzureADTokenProvider)
with (
patch("agent_framework_azure._assistants_client.AzureOpenAISettings") as mock_settings_class,
patch("agent_framework_azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client,
patch("agent_framework.azure._assistants_client.AzureOpenAISettings") as mock_settings_class,
patch("agent_framework.azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client,
patch("agent_framework.openai.OpenAIAssistantsClient.__init__", return_value=None),
):
mock_settings = MagicMock()
@@ -648,7 +648,7 @@ def test_azure_assistants_client_ad_token_provider_authentication() -> None:
mock_settings.base_url = None
mock_settings_class.return_value = mock_settings
client = AzureAssistantsClient(
client = AzureOpenAIAssistantsClient(
deployment_name="test-deployment",
endpoint="https://test-endpoint.openai.azure.com",
ad_token_provider=mock_token_provider,
@@ -660,14 +660,14 @@ def test_azure_assistants_client_ad_token_provider_authentication() -> None:
assert call_args["azure_ad_token_provider"] is mock_token_provider
assert client is not None
assert isinstance(client, AzureAssistantsClient)
assert isinstance(client, AzureOpenAIAssistantsClient)
def test_azure_assistants_client_base_url_configuration() -> None:
"""Test base_url client parameter path."""
with (
patch("agent_framework_azure._assistants_client.AzureOpenAISettings") as mock_settings_class,
patch("agent_framework_azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client,
patch("agent_framework.azure._assistants_client.AzureOpenAISettings") as mock_settings_class,
patch("agent_framework.azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client,
patch("agent_framework.openai.OpenAIAssistantsClient.__init__", return_value=None),
):
mock_settings = MagicMock()
@@ -678,7 +678,7 @@ def test_azure_assistants_client_base_url_configuration() -> None:
mock_settings.api_version = "2024-05-01-preview"
mock_settings_class.return_value = mock_settings
client = AzureAssistantsClient(
client = AzureOpenAIAssistantsClient(
deployment_name="test-deployment", api_key="test-api-key", base_url="https://custom-base-url.com"
)
@@ -689,14 +689,14 @@ def test_azure_assistants_client_base_url_configuration() -> None:
assert "azure_endpoint" not in call_args
assert client is not None
assert isinstance(client, AzureAssistantsClient)
assert isinstance(client, AzureOpenAIAssistantsClient)
def test_azure_assistants_client_azure_endpoint_configuration() -> None:
"""Test azure_endpoint client parameter path."""
with (
patch("agent_framework_azure._assistants_client.AzureOpenAISettings") as mock_settings_class,
patch("agent_framework_azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client,
patch("agent_framework.azure._assistants_client.AzureOpenAISettings") as mock_settings_class,
patch("agent_framework.azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client,
patch("agent_framework.openai.OpenAIAssistantsClient.__init__", return_value=None),
):
mock_settings = MagicMock()
@@ -707,7 +707,7 @@ def test_azure_assistants_client_azure_endpoint_configuration() -> None:
mock_settings.api_version = "2024-05-01-preview"
mock_settings_class.return_value = mock_settings
client = AzureAssistantsClient(
client = AzureOpenAIAssistantsClient(
deployment_name="test-deployment",
api_key="test-api-key",
endpoint="https://test-endpoint.openai.azure.com",
@@ -720,4 +720,4 @@ def test_azure_assistants_client_azure_endpoint_configuration() -> None:
assert "base_url" not in call_args
assert client is not None
assert isinstance(client, AzureAssistantsClient)
assert isinstance(client, AzureOpenAIAssistantsClient)
@@ -6,6 +6,16 @@ from unittest.mock import AsyncMock, MagicMock, patch
import openai
import pytest
from azure.identity import AzureCliCredential
from httpx import Request, Response
from openai import AsyncAzureOpenAI, AsyncStream
from openai.resources.chat.completions import AsyncCompletions as AsyncChatCompletions
from openai.types.chat import ChatCompletion, ChatCompletionChunk
from openai.types.chat.chat_completion import Choice
from openai.types.chat.chat_completion_chunk import Choice as ChunkChoice
from openai.types.chat.chat_completion_chunk import ChoiceDelta as ChunkChoiceDelta
from openai.types.chat.chat_completion_message import ChatCompletionMessage
from agent_framework import (
AgentRunResponse,
AgentRunResponseUpdate,
@@ -19,22 +29,12 @@ from agent_framework import (
ai_function,
)
from agent_framework._telemetry import USER_AGENT_KEY
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.exceptions import ServiceInitializationError, ServiceResponseException
from agent_framework.openai import (
ContentFilterResultSeverity,
OpenAIContentFilterException,
)
from azure.identity import AzureCliCredential
from httpx import Request, Response
from openai import AsyncAzureOpenAI, AsyncStream
from openai.resources.chat.completions import AsyncCompletions as AsyncChatCompletions
from openai.types.chat import ChatCompletion, ChatCompletionChunk
from openai.types.chat.chat_completion import Choice
from openai.types.chat.chat_completion_chunk import Choice as ChunkChoice
from openai.types.chat.chat_completion_chunk import ChoiceDelta as ChunkChoiceDelta
from openai.types.chat.chat_completion_message import ChatCompletionMessage
from agent_framework_azure import AzureChatClient
# region Service Setup
@@ -49,7 +49,7 @@ skip_if_azure_integration_tests_disabled = pytest.mark.skipif(
def test_init(azure_openai_unit_test_env: dict[str, str]) -> None:
# Test successful initialization
azure_chat_client = AzureChatClient()
azure_chat_client = AzureOpenAIChatClient()
assert azure_chat_client.client is not None
assert isinstance(azure_chat_client.client, AsyncAzureOpenAI)
@@ -60,7 +60,7 @@ def test_init(azure_openai_unit_test_env: dict[str, str]) -> None:
def test_init_client(azure_openai_unit_test_env: dict[str, str]) -> None:
# Test successful initialization with client
client = MagicMock(spec=AsyncAzureOpenAI)
azure_chat_client = AzureChatClient(async_client=client)
azure_chat_client = AzureOpenAIChatClient(async_client=client)
assert azure_chat_client.client is not None
assert isinstance(azure_chat_client.client, AsyncAzureOpenAI)
@@ -70,7 +70,7 @@ def test_init_base_url(azure_openai_unit_test_env: dict[str, str]) -> None:
# Custom header for testing
default_headers = {"X-Unit-Test": "test-guid"}
azure_chat_client = AzureChatClient(
azure_chat_client = AzureOpenAIChatClient(
default_headers=default_headers,
)
@@ -85,7 +85,7 @@ def test_init_base_url(azure_openai_unit_test_env: dict[str, str]) -> None:
@pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_BASE_URL"]], indirect=True)
def test_init_endpoint(azure_openai_unit_test_env: dict[str, str]) -> None:
azure_chat_client = AzureChatClient()
azure_chat_client = AzureOpenAIChatClient()
assert azure_chat_client.client is not None
assert isinstance(azure_chat_client.client, AsyncAzureOpenAI)
@@ -96,7 +96,7 @@ def test_init_endpoint(azure_openai_unit_test_env: dict[str, str]) -> None:
@pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"]], indirect=True)
def test_init_with_empty_deployment_name(azure_openai_unit_test_env: dict[str, str]) -> None:
with pytest.raises(ServiceInitializationError):
AzureChatClient(
AzureOpenAIChatClient(
env_file_path="test.env",
)
@@ -104,7 +104,7 @@ def test_init_with_empty_deployment_name(azure_openai_unit_test_env: dict[str, s
@pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_BASE_URL"]], indirect=True)
def test_init_with_empty_endpoint_and_base_url(azure_openai_unit_test_env: dict[str, str]) -> None:
with pytest.raises(ServiceInitializationError):
AzureChatClient(
AzureOpenAIChatClient(
env_file_path="test.env",
)
@@ -112,7 +112,7 @@ def test_init_with_empty_endpoint_and_base_url(azure_openai_unit_test_env: dict[
@pytest.mark.parametrize("override_env_param_dict", [{"AZURE_OPENAI_ENDPOINT": "http://test.com"}], indirect=True)
def test_init_with_invalid_endpoint(azure_openai_unit_test_env: dict[str, str]) -> None:
with pytest.raises(ServiceInitializationError):
AzureChatClient()
AzureOpenAIChatClient()
@pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_BASE_URL"]], indirect=True)
@@ -128,7 +128,7 @@ def test_serialize(azure_openai_unit_test_env: dict[str, str]) -> None:
"env_file_path": "test.env",
}
azure_chat_client = AzureChatClient.from_dict(settings)
azure_chat_client = AzureOpenAIChatClient.from_dict(settings)
dumped_settings = azure_chat_client.to_dict()
assert dumped_settings["ai_model_id"] == settings["deployment_name"]
assert str(settings["endpoint"]) in str(dumped_settings["base_url"])
@@ -186,7 +186,7 @@ async def test_cmc(
mock_create.return_value = mock_chat_completion_response
chat_history.append(ChatMessage(text="hello world", role="user"))
azure_chat_client = AzureChatClient()
azure_chat_client = AzureOpenAIChatClient()
await azure_chat_client.get_response(
messages=chat_history,
)
@@ -210,7 +210,7 @@ async def test_cmc_with_logit_bias(
token_bias: dict[str | int, float] = {"1": -100}
azure_chat_client = AzureChatClient()
azure_chat_client = AzureOpenAIChatClient()
await azure_chat_client.get_response(messages=chat_history, logit_bias=token_bias)
@@ -235,7 +235,7 @@ async def test_cmc_with_stop(
stop = ["!"]
azure_chat_client = AzureChatClient()
azure_chat_client = AzureOpenAIChatClient()
await azure_chat_client.get_response(messages=chat_history, stop=stop)
@@ -296,7 +296,7 @@ async def test_azure_on_your_data(
]
}
azure_chat_client = AzureChatClient()
azure_chat_client = AzureOpenAIChatClient()
content = await azure_chat_client.get_response(
messages=messages_in,
@@ -366,7 +366,7 @@ async def test_azure_on_your_data_string(
]
}
azure_chat_client = AzureChatClient()
azure_chat_client = AzureOpenAIChatClient()
content = await azure_chat_client.get_response(
messages=messages_in,
@@ -425,7 +425,7 @@ async def test_azure_on_your_data_fail(
]
}
azure_chat_client = AzureChatClient()
azure_chat_client = AzureOpenAIChatClient()
content = await azure_chat_client.get_response(
messages=messages_in,
@@ -489,7 +489,7 @@ async def test_content_filtering_raises_correct_exception(
},
)
azure_chat_client = AzureChatClient()
azure_chat_client = AzureOpenAIChatClient()
with pytest.raises(OpenAIContentFilterException, match="service encountered a content error") as exc_info:
await azure_chat_client.get_response(
@@ -533,7 +533,7 @@ async def test_content_filtering_without_response_code_raises_with_default_code(
},
)
azure_chat_client = AzureChatClient()
azure_chat_client = AzureOpenAIChatClient()
with pytest.raises(OpenAIContentFilterException, match="service encountered a content error"):
await azure_chat_client.get_response(
@@ -556,7 +556,7 @@ async def test_bad_request_non_content_filter(
"The request was bad.", response=Response(400, request=Request("POST", test_endpoint)), body={}
)
azure_chat_client = AzureChatClient()
azure_chat_client = AzureOpenAIChatClient()
with pytest.raises(ServiceResponseException, match="service failed to complete the prompt"):
await azure_chat_client.get_response(
@@ -574,7 +574,7 @@ async def test_get_streaming(
mock_create.return_value = mock_streaming_chat_completion_response
chat_history.append(ChatMessage(text="hello world", role="user"))
azure_chat_client = AzureChatClient()
azure_chat_client = AzureOpenAIChatClient()
async for msg in azure_chat_client.get_streaming_response(
messages=chat_history,
):
@@ -612,7 +612,7 @@ def get_weather(location: str) -> str:
@skip_if_azure_integration_tests_disabled
async def test_azure_openai_chat_client_response() -> None:
"""Test Azure OpenAI chat completion responses."""
azure_chat_client = AzureChatClient(credential=AzureCliCredential())
azure_chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
assert isinstance(azure_chat_client, ChatClientProtocol)
messages: list[ChatMessage] = []
@@ -641,7 +641,7 @@ async def test_azure_openai_chat_client_response() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_openai_chat_client_response_tools() -> None:
"""Test AzureOpenAI chat completion responses."""
azure_chat_client = AzureChatClient(credential=AzureCliCredential())
azure_chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
assert isinstance(azure_chat_client, ChatClientProtocol)
messages: list[ChatMessage] = []
@@ -662,7 +662,7 @@ async def test_azure_openai_chat_client_response_tools() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_openai_chat_client_streaming() -> None:
"""Test Azure OpenAI chat completion responses."""
azure_chat_client = AzureChatClient(credential=AzureCliCredential())
azure_chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
assert isinstance(azure_chat_client, ChatClientProtocol)
messages: list[ChatMessage] = []
@@ -696,7 +696,7 @@ async def test_azure_openai_chat_client_streaming() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_openai_chat_client_streaming_tools() -> None:
"""Test AzureOpenAI chat completion responses."""
azure_chat_client = AzureChatClient(credential=AzureCliCredential())
azure_chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
assert isinstance(azure_chat_client, ChatClientProtocol)
messages: list[ChatMessage] = []
@@ -721,9 +721,9 @@ async def test_azure_openai_chat_client_streaming_tools() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_openai_chat_client_agent_basic_run():
"""Test Azure OpenAI chat client agent basic run functionality with AzureChatClient."""
"""Test Azure OpenAI chat client agent basic run functionality with AzureOpenAIChatClient."""
async with ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
) as agent:
# Test basic run
response = await agent.run("Hello! Please respond with 'Hello World' exactly.")
@@ -736,9 +736,9 @@ async def test_azure_openai_chat_client_agent_basic_run():
@skip_if_azure_integration_tests_disabled
async def test_azure_openai_chat_client_agent_basic_run_streaming():
"""Test Azure OpenAI chat client agent basic streaming functionality with AzureChatClient."""
"""Test Azure OpenAI chat client agent basic streaming functionality with AzureOpenAIChatClient."""
async with ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
) as agent:
# Test streaming run
full_text = ""
@@ -753,9 +753,9 @@ async def test_azure_openai_chat_client_agent_basic_run_streaming():
@skip_if_azure_integration_tests_disabled
async def test_azure_openai_chat_client_agent_thread_persistence():
"""Test Azure OpenAI chat client agent thread persistence across runs with AzureChatClient."""
"""Test Azure OpenAI chat client agent thread persistence across runs with AzureOpenAIChatClient."""
async with ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant with good memory.",
) as agent:
# Create a new thread that will be reused
@@ -782,7 +782,7 @@ async def test_azure_openai_chat_client_agent_existing_thread():
preserved_thread = None
async with ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant with good memory.",
) as first_agent:
# Start a conversation and capture the thread
@@ -798,7 +798,7 @@ async def test_azure_openai_chat_client_agent_existing_thread():
# Second conversation - reuse the thread in a new agent instance
if preserved_thread:
async with ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant with good memory.",
) as second_agent:
# Reuse the preserved thread
@@ -814,7 +814,7 @@ async def test_azure_chat_client_agent_level_tool_persistence():
"""Test that agent-level tools persist across multiple runs with Azure Chat Client."""
async with ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that uses available tools.",
tools=[get_weather], # Agent-level tool
) as agent:
@@ -4,6 +4,9 @@ import os
from typing import Annotated
import pytest
from azure.identity import AzureCliCredential
from pydantic import BaseModel
from agent_framework import (
AgentRunResponse,
AgentRunResponseUpdate,
@@ -20,10 +23,8 @@ from agent_framework import (
TextContent,
ai_function,
)
from agent_framework.azure import AzureResponsesClient
from agent_framework.azure import AzureOpenAIResponsesClient
from agent_framework.exceptions import ServiceInitializationError
from azure.identity import AzureCliCredential
from pydantic import BaseModel
skip_if_azure_integration_tests_disabled = pytest.mark.skipif(
os.getenv("RUN_INTEGRATION_TESTS", "false").lower() != "true"
@@ -48,7 +49,7 @@ async def get_weather(location: Annotated[str, "The location as a city name"]) -
return f"The weather in {location} is sunny and 72°F."
async def create_vector_store(client: AzureResponsesClient) -> tuple[str, HostedVectorStoreContent]:
async def create_vector_store(client: AzureOpenAIResponsesClient) -> tuple[str, HostedVectorStoreContent]:
"""Create a vector store with sample documents for testing."""
file = await client.client.files.create(
file=("todays_weather.txt", b"The weather today is sunny with a high of 75F."), purpose="assistants"
@@ -64,7 +65,7 @@ async def create_vector_store(client: AzureResponsesClient) -> tuple[str, Hosted
return file.id, HostedVectorStoreContent(vector_store_id=vector_store.id)
async def delete_vector_store(client: AzureResponsesClient, file_id: str, vector_store_id: str) -> None:
async def delete_vector_store(client: AzureOpenAIResponsesClient, file_id: str, vector_store_id: str) -> None:
"""Delete the vector store after tests."""
await client.client.vector_stores.delete(vector_store_id=vector_store_id)
@@ -73,7 +74,7 @@ async def delete_vector_store(client: AzureResponsesClient, file_id: str, vector
def test_init(azure_openai_unit_test_env: dict[str, str]) -> None:
# Test successful initialization
azure_responses_client = AzureResponsesClient()
azure_responses_client = AzureOpenAIResponsesClient()
assert azure_responses_client.ai_model_id == azure_openai_unit_test_env["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"]
assert isinstance(azure_responses_client, ChatClientProtocol)
@@ -82,13 +83,13 @@ def test_init(azure_openai_unit_test_env: dict[str, str]) -> None:
def test_init_validation_fail() -> None:
# Test successful initialization
with pytest.raises(ServiceInitializationError):
AzureResponsesClient(api_key="34523", deployment_name={"test": "dict"}) # type: ignore
AzureOpenAIResponsesClient(api_key="34523", deployment_name={"test": "dict"}) # type: ignore
def test_init_ai_model_id_constructor(azure_openai_unit_test_env: dict[str, str]) -> None:
# Test successful initialization
ai_model_id = "test_model_id"
azure_responses_client = AzureResponsesClient(deployment_name=ai_model_id)
azure_responses_client = AzureOpenAIResponsesClient(deployment_name=ai_model_id)
assert azure_responses_client.ai_model_id == ai_model_id
assert isinstance(azure_responses_client, ChatClientProtocol)
@@ -98,7 +99,7 @@ def test_init_with_default_header(azure_openai_unit_test_env: dict[str, str]) ->
default_headers = {"X-Unit-Test": "test-guid"}
# Test successful initialization
azure_responses_client = AzureResponsesClient(
azure_responses_client = AzureOpenAIResponsesClient(
default_headers=default_headers,
)
@@ -114,7 +115,7 @@ def test_init_with_default_header(azure_openai_unit_test_env: dict[str, str]) ->
@pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"]], indirect=True)
def test_init_with_empty_model_id(azure_openai_unit_test_env: dict[str, str]) -> None:
with pytest.raises(ServiceInitializationError):
AzureResponsesClient(
AzureOpenAIResponsesClient(
env_file_path="test.env",
)
@@ -128,7 +129,7 @@ def test_serialize(azure_openai_unit_test_env: dict[str, str]) -> None:
"default_headers": default_headers,
}
azure_responses_client = AzureResponsesClient.from_dict(settings)
azure_responses_client = AzureOpenAIResponsesClient.from_dict(settings)
dumped_settings = azure_responses_client.to_dict()
assert dumped_settings["ai_model_id"] == azure_openai_unit_test_env["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"]
assert dumped_settings["api_key"] == azure_openai_unit_test_env["AZURE_OPENAI_API_KEY"]
@@ -143,7 +144,7 @@ def test_serialize(azure_openai_unit_test_env: dict[str, str]) -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_responses_client_response() -> None:
"""Test azure responses client responses."""
azure_responses_client = AzureResponsesClient(credential=AzureCliCredential())
azure_responses_client = AzureOpenAIResponsesClient(credential=AzureCliCredential())
assert isinstance(azure_responses_client, ChatClientProtocol)
@@ -186,7 +187,7 @@ async def test_azure_responses_client_response() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_responses_client_response_tools() -> None:
"""Test azure responses client tools."""
azure_responses_client = AzureResponsesClient(credential=AzureCliCredential())
azure_responses_client = AzureOpenAIResponsesClient(credential=AzureCliCredential())
assert isinstance(azure_responses_client, ChatClientProtocol)
@@ -225,7 +226,7 @@ async def test_azure_responses_client_response_tools() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_responses_client_streaming() -> None:
"""Test Azure azure responses client streaming responses."""
azure_responses_client = AzureResponsesClient(credential=AzureCliCredential())
azure_responses_client = AzureOpenAIResponsesClient(credential=AzureCliCredential())
assert isinstance(azure_responses_client, ChatClientProtocol)
@@ -275,7 +276,7 @@ async def test_azure_responses_client_streaming() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_responses_client_streaming_tools() -> None:
"""Test azure responses client streaming tools."""
azure_responses_client = AzureResponsesClient(credential=AzureCliCredential())
azure_responses_client = AzureOpenAIResponsesClient(credential=AzureCliCredential())
assert isinstance(azure_responses_client, ChatClientProtocol)
@@ -321,8 +322,8 @@ async def test_azure_responses_client_streaming_tools() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_responses_client_agent_basic_run():
"""Test Azure Responses Client agent basic run functionality with AzureResponsesClient."""
agent = AzureResponsesClient(credential=AzureCliCredential()).create_agent(
"""Test Azure Responses Client agent basic run functionality with AzureOpenAIResponsesClient."""
agent = AzureOpenAIResponsesClient(credential=AzureCliCredential()).create_agent(
instructions="You are a helpful assistant.",
)
@@ -337,9 +338,9 @@ async def test_azure_responses_client_agent_basic_run():
@skip_if_azure_integration_tests_disabled
async def test_azure_responses_client_agent_basic_run_streaming():
"""Test Azure Responses Client agent basic streaming functionality with AzureResponsesClient."""
"""Test Azure Responses Client agent basic streaming functionality with AzureOpenAIResponsesClient."""
async with ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
) as agent:
# Test streaming run
full_text = ""
@@ -354,9 +355,9 @@ async def test_azure_responses_client_agent_basic_run_streaming():
@skip_if_azure_integration_tests_disabled
async def test_azure_responses_client_agent_thread_persistence():
"""Test Azure Responses Client agent thread persistence across runs with AzureResponsesClient."""
"""Test Azure Responses Client agent thread persistence across runs with AzureOpenAIResponsesClient."""
async with ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant with good memory.",
) as agent:
# Create a new thread that will be reused
@@ -379,7 +380,7 @@ async def test_azure_responses_client_agent_thread_persistence():
async def test_azure_responses_client_agent_thread_storage_with_store_true():
"""Test Azure Responses Client agent with store=True to verify service_thread_id is returned."""
async with ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant.",
) as agent:
# Create a new thread
@@ -413,7 +414,7 @@ async def test_azure_responses_client_agent_existing_thread():
preserved_thread = None
async with ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant with good memory.",
) as first_agent:
# Start a conversation and capture the thread
@@ -429,7 +430,7 @@ async def test_azure_responses_client_agent_existing_thread():
# Second conversation - reuse the thread in a new agent instance
if preserved_thread:
async with ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant with good memory.",
) as second_agent:
# Reuse the preserved thread
@@ -442,9 +443,9 @@ async def test_azure_responses_client_agent_existing_thread():
@skip_if_azure_integration_tests_disabled
async def test_azure_responses_client_agent_hosted_code_interpreter_tool():
"""Test Azure Responses Client agent with HostedCodeInterpreterTool through AzureResponsesClient."""
"""Test Azure Responses Client agent with HostedCodeInterpreterTool through AzureOpenAIResponsesClient."""
async with ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can execute Python code.",
tools=[HostedCodeInterpreterTool()],
) as agent:
@@ -466,7 +467,7 @@ async def test_azure_responses_client_agent_level_tool_persistence():
"""Test that agent-level tools persist across multiple runs with Azure Responses Client."""
async with ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that uses available tools.",
tools=[get_weather], # Agent-level tool
) as agent:
@@ -491,7 +492,7 @@ async def test_azure_responses_client_agent_level_tool_persistence():
async def test_azure_responses_client_agent_chat_options_run_level() -> None:
"""Integration test for comprehensive ChatOptions parameter coverage with Azure Response Agent."""
async with ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant.",
) as agent:
response = await agent.run(
@@ -514,7 +515,7 @@ async def test_azure_responses_client_agent_chat_options_run_level() -> None:
async def test_azure_responses_client_agent_chat_options_agent_level() -> None:
"""Integration test for comprehensive ChatOptions parameter coverage with Azure Response Agent."""
async with ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant.",
max_tokens=100,
temperature=0.7,
@@ -536,7 +537,7 @@ async def test_azure_responses_client_agent_chat_options_agent_level() -> None:
@skip_if_azure_integration_tests_disabled
async def test_azure_responses_client_agent_hosted_mcp_tool() -> None:
"""Integration test for HostedMCPTool with Azure Response Agent using Microsoft Learn MCP."""
# Use the same MCP server as the Foundry example
mcp_tool = HostedMCPTool(
name="Microsoft Learn MCP",
url="https://learn.microsoft.com/api/mcp",
@@ -545,11 +546,10 @@ async def test_azure_responses_client_agent_hosted_mcp_tool() -> None:
)
async with ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
tools=[mcp_tool],
) as agent:
# Use the same query as the Foundry example
response = await agent.run(
"How to create an Azure storage account using az cli?",
max_tokens=200,
@@ -566,7 +566,7 @@ async def test_azure_responses_client_agent_hosted_mcp_tool() -> None:
@pytest.mark.skip(reason="File search requires API key auth, subscription only allows token auth")
async def test_azure_responses_client_file_search() -> None:
"""Test Azure responses client with file search tool."""
azure_responses_client = AzureResponsesClient(credential=AzureCliCredential())
azure_responses_client = AzureOpenAIResponsesClient(credential=AzureCliCredential())
assert isinstance(azure_responses_client, ChatClientProtocol)
@@ -592,7 +592,7 @@ async def test_azure_responses_client_file_search() -> None:
@pytest.mark.skip(reason="File search requires API key auth, subscription only allows token auth")
async def test_azure_responses_client_file_search_streaming() -> None:
"""Test Azure responses client with file search tool and streaming."""
azure_responses_client = AzureResponsesClient(credential=AzureCliCredential())
azure_responses_client = AzureOpenAIResponsesClient(credential=AzureCliCredential())
assert isinstance(azure_responses_client, ChatClientProtocol)
@@ -3,13 +3,13 @@
from unittest.mock import AsyncMock, MagicMock
import pytest
from agent_framework.exceptions import ServiceInvalidAuthError
from azure.core.exceptions import ClientAuthenticationError
from agent_framework_azure._entra_id_authentication import (
from agent_framework.azure._entra_id_authentication import (
get_entra_auth_token,
get_entra_auth_token_async,
)
from agent_framework.exceptions import ServiceInvalidAuthError
@pytest.fixture
@@ -1560,7 +1560,7 @@ async def test_openai_responses_client_agent_chat_options_agent_level() -> None:
@skip_if_openai_integration_tests_disabled
async def test_openai_responses_client_agent_hosted_mcp_tool() -> None:
"""Integration test for HostedMCPTool with OpenAI Response Agent using Microsoft Learn MCP."""
# Use the same MCP server as the Foundry example
mcp_tool = HostedMCPTool(
name="Microsoft Learn MCP",
url="https://learn.microsoft.com/api/mcp",
@@ -1573,7 +1573,6 @@ async def test_openai_responses_client_agent_hosted_mcp_tool() -> None:
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
tools=[mcp_tool],
) as agent:
# Use the same query as the Foundry example
response = await agent.run(
"How to create an Azure storage account using az cli?",
max_tokens=200,
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "agent-framework-mem0"
description = "Mem0 integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
@@ -1,28 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
def test_self_through_main() -> None:
try:
from agent_framework.mem0 import __version__
except ImportError:
__version__ = None
assert __version__ is not None
def test_self() -> None:
try:
from agent_framework_mem0 import __version__
except ImportError:
__version__ = None
assert __version__ is not None
def test_agent_framework() -> None:
try:
from agent_framework import __version__
except ImportError:
__version__ = None
assert __version__ is not None
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "agent-framework-redis"
description = "Redis integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.0.0b1"
@@ -1,28 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
def test_self_through_main() -> None:
try:
from agent_framework.redis import __version__
except ImportError:
__version__ = None
assert __version__ is not None
def test_self() -> None:
try:
from agent_framework_redis import __version__
except ImportError:
__version__ = None
assert __version__ is not None
def test_agent_framework() -> None:
try:
from agent_framework import __version__
except ImportError:
__version__ = None
assert __version__ is not None
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "agent-framework-runtime"
description = "Runtime integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "0.1.0b1"
+30 -7
View File
@@ -5,9 +5,8 @@ version = "0.0.0"
requires-python = ">=3.10"
dependencies = [
"agent-framework",
"agent-framework-azure",
"agent-framework-azure-ai",
"agent-framework-copilotstudio",
"agent-framework-foundry",
"agent-framework-mem0",
"agent-framework-redis",
"agent-framework-devui",
@@ -55,9 +54,8 @@ exclude = [ "packages/agent_framework_project.egg-info", "packages/lab", "packag
[tool.uv.sources]
agent-framework = { workspace = true }
agent-framework-azure = { workspace = true }
agent-framework-azure-ai = { workspace = true }
agent-framework-copilotstudio = { workspace = true }
agent-framework-foundry = { workspace = true }
agent-framework-lab-gaia = { workspace = true }
agent-framework-mem0 = { workspace = true }
agent-framework-redis = { workspace = true }
@@ -70,6 +68,9 @@ target-version = "py310"
fix = true
include = ["*.py", "*.pyi", "**/pyproject.toml", "*.ipynb"]
exclude = ["docs/*", "run_tasks_in_packages_if_exists.py", "check_md_code_blocks.py"]
extend-exclude = [
"[{][{]cookiecutter.package_name[}][}]",
]
preview = true
[tool.ruff.lint]
@@ -131,7 +132,7 @@ filterwarnings = []
timeout = 120
markers = [
"azure: marks tests as Azure provider specific",
"foundry: marks tests as Foundry provider specific",
"azure-ai: marks tests as Azure AI provider specific",
"openai: marks tests as OpenAI provider specific",
]
@@ -187,8 +188,30 @@ build = "python run_tasks_in_packages_if_exists.py build"
# combined checks
check = ["fmt", "lint", "pyright", "mypy", "test", "markdown-code-lint", "samples-code-check"]
pre-commit-check = ["fmt", "lint", "pyright", "markdown-code-lint", "samples-code-check"]
all-tests = "pytest --import-mode=importlib --cov=agent_framework --cov=agent_framework_azure --cov=agent_framework_copilotstudio --cov=agent_framework_foundry --cov=agent_framework_mem0 --cov-report=term-missing:skip-covered packages/azure/tests packages/copilotstudio/tests packages/foundry/tests packages/main/tests packages/mem0/tests packages/redis/tests"
[tool.poe.tasks.all-tests-cov]
cmd = """
pytest --import-mode=importlib
--cov=agent_framework
--cov=agent_framework_azure_ai
--cov=agent_framework_copilotstudio
--cov=agent_framework_mem0
--cov=agent_framework_redis
--cov-report=term-missing:skip-covered
--ignore-glob=packages/lab/**
--ignore-glob=packages/devui/**
-n logical --dist loadfile --dist worksteal
packages/**/tests
"""
[tool.poe.tasks.all-tests]
cmd = """
pytest --import-mode=importlib
--ignore-glob=packages/lab/**
--ignore-glob=packages/devui/**
-n logical --dist loadfile --dist worksteal
packages/**/tests
"""
[tool.poe.tasks.venv]
cmd = "uv venv --clear --python $python"
@@ -227,4 +250,4 @@ sequence = [
[tool.setuptools.packages.find]
where = ["packages"]
include = ["agent_framework**"]
namespaces = true
namespaces = true
@@ -8,7 +8,7 @@ This folder contains examples demonstrating how to create and use agents with di
| Folder | Description |
|--------|-------------|
| **[`foundry/`](foundry/)** | Create agents using Azure AI Foundry |
| **[`azure_ai/`](azure_ai/)** | Create agents using Azure AI Foundry Agent Service |
### Microsoft Copilot Studio Examples
@@ -20,14 +20,17 @@ This folder contains examples demonstrating how to create and use agents with di
| Folder | Description |
|--------|-------------|
| **[`azure_assistants_client/`](azure_assistants_client/)** | Create agents using Azure OpenAI Assistants API |
| **[`azure_chat_client/`](azure_chat_client/)** | Create agents using Azure OpenAI Chat Completions API |
| **[`azure_responses_client/`](azure_responses_client/)** | Create agents using Azure OpenAI Responses API |
| **[`azure_openai/`](azure_openai/)** | Create agents using Azure OpenAI APIs |
### OpenAI Examples
| Folder | Description |
|--------|-------------|
| **[`openai_assistants_client/`](openai_assistants_client/)** | Create agents using OpenAI Assistants API |
| **[`openai_chat_client/`](openai_chat_client/)** | Create agents using OpenAI Chat Completions API |
| **[`openai_responses_client/`](openai_responses_client/)** | Create agents using OpenAI Responses API |
| **[`openai/`](openai/)** | Create agents using OpenAI APIs |
### Custom Client Examples
| Folder | Description |
|--------|-------------|
| **[`custom_client/`](custom_client/)** | Create agents using a custom chat client or a custom agent |
| **[`anthropic/`](anthropic/)** | Create agents using Anthropic APIs |
@@ -0,0 +1,25 @@
# Azure AI Agent Examples
This folder contains examples demonstrating different ways to create and use agents with the Azure AI chat client from the `agent_framework.azure` package.
## Examples
| File | Description |
|------|-------------|
| [`azure_ai_basic.py`](azure_ai_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureAIAgentClient`. It automatically handles all configuration using environment variables. |
| [`azure_ai_with_explicit_settings.py`](azure_ai_with_explicit_settings.py) | Shows how to create an agent with explicitly configured `AzureAIAgentClient` settings, including project endpoint, model deployment, credentials, and agent name. |
| [`azure_ai_with_existing_agent.py`](azure_ai_with_existing_agent.py) | Shows how to work with a pre-existing agent by providing the agent ID to the Azure AI chat client. This example also demonstrates proper cleanup of manually created agents. |
| [`azure_ai_with_function_tools.py`](azure_ai_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`azure_ai_with_code_interpreter.py`](azure_ai_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure AI agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
| [`azure_ai_with_local_mcp.py`](azure_ai_with_local_mcp.py) | Shows how to integrate Azure AI agents with Model Context Protocol (MCP) servers for enhanced functionality and tool integration. Demonstrates both agent-level and run-level tool configuration. |
| [`azure_ai_with_thread.py`](azure_ai_with_thread.py) | Demonstrates thread management with Azure AI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
## Environment Variables
Make sure to set the following environment variables before running the examples:
- `AZURE_AZURE_FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI project endpoint
- `AZURE_AZURE_FOUNDRY_MODEL_DEPLOYMENT_NAME`: The name of your model deployment
Optionally, you can set:
- `AZURE_AZURE_FOUNDRY_AGENT_NAME`: The name of your agent, this can also be set programmatically when creating the agent.
@@ -4,7 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework.foundry import FoundryChatClient
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
@@ -27,7 +27,7 @@ async def non_streaming_example() -> None:
# authentication option.
async with (
AzureCliCredential() as credential,
FoundryChatClient(async_credential=credential).create_agent(
AzureAIAgentClient(async_credential=credential).create_agent(
name="WeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
@@ -49,7 +49,7 @@ async def streaming_example() -> None:
# authentication option.
async with (
AzureCliCredential() as credential,
FoundryChatClient(async_credential=credential).create_agent(
AzureAIAgentClient(async_credential=credential).create_agent(
name="WeatherAgent",
instructions="You are a helpful weather agent.",
tools=get_weather,
@@ -65,7 +65,7 @@ async def streaming_example() -> None:
async def main() -> None:
print("=== Basic Foundry Chat Client Agent Example ===")
print("=== Basic Azure AI Chat Client Agent Example ===")
await non_streaming_example()
await streaming_example()
@@ -2,20 +2,16 @@
import asyncio
from agent_framework import (
AgentRunResponse,
HostedCodeInterpreterTool,
from agent_framework import AgentRunResponse, ChatResponseUpdate, HostedCodeInterpreterTool
from agent_framework.azure import AzureAIAgentClient
from azure.ai.agents.models import (
RunStepDeltaCodeInterpreterDetailItemObject,
)
from agent_framework.foundry import FoundryChatClient
from azure.identity.aio import AzureCliCredential
def print_code_interpreter_inputs(response: AgentRunResponse) -> None:
"""Helper method to access code interpreter data."""
from agent_framework import ChatResponseUpdate
from azure.ai.agents.models import (
RunStepDeltaCodeInterpreterDetailItemObject,
)
print("\nCode Interpreter Inputs during the run:")
if response.raw_representation is None:
@@ -29,14 +25,14 @@ def print_code_interpreter_inputs(response: AgentRunResponse) -> None:
async def main() -> None:
"""Example showing how to use the HostedCodeInterpreterTool with Foundry."""
print("=== Foundry Agent with Code Interpreter Example ===")
"""Example showing how to use the HostedCodeInterpreterTool with Azure AI."""
print("=== Azure AI Agent with Code Interpreter Example ===")
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with (
AzureCliCredential() as credential,
FoundryChatClient(async_credential=credential) as chat_client,
AzureAIAgentClient(async_credential=credential) as chat_client,
):
agent = chat_client.create_agent(
name="CodingAgent",
@@ -6,7 +6,7 @@ from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework.foundry import FoundryChatClient
from agent_framework.azure import AzureAIAgentClient
from azure.ai.projects.aio import AIProjectClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
@@ -21,23 +21,23 @@ def get_weather(
async def main() -> None:
print("=== Foundry Chat Client with Existing Agent ===")
print("=== Azure AI Chat Client with Existing Agent ===")
# Create the client
async with (
AzureCliCredential() as credential,
AIProjectClient(endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], credential=credential) as client,
AIProjectClient(endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], credential=credential) as client,
):
# Create an agent that will persist
created_agent = await client.agents.create_agent(
model=os.environ["FOUNDRY_MODEL_DEPLOYMENT_NAME"], name="WeatherAgent"
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], name="WeatherAgent"
)
try:
async with ChatAgent(
# passing in the client is optional here, so if you take the agent_id from the portal
# you can use it directly without the two lines above.
chat_client=FoundryChatClient(client=client, agent_id=created_agent.id),
chat_client=AzureAIAgentClient(client=client, agent_id=created_agent.id),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -6,7 +6,7 @@ from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework.foundry import FoundryChatClient
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
@@ -20,7 +20,7 @@ def get_weather(
async def main() -> None:
print("=== Foundry Chat Client with Explicit Settings ===")
print("=== Azure AI Chat Client with Explicit Settings ===")
# Since no Agent ID is provided, the agent will be automatically created
# and deleted after getting a response
@@ -29,9 +29,9 @@ async def main() -> None:
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model_deployment_name=os.environ["FOUNDRY_MODEL_DEPLOYMENT_NAME"],
chat_client=AzureAIAgentClient(
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
model_deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
async_credential=credential,
agent_name="WeatherAgent",
),
@@ -6,7 +6,7 @@ from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework.foundry import FoundryChatClient
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
@@ -36,7 +36,7 @@ async def tools_on_agent_level() -> None:
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=FoundryChatClient(async_credential=credential),
chat_client=AzureAIAgentClient(async_credential=credential),
instructions="You are a helpful assistant that can provide weather and time information.",
tools=[get_weather, get_time], # Tools defined at agent creation
) as agent,
@@ -70,7 +70,7 @@ async def tools_on_run_level() -> None:
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=FoundryChatClient(async_credential=credential),
chat_client=AzureAIAgentClient(async_credential=credential),
instructions="You are a helpful assistant.",
# No tools defined here
) as agent,
@@ -104,7 +104,7 @@ async def mixed_tools_example() -> None:
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=FoundryChatClient(async_credential=credential),
chat_client=AzureAIAgentClient(async_credential=credential),
instructions="You are a comprehensive assistant that can help with various information requests.",
tools=[get_weather], # Base tool available for all queries
) as agent,
@@ -122,7 +122,7 @@ async def mixed_tools_example() -> None:
async def main() -> None:
print("=== Foundry Chat Client Agent with Function Tools Examples ===\n")
print("=== Azure AI Chat Client Agent with Function Tools Examples ===\n")
await tools_on_agent_level()
await tools_on_run_level()
@@ -4,7 +4,7 @@ import asyncio
from typing import Any
from agent_framework import AgentProtocol, AgentThread, HostedMCPTool
from agent_framework.foundry import FoundryChatClient
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
@@ -32,13 +32,13 @@ async def handle_approvals_with_thread(query: str, agent: "AgentProtocol", threa
async def main() -> None:
"""Example showing Hosted MCP tools for a Foundry Agent."""
"""Example showing Hosted MCP tools for a Azure AI Agent."""
async with (
AzureCliCredential() as credential,
FoundryChatClient(async_credential=credential) as chat_client,
AzureAIAgentClient(async_credential=credential) as chat_client,
):
# enable foundry observability
await chat_client.setup_foundry_observability()
# enable azure-ai observability
await chat_client.setup_observability()
agent = chat_client.create_agent(
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
@@ -3,7 +3,7 @@
import asyncio
from agent_framework import ChatAgent, MCPStreamableHTTPTool
from agent_framework.foundry import FoundryChatClient
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
@@ -21,7 +21,7 @@ async def mcp_tools_on_run_level() -> None:
url="https://learn.microsoft.com/api/mcp",
) as mcp_server,
ChatAgent(
chat_client=FoundryChatClient(async_credential=credential),
chat_client=AzureAIAgentClient(async_credential=credential),
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
) as agent,
@@ -48,7 +48,7 @@ async def mcp_tools_on_agent_level() -> None:
# The agent will connect to the MCP server through its context manager.
async with (
AzureCliCredential() as credential,
FoundryChatClient(async_credential=credential).create_agent(
AzureAIAgentClient(async_credential=credential).create_agent(
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
tools=MCPStreamableHTTPTool( # Tools defined at agent creation
@@ -71,7 +71,7 @@ async def mcp_tools_on_agent_level() -> None:
async def main() -> None:
print("=== Foundry Chat Client Agent with MCP Tools Examples ===\n")
print("=== Azure AI Chat Client Agent with MCP Tools Examples ===\n")
await mcp_tools_on_agent_level()
await mcp_tools_on_run_level()
@@ -10,7 +10,7 @@ from agent_framework import (
HostedMCPTool,
HostedWebSearchTool,
)
from agent_framework.foundry import FoundryChatClient
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
@@ -44,13 +44,13 @@ async def handle_approvals_with_thread(query: str, agent: "AgentProtocol", threa
async def main() -> None:
"""Example showing Hosted MCP tools for a Foundry Agent."""
"""Example showing Hosted MCP tools for a Azure AI Agent."""
async with (
AzureCliCredential() as credential,
FoundryChatClient(async_credential=credential) as chat_client,
AzureAIAgentClient(async_credential=credential) as chat_client,
):
# enable foundry observability
await chat_client.setup_foundry_observability()
# enable azure-ai observability
await chat_client.setup_observability()
agent = chat_client.create_agent(
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
@@ -5,7 +5,7 @@ from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent
from agent_framework.foundry import FoundryChatClient
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
from pydantic import Field
@@ -27,7 +27,7 @@ async def example_with_automatic_thread_creation() -> None:
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=FoundryChatClient(async_credential=credential),
chat_client=AzureAIAgentClient(async_credential=credential),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
@@ -56,7 +56,7 @@ async def example_with_thread_persistence() -> None:
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=FoundryChatClient(async_credential=credential),
chat_client=AzureAIAgentClient(async_credential=credential),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
@@ -97,7 +97,7 @@ async def example_with_existing_thread_id() -> None:
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=FoundryChatClient(async_credential=credential),
chat_client=AzureAIAgentClient(async_credential=credential),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
@@ -120,7 +120,7 @@ async def example_with_existing_thread_id() -> None:
async with (
AzureCliCredential() as credential,
ChatAgent(
chat_client=FoundryChatClient(thread_id=existing_thread_id, async_credential=credential),
chat_client=AzureAIAgentClient(thread_id=existing_thread_id, async_credential=credential),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent,
@@ -136,7 +136,7 @@ async def example_with_existing_thread_id() -> None:
async def main() -> None:
print("=== Foundry Chat Client Agent Thread Management Examples ===\n")
print("=== Azure AI Chat Client Agent Thread Management Examples ===\n")
await example_with_automatic_thread_creation()
await example_with_thread_persistence()
@@ -1,36 +0,0 @@
# Azure Assistants Agent Examples
This folder contains examples demonstrating different ways to create and use agents with the Azure Assistants client from the `agent_framework.azure` package.
## Examples
| File | Description |
|------|-------------|
| [`azure_assistants_basic.py`](azure_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. |
| [`azure_assistants_with_existing_assistant.py`](azure_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the Azure Assistants client. Demonstrates proper cleanup of manually created assistants. |
| [`azure_assistants_with_explicit_settings.py`](azure_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including endpoint and deployment name. |
| [`azure_assistants_with_function_tools.py`](azure_assistants_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`azure_assistants_with_code_interpreter.py`](azure_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
| [`azure_assistants_with_thread.py`](azure_assistants_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
## Environment Variables
Make sure to set the following environment variables before running the examples:
- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint
- `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`: The name of your Azure OpenAI deployment
## Authentication
All examples use `AzureCliCredential` for authentication. Run `az login` in your terminal before running the examples, or replace `AzureCliCredential` with your preferred authentication method.
## Required role-based access control (RBAC) roles
To access the Azure OpenAI API, your Azure account or service principal needs one of the following RBAC roles assigned to the Azure OpenAI resource:
- **Cognitive Services OpenAI User**: Provides read access to Azure OpenAI resources and the ability to call the inference APIs. This is the minimum role required for running these examples.
- **Cognitive Services OpenAI Contributor**: Provides full access to Azure OpenAI resources, including the ability to create, update, and delete deployments and models.
For most scenarios, the **Cognitive Services OpenAI User** role is sufficient. You can assign this role through the Azure portal under the Azure OpenAI resource's "Access control (IAM)" section.
For more detailed information about Azure OpenAI RBAC roles, see: [Role-based access control for Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/role-based-access-control)
@@ -1,34 +0,0 @@
# Azure Chat Agent Examples
This folder contains examples demonstrating different ways to create and use agents with the Azure Chat client from the `agent_framework.azure` package.
## Examples
| File | Description |
|------|-------------|
| [`azure_chat_client_basic.py`](azure_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with Azure OpenAI models. |
| [`azure_chat_client_with_explicit_settings.py`](azure_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including endpoint and deployment name. |
| [`azure_chat_client_with_function_tools.py`](azure_chat_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`azure_chat_client_with_thread.py`](azure_chat_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
## Environment Variables
Make sure to set the following environment variables before running the examples:
- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint
- `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`: The name of your Azure OpenAI deployment
## Authentication
All examples use `AzureCliCredential` for authentication. Run `az login` in your terminal before running the examples, or replace `AzureCliCredential` with your preferred authentication method.
## Required role-based access control (RBAC) roles
To access the Azure OpenAI API, your Azure account or service principal needs one of the following RBAC roles assigned to the Azure OpenAI resource:
- **Cognitive Services OpenAI User**: Provides read access to Azure OpenAI resources and the ability to call the inference APIs. This is the minimum role required for running these examples.
- **Cognitive Services OpenAI Contributor**: Provides full access to Azure OpenAI resources, including the ability to create, update, and delete deployments and models.
For most scenarios, the **Cognitive Services OpenAI User** role is sufficient. You can assign this role through the Azure portal under the Azure OpenAI resource's "Access control (IAM)" section.
For more detailed information about Azure OpenAI RBAC roles, see: [Role-based access control for Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/role-based-access-control)
@@ -0,0 +1,51 @@
# Azure OpenAI Agent Examples
This folder contains examples demonstrating different ways to create and use agents with the different Azure OpenAI chat client from the `agent_framework.azure` package.
## Examples
| File | Description |
|------|-------------|
| [`azure_assistants_basic.py`](azure_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. |
| [`azure_assistants_with_existing_assistant.py`](azure_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the Azure Assistants client. Demonstrates proper cleanup of manually created assistants. |
| [`azure_assistants_with_explicit_settings.py`](azure_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including endpoint and deployment name. |
| [`azure_assistants_with_function_tools.py`](azure_assistants_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`azure_assistants_with_code_interpreter.py`](azure_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
| [`azure_assistants_with_thread.py`](azure_assistants_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
| [`azure_chat_client_basic.py`](azure_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with Azure OpenAI models. |
| [`azure_chat_client_with_explicit_settings.py`](azure_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including endpoint and deployment name. |
| [`azure_chat_client_with_function_tools.py`](azure_chat_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`azure_chat_client_with_thread.py`](azure_chat_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
| [`azure_responses_client_basic.py`](azure_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with Azure OpenAI models. |
| [`azure_responses_client_with_explicit_settings.py`](azure_responses_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific responses client, configuring settings explicitly including endpoint and deployment name. |
| [`azure_responses_client_with_function_tools.py`](azure_responses_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`azure_responses_client_with_code_interpreter.py`](azure_responses_client_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
| [`azure_responses_client_with_thread.py`](azure_responses_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
## Environment Variables
Make sure to set the following environment variables before running the examples:
- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint
- `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`: The name of your Azure OpenAI chat model deployment
- `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME`: The name of your Azure OpenAI Responses deployment
Optionally, you can set:
- `AZURE_OPENAI_API_VERSION`: The API version to use (default is `2024-02-15-preview`)
- `AZURE_OPENAI_API_KEY`: Your Azure OpenAI API key (if not using `AzureCliCredential`)
- `AZURE_OPENAI_BASE_URL`: Your Azure OpenAI base URL (if different from the endpoint)
## Authentication
All examples use `AzureCliCredential` for authentication. Run `az login` in your terminal before running the examples, or replace `AzureCliCredential` with your preferred authentication method.
## Required role-based access control (RBAC) roles
To access the Azure OpenAI API, your Azure account or service principal needs one of the following RBAC roles assigned to the Azure OpenAI resource:
- **Cognitive Services OpenAI User**: Provides read access to Azure OpenAI resources and the ability to call the inference APIs. This is the minimum role required for running these examples.
- **Cognitive Services OpenAI Contributor**: Provides full access to Azure OpenAI resources, including the ability to create, update, and delete deployments and models.
For most scenarios, the **Cognitive Services OpenAI User** role is sufficient. You can assign this role through the Azure portal under the Azure OpenAI resource's "Access control (IAM)" section.
For more detailed information about Azure OpenAI RBAC roles, see: [Role-based access control for Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/role-based-access-control)
@@ -4,7 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework.azure import AzureAssistantsClient
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -25,7 +25,7 @@ async def non_streaming_example() -> None:
# and deleted after getting a response
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with AzureAssistantsClient(credential=AzureCliCredential()).create_agent(
async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()).create_agent(
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -41,7 +41,7 @@ async def streaming_example() -> None:
# Since no assistant ID is provided, the assistant will be automatically created
# and deleted after getting a response
async with AzureAssistantsClient(credential=AzureCliCredential()).create_agent(
async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()).create_agent(
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -3,7 +3,7 @@
import asyncio
from agent_framework import AgentRunResponseUpdate, ChatAgent, ChatResponseUpdate, HostedCodeInterpreterTool
from agent_framework.azure import AzureAssistantsClient
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from openai.types.beta.threads.runs import (
CodeInterpreterToolCallDelta,
@@ -40,7 +40,7 @@ async def main() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can write and execute Python code to solve problems.",
tools=HostedCodeInterpreterTool(),
) as agent:
@@ -6,7 +6,7 @@ from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework.azure import AzureAssistantsClient
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential, get_bearer_token_provider
from openai import AsyncAzureOpenAI
from pydantic import Field
@@ -38,7 +38,7 @@ async def main() -> None:
try:
async with ChatAgent(
chat_client=AzureAssistantsClient(async_client=client, assistant_id=created_assistant.id),
chat_client=AzureOpenAIAssistantsClient(async_client=client, assistant_id=created_assistant.id),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -5,7 +5,7 @@ import os
from random import randint
from typing import Annotated
from agent_framework.azure import AzureAssistantsClient
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -23,7 +23,7 @@ async def main() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with AzureAssistantsClient(
async with AzureOpenAIAssistantsClient(
endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
deployment_name=os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
@@ -6,7 +6,7 @@ from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework.azure import AzureAssistantsClient
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -34,7 +34,7 @@ async def tools_on_agent_level() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can provide weather and time information.",
tools=[get_weather, get_time], # Tools defined at agent creation
) as agent:
@@ -65,7 +65,7 @@ async def tools_on_run_level() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant.",
# No tools defined here
) as agent:
@@ -96,7 +96,7 @@ async def mixed_tools_example() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a comprehensive assistant that can help with various information requests.",
tools=[get_weather], # Base tool available for all queries
) as agent:
@@ -5,7 +5,7 @@ from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent
from agent_framework.azure import AzureAssistantsClient
from agent_framework.azure import AzureOpenAIAssistantsClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -25,7 +25,7 @@ async def example_with_automatic_thread_creation() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -51,7 +51,7 @@ async def example_with_thread_persistence() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -89,7 +89,7 @@ async def example_with_existing_thread_id() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
async with ChatAgent(
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -109,7 +109,7 @@ async def example_with_existing_thread_id() -> None:
# Create a new agent instance but use the existing thread ID
async with ChatAgent(
chat_client=AzureAssistantsClient(thread_id=existing_thread_id, credential=AzureCliCredential()),
chat_client=AzureOpenAIAssistantsClient(thread_id=existing_thread_id, credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
@@ -4,7 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework.azure import AzureChatClient
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -24,7 +24,7 @@ async def non_streaming_example() -> None:
# Create agent with Azure Chat Client
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = AzureChatClient(credential=AzureCliCredential()).create_agent(
agent = AzureOpenAIChatClient(credential=AzureCliCredential()).create_agent(
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -42,7 +42,7 @@ async def streaming_example() -> None:
# Create agent with Azure Chat Client
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = AzureChatClient(credential=AzureCliCredential()).create_agent(
agent = AzureOpenAIChatClient(credential=AzureCliCredential()).create_agent(
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -5,7 +5,7 @@ import os
from random import randint
from typing import Annotated
from agent_framework.azure import AzureChatClient
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -23,7 +23,7 @@ async def main() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = AzureChatClient(
agent = AzureOpenAIChatClient(
deployment_name=os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"],
endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
credential=AzureCliCredential(),
@@ -6,7 +6,7 @@ from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework.azure import AzureChatClient
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -34,7 +34,7 @@ async def tools_on_agent_level() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can provide weather and time information.",
tools=[get_weather, get_time], # Tools defined at agent creation
)
@@ -66,7 +66,7 @@ async def tools_on_run_level() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant.",
# No tools defined here
)
@@ -98,7 +98,7 @@ async def mixed_tools_example() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a comprehensive assistant that can help with various information requests.",
tools=[get_weather], # Base tool available for all queries
)
@@ -5,7 +5,7 @@ from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent, ChatMessageList
from agent_framework.azure import AzureChatClient
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -25,7 +25,7 @@ async def example_with_automatic_thread_creation() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -52,7 +52,7 @@ async def example_with_thread_persistence() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -87,7 +87,7 @@ async def example_with_existing_thread_messages() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -109,7 +109,7 @@ async def example_with_existing_thread_messages() -> None:
# Create a new agent instance but use the existing thread with its message history
new_agent = ChatAgent(
chat_client=AzureChatClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -4,7 +4,7 @@ import asyncio
from random import randint
from typing import Annotated
from agent_framework.azure import AzureResponsesClient
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -23,7 +23,7 @@ async def non_streaming_example() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = AzureResponsesClient(credential=AzureCliCredential()).create_agent(
agent = AzureOpenAIResponsesClient(credential=AzureCliCredential()).create_agent(
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -40,7 +40,7 @@ async def streaming_example() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = AzureResponsesClient(credential=AzureCliCredential()).create_agent(
agent = AzureOpenAIResponsesClient(credential=AzureCliCredential()).create_agent(
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -3,7 +3,7 @@
import asyncio
from agent_framework import ChatAgent, ChatResponse, HostedCodeInterpreterTool
from agent_framework.azure import AzureResponsesClient
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from openai.types.responses.response import Response as OpenAIResponse
from openai.types.responses.response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall
@@ -16,7 +16,7 @@ async def main() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can write and execute Python code to solve problems.",
tools=HostedCodeInterpreterTool(),
)
@@ -5,7 +5,7 @@ import os
from random import randint
from typing import Annotated
from agent_framework.azure import AzureResponsesClient
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -23,7 +23,7 @@ async def main() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = AzureResponsesClient(
agent = AzureOpenAIResponsesClient(
deployment_name=os.environ["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"],
endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
credential=AzureCliCredential(),
@@ -6,7 +6,7 @@ from random import randint
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework.azure import AzureResponsesClient
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -34,7 +34,7 @@ async def tools_on_agent_level() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant that can provide weather and time information.",
tools=[get_weather, get_time], # Tools defined at agent creation
)
@@ -66,7 +66,7 @@ async def tools_on_run_level() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful assistant.",
# No tools defined here
)
@@ -98,7 +98,7 @@ async def mixed_tools_example() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a comprehensive assistant that can help with various information requests.",
tools=[get_weather], # Base tool available for all queries
)
@@ -5,7 +5,7 @@ from random import randint
from typing import Annotated
from agent_framework import AgentThread, ChatAgent
from agent_framework.azure import AzureResponsesClient
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential
from pydantic import Field
@@ -25,7 +25,7 @@ async def example_with_automatic_thread_creation() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -54,7 +54,7 @@ async def example_with_thread_persistence_in_memory() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -95,7 +95,7 @@ async def example_with_existing_thread_id() -> None:
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
# authentication option.
agent = ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -117,7 +117,7 @@ async def example_with_existing_thread_id() -> None:
print("\n--- Continuing with the same thread ID in a new agent instance ---")
agent = ChatAgent(
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
instructions="You are a helpful weather agent.",
tools=get_weather,
)
@@ -1,35 +0,0 @@
# Azure Responses Agent Examples
This folder contains examples demonstrating different ways to create and use agents with the Azure Responses client from the `agent_framework.azure` package.
## Examples
| File | Description |
|------|-------------|
| [`azure_responses_client_basic.py`](azure_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with Azure OpenAI models. |
| [`azure_responses_client_with_explicit_settings.py`](azure_responses_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific responses client, configuring settings explicitly including endpoint and deployment name. |
| [`azure_responses_client_with_function_tools.py`](azure_responses_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`azure_responses_client_with_code_interpreter.py`](azure_responses_client_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
| [`azure_responses_client_with_thread.py`](azure_responses_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
## Environment Variables
Make sure to set the following environment variables before running the examples:
- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint
- `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME`: The name of your Azure OpenAI deployment
## Authentication
All examples use `AzureCliCredential` for authentication. Run `az login` in your terminal before running the examples, or replace `AzureCliCredential` with your preferred authentication method.
## Required role-based access control (RBAC) roles
To access the Azure OpenAI API, your Azure account or service principal needs one of the following RBAC roles assigned to the Azure OpenAI resource:
- **Cognitive Services OpenAI User**: Provides read access to Azure OpenAI resources and the ability to call the inference APIs. This is the minimum role required for running these examples.
- **Cognitive Services OpenAI Contributor**: Provides full access to Azure OpenAI resources, including the ability to create, update, and delete deployments and models.
For most scenarios, the **Cognitive Services OpenAI User** role is sufficient. You can assign this role through the Azure portal under the Azure OpenAI resource's "Access control (IAM)" section.
For more detailed information about Azure OpenAI RBAC roles, see: [Role-based access control for Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/role-based-access-control)
@@ -46,7 +46,7 @@ Your Azure AD App Registration should have:
```python
import asyncio
from agent_framework.copilotstudio import CopilotStudioAgent
from agent_framework.microsoft import CopilotStudioAgent
# Uses environment variables for configuration
async def main():
@@ -63,7 +63,7 @@ asyncio.run(main())
### Explicit Configuration
```python
from agent_framework.copilotstudio import CopilotStudioAgent, acquire_token
from agent_framework.microsoft import CopilotStudioAgent, acquire_token
from microsoft_agents.copilotstudio.client import ConnectionSettings, CopilotClient, PowerPlatformCloud, AgentType
# Acquire token manually
@@ -2,7 +2,7 @@
import asyncio
from agent_framework.copilotstudio import CopilotStudioAgent
from agent_framework.microsoft import CopilotStudioAgent
# Environment variables needed:
# COPILOTSTUDIOAGENT__ENVIRONMENTID - Environment ID where your copilot is deployed
@@ -3,7 +3,7 @@
import asyncio
import os
from agent_framework.copilotstudio import CopilotStudioAgent, acquire_token
from agent_framework.microsoft import CopilotStudioAgent, acquire_token
from microsoft_agents.copilotstudio.client import AgentType, ConnectionSettings, CopilotClient, PowerPlatformCloud
# Environment variables needed:
@@ -1,22 +0,0 @@
# Foundry Agent Examples
This folder contains examples demonstrating different ways to create and use agents with the Foundry chat client from the `agent_framework.foundry` package.
## Examples
| File | Description |
|------|-------------|
| [`foundry_basic.py`](foundry_basic.py) | The simplest way to create an agent using `ChatAgent` with `FoundryChatClient`. It automatically handles all configuration using environment variables. |
| [`foundry_with_explicit_settings.py`](foundry_with_explicit_settings.py) | Shows how to create an agent with explicitly configured `FoundryChatClient` settings, including project endpoint, model deployment, credentials, and agent name. |
| [`foundry_with_existing_agent.py`](foundry_with_existing_agent.py) | Shows how to work with a pre-existing agent by providing the agent ID to the Foundry chat client. This example also demonstrates proper cleanup of manually created agents. |
| [`foundry_with_function_tools.py`](foundry_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`foundry_with_code_interpreter.py`](foundry_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Foundry agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
| [`foundry_with_local_mcp.py`](foundry_with_local_mcp.py) | Shows how to integrate Foundry agents with Model Context Protocol (MCP) servers for enhanced functionality and tool integration. Demonstrates both agent-level and run-level tool configuration. |
| [`foundry_with_thread.py`](foundry_with_thread.py) | Demonstrates thread management with Foundry agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
## Environment Variables
Make sure to set the following environment variables before running the examples:
- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint
- `FOUNDRY_MODEL_DEPLOYMENT_NAME`: The name of your model deployment
@@ -1,11 +1,24 @@
# OpenAI Responses Agent Examples
# OpenAI Assistants Agent Examples
This folder contains examples demonstrating different ways to create and use agents with the OpenAI Responses client from the `agent_framework.openai` package.
This folder contains examples demonstrating different ways to create and use agents with the OpenAI Assistants client from the `agent_framework.openai` package.
## Examples
| File | Description |
|------|-------------|
| [`openai_assistants_basic.py`](openai_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. |
| [`openai_assistants_with_existing_assistant.py`](openai_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the OpenAI Assistants client. Demonstrates proper cleanup of manually created assistants. |
| [`openai_assistants_with_explicit_settings.py`](openai_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including API key and model ID. |
| [`openai_assistants_with_function_tools.py`](openai_assistants_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`openai_assistants_with_code_interpreter.py`](openai_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with OpenAI agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
| [`openai_assistants_with_file_search.py`](openai_assistants_with_file_search.py) | Demonstrates how to use file search capabilities with OpenAI agents, allowing the agent to search through uploaded files to answer questions. |
| [`openai_assistants_with_thread.py`](openai_assistants_with_thread.py) | Demonstrates thread management with OpenAI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
| [`openai_chat_client_basic.py`](openai_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with OpenAI models. |
| [`openai_chat_client_with_explicit_settings.py`](openai_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including API key and model ID. |
| [`openai_chat_client_with_function_tools.py`](openai_chat_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
| [`openai_chat_client_with_local_mcp.py`](openai_chat_client_with_local_mcp.py) | Shows how to integrate OpenAI agents with local Model Context Protocol (MCP) servers for enhanced functionality and tool integration. |
| [`openai_chat_client_with_thread.py`](openai_chat_client_with_thread.py) | Demonstrates thread management with OpenAI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
| [`openai_chat_client_with_web_search.py`](openai_chat_client_with_web_search.py) | Shows how to use web search capabilities with OpenAI agents to retrieve and use information from the internet in responses. |
| [`openai_responses_client_basic.py`](openai_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with OpenAI models. |
| [`openai_responses_client_reasoning.py`](openai_responses_client_reasoning.py) | Demonstrates how to use reasoning capabilities with OpenAI agents, showing how the agent can provide detailed reasoning for its responses. |
| [`openai_responses_client_with_explicit_settings.py`](openai_responses_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific responses client, configuring settings explicitly including API key and model ID. |
@@ -25,9 +38,14 @@ This folder contains examples demonstrating different ways to create and use age
Make sure to set the following environment variables before running the examples:
- `OPENAI_API_KEY`: Your OpenAI API key
- `OPENAI_CHAT_MODEL_ID`: The OpenAI model to use (e.g., `gpt-4o`, `gpt-4o-mini`, `gpt-3.5-turbo`)
- `OPENAI_RESPONSES_MODEL_ID`: The OpenAI model to use (e.g., `gpt-4o`, `gpt-4o-mini`, `gpt-3.5-turbo`)
- For image processing examples, use a vision-capable model like `gpt-4o` or `gpt-4o-mini`
Optionally, you can set:
- `OPENAI_ORG_ID`: Your OpenAI organization ID (if applicable)
- `OPENAI_API_BASE_URL`: Your OpenAI base URL (if using a different base URL)
## Optional Dependencies
Some examples require additional dependencies:
@@ -36,7 +54,7 @@ Some examples require additional dependencies:
```bash
# Using uv
uv add pillow
# Or using pip
pip install pillow
```

Some files were not shown because too many files have changed in this diff Show More