From 03d69455559099d963e593bfdcd1b5c9e3385b13 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Thu, 17 Apr 2025 18:38:01 -0700 Subject: [PATCH 1/2] don't dump args and simplify code --- .../agents/_ai_agents_instrumentor.py | 63 +++---------------- .../ai/projects/telemetry/agents/_utils.py | 2 - 2 files changed, 9 insertions(+), 56 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 2cab106a6f8b..d5a30cbb74dd 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 @@ -55,8 +55,6 @@ GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_RUNSTEP_START_TIMESTAMP, GEN_AI_RUNSTEP_END_TIMESTAMP, - GEN_AI_RUNSTEP_CANCEL_TIMESTAMP, - GEN_AI_RUNSTEP_FAIL_TIMESTAMP, GEN_AI_RUN_STEP_STATUS, ERROR_MESSAGE, OperationName, @@ -306,17 +304,17 @@ def _create_event_attributes( if cancelled_at: if isinstance(cancelled_at, datetime): - attrs[GEN_AI_RUNSTEP_CANCEL_TIMESTAMP] = cancelled_at.isoformat() + attrs[GEN_AI_RUNSTEP_END_TIMESTAMP] = cancelled_at.isoformat() else: # fallback in case int or string gets passed - attrs[GEN_AI_RUNSTEP_CANCEL_TIMESTAMP] = str(cancelled_at) + attrs[GEN_AI_RUNSTEP_END_TIMESTAMP] = str(cancelled_at) if failed_at: if isinstance(failed_at, datetime): - attrs[GEN_AI_RUNSTEP_FAIL_TIMESTAMP] = failed_at.isoformat() + attrs[GEN_AI_RUNSTEP_END_TIMESTAMP] = failed_at.isoformat() else: # fallback in case int or string gets passed - attrs[GEN_AI_RUNSTEP_FAIL_TIMESTAMP] = failed_at.isoformat() + attrs[GEN_AI_RUNSTEP_END_TIMESTAMP] = str(failed_at) if run_step_last_error: attrs[ERROR_MESSAGE] = run_step_last_error.message @@ -356,32 +354,6 @@ def add_thread_message_event( usage=usage, ) - def _get_tool_details(self, tool: Any) -> Dict[str, Any]: - """ - Extracts tool details from a tool object dynamically and ensures the result is JSON-serializable. - - :param tool: The tool object (e.g., RunStepToolCallDetails). - :return: A dictionary containing the tool details. - """ - tool_details = {} - - # Dynamically extract attributes from the tool object - for attr in dir(tool): - # Skip private or special attributes - if not attr.startswith("_") and not callable(getattr(tool, attr)): - try: - value = getattr(tool, attr) - # Ensure the value is JSON-serializable - if isinstance(value, (str, int, float, bool, list, dict, type(None))): - tool_details[attr] = value - else: - # Convert non-serializable objects to strings - tool_details[attr] = str(value) - except Exception as e: - logging.warning("Error extracting attribute '%s': %s", attr, str(e)) - - return tool_details - def _process_tool_calls(self, step: RunStep) -> List[Dict[str, Any]]: """ Helper method to process tool calls and return a list of tool call dictionaries. @@ -408,41 +380,24 @@ def _process_tool_calls(self, step: RunStep) -> List[Dict[str, Any]]: }, } elif isinstance(t, RunStepCodeInterpreterToolCall): - try: - parsed_outputs = json.dumps(t.code_interpreter.outputs) - except Exception as e: - logging.warning("Error parsing code interpreter outputs: '%s'", str(e)) - parsed_outputs = {} - tool_call = { "id": t.id, "type": t.type, "code_interpreter": { "input": t.code_interpreter.input, - "output": parsed_outputs, + "outputs": [output.as_dict() for output in t.code_interpreter.outputs], }, } elif isinstance(t, RunStepBingGroundingToolCall): - bing_grounding = {} - try: - bing_grounding = json.dumps(t.bing_grounding) - except Exception as e: - logging.warning("Error parsing Bing grounding: '%s'", str(e)) - parsed_outputs = {} - tool_call = { "id": t.id, "type": t.type, t.type: { - "bing_grounding": bing_grounding + "bing_grounding": t.bing_grounding }, } else: - try: - tool_details = json.dumps(self._get_tool_details(t)) - except Exception as e: - logging.warning("Error parsing step details: '%s'", str(e)) - tool_details = "" + tool_details = t.as_dict()[t.type] tool_call = { "id": t.id, @@ -625,10 +580,10 @@ def _add_tool_event_from_thread_run(self, span, run: ThreadRun) -> None: ) if _trace_agents_content: - attributes[GEN_AI_EVENT_CONTENT] = json.dumps({"tool_calls": tool_calls}) + attributes[GEN_AI_EVENT_CONTENT] = json.dumps({"tool_calls": tool_calls}, ensure_ascii=False) 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}) + attributes[GEN_AI_EVENT_CONTENT] = json.dumps({"tool_calls": tool_calls_non_recording}, ensure_ascii=False) span.span_instance.add_event(name="gen_ai.assistant.message", attributes=attributes) def set_end_run(self, span: "AbstractSpan", run: Optional[ThreadRun]) -> None: 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 48bdd89a20e8..4023159a7dfa 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 @@ -44,8 +44,6 @@ GEN_AI_EVENT_CONTENT = "gen_ai.event.content" GEN_AI_RUNSTEP_START_TIMESTAMP = "gen_ai.run_step.start.timestamp" GEN_AI_RUNSTEP_END_TIMESTAMP = "gen_ai.run_step.end.timestamp" -GEN_AI_RUNSTEP_CANCEL_TIMESTAMP = "gen_ai.run_step.cancel.timestamp" -GEN_AI_RUNSTEP_FAIL_TIMESTAMP = "gen_ai.run_step.fail.timestamp" GEN_AI_RUN_STEP_STATUS = "gen_ai.run_step.status" ERROR_TYPE = "error.type" ERROR_MESSAGE = "error.message" From 2be99aef2757e1f7b8010c0cba0bdd4f4ca5ac53 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Thu, 17 Apr 2025 18:43:57 -0700 Subject: [PATCH 2/2] up --- .../agents/_ai_agents_instrumentor.py | 39 ++++++++----------- .../ai/projects/telemetry/agents/_utils.py | 4 +- 2 files changed, 18 insertions(+), 25 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 d5a30cbb74dd..6528b36b8aa6 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 @@ -53,8 +53,8 @@ GEN_AI_THREAD_RUN_STATUS, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, - GEN_AI_RUNSTEP_START_TIMESTAMP, - GEN_AI_RUNSTEP_END_TIMESTAMP, + GEN_AI_RUN_STEP_START_TIMESTAMP, + GEN_AI_RUN_STEP_END_TIMESTAMP, GEN_AI_RUN_STEP_STATUS, ERROR_MESSAGE, OperationName, @@ -290,31 +290,24 @@ def _create_event_attributes( if created_at: if isinstance(created_at, datetime): - attrs[GEN_AI_RUNSTEP_START_TIMESTAMP] = created_at.isoformat() + attrs[GEN_AI_RUN_STEP_START_TIMESTAMP] = created_at.isoformat() else: # fallback in case int or string gets passed - attrs[GEN_AI_RUNSTEP_START_TIMESTAMP] = str(created_at) + attrs[GEN_AI_RUN_STEP_START_TIMESTAMP] = str(created_at) + end_timestamp = None if completed_at: - if isinstance(completed_at, datetime): - attrs[GEN_AI_RUNSTEP_END_TIMESTAMP] = completed_at.isoformat() - else: - # fallback in case int or string gets passed - attrs[GEN_AI_RUNSTEP_END_TIMESTAMP] = str(completed_at) - - if cancelled_at: - if isinstance(cancelled_at, datetime): - attrs[GEN_AI_RUNSTEP_END_TIMESTAMP] = cancelled_at.isoformat() - else: - # fallback in case int or string gets passed - attrs[GEN_AI_RUNSTEP_END_TIMESTAMP] = str(cancelled_at) - - if failed_at: - if isinstance(failed_at, datetime): - attrs[GEN_AI_RUNSTEP_END_TIMESTAMP] = failed_at.isoformat() - else: - # fallback in case int or string gets passed - attrs[GEN_AI_RUNSTEP_END_TIMESTAMP] = str(failed_at) + end_timestamp = completed_at + elif cancelled_at: + end_timestamp = cancelled_at + elif failed_at: + end_timestamp = failed_at + + if isinstance(end_timestamp, datetime): + attrs[GEN_AI_RUN_STEP_END_TIMESTAMP] = end_timestamp.isoformat() + else: + # fallback in case int or string gets passed + attrs[GEN_AI_RUN_STEP_END_TIMESTAMP] = str(end_timestamp) if run_step_last_error: attrs[ERROR_MESSAGE] = run_step_last_error.message 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 4023159a7dfa..04d0e3b36801 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,8 +42,8 @@ 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_RUNSTEP_START_TIMESTAMP = "gen_ai.run_step.start.timestamp" -GEN_AI_RUNSTEP_END_TIMESTAMP = "gen_ai.run_step.end.timestamp" +GEN_AI_RUN_STEP_START_TIMESTAMP = "gen_ai.run_step.start.timestamp" +GEN_AI_RUN_STEP_END_TIMESTAMP = "gen_ai.run_step.end.timestamp" GEN_AI_RUN_STEP_STATUS = "gen_ai.run_step.status" ERROR_TYPE = "error.type" ERROR_MESSAGE = "error.message"