Skip to content

Commit cbc0444

Browse files
authored
fix(tracing): skip missing span attributes (#917)
1 parent 5c2e441 commit cbc0444

2 files changed

Lines changed: 39 additions & 3 deletions

File tree

tests/test_llm_attributes_extractors.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@
1414

1515
import json
1616
from types import SimpleNamespace
17-
from unittest.mock import Mock
17+
from unittest.mock import Mock, call
1818

1919
from google.adk.models.llm_request import LlmRequest
2020
from google.adk.tools.function_tool import FunctionTool
2121

2222
from veadk.tracing.telemetry.attributes.extractors.llm_attributes_extractors import (
2323
llm_gen_ai_request_functions,
24+
llm_gen_ai_usage_output_tokens,
2425
)
26+
from veadk.tracing.telemetry.attributes.extractors.types import ExtractorResponse
2527

2628

2729
def test_request_functions_reuses_adk_request_declaration(monkeypatch):
@@ -99,3 +101,35 @@ def test_request_functions_builds_missing_declaration_once():
99101
)
100102
assert parameters == {"type": "object"}
101103
get_declaration.assert_called_once_with()
104+
105+
106+
def test_missing_output_token_count_is_not_written_to_span():
107+
params = SimpleNamespace(
108+
llm_response=SimpleNamespace(
109+
usage_metadata=SimpleNamespace(candidates_token_count=None)
110+
)
111+
)
112+
response = llm_gen_ai_usage_output_tokens(params)
113+
span = Mock()
114+
115+
ExtractorResponse.update_span(span, "gen_ai.usage.output_tokens", response)
116+
117+
span.set_attribute.assert_not_called()
118+
119+
120+
def test_falsy_attribute_values_are_written_to_span():
121+
span = Mock()
122+
123+
ExtractorResponse.update_span(span, "zero", ExtractorResponse(content=0))
124+
ExtractorResponse.update_span(span, "false", ExtractorResponse(content=False))
125+
126+
assert span.set_attribute.call_args_list == [call("zero", 0), call("false", False)]
127+
128+
129+
def test_none_values_in_attribute_mappings_are_not_written_to_span():
130+
span = Mock()
131+
response = ExtractorResponse(content=[{"present": 0, "missing": None}])
132+
133+
ExtractorResponse.update_span(span, "unused", response)
134+
135+
span.set_attribute.assert_called_once_with("present", 0)

veadk/tracing/telemetry/attributes/extractors/types.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,15 @@ def update_span(
8686
"""
8787
if response.type == "attribute":
8888
res = response.content
89+
if res is None:
90+
return
8991
if isinstance(res, list):
9092
for _res in res:
9193
if isinstance(_res, dict):
9294
for k, v in _res.items():
93-
span.set_attribute(k, v)
95+
if v is not None:
96+
span.set_attribute(k, v)
9497
else:
95-
# set anyway
9698
span.set_attribute(attr_name, res) # type: ignore
9799
elif response.type == "event":
98100
if isinstance(response.content, dict):

0 commit comments

Comments
 (0)