|
9 | 9 | from contextlib import contextmanager, nullcontext |
10 | 10 | from typing import Optional, Dict, Any |
11 | 11 |
|
12 | | -from opentelemetry import baggage as otel_baggage |
13 | | -from opentelemetry import context as otel_context |
14 | 12 | from opentelemetry import trace |
15 | 13 | from opentelemetry.trace import Status, StatusCode, Span |
16 | 14 |
|
|
19 | 17 | get_tenant_id, |
20 | 18 | get_propagated_attributes, |
21 | 19 | _propagated_attrs_var, |
| 20 | + _invoke_agent_identity_var, |
22 | 21 | ) |
23 | 22 | from sap_cloud_sdk.core.telemetry.constants import ATTR_SAP_TENANT_ID |
24 | 23 |
|
@@ -49,29 +48,30 @@ def _propagate_attributes(attrs: Dict[str, Any]): |
49 | 48 |
|
50 | 49 |
|
51 | 50 | @contextmanager |
52 | | -def _otel_baggage_for_invoke_agent(span_attrs: Dict[str, Any]): |
53 | | - """Mirror gen_ai.agent.* identity into W3C Baggage for the duration of the context. |
| 51 | +def _invoke_agent_identity_scope(span_attrs: Dict[str, Any]): |
| 52 | + """Push gen_ai.agent.{name,id,description} onto a ContextVar for the duration of the context. |
54 | 53 |
|
55 | | - Third-party instrumentations (e.g. LangGraph / Traceloop) create spans without merging |
56 | | - :func:`get_propagated_attributes`. :class:`~opentelemetry.processor.baggage.BaggageSpanProcessor` |
57 | | - (registered by :func:`sap_cloud_sdk.core.telemetry.auto_instrument.auto_instrument`) copies |
58 | | - baggage onto every started span, so agent id/name stay consistent on those spans. |
| 54 | + Third-party instrumentations create spans without merging :func:`get_propagated_attributes`. |
| 55 | + :class:`~sap_cloud_sdk.core.telemetry.invoke_agent_identity_processor.InvokeAgentIdentitySpanProcessor` |
| 56 | + (registered by :func:`sap_cloud_sdk.core.telemetry.auto_instrument.auto_instrument`) copies these |
| 57 | + values onto every started span while the scope is active, without using W3C Baggage. |
59 | 58 | """ |
60 | 59 | keys = ( |
61 | 60 | _ATTR_GEN_AI_AGENT_NAME, |
62 | 61 | _ATTR_GEN_AI_AGENT_ID, |
63 | 62 | _ATTR_GEN_AI_AGENT_DESCRIPTION, |
64 | 63 | ) |
65 | | - ctx = otel_context.get_current() |
66 | | - for key in keys: |
67 | | - val = span_attrs.get(key) |
68 | | - if val is not None: |
69 | | - ctx = otel_baggage.set_baggage(key, str(val), ctx) |
70 | | - token = otel_context.attach(ctx) |
| 64 | + patch_dict = {k: str(span_attrs[k]) for k in keys if span_attrs.get(k) is not None} |
| 65 | + if not patch_dict: |
| 66 | + yield |
| 67 | + return |
| 68 | + prev = _invoke_agent_identity_var.get() |
| 69 | + merged: Dict[str, str] = {**(prev or {}), **patch_dict} |
| 70 | + token = _invoke_agent_identity_var.set(merged) |
71 | 71 | try: |
72 | 72 | yield |
73 | 73 | finally: |
74 | | - otel_context.detach(token) |
| 74 | + _invoke_agent_identity_var.reset(token) |
75 | 75 |
|
76 | 76 |
|
77 | 77 | @contextmanager |
@@ -309,9 +309,10 @@ def invoke_agent_span( |
309 | 309 | attributes: Optional dict of extra attributes to add or override on the span. |
310 | 310 | propagate: If True, this span's attributes are passed to all nested spans |
311 | 311 | within its scope as the lowest-priority layer. Additionally, |
312 | | - ``gen_ai.agent.{name,id,description}`` are attached as OpenTelemetry |
313 | | - **Baggage** so spans created by external instrumentations still receive |
314 | | - those attributes when BaggageSpanProcessor is active. |
| 312 | + ``gen_ai.agent.{name,id,description}`` are stored in a ContextVar and |
| 313 | + copied onto every nested span by |
| 314 | + :class:`~sap_cloud_sdk.core.telemetry.invoke_agent_identity_processor.InvokeAgentIdentitySpanProcessor` |
| 315 | + when it is registered (e.g. via :func:`sap_cloud_sdk.core.telemetry.auto_instrument.auto_instrument`). |
315 | 316 |
|
316 | 317 | Yields: |
317 | 318 | The created Span (e.g. to set usage, response attributes). |
@@ -356,11 +357,11 @@ def invoke_agent_span( |
356 | 357 | span_attrs = {**propagated, **(attributes or {}), **base_attrs} |
357 | 358 |
|
358 | 359 | ctx_prop = _propagate_attributes(span_attrs) if propagate else nullcontext() |
359 | | - ctx_baggage = ( |
360 | | - _otel_baggage_for_invoke_agent(span_attrs) if propagate else nullcontext() |
| 360 | + ctx_identity = ( |
| 361 | + _invoke_agent_identity_scope(span_attrs) if propagate else nullcontext() |
361 | 362 | ) |
362 | 363 | with ctx_prop: |
363 | | - with ctx_baggage: |
| 364 | + with ctx_identity: |
364 | 365 | with tracer.start_as_current_span( |
365 | 366 | span_name, |
366 | 367 | kind=kind, |
|
0 commit comments