From 4638e27fc05803fd5245af52a8180934c51c429b Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Fri, 7 Mar 2025 15:18:39 -0800 Subject: [PATCH 01/10] Update to version 1.0.0b8 --- sdk/ai/azure-ai-projects/CHANGELOG.md | 10 ++++++++++ sdk/ai/azure-ai-projects/azure/ai/projects/_version.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/sdk/ai/azure-ai-projects/CHANGELOG.md b/sdk/ai/azure-ai-projects/CHANGELOG.md index 1e1f545fe11e..f1e35b1f6c6c 100644 --- a/sdk/ai/azure-ai-projects/CHANGELOG.md +++ b/sdk/ai/azure-ai-projects/CHANGELOG.md @@ -1,5 +1,15 @@ # Release History +## 1.0.0b8 (Unreleased) + +### Features added + +### Sample updates + +### Bugs Fixed + +### Breaking Changes + ## 1.0.0b7 (2025-03-06) ### Features added diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/_version.py b/sdk/ai/azure-ai-projects/azure/ai/projects/_version.py index 84058978c521..ca67f288ad6b 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/_version.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/_version.py @@ -6,4 +6,4 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -VERSION = "1.0.0b7" +VERSION = "1.0.0b8" From 6f528bb8b114b10b02a82f638341600206107560 Mon Sep 17 00:00:00 2001 From: M-Hietala <78813398+M-Hietala@users.noreply.github.com> Date: Tue, 11 Mar 2025 17:09:04 -0500 Subject: [PATCH 02/10] =?UTF-8?q?fixing=20a=20bug=20with=20agent=20tracing?= =?UTF-8?q?=20causing=20event=20hander=20return=20values=20to=E2=80=A6=20(?= =?UTF-8?q?#40023)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fixing a bug with agent tracing causing event hander return values to no be returned * code checker tool related changes --------- Co-authored-by: Marko Hietala --- sdk/ai/azure-ai-projects/CHANGELOG.md | 2 + .../agents/_ai_agents_instrumentor.py | 52 ++++++++++--------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/sdk/ai/azure-ai-projects/CHANGELOG.md b/sdk/ai/azure-ai-projects/CHANGELOG.md index f1e35b1f6c6c..1e03aee1803f 100644 --- a/sdk/ai/azure-ai-projects/CHANGELOG.md +++ b/sdk/ai/azure-ai-projects/CHANGELOG.md @@ -8,6 +8,8 @@ ### Bugs Fixed +* Fix for a bug in agent tracing causing event handler return values to not be returned when tracing is enabled. + ### Breaking Changes ## 1.0.0b7 (2025-03-06) diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py index 638b7e45b120..1bdcbb6c1f5a 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py @@ -1648,25 +1648,26 @@ def __init__( self.last_message: Optional[ThreadMessage] = None self.instrumentor = instrumentor - def on_message_delta(self, delta: "MessageDeltaChunk") -> None: + # pylint: disable=R1710 + def on_message_delta(self, delta: "MessageDeltaChunk") -> None: # type: ignore[func-returns-value] if self.inner_handler: - self.inner_handler.on_message_delta(delta) + return self.inner_handler.on_message_delta(delta) # type: ignore - def on_thread_message(self, message: "ThreadMessage") -> None: + def on_thread_message(self, message: "ThreadMessage") -> None: # type: ignore[func-returns-value] if self.inner_handler: - self.inner_handler.on_thread_message(message) + return self.inner_handler.on_thread_message(message) # type: ignore if message.status in {"completed", "incomplete"}: self.last_message = message - def on_thread_run(self, run: "ThreadRun") -> None: + def on_thread_run(self, run: "ThreadRun") -> None: # type: ignore[func-returns-value] if self.inner_handler: - self.inner_handler.on_thread_run(run) + return self.inner_handler.on_thread_run(run) # type: ignore self.last_run = run - def on_run_step(self, step: "RunStep") -> None: + def on_run_step(self, step: "RunStep") -> None: # type: ignore[func-returns-value] if self.inner_handler: - self.inner_handler.on_run_step(step) + return self.inner_handler.on_run_step(step) # type: ignore if step.status == RunStepStatus.IN_PROGRESS: return @@ -1680,22 +1681,23 @@ def on_run_step(self, step: "RunStep") -> None: self.instrumentor.add_thread_message_event(self.span, cast(ThreadMessage, self.last_message), step.usage) self.last_message = None - def on_run_step_delta(self, delta: "RunStepDeltaChunk") -> None: + def on_run_step_delta(self, delta: "RunStepDeltaChunk") -> None: # type: ignore[func-returns-value] if self.inner_handler: - self.inner_handler.on_run_step_delta(delta) + return self.inner_handler.on_run_step_delta(delta) # type: ignore - def on_error(self, data: str) -> None: + def on_error(self, data: str) -> None: # type: ignore[func-returns-value] if self.inner_handler: - self.inner_handler.on_error(data) + return self.inner_handler.on_error(data) # type: ignore - def on_done(self) -> None: + def on_done(self) -> None: # type: ignore[func-returns-value] if self.inner_handler: - self.inner_handler.on_done() + return self.inner_handler.on_done() # type: ignore # it could be called multiple tines (for each step) __exit__ - def on_unhandled_event(self, event_type: str, event_data: Any) -> None: + def on_unhandled_event(self, event_type: str, event_data: Any) -> None: # type: ignore[func-returns-value] if self.inner_handler: - self.inner_handler.on_unhandled_event(event_type, event_data) + return self.inner_handler.on_unhandled_event(event_type, event_data) # type: ignore + # pylint: enable=R1710 def __exit__(self, exc_type, exc_val, exc_tb): if not self.ended: @@ -1728,25 +1730,26 @@ def __init__( self.last_message: Optional[ThreadMessage] = None self.instrumentor = instrumentor + # pylint: disable=R1710 async def on_message_delta(self, delta: "MessageDeltaChunk") -> None: # type: ignore[func-returns-value] if self.inner_handler: - await self.inner_handler.on_message_delta(delta) + return await self.inner_handler.on_message_delta(delta) # type: ignore async def on_thread_message(self, message: "ThreadMessage") -> None: # type: ignore[func-returns-value] if self.inner_handler: - await self.inner_handler.on_thread_message(message) + return await self.inner_handler.on_thread_message(message) # type: ignore if message.status in {"completed", "incomplete"}: self.last_message = message async def on_thread_run(self, run: "ThreadRun") -> None: # type: ignore[func-returns-value] if self.inner_handler: - await self.inner_handler.on_thread_run(run) + return await self.inner_handler.on_thread_run(run) # type: ignore self.last_run = run async def on_run_step(self, step: "RunStep") -> None: # type: ignore[func-returns-value] if self.inner_handler: - await self.inner_handler.on_run_step(step) + return await self.inner_handler.on_run_step(step) # type: ignore if step.status == RunStepStatus.IN_PROGRESS: return @@ -1762,20 +1765,21 @@ async def on_run_step(self, step: "RunStep") -> None: # type: ignore[func-retur async def on_run_step_delta(self, delta: "RunStepDeltaChunk") -> None: # type: ignore[func-returns-value] if self.inner_handler: - await self.inner_handler.on_run_step_delta(delta) + return await self.inner_handler.on_run_step_delta(delta) # type: ignore async def on_error(self, data: str) -> None: # type: ignore[func-returns-value] if self.inner_handler: - await self.inner_handler.on_error(data) + return await self.inner_handler.on_error(data) # type: ignore async def on_done(self) -> None: # type: ignore[func-returns-value] if self.inner_handler: - await self.inner_handler.on_done() + return await self.inner_handler.on_done() # type: ignore # it could be called multiple tines (for each step) __exit__ async def on_unhandled_event(self, event_type: str, event_data: Any) -> None: # type: ignore[func-returns-value] if self.inner_handler: - await self.inner_handler.on_unhandled_event(event_type, event_data) + return await self.inner_handler.on_unhandled_event(event_type, event_data) # type: ignore + # pylint: enable=R1710 def __aexit__(self, exc_type, exc_val, exc_tb): if not self.ended: From 413048bce7774045796708fea4ef9df779d66a8d Mon Sep 17 00:00:00 2001 From: Nikolay Rovinskiy <30440255+nick863@users.noreply.github.com> Date: Fri, 14 Mar 2025 10:23:11 -0700 Subject: [PATCH 03/10] Fix AI Search sample (#40075) * Show the reference in sample * Fix * Fixes * Fix --- sdk/ai/azure-ai-projects/README.md | 30 ++++++++++++++++++- .../agents/_ai_agents_instrumentor.py | 2 ++ ...basics_async_with_azure_monitor_tracing.py | 5 +++- .../agents/sample_agents_azure_ai_search.py | 26 ++++++++++++---- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/sdk/ai/azure-ai-projects/README.md b/sdk/ai/azure-ai-projects/README.md index 6881da1db03e..700046ddc095 100644 --- a/sdk/ai/azure-ai-projects/README.md +++ b/sdk/ai/azure-ai-projects/README.md @@ -465,7 +465,7 @@ for conn in conn_list: print(conn_id) # Initialize agent AI search tool and add the search index connection id -ai_search = AzureAISearchTool(index_connection_id=conn_id, index_name="myindexname") +ai_search = AzureAISearchTool(index_connection_id=conn_id, index_name="sample_index") # Create agent with AI search tool and process assistant run with project_client: @@ -480,6 +480,34 @@ with project_client: +If the agent has found the relevant information in the index, the reference +and annotation will be provided in the message response. In the example above, we replace +the reference placeholder by the actual reference and url. Please note, that to +get sensible result, the index needs to have fields "title" and "url". + + + +```python +# Fetch and log all messages +messages = project_client.agents.list_messages(thread_id=thread.id, order=ListSortOrder.ASCENDING) +for message in messages.data: + if message.role == MessageRole.AGENT and message.url_citation_annotations: + placeholder_annotations = { + annotation.text: f" [see {annotation.url_citation.title}] ({annotation.url_citation.url})" + for annotation in message.url_citation_annotations + } + for message_text in message.text_messages: + message_str = message_text.text.value + for k, v in placeholder_annotations.items(): + message_str = message_str.replace(k, v) + print(f"{message.role}: {message_str}") + else: + for message_text in message.text_messages: + print(f"{message.role}: {message_text.text.value}") +``` + + + #### Create Agent with Function Call You can enhance your Agents by defining callback functions as function tools. These can be provided to `create_agent` via either the `toolset` parameter or the combination of `tools` and `tool_resources`. Here are the distinctions: diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py index 1bdcbb6c1f5a..3db2479957e6 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py @@ -1697,6 +1697,7 @@ def on_done(self) -> None: # type: ignore[func-returns-value] def on_unhandled_event(self, event_type: str, event_data: Any) -> None: # type: ignore[func-returns-value] if self.inner_handler: return self.inner_handler.on_unhandled_event(event_type, event_data) # type: ignore + # pylint: enable=R1710 def __exit__(self, exc_type, exc_val, exc_tb): @@ -1779,6 +1780,7 @@ async def on_done(self) -> None: # type: ignore[func-returns-value] async def on_unhandled_event(self, event_type: str, event_data: Any) -> None: # type: ignore[func-returns-value] if self.inner_handler: return await self.inner_handler.on_unhandled_event(event_type, event_data) # type: ignore + # pylint: enable=R1710 def __aexit__(self, exc_type, exc_val, exc_tb): diff --git a/sdk/ai/azure-ai-projects/samples/agents/async_samples/sample_agents_basics_async_with_azure_monitor_tracing.py b/sdk/ai/azure-ai-projects/samples/agents/async_samples/sample_agents_basics_async_with_azure_monitor_tracing.py index 5247d6a7cf46..1038d6882c2e 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/async_samples/sample_agents_basics_async_with_azure_monitor_tracing.py +++ b/sdk/ai/azure-ai-projects/samples/agents/async_samples/sample_agents_basics_async_with_azure_monitor_tracing.py @@ -32,6 +32,7 @@ scenario = os.path.basename(__file__) tracer = trace.get_tracer(__name__) + async def main() -> None: async with DefaultAzureCredential() as creds: @@ -51,7 +52,9 @@ async def main() -> None: async with project_client: project_client.telemetry.enable() agent = await project_client.agents.create_agent( - model=os.environ["MODEL_DEPLOYMENT_NAME"], name="my-assistant", instructions="You are helpful assistant" + model=os.environ["MODEL_DEPLOYMENT_NAME"], + name="my-assistant", + instructions="You are helpful assistant", ) print(f"Created agent, agent ID: {agent.id}") diff --git a/sdk/ai/azure-ai-projects/samples/agents/sample_agents_azure_ai_search.py b/sdk/ai/azure-ai-projects/samples/agents/sample_agents_azure_ai_search.py index f18757dce744..187708df3031 100644 --- a/sdk/ai/azure-ai-projects/samples/agents/sample_agents_azure_ai_search.py +++ b/sdk/ai/azure-ai-projects/samples/agents/sample_agents_azure_ai_search.py @@ -4,6 +4,7 @@ # Licensed under the MIT License. # ------------------------------------ + """ DESCRIPTION: This sample demonstrates how to use agent operations with the @@ -34,7 +35,7 @@ import os from azure.ai.projects import AIProjectClient from azure.identity import DefaultAzureCredential -from azure.ai.projects.models import AzureAISearchTool, ConnectionType +from azure.ai.projects.models import AzureAISearchTool, ConnectionType, ListSortOrder, MessageRole project_client = AIProjectClient.from_connection_string( credential=DefaultAzureCredential(), @@ -52,7 +53,7 @@ print(conn_id) # Initialize agent AI search tool and add the search index connection id -ai_search = AzureAISearchTool(index_connection_id=conn_id, index_name="myindexname") +ai_search = AzureAISearchTool(index_connection_id=conn_id, index_name="sample_index") # Create agent with AI search tool and process assistant run with project_client: @@ -74,7 +75,7 @@ message = project_client.agents.create_message( thread_id=thread.id, role="user", - content="What inventory is available currently?", + content="What is the temperature rating of the cozynights sleeping bag?", ) print(f"Created message, ID: {message.id}") @@ -108,6 +109,21 @@ project_client.agents.delete_agent(agent.id) print("Deleted agent") + # [START populate_references_agent_with_azure_ai_search_tool] # Fetch and log all messages - messages = project_client.agents.list_messages(thread_id=thread.id) - print(f"Messages: {messages}") + messages = project_client.agents.list_messages(thread_id=thread.id, order=ListSortOrder.ASCENDING) + for message in messages.data: + if message.role == MessageRole.AGENT and message.url_citation_annotations: + placeholder_annotations = { + annotation.text: f" [see {annotation.url_citation.title}] ({annotation.url_citation.url})" + for annotation in message.url_citation_annotations + } + for message_text in message.text_messages: + message_str = message_text.text.value + for k, v in placeholder_annotations.items(): + message_str = message_str.replace(k, v) + print(f"{message.role}: {message_str}") + else: + for message_text in message.text_messages: + print(f"{message.role}: {message_text.text.value}") + # [END populate_references_agent_with_azure_ai_search_tool] From c109f34a77c13e12ac74fdc3bce374d5aef15400 Mon Sep 17 00:00:00 2001 From: Nikolay Rovinskiy <30440255+nick863@users.noreply.github.com> Date: Mon, 17 Mar 2025 18:14:09 -0700 Subject: [PATCH 04/10] Regenerate python code to use extensible connection types (#40097) * Regeerate python code to use extensible connection types * Regenerate from upstream --- .../azure/ai/projects/_serialization.py | 2 +- .../ai/projects/aio/operations/_operations.py | 4 +- .../azure/ai/projects/models/__init__.py | 8 + .../azure/ai/projects/models/_enums.py | 6 + .../azure/ai/projects/models/_models.py | 470 +++++++++--------- .../ai/projects/operations/_operations.py | 16 +- sdk/ai/azure-ai-projects/tsp-location.yaml | 2 +- 7 files changed, 251 insertions(+), 257 deletions(-) diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/_serialization.py b/sdk/ai/azure-ai-projects/azure/ai/projects/_serialization.py index e2a20b1d534c..7a0232de5ddc 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/_serialization.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/_serialization.py @@ -411,7 +411,7 @@ def from_dict( :param function key_extractors: A key extractor function. :param str content_type: JSON by default, set application/xml if XML. :returns: An instance of this model - :raises: DeserializationError if something went wrong + :raises DeserializationError: if something went wrong :rtype: Self """ deserializer = Deserializer(cls._infer_class_models()) diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py index a5a59c511450..fc32e1aeaf02 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py @@ -5144,8 +5144,8 @@ async def _list_connections( """List the details of all the connections (not including their credentials). :keyword category: Category of the workspace connection. Known values are: "AzureOpenAI", - "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", and "API Key". Default value is - None. + "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", "ApiKey", "CustomKeys", and + "CognitiveService". Default value is None. :paramtype category: str or ~azure.ai.projects.models.ConnectionType :keyword include_all: Indicates whether to list datastores. Service default: do not list datastores. Default value is None. diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py b/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py index 4217e52a3cc0..1cd932660223 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py @@ -14,6 +14,7 @@ from ._models import ( # type: ignore + AOAIModelConfig, Agent, AgentDeletionStatus, AgentThread, @@ -34,6 +35,7 @@ Dataset, Evaluation, EvaluationSchedule, + EvaluationTarget, EvaluatorConfiguration, FileDeletionStatus, FileListResponse, @@ -48,6 +50,7 @@ IncompleteRunDetails, IndexResource, InputData, + MAASModelConfig, MessageAttachment, MessageContent, MessageDelta, @@ -145,6 +148,7 @@ SubmitToolOutputsAction, SubmitToolOutputsDetails, SystemData, + TargetModelConfig, ThreadDeletionStatus, ThreadMessage, ThreadMessageOptions, @@ -224,6 +228,7 @@ from ._patch import patch_sdk as _patch_sdk __all__ = [ + "AOAIModelConfig", "Agent", "AgentDeletionStatus", "AgentThread", @@ -244,6 +249,7 @@ "Dataset", "Evaluation", "EvaluationSchedule", + "EvaluationTarget", "EvaluatorConfiguration", "FileDeletionStatus", "FileListResponse", @@ -258,6 +264,7 @@ "IncompleteRunDetails", "IndexResource", "InputData", + "MAASModelConfig", "MessageAttachment", "MessageContent", "MessageDelta", @@ -355,6 +362,7 @@ "SubmitToolOutputsAction", "SubmitToolOutputsDetails", "SystemData", + "TargetModelConfig", "ThreadDeletionStatus", "ThreadMessage", "ThreadMessageOptions", diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py index dd8e03f3700f..0f29c95308f5 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py @@ -134,6 +134,8 @@ class AuthenticationType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Entra ID authentication (formerly known as AAD)""" SAS = "SAS" """Shared Access Signature (SAS) authentication""" + CUSTOM = "CustomKeys" + """Custom authentication""" NONE = "None" """No authentication""" @@ -153,6 +155,10 @@ class ConnectionType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Azure AI Search""" API_KEY = "ApiKey" """Generic connection that uses API Key authentication""" + CUSTOM = "CustomKeys" + """Generic connection that uses Custom authentication""" + COGNITIVE_SERVICE = "CognitiveService" + """Cognitive Service""" class DoneEvent(str, Enum, metaclass=CaseInsensitiveEnumMeta): diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py index 84eccaed8568..2ca2b18e0601 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py @@ -28,9 +28,6 @@ class Agent(_model_base.Model): """Represents an agent that can call the model and use tools. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The identifier, which can be referenced in API endpoints. Required. :vartype id: str :ivar object: The object type, which is always assistant. Required. Default value is @@ -153,9 +150,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class AgentDeletionStatus(_model_base.Model): """The status of an agent deletion operation. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The ID of the resource specified for deletion. Required. :vartype id: str :ivar deleted: A value indicating whether deletion was successful. Required. @@ -229,7 +223,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class AgentsNamedToolChoice(_model_base.Model): """Specifies a tool the model should use. Use to force the model to call a specific tool. - :ivar type: the type of tool. If type is ``function``\\ , the function name must be set. Required. Known values are: "function", "code_interpreter", "file_search", "bing_grounding", "fabric_aiskill", "sharepoint_grounding", and "azure_ai_search". @@ -241,7 +234,7 @@ class AgentsNamedToolChoice(_model_base.Model): type: Union[str, "_models.AgentsNamedToolChoiceType"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) - """the type of tool. If type is ``function``, the function name must be set. Required. Known + """the type of tool. If type is ``function``\ , the function name must be set. Required. Known values are: \"function\", \"code_interpreter\", \"file_search\", \"bing_grounding\", \"fabric_aiskill\", \"sharepoint_grounding\", and \"azure_ai_search\".""" function: Optional["_models.FunctionName"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -269,9 +262,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class AgentThread(_model_base.Model): """Information about a single thread associated with an agent. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The identifier, which can be referenced in API endpoints. Required. :vartype id: str :ivar object: The object type, which is always 'thread'. Required. Default value is "thread". @@ -386,10 +376,86 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class TargetModelConfig(_model_base.Model): + """Abstract class for model configuration. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + AOAIModelConfig, MAASModelConfig + + :ivar type: Type of the model configuration. Required. Default value is None. + :vartype type: str + """ + + __mapping__: Dict[str, _model_base.Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """Type of the model configuration. Required. Default value is None.""" + + @overload + def __init__( + self, + *, + type: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class AOAIModelConfig(TargetModelConfig, discriminator="AOAI"): + """Azure OpenAI model configuration. The API version would be selected by the service for querying + the model. + + :ivar type: Required. Default value is "AOAI". + :vartype type: str + :ivar azure_endpoint: Endpoint URL for AOAI model. Required. + :vartype azure_endpoint: str + :ivar api_key: API Key for AOAI model. Required. + :vartype api_key: str + :ivar azure_deployment: Deployment name for AOAI model. Required. + :vartype azure_deployment: str + """ + + type: Literal["AOAI"] = rest_discriminator(name="type", visibility=["read"]) # type: ignore + """Required. Default value is \"AOAI\".""" + azure_endpoint: str = rest_field(name="azureEndpoint", visibility=["read", "create", "update", "delete", "query"]) + """Endpoint URL for AOAI model. Required.""" + api_key: str = rest_field(name="apiKey", visibility=["read", "create", "update", "delete", "query"]) + """API Key for AOAI model. Required.""" + azure_deployment: str = rest_field( + name="azureDeployment", visibility=["read", "create", "update", "delete", "query"] + ) + """Deployment name for AOAI model. Required.""" + + @overload + def __init__( + self, + *, + azure_endpoint: str, + api_key: str, + azure_deployment: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, type="AOAI", **kwargs) + + class AppInsightsProperties(_model_base.Model): """The properties of the Application Insights resource. - :ivar connection_string: Authentication type of the connection target. Required. :vartype connection_string: str """ @@ -423,7 +489,6 @@ class InputData(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: ApplicationInsightsConfiguration, Dataset - :ivar type: Type of the data. Required. Default value is None. :vartype type: str """ @@ -453,9 +518,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class ApplicationInsightsConfiguration(InputData, discriminator="app_insights"): """Data Source for Application Insights. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar type: Required. Default value is "app_insights". :vartype type: str :ivar resource_id: LogAnalytic Workspace resourceID associated with ApplicationInsights. @@ -545,7 +607,6 @@ class ToolDefinition(_model_base.Model): CodeInterpreterToolDefinition, MicrosoftFabricToolDefinition, FileSearchToolDefinition, FunctionToolDefinition, OpenApiToolDefinition, SharepointToolDefinition - :ivar type: The object type. Required. Default value is None. :vartype type: str """ @@ -575,7 +636,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class AzureAISearchToolDefinition(ToolDefinition, discriminator="azure_ai_search"): """The input definition information for an Azure AI search tool as used to configure an agent. - :ivar type: The object type, which is always 'azure_ai_search'. Required. Default value is "azure_ai_search". :vartype type: str @@ -604,9 +664,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class AzureFunctionBinding(_model_base.Model): """The structure for keeping storage queue name and URI. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar type: The type of binding, which is always 'storage_queue'. Required. Default value is "storage_queue". :vartype type: str @@ -644,7 +701,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class AzureFunctionDefinition(_model_base.Model): """The definition of Azure function. - :ivar function: The definition of azure function and its parameters. Required. :vartype function: ~azure.ai.projects.models.FunctionDefinition :ivar input_binding: Input storage queue. The queue storage trigger runs a function as messages @@ -691,7 +747,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class AzureFunctionStorageQueue(_model_base.Model): """The structure for keeping storage queue name and URI. - :ivar storage_service_endpoint: URI to the Azure Storage Queue service allowing you to manipulate a queue. Required. :vartype storage_service_endpoint: str @@ -728,7 +783,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class AzureFunctionToolDefinition(ToolDefinition, discriminator="azure_function"): """The input definition information for a azure function tool as used to configure an agent. - :ivar type: The object type, which is always 'azure_function'. Required. Default value is "azure_function". :vartype type: str @@ -767,7 +821,6 @@ class BingGroundingToolDefinition(ToolDefinition, discriminator="bing_grounding" """The input definition information for a bing grounding search tool as used to configure an agent. - :ivar type: The object type, which is always 'bing_grounding'. Required. Default value is "bing_grounding". :vartype type: str @@ -804,7 +857,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class CodeInterpreterToolDefinition(ToolDefinition, discriminator="code_interpreter"): """The input definition information for a code interpreter tool as used to configure an agent. - :ivar type: The object type, which is always 'code_interpreter'. Required. Default value is "code_interpreter". :vartype type: str @@ -873,7 +925,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class CredentialsApiKeyAuth(_model_base.Model): """The credentials needed for API key authentication. - :ivar key: The API key. Required. :vartype key: str """ @@ -902,7 +953,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class CredentialsSASAuth(_model_base.Model): """The credentials needed for Shared Access Signatures (SAS) authentication. - :ivar sas: The Shared Access Signatures (SAS) token. Required. :vartype sas: str """ @@ -934,7 +984,6 @@ class Trigger(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: CronTrigger, RecurrenceTrigger - :ivar type: Type of the trigger. Required. Default value is None. :vartype type: str """ @@ -964,9 +1013,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class CronTrigger(Trigger, discriminator="Cron"): """Cron Trigger Definition. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar type: Required. Default value is "Cron". :vartype type: str :ivar expression: Cron expression for the trigger. Required. @@ -999,9 +1045,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class Dataset(InputData, discriminator="dataset"): """Dataset as source for evaluation. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar type: Required. Default value is "dataset". :vartype type: str :ivar id: Evaluation input data. Required. @@ -1034,13 +1077,12 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class Evaluation(_model_base.Model): """Evaluation Definition. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: Identifier of the evaluation. Required. :vartype id: str :ivar data: Data for evaluation. Required. :vartype data: ~azure.ai.projects.models.InputData + :ivar target: Evaluation target specifying the model config and parameters. + :vartype target: ~azure.ai.projects.models.EvaluationTarget :ivar display_name: Display Name for evaluation. It helps to find the evaluation easily in AI Foundry. It does not need to be unique. :vartype display_name: str @@ -1064,6 +1106,8 @@ class Evaluation(_model_base.Model): """Identifier of the evaluation. Required.""" data: "_models.InputData" = rest_field(visibility=["read", "create"]) """Data for evaluation. Required.""" + target: Optional["_models.EvaluationTarget"] = rest_field(visibility=["read", "create"]) + """Evaluation target specifying the model config and parameters.""" display_name: Optional[str] = rest_field( name="displayName", visibility=["read", "create", "update", "delete", "query"] ) @@ -1090,6 +1134,7 @@ def __init__( *, data: "_models.InputData", evaluators: Dict[str, "_models.EvaluatorConfiguration"], + target: Optional["_models.EvaluationTarget"] = None, display_name: Optional[str] = None, description: Optional[str] = None, tags: Optional[Dict[str, str]] = None, @@ -1110,9 +1155,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class EvaluationSchedule(_model_base.Model): """Evaluation Schedule Definition. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar name: Name of the schedule, which also serves as the unique identifier for the evaluation. Required. :vartype name: str @@ -1185,10 +1227,51 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class EvaluationTarget(_model_base.Model): + """Target for the evaluation process. + + :ivar system_message: System message related to the evaluation target. Required. + :vartype system_message: str + :ivar model_config: Model configuration for the evaluation. Required. + :vartype model_config: ~azure.ai.projects.models.TargetModelConfig + :ivar model_params: A dictionary of parameters for the model. + :vartype model_params: dict[str, any] + """ + + system_message: str = rest_field(name="systemMessage", visibility=["read", "create", "update", "delete", "query"]) + """System message related to the evaluation target. Required.""" + model_config: "_models.TargetModelConfig" = rest_field( + name="modelConfig", visibility=["read", "create", "update", "delete", "query"] + ) + """Model configuration for the evaluation. Required.""" + model_params: Optional[Dict[str, Any]] = rest_field( + name="modelParams", visibility=["read", "create", "update", "delete", "query"] + ) + """A dictionary of parameters for the model.""" + + @overload + def __init__( + self, + *, + system_message: str, + model_config: "_models.TargetModelConfig", + model_params: Optional[Dict[str, Any]] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + class EvaluatorConfiguration(_model_base.Model): """Evaluator Configuration. - :ivar id: Identifier of the evaluator. Required. :vartype id: str :ivar init_params: Initialization parameters of the evaluator. @@ -1231,9 +1314,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class FileDeletionStatus(_model_base.Model): """A status response from a file deletion operation. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The ID of the resource specified for deletion. Required. :vartype id: str :ivar deleted: A value indicating whether deletion was successful. Required. @@ -1272,9 +1352,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class FileListResponse(_model_base.Model): """The response data from a file list operation. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar object: The object type, which is always 'list'. Required. Default value is "list". :vartype object: str :ivar data: The files returned for the request. Required. @@ -1308,7 +1385,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class FileSearchRankingOptions(_model_base.Model): """Ranking options for file search. - :ivar ranker: File search ranker. Required. :vartype ranker: str :ivar score_threshold: Ranker search threshold. Required. @@ -1342,9 +1418,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class FileSearchToolCallContent(_model_base.Model): """The file search result content object. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar type: The type of the content. Required. Default value is "text". :vartype type: str :ivar text: The text content of the file. Required. @@ -1378,7 +1451,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class FileSearchToolDefinition(ToolDefinition, discriminator="file_search"): """The input definition information for a file search tool as used to configure an agent. - :ivar type: The object type, which is always 'file_search'. Required. Default value is "file_search". :vartype type: str @@ -1500,7 +1572,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class FunctionDefinition(_model_base.Model): """The input definition information for a function. - :ivar name: The name of the function to be called. Required. :vartype name: str :ivar description: A description of what the function does, used by the model to choose when @@ -1542,7 +1613,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class FunctionName(_model_base.Model): """The function name that will be used, if using the ``function`` tool. - :ivar name: The name of the function to call. Required. :vartype name: str """ @@ -1571,7 +1641,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class FunctionToolDefinition(ToolDefinition, discriminator="function"): """The input definition information for a function tool as used to configure an agent. - :ivar type: The object type, which is always 'function'. Required. Default value is "function". :vartype type: str :ivar function: The definition of the concrete function that the function tool should call. @@ -1605,7 +1674,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class GetAppInsightsResponse(_model_base.Model): """Response from getting properties of the Application Insights resource. - :ivar id: A unique identifier for the resource. Required. :vartype id: str :ivar name: The name of the resource. Required. @@ -1646,7 +1714,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class GetConnectionResponse(_model_base.Model): """Response from the listSecrets operation. - :ivar id: A unique identifier for the connection. Required. :vartype id: str :ivar name: The name of the resource. Required. @@ -1687,7 +1754,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class GetWorkspaceResponse(_model_base.Model): """Response from the Workspace - Get operation. - :ivar id: A unique identifier for the resource. Required. :vartype id: str :ivar name: The name of the resource. Required. @@ -1728,7 +1794,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class IncompleteRunDetails(_model_base.Model): """Details on why the run is incomplete. Will be ``null`` if the run is not incomplete. - :ivar reason: The reason why the run is incomplete. This indicates which specific token limit was reached during the run. Required. Known values are: "max_completion_tokens" and "max_prompt_tokens". @@ -1763,7 +1828,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class IndexResource(_model_base.Model): """A Index resource. - :ivar index_connection_id: An index connection id in an IndexResource attached to this agent. Required. :vartype index_connection_id: str @@ -1800,14 +1864,15 @@ class InternalConnectionProperties(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: InternalConnectionPropertiesAADAuth, InternalConnectionPropertiesApiKeyAuth, - InternalConnectionPropertiesNoAuth, InternalConnectionPropertiesSASAuth - + InternalConnectionPropertiesCustomAuth, InternalConnectionPropertiesNoAuth, + InternalConnectionPropertiesSASAuth :ivar auth_type: Authentication type of the connection target. Required. Known values are: - "ApiKey", "AAD", "SAS", and "None". + "ApiKey", "AAD", "SAS", "CustomKeys", and "None". :vartype auth_type: str or ~azure.ai.projects.models.AuthenticationType :ivar category: Category of the connection. Required. Known values are: "AzureOpenAI", - "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", and "API Key". + "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", "ApiKey", "CustomKeys", and + "CognitiveService". :vartype category: str or ~azure.ai.projects.models.ConnectionType :ivar target: The connection URL to be used for this service. Required. :vartype target: str @@ -1816,12 +1881,13 @@ class InternalConnectionProperties(_model_base.Model): __mapping__: Dict[str, _model_base.Model] = {} auth_type: str = rest_discriminator(name="authType", visibility=["read", "create", "update", "delete", "query"]) """Authentication type of the connection target. Required. Known values are: \"ApiKey\", \"AAD\", - \"SAS\", and \"None\".""" + \"SAS\", \"CustomKeys\", and \"None\".""" category: Union[str, "_models.ConnectionType"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) """Category of the connection. Required. Known values are: \"AzureOpenAI\", \"Serverless\", - \"AzureBlob\", \"AIServices\", \"CognitiveSearch\", and \"API Key\".""" + \"AzureBlob\", \"AIServices\", \"CognitiveSearch\", \"ApiKey\", \"CustomKeys\", and + \"CognitiveService\".""" target: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The connection URL to be used for this service. Required.""" @@ -1849,9 +1915,9 @@ class InternalConnectionPropertiesAADAuth(InternalConnectionProperties, discrimi """Connection properties for connections with AAD authentication (aka ``Entra ID passthrough``\\ ). - :ivar category: Category of the connection. Required. Known values are: "AzureOpenAI", - "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", and "API Key". + "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", "ApiKey", "CustomKeys", and + "CognitiveService". :vartype category: str or ~azure.ai.projects.models.ConnectionType :ivar target: The connection URL to be used for this service. Required. :vartype target: str @@ -1886,9 +1952,9 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class InternalConnectionPropertiesApiKeyAuth(InternalConnectionProperties, discriminator="ApiKey"): """Connection properties for connections with API key authentication. - :ivar category: Category of the connection. Required. Known values are: "AzureOpenAI", - "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", and "API Key". + "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", "ApiKey", "CustomKeys", and + "CognitiveService". :vartype category: str or ~azure.ai.projects.models.ConnectionType :ivar target: The connection URL to be used for this service. Required. :vartype target: str @@ -1925,12 +1991,47 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, auth_type=AuthenticationType.API_KEY, **kwargs) +class InternalConnectionPropertiesCustomAuth(InternalConnectionProperties, discriminator="CustomKeys"): + """Connection properties for connections with Custom authentication. + + :ivar category: Category of the connection. Required. Known values are: "AzureOpenAI", + "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", "ApiKey", "CustomKeys", and + "CognitiveService". + :vartype category: str or ~azure.ai.projects.models.ConnectionType + :ivar target: The connection URL to be used for this service. Required. + :vartype target: str + :ivar auth_type: Authentication type of the connection target. Required. Custom authentication + :vartype auth_type: str or ~azure.ai.projects.models.CUSTOM + """ + + auth_type: Literal[AuthenticationType.CUSTOM] = rest_discriminator(name="authType", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Authentication type of the connection target. Required. Custom authentication""" + + @overload + def __init__( + self, + *, + category: Union[str, "_models.ConnectionType"], + target: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, auth_type=AuthenticationType.CUSTOM, **kwargs) + + class InternalConnectionPropertiesNoAuth(InternalConnectionProperties, discriminator="None"): """Connection properties for connections with no authentication. - :ivar category: Category of the connection. Required. Known values are: "AzureOpenAI", - "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", and "API Key". + "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", "ApiKey", "CustomKeys", and + "CognitiveService". :vartype category: str or ~azure.ai.projects.models.ConnectionType :ivar target: The connection URL to be used for this service. Required. :vartype target: str @@ -1963,9 +2064,9 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class InternalConnectionPropertiesSASAuth(InternalConnectionProperties, discriminator="SAS"): """Connection properties for connections with SAS authentication. - :ivar category: Category of the connection. Required. Known values are: "AzureOpenAI", - "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", and "API Key". + "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", "ApiKey", "CustomKeys", and + "CognitiveService". :vartype category: str or ~azure.ai.projects.models.ConnectionType :ivar target: The connection URL to be used for this service. Required. :vartype target: str @@ -2007,7 +2108,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class ListConnectionsResponse(_model_base.Model): """Response from the list operation. - :ivar value: A list of connection list secrets. Required. :vartype value: list[~azure.ai.projects.models._models.GetConnectionResponse] """ @@ -2035,10 +2135,47 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class MAASModelConfig(TargetModelConfig, discriminator="MAAS"): + """MaaS model configuration. The API version would be selected by the service for querying the + model. + + :ivar type: Required. Default value is "MAAS". + :vartype type: str + :ivar azure_endpoint: Endpoint URL for MAAS model. Required. + :vartype azure_endpoint: str + :ivar api_key: API Key for MAAS model. Required. + :vartype api_key: str + """ + + type: Literal["MAAS"] = rest_discriminator(name="type", visibility=["read"]) # type: ignore + """Required. Default value is \"MAAS\".""" + azure_endpoint: str = rest_field(name="azureEndpoint", visibility=["read", "create", "update", "delete", "query"]) + """Endpoint URL for MAAS model. Required.""" + api_key: str = rest_field(name="apiKey", visibility=["read", "create", "update", "delete", "query"]) + """API Key for MAAS model. Required.""" + + @overload + def __init__( + self, + *, + azure_endpoint: str, + api_key: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, type="MAAS", **kwargs) + + class MessageAttachment(_model_base.Model): """This describes to which tools a file has been attached. - :ivar file_id: The ID of the file to attach to the message. :vartype file_id: str :ivar data_source: Azure asset ID. @@ -2085,7 +2222,6 @@ class MessageContent(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: MessageImageFileContent, MessageTextContent - :ivar type: The object type. Required. Default value is None. :vartype type: str """ @@ -2115,7 +2251,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageDelta(_model_base.Model): """Represents the typed 'delta' payload within a streaming message delta chunk. - :ivar role: The entity that produced the message. Required. Known values are: "user" and "assistant". :vartype role: str or ~azure.ai.projects.models.MessageRole @@ -2152,9 +2287,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageDeltaChunk(_model_base.Model): """Represents a message delta i.e. any changed fields on a message during streaming. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The identifier of the message, which can be referenced in API endpoints. Required. :vartype id: str :ivar object: The object type, which is always ``thread.message.delta``. Required. Default @@ -2198,7 +2330,6 @@ class MessageDeltaContent(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: MessageDeltaImageFileContent, MessageDeltaTextContent - :ivar index: The index of the content part of the message. Required. :vartype index: int :ivar type: The type of content for this content part. Required. Default value is None. @@ -2233,7 +2364,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageDeltaImageFileContent(MessageDeltaContent, discriminator="image_file"): """Represents a streamed image file content part within a streaming message delta chunk. - :ivar index: The index of the content part of the message. Required. :vartype index: int :ivar type: The type of content for this content part, which is always "image_file.". Required. @@ -2305,7 +2435,6 @@ class MessageDeltaTextAnnotation(_model_base.Model): MessageDeltaTextFileCitationAnnotation, MessageDeltaTextFilePathAnnotation, MessageDeltaTextUrlCitationAnnotation - :ivar index: The index of the annotation within a text content part. Required. :vartype index: int :ivar type: The type of the text content annotation. Required. Default value is None. @@ -2340,7 +2469,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageDeltaTextContent(MessageDeltaContent, discriminator="text"): """Represents a streamed text content part within a streaming message delta chunk. - :ivar index: The index of the content part of the message. Required. :vartype index: int :ivar type: The type of content for this content part, which is always "text.". Required. @@ -2415,7 +2543,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageDeltaTextFileCitationAnnotation(MessageDeltaTextAnnotation, discriminator="file_citation"): """Represents a streamed file citation applied to a streaming text content part. - :ivar index: The index of the annotation within a text content part. Required. :vartype index: int :ivar type: The type of the text content annotation, which is always "file_citation.". @@ -2503,7 +2630,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageDeltaTextFilePathAnnotation(MessageDeltaTextAnnotation, discriminator="file_path"): """Represents a streamed file path annotation applied to a streaming text content part. - :ivar index: The index of the annotation within a text content part. Required. :vartype index: int :ivar type: The type of the text content annotation, which is always "file_path.". Required. @@ -2588,7 +2714,6 @@ class MessageDeltaTextUrlCitationAnnotation(MessageDeltaTextAnnotation, discrimi """A citation within the message that points to a specific URL associated with the message. Generated when the agent uses tools such as 'bing_grounding' to search the Internet. - :ivar index: The index of the annotation within a text content part. Required. :vartype index: int :ivar type: The object type, which is always 'url_citation'. Required. Default value is @@ -2637,7 +2762,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageDeltaTextUrlCitationDetails(_model_base.Model): """A representation of a URL citation, as used in text thread message content. - :ivar url: The URL associated with this citation. Required. :vartype url: str :ivar title: The title of the URL. @@ -2671,7 +2795,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageImageFileContent(MessageContent, discriminator="image_file"): """A representation of image file content in a thread message. - :ivar type: The object type, which is always 'image_file'. Required. Default value is "image_file". :vartype type: str @@ -2707,7 +2830,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageImageFileDetails(_model_base.Model): """An image reference, as represented in thread message content. - :ivar file_id: The ID for the file associated with this image. Required. :vartype file_id: str """ @@ -2736,7 +2858,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageIncompleteDetails(_model_base.Model): """Information providing additional detail about a message entering an incomplete status. - :ivar reason: The provided reason describing why the message was marked as incomplete. Required. Known values are: "content_filter", "max_tokens", "run_cancelled", "run_failed", and "run_expired". @@ -2775,7 +2896,6 @@ class MessageTextAnnotation(_model_base.Model): MessageTextFileCitationAnnotation, MessageTextFilePathAnnotation, MessageTextUrlCitationAnnotation - :ivar type: The object type. Required. Default value is None. :vartype type: str :ivar text: The textual content associated with this text annotation item. Required. @@ -2810,7 +2930,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageTextContent(MessageContent, discriminator="text"): """A representation of a textual item of thread message content. - :ivar type: The object type, which is always 'text'. Required. Default value is "text". :vartype type: str :ivar text: The text and associated annotations for this thread message content item. Required. @@ -2843,7 +2962,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageTextDetails(_model_base.Model): """The text and associated annotations for a single item of agent thread message content. - :ivar value: The text data. Required. :vartype value: str :ivar annotations: A list of annotations associated with this text. Required. @@ -2881,7 +2999,6 @@ class MessageTextFileCitationAnnotation(MessageTextAnnotation, discriminator="fi with the agent or the message. Generated when the agent uses the 'file_search' tool to search files. - :ivar text: The textual content associated with this text annotation item. Required. :vartype text: str :ivar type: The object type, which is always 'file_citation'. Required. Default value is @@ -2934,7 +3051,6 @@ class MessageTextFileCitationDetails(_model_base.Model): """A representation of a file-based text citation, as used in a file-based annotation of text thread message content. - :ivar file_id: The ID of the file associated with this citation. Required. :vartype file_id: str :ivar quote: The specific quote cited in the associated file. Required. @@ -2968,7 +3084,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageTextFilePathAnnotation(MessageTextAnnotation, discriminator="file_path"): """A citation within the message that points to a file located at a specific path. - :ivar text: The textual content associated with this text annotation item. Required. :vartype text: str :ivar type: The object type, which is always 'file_path'. Required. Default value is @@ -3019,7 +3134,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageTextFilePathDetails(_model_base.Model): """An encapsulation of an image file ID, as used by message image content. - :ivar file_id: The ID of the specific file that the citation is from. Required. :vartype file_id: str """ @@ -3049,7 +3163,6 @@ class MessageTextUrlCitationAnnotation(MessageTextAnnotation, discriminator="url """A citation within the message that points to a specific URL associated with the message. Generated when the agent uses tools such as 'bing_grounding' to search the Internet. - :ivar text: The textual content associated with this text annotation item. Required. :vartype text: str :ivar type: The object type, which is always 'url_citation'. Required. Default value is @@ -3098,7 +3211,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MessageTextUrlCitationDetails(_model_base.Model): """A representation of a URL citation, as used in text thread message content. - :ivar url: The URL associated with this citation. Required. :vartype url: str :ivar title: The title of the URL. @@ -3132,7 +3244,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class MicrosoftFabricToolDefinition(ToolDefinition, discriminator="fabric_aiskill"): """The input definition information for a Microsoft Fabric tool as used to configure an agent. - :ivar type: The object type, which is always 'fabric_aiskill'. Required. Default value is "fabric_aiskill". :vartype type: str @@ -3169,9 +3280,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenAIFile(_model_base.Model): """Represents an agent that can call the model and use tools. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar object: The object type, which is always 'file'. Required. Default value is "file". :vartype object: str :ivar id: The identifier, which can be referenced in API endpoints. Required. @@ -3248,9 +3356,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenAIPageableListOfAgent(_model_base.Model): """The response data for a requested list of items. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar object: The object type, which is always list. Required. Default value is "list". :vartype object: str :ivar data: The requested list of items. Required. @@ -3301,9 +3406,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenAIPageableListOfRunStep(_model_base.Model): """The response data for a requested list of items. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar object: The object type, which is always list. Required. Default value is "list". :vartype object: str :ivar data: The requested list of items. Required. @@ -3354,9 +3456,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenAIPageableListOfThreadMessage(_model_base.Model): """The response data for a requested list of items. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar object: The object type, which is always list. Required. Default value is "list". :vartype object: str :ivar data: The requested list of items. Required. @@ -3407,9 +3506,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenAIPageableListOfThreadRun(_model_base.Model): """The response data for a requested list of items. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar object: The object type, which is always list. Required. Default value is "list". :vartype object: str :ivar data: The requested list of items. Required. @@ -3460,9 +3556,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenAIPageableListOfVectorStore(_model_base.Model): """The response data for a requested list of items. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar object: The object type, which is always list. Required. Default value is "list". :vartype object: str :ivar data: The requested list of items. Required. @@ -3513,9 +3606,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenAIPageableListOfVectorStoreFile(_model_base.Model): """The response data for a requested list of items. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar object: The object type, which is always list. Required. Default value is "list". :vartype object: str :ivar data: The requested list of items. Required. @@ -3569,7 +3659,6 @@ class OpenApiAuthDetails(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: OpenApiAnonymousAuthDetails, OpenApiConnectionAuthDetails, OpenApiManagedAuthDetails - :ivar type: The type of authentication, must be anonymous/connection/managed_identity. Required. Known values are: "anonymous", "connection", and "managed_identity". :vartype type: str or ~azure.ai.projects.models.OpenApiAuthType @@ -3601,7 +3690,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenApiAnonymousAuthDetails(OpenApiAuthDetails, discriminator="anonymous"): """Security details for OpenApi anonymous authentication. - :ivar type: The object type, which is always 'anonymous'. Required. :vartype type: str or ~azure.ai.projects.models.ANONYMOUS """ @@ -3628,7 +3716,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenApiConnectionAuthDetails(OpenApiAuthDetails, discriminator="connection"): """Security details for OpenApi connection authentication. - :ivar type: The object type, which is always 'connection'. Required. :vartype type: str or ~azure.ai.projects.models.CONNECTION :ivar security_scheme: Connection auth security details. Required. @@ -3663,7 +3750,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenApiConnectionSecurityScheme(_model_base.Model): """Security scheme for OpenApi managed_identity authentication. - :ivar connection_id: Connection id for Connection auth type. Required. :vartype connection_id: str """ @@ -3692,7 +3778,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenApiFunctionDefinition(_model_base.Model): """The input definition information for an openapi function. - :ivar name: The name of the function to be called. Required. :vartype name: str :ivar description: A description of what the function does, used by the model to choose when @@ -3738,7 +3823,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenApiManagedAuthDetails(OpenApiAuthDetails, discriminator="managed_identity"): """Security details for OpenApi managed_identity authentication. - :ivar type: The object type, which is always 'managed_identity'. Required. :vartype type: str or ~azure.ai.projects.models.MANAGED_IDENTITY :ivar security_scheme: Connection auth security details. Required. @@ -3773,7 +3857,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenApiManagedSecurityScheme(_model_base.Model): """Security scheme for OpenApi managed_identity authentication. - :ivar audience: Authentication scope for managed_identity auth type. Required. :vartype audience: str """ @@ -3802,7 +3885,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OpenApiToolDefinition(ToolDefinition, discriminator="openapi"): """The input definition information for an OpenAPI tool as used to configure an agent. - :ivar type: The object type, which is always 'openapi'. Required. Default value is "openapi". :vartype type: str :ivar openapi: The openapi function definition. Required. @@ -3837,7 +3919,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RecurrenceSchedule(_model_base.Model): """RecurrenceSchedule Definition. - :ivar hours: List of hours for the schedule. Required. :vartype hours: list[int] :ivar minutes: List of minutes for the schedule. Required. @@ -3885,9 +3966,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RecurrenceTrigger(Trigger, discriminator="Recurrence"): """Recurrence Trigger Definition. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar type: Required. Default value is "Recurrence". :vartype type: str :ivar frequency: The frequency to trigger schedule. Required. Known values are: "Month", @@ -3937,7 +4015,6 @@ class RequiredAction(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: SubmitToolOutputsAction - :ivar type: The object type. Required. Default value is None. :vartype type: str """ @@ -3970,7 +4047,6 @@ class RequiredToolCall(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: RequiredFunctionToolCall - :ivar type: The object type for the required tool call. Required. Default value is None. :vartype type: str :ivar id: The ID of the tool call. This ID must be referenced when submitting tool outputs. @@ -4007,7 +4083,6 @@ class RequiredFunctionToolCall(RequiredToolCall, discriminator="function"): """A representation of a requested call to a function tool, needed by the model to continue evaluation of a run. - :ivar id: The ID of the tool call. This ID must be referenced when submitting tool outputs. Required. :vartype id: str @@ -4051,7 +4126,6 @@ class RequiredFunctionToolCallDetails(_model_base.Model): """The detailed information for a function invocation, as provided by a required action invoking a function tool, that includes the name of and arguments to the function. - :ivar name: The name of the function. Required. :vartype name: str :ivar arguments: The arguments to use when invoking the named function, as provided by the @@ -4089,7 +4163,6 @@ class ResponseFormatJsonSchema(_model_base.Model): """A description of what the response format is for, used by the model to determine how to respond in the format. - :ivar description: A description of what the response format is for, used by the model to determine how to respond in the format. :vartype description: str @@ -4130,9 +4203,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class ResponseFormatJsonSchemaType(_model_base.Model): """The type of response format being defined: ``json_schema``. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar type: Type. Required. Default value is "json_schema". :vartype type: str :ivar json_schema: The JSON schema, describing response format. Required. @@ -4169,7 +4239,6 @@ class RunCompletionUsage(_model_base.Model): """Usage statistics related to the run. This value will be ``null`` if the run is not in a terminal state (i.e. ``in_progress``\\ , ``queued``\\ , etc.). - :ivar completion_tokens: Number of completion tokens used over the course of the run. Required. :vartype completion_tokens: int :ivar prompt_tokens: Number of prompt tokens used over the course of the run. Required. @@ -4208,7 +4277,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunError(_model_base.Model): """The details of an error as encountered by an agent thread run. - :ivar code: The status for the error. Required. :vartype code: str :ivar message: The human-readable text associated with the error. Required. @@ -4242,9 +4310,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStep(_model_base.Model): """Detailed information about a single step of an agent thread run. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The identifier, which can be referenced in API endpoints. Required. :vartype id: str :ivar object: The object type, which is always 'thread.run.step'. Required. Default value is @@ -4382,7 +4447,6 @@ class RunStepToolCall(_model_base.Model): RunStepMicrosoftFabricToolCall, RunStepFileSearchToolCall, RunStepFunctionToolCall, RunStepSharepointToolCall - :ivar type: The object type. Required. Default value is None. :vartype type: str :ivar id: The ID of the tool call. This ID must be referenced when you submit tool outputs. @@ -4420,7 +4484,6 @@ class RunStepAzureAISearchToolCall(RunStepToolCall, discriminator="azure_ai_sear tool, that represents executed Azure AI search. - :ivar id: The ID of the tool call. This ID must be referenced when you submit tool outputs. Required. :vartype id: str @@ -4461,7 +4524,6 @@ class RunStepBingGroundingToolCall(RunStepToolCall, discriminator="bing_groundin tool, that represents executed search with bing grounding. - :ivar id: The ID of the tool call. This ID must be referenced when you submit tool outputs. Required. :vartype id: str @@ -4503,7 +4565,6 @@ class RunStepCodeInterpreterToolCallOutput(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: RunStepCodeInterpreterImageOutput, RunStepCodeInterpreterLogOutput - :ivar type: The object type. Required. Default value is None. :vartype type: str """ @@ -4534,7 +4595,6 @@ class RunStepCodeInterpreterImageOutput(RunStepCodeInterpreterToolCallOutput, di """A representation of an image output emitted by a code interpreter tool in response to a tool call by the model. - :ivar type: The object type, which is always 'image'. Required. Default value is "image". :vartype type: str :ivar image: Referential information for the image associated with this output. Required. @@ -4569,7 +4629,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepCodeInterpreterImageReference(_model_base.Model): """An image reference emitted by a code interpreter tool in response to a tool call by the model. - :ivar file_id: The ID of the file associated with this image. Required. :vartype file_id: str """ @@ -4599,7 +4658,6 @@ class RunStepCodeInterpreterLogOutput(RunStepCodeInterpreterToolCallOutput, disc """A representation of a log output emitted by a code interpreter tool in response to a tool call by the model. - :ivar type: The object type, which is always 'logs'. Required. Default value is "logs". :vartype type: str :ivar logs: The serialized log output emitted by the code interpreter. Required. @@ -4634,7 +4692,6 @@ class RunStepCodeInterpreterToolCall(RunStepToolCall, discriminator="code_interp tool, that represents inputs and outputs consumed and emitted by the code interpreter. - :ivar id: The ID of the tool call. This ID must be referenced when you submit tool outputs. Required. :vartype id: str @@ -4675,7 +4732,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepCodeInterpreterToolCallDetails(_model_base.Model): """The detailed information about a code interpreter invocation by the model. - :ivar input: The input provided by the model to the code interpreter tool. Required. :vartype input: str :ivar outputs: The outputs produced by the code interpreter tool back to the model in response @@ -4713,7 +4769,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepCompletionUsage(_model_base.Model): """Usage statistics related to the run step. - :ivar completion_tokens: Number of completion tokens used over the course of the run step. Required. :vartype completion_tokens: int @@ -4783,9 +4838,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepDeltaChunk(_model_base.Model): """Represents a run step delta i.e. any changed fields on a run step during streaming. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The identifier of the run step, which can be referenced in API endpoints. Required. :vartype id: str :ivar object: The object type, which is always ``thread.run.step.delta``. Required. Default @@ -4842,7 +4894,7 @@ class RunStepDeltaCodeInterpreterDetailItemObject(_model_base.Model): # pylint: visibility=["read", "create", "update", "delete", "query"] ) """The outputs from the Code Interpreter tool call. Code Interpreter can output one or more - items, including text (``logs``) or images (``image``). Each of these are represented + items, including text (\ ``logs``\ ) or images (\ ``image``\ ). Each of these are represented by a different object type.""" @@ -4872,7 +4924,6 @@ class RunStepDeltaCodeInterpreterOutput(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: RunStepDeltaCodeInterpreterImageOutput, RunStepDeltaCodeInterpreterLogOutput - :ivar index: The index of the output in the streaming run step tool call's Code Interpreter outputs array. Required. :vartype index: int @@ -4912,7 +4963,6 @@ class RunStepDeltaCodeInterpreterImageOutput(RunStepDeltaCodeInterpreterOutput, """Represents an image output as produced the Code interpreter tool and as represented in a streaming run step's delta tool calls collection. - :ivar index: The index of the output in the streaming run step tool call's Code Interpreter outputs array. Required. :vartype index: int @@ -4980,7 +5030,6 @@ class RunStepDeltaCodeInterpreterLogOutput(RunStepDeltaCodeInterpreterOutput, di """Represents a log output as produced by the Code Interpreter tool and as represented in a streaming run step's delta tool calls collection. - :ivar index: The index of the output in the streaming run step tool call's Code Interpreter outputs array. Required. :vartype index: int @@ -5022,7 +5071,6 @@ class RunStepDeltaToolCall(_model_base.Model): RunStepDeltaCodeInterpreterToolCall, RunStepDeltaFileSearchToolCall, RunStepDeltaFunctionToolCall - :ivar index: The index of the tool call detail in the run step's tool_calls array. Required. :vartype index: int :ivar id: The ID of the tool call, used when submitting outputs to the run. Required. @@ -5064,7 +5112,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepDeltaCodeInterpreterToolCall(RunStepDeltaToolCall, discriminator="code_interpreter"): """Represents a Code Interpreter tool call within a streaming run step's tool call details. - :ivar index: The index of the tool call detail in the run step's tool_calls array. Required. :vartype index: int :ivar id: The ID of the tool call, used when submitting outputs to the run. Required. @@ -5111,7 +5158,6 @@ class RunStepDeltaDetail(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: RunStepDeltaMessageCreation, RunStepDeltaToolCallObject - :ivar type: The object type for the run step detail object. Required. Default value is None. :vartype type: str """ @@ -5141,7 +5187,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepDeltaFileSearchToolCall(RunStepDeltaToolCall, discriminator="file_search"): """Represents a file search tool call within a streaming run step's tool call details. - :ivar index: The index of the tool call detail in the run step's tool_calls array. Required. :vartype index: int :ivar id: The ID of the tool call, used when submitting outputs to the run. Required. @@ -5221,7 +5266,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepDeltaFunctionToolCall(RunStepDeltaToolCall, discriminator="function"): """Represents a function tool call within a streaming run step's tool call details. - :ivar index: The index of the tool call detail in the run step's tool_calls array. Required. :vartype index: int :ivar id: The ID of the tool call, used when submitting outputs to the run. Required. @@ -5263,7 +5307,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepDeltaMessageCreation(RunStepDeltaDetail, discriminator="message_creation"): """Represents a message creation within a streaming run step delta. - :ivar type: The object type, which is always "message_creation.". Required. Default value is "message_creation". :vartype type: str @@ -5328,7 +5371,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepDeltaToolCallObject(RunStepDeltaDetail, discriminator="tool_calls"): """Represents an invocation of tool calls as part of a streaming run step. - :ivar type: The object type, which is always "tool_calls.". Required. Default value is "tool_calls". :vartype type: str @@ -5367,7 +5409,6 @@ class RunStepDetails(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: RunStepMessageCreationDetails, RunStepToolCallDetails - :ivar type: The object type. Required. Known values are: "message_creation" and "tool_calls". :vartype type: str or ~azure.ai.projects.models.RunStepType """ @@ -5397,7 +5438,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepError(_model_base.Model): """The error information associated with a failed run step. - :ivar code: The error code for this error. Required. Known values are: "server_error" and "rate_limit_exceeded". :vartype code: str or ~azure.ai.projects.models.RunStepErrorCode @@ -5437,7 +5477,6 @@ class RunStepFileSearchToolCall(RunStepToolCall, discriminator="file_search"): that represents executed file search. - :ivar type: The object type, which is always 'file_search'. Required. Default value is "file_search". :vartype type: str @@ -5477,7 +5516,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepFileSearchToolCallResult(_model_base.Model): """File search tool call result. - :ivar file_id: The ID of the file that result was found in. Required. :vartype file_id: str :ivar file_name: The name of the file that result was found in. Required. @@ -5526,7 +5564,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepFileSearchToolCallResults(_model_base.Model): """The results of the file search. - :ivar ranking_options: Ranking options for file search. :vartype ranking_options: ~azure.ai.projects.models.FileSearchRankingOptions :ivar results: The array of a file search results. Required. @@ -5566,7 +5603,6 @@ class RunStepFunctionToolCall(RunStepToolCall, discriminator="function"): that represents the inputs and output consumed and emitted by the specified function. - :ivar id: The ID of the tool call. This ID must be referenced when you submit tool outputs. Required. :vartype id: str @@ -5605,7 +5641,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepFunctionToolCallDetails(_model_base.Model): """The detailed information about the function called by the model. - :ivar name: The name of the function. Required. :vartype name: str :ivar arguments: The arguments that the model requires are provided to the named function. @@ -5647,7 +5682,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepMessageCreationDetails(RunStepDetails, discriminator="message_creation"): """The detailed information associated with a message creation run step. - :ivar type: The object type, which is always 'message_creation'. Required. Represents a run step to create a message. :vartype type: str or ~azure.ai.projects.models.MESSAGE_CREATION @@ -5685,7 +5719,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepMessageCreationReference(_model_base.Model): """The details of a message created as a part of a run step. - :ivar message_id: The ID of the message created by this run step. Required. :vartype message_id: str """ @@ -5716,7 +5749,6 @@ class RunStepMicrosoftFabricToolCall(RunStepToolCall, discriminator="fabric_aisk tool, that represents executed Microsoft Fabric operations. - :ivar id: The ID of the tool call. This ID must be referenced when you submit tool outputs. Required. :vartype id: str @@ -5759,7 +5791,6 @@ class RunStepSharepointToolCall(RunStepToolCall, discriminator="sharepoint_groun that represents executed SharePoint actions. - :ivar id: The ID of the tool call. This ID must be referenced when you submit tool outputs. Required. :vartype id: str @@ -5800,7 +5831,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class RunStepToolCallDetails(RunStepDetails, discriminator="tool_calls"): """The detailed information associated with a run step calling tools. - :ivar type: The object type, which is always 'tool_calls'. Required. Represents a run step that calls tools. :vartype type: str or ~azure.ai.projects.models.TOOL_CALLS @@ -5835,7 +5865,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class SharepointToolDefinition(ToolDefinition, discriminator="sharepoint_grounding"): """The input definition information for a sharepoint tool as used to configure an agent. - :ivar type: The object type, which is always 'sharepoint_grounding'. Required. Default value is "sharepoint_grounding". :vartype type: str @@ -5872,7 +5901,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class SubmitToolOutputsAction(RequiredAction, discriminator="submit_tool_outputs"): """The details for required tool calls that must be submitted for an agent thread run to continue. - :ivar type: The object type, which is always 'submit_tool_outputs'. Required. Default value is "submit_tool_outputs". :vartype type: str @@ -5910,7 +5938,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class SubmitToolOutputsDetails(_model_base.Model): """The details describing tools that should be called to submit tool outputs. - :ivar tool_calls: The list of tool calls that must be resolved for the agent thread run to continue. Required. :vartype tool_calls: list[~azure.ai.projects.models.RequiredToolCall] @@ -5942,8 +5969,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class SystemData(_model_base.Model): """Metadata pertaining to creation and last modification of the resource. - Readonly variables are only populated by the server, and will be ignored when sending a request. - :ivar created_at: The timestamp the resource was created at. :vartype created_at: ~datetime.datetime :ivar created_by: The identity that created the resource. @@ -5969,9 +5994,6 @@ class SystemData(_model_base.Model): class ThreadDeletionStatus(_model_base.Model): """The status of a thread deletion operation. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The ID of the resource specified for deletion. Required. :vartype id: str :ivar deleted: A value indicating whether deletion was successful. Required. @@ -6012,9 +6034,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class ThreadMessage(_model_base.Model): """A single, existing message within an agent thread. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The identifier, which can be referenced in API endpoints. Required. :vartype id: str :ivar object: The object type, which is always 'thread.message'. Required. Default value is @@ -6135,8 +6154,6 @@ class ThreadMessageOptions(_model_base.Model): """A single message within an agent thread, as provided during that thread's creation for its initial state. - All required parameters must be populated in order to send to server. - :ivar role: The role of the entity that is creating the message. Allowed values include: @@ -6163,9 +6180,9 @@ class ThreadMessageOptions(_model_base.Model): """The role of the entity that is creating the message. Allowed values include: - * ``user``: Indicates the message is sent by an actual user and should be used in most + * ``user``\ : Indicates the message is sent by an actual user and should be used in most cases to represent user-generated messages. - * ``assistant``: Indicates the message is generated by the agent. Use this value to insert + * ``assistant``\ : Indicates the message is generated by the agent. Use this value to insert messages from the agent into the conversation. Required. Known values are: \"user\" and \"assistant\".""" content: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -6205,9 +6222,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class ThreadRun(_model_base.Model): """Data representing a single evaluation run of an agent thread. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The identifier, which can be referenced in API endpoints. Required. :vartype id: str :ivar object: The object type, which is always 'thread.run'. Required. Default value is @@ -6344,7 +6358,7 @@ class ThreadRun(_model_base.Model): """Details on why the run is incomplete. Will be ``null`` if the run is not incomplete. Required.""" usage: "_models.RunCompletionUsage" = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Usage statistics related to the run. This value will be ``null`` if the run is not in a - terminal state (i.e. ``in_progress``, ``queued``, etc.). Required.""" + terminal state (i.e. ``in_progress``\ , ``queued``\ , etc.). Required.""" temperature: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The sampling temperature used for this run. If not set, defaults to 1.""" top_p: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -6431,7 +6445,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class ToolConnection(_model_base.Model): """A connection resource. - :ivar connection_id: A connection in a ToolConnectionList attached to this tool. Required. :vartype connection_id: str """ @@ -6582,7 +6595,6 @@ class TruncationObject(_model_base.Model): """Controls for how a thread will be truncated prior to the run. Use this to control the initial context window of the run. - :ivar type: The truncation strategy to use for the thread. The default is ``auto``. If set to ``last_messages``\\ , the thread will be truncated to the ``lastMessages`` count most recent messages in the thread. When set to @@ -6599,9 +6611,9 @@ class TruncationObject(_model_base.Model): visibility=["read", "create", "update", "delete", "query"] ) """The truncation strategy to use for the thread. The default is ``auto``. If set to - ``last_messages``, the thread will + ``last_messages``\ , the thread will be truncated to the ``lastMessages`` count most recent messages in the thread. When set to - ``auto``, messages in the middle of the thread + ``auto``\ , messages in the middle of the thread will be dropped to fit the context length of the model, ``max_prompt_tokens``. Required. Known values are: \"auto\" and \"last_messages\".""" last_messages: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -6741,9 +6753,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class VectorStore(_model_base.Model): """A vector store is a collection of processed files can be used by the ``file_search`` tool. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The identifier, which can be referenced in API endpoints. Required. :vartype id: str :ivar object: The object type, which is always ``vector_store``. Required. Default value is @@ -6793,7 +6802,7 @@ class VectorStore(_model_base.Model): status: Union[str, "_models.VectorStoreStatus"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) - """The status of the vector store, which can be either ``expired``, ``in_progress``, or + """The status of the vector store, which can be either ``expired``\ , ``in_progress``\ , or ``completed``. A status of ``completed`` indicates that the vector store is ready for use. Required. Known values are: \"expired\", \"in_progress\", and \"completed\".""" expires_after: Optional["_models.VectorStoreExpirationPolicy"] = rest_field( @@ -6847,8 +6856,6 @@ class VectorStoreChunkingStrategyRequest(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: VectorStoreAutoChunkingStrategyRequest, VectorStoreStaticChunkingStrategyRequest - All required parameters must be populated in order to send to server. - :ivar type: The object type. Required. Known values are: "auto" and "static". :vartype type: str or ~azure.ai.projects.models.VectorStoreChunkingStrategyRequestType """ @@ -6879,8 +6886,6 @@ class VectorStoreAutoChunkingStrategyRequest(VectorStoreChunkingStrategyRequest, """The default strategy. This strategy currently uses a max_chunk_size_tokens of 800 and chunk_overlap_tokens of 400. - All required parameters must be populated in order to send to server. - :ivar type: The object type, which is always 'auto'. Required. :vartype type: str or ~azure.ai.projects.models.AUTO """ @@ -6910,7 +6915,6 @@ class VectorStoreChunkingStrategyResponse(_model_base.Model): You probably want to use the sub-classes and not this class directly. Known sub-classes are: VectorStoreAutoChunkingStrategyResponse, VectorStoreStaticChunkingStrategyResponse - :ivar type: The object type. Required. Known values are: "other" and "static". :vartype type: str or ~azure.ai.projects.models.VectorStoreChunkingStrategyResponseType """ @@ -6941,7 +6945,6 @@ class VectorStoreAutoChunkingStrategyResponse(VectorStoreChunkingStrategyRespons """This is returned when the chunking strategy is unknown. Typically, this is because the file was indexed before the chunking_strategy concept was introduced in the API. - :ivar type: The object type, which is always 'other'. Required. :vartype type: str or ~azure.ai.projects.models.OTHER """ @@ -6969,7 +6972,6 @@ class VectorStoreConfiguration(_model_base.Model): """Vector storage configuration is the list of data sources, used when multiple files can be used for the enterprise file search. - :ivar data_sources: Data sources. Required. :vartype data_sources: list[~azure.ai.projects.models.VectorStoreDataSource] """ @@ -7001,7 +7003,6 @@ class VectorStoreConfigurations(_model_base.Model): """The structure, containing the list of vector storage configurations i.e. the list of azure asset IDs. - :ivar store_name: Name. Required. :vartype store_name: str :ivar store_configuration: Configurations. Required. @@ -7039,7 +7040,6 @@ class VectorStoreDataSource(_model_base.Model): source for the enterprise file search. - :ivar asset_identifier: Asset URI. Required. :vartype asset_identifier: str :ivar asset_type: The asset type. Required. Known values are: "uri_asset" and "id_asset". @@ -7075,9 +7075,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class VectorStoreDeletionStatus(_model_base.Model): """Response object for deleting a vector store. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The ID of the resource specified for deletion. Required. :vartype id: str :ivar deleted: A value indicating whether deletion was successful. Required. @@ -7118,7 +7115,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class VectorStoreExpirationPolicy(_model_base.Model): """The expiration policy for a vector store. - :ivar anchor: Anchor timestamp after which the expiration policy applies. Supported anchors: ``last_active_at``. Required. "last_active_at" :vartype anchor: str or ~azure.ai.projects.models.VectorStoreExpirationPolicyAnchor @@ -7156,9 +7152,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class VectorStoreFile(_model_base.Model): """Description of a file attached to a vector store. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The identifier, which can be referenced in API endpoints. Required. :vartype id: str :ivar object: The object type, which is always ``vector_store.file``. Required. Default value @@ -7202,8 +7195,8 @@ class VectorStoreFile(_model_base.Model): status: Union[str, "_models.VectorStoreFileStatus"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) - """The status of the vector store file, which can be either ``in_progress``, ``completed``, - ``cancelled``, or ``failed``. The status ``completed`` indicates that the vector store file + """The status of the vector store file, which can be either ``in_progress``\ , ``completed``\ , + ``cancelled``\ , or ``failed``. The status ``completed`` indicates that the vector store file is ready for use. Required. Known values are: \"in_progress\", \"completed\", \"failed\", and \"cancelled\".""" last_error: "_models.VectorStoreFileError" = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -7242,9 +7235,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class VectorStoreFileBatch(_model_base.Model): """A batch of files attached to a vector store. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The identifier, which can be referenced in API endpoints. Required. :vartype id: str :ivar object: The object type, which is always ``vector_store.file_batch``. Required. Default @@ -7278,8 +7268,8 @@ class VectorStoreFileBatch(_model_base.Model): status: Union[str, "_models.VectorStoreFileBatchStatus"] = rest_field( visibility=["read", "create", "update", "delete", "query"] ) - """The status of the vector store files batch, which can be either ``in_progress``, - ``completed``, ``cancelled`` or ``failed``. Required. Known values are: \"in_progress\", + """The status of the vector store files batch, which can be either ``in_progress``\ , + ``completed``\ , ``cancelled`` or ``failed``. Required. Known values are: \"in_progress\", \"completed\", \"cancelled\", and \"failed\".""" file_counts: "_models.VectorStoreFileCount" = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Files count grouped by status processed or being processed by this vector store. Required.""" @@ -7310,7 +7300,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class VectorStoreFileCount(_model_base.Model): """Counts of files processed or being processed by this vector store grouped by status. - :ivar in_progress: The number of files that are currently being processed. Required. :vartype in_progress: int :ivar completed: The number of files that have been successfully processed. Required. @@ -7359,9 +7348,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class VectorStoreFileDeletionStatus(_model_base.Model): """Response object for deleting a vector store file relationship. - Readonly variables are only populated by the server, and will be ignored when sending a request. - - :ivar id: The ID of the resource specified for deletion. Required. :vartype id: str :ivar deleted: A value indicating whether deletion was successful. Required. @@ -7404,7 +7390,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class VectorStoreFileError(_model_base.Model): """Details on the error that may have occurred while processing a file for this vector store. - :ivar code: One of ``server_error`` or ``rate_limit_exceeded``. Required. Known values are: "server_error", "invalid_file", and "unsupported_file". :vartype code: str or ~azure.ai.projects.models.VectorStoreFileErrorCode @@ -7442,7 +7427,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class VectorStoreStaticChunkingStrategyOptions(_model_base.Model): """Options to configure a vector store static chunking strategy. - :ivar max_chunk_size_tokens: The maximum number of tokens in each chunk. The default value is 800. The minimum value is 100 and the maximum value is 4096. Required. :vartype max_chunk_size_tokens: int @@ -7481,8 +7465,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class VectorStoreStaticChunkingStrategyRequest(VectorStoreChunkingStrategyRequest, discriminator="static"): """A statically configured chunking strategy. - All required parameters must be populated in order to send to server. - :ivar type: The object type, which is always 'static'. Required. :vartype type: str or ~azure.ai.projects.models.STATIC :ivar static: The options for the static chunking strategy. Required. @@ -7519,7 +7501,6 @@ class VectorStoreStaticChunkingStrategyResponse( ): # pylint: disable=name-too-long """A statically configured chunking strategy. - :ivar type: The object type, which is always 'static'. Required. :vartype type: str or ~azure.ai.projects.models.STATIC :ivar static: The options for the static chunking strategy. Required. @@ -7554,7 +7535,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class WorkspaceProperties(_model_base.Model): """workspace properties. - :ivar application_insights: Authentication type of the connection target. Required. :vartype application_insights: str """ diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py index a0932c0c5f62..bacb9513b236 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py @@ -115,9 +115,9 @@ def build_agents_get_agent_request(agent_id: str, **kwargs: Any) -> HttpRequest: accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/assistants/{agentId}" + _url = "/assistants/{assistantId}" path_format_arguments = { - "agentId": _SERIALIZER.url("agent_id", agent_id, "str"), + "assistantId": _SERIALIZER.url("agent_id", agent_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -140,9 +140,9 @@ def build_agents_update_agent_request(agent_id: str, **kwargs: Any) -> HttpReque accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/assistants/{agentId}" + _url = "/assistants/{assistantId}" path_format_arguments = { - "agentId": _SERIALIZER.url("agent_id", agent_id, "str"), + "assistantId": _SERIALIZER.url("agent_id", agent_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -166,9 +166,9 @@ def build_agents_delete_agent_request(agent_id: str, **kwargs: Any) -> HttpReque accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/assistants/{agentId}" + _url = "/assistants/{assistantId}" path_format_arguments = { - "agentId": _SERIALIZER.url("agent_id", agent_id, "str"), + "assistantId": _SERIALIZER.url("agent_id", agent_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -6553,8 +6553,8 @@ def _list_connections( """List the details of all the connections (not including their credentials). :keyword category: Category of the workspace connection. Known values are: "AzureOpenAI", - "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", and "API Key". Default value is - None. + "Serverless", "AzureBlob", "AIServices", "CognitiveSearch", "ApiKey", "CustomKeys", and + "CognitiveService". Default value is None. :paramtype category: str or ~azure.ai.projects.models.ConnectionType :keyword include_all: Indicates whether to list datastores. Service default: do not list datastores. Default value is None. diff --git a/sdk/ai/azure-ai-projects/tsp-location.yaml b/sdk/ai/azure-ai-projects/tsp-location.yaml index 2464af5f47d1..485b84ac6b17 100644 --- a/sdk/ai/azure-ai-projects/tsp-location.yaml +++ b/sdk/ai/azure-ai-projects/tsp-location.yaml @@ -1,4 +1,4 @@ directory: specification/ai/Azure.AI.Projects -commit: df052103b67daac4a879d24ffb31fce32ba553d2 +commit: 5e8cee065823486e7d457bef724344b118cd50ef repo: Azure/azure-rest-api-specs additionalDirectories: From e777d34bfce689891e0a36211366911481ee96f7 Mon Sep 17 00:00:00 2001 From: M-Hietala <78813398+M-Hietala@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:04:33 -0500 Subject: [PATCH 05/10] fixing tool call tracing (#40119) * fixing tool call tracing * review related changes and code checker tool related changes --------- Co-authored-by: Marko Hietala --- .../agents/_ai_agents_instrumentor.py | 97 ++++++++++++++----- 1 file changed, 74 insertions(+), 23 deletions(-) diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py index 3db2479957e6..8e66d2e99d6e 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py @@ -20,10 +20,12 @@ MessageAttachment, MessageDeltaChunk, MessageIncompleteDetails, + RequiredFunctionToolCall, RunStep, RunStepDeltaChunk, RunStepFunctionToolCall, RunStepToolCallDetails, + SubmitToolOutputsAction, ThreadMessage, ThreadRun, ToolDefinition, @@ -390,7 +392,6 @@ def _status_to_string(self, status: Any) -> str: return status.value if hasattr(status, "value") else status def _add_tool_assistant_message_event(self, span, step: RunStep) -> None: - # do we want a new event for it ? tool_calls = [ { "id": t.id, @@ -419,6 +420,40 @@ def _add_tool_assistant_message_event(self, span, step: RunStep) -> None: attributes[GEN_AI_EVENT_CONTENT] = json.dumps({"tool_calls": tool_calls_non_recording}) span.span_instance.add_event(name="gen_ai.assistant.message", attributes=attributes) + def _add_tool_event_from_thread_run(self, span, run: ThreadRun) -> None: + tool_calls = [] + + for t in run.required_action.submit_tool_outputs.tool_calls: # type: ignore + try: + parsed_arguments = json.loads(t.function.arguments) + except json.JSONDecodeError: + parsed_arguments = {} + + tool_call = { + "id": t.id, + "type": t.type, + "function": ( + {"name": t.function.name, "arguments": parsed_arguments} + if isinstance(t, RequiredFunctionToolCall) + else None + ), + } + tool_calls.append(tool_call) + + attributes = self._create_event_attributes( + thread_id=run.thread_id, + agent_id=run.agent_id, + thread_run_id=run.id, + message_status=run.status, + ) + + if _trace_agents_content: + attributes[GEN_AI_EVENT_CONTENT] = json.dumps({"tool_calls": tool_calls}) + else: + tool_calls_non_recording = self._remove_function_call_names_and_arguments(tool_calls=tool_calls) + attributes[GEN_AI_EVENT_CONTENT] = json.dumps({"tool_calls": tool_calls_non_recording}) + span.span_instance.add_event(name="gen_ai.assistant.message", attributes=attributes) + def set_end_run(self, span: "AbstractSpan", run: Optional[ThreadRun]) -> None: if run and span and span.span_instance.is_recording: span.add_attribute(GEN_AI_THREAD_RUN_STATUS, self._status_to_string(run.status)) @@ -1654,33 +1689,41 @@ def on_message_delta(self, delta: "MessageDeltaChunk") -> None: # type: ignore[ return self.inner_handler.on_message_delta(delta) # type: ignore def on_thread_message(self, message: "ThreadMessage") -> None: # type: ignore[func-returns-value] + retval = None if self.inner_handler: - return self.inner_handler.on_thread_message(message) # type: ignore + retval = self.inner_handler.on_thread_message(message) # type: ignore if message.status in {"completed", "incomplete"}: self.last_message = message + return retval # type: ignore + def on_thread_run(self, run: "ThreadRun") -> None: # type: ignore[func-returns-value] + retval = None + + if run.status == "requires_action" and isinstance(run.required_action, SubmitToolOutputsAction): + self.instrumentor._add_tool_event_from_thread_run( # pylint: disable=protected-access # pyright: ignore [reportFunctionMemberAccess] + self.span, run + ) + if self.inner_handler: - return self.inner_handler.on_thread_run(run) # type: ignore + retval = self.inner_handler.on_thread_run(run) # type: ignore self.last_run = run + return retval # type: ignore + def on_run_step(self, step: "RunStep") -> None: # type: ignore[func-returns-value] + retval = None if self.inner_handler: - return self.inner_handler.on_run_step(step) # type: ignore - - if step.status == RunStepStatus.IN_PROGRESS: - return + retval = self.inner_handler.on_run_step(step) # type: ignore # todo - report errors for failure statuses here and in run ? - if step.type == "tool_calls" and isinstance(step.step_details, RunStepToolCallDetails): - self.instrumentor._add_tool_assistant_message_event( # pylint: disable=protected-access # pyright: ignore [reportFunctionMemberAccess] - self.span, step - ) - elif step.type == "message_creation" and step.status == RunStepStatus.COMPLETED: + if step.type == "message_creation" and step.status == RunStepStatus.COMPLETED: self.instrumentor.add_thread_message_event(self.span, cast(ThreadMessage, self.last_message), step.usage) self.last_message = None + return retval # type: ignore + def on_run_step_delta(self, delta: "RunStepDeltaChunk") -> None: # type: ignore[func-returns-value] if self.inner_handler: return self.inner_handler.on_run_step_delta(delta) # type: ignore @@ -1737,33 +1780,41 @@ async def on_message_delta(self, delta: "MessageDeltaChunk") -> None: # type: i return await self.inner_handler.on_message_delta(delta) # type: ignore async def on_thread_message(self, message: "ThreadMessage") -> None: # type: ignore[func-returns-value] + retval = None if self.inner_handler: - return await self.inner_handler.on_thread_message(message) # type: ignore + retval = await self.inner_handler.on_thread_message(message) # type: ignore if message.status in {"completed", "incomplete"}: self.last_message = message + return retval # type: ignore + async def on_thread_run(self, run: "ThreadRun") -> None: # type: ignore[func-returns-value] + retval = None + + if run.status == "requires_action" and isinstance(run.required_action, SubmitToolOutputsAction): + self.instrumentor._add_tool_event_from_thread_run( # pylint: disable=protected-access # pyright: ignore [reportFunctionMemberAccess] + self.span, run + ) + if self.inner_handler: - return await self.inner_handler.on_thread_run(run) # type: ignore + retval = await self.inner_handler.on_thread_run(run) # type: ignore self.last_run = run + return retval # type: ignore + async def on_run_step(self, step: "RunStep") -> None: # type: ignore[func-returns-value] + retval = None if self.inner_handler: - return await self.inner_handler.on_run_step(step) # type: ignore - - if step.status == RunStepStatus.IN_PROGRESS: - return + retval = await self.inner_handler.on_run_step(step) # type: ignore # todo - report errors for failure statuses here and in run ? - if step.type == "tool_calls" and isinstance(step.step_details, RunStepToolCallDetails): - self.instrumentor._add_tool_assistant_message_event( # pylint: disable=protected-access # pyright: ignore [reportFunctionMemberAccess] - self.span, step - ) - elif step.type == "message_creation" and step.status == RunStepStatus.COMPLETED: + if step.type == "message_creation" and step.status == RunStepStatus.COMPLETED: self.instrumentor.add_thread_message_event(self.span, cast(ThreadMessage, self.last_message), step.usage) self.last_message = None + return retval # type: ignore + async def on_run_step_delta(self, delta: "RunStepDeltaChunk") -> None: # type: ignore[func-returns-value] if self.inner_handler: return await self.inner_handler.on_run_step_delta(delta) # type: ignore From 0022bc58774192a75309cb6ba00aab057c9a4ec6 Mon Sep 17 00:00:00 2001 From: M-Hietala <78813398+M-Hietala@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:39:54 -0500 Subject: [PATCH 06/10] Update CHANGELOG.md --- sdk/ai/azure-ai-projects/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/ai/azure-ai-projects/CHANGELOG.md b/sdk/ai/azure-ai-projects/CHANGELOG.md index 1e03aee1803f..be5ec9468a27 100644 --- a/sdk/ai/azure-ai-projects/CHANGELOG.md +++ b/sdk/ai/azure-ai-projects/CHANGELOG.md @@ -9,6 +9,7 @@ ### Bugs Fixed * Fix for a bug in agent tracing causing event handler return values to not be returned when tracing is enabled. +* Fix for a bug in agent tracing causing tool calls not to be recorded in traces. ### Breaking Changes From 57a2b515861fa9bd074adc771b1db67bc0b674bc Mon Sep 17 00:00:00 2001 From: Howie Leung Date: Tue, 18 Mar 2025 20:03:45 -0700 Subject: [PATCH 07/10] Fixed toolset (#40121) Co-authored-by: Darren Cohen <39422044+dargilco@users.noreply.github.com> --- sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py index a0d14a9327ea..334add93b802 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py @@ -162,6 +162,9 @@ def _parse_event(event_data_str: str) -> Tuple[str, StreamEventData]: if event_type.startswith("thread.run.step") and isinstance(parsed_data, dict) and "expires_at" in parsed_data: parsed_data["expired_at"] = parsed_data.pop("expires_at") + if isinstance(parsed_data, dict) and "assistant_id" in parsed_data: + parsed_data["agent_id"] = parsed_data.pop("assistant_id") + # Map to the appropriate class instance if event_type in { AgentStreamEvent.THREAD_RUN_CREATED.value, From dfefbf207048f25346b3d7d50181c823184da9f1 Mon Sep 17 00:00:00 2001 From: M-Hietala <78813398+M-Hietala@users.noreply.github.com> Date: Wed, 19 Mar 2025 14:25:39 -0500 Subject: [PATCH 08/10] fixing agent tracing tests (#40140) Co-authored-by: Marko Hietala --- .../tests/telemetry/gen_ai_trace_verifier.py | 5 ++-- .../telemetry/test_ai_agents_instrumentor.py | 30 +++++++++---------- .../test_ai_agents_instrumentor_async.py | 20 ++++++------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/sdk/ai/azure-ai-projects/tests/telemetry/gen_ai_trace_verifier.py b/sdk/ai/azure-ai-projects/tests/telemetry/gen_ai_trace_verifier.py index 47e423ae29eb..d512d33bdce2 100644 --- a/sdk/ai/azure-ai-projects/tests/telemetry/gen_ai_trace_verifier.py +++ b/sdk/ai/azure-ai-projects/tests/telemetry/gen_ai_trace_verifier.py @@ -192,13 +192,14 @@ def check_event_attributes(self, expected_dict, actual_dict): return True def check_span_events(self, span, expected_events): + print("Checking span: " + span.name) span_events = list(span.events) # Create a list of events from the span for expected_event in expected_events: for actual_event in span_events: if expected_event["name"] == actual_event.name: - if not self.check_event_attributes(expected_event["attributes"], actual_event.attributes): - print("check_span_events: event attributes do not match") + if not self.check_event_attributes(expected_event["attributes"], actual_event.attributes._dict): + print("Event attributes do not match") return False span_events.remove(actual_event) # Remove the matched event from the span_events break diff --git a/sdk/ai/azure-ai-projects/tests/telemetry/test_ai_agents_instrumentor.py b/sdk/ai/azure-ai-projects/tests/telemetry/test_ai_agents_instrumentor.py index 0ace16597057..2eca8bd339ab 100644 --- a/sdk/ai/azure-ai-projects/tests/telemetry/test_ai_agents_instrumentor.py +++ b/sdk/ai/azure-ai-projects/tests/telemetry/test_ai_agents_instrumentor.py @@ -624,11 +624,11 @@ def fetch_weather(location: str) -> str: "attributes": { "gen_ai.system": "az.ai.agents", "gen_ai.thread.id": "*", - # "gen_ai.agent.id": "*", - workaround for https://github.com/Azure/azure-sdk-for-python/issues/40086 + "gen_ai.agent.id": "*", "gen_ai.thread.run.id": "*", - "gen_ai.message.status": "completed", - "gen_ai.usage.input_tokens": "+", - "gen_ai.usage.output_tokens": "+", + "gen_ai.message.status": "requires_action", + # "gen_ai.usage.input_tokens": "+", # not available at the moment + # "gen_ai.usage.output_tokens": "+", # not available at the moment "gen_ai.event.content": '{"tool_calls": [{"id": "*", "type": "function", "function": {"name": "fetch_weather", "arguments": {"location": "New York"}}}]}', }, }, @@ -637,7 +637,7 @@ def fetch_weather(location: str) -> str: "attributes": { "gen_ai.system": "az.ai.agents", "gen_ai.thread.id": "*", - # "gen_ai.agent.id": "*", - workaround for https://github.com/Azure/azure-sdk-for-python/issues/40086 + "gen_ai.agent.id": "*", "gen_ai.thread.run.id": "*", "gen_ai.message.id": "*", "gen_ai.message.status": "completed", @@ -772,11 +772,11 @@ def fetch_weather(location: str) -> str: "attributes": { "gen_ai.system": "az.ai.agents", "gen_ai.thread.id": "*", - # "gen_ai.agent.id": "*", - workaround for https://github.com/Azure/azure-sdk-for-python/issues/40086 + "gen_ai.agent.id": "*", "gen_ai.thread.run.id": "*", - "gen_ai.message.status": "completed", - "gen_ai.usage.input_tokens": "+", - "gen_ai.usage.output_tokens": "+", + "gen_ai.message.status": "requires_action", + # "gen_ai.usage.input_tokens": "+", # not available at the moment + # "gen_ai.usage.output_tokens": "+", # not available at the moment "gen_ai.event.content": '{"tool_calls": [{"id": "*", "type": "function", "function": {"name": "fetch_weather", "arguments": {"location": "Sofia"}}}]}', }, }, @@ -785,7 +785,7 @@ def fetch_weather(location: str) -> str: "attributes": { "gen_ai.system": "az.ai.agents", "gen_ai.thread.id": "*", - # "gen_ai.agent.id": "*", - workaround for https://github.com/Azure/azure-sdk-for-python/issues/40086 + "gen_ai.agent.id": "*", "gen_ai.thread.run.id": "*", "gen_ai.message.id": "*", "gen_ai.message.status": "completed", @@ -953,11 +953,11 @@ def fetch_weather(location: str) -> str: "attributes": { "gen_ai.system": "az.ai.agents", "gen_ai.thread.id": "*", - # "gen_ai.agent.id": "*", - workaround for https://github.com/Azure/azure-sdk-for-python/issues/40086 + "gen_ai.agent.id": "*", "gen_ai.thread.run.id": "*", - "gen_ai.message.status": "completed", - "gen_ai.usage.input_tokens": "+", - "gen_ai.usage.output_tokens": "+", + "gen_ai.message.status": "requires_action", + # "gen_ai.usage.input_tokens": "+", # not available at the moment + # "gen_ai.usage.output_tokens": "+", # not available at the moment "gen_ai.event.content": '{"tool_calls": [{"id": "*", "type": "function"}]}', }, }, @@ -966,7 +966,7 @@ def fetch_weather(location: str) -> str: "attributes": { "gen_ai.system": "az.ai.agents", "gen_ai.thread.id": "*", - # "gen_ai.agent.id": "*", - workaround for https://github.com/Azure/azure-sdk-for-python/issues/40086 + "gen_ai.agent.id": "*", "gen_ai.thread.run.id": "*", "gen_ai.message.id": "*", "gen_ai.message.status": "completed", diff --git a/sdk/ai/azure-ai-projects/tests/telemetry/test_ai_agents_instrumentor_async.py b/sdk/ai/azure-ai-projects/tests/telemetry/test_ai_agents_instrumentor_async.py index cd1569de8e34..804a4fc287fb 100644 --- a/sdk/ai/azure-ai-projects/tests/telemetry/test_ai_agents_instrumentor_async.py +++ b/sdk/ai/azure-ai-projects/tests/telemetry/test_ai_agents_instrumentor_async.py @@ -565,11 +565,11 @@ def fetch_weather(location: str) -> str: "attributes": { "gen_ai.system": "az.ai.agents", "gen_ai.thread.id": "*", - # "gen_ai.agent.id": "*", - workaround for https://github.com/Azure/azure-sdk-for-python/issues/40086 + "gen_ai.agent.id": "*", "gen_ai.thread.run.id": "*", - "gen_ai.message.status": "completed", - "gen_ai.usage.input_tokens": "+", - "gen_ai.usage.output_tokens": "+", + "gen_ai.message.status": "requires_action", + # "gen_ai.usage.input_tokens": "+", # not available at the moment + # "gen_ai.usage.output_tokens": "+", # not available at the moment "gen_ai.event.content": '{"tool_calls": [{"id": "*", "type": "function", "function": {"name": "fetch_weather", "arguments": {"location": "New York"}}}]}', }, }, @@ -578,7 +578,7 @@ def fetch_weather(location: str) -> str: "attributes": { "gen_ai.system": "az.ai.agents", "gen_ai.thread.id": "*", - # "gen_ai.agent.id": "*", - workaround for https://github.com/Azure/azure-sdk-for-python/issues/40086 + "gen_ai.agent.id": "*", "gen_ai.thread.run.id": "*", "gen_ai.message.id": "*", "gen_ai.message.status": "completed", @@ -785,11 +785,11 @@ def fetch_weather(location: str) -> str: "attributes": { "gen_ai.system": "az.ai.agents", "gen_ai.thread.id": "*", - # "gen_ai.agent.id": "*", - workaround for https://github.com/Azure/azure-sdk-for-python/issues/40086 + "gen_ai.agent.id": "*", "gen_ai.thread.run.id": "*", - "gen_ai.message.status": "completed", - "gen_ai.usage.input_tokens": "+", - "gen_ai.usage.output_tokens": "+", + "gen_ai.message.status": "requires_action", + # "gen_ai.usage.input_tokens": "+", # not available at the moment + # "gen_ai.usage.output_tokens": "+", # not available at the moment "gen_ai.event.content": '{"tool_calls": [{"id": "*", "type": "function"}]}', }, }, @@ -798,7 +798,7 @@ def fetch_weather(location: str) -> str: "attributes": { "gen_ai.system": "az.ai.agents", "gen_ai.thread.id": "*", - # "gen_ai.agent.id": "*", - workaround for https://github.com/Azure/azure-sdk-for-python/issues/40086 + "gen_ai.agent.id": "*", "gen_ai.thread.run.id": "*", "gen_ai.message.id": "*", "gen_ai.message.status": "completed", From f6fbef58a651ffef874e02f54ada73e9fcd1fc7f Mon Sep 17 00:00:00 2001 From: Marko Hietala Date: Thu, 20 Mar 2025 16:34:43 -0500 Subject: [PATCH 09/10] adding tracing for list_run_steps --- sdk/ai/azure-ai-projects/README.md | 9 + .../agents/_ai_agents_instrumentor.py | 232 +++++++++++++++++- .../ai/projects/telemetry/agents/_utils.py | 8 + 3 files changed, 240 insertions(+), 9 deletions(-) diff --git a/sdk/ai/azure-ai-projects/README.md b/sdk/ai/azure-ai-projects/README.md index 7b195a4a051b..a967cd96f44d 100644 --- a/sdk/ai/azure-ai-projects/README.md +++ b/sdk/ai/azure-ai-projects/README.md @@ -1333,6 +1333,15 @@ In addition, you might find helpful to see the tracing logs in console. You can ```python project_client.telemetry.enable(destination=sys.stdout) ``` + +#### How to get traces for service-side tool calls + +Additional traces related to available run steps, which include service-side tool calls, will be recorded when tracing is enabled and the run steps are retrieved in your code: +```python +steps = project_client.agents.list_run_steps(thread_id=thread.id, run_id=run.id) +``` +You do not have to iterate over the steps in your code to get them traced. Content recording has to be enabled for the tool call details to be included in the traces. + #### How to trace your own functions The decorator `trace_function` is provided for tracing your own function calls using OpenTelemetry. By default the function name is used as the name for the span. Alternatively you can provide the name for the span as a parameter to the decorator. diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py index 1d98a6bef83b..4a7a2308cb11 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_ai_agents_instrumentor.py @@ -50,6 +50,13 @@ GEN_AI_THREAD_RUN_STATUS, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, + GEN_AI_CREATED_AT, + GEN_AI_COMPLETED_AT, + GEN_AI_CANCELLED_AT, + GEN_AI_FAILED_AT, + GEN_AI_RUN_STEP_STATUS, + GEN_AI_RUN_STEP_LAST_ERROR, + GEN_AI_RUN_STEP_DETAILS, OperationName, start_span, ) @@ -254,6 +261,12 @@ def _create_event_attributes( thread_run_id: Optional[str] = None, message_id: Optional[str] = None, message_status: Optional[str] = None, + run_step_status: Optional[str] = None, + created_at: Optional[int] = None, + completed_at: Optional[int] = None, + cancelled_at: Optional[int] = None, + failed_at: Optional[int] = None, + run_step_last_error: Optional[str] = None, usage: Optional[_models.RunStepCompletionUsage] = None, ) -> Dict[str, Any]: attrs: Dict[str, Any] = {GEN_AI_SYSTEM: AZ_AI_AGENT_SYSTEM} @@ -272,6 +285,24 @@ def _create_event_attributes( if message_status: attrs[GEN_AI_MESSAGE_STATUS] = self._status_to_string(message_status) + if run_step_status: + attrs[GEN_AI_RUN_STEP_STATUS] = self._status_to_string(run_step_status) + + if created_at: + attrs[GEN_AI_CREATED_AT] = created_at + + if completed_at: + attrs[GEN_AI_COMPLETED_AT] = completed_at + + if cancelled_at: + attrs[GEN_AI_CANCELLED_AT] = cancelled_at + + if failed_at: + attrs[GEN_AI_FAILED_AT] = failed_at + + if run_step_last_error: + attrs[GEN_AI_RUN_STEP_LAST_ERROR] = run_step_last_error + if usage: attrs[GEN_AI_USAGE_INPUT_TOKENS] = usage.prompt_tokens attrs[GEN_AI_USAGE_OUTPUT_TOKENS] = usage.completion_tokens @@ -306,6 +337,105 @@ def add_thread_message_event( usage=usage, ) + def add_run_step_event(self, span, step: RunStep) -> None: + if step["type"] == "message_creation": + self._add_message_creation_run_step_event( + span, + thread_id=step["thread_id"], + thread_run_id=step["run_id"], + agent_id=step["assistant_id"], + created_at=step["created_at"], + run_step_status=step["status"], + completed_at=step["completed_at"], + cancelled_at=step["cancelled_at"], + failed_at=step["failed_at"], + run_step_last_error=step["last_error"], + message_id=step["step_details"]["message_creation"]["message_id"], + usage=step["usage"], + ) + elif step["type"] == "tool_calls": + step_details = None + if _trace_agents_content: + step_details = step["step_details"]["tool_calls"] + + self._add_tool_call_run_step_event( + span, + thread_id=step["thread_id"], + thread_run_id=step["run_id"], + agent_id=step["assistant_id"], + created_at=step["created_at"], + run_step_status=step["status"], + completed_at=step["completed_at"], + cancelled_at=step["cancelled_at"], + failed_at=step["failed_at"], + run_step_last_error=step["last_error"], + usage=step["usage"], + run_step_details=step_details, + ) + + def _add_message_creation_run_step_event( + self, + span, + thread_id: Optional[str] = None, + thread_run_id: Optional[str] = None, + agent_id: Optional[str] = None, + created_at: Optional[int] = None, + run_step_status: Optional[str] = None, + completed_at: Optional[int] = None, + cancelled_at: Optional[int] = None, + failed_at: Optional[int] = None, + run_step_last_error: Optional[str] = None, + message_id: Optional[str] = None, + usage: Optional[_models.RunStepCompletionUsage] = None, + ) -> None: + attributes = self._create_event_attributes( + thread_id=thread_id, + agent_id=agent_id, + thread_run_id=thread_run_id, + message_id=message_id, + run_step_status=run_step_status, + created_at=created_at, + completed_at=completed_at, + cancelled_at=cancelled_at, + failed_at=failed_at, + run_step_last_error=run_step_last_error, + usage=usage, + ) + span.span_instance.add_event(name="gen_ai.run_step.message_creation", attributes=attributes) + + def _add_tool_call_run_step_event( + self, + span, + thread_id: Optional[str] = None, + thread_run_id: Optional[str] = None, + agent_id: Optional[str] = None, + created_at: Optional[int] = None, + run_step_status: Optional[str] = None, + completed_at: Optional[int] = None, + cancelled_at: Optional[int] = None, + failed_at: Optional[int] = None, + run_step_last_error: Optional[str] = None, + usage: Optional[_models.RunStepCompletionUsage] = None, + run_step_details: Optional[int] = None, + ) -> None: + attributes = self._create_event_attributes( + thread_id=thread_id, + agent_id=agent_id, + thread_run_id=thread_run_id, + run_step_status=run_step_status, + created_at=created_at, + completed_at=completed_at, + cancelled_at=cancelled_at, + failed_at=failed_at, + run_step_last_error=run_step_last_error, + usage=usage, + ) + + if run_step_details: + attributes[GEN_AI_RUN_STEP_DETAILS] = json.dumps(str(run_step_details), ensure_ascii=False) + + span.span_instance.add_event(name="gen_ai.run_step.tool_calls", attributes=attributes) + def _add_message_event( self, span, @@ -423,7 +553,7 @@ def _add_tool_assistant_message_event(self, span, step: RunStep) -> None: def _add_tool_event_from_thread_run(self, span, run: ThreadRun) -> None: tool_calls = [] - for t in run.required_action.submit_tool_outputs.tool_calls: # type: ignore + for t in run.required_action.submit_tool_outputs.tool_calls: # type: ignore try: parsed_arguments = json.loads(t.function.arguments) except json.JSONDecodeError: @@ -608,6 +738,9 @@ def start_create_thread_span( def start_list_messages_span(self, project_name: str, thread_id: Optional[str] = None) -> "Optional[AbstractSpan]": return start_span(OperationName.LIST_MESSAGES, project_name, thread_id=thread_id) + def start_list_run_steps_span(self, project_name: str, run_id: Optional[str] = None, thread_id: Optional[str] = None) -> "Optional[AbstractSpan]": + return start_span(OperationName.LIST_RUN_STEPS, project_name, run_id=run_id, thread_id=thread_id) + def trace_create_agent(self, function, *args, **kwargs): project_name = args[ # pylint: disable=protected-access # pyright: ignore [reportFunctionMemberAccess] 0 @@ -1248,6 +1381,74 @@ def trace_list_messages(self, function, *args, **kwargs): return result + def trace_list_run_steps(self, function, *args, **kwargs): + project_name = args[ # pylint: disable=protected-access # pyright: ignore [reportFunctionMemberAccess] + 0 + ]._config.project_name + run_id = kwargs.get("run_id") + thread_id = kwargs.get("thread_id") + + span = self.start_list_run_steps_span(project_name=project_name, run_id=run_id, thread_id=thread_id) + + if span is None: + return function(*args, **kwargs) + + with span: + try: + result = function(*args, **kwargs) + if hasattr(result, "data") and result.data is not None: + for step in result.data: + self.add_run_step_event(span, step) + + except Exception as exc: + # Set the span status to error + if isinstance(span.span_instance, Span): # pyright: ignore [reportPossiblyUnboundVariable] + span.span_instance.set_status( + StatusCode.ERROR, # pyright: ignore [reportPossiblyUnboundVariable] + description=str(exc), + ) + module = getattr(exc, "__module__", "") + module = module if module != "builtins" else "" + error_type = f"{module}.{type(exc).__name__}" if module else type(exc).__name__ + self._set_attributes(span, ("error.type", error_type)) + raise + + return result + + async def trace_list_run_steps_async(self, function, *args, **kwargs): + project_name = args[ # pylint: disable=protected-access # pyright: ignore [reportFunctionMemberAccess] + 0 + ]._config.project_name + run_id = kwargs.get("run_id") + thread_id = kwargs.get("thread_id") + + span = self.start_list_run_steps_span(project_name=project_name, run_id=run_id, thread_id=thread_id) + + if span is None: + return function(*args, **kwargs) + + with span: + try: + result = await function(*args, **kwargs) + if hasattr(result, "data") and result.data is not None: + for step in result.data: + self.add_run_step_event(span, step) + + except Exception as exc: + # Set the span status to error + if isinstance(span.span_instance, Span): # pyright: ignore [reportPossiblyUnboundVariable] + span.span_instance.set_status( + StatusCode.ERROR, # pyright: ignore [reportPossiblyUnboundVariable] + description=str(exc), + ) + module = getattr(exc, "__module__", "") + module = module if module != "builtins" else "" + error_type = f"{module}.{type(exc).__name__}" if module else type(exc).__name__ + self._set_attributes(span, ("error.type", error_type)) + raise + + return result + async def trace_list_messages_async(self, function, *args, **kwargs): project_name = args[ # pylint: disable=protected-access # pyright: ignore [reportFunctionMemberAccess] 0 @@ -1395,6 +1596,8 @@ def inner(*args, **kwargs): # pylint: disable=R0911 if class_function_name.startswith("AgentsOperations.list_messages"): kwargs.setdefault("merge_span", True) return self.trace_list_messages(function, *args, **kwargs) + if class_function_name.startswith("AgentsOperations.list_run_steps"): + return self.trace_list_run_steps(function, *args, **kwargs) if class_function_name.startswith("AgentRunStream.__exit__"): return self.handle_run_stream_exit(function, *args, **kwargs) # Handle the default case (if the function name does not match) @@ -1463,6 +1666,9 @@ async def inner(*args, **kwargs): # pylint: disable=R0911 if class_function_name.startswith("AgentsOperations.list_messages"): kwargs.setdefault("merge_span", True) return await self.trace_list_messages_async(function, *args, **kwargs) + if class_function_name.startswith("AgentsOperations.list_run_steps"): + kwargs.setdefault("merge_span", True) + return await self.trace_list_run_steps_async(function, *args, **kwargs) if class_function_name.startswith("AsyncAgentRunStream.__aexit__"): return self.handle_run_stream_exit(function, *args, **kwargs) # Handle the default case (if the function name does not match) @@ -1516,6 +1722,7 @@ def _agents_apis(self): ), ("azure.ai.projects.operations", "AgentsOperations", "create_stream", TraceType.AGENTS, "create_stream"), ("azure.ai.projects.operations", "AgentsOperations", "list_messages", TraceType.AGENTS, "list_messages"), + ("azure.ai.projects.operations", "AgentsOperations", "list_run_steps", TraceType.AGENTS, "list_run_steps"), ("azure.ai.projects.models", "AgentRunStream", "__exit__", TraceType.AGENTS, "__exit__"), ) async_apis = ( @@ -1577,6 +1784,13 @@ def _agents_apis(self): TraceType.AGENTS, "list_messages", ), + ( + "azure.ai.projects.aio.operations", + "AgentsOperations", + "list_run_steps", + TraceType.AGENTS, + "list_run_steps", + ), ("azure.ai.projects.models", "AsyncAgentRunStream", "__aexit__", TraceType.AGENTS, "__aexit__"), ) return sync_apis, async_apis @@ -1714,13 +1928,13 @@ def on_thread_message(self, message: "ThreadMessage") -> None: # type: ignore[f if message.status in {"completed", "incomplete"}: self.last_message = message - return retval # type: ignore + return retval # type: ignore def on_thread_run(self, run: "ThreadRun") -> None: # type: ignore[func-returns-value] retval = None if run.status == "requires_action" and isinstance(run.required_action, SubmitToolOutputsAction): - self.instrumentor._add_tool_event_from_thread_run( # pylint: disable=protected-access # pyright: ignore [reportFunctionMemberAccess] + self.instrumentor._add_tool_event_from_thread_run( # pylint: disable=protected-access # pyright: ignore [reportFunctionMemberAccess] self.span, run ) @@ -1728,7 +1942,7 @@ def on_thread_run(self, run: "ThreadRun") -> None: # type: ignore[func-returns- retval = self.inner_handler.on_thread_run(run) # type: ignore self.last_run = run - return retval # type: ignore + return retval # type: ignore def on_run_step(self, step: "RunStep") -> None: # type: ignore[func-returns-value] retval = None @@ -1740,7 +1954,7 @@ def on_run_step(self, step: "RunStep") -> None: # type: ignore[func-returns-val self.instrumentor.add_thread_message_event(self.span, cast(ThreadMessage, self.last_message), step.usage) self.last_message = None - return retval # type: ignore + return retval # type: ignore def on_run_step_delta(self, delta: "RunStepDeltaChunk") -> None: # type: ignore[func-returns-value] if self.inner_handler: @@ -1805,13 +2019,13 @@ async def on_thread_message(self, message: "ThreadMessage") -> None: # type: ig if message.status in {"completed", "incomplete"}: self.last_message = message - return retval # type: ignore + return retval # type: ignore async def on_thread_run(self, run: "ThreadRun") -> None: # type: ignore[func-returns-value] retval = None if run.status == "requires_action" and isinstance(run.required_action, SubmitToolOutputsAction): - self.instrumentor._add_tool_event_from_thread_run( # pylint: disable=protected-access # pyright: ignore [reportFunctionMemberAccess] + self.instrumentor._add_tool_event_from_thread_run( # pylint: disable=protected-access # pyright: ignore [reportFunctionMemberAccess] self.span, run ) @@ -1819,7 +2033,7 @@ async def on_thread_run(self, run: "ThreadRun") -> None: # type: ignore[func-re retval = await self.inner_handler.on_thread_run(run) # type: ignore self.last_run = run - return retval # type: ignore + return retval # type: ignore async def on_run_step(self, step: "RunStep") -> None: # type: ignore[func-returns-value] retval = None @@ -1831,7 +2045,7 @@ async def on_run_step(self, step: "RunStep") -> None: # type: ignore[func-retur self.instrumentor.add_thread_message_event(self.span, cast(ThreadMessage, self.last_message), step.usage) self.last_message = None - return retval # type: ignore + return retval # type: ignore async def on_run_step_delta(self, delta: "RunStepDeltaChunk") -> None: # type: ignore[func-returns-value] if self.inner_handler: diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_utils.py b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_utils.py index bdc18e1381e8..11af0a8f1da4 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_utils.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/agents/_utils.py @@ -42,6 +42,13 @@ GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens" GEN_AI_SYSTEM_MESSAGE = "gen_ai.system.message" GEN_AI_EVENT_CONTENT = "gen_ai.event.content" +GEN_AI_CREATED_AT = "gen_ai.created_at" +GEN_AI_COMPLETED_AT = "gen_ai.completed_at" +GEN_AI_CANCELLED_AT = "gen_ai.cancelled_at" +GEN_AI_FAILED_AT = "gen_ai.failed_at" +GEN_AI_RUN_STEP_STATUS = "gen_ai.run_step.status" +GEN_AI_RUN_STEP_LAST_ERROR = "gen_ai.run_step.last_error" +GEN_AI_RUN_STEP_DETAILS = "gen_ai.run_step.details" ERROR_TYPE = "error.type" @@ -52,6 +59,7 @@ class OperationName(Enum): START_THREAD_RUN = "start_thread_run" EXECUTE_TOOL = "execute_tool" LIST_MESSAGES = "list_messages" + LIST_RUN_STEPS = "list_run_steps" SUBMIT_TOOL_OUTPUTS = "submit_tool_outputs" PROCESS_THREAD_RUN = "process_thread_run" From 783cd3bec180cf80b5587440d5a1b2a28f282da3 Mon Sep 17 00:00:00 2001 From: Marko Hietala Date: Thu, 20 Mar 2025 16:39:39 -0500 Subject: [PATCH 10/10] update changelog --- sdk/ai/azure-ai-projects/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/ai/azure-ai-projects/CHANGELOG.md b/sdk/ai/azure-ai-projects/CHANGELOG.md index be5ec9468a27..9efe3e46714d 100644 --- a/sdk/ai/azure-ai-projects/CHANGELOG.md +++ b/sdk/ai/azure-ai-projects/CHANGELOG.md @@ -4,6 +4,8 @@ ### Features added +* Added support for tracing run steps when agents.list_run_steps is called. This can be used to trace service-side tool calls. + ### Sample updates ### Bugs Fixed