|
48 | 48 | )
|
49 | 49 | from opentelemetry.semconv.attributes import error_attributes
|
50 | 50 | from opentelemetry.trace.span import Span
|
51 |
| -from opentelemetry.util.genai.types import ContentCapturingMode |
| 51 | +from opentelemetry.util.genai.types import ContentCapturingMode, MessagePart |
52 | 52 | from opentelemetry.util.genai.upload_hook import load_upload_hook
|
53 | 53 |
|
54 | 54 | from .allowlist_util import AllowList
|
|
60 | 60 | OutputMessage,
|
61 | 61 | to_input_messages,
|
62 | 62 | to_output_messages,
|
63 |
| - to_system_instruction, |
| 63 | + to_system_instructions, |
64 | 64 | )
|
65 | 65 | from .otel_wrapper import OTelWrapper
|
66 | 66 | from .tool_call_wrapper import wrapped as wrapped_tool
|
@@ -259,20 +259,18 @@ def _config_to_system_instruction(
|
259 | 259 | def _create_completion_details_attributes(
|
260 | 260 | input_messages: list[InputMessage],
|
261 | 261 | output_messages: list[OutputMessage],
|
262 |
| - system_instruction: Union[InputMessage, None], |
263 |
| -): |
264 |
| - attributes = { |
265 |
| - "gen_ai.input.messages": json.dumps( |
266 |
| - [dataclasses.asdict(input_message) for input_message in input_messages] |
267 |
| - ), |
268 |
| - "gen_ai.output.messages": json.dumps( |
269 |
| - [dataclasses.asdict(output_message) for output_message in output_messages] |
270 |
| - ), |
| 262 | + system_instructions: list[MessagePart], |
| 263 | + as_str: bool = False, |
| 264 | +) -> dict[str, Any]: |
| 265 | + attributes: dict[str, Any] = { |
| 266 | + "gen_ai.input.messages": [dataclasses.asdict(input_message) for input_message in input_messages], |
| 267 | + "gen_ai.output.messages": [dataclasses.asdict(output_message) for output_message in output_messages], |
271 | 268 | }
|
272 |
| - if system_instruction: |
273 |
| - attributes["gen_ai.system.instructions"] = json.dumps( |
274 |
| - dataclasses.asdict(system_instruction) |
275 |
| - ) |
| 269 | + if system_instructions: |
| 270 | + attributes["gen_ai.system.instructions"] = [dataclasses.asdict(sys_instr) for sys_instr in system_instructions] |
| 271 | + |
| 272 | + if as_str: |
| 273 | + return {k: json.dumps(v) for k, v in attributes.items()} |
276 | 274 |
|
277 | 275 | return attributes
|
278 | 276 |
|
@@ -449,46 +447,45 @@ def _maybe_log_completion_details(
|
449 | 447 | attributes = {
|
450 | 448 | gen_ai_attributes.GEN_AI_SYSTEM: self._genai_system,
|
451 | 449 | }
|
452 |
| - system_instruction = None |
| 450 | + system_instructions = [] |
453 | 451 | if system_content := _config_to_system_instruction(config):
|
454 |
| - system_instruction = to_system_instruction( |
| 452 | + system_instructions = to_system_instructions( |
455 | 453 | content=transformers.t_contents(system_content)[0]
|
456 | 454 | )
|
457 | 455 | input_messages = to_input_messages(contents=transformers.t_contents(request))
|
458 | 456 | output_messages = to_output_messages(candidates=response.candidates or [])
|
459 | 457 |
|
460 |
| - completion_details_attributes = _create_completion_details_attributes( |
461 |
| - input_messages, output_messages, system_instruction |
462 |
| - ) |
| 458 | + |
463 | 459 |
|
464 | 460 | span = None
|
465 | 461 | if self._content_recording_enabled in [
|
466 | 462 | ContentCapturingMode.SPAN_ONLY,
|
467 | 463 | ContentCapturingMode.SPAN_AND_EVENT,
|
468 | 464 | ]:
|
| 465 | + completion_details_attributes = _create_completion_details_attributes( |
| 466 | + input_messages, output_messages, system_instructions, as_str=True, |
| 467 | + ) |
469 | 468 | span = trace.get_current_span()
|
470 | 469 | span.set_attributes(completion_details_attributes)
|
471 | 470 | if self._content_recording_enabled in [
|
472 | 471 | ContentCapturingMode.EVENT_ONLY,
|
473 | 472 | ContentCapturingMode.SPAN_AND_EVENT,
|
474 | 473 | ]:
|
| 474 | + completion_details_attributes = _create_completion_details_attributes( |
| 475 | + input_messages, output_messages, system_instructions, |
| 476 | + ) |
475 | 477 | attributes.update(completion_details_attributes)
|
476 | 478 | event = Event(name="gen_ai.completion.details", attributes=attributes)
|
477 | 479 | hook = load_upload_hook()
|
478 | 480 | hook.upload(
|
479 | 481 | inputs=input_messages,
|
480 | 482 | outputs=output_messages,
|
481 |
| - system_instruction=( |
482 |
| - system_instruction.parts if system_instruction else [] |
483 |
| - ), |
| 483 | + system_instruction=system_instructions, |
484 | 484 | span=span,
|
485 | 485 | log_record=event,
|
486 | 486 | )
|
487 |
| - # TODO Cannot access attribute shutdown for class UploadHook |
488 |
| - # hook.shutdown() |
489 |
| - self._otel_wrapper.log_completion_details( |
490 |
| - event=event, |
491 |
| - ) |
| 487 | + hook.shutdown() |
| 488 | + self._otel_wrapper.log_completion_details(event=event) |
492 | 489 |
|
493 | 490 | def _maybe_log_system_instruction(
|
494 | 491 | self, config: Optional[GenerateContentConfigOrDict] = None
|
|
0 commit comments