Skip to content

Commit 033d0cc

Browse files
authored
Limit span resource attributes (#1120)
Add tests for token metrics filtering and resource attributes
1 parent edf7231 commit 033d0cc

File tree

5 files changed

+78
-10
lines changed

5 files changed

+78
-10
lines changed

agentops/instrumentation/common/token_counting.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,29 @@ class TokenUsage:
2323
reasoning_tokens: Optional[int] = None
2424

2525
def to_attributes(self) -> Dict[str, int]:
26-
"""Convert to span attributes dictionary."""
26+
"""Convert to span attributes dictionary.
27+
28+
Only metrics greater than zero are included so that non‑LLM spans do
29+
not contain empty token usage fields.
30+
"""
2731
attributes = {}
2832

29-
if self.prompt_tokens is not None:
33+
if self.prompt_tokens:
3034
attributes[SpanAttributes.LLM_USAGE_PROMPT_TOKENS] = self.prompt_tokens
3135

32-
if self.completion_tokens is not None:
36+
if self.completion_tokens:
3337
attributes[SpanAttributes.LLM_USAGE_COMPLETION_TOKENS] = self.completion_tokens
3438

35-
if self.total_tokens is not None:
39+
if self.total_tokens:
3640
attributes[SpanAttributes.LLM_USAGE_TOTAL_TOKENS] = self.total_tokens
3741

38-
if self.cached_prompt_tokens is not None:
42+
if self.cached_prompt_tokens:
3943
attributes[SpanAttributes.LLM_USAGE_CACHE_CREATION_INPUT_TOKENS] = self.cached_prompt_tokens
4044

41-
if self.cached_read_tokens is not None:
45+
if self.cached_read_tokens:
4246
attributes[SpanAttributes.LLM_USAGE_CACHE_READ_INPUT_TOKENS] = self.cached_read_tokens
4347

44-
if self.reasoning_tokens is not None:
48+
if self.reasoning_tokens:
4549
attributes[SpanAttributes.LLM_USAGE_REASONING_TOKENS] = self.reasoning_tokens
4650

4751
return attributes

agentops/sdk/attributes.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ def get_global_resource_attributes(
6060
"""
6161
Get all global resource attributes for telemetry.
6262
63-
Combines service metadata, system information, and imported libraries
64-
into a complete resource attributes dictionary.
63+
Combines service metadata and imported libraries into a complete
64+
resource attributes dictionary.
6565
6666
Args:
6767
service_name: Name of the service
@@ -73,7 +73,6 @@ def get_global_resource_attributes(
7373
# Start with service attributes
7474
attributes: dict[str, Any] = {
7575
ResourceAttributes.SERVICE_NAME: service_name,
76-
**get_system_resource_attributes(),
7776
}
7877

7978
if project_id:

agentops/sdk/core.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
get_trace_attributes,
2424
get_span_attributes,
2525
get_session_end_attributes,
26+
get_system_resource_attributes,
2627
)
2728
from agentops.semconv import SpanKind
2829
from agentops.helpers.dashboard import log_trace_url
@@ -354,6 +355,9 @@ def start_trace(
354355

355356
# Build trace attributes
356357
attributes = get_trace_attributes(tags=tags)
358+
# Include system metadata only for the default session trace
359+
if trace_name == "session":
360+
attributes.update(get_system_resource_attributes())
357361

358362
# make_span creates and starts the span, and activates it in the current context
359363
# It returns: span, context_object, context_token
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from agentops.instrumentation.common.token_counting import TokenUsage
2+
from agentops.semconv import SpanAttributes
3+
4+
5+
class TestTokenUsageToAttributes:
6+
def test_skips_zero_values(self):
7+
usage = TokenUsage(
8+
prompt_tokens=0,
9+
completion_tokens=0,
10+
total_tokens=0,
11+
cached_prompt_tokens=0,
12+
cached_read_tokens=0,
13+
reasoning_tokens=0,
14+
)
15+
16+
attrs = usage.to_attributes()
17+
assert attrs == {}
18+
19+
def test_includes_positive_values_only(self):
20+
usage = TokenUsage(prompt_tokens=5, completion_tokens=0, total_tokens=5)
21+
attrs = usage.to_attributes()
22+
assert SpanAttributes.LLM_USAGE_PROMPT_TOKENS in attrs
23+
assert attrs[SpanAttributes.LLM_USAGE_PROMPT_TOKENS] == 5
24+
assert SpanAttributes.LLM_USAGE_COMPLETION_TOKENS not in attrs
25+
assert SpanAttributes.LLM_USAGE_TOTAL_TOKENS in attrs
26+
assert attrs[SpanAttributes.LLM_USAGE_TOTAL_TOKENS] == 5
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from unittest.mock import patch
2+
3+
4+
from agentops.sdk.core import tracer
5+
from agentops.sdk.attributes import get_global_resource_attributes
6+
from agentops.semconv.resource import ResourceAttributes
7+
8+
9+
@patch("agentops.sdk.attributes.get_imported_libraries", return_value=["agentops"])
10+
def test_global_resource_attributes_no_system(mock_libs):
11+
attrs = get_global_resource_attributes("svc", project_id="proj")
12+
assert attrs[ResourceAttributes.SERVICE_NAME] == "svc"
13+
assert attrs[ResourceAttributes.PROJECT_ID] == "proj"
14+
assert ResourceAttributes.IMPORTED_LIBRARIES in attrs
15+
assert ResourceAttributes.HOST_MACHINE not in attrs
16+
assert ResourceAttributes.CPU_COUNT not in attrs
17+
18+
19+
@patch("agentops.sdk.core.get_system_resource_attributes")
20+
def test_system_metadata_only_for_session(mock_sys_attrs, instrumentation):
21+
mock_sys_attrs.return_value = {ResourceAttributes.HOST_MACHINE: "test"}
22+
23+
ctx = tracer.start_trace("session")
24+
tracer.end_trace(ctx, end_state="Success")
25+
spans = instrumentation.get_finished_spans()
26+
assert len(spans) == 1
27+
assert spans[0].attributes.get(ResourceAttributes.HOST_MACHINE) == "test"
28+
29+
instrumentation.clear_spans()
30+
31+
ctx = tracer.start_trace("custom")
32+
tracer.end_trace(ctx, end_state="Success")
33+
spans = instrumentation.get_finished_spans()
34+
assert len(spans) == 1
35+
assert ResourceAttributes.HOST_MACHINE not in spans[0].attributes

0 commit comments

Comments
 (0)