Python: added inline yaml sample (#2582)

* added inline yaml sample

* fixed some typos and added intro comment

* added description params and pass through to client

* add azure assistants

* fix tests

* observabiltiy mypy fix

* for some reason mypy doesn't accept a subclass

---------

Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
This commit is contained in:
Eduard van Valkenburg
2025-12-04 02:30:56 +01:00
committed by GitHub
Unverified
parent 42ffe59592
commit d774b64df0
12 changed files with 136 additions and 40 deletions
@@ -118,6 +118,7 @@ class AzureAIAgentClient(BaseChatClient):
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 @@ class AzureAIAgentClient(BaseChatClient):
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 @@ class AzureAIAgentClient(BaseChatClient):
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 @@ class AzureAIAgentClient(BaseChatClient):
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 @@ class AzureAIAgentClient(BaseChatClient):
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.
@@ -62,6 +62,7 @@ class AzureAIClient(OpenAIBaseResponsesClient):
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 @@ class AzureAIClient(OpenAIBaseResponsesClient):
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 @@ class AzureAIClient(OpenAIBaseResponsesClient):
# 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 @@ class AzureAIClient(OpenAIBaseResponsesClient):
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 @@ class AzureAIClient(OpenAIBaseResponsesClient):
"""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."""
@@ -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
@@ -441,34 +442,43 @@ 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", "description") # type: ignore
assert chat_client.agent_name == "NewAgentName"
assert chat_client.agent_description == "description"
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.agent_description = "ExistingDescription" # type: ignore
chat_client._update_agent_name("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_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.agent_description = None # type: ignore
chat_client._update_agent_name(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:
@@ -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
@@ -397,14 +398,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 +413,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)