Skip to content

Commit 7ec6c23

Browse files
committed
Always call completion hook
1 parent 4d17437 commit 7ec6c23

File tree

1 file changed

+65
-39
lines changed
  • instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai

1 file changed

+65
-39
lines changed

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

Lines changed: 65 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ def _to_dict(value: object):
164164

165165

166166
def _add_request_options_to_span(
167-
span: Span, config: Optional[GenerateContentConfigOrDict], allow_list: AllowList
167+
span: Span,
168+
config: Optional[GenerateContentConfigOrDict],
169+
allow_list: AllowList,
168170
):
169171
if config is None:
170172
return
@@ -208,7 +210,9 @@ def _add_request_options_to_span(
208210
},
209211
)
210212
for key, value in attributes.items():
211-
if key.startswith(GCP_GENAI_OPERATION_CONFIG) and not allow_list.allowed(key):
213+
if key.startswith(
214+
GCP_GENAI_OPERATION_CONFIG
215+
) and not allow_list.allowed(key):
212216
# The allowlist is used to control inclusion of the dynamic keys.
213217
continue
214218
span.set_attribute(key, value)
@@ -244,7 +248,9 @@ def _wrapped_config_with_tools(
244248
if not config.tools:
245249
return config
246250
result = copy.copy(config)
247-
result.tools = [wrapped_tool(tool, otel_wrapper, **kwargs) for tool in config.tools]
251+
result.tools = [
252+
wrapped_tool(tool, otel_wrapper, **kwargs) for tool in config.tools
253+
]
248254
return result
249255

250256

@@ -267,10 +273,12 @@ def _create_completion_details_attributes(
267273
) -> dict[str, Any]:
268274
attributes: dict[str, Any] = {
269275
gen_ai_attributes.GEN_AI_INPUT_MESSAGES: [
270-
dataclasses.asdict(input_message) for input_message in input_messages
276+
dataclasses.asdict(input_message)
277+
for input_message in input_messages
271278
],
272279
gen_ai_attributes.GEN_AI_OUTPUT_MESSAGES: [
273-
dataclasses.asdict(output_message) for output_message in output_messages
280+
dataclasses.asdict(output_message)
281+
for output_message in output_messages
274282
],
275283
}
276284
if system_instructions:
@@ -463,43 +471,54 @@ def _maybe_log_completion_details(
463471
system_instructions = to_system_instructions(
464472
content=transformers.t_contents(system_content)[0]
465473
)
466-
input_messages = to_input_messages(contents=transformers.t_contents(request))
467-
output_messages = to_output_messages(candidates=response.candidates or [])
474+
input_messages = to_input_messages(
475+
contents=transformers.t_contents(request)
476+
)
477+
output_messages = to_output_messages(
478+
candidates=response.candidates or []
479+
)
468480

469481
span = None
470482
if self._content_recording_enabled in [
471483
ContentCapturingMode.SPAN_ONLY,
472484
ContentCapturingMode.SPAN_AND_EVENT,
473485
]:
474-
completion_details_attributes = _create_completion_details_attributes(
475-
input_messages,
476-
output_messages,
477-
system_instructions,
478-
as_str=True,
486+
completion_details_attributes = (
487+
_create_completion_details_attributes(
488+
input_messages,
489+
output_messages,
490+
system_instructions,
491+
as_str=True,
492+
)
479493
)
480494
span = trace.get_current_span()
481495
span.set_attributes(completion_details_attributes)
496+
497+
event = Event(
498+
name="gen_ai.client.inference.operation.details",
499+
attributes=attributes,
500+
)
482501
if self._content_recording_enabled in [
483502
ContentCapturingMode.EVENT_ONLY,
484503
ContentCapturingMode.SPAN_AND_EVENT,
485504
]:
486-
completion_details_attributes = _create_completion_details_attributes(
487-
input_messages,
488-
output_messages,
489-
system_instructions,
490-
)
491-
attributes.update(completion_details_attributes)
492-
event = Event(
493-
name="gen_ai.client.inference.operation.details", attributes=attributes
494-
)
495-
self.completion_hook.on_completion(
496-
inputs=input_messages,
497-
outputs=output_messages,
498-
system_instruction=system_instructions,
499-
span=span,
500-
log_record=event,
505+
completion_details_attributes = (
506+
_create_completion_details_attributes(
507+
input_messages,
508+
output_messages,
509+
system_instructions,
510+
)
501511
)
502-
self._otel_wrapper.log_completion_details(event=event)
512+
event.attributes.update(completion_details_attributes)
513+
514+
self.completion_hook.on_completion(
515+
inputs=input_messages,
516+
outputs=output_messages,
517+
system_instruction=system_instructions,
518+
span=span,
519+
log_record=event,
520+
)
521+
self._otel_wrapper.log_completion_details(event=event)
503522

504523
def _maybe_log_system_instruction(
505524
self, config: Optional[GenerateContentConfigOrDict] = None
@@ -538,7 +557,9 @@ def _maybe_log_user_prompt(
538557
total = len(contents)
539558
index = 0
540559
for entry in contents:
541-
self._maybe_log_single_user_prompt(entry, index=index, total=total)
560+
self._maybe_log_single_user_prompt(
561+
entry, index=index, total=total
562+
)
542563
index += 1
543564
else:
544565
self._maybe_log_single_user_prompt(contents)
@@ -644,7 +665,9 @@ def _maybe_log_response_stats(self, response: GenerateContentResponse):
644665
#
645666
pass
646667

647-
def _maybe_log_response_safety_ratings(self, response: GenerateContentResponse):
668+
def _maybe_log_response_safety_ratings(
669+
self, response: GenerateContentResponse
670+
):
648671
# TODO: Determine if there is a way that we can log
649672
# the "prompt_feedback". This would be especially useful
650673
# in the case where the response is blocked.
@@ -914,13 +937,18 @@ async def _response_async_generator_wrapper():
914937
with trace.use_span(span, end_on_exit=True):
915938
try:
916939
async for response in response_async_generator:
917-
if helper.sem_conv_opt_in_mode == _StabilityMode.DEFAULT:
940+
if (
941+
helper.sem_conv_opt_in_mode
942+
== _StabilityMode.DEFAULT
943+
):
918944
helper.process_response(response)
919945
elif (
920946
helper.sem_conv_opt_in_mode
921947
== _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL
922948
):
923-
helper.process_completion(contents, response, config)
949+
helper.process_completion(
950+
contents, response, config
951+
)
924952
else:
925953
raise ValueError(
926954
f"Sem Conv opt in mode {helper.sem_conv_opt_in_mode} not supported."
@@ -966,12 +994,10 @@ def instrument_generate_content(
966994
completion_hook,
967995
generate_content_config_key_allowlist=generate_content_config_key_allowlist,
968996
)
969-
AsyncModels.generate_content_stream = (
970-
_create_instrumented_async_generate_content_stream(
971-
snapshot,
972-
otel_wrapper,
973-
completion_hook,
974-
generate_content_config_key_allowlist=generate_content_config_key_allowlist,
975-
)
997+
AsyncModels.generate_content_stream = _create_instrumented_async_generate_content_stream(
998+
snapshot,
999+
otel_wrapper,
1000+
completion_hook,
1001+
generate_content_config_key_allowlist=generate_content_config_key_allowlist,
9761002
)
9771003
return snapshot

0 commit comments

Comments
 (0)