diff --git a/instrumentation/opentelemetry-instrumentation-google-genai/.changelog/233.added b/instrumentation/opentelemetry-instrumentation-google-genai/.changelog/233.added new file mode 100644 index 00000000..3ae4c5d4 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-google-genai/.changelog/233.added @@ -0,0 +1 @@ +Add response.id semantic convention attribute to interactions telemetry. diff --git a/instrumentation/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/interactions.py b/instrumentation/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/interactions.py index 1d52e195..908daed9 100644 --- a/instrumentation/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/interactions.py +++ b/instrumentation/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/interactions.py @@ -131,6 +131,8 @@ def _apply_interaction_response_attributes( telemetry_handler: TelemetryHandler, ) -> None: invocation.response_model_name = response.model + if getattr(response, "id", None): + invocation.response_id = response.id usage = response.usage or Usage() diff --git a/instrumentation/opentelemetry-instrumentation-google-genai/tests/conformance/generate_content.py b/instrumentation/opentelemetry-instrumentation-google-genai/tests/conformance/generate_content.py index 8d7458ad..7cddd23f 100644 --- a/instrumentation/opentelemetry-instrumentation-google-genai/tests/conformance/generate_content.py +++ b/instrumentation/opentelemetry-instrumentation-google-genai/tests/conformance/generate_content.py @@ -15,6 +15,7 @@ from opentelemetry.sdk._logs import LoggerProvider from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.test.weaver_live_check import LiveCheckReport from opentelemetry.test_util_genai.conformance import Scenario from opentelemetry.test_util_genai.instrumentor import instrument @@ -49,3 +50,16 @@ def run( model="gemini-2.5-flash", contents="Say this is a test", ) + + def validate(self, report: LiveCheckReport) -> None: + super().validate(report) + response_ids = [ + attr["value"] + for entry in report["samples"] + if "span" in entry + for attr in entry["span"]["attributes"] + if attr["name"] == "gen_ai.response.id" + ] + assert response_ids == ["Ibo-aoj4EpSy1MkPsezpqQE"], ( + f"Expected gen_ai.response.id ['Ibo-aoj4EpSy1MkPsezpqQE'] but saw {response_ids}" + ) diff --git a/instrumentation/opentelemetry-instrumentation-google-genai/tests/conformance/inference.py b/instrumentation/opentelemetry-instrumentation-google-genai/tests/conformance/inference.py index e9a4bc48..fc4a863f 100644 --- a/instrumentation/opentelemetry-instrumentation-google-genai/tests/conformance/inference.py +++ b/instrumentation/opentelemetry-instrumentation-google-genai/tests/conformance/inference.py @@ -15,6 +15,7 @@ from opentelemetry.sdk._logs import LoggerProvider from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.test.weaver_live_check import LiveCheckReport from opentelemetry.test_util_genai.conformance import ( ExpectedViolation, Scenario, @@ -58,3 +59,20 @@ def run( model="gemini-2.5-flash", input="Hello, how can you help me today?", ) + + def validate(self, report: LiveCheckReport) -> None: + super().validate(report) + response_ids = [ + attr["value"] + for entry in report["samples"] + if "span" in entry + for attr in entry["span"]["attributes"] + if attr["name"] == "gen_ai.response.id" + ] + assert response_ids == [ + "v1_ChdMaWM4YXF2ekNMQ1k5TW9QLUpHZ3dBYxIXTGljOGFxdnpDTENZOU1vUC1KR2d3QWM" + ], ( + "Expected gen_ai.response.id " + "['v1_ChdMaWM4YXF2ekNMQ1k5TW9QLUpHZ3dBYxIXTGljOGFxdnpDTENZOU1vUC1KR2d3QWM'] " + f"but saw {response_ids}" + ) diff --git a/instrumentation/opentelemetry-instrumentation-google-genai/tests/interactions/base.py b/instrumentation/opentelemetry-instrumentation-google-genai/tests/interactions/base.py index 2236463f..b5c93a94 100644 --- a/instrumentation/opentelemetry-instrumentation-google-genai/tests/interactions/base.py +++ b/instrumentation/opentelemetry-instrumentation-google-genai/tests/interactions/base.py @@ -170,6 +170,26 @@ def test_generated_span_has_minimal_genai_attributes(self) -> None: "generativelanguage.googleapis.com", ) + def test_generated_span_has_response_id(self) -> None: + self.configure_valid_interaction(interaction_id="interaction-123") + self.run_interaction( + model="gemini-2.5-flash", + input="Follow-up question", + ) + span = self.otel.get_span_named("interactions.create gemini-2.5-flash") + self.assertEqual( + span.attributes["gen_ai.response.id"], "interaction-123" + ) + self.otel.assert_has_event_named( + "gen_ai.client.inference.operation.details" + ) + event = self.otel.get_event_named( + "gen_ai.client.inference.operation.details" + ) + self.assertEqual( + event.attributes["gen_ai.response.id"], "interaction-123" + ) + def test_span_and_event_still_written_when_response_is_exception( self, ) -> None: