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
@@ -719,7 +719,7 @@ class ChatAgent(BaseAgent):
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 @@ class ChatAgent(BaseAgent):
"""
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,
@@ -27,6 +27,7 @@ class AzureOpenAIAssistantsClient(OpenAIAssistantsClient):
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 @@ class AzureOpenAIAssistantsClient(OpenAIAssistantsClient):
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 @@ class AzureOpenAIAssistantsClient(OpenAIAssistantsClient):
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,
@@ -268,9 +268,9 @@ def _get_otlp_exporters(endpoints: list[str]) -> list["LogRecordExporter | SpanE
exporters: list["LogRecordExporter | 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
@@ -64,6 +64,7 @@ class OpenAIAssistantsClient(OpenAIConfigMixin, BaseChatClient):
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 @@ class OpenAIAssistantsClient(OpenAIConfigMixin, BaseChatClient):
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 @@ class OpenAIAssistantsClient(OpenAIConfigMixin, BaseChatClient):
)
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 @@ class OpenAIAssistantsClient(OpenAIConfigMixin, BaseChatClient):
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 @@ class OpenAIAssistantsClient(OpenAIConfigMixin, BaseChatClient):
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
@@ -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