Skip to content

Commit 1f64f15

Browse files
authored
Fix bug in how tokens are counted when streaming generateContent method is called. (#4152)
* Fix token count bug * Add changelog * Fix changelog
1 parent 262a097 commit 1f64f15

File tree

3 files changed

+10
-13
lines changed

3 files changed

+10
-13
lines changed

instrumentation-genai/opentelemetry-instrumentation-google-genai/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## Unreleased
9+
- Fix bug in how tokens are counted when using the streaming `generateContent` method. ([#4152](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4152)).
910

1011
## Version 0.6b0 (2026-01-27)
1112

instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/generate_content.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,9 @@ def _maybe_update_token_counts(self, response: GenerateContentResponse):
430430
response, "usage_metadata.candidates_token_count"
431431
)
432432
if input_tokens and isinstance(input_tokens, int):
433-
self._input_tokens += input_tokens
433+
self._input_tokens = input_tokens
434434
if output_tokens and isinstance(output_tokens, int):
435-
self._output_tokens += output_tokens
435+
self._output_tokens = output_tokens
436436

437437
def _maybe_update_error_type(self, response: GenerateContentResponse):
438438
if response.candidates:

instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/streaming_base.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,22 +89,18 @@ def test_handles_multiple_ressponses(self):
8989
choice_events = self.otel.get_events_named("gen_ai.choice")
9090
self.assertEqual(len(choice_events), 2)
9191

92-
def test_includes_token_counts_in_span_aggregated_from_responses(self):
93-
# Configure multiple responses whose input/output tokens should be
94-
# accumulated together when summarizing the end-to-end request.
95-
#
96-
# Input: 1 + 3 + 5 => 4 + 5 => 9
97-
# Output: 2 + 4 + 6 => 6 + 6 => 12
98-
self.configure_valid_response(input_tokens=1, output_tokens=2)
99-
self.configure_valid_response(input_tokens=3, output_tokens=4)
100-
self.configure_valid_response(input_tokens=5, output_tokens=6)
92+
def test_includes_token_counts_in_span_not_aggregated_from_responses(self):
93+
# Tokens should not be aggregated in streaming. Cumulative counts are returned on each response.
94+
self.configure_valid_response(input_tokens=3, output_tokens=5)
95+
self.configure_valid_response(input_tokens=3, output_tokens=5)
96+
self.configure_valid_response(input_tokens=3, output_tokens=5)
10197

10298
self.generate_content(model="gemini-2.0-flash", contents="Some input")
10399

104100
self.otel.assert_has_span_named("generate_content gemini-2.0-flash")
105101
span = self.otel.get_span_named("generate_content gemini-2.0-flash")
106-
self.assertEqual(span.attributes["gen_ai.usage.input_tokens"], 9)
107-
self.assertEqual(span.attributes["gen_ai.usage.output_tokens"], 12)
102+
self.assertEqual(span.attributes["gen_ai.usage.input_tokens"], 3)
103+
self.assertEqual(span.attributes["gen_ai.usage.output_tokens"], 5)
108104

109105
def test_new_semconv_log_has_extra_genai_attributes(self):
110106
patched_environ = patch.dict(

0 commit comments

Comments
 (0)