Skip to content

Commit f38a2f2

Browse files
authored
[opentelemetry-instrumentation-google-genai] Add response ID attribute to the interactions API telemetry (inference span and event) (#233)
* feat: add gen_ai.conversation.id support to inference telemetry and Google GenAI instrumentation * Addchangelog * Address copilot comments * Run precommit * Use response id instead of conversation id * use response id instead of conversation id * Precommit * Revert lock file * Add validation
1 parent 68f7ecd commit f38a2f2

5 files changed

Lines changed: 55 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add response.id semantic convention attribute to interactions telemetry.

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ def _apply_interaction_response_attributes(
131131
telemetry_handler: TelemetryHandler,
132132
) -> None:
133133
invocation.response_model_name = response.model
134+
if getattr(response, "id", None):
135+
invocation.response_id = response.id
134136

135137
usage = response.usage or Usage()
136138

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from opentelemetry.sdk._logs import LoggerProvider
1616
from opentelemetry.sdk.metrics import MeterProvider
1717
from opentelemetry.sdk.trace import TracerProvider
18+
from opentelemetry.test.weaver_live_check import LiveCheckReport
1819
from opentelemetry.test_util_genai.conformance import Scenario
1920
from opentelemetry.test_util_genai.instrumentor import instrument
2021

@@ -49,3 +50,16 @@ def run(
4950
model="gemini-2.5-flash",
5051
contents="Say this is a test",
5152
)
53+
54+
def validate(self, report: LiveCheckReport) -> None:
55+
super().validate(report)
56+
response_ids = [
57+
attr["value"]
58+
for entry in report["samples"]
59+
if "span" in entry
60+
for attr in entry["span"]["attributes"]
61+
if attr["name"] == "gen_ai.response.id"
62+
]
63+
assert response_ids == ["Ibo-aoj4EpSy1MkPsezpqQE"], (
64+
f"Expected gen_ai.response.id ['Ibo-aoj4EpSy1MkPsezpqQE'] but saw {response_ids}"
65+
)

instrumentation/opentelemetry-instrumentation-google-genai/tests/conformance/inference.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from opentelemetry.sdk._logs import LoggerProvider
1616
from opentelemetry.sdk.metrics import MeterProvider
1717
from opentelemetry.sdk.trace import TracerProvider
18+
from opentelemetry.test.weaver_live_check import LiveCheckReport
1819
from opentelemetry.test_util_genai.conformance import (
1920
ExpectedViolation,
2021
Scenario,
@@ -58,3 +59,20 @@ def run(
5859
model="gemini-2.5-flash",
5960
input="Hello, how can you help me today?",
6061
)
62+
63+
def validate(self, report: LiveCheckReport) -> None:
64+
super().validate(report)
65+
response_ids = [
66+
attr["value"]
67+
for entry in report["samples"]
68+
if "span" in entry
69+
for attr in entry["span"]["attributes"]
70+
if attr["name"] == "gen_ai.response.id"
71+
]
72+
assert response_ids == [
73+
"v1_ChdMaWM4YXF2ekNMQ1k5TW9QLUpHZ3dBYxIXTGljOGFxdnpDTENZOU1vUC1KR2d3QWM"
74+
], (
75+
"Expected gen_ai.response.id "
76+
"['v1_ChdMaWM4YXF2ekNMQ1k5TW9QLUpHZ3dBYxIXTGljOGFxdnpDTENZOU1vUC1KR2d3QWM'] "
77+
f"but saw {response_ids}"
78+
)

instrumentation/opentelemetry-instrumentation-google-genai/tests/interactions/base.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,26 @@ def test_generated_span_has_minimal_genai_attributes(self) -> None:
170170
"generativelanguage.googleapis.com",
171171
)
172172

173+
def test_generated_span_has_response_id(self) -> None:
174+
self.configure_valid_interaction(interaction_id="interaction-123")
175+
self.run_interaction(
176+
model="gemini-2.5-flash",
177+
input="Follow-up question",
178+
)
179+
span = self.otel.get_span_named("interactions.create gemini-2.5-flash")
180+
self.assertEqual(
181+
span.attributes["gen_ai.response.id"], "interaction-123"
182+
)
183+
self.otel.assert_has_event_named(
184+
"gen_ai.client.inference.operation.details"
185+
)
186+
event = self.otel.get_event_named(
187+
"gen_ai.client.inference.operation.details"
188+
)
189+
self.assertEqual(
190+
event.attributes["gen_ai.response.id"], "interaction-123"
191+
)
192+
173193
def test_span_and_event_still_written_when_response_is_exception(
174194
self,
175195
) -> None:

0 commit comments

Comments
 (0)