Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# Copied from opentelemetry-semantic-conventions to avoid dependency

# GenAI Agent attributes
GEN_AI_AGENT_ACTION_REF = "gen_ai.agent.action_ref"
GEN_AI_AGENT_DESCRIPTION = "gen_ai.agent.description"
GEN_AI_AGENT_ID = "gen_ai.agent.id"
GEN_AI_AGENT_NAME = "gen_ai.agent.name"
Expand Down Expand Up @@ -53,6 +54,7 @@ def trace_tool_span(
parent: Optional[Span] = None,
tool_description: Optional[str] = None,
tool_call_id: Optional[str] = None,
action_ref: Optional[str] = None,
) -> Generator[Span, Any, None]:
"""Context manager to create a span for tool execution following the
OpenTelemetry Semantic conventions for generative AI systems.
Expand All @@ -72,6 +74,7 @@ def trace_tool_span(
parent (Optional[Span]): The parent span to link this span to.
tool_description (Optional[str]): A description of the tool.
tool_call_id (Optional[str]): A unique identifier for the tool call.
action_ref (Optional[str]): A deterministic, recomputable handle for cross-producer audit correlation.
"""
if tracer is None:
tracer = trace.get_tracer("autogen-core")
Expand All @@ -84,6 +87,8 @@ def trace_tool_span(
span_attributes[GEN_AI_TOOL_DESCRIPTION] = tool_description
if tool_call_id is not None:
span_attributes[GEN_AI_TOOL_CALL_ID] = tool_call_id
if action_ref is not None:
span_attributes[GEN_AI_AGENT_ACTION_REF] = action_ref
with tracer.start_as_current_span(
f"{GenAiOperationNameValues.EXECUTE_TOOL.value} {tool_name}",
kind=SpanKind.INTERNAL,
Expand Down
48 changes: 48 additions & 0 deletions python/packages/autogen-core/tests/test_telemetry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Tests for telemetry module."""

from autogen_core._telemetry._genai import (
GEN_AI_AGENT_ACTION_REF,
trace_tool_span,
)


def test_trace_tool_span_with_action_ref() -> None:
"""Test that action_ref is set on the span when provided."""
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
from opentelemetry.sdk.trace.export import SimpleSpanProcessor

# Set up in-memory span exporter
exporter = InMemorySpanExporter()
provider = TracerProvider()
provider.add_span_processor(SimpleSpanProcessor(exporter))
tracer = provider.get_tracer("test")

with trace_tool_span("test_tool", tracer=tracer, action_ref="abc123"):
pass

spans = exporter.get_finished_spans()
assert len(spans) == 1
assert spans[0].attributes.get(GEN_AI_AGENT_ACTION_REF) == "abc123"


def test_trace_tool_span_without_action_ref() -> None:
"""Test that action_ref is not set on the span when not provided."""
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
from opentelemetry.sdk.trace.export import SimpleSpanProcessor

# Set up in-memory span exporter
exporter = InMemorySpanExporter()
provider = TracerProvider()
provider.add_span_processor(SimpleSpanProcessor(exporter))
tracer = provider.get_tracer("test")

with trace_tool_span("test_tool", tracer=tracer):
pass

spans = exporter.get_finished_spans()
assert len(spans) == 1
assert GEN_AI_AGENT_ACTION_REF not in (spans[0].attributes or {})