Skip to content

Commit 13c9e6b

Browse files
authored
adding support for workflow agent tracing (#43975)
* adding support for workflow agent tracing * updates based on review comments * disabling workflow tests
1 parent 8dcbf46 commit 13c9e6b

File tree

6 files changed

+1571
-118
lines changed

6 files changed

+1571
-118
lines changed

sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_ai_project_instrumentor.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -431,15 +431,20 @@ def _add_instructions_event(
431431
agent_id: Optional[str] = None,
432432
thread_id: Optional[str] = None,
433433
) -> None:
434+
# Early return if no instructions to trace
434435
if not instructions:
435436
return
436437

437438
event_body: Dict[str, Any] = {}
438-
if _trace_agents_content and (instructions or additional_instructions):
439-
if instructions and additional_instructions:
440-
event_body["text"] = f"{instructions} {additional_instructions}"
439+
if _trace_agents_content:
440+
# Combine instructions if both exist
441+
if additional_instructions:
442+
combined_text = f"{instructions} {additional_instructions}"
441443
else:
442-
event_body["text"] = instructions or additional_instructions
444+
combined_text = instructions
445+
446+
# Use standard content format
447+
event_body["content"] = [{"type": "text", "text": combined_text}]
443448

444449
attributes = self._create_event_attributes(agent_id=agent_id, thread_id=thread_id)
445450
attributes[GEN_AI_EVENT_CONTENT] = json.dumps(event_body, ensure_ascii=False)
@@ -481,6 +486,8 @@ def start_create_agent_span( # pylint: disable=too-many-locals
481486
reasoning_summary: Optional[str] = None,
482487
text: Optional[Any] = None, # pylint: disable=unused-argument
483488
structured_inputs: Optional[Any] = None,
489+
agent_type: Optional[str] = None,
490+
workflow_yaml: Optional[str] = None,
484491
) -> "Optional[AbstractSpan]":
485492
span = start_span(
486493
OperationName.CREATE_AGENT,
@@ -501,8 +508,19 @@ def start_create_agent_span( # pylint: disable=too-many-locals
501508
span.add_attribute(GEN_AI_AGENT_NAME, name)
502509
if description:
503510
span.add_attribute(GEN_AI_AGENT_DESCRIPTION, description)
511+
if agent_type:
512+
span.add_attribute("gen_ai.agent.type", agent_type)
513+
514+
# Add instructions event (if instructions exist)
504515
self._add_instructions_event(span, instructions, None)
505516

517+
# Add workflow YAML as event if content recording is enabled and workflow exists
518+
if _trace_agents_content and workflow_yaml:
519+
event_body: Dict[str, Any] = {"content": [{"type": "workflow", "workflow": workflow_yaml}]}
520+
attributes = self._create_event_attributes()
521+
attributes[GEN_AI_EVENT_CONTENT] = json.dumps(event_body, ensure_ascii=False)
522+
span.span_instance.add_event(name="gen_ai.agent.workflow", attributes=attributes)
523+
506524
return span
507525

508526
def start_create_thread_span(
@@ -543,7 +561,7 @@ def get_server_address_from_arg(self, arg: Any) -> Optional[Tuple[str, Optional[
543561

544562
def _create_agent_span_from_parameters(
545563
self, *args, **kwargs
546-
): # pylint: disable=too-many-statements,too-many-locals,docstring-missing-param
564+
): # pylint: disable=too-many-statements,too-many-locals,too-many-branches,docstring-missing-param
547565
"""Extract parameters and create span for create_agent tracing."""
548566
server_address_info = self.get_server_address_from_arg(args[0])
549567
server_address = server_address_info[0] if server_address_info else None
@@ -567,8 +585,23 @@ def _create_agent_span_from_parameters(
567585
structured_inputs = None
568586
description = definition.get("description")
569587
tool_resources = definition.get("tool_resources")
588+
workflow_yaml = definition.get("workflow") # Extract workflow YAML for workflow agents
570589
# toolset = definition.get("toolset")
571590

591+
# Determine agent type from definition
592+
# Check for workflow first (most specific)
593+
agent_type = None
594+
if workflow_yaml:
595+
agent_type = "workflow"
596+
elif instructions or model:
597+
# Prompt agent - identified by having instructions and/or a model.
598+
# Note: An agent with only a model (no instructions) is treated as a prompt agent,
599+
# though this is uncommon. Typically prompt agents have both model and instructions.
600+
agent_type = "prompt"
601+
else:
602+
# Unknown type - set to "unknown" to indicate we couldn't determine it
603+
agent_type = "unknown"
604+
572605
# Extract reasoning effort and summary from reasoning if available
573606
reasoning_effort = None
574607
reasoning_summary = None
@@ -645,6 +678,8 @@ def _create_agent_span_from_parameters(
645678
reasoning_summary=reasoning_summary,
646679
text=text,
647680
structured_inputs=structured_inputs,
681+
agent_type=agent_type,
682+
workflow_yaml=workflow_yaml,
648683
)
649684

650685
def trace_create_agent(self, function, *args, **kwargs):

0 commit comments

Comments
 (0)