Skip to content

Commit b791932

Browse files
CopilotnikhilNava
andcommitted
Fix suppression logic: check current span instead of parent, remove unused span_processor param
Co-authored-by: nikhilNava <211831449+nikhilNava@users.noreply.github.com>
1 parent a5a144b commit b791932

4 files changed

Lines changed: 16 additions & 61 deletions

File tree

libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def _configure_internal(
167167

168168
# Create BatchSpanProcessor with optimized settings
169169
batch_processor = BatchSpanProcessor(exporter, **batch_processor_kwargs)
170-
agent_processor = SpanProcessor(suppress_invoke_agent_input=suppress_invoke_agent_input)
170+
agent_processor = SpanProcessor()
171171

172172
tracer_provider.add_span_processor(batch_processor)
173173
tracer_provider.add_span_processor(agent_processor)

libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -225,22 +225,13 @@ def _post_with_retries(self, url: str, body: str, headers: dict[str, str]) -> bo
225225
# ------------- Payload mapping ------------------
226226

227227
def _build_export_request(self, spans: Sequence[ReadableSpan]) -> dict[str, Any]:
228-
# Build a map of span IDs to their operation names for parent lookups
229-
span_operation_map = {}
230-
if self._suppress_invoke_agent_input:
231-
for sp in spans:
232-
attrs = sp.attributes or {}
233-
operation_name = attrs.get(GEN_AI_OPERATION_NAME_KEY)
234-
if operation_name:
235-
span_operation_map[sp.context.span_id] = operation_name
236-
237228
# Group by instrumentation scope (name, version)
238229
scope_map: dict[tuple[str, str | None], list[dict[str, Any]]] = {}
239230

240231
for sp in spans:
241232
scope = sp.instrumentation_scope
242233
scope_key = (scope.name, scope.version)
243-
scope_map.setdefault(scope_key, []).append(self._map_span(sp, span_operation_map))
234+
scope_map.setdefault(scope_key, []).append(self._map_span(sp))
244235

245236
scope_spans: list[dict[str, Any]] = []
246237
for (name, version), mapped_spans in scope_map.items():
@@ -269,7 +260,7 @@ def _build_export_request(self, spans: Sequence[ReadableSpan]) -> dict[str, Any]
269260
]
270261
}
271262

272-
def _map_span(self, sp: ReadableSpan, span_operation_map: dict[int, str] = None) -> dict[str, Any]:
263+
def _map_span(self, sp: ReadableSpan) -> dict[str, Any]:
273264
ctx = sp.context
274265

275266
parent_span_id = None
@@ -279,14 +270,18 @@ def _map_span(self, sp: ReadableSpan, span_operation_map: dict[int, str] = None)
279270
# attributes
280271
attrs = dict(sp.attributes or {})
281272

282-
# Suppress input messages if configured and parent is an InvokeAgent span
283-
if self._suppress_invoke_agent_input and span_operation_map:
284-
# Check if parent span is an InvokeAgent span
285-
if sp.parent is not None and sp.parent.span_id != 0:
286-
parent_operation = span_operation_map.get(sp.parent.span_id)
287-
if parent_operation == INVOKE_AGENT_OPERATION_NAME:
288-
# Remove input messages attribute
289-
attrs.pop(GEN_AI_INPUT_MESSAGES_KEY, None)
273+
# Suppress input messages if configured and current span is an InvokeAgent span
274+
if self._suppress_invoke_agent_input:
275+
# Check if current span is an InvokeAgent span by:
276+
# 1. Span name starts with "invoke_agent"
277+
# 2. Has attribute gen_ai.operation.name set to INVOKE_AGENT_OPERATION_NAME
278+
operation_name = attrs.get(GEN_AI_OPERATION_NAME_KEY)
279+
if (
280+
sp.name.startswith(INVOKE_AGENT_OPERATION_NAME)
281+
and operation_name == INVOKE_AGENT_OPERATION_NAME
282+
):
283+
# Remove input messages attribute
284+
attrs.pop(GEN_AI_INPUT_MESSAGES_KEY, None)
290285

291286
# events
292287
events = []

libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/trace_processor/span_processor.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,8 @@
2424
class SpanProcessor(BaseSpanProcessor):
2525
"""Span processor that propagates every baggage key/value to span attributes."""
2626

27-
def __init__(self, suppress_invoke_agent_input: bool = False):
28-
"""Initialize the span processor.
29-
30-
Args:
31-
suppress_invoke_agent_input: If True, suppress input messages for spans
32-
that are children of InvokeAgent spans.
33-
"""
27+
def __init__(self):
3428
super().__init__()
35-
self._suppress_invoke_agent_input = suppress_invoke_agent_input
3629

3730
def on_start(self, span, parent_context=None):
3831
ctx = parent_context or context.get_current()
@@ -88,9 +81,4 @@ def on_start(self, span, parent_context=None):
8881
return super().on_start(span, parent_context)
8982

9083
def on_end(self, span):
91-
"""Called when a span ends.
92-
93-
Note: Input suppression for InvokeAgent scopes is handled by the exporter,
94-
not in this processor, because span attributes cannot be removed after they're set.
95-
"""
9684
super().on_end(span)

tests/observability/extensions/openai/test_prompt_suppression.py

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,11 @@
33
import unittest
44

55
from microsoft_agents_a365.observability.core.exporters.agent365_exporter import _Agent365Exporter
6-
from microsoft_agents_a365.observability.core.trace_processor.span_processor import SpanProcessor
76

87

98
class TestPromptSuppressionConfiguration(unittest.TestCase):
109
"""Unit tests for prompt suppression configuration in the core SDK."""
1110

12-
def test_span_processor_default_suppression_is_false(self):
13-
"""Test that the default value for suppress_invoke_agent_input is False in SpanProcessor."""
14-
processor = SpanProcessor()
15-
16-
self.assertFalse(
17-
processor._suppress_invoke_agent_input,
18-
"Default value for suppress_invoke_agent_input should be False",
19-
)
20-
21-
def test_span_processor_can_enable_suppression(self):
22-
"""Test that suppression can be enabled via SpanProcessor constructor."""
23-
processor = SpanProcessor(suppress_invoke_agent_input=True)
24-
25-
self.assertTrue(
26-
processor._suppress_invoke_agent_input,
27-
"suppress_invoke_agent_input should be True when explicitly set",
28-
)
29-
30-
def test_span_processor_can_disable_suppression(self):
31-
"""Test that suppression can be explicitly disabled via SpanProcessor constructor."""
32-
processor = SpanProcessor(suppress_invoke_agent_input=False)
33-
34-
self.assertFalse(
35-
processor._suppress_invoke_agent_input,
36-
"suppress_invoke_agent_input should be False when explicitly set",
37-
)
38-
3911
def test_exporter_default_suppression_is_false(self):
4012
"""Test that the default value for suppress_invoke_agent_input is False in exporter."""
4113
exporter = _Agent365Exporter(token_resolver=lambda x, y: "test")

0 commit comments

Comments
 (0)