|
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