Skip to content
Merged
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
28 changes: 14 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ classifiers = [
dependencies = [
"azure-core<2.0.0,>=1.28.0",
"azure-core-tracing-opentelemetry~=1.0.0b11",
"azure-monitor-opentelemetry-exporter~=1.0.0b53",
"opentelemetry-sdk==1.40",
"opentelemetry-api==1.40",
"opentelemetry-exporter-otlp-proto-http==1.40",
"opentelemetry-instrumentation==0.61b0",
"opentelemetry-instrumentation-django==0.61b0",
"opentelemetry-instrumentation-fastapi==0.61b0",
"opentelemetry-instrumentation-flask==0.61b0",
"opentelemetry-instrumentation-httpx==0.61b0",
"opentelemetry-instrumentation-psycopg2==0.61b0",
"opentelemetry-instrumentation-requests==0.61b0",
"opentelemetry-instrumentation-urllib==0.61b0",
"opentelemetry-instrumentation-urllib3==0.61b0",
"opentelemetry-instrumentation-logging==0.61b0",
"azure-monitor-opentelemetry-exporter~=1.0.0b54",
"opentelemetry-sdk~=1.43.0",
"opentelemetry-api~=1.43.0",
"opentelemetry-exporter-otlp-proto-http~=1.43.0",
"opentelemetry-instrumentation>=0.64b0,<0.65b0",
"opentelemetry-instrumentation-django>=0.64b0,<0.65b0",
"opentelemetry-instrumentation-fastapi>=0.64b0,<0.65b0",
"opentelemetry-instrumentation-flask>=0.64b0,<0.65b0",
"opentelemetry-instrumentation-httpx>=0.64b0,<0.65b0",
"opentelemetry-instrumentation-psycopg2>=0.64b0,<0.65b0",
"opentelemetry-instrumentation-requests>=0.64b0,<0.65b0",
"opentelemetry-instrumentation-urllib>=0.64b0,<0.65b0",
"opentelemetry-instrumentation-urllib3>=0.64b0,<0.65b0",
"opentelemetry-instrumentation-logging>=0.64b0,<0.65b0",
"opentelemetry-instrumentation-openai-agents-v2==0.1.0",
"opentelemetry-instrumentation-openai-v2==2.3b0",
"opentelemetry-resource-detector-azure<1.0.0,>=0.1.5",
Expand Down
27 changes: 24 additions & 3 deletions src/microsoft/opentelemetry/_genai/main_agent/_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from opentelemetry.sdk.trace import ReadableSpan, Span
from opentelemetry.sdk.trace.export import SpanProcessor
from opentelemetry.trace import Span as SpanAPI
from opentelemetry.util.types import AttributeValue

# Each row: (target attribute on current span,
# primary source attribute on parent span,
Expand Down Expand Up @@ -108,15 +109,16 @@ def on_end(self, span: ReadableSpan) -> None:
if mutable is None:
return

# Build the attributes to write before touching the (now frozen) span.
updates: dict[str, AttributeValue] = {}

# Self-promotion: top-level invoke_agent spans copy their own
# gen_ai.agent.* → microsoft.gen_ai.main_agent.*
if attributes.get(GEN_AI_OPERATION_NAME_KEY) == INVOKE_AGENT_OPERATION_NAME:
for target, source in _SELF_COPY_TABLE:
value = attributes.get(source)
if value is not None:
mutable[target] = value
return

updates[target] = value
# Fallback propagation: re-read from the parent span whose attributes
# may have been set after this child was created (timing issue).
if parent is not None:
Expand All @@ -126,7 +128,26 @@ def on_end(self, span: ReadableSpan) -> None:
if value is None:
value = parent_attributes.get(fallback)
if value is not None:
updates[target] = value

if not updates:
return

# OTel SDK >= 1.43 freezes span attributes (``_immutable = True``) inside
# ``end()`` *before* invoking ``on_end``. Writing then raises ``TypeError``.
# Temporarily lift the freeze for our own synchronous writes and always
# restore it so the exported ``ReadableSpan`` snapshot stays frozen.
was_immutable = getattr(mutable, "_immutable", False)
if was_immutable:
mutable._immutable = False # pylint: disable=protected-access
try:
for target, value in updates.items():
mutable[target] = value
finally:
mutable._immutable = True # pylint: disable=protected-access
else:
for target, value in updates.items():
mutable[target] = value

def shutdown(self) -> None:
self._parent_spans.clear()
Expand Down
Loading