|
17 | 17 | from unittest.mock import patch
|
18 | 18 |
|
19 | 19 | from google.genai.types import GenerateContentConfig
|
| 20 | +from opentelemetry._events import Event |
20 | 21 | from opentelemetry.instrumentation._semconv import (
|
21 | 22 | _OpenTelemetrySemanticConventionStability,
|
22 | 23 | _OpenTelemetryStabilitySignalType,
|
23 | 24 | _StabilityMode,
|
24 | 25 | )
|
| 26 | +from opentelemetry.semconv._incubating.attributes import ( |
| 27 | + gen_ai_attributes, |
| 28 | +) |
25 | 29 | from opentelemetry.util.genai.types import ContentCapturingMode
|
26 | 30 |
|
27 | 31 | from .base import TestCase
|
@@ -204,20 +208,33 @@ def test_new_semconv_record_completion_as_log(self):
|
204 | 208 | _OpenTelemetryStabilitySignalType.GEN_AI: _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL
|
205 | 209 | },
|
206 | 210 | )
|
207 |
| - with self.subTest(f'mode: {mode}', patched_environ=patched_environ): |
| 211 | + content = "Some input" |
| 212 | + output = "Some response content" |
| 213 | + sys_instr = "System instruction" |
| 214 | + with self.subTest(f"mode: {mode}", patched_environ=patched_environ): |
208 | 215 | self.setUp()
|
209 | 216 | with patched_environ, patched_otel_mapping:
|
210 |
| - self.configure_valid_response(text="Some response content") |
211 |
| - self.generate_content(model="gemini-2.0-flash", contents="Some input") |
212 |
| - |
| 217 | + self.configure_valid_response(text=output) |
| 218 | + self.generate_content(model="gemini-2.0-flash", contents=content, config=GenerateContentConfig(system_instruction=sys_instr)) |
| 219 | + self.otel.assert_has_event_named("gen_ai.client.inference.operation.details") |
| 220 | + event = self.otel.get_event_named("gen_ai.client.inference.operation.details") |
213 | 221 | if mode in [
|
214 | 222 | ContentCapturingMode.NO_CONTENT,
|
215 | 223 | ContentCapturingMode.SPAN_ONLY,
|
216 | 224 | ]:
|
217 |
| - self.otel.assert_does_not_have_event_named("gen_ai.client.inference.operation.details") |
| 225 | + self.assertNotIn(gen_ai_attributes.GEN_AI_INPUT_MESSAGES, event.attributes) |
| 226 | + self.assertNotIn(gen_ai_attributes.GEN_AI_OUTPUT_MESSAGES, event.attributes) |
| 227 | + self.assertNotIn(gen_ai_attributes.GEN_AI_SYSTEM_INSTRUCTIONS, event.attributes) |
218 | 228 | else:
|
219 |
| - self.otel.assert_has_event_named("gen_ai.client.inference.operation.details") |
220 |
| - |
| 229 | + attrs = { |
| 230 | + gen_ai_attributes.GEN_AI_INPUT_MESSAGES: ({"role": "user", "parts": ({"content": content, "type": "text"},)},), |
| 231 | + gen_ai_attributes.GEN_AI_OUTPUT_MESSAGES: ({"role": "assistant", "parts": ({"content": output, "type": "text"},), "finish_reason": ""},), |
| 232 | + gen_ai_attributes.GEN_AI_SYSTEM_INSTRUCTIONS: ({"content": sys_instr, "type": "text"},) |
| 233 | + } |
| 234 | + expected_event = Event("gen_ai.client.inference.operation.details", attributes=attrs) |
| 235 | + self.assertEqual(event.attributes[gen_ai_attributes.GEN_AI_INPUT_MESSAGES], expected_event.attributes[gen_ai_attributes.GEN_AI_INPUT_MESSAGES]) |
| 236 | + self.assertEqual(event.attributes[gen_ai_attributes.GEN_AI_OUTPUT_MESSAGES], expected_event.attributes[gen_ai_attributes.GEN_AI_OUTPUT_MESSAGES]) |
| 237 | + self.assertEqual(event.attributes[gen_ai_attributes.GEN_AI_SYSTEM_INSTRUCTIONS], expected_event.attributes[gen_ai_attributes.GEN_AI_SYSTEM_INSTRUCTIONS]) |
221 | 238 | self.tearDown()
|
222 | 239 |
|
223 | 240 | def test_new_semconv_record_completion_in_span(self):
|
@@ -245,13 +262,13 @@ def test_new_semconv_record_completion_in_span(self):
|
245 | 262 | ContentCapturingMode.SPAN_ONLY,
|
246 | 263 | ContentCapturingMode.SPAN_AND_EVENT,
|
247 | 264 | ]:
|
248 |
| - self.assertEqual(span.attributes["gen_ai.input.messages"], '[{"role": "user", "parts": [{"content": "Some input", "type": "text"}]}]') |
249 |
| - self.assertEqual(span.attributes["gen_ai.output.messages"], '[{"role": "assistant", "parts": [{"content": "Some response content", "type": "text"}], "finish_reason": ""}]') |
250 |
| - self.assertEqual(span.attributes["gen_ai.system_instructions"], '[{"content": "System instruction", "type": "text"}]') |
| 265 | + self.assertEqual(span.attributes[gen_ai_attributes.GEN_AI_INPUT_MESSAGES], '[{"role": "user", "parts": [{"content": "Some input", "type": "text"}]}]') |
| 266 | + self.assertEqual(span.attributes[gen_ai_attributes.GEN_AI_OUTPUT_MESSAGES], '[{"role": "assistant", "parts": [{"content": "Some response content", "type": "text"}], "finish_reason": ""}]') |
| 267 | + self.assertEqual(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM_INSTRUCTIONS], '[{"content": "System instruction", "type": "text"}]') |
251 | 268 | else:
|
252 |
| - self.assertNotIn("gen_ai.input.messages", span.attributes) |
253 |
| - self.assertNotIn("gen_ai.output.messages", span.attributes) |
254 |
| - self.assertNotIn("gen_ai.system_instructions", span.attributes) |
| 269 | + self.assertNotIn(gen_ai_attributes.GEN_AI_INPUT_MESSAGES, span.attributes) |
| 270 | + self.assertNotIn(gen_ai_attributes.GEN_AI_OUTPUT_MESSAGES, span.attributes) |
| 271 | + self.assertNotIn(gen_ai_attributes.GEN_AI_SYSTEM_INSTRUCTIONS, span.attributes) |
255 | 272 |
|
256 | 273 | self.tearDown()
|
257 | 274 |
|
|
0 commit comments