From acc9a18f5ec89ab8193c18d8db8e4e486116c94d Mon Sep 17 00:00:00 2001 From: Eduard van Valkenburg Date: Tue, 2 Dec 2025 15:10:34 +0100 Subject: [PATCH 1/7] added inline yaml sample --- python/.vscode/settings.json | 4 ++- .../getting_started/declarative/README.md | 15 +++++++-- .../declarative/get_weather_agent.py | 2 +- .../declarative/inline_yaml.py | 31 +++++++++++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 python/samples/getting_started/declarative/inline_yaml.py diff --git a/python/.vscode/settings.json b/python/.vscode/settings.json index 47da1de9e4..4a6441e319 100644 --- a/python/.vscode/settings.json +++ b/python/.vscode/settings.json @@ -38,5 +38,7 @@ "name": "azure", "depth": 2 } - ] + ], + "python-envs.defaultEnvManager": "ms-python.python:system", + "python-envs.pythonProjects": [] } diff --git a/python/samples/getting_started/declarative/README.md b/python/samples/getting_started/declarative/README.md index 0956c33a67..92bc7fa106 100644 --- a/python/samples/getting_started/declarative/README.md +++ b/python/samples/getting_started/declarative/README.md @@ -47,7 +47,17 @@ Shows how to create an agent that can search and retrieve information from Micro **Key concepts**: Azure AI Foundry integration, MCP server usage, async patterns, resource management -### 3. **Azure OpenAI Responses Agent** ([`azure_openai_responses_agent.py`](./azure_openai_responses_agent.py)) +### 3. **Inline Yaml Agent** ([`inline_yaml.py`](./inline_yaml.py)) + +Shows how to create an agent using an inline YAML string rather than a file. + +- Uses Azure AI Foundry v2 Client with instructions. + +**Requirements**: `pip install agent-framework-azure-ai --pre` + +**Key concepts**: Inline yaml definition. + +### 4. **Azure OpenAI Responses Agent** ([`azure_openai_responses_agent.py`](./azure_openai_responses_agent.py)) Illustrates a basic agent using Azure OpenAI with structured responses. @@ -58,7 +68,7 @@ Illustrates a basic agent using Azure OpenAI with structured responses. **Key concepts**: Azure OpenAI integration, credential management, structured outputs -### 4. **OpenAI Responses Agent** ([`openai_responses_agent.py`](./openai_responses_agent.py)) +### 5. **OpenAI Responses Agent** ([`openai_responses_agent.py`](./openai_responses_agent.py)) Demonstrates the simplest possible agent using OpenAI directly. @@ -243,6 +253,7 @@ Each sample can be run independently. Make sure you have the required environmen # Run a specific sample python get_weather_agent.py python microsoft_learn_agent.py +python inline_yaml.py python azure_openai_responses_agent.py python openai_responses_agent.py ``` diff --git a/python/samples/getting_started/declarative/get_weather_agent.py b/python/samples/getting_started/declarative/get_weather_agent.py index 3994d8ede7..4e54af2461 100644 --- a/python/samples/getting_started/declarative/get_weather_agent.py +++ b/python/samples/getting_started/declarative/get_weather_agent.py @@ -26,7 +26,7 @@ async def main(): # create the AgentFactory with a chat client and bindings agent_factory = AgentFactory( - AzureOpenAIResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), bindings={"get_weather": get_weather}, ) # create the agent from the yaml diff --git a/python/samples/getting_started/declarative/inline_yaml.py b/python/samples/getting_started/declarative/inline_yaml.py new file mode 100644 index 0000000000..f14e2032fe --- /dev/null +++ b/python/samples/getting_started/declarative/inline_yaml.py @@ -0,0 +1,31 @@ +# Copyright (c) Microsoft. All rights reserved. +import asyncio + +from agent_framework.declarative import AgentFactory +from azure.identity.aio import AzureCliCredential + + +async def main(): + """Create an agent from a declarative yaml specification and run it.""" + yaml_definition = """kind: Prompt +name: DiagnosticAgent +displayName: Diagnostic Assistant +instructions: Specialized diagnostic and issue detection agent for systems with critical error protocol and automatic handoff capabilities + +model: + id: =Env.AZURE_OPENAI_MODEL + connection: + kind: remote + endpoint: =Env.AZURE_AI_PROJECT_ENDPOINT +""" + # create the agent from the yaml + async with ( + AzureCliCredential() as credential, + AgentFactory(client_kwargs={"async_credential": credential}).create_agent_from_yaml(yaml_definition) as agent, + ): + response = await agent.run("What can you do for me?") + print("Agent response:", response.text) + + +if __name__ == "__main__": + asyncio.run(main()) From dd19a9352b5cc0bff3c3f1b3b71044bc4d5816cc Mon Sep 17 00:00:00 2001 From: Eduard van Valkenburg Date: Tue, 2 Dec 2025 16:20:47 +0100 Subject: [PATCH 2/7] fixed some typos and added intro comment --- .../samples/getting_started/declarative/README.md | 4 ++-- .../getting_started/declarative/inline_yaml.py | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/python/samples/getting_started/declarative/README.md b/python/samples/getting_started/declarative/README.md index 92bc7fa106..2db3d0a82f 100644 --- a/python/samples/getting_started/declarative/README.md +++ b/python/samples/getting_started/declarative/README.md @@ -47,7 +47,7 @@ Shows how to create an agent that can search and retrieve information from Micro **Key concepts**: Azure AI Foundry integration, MCP server usage, async patterns, resource management -### 3. **Inline Yaml Agent** ([`inline_yaml.py`](./inline_yaml.py)) +### 3. **Inline YAML Agent** ([`inline_yaml.py`](./inline_yaml.py)) Shows how to create an agent using an inline YAML string rather than a file. @@ -55,7 +55,7 @@ Shows how to create an agent using an inline YAML string rather than a file. **Requirements**: `pip install agent-framework-azure-ai --pre` -**Key concepts**: Inline yaml definition. +**Key concepts**: Inline YAML definition. ### 4. **Azure OpenAI Responses Agent** ([`azure_openai_responses_agent.py`](./azure_openai_responses_agent.py)) diff --git a/python/samples/getting_started/declarative/inline_yaml.py b/python/samples/getting_started/declarative/inline_yaml.py index f14e2032fe..fb5683dea2 100644 --- a/python/samples/getting_started/declarative/inline_yaml.py +++ b/python/samples/getting_started/declarative/inline_yaml.py @@ -4,9 +4,21 @@ from agent_framework.declarative import AgentFactory from azure.identity.aio import AzureCliCredential +""" +This sample shows how to create an agent using an inline YAML string rather than a file. + +It uses a Azure AI Client so it needs the credential to be passed into the AgentFactory. + +Prerequisites: +- `pip install agent-framework-azure-ai agent-framework-declarative --pre` +- Set the following environment variables in a .env file or your environment: + - AZURE_AI_PROJECT_ENDPOINT + - AZURE_OPENAI_MODEL +""" + async def main(): - """Create an agent from a declarative yaml specification and run it.""" + """Create an agent from a declarative YAML specification and run it.""" yaml_definition = """kind: Prompt name: DiagnosticAgent displayName: Diagnostic Assistant From 314025b0648a96f466c963969447713bcfb8baa5 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 3 Dec 2025 09:39:10 +0100 Subject: [PATCH 3/7] added description params and pass through to client --- python/.vscode/settings.json | 4 +--- .../agent_framework_azure_ai/_chat_client.py | 9 +++++++- .../agent_framework_azure_ai/_client.py | 12 ++++++++-- .../tests/test_azure_ai_agent_client.py | 22 +++++++++++-------- .../azure-ai/tests/test_azure_ai_client.py | 12 +++++----- .../packages/core/agent_framework/_agents.py | 10 +++++---- .../openai/_assistants_client.py | 16 +++++++++++--- .../openai/test_openai_assistants_client.py | 18 +++++++-------- .../declarative/inline_yaml.py | 1 + 9 files changed, 67 insertions(+), 37 deletions(-) diff --git a/python/.vscode/settings.json b/python/.vscode/settings.json index 4a6441e319..47da1de9e4 100644 --- a/python/.vscode/settings.json +++ b/python/.vscode/settings.json @@ -38,7 +38,5 @@ "name": "azure", "depth": 2 } - ], - "python-envs.defaultEnvManager": "ms-python.python:system", - "python-envs.pythonProjects": [] + ] } diff --git a/python/packages/azure-ai/agent_framework_azure_ai/_chat_client.py b/python/packages/azure-ai/agent_framework_azure_ai/_chat_client.py index d85dc95111..a9c01fb066 100644 --- a/python/packages/azure-ai/agent_framework_azure_ai/_chat_client.py +++ b/python/packages/azure-ai/agent_framework_azure_ai/_chat_client.py @@ -118,6 +118,7 @@ def __init__( agents_client: AgentsClient | None = None, agent_id: str | None = None, agent_name: str | None = None, + agent_description: str | None = None, thread_id: str | None = None, project_endpoint: str | None = None, model_deployment_name: str | None = None, @@ -135,6 +136,7 @@ def __init__( a new agent will be created (and deleted after the request). If neither agents_client nor agent_id is provided, both will be created and managed automatically. agent_name: The name to use when creating new agents. + agent_description: The description 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 Project endpoint URL. @@ -215,6 +217,7 @@ def __init__( self.credential = async_credential self.agent_id = agent_id self.agent_name = agent_name + self.agent_description = agent_description self.model_id = azure_ai_settings.model_deployment_name self.thread_id = thread_id self.should_cleanup_agent = should_cleanup_agent # Track whether we should delete the agent @@ -311,6 +314,7 @@ async def _get_agent_id_or_create(self, run_options: dict[str, Any] | None = Non args: dict[str, Any] = { "model": run_options["model"], "name": agent_name, + "description": self.agent_description, } if "tools" in run_options: args["tools"] = run_options["tools"] @@ -1038,16 +1042,19 @@ def _convert_required_action_to_tool_output( return run_id, tool_outputs, tool_approvals - def _update_agent_name(self, agent_name: str | None) -> None: + def _update_agent_name_and_description(self, agent_name: str | None, description: str | None) -> None: """Update the agent name in the chat client. Args: agent_name: The new name for the agent. + description: The new description for the agent. """ # This is a no-op in the base class, but can be overridden by subclasses # to update the agent name in the client. if agent_name and not self.agent_name: self.agent_name = agent_name + if description and not self.agent_description: + self.agent_description = description def service_url(self) -> str: """Get the service URL for the chat client. diff --git a/python/packages/azure-ai/agent_framework_azure_ai/_client.py b/python/packages/azure-ai/agent_framework_azure_ai/_client.py index 67bd0bae6a..ad0e3ac961 100644 --- a/python/packages/azure-ai/agent_framework_azure_ai/_client.py +++ b/python/packages/azure-ai/agent_framework_azure_ai/_client.py @@ -62,6 +62,7 @@ def __init__( project_client: AIProjectClient | None = None, agent_name: str | None = None, agent_version: str | None = None, + agent_description: str | None = None, conversation_id: str | None = None, project_endpoint: str | None = None, model_deployment_name: str | None = None, @@ -77,6 +78,7 @@ def __init__( project_client: An existing AIProjectClient to use. If not provided, one will be created. agent_name: The name to use when creating new agents or using existing agents. agent_version: The version of the agent to use. + agent_description: The description to use when creating new agents. conversation_id: Default conversation ID to use for conversations. Can be overridden by conversation_id property when making a request. project_endpoint: The Azure AI Project endpoint URL. @@ -150,6 +152,7 @@ def __init__( # Initialize instance variables self.agent_name = agent_name self.agent_version = agent_version + self.agent_description = agent_description self.use_latest_version = use_latest_version self.project_client = project_client self.credential = async_credential @@ -280,7 +283,9 @@ async def _get_agent_reference_or_create( args["instructions"] = "".join(combined_instructions) created_agent = await self.project_client.agents.create_version( - agent_name=self.agent_name, definition=PromptAgentDefinition(**args) + agent_name=self.agent_name, + definition=PromptAgentDefinition(**args), + description=self.agent_description, ) self.agent_version = created_agent.version @@ -352,16 +357,19 @@ async def initialize_client(self) -> None: """Initialize OpenAI client.""" self.client = self.project_client.get_openai_client() # type: ignore - def _update_agent_name(self, agent_name: str | None) -> None: + def _update_agent_name_and_description(self, agent_name: str | None, description: str | None = None) -> None: """Update the agent name in the chat client. Args: agent_name: The new name for the agent. + description: The new description for the agent. """ # This is a no-op in the base class, but can be overridden by subclasses # to update the agent name in the client. if agent_name and not self.agent_name: self.agent_name = agent_name + if description and not self.agent_description: + self.agent_description = description def get_mcp_tool(self, tool: HostedMCPTool) -> Any: """Get MCP tool from HostedMCPTool.""" diff --git a/python/packages/azure-ai/tests/test_azure_ai_agent_client.py b/python/packages/azure-ai/tests/test_azure_ai_agent_client.py index d839eca376..53e941f9d2 100644 --- a/python/packages/azure-ai/tests/test_azure_ai_agent_client.py +++ b/python/packages/azure-ai/tests/test_azure_ai_agent_client.py @@ -441,32 +441,36 @@ async def test_azure_ai_chat_client_close_client_when_should_close_false(mock_ag mock_agents_client.close.assert_not_called() -def test_azure_ai_chat_client_update_agent_name_when_current_is_none(mock_agents_client: MagicMock) -> None: - """Test _update_agent_name updates name when current agent_name is None.""" +def test_azure_ai_chat_client_update_agent_name_and_description_when_current_is_none( + mock_agents_client: MagicMock, +) -> None: + """Test _update_agent_name_and_description updates name when current agent_name is None.""" chat_client = create_test_azure_ai_chat_client(mock_agents_client) chat_client.agent_name = None # type: ignore - chat_client._update_agent_name("NewAgentName") # type: ignore + chat_client._update_agent_name_and_description("NewAgentName") # type: ignore assert chat_client.agent_name == "NewAgentName" -def test_azure_ai_chat_client_update_agent_name_when_current_exists(mock_agents_client: MagicMock) -> None: - """Test _update_agent_name does not update when current agent_name exists.""" +def test_azure_ai_chat_client_update_agent_name_and_description_when_current_exists( + mock_agents_client: MagicMock, +) -> None: + """Test _update_agent_name_and_description does not update when current agent_name exists.""" chat_client = create_test_azure_ai_chat_client(mock_agents_client) chat_client.agent_name = "ExistingName" # type: ignore - chat_client._update_agent_name("NewAgentName") # type: ignore + chat_client._update_agent_name_and_description("NewAgentName") # type: ignore assert chat_client.agent_name == "ExistingName" -def test_azure_ai_chat_client_update_agent_name_with_none_input(mock_agents_client: MagicMock) -> None: - """Test _update_agent_name with None input.""" +def test_azure_ai_chat_client_update_agent_name_and_description_with_none_input(mock_agents_client: MagicMock) -> None: + """Test _update_agent_name_and_description with None input.""" chat_client = create_test_azure_ai_chat_client(mock_agents_client) chat_client.agent_name = None # type: ignore - chat_client._update_agent_name(None) # type: ignore + chat_client._update_agent_name_and_description(None) # type: ignore assert chat_client.agent_name is None diff --git a/python/packages/azure-ai/tests/test_azure_ai_client.py b/python/packages/azure-ai/tests/test_azure_ai_client.py index 8a31abcf35..94da022a4e 100644 --- a/python/packages/azure-ai/tests/test_azure_ai_client.py +++ b/python/packages/azure-ai/tests/test_azure_ai_client.py @@ -397,14 +397,14 @@ async def test_azure_ai_client_initialize_client(mock_project_client: MagicMock) mock_project_client.get_openai_client.assert_called_once() -def test_azure_ai_client_update_agent_name(mock_project_client: MagicMock) -> None: - """Test _update_agent_name method.""" +def test_azure_ai_client_update_agent_name_and_description(mock_project_client: MagicMock) -> None: + """Test _update_agent_name_and_description method.""" client = create_test_azure_ai_client(mock_project_client) # Test updating agent name when current is None - with patch.object(client, "_update_agent_name") as mock_update: + with patch.object(client, "_update_agent_name_and_description") as mock_update: mock_update.return_value = None - client._update_agent_name("new-agent") # type: ignore + client._update_agent_name_and_description("new-agent") # type: ignore mock_update.assert_called_once_with("new-agent") # Test behavior when agent name is updated @@ -412,9 +412,9 @@ def test_azure_ai_client_update_agent_name(mock_project_client: MagicMock) -> No client.agent_name = "test-agent" # Manually set for the test # Test with None input - with patch.object(client, "_update_agent_name") as mock_update: + with patch.object(client, "_update_agent_name_and_description") as mock_update: mock_update.return_value = None - client._update_agent_name(None) # type: ignore + client._update_agent_name_and_description(None) # type: ignore mock_update.assert_called_once_with(None) diff --git a/python/packages/core/agent_framework/_agents.py b/python/packages/core/agent_framework/_agents.py index b000d4d41d..1399a4c5f0 100644 --- a/python/packages/core/agent_framework/_agents.py +++ b/python/packages/core/agent_framework/_agents.py @@ -719,7 +719,7 @@ def __init__( additional_properties=additional_chat_options or {}, # type: ignore ) self._async_exit_stack = AsyncExitStack() - self._update_agent_name() + self._update_agent_name_and_description() async def __aenter__(self) -> "Self": """Enter the async context manager. @@ -755,15 +755,17 @@ async def __aexit__( """ await self._async_exit_stack.aclose() - def _update_agent_name(self) -> None: + def _update_agent_name_and_description(self) -> None: """Update the agent name in the chat client. Checks if the chat client supports agent name updates. The implementation should check if there is already an agent name defined, and if not set it to this value. """ - if hasattr(self.chat_client, "_update_agent_name") and callable(self.chat_client._update_agent_name): # type: ignore[reportAttributeAccessIssue, attr-defined] - self.chat_client._update_agent_name(self.name) # type: ignore[reportAttributeAccessIssue, attr-defined] + if hasattr(self.chat_client, "_update_agent_name_and_description") and callable( + self.chat_client._update_agent_name_and_description + ): # type: ignore[reportAttributeAccessIssue, attr-defined] + self.chat_client._update_agent_name_and_description(self.name, self.description) # type: ignore[reportAttributeAccessIssue, attr-defined] async def run( self, diff --git a/python/packages/core/agent_framework/openai/_assistants_client.py b/python/packages/core/agent_framework/openai/_assistants_client.py index 6255a6b8db..0f3bb3de63 100644 --- a/python/packages/core/agent_framework/openai/_assistants_client.py +++ b/python/packages/core/agent_framework/openai/_assistants_client.py @@ -64,6 +64,7 @@ def __init__( model_id: str | None = None, assistant_id: str | None = None, assistant_name: str | None = None, + assistant_description: str | None = None, thread_id: str | None = None, api_key: str | Callable[[], str | Awaitable[str]] | None = None, org_id: str | None = None, @@ -82,6 +83,7 @@ def __init__( assistant_id: The ID of an OpenAI assistant to use. If not provided, a new assistant will be created (and deleted after the request). assistant_name: The name to use when creating new assistants. + assistant_description: The description to use when creating new assistants. thread_id: Default thread ID to use for conversations. Can be overridden by conversation_id property when making a request. If not provided, a new thread will be created (and deleted after the request). @@ -147,6 +149,7 @@ def __init__( ) self.assistant_id: str | None = assistant_id self.assistant_name: str | None = assistant_name + self.assistant_description: str | None = assistant_description self.thread_id: str | None = thread_id self._should_delete_assistant: bool = False @@ -220,7 +223,11 @@ async def _get_assistant_id_or_create(self) -> str: raise ServiceInitializationError("Parameter 'model_id' is required for assistant creation.") client = await self.ensure_client() - created_assistant = await client.beta.assistants.create(name=self.assistant_name, model=self.model_id) + created_assistant = await client.beta.assistants.create( + model=self.model_id, + description=self.assistant_description, + name=self.assistant_name, + ) self.assistant_id = created_assistant.id self._should_delete_assistant = True @@ -516,13 +523,16 @@ def _convert_function_results_to_tool_output( return run_id, tool_outputs - def _update_agent_name(self, agent_name: str | None) -> None: + def _update_agent_name_and_description(self, agent_name: str | None, description: str | None = None) -> None: """Update the agent name in the chat client. Args: agent_name: The new name for the agent. + description: The new description for the agent. """ # This is a no-op in the base class, but can be overridden by subclasses # to update the agent name in the client. if agent_name and not self.assistant_name: - object.__setattr__(self, "assistant_name", agent_name) + self.assistant_name = agent_name + if description and not self.assistant_description: + self.assistant_description = description diff --git a/python/packages/core/tests/openai/test_openai_assistants_client.py b/python/packages/core/tests/openai/test_openai_assistants_client.py index 90947dd437..b9c32b14b5 100644 --- a/python/packages/core/tests/openai/test_openai_assistants_client.py +++ b/python/packages/core/tests/openai/test_openai_assistants_client.py @@ -872,36 +872,36 @@ def test_openai_assistants_client_convert_function_results_to_tool_output_mismat assert tool_outputs[0].get("tool_call_id") == "call-456" -def test_openai_assistants_client_update_agent_name(mock_async_openai: MagicMock) -> None: - """Test _update_agent_name method updates assistant_name when not already set.""" +def test_openai_assistants_client_update_agent_name_and_description(mock_async_openai: MagicMock) -> None: + """Test _update_agent_name_and_description method updates assistant_name when not already set.""" # Test updating agent name when assistant_name is None chat_client = create_test_openai_assistants_client(mock_async_openai, assistant_name=None) # Call the private method to update agent name - chat_client._update_agent_name("New Assistant Name") # type: ignore + chat_client._update_agent_name_and_description("New Assistant Name") # type: ignore assert chat_client.assistant_name == "New Assistant Name" -def test_openai_assistants_client_update_agent_name_existing(mock_async_openai: MagicMock) -> None: - """Test _update_agent_name method doesn't override existing assistant_name.""" +def test_openai_assistants_client_update_agent_name_and_description_existing(mock_async_openai: MagicMock) -> None: + """Test _update_agent_name_and_description method doesn't override existing assistant_name.""" # Test that existing assistant_name is not overridden chat_client = create_test_openai_assistants_client(mock_async_openai, assistant_name="Existing Assistant") # Call the private method to update agent name - chat_client._update_agent_name("New Assistant Name") # type: ignore + chat_client._update_agent_name_and_description("New Assistant Name") # type: ignore # Should keep the existing name assert chat_client.assistant_name == "Existing Assistant" -def test_openai_assistants_client_update_agent_name_none(mock_async_openai: MagicMock) -> None: - """Test _update_agent_name method with None agent_name parameter.""" +def test_openai_assistants_client_update_agent_name_and_description_none(mock_async_openai: MagicMock) -> None: + """Test _update_agent_name_and_description method with None agent_name parameter.""" # Test that None agent_name doesn't change anything chat_client = create_test_openai_assistants_client(mock_async_openai, assistant_name=None) # Call the private method with None - chat_client._update_agent_name(None) # type: ignore + chat_client._update_agent_name_and_description(None) # type: ignore # Should remain None assert chat_client.assistant_name is None diff --git a/python/samples/getting_started/declarative/inline_yaml.py b/python/samples/getting_started/declarative/inline_yaml.py index fb5683dea2..96a3bd7b18 100644 --- a/python/samples/getting_started/declarative/inline_yaml.py +++ b/python/samples/getting_started/declarative/inline_yaml.py @@ -23,6 +23,7 @@ async def main(): name: DiagnosticAgent displayName: Diagnostic Assistant instructions: Specialized diagnostic and issue detection agent for systems with critical error protocol and automatic handoff capabilities +description: A agent that performs diagnostics on systems and can escalate issues when critical errors are detected. model: id: =Env.AZURE_OPENAI_MODEL From 0ffc2bcc35933d5f780a5796e7016c183f0dcd86 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 3 Dec 2025 09:41:31 +0100 Subject: [PATCH 4/7] add azure assistants --- .../packages/core/agent_framework/azure/_assistants_client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/packages/core/agent_framework/azure/_assistants_client.py b/python/packages/core/agent_framework/azure/_assistants_client.py index f0b70066df..58d2dbe309 100644 --- a/python/packages/core/agent_framework/azure/_assistants_client.py +++ b/python/packages/core/agent_framework/azure/_assistants_client.py @@ -27,6 +27,7 @@ def __init__( deployment_name: str | None = None, assistant_id: str | None = None, assistant_name: str | None = None, + assistant_description: str | None = None, thread_id: str | None = None, api_key: str | None = None, endpoint: str | None = None, @@ -49,6 +50,7 @@ def __init__( assistant_id: The ID of an Azure OpenAI assistant to use. If not provided, a new assistant will be created (and deleted after the request). assistant_name: The name to use when creating new assistants. + assistant_description: The description to use when creating new assistants. thread_id: Default thread ID to use for conversations. Can be overridden by conversation_id property when making a request. If not provided, a new thread will be created (and deleted after the request). @@ -155,6 +157,7 @@ def __init__( model_id=azure_openai_settings.chat_deployment_name, assistant_id=assistant_id, assistant_name=assistant_name, + assistant_description=assistant_description, thread_id=thread_id, async_client=async_client, # type: ignore[reportArgumentType] default_headers=default_headers, From ae8f729ea3258189e79279b0b5485e2418c069eb Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 3 Dec 2025 09:47:36 +0100 Subject: [PATCH 5/7] fix tests --- .../azure-ai/tests/test_azure_ai_agent_client.py | 12 +++++++++--- .../packages/azure-ai/tests/test_azure_ai_client.py | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/python/packages/azure-ai/tests/test_azure_ai_agent_client.py b/python/packages/azure-ai/tests/test_azure_ai_agent_client.py index 53e941f9d2..98c9097072 100644 --- a/python/packages/azure-ai/tests/test_azure_ai_agent_client.py +++ b/python/packages/azure-ai/tests/test_azure_ai_agent_client.py @@ -86,6 +86,7 @@ def create_test_azure_ai_chat_client( client.credential = None client.agent_id = agent_id client.agent_name = agent_name + client.agent_description = None client.model_id = azure_ai_settings.model_deployment_name client.thread_id = thread_id client.should_cleanup_agent = should_cleanup_agent @@ -448,9 +449,10 @@ def test_azure_ai_chat_client_update_agent_name_and_description_when_current_is_ chat_client = create_test_azure_ai_chat_client(mock_agents_client) chat_client.agent_name = None # type: ignore - chat_client._update_agent_name_and_description("NewAgentName") # type: ignore + chat_client._update_agent_name_and_description("NewAgentName", "description") # type: ignore assert chat_client.agent_name == "NewAgentName" + assert chat_client.agent_description == "description" def test_azure_ai_chat_client_update_agent_name_and_description_when_current_exists( @@ -459,20 +461,24 @@ def test_azure_ai_chat_client_update_agent_name_and_description_when_current_exi """Test _update_agent_name_and_description does not update when current agent_name exists.""" chat_client = create_test_azure_ai_chat_client(mock_agents_client) chat_client.agent_name = "ExistingName" # type: ignore + chat_client.agent_description = "ExistingDescription" # type: ignore - chat_client._update_agent_name_and_description("NewAgentName") # type: ignore + chat_client._update_agent_name_and_description("NewAgentName", "description") # type: ignore assert chat_client.agent_name == "ExistingName" + assert chat_client.agent_description == "ExistingDescription" def test_azure_ai_chat_client_update_agent_name_and_description_with_none_input(mock_agents_client: MagicMock) -> None: """Test _update_agent_name_and_description with None input.""" chat_client = create_test_azure_ai_chat_client(mock_agents_client) chat_client.agent_name = None # type: ignore + chat_client.agent_description = None # type: ignore - chat_client._update_agent_name_and_description(None) # type: ignore + chat_client._update_agent_name_and_description(None, None) # type: ignore assert chat_client.agent_name is None + assert chat_client.agent_description is None async def test_azure_ai_chat_client_create_run_options_with_messages(mock_agents_client: MagicMock) -> None: diff --git a/python/packages/azure-ai/tests/test_azure_ai_client.py b/python/packages/azure-ai/tests/test_azure_ai_client.py index 94da022a4e..151ab62041 100644 --- a/python/packages/azure-ai/tests/test_azure_ai_client.py +++ b/python/packages/azure-ai/tests/test_azure_ai_client.py @@ -84,6 +84,7 @@ def create_test_azure_ai_client( client.credential = None client.agent_name = agent_name client.agent_version = agent_version + client.agent_description = None client.use_latest_version = use_latest_version client.model_id = azure_ai_settings.model_deployment_name client.conversation_id = conversation_id From 840d3d52ded9d07152f638e9ece904e032718064 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 3 Dec 2025 20:09:13 +0100 Subject: [PATCH 6/7] observabiltiy mypy fix --- python/packages/core/agent_framework/observability.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/packages/core/agent_framework/observability.py b/python/packages/core/agent_framework/observability.py index 1543b53251..2c419c1d9e 100644 --- a/python/packages/core/agent_framework/observability.py +++ b/python/packages/core/agent_framework/observability.py @@ -3,7 +3,7 @@ import contextlib import json import logging -from collections.abc import AsyncIterable, Awaitable, Callable, Generator, Mapping +from collections.abc import AsyncIterable, Awaitable, Callable, Generator, Mapping, MutableSequence from enum import Enum from functools import wraps from time import perf_counter, time_ns @@ -259,13 +259,13 @@ def __str__(self) -> str: # region Telemetry utils -def _get_otlp_exporters(endpoints: list[str]) -> list["LogExporter | SpanExporter | MetricExporter"]: +def _get_otlp_exporters(endpoints: list[str]) -> MutableSequence["LogExporter | SpanExporter | MetricExporter"]: """Create standard OTLP Exporters for the supplied endpoints.""" from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter - exporters: list["LogExporter | SpanExporter | MetricExporter"] = [] + exporters: MutableSequence["LogExporter | SpanExporter | MetricExporter"] = [] for endpoint in endpoints: exporters.append(OTLPLogExporter(endpoint=endpoint)) From 7f0b889a958c7faf4b36658a380addebb75f41b7 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 3 Dec 2025 20:16:15 +0100 Subject: [PATCH 7/7] for some reason mypy doesn't accept a subclass --- .../packages/core/agent_framework/observability.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/packages/core/agent_framework/observability.py b/python/packages/core/agent_framework/observability.py index 2c419c1d9e..fde0dc13f9 100644 --- a/python/packages/core/agent_framework/observability.py +++ b/python/packages/core/agent_framework/observability.py @@ -3,7 +3,7 @@ import contextlib import json import logging -from collections.abc import AsyncIterable, Awaitable, Callable, Generator, Mapping, MutableSequence +from collections.abc import AsyncIterable, Awaitable, Callable, Generator, Mapping from enum import Enum from functools import wraps from time import perf_counter, time_ns @@ -259,18 +259,18 @@ def __str__(self) -> str: # region Telemetry utils -def _get_otlp_exporters(endpoints: list[str]) -> MutableSequence["LogExporter | SpanExporter | MetricExporter"]: +def _get_otlp_exporters(endpoints: list[str]) -> list["LogExporter | SpanExporter | MetricExporter"]: """Create standard OTLP Exporters for the supplied endpoints.""" from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter - exporters: MutableSequence["LogExporter | SpanExporter | MetricExporter"] = [] + exporters: list["LogExporter | SpanExporter | MetricExporter"] = [] for endpoint in endpoints: - exporters.append(OTLPLogExporter(endpoint=endpoint)) - exporters.append(OTLPSpanExporter(endpoint=endpoint)) - exporters.append(OTLPMetricExporter(endpoint=endpoint)) + exporters.append(OTLPLogExporter(endpoint=endpoint)) # type: ignore[arg-type] + exporters.append(OTLPSpanExporter(endpoint=endpoint)) # type: ignore[arg-type] + exporters.append(OTLPMetricExporter(endpoint=endpoint)) # type: ignore[arg-type] return exporters