Skip to content

Commit 5d8be58

Browse files
dmytrostrukCopilot
andauthored
Python: Added support for application endpoints in Azure AI client (#2460)
* Added support for application endpoints in Azure AI client * Fixed tests * Update python/samples/getting_started/agents/azure_ai/azure_ai_with_application_endpoint.py Co-authored-by: Copilot <[email protected]> * Addressed comments --------- Co-authored-by: Copilot <[email protected]>
1 parent 0c1d12f commit 5d8be58

File tree

4 files changed

+128
-3
lines changed

4 files changed

+128
-3
lines changed

python/packages/azure-ai/agent_framework_azure_ai/_client.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,11 @@ def __init__(
155155
self.credential = async_credential
156156
self.model_id = azure_ai_settings.model_deployment_name
157157
self.conversation_id = conversation_id
158-
self._should_close_client = should_close_client # Track whether we should close client connection
158+
159+
# Track whether the application endpoint is used
160+
self._is_application_endpoint = "/applications/" in project_client._config.endpoint # type: ignore
161+
# Track whether we should close client connection
162+
self._should_close_client = should_close_client
159163

160164
async def setup_azure_ai_observability(self, enable_sensitive_data: bool | None = None) -> None:
161165
"""Use this method to setup tracing in your Azure AI Project.
@@ -316,9 +320,11 @@ async def prepare_options(
316320
"""Take ChatOptions and create the specific options for Azure AI."""
317321
prepared_messages, instructions = self._prepare_input(messages)
318322
run_options = await super().prepare_options(prepared_messages, chat_options, **kwargs)
319-
agent_reference = await self._get_agent_reference_or_create(run_options, instructions)
320323

321-
run_options["extra_body"] = {"agent": agent_reference}
324+
if not self._is_application_endpoint:
325+
# Application-scoped response APIs do not support "agent" property.
326+
agent_reference = await self._get_agent_reference_or_create(run_options, instructions)
327+
run_options["extra_body"] = {"agent": agent_reference}
322328

323329
conversation_id = chat_options.conversation_id or self.conversation_id
324330

python/packages/azure-ai/tests/test_azure_ai_client.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ def create_test_azure_ai_client(
8787
client.use_latest_version = use_latest_version
8888
client.model_id = azure_ai_settings.model_deployment_name
8989
client.conversation_id = conversation_id
90+
client._is_application_endpoint = False # type: ignore
9091
client._should_close_client = should_close_client # type: ignore
9192
client.additional_properties = {}
9293
client.middleware = None
@@ -305,6 +306,84 @@ async def test_azure_ai_client_prepare_options_basic(mock_project_client: MagicM
305306
assert run_options["extra_body"]["agent"]["name"] == "test-agent"
306307

307308

309+
@pytest.mark.parametrize(
310+
"endpoint,expects_agent",
311+
[
312+
("https://example.com/api/projects/my-project/applications/my-application/protocols", False),
313+
("https://example.com/api/projects/my-project", True),
314+
],
315+
)
316+
async def test_azure_ai_client_prepare_options_with_application_endpoint(
317+
mock_azure_credential: MagicMock, endpoint: str, expects_agent: bool
318+
) -> None:
319+
client = AzureAIClient(
320+
project_endpoint=endpoint,
321+
model_deployment_name="test-model",
322+
async_credential=mock_azure_credential,
323+
agent_name="test-agent",
324+
agent_version="1",
325+
)
326+
327+
messages = [ChatMessage(role=Role.USER, contents=[TextContent(text="Hello")])]
328+
chat_options = ChatOptions()
329+
330+
with (
331+
patch.object(client.__class__.__bases__[0], "prepare_options", return_value={"model": "test-model"}),
332+
patch.object(
333+
client,
334+
"_get_agent_reference_or_create",
335+
return_value={"name": "test-agent", "version": "1", "type": "agent_reference"},
336+
),
337+
):
338+
run_options = await client.prepare_options(messages, chat_options)
339+
340+
if expects_agent:
341+
assert "extra_body" in run_options
342+
assert run_options["extra_body"]["agent"]["name"] == "test-agent"
343+
else:
344+
assert "extra_body" not in run_options
345+
346+
347+
@pytest.mark.parametrize(
348+
"endpoint,expects_agent",
349+
[
350+
("https://example.com/api/projects/my-project/applications/my-application/protocols", False),
351+
("https://example.com/api/projects/my-project", True),
352+
],
353+
)
354+
async def test_azure_ai_client_prepare_options_with_application_project_client(
355+
mock_project_client: MagicMock, endpoint: str, expects_agent: bool
356+
) -> None:
357+
mock_project_client._config = MagicMock()
358+
mock_project_client._config.endpoint = endpoint
359+
360+
client = AzureAIClient(
361+
project_client=mock_project_client,
362+
model_deployment_name="test-model",
363+
agent_name="test-agent",
364+
agent_version="1",
365+
)
366+
367+
messages = [ChatMessage(role=Role.USER, contents=[TextContent(text="Hello")])]
368+
chat_options = ChatOptions()
369+
370+
with (
371+
patch.object(client.__class__.__bases__[0], "prepare_options", return_value={"model": "test-model"}),
372+
patch.object(
373+
client,
374+
"_get_agent_reference_or_create",
375+
return_value={"name": "test-agent", "version": "1", "type": "agent_reference"},
376+
),
377+
):
378+
run_options = await client.prepare_options(messages, chat_options)
379+
380+
if expects_agent:
381+
assert "extra_body" in run_options
382+
assert run_options["extra_body"]["agent"]["name"] == "test-agent"
383+
else:
384+
assert "extra_body" not in run_options
385+
386+
308387
async def test_azure_ai_client_initialize_client(mock_project_client: MagicMock) -> None:
309388
"""Test initialize_client method."""
310389
client = create_test_azure_ai_client(mock_project_client)

python/samples/getting_started/agents/azure_ai/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ This folder contains examples demonstrating different ways to create and use age
1616
| [`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 for mathematical problem solving and data analysis. |
1717
| [`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 name and version to the Azure AI client. Demonstrates agent reuse patterns for production scenarios. |
1818
| [`azure_ai_with_existing_conversation.py`](azure_ai_with_existing_conversation.py) | Demonstrates how to use an existing conversation created on the service side with Azure AI agents. Shows two approaches: specifying conversation ID at the client level and using AgentThread with an existing conversation ID. |
19+
| [`azure_ai_with_application_endpoint.py`](azure_ai_with_application_endpoint.py) | Demonstrates calling the Azure AI application-scoped endpoint. |
1920
| [`azure_ai_with_explicit_settings.py`](azure_ai_with_explicit_settings.py) | Shows how to create an agent with explicitly configured `AzureAIClient` settings, including project endpoint, model deployment, and credentials rather than relying on environment variable defaults. |
2021
| [`azure_ai_with_file_search.py`](azure_ai_with_file_search.py) | Shows how to use the `HostedFileSearchTool` with Azure AI agents to upload files, create vector stores, and enable agents to search through uploaded documents to answer user questions. |
2122
| [`azure_ai_with_hosted_mcp.py`](azure_ai_with_hosted_mcp.py) | Shows how to integrate hosted Model Context Protocol (MCP) tools with Azure AI Agent. |
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
import asyncio
4+
import os
5+
6+
from agent_framework import ChatAgent
7+
from agent_framework.azure import AzureAIClient
8+
from azure.ai.projects.aio import AIProjectClient
9+
from azure.identity.aio import AzureCliCredential
10+
11+
"""
12+
Azure AI Agent with Application Endpoint Example
13+
14+
This sample demonstrates working with pre-existing Azure AI Agents by providing
15+
application endpoint instead of project endpoint.
16+
"""
17+
18+
19+
async def main() -> None:
20+
# Create the client
21+
async with (
22+
AzureCliCredential() as credential,
23+
# Endpoint here should be application endpoint with format:
24+
# /api/projects/<project-name>/applications/<application-name>/protocols
25+
AIProjectClient(endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], credential=credential) as project_client,
26+
ChatAgent(
27+
chat_client=AzureAIClient(
28+
project_client=project_client,
29+
),
30+
) as agent,
31+
):
32+
query = "How are you?"
33+
print(f"User: {query}")
34+
result = await agent.run(query)
35+
print(f"Agent: {result}\n")
36+
37+
38+
if __name__ == "__main__":
39+
asyncio.run(main())

0 commit comments

Comments
 (0)