mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
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:
committed by
GitHub
Unverified
parent
366a7f7d47
commit
9355329dfd
+3
-3
@@ -1,6 +1,6 @@
|
||||
# Foundry
|
||||
FOUNDRY_PROJECT_ENDPOINT=""
|
||||
FOUNDRY_MODEL_DEPLOYMENT_NAME=""
|
||||
# Azure AI
|
||||
AZURE_AI_PROJECT_ENDPOINT=""
|
||||
AZURE_AI_MODEL_DEPLOYMENT_NAME=""
|
||||
# OpenAI
|
||||
OPENAI_API_KEY=""
|
||||
OPENAI_CHAT_MODEL_ID=""
|
||||
|
||||
+1
-1
@@ -275,7 +275,7 @@ The package follows a flat import structure:
|
||||
- **Connectors**: Import from `agent_framework.<vendor/platform>`
|
||||
```python
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from agent_framework.azure import AzureChatClient
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
+13
-11
@@ -5,12 +5,14 @@
|
||||
```bash
|
||||
# Base package including workflow support
|
||||
pip install agent-framework
|
||||
# Optional: Add Azure integration
|
||||
pip install agent-framework[azure]
|
||||
# Optional: Add Foundry integration
|
||||
pip install agent-framework[foundry]
|
||||
# Optional: Add Azure AI integration (Foundry)
|
||||
pip install agent-framework[azure-ai]
|
||||
# Optional: Add Microsoft integrations, currently only CopilotStudioAgents
|
||||
pip install agent-framework[microsoft]
|
||||
# Optional: Both
|
||||
pip install agent-framework[azure,foundry]
|
||||
pip install agent-framework[microsoft,azure-ai]
|
||||
# All-in-one
|
||||
pip install agent-framework[all]
|
||||
```
|
||||
|
||||
Supported Platforms:
|
||||
@@ -29,16 +31,16 @@ AZURE_OPENAI_API_KEY=...
|
||||
AZURE_OPENAI_ENDPOINT=...
|
||||
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME=...
|
||||
...
|
||||
FOUNDRY_PROJECT_ENDPOINT=...
|
||||
FOUNDRY_MODEL_DEPLOYMENT_NAME=...
|
||||
AZURE_AI_PROJECT_ENDPOINT=...
|
||||
AZURE_AI_MODEL_DEPLOYMENT_NAME=...
|
||||
```
|
||||
|
||||
You can also override environment variables by explicitly passing configuration parameters to the chat client constructor:
|
||||
|
||||
```python
|
||||
from agent_framework.azure import AzureChatClient
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
|
||||
chat_client = AzureChatClient(
|
||||
chat_client = AzureOpenAIChatClient(
|
||||
api_key='',
|
||||
endpoint='',
|
||||
deployment_name='',
|
||||
@@ -215,8 +217,8 @@ if __name__ == "__main__":
|
||||
|
||||
- [Getting Started with Agents](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/agents): Basic agent creation and tool usage
|
||||
- [Chat Client Examples](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/chat_client): Direct chat client usage patterns
|
||||
- [Azure Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure): Azure OpenAI and AI Foundry integration
|
||||
- [.NET Orchestration Samples](https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/GettingStarted/Orchestration): Advanced multi-agent patterns (.NET)
|
||||
- [Azure AI Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure-ai): Azure AI integration
|
||||
- [Workflow Samples](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/workflow): Advanced multi-agent patterns
|
||||
|
||||
## Agent Framework Documentation
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Get Started with Microsoft Agent Framework Azure
|
||||
# Get Started with Microsoft Agent Framework Azure AI
|
||||
|
||||
Please install this package as the extra for `agent-framework`:
|
||||
|
||||
```bash
|
||||
pip install agent-framework[azure]
|
||||
pip install agent-framework[azure_ai]
|
||||
```
|
||||
|
||||
and see the [README](https://github.com/microsoft/agent-framework/tree/main/python/README.md) for more information.
|
||||
+3
-3
@@ -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__",
|
||||
]
|
||||
+75
-70
@@ -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"]
|
||||
+4
-5
@@ -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
|
||||
+186
-188
@@ -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",
|
||||
]
|
||||
@@ -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
|
||||
@@ -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,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,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],
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -92,4 +92,4 @@ addopts = "--strict-markers --strict-config"
|
||||
markers = [
|
||||
"unit: marks tests as unit tests",
|
||||
"integration: marks tests as integration tests",
|
||||
]
|
||||
]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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"
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
+4
-4
@@ -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"
|
||||
+14
-14
@@ -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:
|
||||
+2
-1
@@ -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
|
||||
+7
-7
@@ -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:
|
||||
+12
-56
@@ -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):
|
||||
|
||||
@@ -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'
|
||||
|
||||
+2
-1
@@ -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
|
||||
+57
-57
@@ -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)
|
||||
+42
-42
@@ -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:
|
||||
+34
-34
@@ -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)
|
||||
|
||||
+2
-2
@@ -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,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,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,7 +1,7 @@
|
||||
[project]
|
||||
name = "agent-framework-runtime"
|
||||
description = "Runtime integration for Microsoft Agent Framework."
|
||||
authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}]
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "0.1.0b1"
|
||||
|
||||
+30
-7
@@ -5,9 +5,8 @@ version = "0.0.0"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"agent-framework",
|
||||
"agent-framework-azure",
|
||||
"agent-framework-azure-ai",
|
||||
"agent-framework-copilotstudio",
|
||||
"agent-framework-foundry",
|
||||
"agent-framework-mem0",
|
||||
"agent-framework-redis",
|
||||
"agent-framework-devui",
|
||||
@@ -55,9 +54,8 @@ exclude = [ "packages/agent_framework_project.egg-info", "packages/lab", "packag
|
||||
|
||||
[tool.uv.sources]
|
||||
agent-framework = { workspace = true }
|
||||
agent-framework-azure = { workspace = true }
|
||||
agent-framework-azure-ai = { workspace = true }
|
||||
agent-framework-copilotstudio = { workspace = true }
|
||||
agent-framework-foundry = { workspace = true }
|
||||
agent-framework-lab-gaia = { workspace = true }
|
||||
agent-framework-mem0 = { workspace = true }
|
||||
agent-framework-redis = { workspace = true }
|
||||
@@ -70,6 +68,9 @@ target-version = "py310"
|
||||
fix = true
|
||||
include = ["*.py", "*.pyi", "**/pyproject.toml", "*.ipynb"]
|
||||
exclude = ["docs/*", "run_tasks_in_packages_if_exists.py", "check_md_code_blocks.py"]
|
||||
extend-exclude = [
|
||||
"[{][{]cookiecutter.package_name[}][}]",
|
||||
]
|
||||
preview = true
|
||||
|
||||
[tool.ruff.lint]
|
||||
@@ -131,7 +132,7 @@ filterwarnings = []
|
||||
timeout = 120
|
||||
markers = [
|
||||
"azure: marks tests as Azure provider specific",
|
||||
"foundry: marks tests as Foundry provider specific",
|
||||
"azure-ai: marks tests as Azure AI provider specific",
|
||||
"openai: marks tests as OpenAI provider specific",
|
||||
]
|
||||
|
||||
@@ -187,8 +188,30 @@ build = "python run_tasks_in_packages_if_exists.py build"
|
||||
# combined checks
|
||||
check = ["fmt", "lint", "pyright", "mypy", "test", "markdown-code-lint", "samples-code-check"]
|
||||
pre-commit-check = ["fmt", "lint", "pyright", "markdown-code-lint", "samples-code-check"]
|
||||
all-tests = "pytest --import-mode=importlib --cov=agent_framework --cov=agent_framework_azure --cov=agent_framework_copilotstudio --cov=agent_framework_foundry --cov=agent_framework_mem0 --cov-report=term-missing:skip-covered packages/azure/tests packages/copilotstudio/tests packages/foundry/tests packages/main/tests packages/mem0/tests packages/redis/tests"
|
||||
|
||||
[tool.poe.tasks.all-tests-cov]
|
||||
cmd = """
|
||||
pytest --import-mode=importlib
|
||||
--cov=agent_framework
|
||||
--cov=agent_framework_azure_ai
|
||||
--cov=agent_framework_copilotstudio
|
||||
--cov=agent_framework_mem0
|
||||
--cov=agent_framework_redis
|
||||
--cov-report=term-missing:skip-covered
|
||||
--ignore-glob=packages/lab/**
|
||||
--ignore-glob=packages/devui/**
|
||||
-n logical --dist loadfile --dist worksteal
|
||||
packages/**/tests
|
||||
"""
|
||||
|
||||
[tool.poe.tasks.all-tests]
|
||||
cmd = """
|
||||
pytest --import-mode=importlib
|
||||
--ignore-glob=packages/lab/**
|
||||
--ignore-glob=packages/devui/**
|
||||
-n logical --dist loadfile --dist worksteal
|
||||
packages/**/tests
|
||||
"""
|
||||
|
||||
[tool.poe.tasks.venv]
|
||||
cmd = "uv venv --clear --python $python"
|
||||
@@ -227,4 +250,4 @@ sequence = [
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["packages"]
|
||||
include = ["agent_framework**"]
|
||||
namespaces = true
|
||||
namespaces = true
|
||||
|
||||
@@ -8,7 +8,7 @@ This folder contains examples demonstrating how to create and use agents with di
|
||||
|
||||
| Folder | Description |
|
||||
|--------|-------------|
|
||||
| **[`foundry/`](foundry/)** | Create agents using Azure AI Foundry |
|
||||
| **[`azure_ai/`](azure_ai/)** | Create agents using Azure AI Foundry Agent Service |
|
||||
|
||||
### Microsoft Copilot Studio Examples
|
||||
|
||||
@@ -20,14 +20,17 @@ This folder contains examples demonstrating how to create and use agents with di
|
||||
|
||||
| Folder | Description |
|
||||
|--------|-------------|
|
||||
| **[`azure_assistants_client/`](azure_assistants_client/)** | Create agents using Azure OpenAI Assistants API |
|
||||
| **[`azure_chat_client/`](azure_chat_client/)** | Create agents using Azure OpenAI Chat Completions API |
|
||||
| **[`azure_responses_client/`](azure_responses_client/)** | Create agents using Azure OpenAI Responses API |
|
||||
| **[`azure_openai/`](azure_openai/)** | Create agents using Azure OpenAI APIs |
|
||||
|
||||
### OpenAI Examples
|
||||
|
||||
| Folder | Description |
|
||||
|--------|-------------|
|
||||
| **[`openai_assistants_client/`](openai_assistants_client/)** | Create agents using OpenAI Assistants API |
|
||||
| **[`openai_chat_client/`](openai_chat_client/)** | Create agents using OpenAI Chat Completions API |
|
||||
| **[`openai_responses_client/`](openai_responses_client/)** | Create agents using OpenAI Responses API |
|
||||
| **[`openai/`](openai/)** | Create agents using OpenAI APIs |
|
||||
|
||||
### Custom Client Examples
|
||||
|
||||
| Folder | Description |
|
||||
|--------|-------------|
|
||||
| **[`custom_client/`](custom_client/)** | Create agents using a custom chat client or a custom agent |
|
||||
| **[`anthropic/`](anthropic/)** | Create agents using Anthropic APIs |
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Azure AI Agent Examples
|
||||
|
||||
This folder contains examples demonstrating different ways to create and use agents with the Azure AI chat client from the `agent_framework.azure` package.
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`azure_ai_basic.py`](azure_ai_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureAIAgentClient`. It automatically handles all configuration using environment variables. |
|
||||
| [`azure_ai_with_explicit_settings.py`](azure_ai_with_explicit_settings.py) | Shows how to create an agent with explicitly configured `AzureAIAgentClient` settings, including project endpoint, model deployment, credentials, and agent name. |
|
||||
| [`azure_ai_with_existing_agent.py`](azure_ai_with_existing_agent.py) | Shows how to work with a pre-existing agent by providing the agent ID to the Azure AI chat client. This example also demonstrates proper cleanup of manually created agents. |
|
||||
| [`azure_ai_with_function_tools.py`](azure_ai_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
|
||||
| [`azure_ai_with_code_interpreter.py`](azure_ai_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure AI agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`azure_ai_with_local_mcp.py`](azure_ai_with_local_mcp.py) | Shows how to integrate Azure AI agents with Model Context Protocol (MCP) servers for enhanced functionality and tool integration. Demonstrates both agent-level and run-level tool configuration. |
|
||||
| [`azure_ai_with_thread.py`](azure_ai_with_thread.py) | Demonstrates thread management with Azure AI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Make sure to set the following environment variables before running the examples:
|
||||
|
||||
- `AZURE_AZURE_FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI project endpoint
|
||||
- `AZURE_AZURE_FOUNDRY_MODEL_DEPLOYMENT_NAME`: The name of your model deployment
|
||||
|
||||
Optionally, you can set:
|
||||
- `AZURE_AZURE_FOUNDRY_AGENT_NAME`: The name of your agent, this can also be set programmatically when creating the agent.
|
||||
+4
-4
@@ -4,7 +4,7 @@ import asyncio
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.azure import AzureAIAgentClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -27,7 +27,7 @@ async def non_streaming_example() -> None:
|
||||
# authentication option.
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
FoundryChatClient(async_credential=credential).create_agent(
|
||||
AzureAIAgentClient(async_credential=credential).create_agent(
|
||||
name="WeatherAgent",
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
@@ -49,7 +49,7 @@ async def streaming_example() -> None:
|
||||
# authentication option.
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
FoundryChatClient(async_credential=credential).create_agent(
|
||||
AzureAIAgentClient(async_credential=credential).create_agent(
|
||||
name="WeatherAgent",
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
@@ -65,7 +65,7 @@ async def streaming_example() -> None:
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Basic Foundry Chat Client Agent Example ===")
|
||||
print("=== Basic Azure AI Chat Client Agent Example ===")
|
||||
|
||||
await non_streaming_example()
|
||||
await streaming_example()
|
||||
+7
-11
@@ -2,20 +2,16 @@
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
HostedCodeInterpreterTool,
|
||||
from agent_framework import AgentRunResponse, ChatResponseUpdate, HostedCodeInterpreterTool
|
||||
from agent_framework.azure import AzureAIAgentClient
|
||||
from azure.ai.agents.models import (
|
||||
RunStepDeltaCodeInterpreterDetailItemObject,
|
||||
)
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
|
||||
def print_code_interpreter_inputs(response: AgentRunResponse) -> None:
|
||||
"""Helper method to access code interpreter data."""
|
||||
from agent_framework import ChatResponseUpdate
|
||||
from azure.ai.agents.models import (
|
||||
RunStepDeltaCodeInterpreterDetailItemObject,
|
||||
)
|
||||
|
||||
print("\nCode Interpreter Inputs during the run:")
|
||||
if response.raw_representation is None:
|
||||
@@ -29,14 +25,14 @@ def print_code_interpreter_inputs(response: AgentRunResponse) -> None:
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Example showing how to use the HostedCodeInterpreterTool with Foundry."""
|
||||
print("=== Foundry Agent with Code Interpreter Example ===")
|
||||
"""Example showing how to use the HostedCodeInterpreterTool with Azure AI."""
|
||||
print("=== Azure AI Agent with Code Interpreter Example ===")
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
FoundryChatClient(async_credential=credential) as chat_client,
|
||||
AzureAIAgentClient(async_credential=credential) as chat_client,
|
||||
):
|
||||
agent = chat_client.create_agent(
|
||||
name="CodingAgent",
|
||||
+5
-5
@@ -6,7 +6,7 @@ from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import ChatAgent
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.azure import AzureAIAgentClient
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from pydantic import Field
|
||||
@@ -21,23 +21,23 @@ def get_weather(
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Chat Client with Existing Agent ===")
|
||||
print("=== Azure AI Chat Client with Existing Agent ===")
|
||||
|
||||
# Create the client
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
AIProjectClient(endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], credential=credential) as client,
|
||||
AIProjectClient(endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], credential=credential) as client,
|
||||
):
|
||||
# Create an agent that will persist
|
||||
created_agent = await client.agents.create_agent(
|
||||
model=os.environ["FOUNDRY_MODEL_DEPLOYMENT_NAME"], name="WeatherAgent"
|
||||
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], name="WeatherAgent"
|
||||
)
|
||||
|
||||
try:
|
||||
async with ChatAgent(
|
||||
# passing in the client is optional here, so if you take the agent_id from the portal
|
||||
# you can use it directly without the two lines above.
|
||||
chat_client=FoundryChatClient(client=client, agent_id=created_agent.id),
|
||||
chat_client=AzureAIAgentClient(client=client, agent_id=created_agent.id),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
) as agent:
|
||||
+5
-5
@@ -6,7 +6,7 @@ from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import ChatAgent
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.azure import AzureAIAgentClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -20,7 +20,7 @@ def get_weather(
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Chat Client with Explicit Settings ===")
|
||||
print("=== Azure AI Chat Client with Explicit Settings ===")
|
||||
|
||||
# Since no Agent ID is provided, the agent will be automatically created
|
||||
# and deleted after getting a response
|
||||
@@ -29,9 +29,9 @@ async def main() -> None:
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
ChatAgent(
|
||||
chat_client=FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model_deployment_name=os.environ["FOUNDRY_MODEL_DEPLOYMENT_NAME"],
|
||||
chat_client=AzureAIAgentClient(
|
||||
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
model_deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
|
||||
async_credential=credential,
|
||||
agent_name="WeatherAgent",
|
||||
),
|
||||
+5
-5
@@ -6,7 +6,7 @@ from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import ChatAgent
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.azure import AzureAIAgentClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -36,7 +36,7 @@ async def tools_on_agent_level() -> None:
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
ChatAgent(
|
||||
chat_client=FoundryChatClient(async_credential=credential),
|
||||
chat_client=AzureAIAgentClient(async_credential=credential),
|
||||
instructions="You are a helpful assistant that can provide weather and time information.",
|
||||
tools=[get_weather, get_time], # Tools defined at agent creation
|
||||
) as agent,
|
||||
@@ -70,7 +70,7 @@ async def tools_on_run_level() -> None:
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
ChatAgent(
|
||||
chat_client=FoundryChatClient(async_credential=credential),
|
||||
chat_client=AzureAIAgentClient(async_credential=credential),
|
||||
instructions="You are a helpful assistant.",
|
||||
# No tools defined here
|
||||
) as agent,
|
||||
@@ -104,7 +104,7 @@ async def mixed_tools_example() -> None:
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
ChatAgent(
|
||||
chat_client=FoundryChatClient(async_credential=credential),
|
||||
chat_client=AzureAIAgentClient(async_credential=credential),
|
||||
instructions="You are a comprehensive assistant that can help with various information requests.",
|
||||
tools=[get_weather], # Base tool available for all queries
|
||||
) as agent,
|
||||
@@ -122,7 +122,7 @@ async def mixed_tools_example() -> None:
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Chat Client Agent with Function Tools Examples ===\n")
|
||||
print("=== Azure AI Chat Client Agent with Function Tools Examples ===\n")
|
||||
|
||||
await tools_on_agent_level()
|
||||
await tools_on_run_level()
|
||||
+5
-5
@@ -4,7 +4,7 @@ import asyncio
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import AgentProtocol, AgentThread, HostedMCPTool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.azure import AzureAIAgentClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
|
||||
@@ -32,13 +32,13 @@ async def handle_approvals_with_thread(query: str, agent: "AgentProtocol", threa
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Example showing Hosted MCP tools for a Foundry Agent."""
|
||||
"""Example showing Hosted MCP tools for a Azure AI Agent."""
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
FoundryChatClient(async_credential=credential) as chat_client,
|
||||
AzureAIAgentClient(async_credential=credential) as chat_client,
|
||||
):
|
||||
# enable foundry observability
|
||||
await chat_client.setup_foundry_observability()
|
||||
# enable azure-ai observability
|
||||
await chat_client.setup_observability()
|
||||
agent = chat_client.create_agent(
|
||||
name="DocsAgent",
|
||||
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
|
||||
+4
-4
@@ -3,7 +3,7 @@
|
||||
import asyncio
|
||||
|
||||
from agent_framework import ChatAgent, MCPStreamableHTTPTool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.azure import AzureAIAgentClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ async def mcp_tools_on_run_level() -> None:
|
||||
url="https://learn.microsoft.com/api/mcp",
|
||||
) as mcp_server,
|
||||
ChatAgent(
|
||||
chat_client=FoundryChatClient(async_credential=credential),
|
||||
chat_client=AzureAIAgentClient(async_credential=credential),
|
||||
name="DocsAgent",
|
||||
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
|
||||
) as agent,
|
||||
@@ -48,7 +48,7 @@ async def mcp_tools_on_agent_level() -> None:
|
||||
# The agent will connect to the MCP server through its context manager.
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
FoundryChatClient(async_credential=credential).create_agent(
|
||||
AzureAIAgentClient(async_credential=credential).create_agent(
|
||||
name="DocsAgent",
|
||||
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
|
||||
tools=MCPStreamableHTTPTool( # Tools defined at agent creation
|
||||
@@ -71,7 +71,7 @@ async def mcp_tools_on_agent_level() -> None:
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Chat Client Agent with MCP Tools Examples ===\n")
|
||||
print("=== Azure AI Chat Client Agent with MCP Tools Examples ===\n")
|
||||
|
||||
await mcp_tools_on_agent_level()
|
||||
await mcp_tools_on_run_level()
|
||||
+5
-5
@@ -10,7 +10,7 @@ from agent_framework import (
|
||||
HostedMCPTool,
|
||||
HostedWebSearchTool,
|
||||
)
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.azure import AzureAIAgentClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
|
||||
@@ -44,13 +44,13 @@ async def handle_approvals_with_thread(query: str, agent: "AgentProtocol", threa
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Example showing Hosted MCP tools for a Foundry Agent."""
|
||||
"""Example showing Hosted MCP tools for a Azure AI Agent."""
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
FoundryChatClient(async_credential=credential) as chat_client,
|
||||
AzureAIAgentClient(async_credential=credential) as chat_client,
|
||||
):
|
||||
# enable foundry observability
|
||||
await chat_client.setup_foundry_observability()
|
||||
# enable azure-ai observability
|
||||
await chat_client.setup_observability()
|
||||
agent = chat_client.create_agent(
|
||||
name="DocsAgent",
|
||||
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
|
||||
+6
-6
@@ -5,7 +5,7 @@ from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import AgentThread, ChatAgent
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.azure import AzureAIAgentClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -27,7 +27,7 @@ async def example_with_automatic_thread_creation() -> None:
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
ChatAgent(
|
||||
chat_client=FoundryChatClient(async_credential=credential),
|
||||
chat_client=AzureAIAgentClient(async_credential=credential),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
) as agent,
|
||||
@@ -56,7 +56,7 @@ async def example_with_thread_persistence() -> None:
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
ChatAgent(
|
||||
chat_client=FoundryChatClient(async_credential=credential),
|
||||
chat_client=AzureAIAgentClient(async_credential=credential),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
) as agent,
|
||||
@@ -97,7 +97,7 @@ async def example_with_existing_thread_id() -> None:
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
ChatAgent(
|
||||
chat_client=FoundryChatClient(async_credential=credential),
|
||||
chat_client=AzureAIAgentClient(async_credential=credential),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
) as agent,
|
||||
@@ -120,7 +120,7 @@ async def example_with_existing_thread_id() -> None:
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
ChatAgent(
|
||||
chat_client=FoundryChatClient(thread_id=existing_thread_id, async_credential=credential),
|
||||
chat_client=AzureAIAgentClient(thread_id=existing_thread_id, async_credential=credential),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
) as agent,
|
||||
@@ -136,7 +136,7 @@ async def example_with_existing_thread_id() -> None:
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Foundry Chat Client Agent Thread Management Examples ===\n")
|
||||
print("=== Azure AI Chat Client Agent Thread Management Examples ===\n")
|
||||
|
||||
await example_with_automatic_thread_creation()
|
||||
await example_with_thread_persistence()
|
||||
@@ -1,36 +0,0 @@
|
||||
# Azure Assistants Agent Examples
|
||||
|
||||
This folder contains examples demonstrating different ways to create and use agents with the Azure Assistants client from the `agent_framework.azure` package.
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`azure_assistants_basic.py`](azure_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. |
|
||||
| [`azure_assistants_with_existing_assistant.py`](azure_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the Azure Assistants client. Demonstrates proper cleanup of manually created assistants. |
|
||||
| [`azure_assistants_with_explicit_settings.py`](azure_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including endpoint and deployment name. |
|
||||
| [`azure_assistants_with_function_tools.py`](azure_assistants_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
|
||||
| [`azure_assistants_with_code_interpreter.py`](azure_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`azure_assistants_with_thread.py`](azure_assistants_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Make sure to set the following environment variables before running the examples:
|
||||
|
||||
- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint
|
||||
- `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`: The name of your Azure OpenAI deployment
|
||||
|
||||
## Authentication
|
||||
|
||||
All examples use `AzureCliCredential` for authentication. Run `az login` in your terminal before running the examples, or replace `AzureCliCredential` with your preferred authentication method.
|
||||
|
||||
## Required role-based access control (RBAC) roles
|
||||
|
||||
To access the Azure OpenAI API, your Azure account or service principal needs one of the following RBAC roles assigned to the Azure OpenAI resource:
|
||||
|
||||
- **Cognitive Services OpenAI User**: Provides read access to Azure OpenAI resources and the ability to call the inference APIs. This is the minimum role required for running these examples.
|
||||
- **Cognitive Services OpenAI Contributor**: Provides full access to Azure OpenAI resources, including the ability to create, update, and delete deployments and models.
|
||||
|
||||
For most scenarios, the **Cognitive Services OpenAI User** role is sufficient. You can assign this role through the Azure portal under the Azure OpenAI resource's "Access control (IAM)" section.
|
||||
|
||||
For more detailed information about Azure OpenAI RBAC roles, see: [Role-based access control for Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/role-based-access-control)
|
||||
@@ -1,34 +0,0 @@
|
||||
# Azure Chat Agent Examples
|
||||
|
||||
This folder contains examples demonstrating different ways to create and use agents with the Azure Chat client from the `agent_framework.azure` package.
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`azure_chat_client_basic.py`](azure_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with Azure OpenAI models. |
|
||||
| [`azure_chat_client_with_explicit_settings.py`](azure_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including endpoint and deployment name. |
|
||||
| [`azure_chat_client_with_function_tools.py`](azure_chat_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
|
||||
| [`azure_chat_client_with_thread.py`](azure_chat_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Make sure to set the following environment variables before running the examples:
|
||||
|
||||
- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint
|
||||
- `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`: The name of your Azure OpenAI deployment
|
||||
|
||||
## Authentication
|
||||
|
||||
All examples use `AzureCliCredential` for authentication. Run `az login` in your terminal before running the examples, or replace `AzureCliCredential` with your preferred authentication method.
|
||||
|
||||
## Required role-based access control (RBAC) roles
|
||||
|
||||
To access the Azure OpenAI API, your Azure account or service principal needs one of the following RBAC roles assigned to the Azure OpenAI resource:
|
||||
|
||||
- **Cognitive Services OpenAI User**: Provides read access to Azure OpenAI resources and the ability to call the inference APIs. This is the minimum role required for running these examples.
|
||||
- **Cognitive Services OpenAI Contributor**: Provides full access to Azure OpenAI resources, including the ability to create, update, and delete deployments and models.
|
||||
|
||||
For most scenarios, the **Cognitive Services OpenAI User** role is sufficient. You can assign this role through the Azure portal under the Azure OpenAI resource's "Access control (IAM)" section.
|
||||
|
||||
For more detailed information about Azure OpenAI RBAC roles, see: [Role-based access control for Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/role-based-access-control)
|
||||
@@ -0,0 +1,51 @@
|
||||
# Azure OpenAI Agent Examples
|
||||
|
||||
This folder contains examples demonstrating different ways to create and use agents with the different Azure OpenAI chat client from the `agent_framework.azure` package.
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`azure_assistants_basic.py`](azure_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. |
|
||||
| [`azure_assistants_with_existing_assistant.py`](azure_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the Azure Assistants client. Demonstrates proper cleanup of manually created assistants. |
|
||||
| [`azure_assistants_with_explicit_settings.py`](azure_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including endpoint and deployment name. |
|
||||
| [`azure_assistants_with_function_tools.py`](azure_assistants_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
|
||||
| [`azure_assistants_with_code_interpreter.py`](azure_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`azure_assistants_with_thread.py`](azure_assistants_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
| [`azure_chat_client_basic.py`](azure_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with Azure OpenAI models. |
|
||||
| [`azure_chat_client_with_explicit_settings.py`](azure_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including endpoint and deployment name. |
|
||||
| [`azure_chat_client_with_function_tools.py`](azure_chat_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
|
||||
| [`azure_chat_client_with_thread.py`](azure_chat_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
| [`azure_responses_client_basic.py`](azure_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with Azure OpenAI models. |
|
||||
| [`azure_responses_client_with_explicit_settings.py`](azure_responses_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific responses client, configuring settings explicitly including endpoint and deployment name. |
|
||||
| [`azure_responses_client_with_function_tools.py`](azure_responses_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
|
||||
| [`azure_responses_client_with_code_interpreter.py`](azure_responses_client_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`azure_responses_client_with_thread.py`](azure_responses_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Make sure to set the following environment variables before running the examples:
|
||||
|
||||
- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint
|
||||
- `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`: The name of your Azure OpenAI chat model deployment
|
||||
- `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME`: The name of your Azure OpenAI Responses deployment
|
||||
|
||||
Optionally, you can set:
|
||||
- `AZURE_OPENAI_API_VERSION`: The API version to use (default is `2024-02-15-preview`)
|
||||
- `AZURE_OPENAI_API_KEY`: Your Azure OpenAI API key (if not using `AzureCliCredential`)
|
||||
- `AZURE_OPENAI_BASE_URL`: Your Azure OpenAI base URL (if different from the endpoint)
|
||||
|
||||
## Authentication
|
||||
|
||||
All examples use `AzureCliCredential` for authentication. Run `az login` in your terminal before running the examples, or replace `AzureCliCredential` with your preferred authentication method.
|
||||
|
||||
## Required role-based access control (RBAC) roles
|
||||
|
||||
To access the Azure OpenAI API, your Azure account or service principal needs one of the following RBAC roles assigned to the Azure OpenAI resource:
|
||||
|
||||
- **Cognitive Services OpenAI User**: Provides read access to Azure OpenAI resources and the ability to call the inference APIs. This is the minimum role required for running these examples.
|
||||
- **Cognitive Services OpenAI Contributor**: Provides full access to Azure OpenAI resources, including the ability to create, update, and delete deployments and models.
|
||||
|
||||
For most scenarios, the **Cognitive Services OpenAI User** role is sufficient. You can assign this role through the Azure portal under the Azure OpenAI resource's "Access control (IAM)" section.
|
||||
|
||||
For more detailed information about Azure OpenAI RBAC roles, see: [Role-based access control for Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/role-based-access-control)
|
||||
+3
-3
@@ -4,7 +4,7 @@ import asyncio
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework.azure import AzureAssistantsClient
|
||||
from agent_framework.azure import AzureOpenAIAssistantsClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -25,7 +25,7 @@ async def non_streaming_example() -> None:
|
||||
# and deleted after getting a response
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with AzureAssistantsClient(credential=AzureCliCredential()).create_agent(
|
||||
async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()).create_agent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
) as agent:
|
||||
@@ -41,7 +41,7 @@ async def streaming_example() -> None:
|
||||
|
||||
# Since no assistant ID is provided, the assistant will be automatically created
|
||||
# and deleted after getting a response
|
||||
async with AzureAssistantsClient(credential=AzureCliCredential()).create_agent(
|
||||
async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()).create_agent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
) as agent:
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
import asyncio
|
||||
|
||||
from agent_framework import AgentRunResponseUpdate, ChatAgent, ChatResponseUpdate, HostedCodeInterpreterTool
|
||||
from agent_framework.azure import AzureAssistantsClient
|
||||
from agent_framework.azure import AzureOpenAIAssistantsClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from openai.types.beta.threads.runs import (
|
||||
CodeInterpreterToolCallDelta,
|
||||
@@ -40,7 +40,7 @@ async def main() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with ChatAgent(
|
||||
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful assistant that can write and execute Python code to solve problems.",
|
||||
tools=HostedCodeInterpreterTool(),
|
||||
) as agent:
|
||||
+2
-2
@@ -6,7 +6,7 @@ from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import ChatAgent
|
||||
from agent_framework.azure import AzureAssistantsClient
|
||||
from agent_framework.azure import AzureOpenAIAssistantsClient
|
||||
from azure.identity import AzureCliCredential, get_bearer_token_provider
|
||||
from openai import AsyncAzureOpenAI
|
||||
from pydantic import Field
|
||||
@@ -38,7 +38,7 @@ async def main() -> None:
|
||||
|
||||
try:
|
||||
async with ChatAgent(
|
||||
chat_client=AzureAssistantsClient(async_client=client, assistant_id=created_assistant.id),
|
||||
chat_client=AzureOpenAIAssistantsClient(async_client=client, assistant_id=created_assistant.id),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
) as agent:
|
||||
+2
-2
@@ -5,7 +5,7 @@ import os
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework.azure import AzureAssistantsClient
|
||||
from agent_framework.azure import AzureOpenAIAssistantsClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -23,7 +23,7 @@ async def main() -> None:
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with AzureAssistantsClient(
|
||||
async with AzureOpenAIAssistantsClient(
|
||||
endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
|
||||
deployment_name=os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"],
|
||||
credential=AzureCliCredential(),
|
||||
+4
-4
@@ -6,7 +6,7 @@ from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import ChatAgent
|
||||
from agent_framework.azure import AzureAssistantsClient
|
||||
from agent_framework.azure import AzureOpenAIAssistantsClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -34,7 +34,7 @@ async def tools_on_agent_level() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with ChatAgent(
|
||||
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful assistant that can provide weather and time information.",
|
||||
tools=[get_weather, get_time], # Tools defined at agent creation
|
||||
) as agent:
|
||||
@@ -65,7 +65,7 @@ async def tools_on_run_level() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with ChatAgent(
|
||||
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful assistant.",
|
||||
# No tools defined here
|
||||
) as agent:
|
||||
@@ -96,7 +96,7 @@ async def mixed_tools_example() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with ChatAgent(
|
||||
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
|
||||
instructions="You are a comprehensive assistant that can help with various information requests.",
|
||||
tools=[get_weather], # Base tool available for all queries
|
||||
) as agent:
|
||||
+5
-5
@@ -5,7 +5,7 @@ from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import AgentThread, ChatAgent
|
||||
from agent_framework.azure import AzureAssistantsClient
|
||||
from agent_framework.azure import AzureOpenAIAssistantsClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -25,7 +25,7 @@ async def example_with_automatic_thread_creation() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with ChatAgent(
|
||||
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
) as agent:
|
||||
@@ -51,7 +51,7 @@ async def example_with_thread_persistence() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with ChatAgent(
|
||||
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
) as agent:
|
||||
@@ -89,7 +89,7 @@ async def example_with_existing_thread_id() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with ChatAgent(
|
||||
chat_client=AzureAssistantsClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
) as agent:
|
||||
@@ -109,7 +109,7 @@ async def example_with_existing_thread_id() -> None:
|
||||
|
||||
# Create a new agent instance but use the existing thread ID
|
||||
async with ChatAgent(
|
||||
chat_client=AzureAssistantsClient(thread_id=existing_thread_id, credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIAssistantsClient(thread_id=existing_thread_id, credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
) as agent:
|
||||
+3
-3
@@ -4,7 +4,7 @@ import asyncio
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework.azure import AzureChatClient
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -24,7 +24,7 @@ async def non_streaming_example() -> None:
|
||||
# Create agent with Azure Chat Client
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = AzureChatClient(credential=AzureCliCredential()).create_agent(
|
||||
agent = AzureOpenAIChatClient(credential=AzureCliCredential()).create_agent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
@@ -42,7 +42,7 @@ async def streaming_example() -> None:
|
||||
# Create agent with Azure Chat Client
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = AzureChatClient(credential=AzureCliCredential()).create_agent(
|
||||
agent = AzureOpenAIChatClient(credential=AzureCliCredential()).create_agent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
+2
-2
@@ -5,7 +5,7 @@ import os
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework.azure import AzureChatClient
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -23,7 +23,7 @@ async def main() -> None:
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = AzureChatClient(
|
||||
agent = AzureOpenAIChatClient(
|
||||
deployment_name=os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"],
|
||||
endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
|
||||
credential=AzureCliCredential(),
|
||||
+4
-4
@@ -6,7 +6,7 @@ from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import ChatAgent
|
||||
from agent_framework.azure import AzureChatClient
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -34,7 +34,7 @@ async def tools_on_agent_level() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureChatClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful assistant that can provide weather and time information.",
|
||||
tools=[get_weather, get_time], # Tools defined at agent creation
|
||||
)
|
||||
@@ -66,7 +66,7 @@ async def tools_on_run_level() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureChatClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful assistant.",
|
||||
# No tools defined here
|
||||
)
|
||||
@@ -98,7 +98,7 @@ async def mixed_tools_example() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureChatClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a comprehensive assistant that can help with various information requests.",
|
||||
tools=[get_weather], # Base tool available for all queries
|
||||
)
|
||||
+5
-5
@@ -5,7 +5,7 @@ from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import AgentThread, ChatAgent, ChatMessageList
|
||||
from agent_framework.azure import AzureChatClient
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -25,7 +25,7 @@ async def example_with_automatic_thread_creation() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureChatClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
@@ -52,7 +52,7 @@ async def example_with_thread_persistence() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureChatClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
@@ -87,7 +87,7 @@ async def example_with_existing_thread_messages() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureChatClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
@@ -109,7 +109,7 @@ async def example_with_existing_thread_messages() -> None:
|
||||
|
||||
# Create a new agent instance but use the existing thread with its message history
|
||||
new_agent = ChatAgent(
|
||||
chat_client=AzureChatClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
+3
-3
@@ -4,7 +4,7 @@ import asyncio
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework.azure import AzureResponsesClient
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -23,7 +23,7 @@ async def non_streaming_example() -> None:
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = AzureResponsesClient(credential=AzureCliCredential()).create_agent(
|
||||
agent = AzureOpenAIResponsesClient(credential=AzureCliCredential()).create_agent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
@@ -40,7 +40,7 @@ async def streaming_example() -> None:
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = AzureResponsesClient(credential=AzureCliCredential()).create_agent(
|
||||
agent = AzureOpenAIResponsesClient(credential=AzureCliCredential()).create_agent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
import asyncio
|
||||
|
||||
from agent_framework import ChatAgent, ChatResponse, HostedCodeInterpreterTool
|
||||
from agent_framework.azure import AzureResponsesClient
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from openai.types.responses.response import Response as OpenAIResponse
|
||||
from openai.types.responses.response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall
|
||||
@@ -16,7 +16,7 @@ async def main() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful assistant that can write and execute Python code to solve problems.",
|
||||
tools=HostedCodeInterpreterTool(),
|
||||
)
|
||||
+2
-2
@@ -5,7 +5,7 @@ import os
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework.azure import AzureResponsesClient
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -23,7 +23,7 @@ async def main() -> None:
|
||||
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = AzureResponsesClient(
|
||||
agent = AzureOpenAIResponsesClient(
|
||||
deployment_name=os.environ["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"],
|
||||
endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
|
||||
credential=AzureCliCredential(),
|
||||
+4
-4
@@ -6,7 +6,7 @@ from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import ChatAgent
|
||||
from agent_framework.azure import AzureResponsesClient
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -34,7 +34,7 @@ async def tools_on_agent_level() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful assistant that can provide weather and time information.",
|
||||
tools=[get_weather, get_time], # Tools defined at agent creation
|
||||
)
|
||||
@@ -66,7 +66,7 @@ async def tools_on_run_level() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful assistant.",
|
||||
# No tools defined here
|
||||
)
|
||||
@@ -98,7 +98,7 @@ async def mixed_tools_example() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
|
||||
instructions="You are a comprehensive assistant that can help with various information requests.",
|
||||
tools=[get_weather], # Base tool available for all queries
|
||||
)
|
||||
+5
-5
@@ -5,7 +5,7 @@ from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import AgentThread, ChatAgent
|
||||
from agent_framework.azure import AzureResponsesClient
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
@@ -25,7 +25,7 @@ async def example_with_automatic_thread_creation() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
@@ -54,7 +54,7 @@ async def example_with_thread_persistence_in_memory() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
@@ -95,7 +95,7 @@ async def example_with_existing_thread_id() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
@@ -117,7 +117,7 @@ async def example_with_existing_thread_id() -> None:
|
||||
print("\n--- Continuing with the same thread ID in a new agent instance ---")
|
||||
|
||||
agent = ChatAgent(
|
||||
chat_client=AzureResponsesClient(credential=AzureCliCredential()),
|
||||
chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()),
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
@@ -1,35 +0,0 @@
|
||||
# Azure Responses Agent Examples
|
||||
|
||||
This folder contains examples demonstrating different ways to create and use agents with the Azure Responses client from the `agent_framework.azure` package.
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`azure_responses_client_basic.py`](azure_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with Azure OpenAI models. |
|
||||
| [`azure_responses_client_with_explicit_settings.py`](azure_responses_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific responses client, configuring settings explicitly including endpoint and deployment name. |
|
||||
| [`azure_responses_client_with_function_tools.py`](azure_responses_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
|
||||
| [`azure_responses_client_with_code_interpreter.py`](azure_responses_client_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`azure_responses_client_with_thread.py`](azure_responses_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Make sure to set the following environment variables before running the examples:
|
||||
|
||||
- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint
|
||||
- `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME`: The name of your Azure OpenAI deployment
|
||||
|
||||
## Authentication
|
||||
|
||||
All examples use `AzureCliCredential` for authentication. Run `az login` in your terminal before running the examples, or replace `AzureCliCredential` with your preferred authentication method.
|
||||
|
||||
## Required role-based access control (RBAC) roles
|
||||
|
||||
To access the Azure OpenAI API, your Azure account or service principal needs one of the following RBAC roles assigned to the Azure OpenAI resource:
|
||||
|
||||
- **Cognitive Services OpenAI User**: Provides read access to Azure OpenAI resources and the ability to call the inference APIs. This is the minimum role required for running these examples.
|
||||
- **Cognitive Services OpenAI Contributor**: Provides full access to Azure OpenAI resources, including the ability to create, update, and delete deployments and models.
|
||||
|
||||
For most scenarios, the **Cognitive Services OpenAI User** role is sufficient. You can assign this role through the Azure portal under the Azure OpenAI resource's "Access control (IAM)" section.
|
||||
|
||||
For more detailed information about Azure OpenAI RBAC roles, see: [Role-based access control for Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/role-based-access-control)
|
||||
@@ -46,7 +46,7 @@ Your Azure AD App Registration should have:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from agent_framework.copilotstudio import CopilotStudioAgent
|
||||
from agent_framework.microsoft import CopilotStudioAgent
|
||||
|
||||
# Uses environment variables for configuration
|
||||
async def main():
|
||||
@@ -63,7 +63,7 @@ asyncio.run(main())
|
||||
### Explicit Configuration
|
||||
|
||||
```python
|
||||
from agent_framework.copilotstudio import CopilotStudioAgent, acquire_token
|
||||
from agent_framework.microsoft import CopilotStudioAgent, acquire_token
|
||||
from microsoft_agents.copilotstudio.client import ConnectionSettings, CopilotClient, PowerPlatformCloud, AgentType
|
||||
|
||||
# Acquire token manually
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework.copilotstudio import CopilotStudioAgent
|
||||
from agent_framework.microsoft import CopilotStudioAgent
|
||||
|
||||
# Environment variables needed:
|
||||
# COPILOTSTUDIOAGENT__ENVIRONMENTID - Environment ID where your copilot is deployed
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from agent_framework.copilotstudio import CopilotStudioAgent, acquire_token
|
||||
from agent_framework.microsoft import CopilotStudioAgent, acquire_token
|
||||
from microsoft_agents.copilotstudio.client import AgentType, ConnectionSettings, CopilotClient, PowerPlatformCloud
|
||||
|
||||
# Environment variables needed:
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# Foundry Agent Examples
|
||||
|
||||
This folder contains examples demonstrating different ways to create and use agents with the Foundry chat client from the `agent_framework.foundry` package.
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`foundry_basic.py`](foundry_basic.py) | The simplest way to create an agent using `ChatAgent` with `FoundryChatClient`. It automatically handles all configuration using environment variables. |
|
||||
| [`foundry_with_explicit_settings.py`](foundry_with_explicit_settings.py) | Shows how to create an agent with explicitly configured `FoundryChatClient` settings, including project endpoint, model deployment, credentials, and agent name. |
|
||||
| [`foundry_with_existing_agent.py`](foundry_with_existing_agent.py) | Shows how to work with a pre-existing agent by providing the agent ID to the Foundry chat client. This example also demonstrates proper cleanup of manually created agents. |
|
||||
| [`foundry_with_function_tools.py`](foundry_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
|
||||
| [`foundry_with_code_interpreter.py`](foundry_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Foundry agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`foundry_with_local_mcp.py`](foundry_with_local_mcp.py) | Shows how to integrate Foundry agents with Model Context Protocol (MCP) servers for enhanced functionality and tool integration. Demonstrates both agent-level and run-level tool configuration. |
|
||||
| [`foundry_with_thread.py`](foundry_with_thread.py) | Demonstrates thread management with Foundry agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Make sure to set the following environment variables before running the examples:
|
||||
|
||||
- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint
|
||||
- `FOUNDRY_MODEL_DEPLOYMENT_NAME`: The name of your model deployment
|
||||
+21
-3
@@ -1,11 +1,24 @@
|
||||
# OpenAI Responses Agent Examples
|
||||
# OpenAI Assistants Agent Examples
|
||||
|
||||
This folder contains examples demonstrating different ways to create and use agents with the OpenAI Responses client from the `agent_framework.openai` package.
|
||||
This folder contains examples demonstrating different ways to create and use agents with the OpenAI Assistants client from the `agent_framework.openai` package.
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`openai_assistants_basic.py`](openai_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. |
|
||||
| [`openai_assistants_with_existing_assistant.py`](openai_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the OpenAI Assistants client. Demonstrates proper cleanup of manually created assistants. |
|
||||
| [`openai_assistants_with_explicit_settings.py`](openai_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including API key and model ID. |
|
||||
| [`openai_assistants_with_function_tools.py`](openai_assistants_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
|
||||
| [`openai_assistants_with_code_interpreter.py`](openai_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with OpenAI agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`openai_assistants_with_file_search.py`](openai_assistants_with_file_search.py) | Demonstrates how to use file search capabilities with OpenAI agents, allowing the agent to search through uploaded files to answer questions. |
|
||||
| [`openai_assistants_with_thread.py`](openai_assistants_with_thread.py) | Demonstrates thread management with OpenAI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
| [`openai_chat_client_basic.py`](openai_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with OpenAI models. |
|
||||
| [`openai_chat_client_with_explicit_settings.py`](openai_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including API key and model ID. |
|
||||
| [`openai_chat_client_with_function_tools.py`](openai_chat_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). |
|
||||
| [`openai_chat_client_with_local_mcp.py`](openai_chat_client_with_local_mcp.py) | Shows how to integrate OpenAI agents with local Model Context Protocol (MCP) servers for enhanced functionality and tool integration. |
|
||||
| [`openai_chat_client_with_thread.py`](openai_chat_client_with_thread.py) | Demonstrates thread management with OpenAI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
| [`openai_chat_client_with_web_search.py`](openai_chat_client_with_web_search.py) | Shows how to use web search capabilities with OpenAI agents to retrieve and use information from the internet in responses. |
|
||||
| [`openai_responses_client_basic.py`](openai_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with OpenAI models. |
|
||||
| [`openai_responses_client_reasoning.py`](openai_responses_client_reasoning.py) | Demonstrates how to use reasoning capabilities with OpenAI agents, showing how the agent can provide detailed reasoning for its responses. |
|
||||
| [`openai_responses_client_with_explicit_settings.py`](openai_responses_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific responses client, configuring settings explicitly including API key and model ID. |
|
||||
@@ -25,9 +38,14 @@ This folder contains examples demonstrating different ways to create and use age
|
||||
Make sure to set the following environment variables before running the examples:
|
||||
|
||||
- `OPENAI_API_KEY`: Your OpenAI API key
|
||||
- `OPENAI_CHAT_MODEL_ID`: The OpenAI model to use (e.g., `gpt-4o`, `gpt-4o-mini`, `gpt-3.5-turbo`)
|
||||
- `OPENAI_RESPONSES_MODEL_ID`: The OpenAI model to use (e.g., `gpt-4o`, `gpt-4o-mini`, `gpt-3.5-turbo`)
|
||||
- For image processing examples, use a vision-capable model like `gpt-4o` or `gpt-4o-mini`
|
||||
|
||||
Optionally, you can set:
|
||||
- `OPENAI_ORG_ID`: Your OpenAI organization ID (if applicable)
|
||||
- `OPENAI_API_BASE_URL`: Your OpenAI base URL (if using a different base URL)
|
||||
|
||||
## Optional Dependencies
|
||||
|
||||
Some examples require additional dependencies:
|
||||
@@ -36,7 +54,7 @@ Some examples require additional dependencies:
|
||||
```bash
|
||||
# Using uv
|
||||
uv add pillow
|
||||
|
||||
|
||||
# Or using pip
|
||||
pip install pillow
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user