From 68f8d49e9a9af940507e56a5161a62a9d8930977 Mon Sep 17 00:00:00 2001 From: QuentinBisson Date: Mon, 15 Jun 2026 22:43:16 +0100 Subject: [PATCH] fix(anthropic): guard tools event emission with truthiness check tools=NOT_GIVEN (the anthropic sentinel) is not None, so the previous is-not-None guard caused the sentinel to leak into the OTLP log body on tool-free requests. Use a truthiness check, matching the system check two lines above. --- .../anthropic/event_emitter.py | 2 +- .../tests/test_semconv_span_attrs.py | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/opentelemetry-instrumentation-anthropic/opentelemetry/instrumentation/anthropic/event_emitter.py b/packages/opentelemetry-instrumentation-anthropic/opentelemetry/instrumentation/anthropic/event_emitter.py index b467d2b983..0fc8514d49 100644 --- a/packages/opentelemetry-instrumentation-anthropic/opentelemetry/instrumentation/anthropic/event_emitter.py +++ b/packages/opentelemetry-instrumentation-anthropic/opentelemetry/instrumentation/anthropic/event_emitter.py @@ -51,7 +51,7 @@ def emit_input_events(event_logger: Optional[Logger], kwargs): MessageEvent(content=message.get("content"), role=message.get("role")), event_logger, ) - if kwargs.get("tools") is not None: + if kwargs.get("tools"): emit_event( MessageEvent(content={"tools": kwargs.get("tools")}, role="user"), event_logger, diff --git a/packages/opentelemetry-instrumentation-anthropic/tests/test_semconv_span_attrs.py b/packages/opentelemetry-instrumentation-anthropic/tests/test_semconv_span_attrs.py index f99385702f..6a3f6688e8 100644 --- a/packages/opentelemetry-instrumentation-anthropic/tests/test_semconv_span_attrs.py +++ b/packages/opentelemetry-instrumentation-anthropic/tests/test_semconv_span_attrs.py @@ -1566,6 +1566,43 @@ def test_event_attributes_uses_provider_name_not_system(): "Deprecated GEN_AI_SYSTEM should not be in EVENT_ATTRIBUTES" +def test_emit_input_events_not_given_tools_emits_no_tools_event(): + """tools=NOT_GIVEN must not produce a tools MessageEvent. + + anthropic.NOT_GIVEN is not None, so the previous `is not None` guard + caused the sentinel to leak into the OTLP log body on tool-free requests. + """ + import anthropic + from unittest.mock import MagicMock, patch + from opentelemetry.instrumentation.anthropic.event_emitter import emit_input_events + + event_logger = MagicMock() + with patch( + "opentelemetry.instrumentation.anthropic.event_emitter.should_emit_events", + return_value=True, + ), patch( + "opentelemetry.instrumentation.anthropic.event_emitter.should_send_prompts", + return_value=True, + ): + emit_input_events( + event_logger, + { + "messages": [{"role": "user", "content": "hello"}], + "tools": anthropic.NOT_GIVEN, + }, + ) + + # Only the user message event should be emitted — no tools event. + assert event_logger.emit.call_count == 1, ( + f"Expected 1 emit call (user message only), got {event_logger.emit.call_count}; " + f"NOT_GIVEN sentinel must not trigger a tools MessageEvent" + ) + emitted_event_name = event_logger.emit.call_args_list[0].args[0].event_name + assert emitted_event_name == "gen_ai.user.message", ( + f"Expected gen_ai.user.message, got {emitted_event_name}" + ) + + # --------------------------------------------------------------------------- # Regression: streaming must set token usage from API data even when # enrich_token_usage is False (the default). Fixes #3949.