From e515c6db4c5f79f243706fc003016702028d7764 Mon Sep 17 00:00:00 2001 From: Giles Odigwe Date: Tue, 17 Feb 2026 15:03:12 -0800 Subject: [PATCH 1/5] Python: Fix Azure AI sample errors - azure_ai_with_application_endpoint: Add missing name to Agent constructor - azure_ai_with_file_search: Fix resource path (parents[2] -> parents[3]) - azure_ai_with_openapi: Fix resource path (parents[2] -> parents[3]) - azure_ai_with_session: Use get_agent/get_session to reuse existing agent version and preserve conversation context across agent instances --- .../azure_ai/azure_ai_with_application_endpoint.py | 1 + .../providers/azure_ai/azure_ai_with_file_search.py | 2 +- .../providers/azure_ai/azure_ai_with_openapi.py | 2 +- .../providers/azure_ai/azure_ai_with_session.py | 9 ++++----- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/python/samples/02-agents/providers/azure_ai/azure_ai_with_application_endpoint.py b/python/samples/02-agents/providers/azure_ai/azure_ai_with_application_endpoint.py index db1c80a597..8bfb72883b 100644 --- a/python/samples/02-agents/providers/azure_ai/azure_ai_with_application_endpoint.py +++ b/python/samples/02-agents/providers/azure_ai/azure_ai_with_application_endpoint.py @@ -24,6 +24,7 @@ async def main() -> None: # /api/projects//applications//protocols AIProjectClient(endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], credential=credential) as project_client, Agent( + name="ApplicationAgent", client=AzureAIClient( project_client=project_client, ), diff --git a/python/samples/02-agents/providers/azure_ai/azure_ai_with_file_search.py b/python/samples/02-agents/providers/azure_ai/azure_ai_with_file_search.py index 13f238a97e..750cb7578e 100644 --- a/python/samples/02-agents/providers/azure_ai/azure_ai_with_file_search.py +++ b/python/samples/02-agents/providers/azure_ai/azure_ai_with_file_search.py @@ -35,7 +35,7 @@ async def main() -> None: ): try: # 1. Upload file and create vector store - pdf_file_path = Path(__file__).parents[2] / "shared" / "resources" / "employees.pdf" + pdf_file_path = Path(__file__).parents[3] / "shared" / "resources" / "employees.pdf" print(f"Uploading file from: {pdf_file_path}") file = await agents_client.files.upload_and_poll(file_path=str(pdf_file_path), purpose="assistants") diff --git a/python/samples/02-agents/providers/azure_ai/azure_ai_with_openapi.py b/python/samples/02-agents/providers/azure_ai/azure_ai_with_openapi.py index 73b8cf5102..55e9ecb602 100644 --- a/python/samples/02-agents/providers/azure_ai/azure_ai_with_openapi.py +++ b/python/samples/02-agents/providers/azure_ai/azure_ai_with_openapi.py @@ -20,7 +20,7 @@ async def main() -> None: # Load the OpenAPI specification - resources_path = Path(__file__).parents[2] / "shared" / "resources" / "countries.json" + resources_path = Path(__file__).parents[3] / "shared" / "resources" / "countries.json" with open(resources_path) as f: openapi_countries = json.load(f) diff --git a/python/samples/02-agents/providers/azure_ai/azure_ai_with_session.py b/python/samples/02-agents/providers/azure_ai/azure_ai_with_session.py index d5fe1ba9c3..1350f3faa9 100644 --- a/python/samples/02-agents/providers/azure_ai/azure_ai_with_session.py +++ b/python/samples/02-agents/providers/azure_ai/azure_ai_with_session.py @@ -133,15 +133,14 @@ async def example_with_existing_session_id() -> None: if existing_session_id: print("\n--- Continuing with the same session ID in a new agent instance ---") - # Create a new agent instance from the same provider - second_agent = await provider.create_agent( + # Retrieve the same agent (reuses existing agent version on the service) + second_agent = await provider.get_agent( name="BasicWeatherAgent", - instructions="You are a helpful weather agent.", tools=get_weather, ) - # Create a session with the existing ID - session = second_agent.create_session(service_session_id=existing_session_id) + # Attach the existing service session ID so conversation context is preserved + session = second_agent.get_session(service_session_id=existing_session_id) second_query = "What was the last city I asked about?" print(f"User: {second_query}") From c93675580e4ee96c4ba175bea1c7406c491202f7 Mon Sep 17 00:00:00 2001 From: Giles Odigwe Date: Tue, 17 Feb 2026 16:56:50 -0800 Subject: [PATCH 2/5] Python: Fix resource paths in azure_ai_agent samples - azure_ai_with_file_search: Fix path to employees.pdf (parent.parent -> parents[3]/shared) - azure_ai_with_openapi_tools: Fix path to weather.json/countries.json (parents[2] -> parents[3]) --- .../providers/azure_ai_agent/azure_ai_with_file_search.py | 2 +- .../providers/azure_ai_agent/azure_ai_with_openapi_tools.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/samples/02-agents/providers/azure_ai_agent/azure_ai_with_file_search.py b/python/samples/02-agents/providers/azure_ai_agent/azure_ai_with_file_search.py index 51613d394f..1f06a6c89d 100644 --- a/python/samples/02-agents/providers/azure_ai_agent/azure_ai_with_file_search.py +++ b/python/samples/02-agents/providers/azure_ai_agent/azure_ai_with_file_search.py @@ -35,7 +35,7 @@ async def main() -> None: ): try: # 1. Upload file and create vector store - pdf_file_path = Path(__file__).parent.parent / "resources" / "employees.pdf" + pdf_file_path = Path(__file__).parents[3] / "shared" / "resources" / "employees.pdf" print(f"Uploading file from: {pdf_file_path}") file = await agents_client.files.upload_and_poll(file_path=str(pdf_file_path), purpose="assistants") diff --git a/python/samples/02-agents/providers/azure_ai_agent/azure_ai_with_openapi_tools.py b/python/samples/02-agents/providers/azure_ai_agent/azure_ai_with_openapi_tools.py index ff5ad8c8dc..aff0c649d8 100644 --- a/python/samples/02-agents/providers/azure_ai_agent/azure_ai_with_openapi_tools.py +++ b/python/samples/02-agents/providers/azure_ai_agent/azure_ai_with_openapi_tools.py @@ -23,7 +23,7 @@ def load_openapi_specs() -> tuple[dict[str, Any], dict[str, Any]]: """Load OpenAPI specification files.""" - resources_path = Path(__file__).parents[2] / "shared" / "resources" + resources_path = Path(__file__).parents[3] / "shared" / "resources" with open(resources_path / "weather.json") as weather_file: weather_spec = json.load(weather_file) From 401b19d8bc4dc552ebb8f6a9b848f653a843ae7e Mon Sep 17 00:00:00 2001 From: Giles Odigwe Date: Tue, 17 Feb 2026 18:56:23 -0800 Subject: [PATCH 3/5] fix V1 SDK hosted tools (FileSearchTool, etc.) silently dropped during agent creation --- .../_agent_provider.py | 28 ++++++------------- .../azure-ai/tests/test_agent_provider.py | 7 +++-- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/python/packages/azure-ai/agent_framework_azure_ai/_agent_provider.py b/python/packages/azure-ai/agent_framework_azure_ai/_agent_provider.py index 65bcb9d6c0..436fc602cb 100644 --- a/python/packages/azure-ai/agent_framework_azure_ai/_agent_provider.py +++ b/python/packages/azure-ai/agent_framework_azure_ai/_agent_provider.py @@ -3,7 +3,7 @@ from __future__ import annotations import sys -from collections.abc import Callable, MutableMapping, Sequence +from collections.abc import Callable, Sequence from typing import Any, Generic, cast from agent_framework import ( @@ -25,7 +25,7 @@ from pydantic import BaseModel from ._chat_client import AzureAIAgentClient, AzureAIAgentOptions -from ._shared import AzureAISettings, from_azure_ai_agent_tools, to_azure_ai_agent_tools +from ._shared import AzureAISettings, to_azure_ai_agent_tools if sys.version_info >= (3, 13): from typing import Self, TypeVar # type: ignore # pragma: no cover @@ -238,13 +238,9 @@ async def create_agent( # Local MCP tools (MCPTool) are handled by Agent at runtime, not stored on the Azure agent normalized_tools = normalize_tools(tools) if normalized_tools: - # Only convert non-MCP tools to Azure AI format - non_mcp_tools: list[FunctionTool | MutableMapping[str, Any]] = [] - for normalized_tool in normalized_tools: - if isinstance(normalized_tool, MCPTool): - continue - if isinstance(normalized_tool, (FunctionTool, MutableMapping)): - non_mcp_tools.append(normalized_tool) + # Collect all non-MCP tools for Azure AI agent creation. + # to_azure_ai_agent_tools handles FunctionTool, SDK Tool types (FileSearchTool, etc.), and dicts. + non_mcp_tools: list[Any] = [t for t in normalized_tools if not isinstance(t, MCPTool)] if non_mcp_tools: # Pass run_options to capture tool_resources (e.g., for file search vector stores) run_options: dict[str, Any] = {} @@ -429,16 +425,10 @@ def _merge_tools( """ merged: list[ToolTypes] = [] - # Convert hosted tools from agent definition - hosted_tools = from_azure_ai_agent_tools(agent_tools) - for hosted_tool in hosted_tools: - # Skip function tool dicts - they don't have implementations - # Skip OpenAPI tool dicts - they're defined on the agent, not needed at runtime - if isinstance(hosted_tool, dict): - tool_type = hosted_tool.get("type") - if tool_type == "function" or tool_type == "openapi": - continue - merged.append(hosted_tool) + # Hosted tools (file_search, code_interpreter, bing_grounding, openapi, etc.) + # are already defined on the server agent and will be read back by the client + # at run time via agent_definition.tools. We skip them here to avoid sending + # them again at request time (which causes API errors like unknown vector_store_ids). # Add user-provided function tools and MCP tools if provided_tools: diff --git a/python/packages/azure-ai/tests/test_agent_provider.py b/python/packages/azure-ai/tests/test_agent_provider.py index b8755ed7d7..e5717341da 100644 --- a/python/packages/azure-ai/tests/test_agent_provider.py +++ b/python/packages/azure-ai/tests/test_agent_provider.py @@ -437,7 +437,7 @@ def test_as_agent_with_hosted_tools( azure_ai_unit_test_env: dict[str, str], mock_agents_client: MagicMock, ) -> None: - """Test as_agent handles hosted tools correctly.""" + """Test as_agent excludes hosted tools from local tools (they stay on the server agent).""" mock_code_interpreter = MagicMock() mock_code_interpreter.type = "code_interpreter" @@ -456,9 +456,10 @@ def test_as_agent_with_hosted_tools( agent = provider.as_agent(mock_agent) assert isinstance(agent, Agent) - # Should have code_interpreter dict tool in the default_options tools + # Hosted tools (code_interpreter, file_search, etc.) are already on the server agent + # and should NOT be in local tools to avoid re-sending them at run time tools = agent.default_options.get("tools") or [] - assert any(isinstance(t, dict) and t.get("type") == "code_interpreter" for t in tools) + assert not any(isinstance(t, dict) and t.get("type") == "code_interpreter" for t in tools) def test_as_agent_with_dict_function_tools_validates( From feb179a7216f23c819165902db9a7d041154631a Mon Sep 17 00:00:00 2001 From: Giles Odigwe Date: Tue, 17 Feb 2026 19:21:20 -0800 Subject: [PATCH 4/5] fix: V2 file search sample uses correct SDK (AIProjectClient instead of AgentsClient) The azure_ai/azure_ai_with_file_search.py sample incorrectly used the V1 AgentsClient for file/vector store operations. Replaced with V2 pattern: AIProjectClient + get_openai_client() for file upload and vector store management, matching the official Azure AI Projects SDK samples. --- .../azure_ai/azure_ai_with_file_search.py | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/python/samples/02-agents/providers/azure_ai/azure_ai_with_file_search.py b/python/samples/02-agents/providers/azure_ai/azure_ai_with_file_search.py index 750cb7578e..fbec6d9717 100644 --- a/python/samples/02-agents/providers/azure_ai/azure_ai_with_file_search.py +++ b/python/samples/02-agents/providers/azure_ai/azure_ai_with_file_search.py @@ -1,12 +1,12 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import contextlib import os from pathlib import Path from agent_framework.azure import AzureAIClient, AzureAIProjectAgentProvider -from azure.ai.agents.aio import AgentsClient -from azure.ai.agents.models import FileInfo, VectorStore +from azure.ai.projects.aio import AIProjectClient from azure.identity.aio import AzureCliCredential """ @@ -25,27 +25,29 @@ async def main() -> None: """Main function demonstrating Azure AI agent with file search capabilities.""" - file: FileInfo | None = None - vector_store: VectorStore | None = None - async with ( AzureCliCredential() as credential, - AgentsClient(endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], credential=credential) as agents_client, - AzureAIProjectAgentProvider(credential=credential) as provider, + AIProjectClient(endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], credential=credential) as project_client, + AzureAIProjectAgentProvider(project_client=project_client) as provider, ): + openai_client = project_client.get_openai_client() + try: - # 1. Upload file and create vector store + # 1. Upload file and create vector store via OpenAI client pdf_file_path = Path(__file__).parents[3] / "shared" / "resources" / "employees.pdf" print(f"Uploading file from: {pdf_file_path}") - file = await agents_client.files.upload_and_poll(file_path=str(pdf_file_path), purpose="assistants") - print(f"Uploaded file, file ID: {file.id}") - - vector_store = await agents_client.vector_stores.create_and_poll(file_ids=[file.id], name="my_vectorstore") + vector_store = await openai_client.vector_stores.create(name="my_vectorstore") print(f"Created vector store, vector store ID: {vector_store.id}") - # 2. Create a client to access hosted tool factory methods - client = AzureAIClient(credential=credential) + file = await openai_client.vector_stores.files.upload_and_poll( + vector_store_id=vector_store.id, + file=open(pdf_file_path, "rb"), + ) + print(f"Uploaded file, file ID: {file.id}") + + # 2. Create a file search tool + client = AzureAIClient(project_client=project_client) file_search_tool = client.get_file_search_tool(vector_store_ids=[vector_store.id]) # 3. Create an agent with file search capabilities using the provider @@ -64,11 +66,9 @@ async def main() -> None: response = await agent.run(user_input) print(f"# Agent: {response.text}") finally: - # 5. Cleanup: Delete the vector store and file in case of earlier failure to prevent orphaned resources. - if vector_store: - await agents_client.vector_stores.delete(vector_store.id) - if file: - await agents_client.files.delete(file.id) + # 5. Cleanup: Delete the vector store (also deletes associated files) + with contextlib.suppress(Exception): + await openai_client.vector_stores.delete(vector_store.id) if __name__ == "__main__": From 7339aae299a46715a51038866704e612f1a20bcf Mon Sep 17 00:00:00 2001 From: Giles Odigwe Date: Tue, 17 Feb 2026 19:24:37 -0800 Subject: [PATCH 5/5] fix: use context manager for file open in V2 file search sample --- .../providers/azure_ai/azure_ai_with_file_search.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/python/samples/02-agents/providers/azure_ai/azure_ai_with_file_search.py b/python/samples/02-agents/providers/azure_ai/azure_ai_with_file_search.py index fbec6d9717..025f17b5a3 100644 --- a/python/samples/02-agents/providers/azure_ai/azure_ai_with_file_search.py +++ b/python/samples/02-agents/providers/azure_ai/azure_ai_with_file_search.py @@ -40,10 +40,11 @@ async def main() -> None: vector_store = await openai_client.vector_stores.create(name="my_vectorstore") print(f"Created vector store, vector store ID: {vector_store.id}") - file = await openai_client.vector_stores.files.upload_and_poll( - vector_store_id=vector_store.id, - file=open(pdf_file_path, "rb"), - ) + with open(pdf_file_path, "rb") as f: + file = await openai_client.vector_stores.files.upload_and_poll( + vector_store_id=vector_store.id, + file=f, + ) print(f"Uploaded file, file ID: {file.id}") # 2. Create a file search tool