mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Allow AzureOpenAIResponsesClient creation with Foundry project endpoint (#3814)
* Initial plan * feat: extend AzureOpenAIResponsesClient to support Foundry project endpoints Add project_client and project_endpoint parameters to allow creating the client via an Azure AI Foundry project. When provided, the client uses AIProjectClient.get_openai_client() to obtain the OpenAI client. The azure-ai-projects package is imported lazily and only required when using the project endpoint path. Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> * fix: address code review - remove duplicate MagicMock imports in tests Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> * fix: add type field to Responses API input items and add Foundry sample - Add 'type: message' to input items in _prepare_message_for_openai to comply with the Responses API schema requirement - Filter out empty dicts from unsupported content types to prevent sending items with invalid empty type values - Add azure_responses_client_with_foundry.py sample demonstrating AzureOpenAIResponsesClient with project_endpoint - Update README and pyrightconfig.samples.json accordingly * updates to response format and setup * fix: patch AIProjectClient at correct module path in test Patch agent_framework.azure._responses_client.AIProjectClient instead of azure.ai.projects.aio.AIProjectClient since the import is at module level. * docs: add Foundry sample to READMEs and document AZURE_AI_PROJECT_ENDPOINT env var --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> Co-authored-by: eavanvalkenburg <github@vanvalkenburg.eu>
This commit is contained in:
committed by
GitHub
Unverified
parent
235c578059
commit
a427af91a9
@@ -24,7 +24,6 @@ classifiers = [
|
||||
]
|
||||
dependencies = [
|
||||
"agent-framework-core>=1.0.0b260210",
|
||||
"azure-ai-projects >= 2.0.0b3",
|
||||
"azure-ai-agents == 1.2.0b5",
|
||||
"aiohttp",
|
||||
]
|
||||
|
||||
@@ -7,11 +7,14 @@ from collections.abc import Mapping, Sequence
|
||||
from typing import TYPE_CHECKING, Any, Generic
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
from azure.core.credentials import TokenCredential
|
||||
from openai.lib.azure import AsyncAzureADTokenProvider, AsyncAzureOpenAI
|
||||
from openai import AsyncOpenAI
|
||||
from openai.lib.azure import AsyncAzureADTokenProvider
|
||||
from pydantic import ValidationError
|
||||
|
||||
from .._middleware import ChatMiddlewareLayer
|
||||
from .._telemetry import AGENT_FRAMEWORK_USER_AGENT
|
||||
from .._tools import FunctionInvocationConfiguration, FunctionInvocationLayer
|
||||
from ..exceptions import ServiceInitializationError
|
||||
from ..observability import ChatTelemetryLayer
|
||||
@@ -72,7 +75,9 @@ class AzureOpenAIResponsesClient( # type: ignore[misc]
|
||||
token_endpoint: str | None = None,
|
||||
credential: TokenCredential | None = None,
|
||||
default_headers: Mapping[str, str] | None = None,
|
||||
async_client: AsyncAzureOpenAI | None = None,
|
||||
async_client: AsyncOpenAI | None = None,
|
||||
project_client: Any | None = None,
|
||||
project_endpoint: str | None = None,
|
||||
env_file_path: str | None = None,
|
||||
env_file_encoding: str | None = None,
|
||||
instruction_role: str | None = None,
|
||||
@@ -82,6 +87,14 @@ class AzureOpenAIResponsesClient( # type: ignore[misc]
|
||||
) -> None:
|
||||
"""Initialize an Azure OpenAI Responses client.
|
||||
|
||||
The client can be created in two ways:
|
||||
|
||||
1. **Direct Azure OpenAI** (default): Provide endpoint, api_key, or credential
|
||||
to connect directly to an Azure OpenAI deployment.
|
||||
2. **Foundry project endpoint**: Provide a ``project_client`` or ``project_endpoint``
|
||||
(with ``credential``) to create the client via an Azure AI Foundry project.
|
||||
This requires the ``azure-ai-projects`` package to be installed.
|
||||
|
||||
Keyword Args:
|
||||
api_key: The API key. If provided, will override the value in the env vars or .env file.
|
||||
Can also be set via environment variable AZURE_OPENAI_API_KEY.
|
||||
@@ -105,6 +118,12 @@ class AzureOpenAIResponsesClient( # type: ignore[misc]
|
||||
default_headers: The default headers mapping of string keys to
|
||||
string values for HTTP requests.
|
||||
async_client: An existing client to use.
|
||||
project_client: An existing ``AIProjectClient`` (from ``azure.ai.projects.aio``) to use.
|
||||
The OpenAI client will be obtained via ``project_client.get_openai_client()``.
|
||||
Requires the ``azure-ai-projects`` package.
|
||||
project_endpoint: The Azure AI Foundry project endpoint URL.
|
||||
When provided with ``credential``, an ``AIProjectClient`` will be created
|
||||
and used to obtain the OpenAI client. Requires the ``azure-ai-projects`` package.
|
||||
env_file_path: Use the environment settings file as a fallback to using env vars.
|
||||
env_file_encoding: The encoding of the environment settings file, defaults to 'utf-8'.
|
||||
instruction_role: The role to use for 'instruction' messages, for example, summarization
|
||||
@@ -132,6 +151,27 @@ class AzureOpenAIResponsesClient( # type: ignore[misc]
|
||||
# Or loading from a .env file
|
||||
client = AzureOpenAIResponsesClient(env_file_path="path/to/.env")
|
||||
|
||||
# Using a Foundry project endpoint
|
||||
from azure.identity import DefaultAzureCredential
|
||||
|
||||
client = AzureOpenAIResponsesClient(
|
||||
project_endpoint="https://your-project.services.ai.azure.com",
|
||||
deployment_name="gpt-4o",
|
||||
credential=DefaultAzureCredential(),
|
||||
)
|
||||
|
||||
# Or using an existing AIProjectClient
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
|
||||
project_client = AIProjectClient(
|
||||
endpoint="https://your-project.services.ai.azure.com",
|
||||
credential=DefaultAzureCredential(),
|
||||
)
|
||||
client = AzureOpenAIResponsesClient(
|
||||
project_client=project_client,
|
||||
deployment_name="gpt-4o",
|
||||
)
|
||||
|
||||
# Using custom ChatOptions with type safety:
|
||||
from typing import TypedDict
|
||||
from agent_framework.azure import AzureOpenAIResponsesOptions
|
||||
@@ -146,6 +186,15 @@ class AzureOpenAIResponsesClient( # type: ignore[misc]
|
||||
"""
|
||||
if model_id := kwargs.pop("model_id", None) and not deployment_name:
|
||||
deployment_name = str(model_id)
|
||||
|
||||
# Project client path: create OpenAI client from an Azure AI Foundry project
|
||||
if async_client is None and (project_client is not None or project_endpoint is not None):
|
||||
async_client = self._create_client_from_project(
|
||||
project_client=project_client,
|
||||
project_endpoint=project_endpoint,
|
||||
credential=credential,
|
||||
)
|
||||
|
||||
try:
|
||||
azure_openai_settings = AzureOpenAISettings(
|
||||
# pydantic settings will see if there is a value, if not, will try the env var or .env file
|
||||
@@ -195,9 +244,48 @@ class AzureOpenAIResponsesClient( # type: ignore[misc]
|
||||
function_invocation_configuration=function_invocation_configuration,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _create_client_from_project(
|
||||
*,
|
||||
project_client: AIProjectClient | None,
|
||||
project_endpoint: str | None,
|
||||
credential: TokenCredential | None,
|
||||
) -> AsyncOpenAI:
|
||||
"""Create an AsyncOpenAI client from an Azure AI Foundry project.
|
||||
|
||||
Args:
|
||||
project_client: An existing AIProjectClient to use.
|
||||
project_endpoint: The Azure AI Foundry project endpoint URL.
|
||||
credential: Azure credential for authentication.
|
||||
|
||||
Returns:
|
||||
An AsyncAzureOpenAI client obtained from the project client.
|
||||
|
||||
Raises:
|
||||
ServiceInitializationError: If required parameters are missing or
|
||||
the azure-ai-projects package is not installed.
|
||||
"""
|
||||
if project_client is not None:
|
||||
return project_client.get_openai_client()
|
||||
|
||||
if not project_endpoint:
|
||||
raise ServiceInitializationError(
|
||||
"Azure AI project endpoint is required when project_client is not provided."
|
||||
)
|
||||
if not credential:
|
||||
raise ServiceInitializationError(
|
||||
"Azure credential is required when using project_endpoint without a project_client."
|
||||
)
|
||||
project_client = AIProjectClient(
|
||||
endpoint=project_endpoint,
|
||||
credential=credential, # type: ignore[arg-type]
|
||||
user_agent=AGENT_FRAMEWORK_USER_AGENT,
|
||||
)
|
||||
return project_client.get_openai_client()
|
||||
|
||||
@override
|
||||
def _check_model_presence(self, run_options: dict[str, Any]) -> None:
|
||||
if not run_options.get("model"):
|
||||
def _check_model_presence(self, options: dict[str, Any]) -> None:
|
||||
if not options.get("model"):
|
||||
if not self.model_id:
|
||||
raise ValueError("deployment_name must be a non-empty string")
|
||||
run_options["model"] = self.model_id
|
||||
options["model"] = self.model_id
|
||||
|
||||
@@ -9,6 +9,7 @@ from copy import copy
|
||||
from typing import Any, ClassVar, Final
|
||||
|
||||
from azure.core.credentials import TokenCredential
|
||||
from openai import AsyncOpenAI
|
||||
from openai.lib.azure import AsyncAzureOpenAI
|
||||
from pydantic import SecretStr, model_validator
|
||||
|
||||
@@ -162,7 +163,7 @@ class AzureOpenAIConfigMixin(OpenAIBase):
|
||||
token_endpoint: str | None = None,
|
||||
credential: TokenCredential | None = None,
|
||||
default_headers: Mapping[str, str] | None = None,
|
||||
client: AsyncAzureOpenAI | None = None,
|
||||
client: AsyncOpenAI | None = None,
|
||||
instruction_role: str | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
|
||||
@@ -901,6 +901,7 @@ class RawOpenAIResponsesClient( # type: ignore[misc]
|
||||
"""Prepare a chat message for the OpenAI Responses API format."""
|
||||
all_messages: list[dict[str, Any]] = []
|
||||
args: dict[str, Any] = {
|
||||
"type": "message",
|
||||
"role": message.role,
|
||||
}
|
||||
for content in message.contents:
|
||||
@@ -911,16 +912,22 @@ class RawOpenAIResponsesClient( # type: ignore[misc]
|
||||
case "function_result":
|
||||
new_args: dict[str, Any] = {}
|
||||
new_args.update(self._prepare_content_for_openai(message.role, content, call_id_to_id)) # type: ignore[arg-type]
|
||||
all_messages.append(new_args)
|
||||
if new_args:
|
||||
all_messages.append(new_args)
|
||||
case "function_call":
|
||||
function_call = self._prepare_content_for_openai(message.role, content, call_id_to_id) # type: ignore[arg-type]
|
||||
all_messages.append(function_call) # type: ignore
|
||||
if function_call:
|
||||
all_messages.append(function_call) # type: ignore
|
||||
case "function_approval_response" | "function_approval_request":
|
||||
all_messages.append(self._prepare_content_for_openai(message.role, content, call_id_to_id)) # type: ignore
|
||||
prepared = self._prepare_content_for_openai(Role(message.role), content, call_id_to_id)
|
||||
if prepared:
|
||||
all_messages.append(prepared) # type: ignore
|
||||
case _:
|
||||
if "content" not in args:
|
||||
args["content"] = []
|
||||
args["content"].append(self._prepare_content_for_openai(message.role, content, call_id_to_id)) # type: ignore
|
||||
prepared_content = self._prepare_content_for_openai(message.role, content, call_id_to_id) # type: ignore
|
||||
if prepared_content:
|
||||
if "content" not in args:
|
||||
args["content"] = []
|
||||
args["content"].append(prepared_content) # type: ignore
|
||||
if "content" in args or "tool_calls" in args:
|
||||
all_messages.append(args)
|
||||
return all_messages
|
||||
|
||||
@@ -34,6 +34,7 @@ dependencies = [
|
||||
# connectors and functions
|
||||
"openai>=1.99.0",
|
||||
"azure-identity>=1,<2",
|
||||
"azure-ai-projects >= 2.0.0b3",
|
||||
"mcp[ws]>=1.24.0,<2",
|
||||
"packaging>=24.1",
|
||||
]
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import json
|
||||
import os
|
||||
from typing import Annotated, Any
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from azure.identity import AzureCliCredential
|
||||
@@ -115,6 +116,119 @@ def test_init_with_empty_model_id(azure_openai_unit_test_env: dict[str, str]) ->
|
||||
)
|
||||
|
||||
|
||||
def test_init_with_project_client(azure_openai_unit_test_env: dict[str, str]) -> None:
|
||||
"""Test initialization with an existing AIProjectClient."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
# Create a mock AIProjectClient that returns a mock AsyncOpenAI client
|
||||
mock_openai_client = MagicMock(spec=AsyncOpenAI)
|
||||
mock_openai_client.default_headers = {}
|
||||
|
||||
mock_project_client = MagicMock()
|
||||
mock_project_client.get_openai_client.return_value = mock_openai_client
|
||||
|
||||
with patch(
|
||||
"agent_framework.azure._responses_client.AzureOpenAIResponsesClient._create_client_from_project",
|
||||
return_value=mock_openai_client,
|
||||
):
|
||||
azure_responses_client = AzureOpenAIResponsesClient(
|
||||
project_client=mock_project_client,
|
||||
deployment_name="gpt-4o",
|
||||
)
|
||||
|
||||
assert azure_responses_client.model_id == "gpt-4o"
|
||||
assert azure_responses_client.client is mock_openai_client
|
||||
assert isinstance(azure_responses_client, SupportsChatGetResponse)
|
||||
|
||||
|
||||
def test_init_with_project_endpoint(azure_openai_unit_test_env: dict[str, str]) -> None:
|
||||
"""Test initialization with a project endpoint and credential."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
mock_openai_client = MagicMock(spec=AsyncOpenAI)
|
||||
mock_openai_client.default_headers = {}
|
||||
|
||||
with patch(
|
||||
"agent_framework.azure._responses_client.AzureOpenAIResponsesClient._create_client_from_project",
|
||||
return_value=mock_openai_client,
|
||||
):
|
||||
azure_responses_client = AzureOpenAIResponsesClient(
|
||||
project_endpoint="https://test-project.services.ai.azure.com",
|
||||
deployment_name="gpt-4o",
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
|
||||
assert azure_responses_client.model_id == "gpt-4o"
|
||||
assert azure_responses_client.client is mock_openai_client
|
||||
assert isinstance(azure_responses_client, SupportsChatGetResponse)
|
||||
|
||||
|
||||
def test_create_client_from_project_with_project_client() -> None:
|
||||
"""Test _create_client_from_project with an existing project client."""
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
mock_openai_client = MagicMock(spec=AsyncOpenAI)
|
||||
mock_project_client = MagicMock()
|
||||
mock_project_client.get_openai_client.return_value = mock_openai_client
|
||||
|
||||
result = AzureOpenAIResponsesClient._create_client_from_project(
|
||||
project_client=mock_project_client,
|
||||
project_endpoint=None,
|
||||
credential=None,
|
||||
)
|
||||
|
||||
assert result is mock_openai_client
|
||||
mock_project_client.get_openai_client.assert_called_once()
|
||||
|
||||
|
||||
def test_create_client_from_project_with_endpoint() -> None:
|
||||
"""Test _create_client_from_project with a project endpoint."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
mock_openai_client = MagicMock(spec=AsyncOpenAI)
|
||||
mock_credential = MagicMock()
|
||||
|
||||
with patch("agent_framework.azure._responses_client.AIProjectClient") as MockAIProjectClient:
|
||||
mock_instance = MockAIProjectClient.return_value
|
||||
mock_instance.get_openai_client.return_value = mock_openai_client
|
||||
|
||||
result = AzureOpenAIResponsesClient._create_client_from_project(
|
||||
project_client=None,
|
||||
project_endpoint="https://test-project.services.ai.azure.com",
|
||||
credential=mock_credential,
|
||||
)
|
||||
|
||||
assert result is mock_openai_client
|
||||
MockAIProjectClient.assert_called_once()
|
||||
mock_instance.get_openai_client.assert_called_once()
|
||||
|
||||
|
||||
def test_create_client_from_project_missing_endpoint() -> None:
|
||||
"""Test _create_client_from_project raises error when endpoint is missing."""
|
||||
with pytest.raises(ServiceInitializationError, match="project endpoint is required"):
|
||||
AzureOpenAIResponsesClient._create_client_from_project(
|
||||
project_client=None,
|
||||
project_endpoint=None,
|
||||
credential=MagicMock(),
|
||||
)
|
||||
|
||||
|
||||
def test_create_client_from_project_missing_credential() -> None:
|
||||
"""Test _create_client_from_project raises error when credential is missing."""
|
||||
with pytest.raises(ServiceInitializationError, match="credential is required"):
|
||||
AzureOpenAIResponsesClient._create_client_from_project(
|
||||
project_client=None,
|
||||
project_endpoint="https://test-project.services.ai.azure.com",
|
||||
credential=None,
|
||||
)
|
||||
|
||||
|
||||
def test_serialize(azure_openai_unit_test_env: dict[str, str]) -> None:
|
||||
default_headers = {"X-Unit-Test": "test-guid"}
|
||||
|
||||
|
||||
@@ -798,12 +798,8 @@ def test_chat_message_with_error_content() -> None:
|
||||
|
||||
result = client._prepare_message_for_openai(message, call_id_to_id)
|
||||
|
||||
# Message should be prepared with empty content list since ErrorContent returns {}
|
||||
assert len(result) == 1
|
||||
prepared_message = result[0]
|
||||
assert prepared_message["role"] == "assistant"
|
||||
# Content should be a list with empty dict since ErrorContent returns {}
|
||||
assert prepared_message.get("content") == [{}]
|
||||
# Message should be empty since ErrorContent is filtered out
|
||||
assert len(result) == 0
|
||||
|
||||
|
||||
def test_chat_message_with_usage_content() -> None:
|
||||
@@ -823,12 +819,8 @@ def test_chat_message_with_usage_content() -> None:
|
||||
|
||||
result = client._prepare_message_for_openai(message, call_id_to_id)
|
||||
|
||||
# Message should be prepared with empty content list since UsageContent returns {}
|
||||
assert len(result) == 1
|
||||
prepared_message = result[0]
|
||||
assert prepared_message["role"] == "assistant"
|
||||
# Content should be a list with empty dict since UsageContent returns {}
|
||||
assert prepared_message.get("content") == [{}]
|
||||
# Message should be empty since UsageContent is filtered out
|
||||
assert len(result) == 0
|
||||
|
||||
|
||||
def test_hosted_file_content_preparation() -> None:
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
"**/autogen-migration/**",
|
||||
"**/semantic-kernel-migration/**",
|
||||
"**/demos/**",
|
||||
"**/agent_with_foundry_tracing.py"
|
||||
"**/agent_with_foundry_tracing.py",
|
||||
"**/azure_responses_client_with_foundry.py"
|
||||
],
|
||||
"typeCheckingMode": "off",
|
||||
"reportMissingImports": "error",
|
||||
|
||||
@@ -78,6 +78,7 @@ This directory contains samples demonstrating the capabilities of Microsoft Agen
|
||||
| [`getting_started/agents/azure_openai/azure_responses_client_image_analysis.py`](./getting_started/agents/azure_openai/azure_responses_client_image_analysis.py) | Azure OpenAI Responses Client with Image Analysis Example |
|
||||
| [`getting_started/agents/azure_openai/azure_responses_client_with_code_interpreter.py`](./getting_started/agents/azure_openai/azure_responses_client_with_code_interpreter.py) | Azure OpenAI Responses Client with Code Interpreter Example |
|
||||
| [`getting_started/agents/azure_openai/azure_responses_client_with_explicit_settings.py`](./getting_started/agents/azure_openai/azure_responses_client_with_explicit_settings.py) | Azure OpenAI Responses Client with Explicit Settings Example |
|
||||
| [`getting_started/agents/azure_openai/azure_responses_client_with_foundry.py`](./getting_started/agents/azure_openai/azure_responses_client_with_foundry.py) | Azure OpenAI Responses Client with Foundry Project Example |
|
||||
| [`getting_started/agents/azure_openai/azure_responses_client_with_function_tools.py`](./getting_started/agents/azure_openai/azure_responses_client_with_function_tools.py) | Azure OpenAI Responses Client with Function Tools Example |
|
||||
| [`getting_started/agents/azure_openai/azure_responses_client_with_hosted_mcp.py`](./getting_started/agents/azure_openai/azure_responses_client_with_hosted_mcp.py) | Azure OpenAI Responses Client with Hosted Model Context Protocol (MCP) Example |
|
||||
| [`getting_started/agents/azure_openai/azure_responses_client_with_local_mcp.py`](./getting_started/agents/azure_openai/azure_responses_client_with_local_mcp.py) | Azure OpenAI Responses Client with local Model Context Protocol (MCP) Example |
|
||||
|
||||
@@ -22,6 +22,7 @@ This folder contains examples demonstrating different ways to create and use age
|
||||
| [`azure_responses_client_with_code_interpreter.py`](azure_responses_client_with_code_interpreter.py) | Shows how to use `AzureOpenAIResponsesClient.get_code_interpreter_tool()` with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. |
|
||||
| [`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_file_search.py`](azure_responses_client_with_file_search.py) | Demonstrates using `AzureOpenAIResponsesClient.get_file_search_tool()` with Azure OpenAI Responses Client for direct document-based question answering and information retrieval from vector stores. |
|
||||
| [`azure_responses_client_with_foundry.py`](azure_responses_client_with_foundry.py) | Shows how to create an agent using an Azure AI Foundry project endpoint instead of a direct Azure OpenAI endpoint. Requires the `azure-ai-projects` package. |
|
||||
| [`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_hosted_mcp.py`](azure_responses_client_with_hosted_mcp.py) | Shows how to integrate Azure OpenAI Responses Client with hosted Model Context Protocol (MCP) servers using `AzureOpenAIResponsesClient.get_mcp_tool()` for extended functionality. |
|
||||
| [`azure_responses_client_with_local_mcp.py`](azure_responses_client_with_local_mcp.py) | Shows how to integrate Azure OpenAI Responses Client with local Model Context Protocol (MCP) servers using MCPStreamableHTTPTool for extended functionality. |
|
||||
@@ -35,6 +36,9 @@ Make sure to set the following environment variables before running the examples
|
||||
- `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
|
||||
|
||||
For the Foundry project sample (`azure_responses_client_with_foundry.py`), also set:
|
||||
- `AZURE_AI_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint
|
||||
|
||||
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`)
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import tool
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import Field
|
||||
|
||||
"""
|
||||
Azure OpenAI Responses Client with Foundry Project Example
|
||||
|
||||
This sample demonstrates how to create an AzureOpenAIResponsesClient using an
|
||||
Azure AI Foundry project endpoint. Instead of providing an Azure OpenAI endpoint
|
||||
directly, you provide a Foundry project endpoint and the client is created via
|
||||
the Azure AI Foundry project SDK.
|
||||
|
||||
This requires:
|
||||
- The `azure-ai-projects` package to be installed.
|
||||
- The `AZURE_AI_PROJECT_ENDPOINT` environment variable set to your Foundry project endpoint.
|
||||
- The `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME` environment variable set to the model deployment name.
|
||||
"""
|
||||
|
||||
load_dotenv() # Load environment variables from .env file if present
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
) -> str:
|
||||
"""Get the weather for a given location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
|
||||
|
||||
|
||||
async def non_streaming_example() -> None:
|
||||
"""Example of non-streaming response (get the complete result at once)."""
|
||||
print("=== Non-streaming Response Example ===")
|
||||
|
||||
# 1. Create the AzureOpenAIResponsesClient using a Foundry project endpoint.
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
credential = AzureCliCredential()
|
||||
agent = AzureOpenAIResponsesClient(
|
||||
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
deployment_name=os.environ["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"],
|
||||
credential=credential,
|
||||
).as_agent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
# 2. Run a query and print the result.
|
||||
query = "What's the weather like in Seattle?"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Result: {result}\n")
|
||||
|
||||
|
||||
async def streaming_example() -> None:
|
||||
"""Example of streaming response (get results as they are generated)."""
|
||||
print("=== Streaming Response Example ===")
|
||||
|
||||
# 1. Create the AzureOpenAIResponsesClient using a Foundry project endpoint.
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
credential = AzureCliCredential()
|
||||
agent = AzureOpenAIResponsesClient(
|
||||
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
deployment_name=os.environ["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"],
|
||||
credential=credential,
|
||||
).as_agent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
# 2. Stream the response and print each chunk as it arrives.
|
||||
query = "What's the weather like in Portland?"
|
||||
print(f"User: {query}")
|
||||
print("Agent: ", end="", flush=True)
|
||||
async for chunk in agent.run(query, stream=True):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
print("\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Azure OpenAI Responses Client with Foundry Project Example ===")
|
||||
|
||||
await non_streaming_example()
|
||||
await streaming_example()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
|
||||
"""
|
||||
Sample output:
|
||||
=== Azure OpenAI Responses Client with Foundry Project Example ===
|
||||
=== Non-streaming Response Example ===
|
||||
User: What's the weather like in Seattle?
|
||||
Result: The weather in Seattle is cloudy with a high of 18°C.
|
||||
|
||||
=== Streaming Response Example ===
|
||||
User: What's the weather like in Portland?
|
||||
Agent: The weather in Portland is sunny with a high of 25°C.
|
||||
"""
|
||||
Generated
+2
-2
@@ -209,7 +209,6 @@ dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "azure-ai-agents", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "azure-ai-projects", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
@@ -217,7 +216,6 @@ requires-dist = [
|
||||
{ name = "agent-framework-core", editable = "packages/core" },
|
||||
{ name = "aiohttp" },
|
||||
{ name = "azure-ai-agents", specifier = "==1.2.0b5" },
|
||||
{ name = "azure-ai-projects", specifier = ">=2.0.0b3" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -324,6 +322,7 @@ name = "agent-framework-core"
|
||||
version = "1.0.0b260210"
|
||||
source = { editable = "packages/core" }
|
||||
dependencies = [
|
||||
{ name = "azure-ai-projects", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "azure-identity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "mcp", extra = ["ws"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
{ name = "openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -378,6 +377,7 @@ requires-dist = [
|
||||
{ name = "agent-framework-orchestrations", marker = "extra == 'all'", editable = "packages/orchestrations" },
|
||||
{ name = "agent-framework-purview", marker = "extra == 'all'", editable = "packages/purview" },
|
||||
{ name = "agent-framework-redis", marker = "extra == 'all'", editable = "packages/redis" },
|
||||
{ name = "azure-ai-projects", specifier = ">=2.0.0b3" },
|
||||
{ name = "azure-identity", specifier = ">=1,<2" },
|
||||
{ name = "mcp", extras = ["ws"], specifier = ">=1.24.0,<2" },
|
||||
{ name = "openai", specifier = ">=1.99.0" },
|
||||
|
||||
Reference in New Issue
Block a user