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
@@ -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"