opentelemetry-instrumentation-openai 0.18.0__py3-none-any.whl → 0.18.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of opentelemetry-instrumentation-openai might be problematic. Click here for more details.

@@ -112,7 +112,9 @@ def _set_request_attributes(span, kwargs):
112
112
  _set_span_attribute(
113
113
  span, SpanAttributes.LLM_REQUEST_MAX_TOKENS, kwargs.get("max_tokens")
114
114
  )
115
- _set_span_attribute(span, SpanAttributes.LLM_REQUEST_TEMPERATURE, kwargs.get("temperature"))
115
+ _set_span_attribute(
116
+ span, SpanAttributes.LLM_REQUEST_TEMPERATURE, kwargs.get("temperature")
117
+ )
116
118
  _set_span_attribute(span, SpanAttributes.LLM_REQUEST_TOP_P, kwargs.get("top_p"))
117
119
  _set_span_attribute(
118
120
  span, SpanAttributes.LLM_FREQUENCY_PENALTY, kwargs.get("frequency_penalty")
@@ -139,6 +141,10 @@ def _set_response_attributes(span, response):
139
141
 
140
142
  _set_span_attribute(span, SpanAttributes.LLM_RESPONSE_MODEL, response.get("model"))
141
143
 
144
+ _set_span_attribute(
145
+ span, "gen_ai.openai.system_fingerprint", response.get("system_fingerprint")
146
+ )
147
+
142
148
  usage = response.get("usage")
143
149
  if not usage:
144
150
  return
@@ -87,18 +87,32 @@ def chat_wrapper(
87
87
 
88
88
  if is_streaming_response(response):
89
89
  # span will be closed after the generator is done
90
- return ChatStream(
91
- span,
92
- response,
93
- instance,
94
- token_counter,
95
- choice_counter,
96
- duration_histogram,
97
- streaming_time_to_first_token,
98
- streaming_time_to_generate,
99
- start_time,
100
- kwargs,
101
- )
90
+ if is_openai_v1():
91
+ return ChatStream(
92
+ span,
93
+ response,
94
+ instance,
95
+ token_counter,
96
+ choice_counter,
97
+ duration_histogram,
98
+ streaming_time_to_first_token,
99
+ streaming_time_to_generate,
100
+ start_time,
101
+ kwargs,
102
+ )
103
+ else:
104
+ return _build_from_streaming_response(
105
+ span,
106
+ response,
107
+ instance,
108
+ token_counter,
109
+ choice_counter,
110
+ duration_histogram,
111
+ streaming_time_to_first_token,
112
+ streaming_time_to_generate,
113
+ start_time,
114
+ kwargs,
115
+ )
102
116
 
103
117
  duration = end_time - start_time
104
118
 
@@ -161,18 +175,32 @@ async def achat_wrapper(
161
175
 
162
176
  if is_streaming_response(response):
163
177
  # span will be closed after the generator is done
164
- return ChatStream(
165
- span,
166
- response,
167
- instance,
168
- token_counter,
169
- choice_counter,
170
- duration_histogram,
171
- streaming_time_to_first_token,
172
- streaming_time_to_generate,
173
- start_time,
174
- kwargs,
175
- )
178
+ if is_openai_v1():
179
+ return ChatStream(
180
+ span,
181
+ response,
182
+ instance,
183
+ token_counter,
184
+ choice_counter,
185
+ duration_histogram,
186
+ streaming_time_to_first_token,
187
+ streaming_time_to_generate,
188
+ start_time,
189
+ kwargs,
190
+ )
191
+ else:
192
+ return _abuild_from_streaming_response(
193
+ span,
194
+ response,
195
+ instance,
196
+ token_counter,
197
+ choice_counter,
198
+ duration_histogram,
199
+ streaming_time_to_first_token,
200
+ streaming_time_to_generate,
201
+ start_time,
202
+ kwargs,
203
+ )
176
204
 
177
205
  duration = end_time - start_time
178
206
 
@@ -242,6 +270,7 @@ def _set_chat_metrics(
242
270
  shared_attributes = {
243
271
  "gen_ai.response.model": response_dict.get("model") or None,
244
272
  "server.address": _get_openai_base_url(instance),
273
+ "stream": False,
245
274
  }
246
275
 
247
276
  # token metrics
@@ -356,7 +385,11 @@ def _set_streaming_token_metrics(
356
385
  # prompt_usage
357
386
  if request_kwargs and request_kwargs.get("messages"):
358
387
  prompt_content = ""
359
- model_name = request_kwargs.get("model") or None
388
+ # setting the default model_name as gpt-4. As this uses the embedding "cl100k_base" that
389
+ # is used by most of the other model.
390
+ model_name = (
391
+ request_kwargs.get("model") or complete_response.get("model") or "gpt-4"
392
+ )
360
393
  for msg in request_kwargs.get("messages"):
361
394
  if msg.get("content"):
362
395
  prompt_content += msg.get("content")
@@ -366,7 +399,9 @@ def _set_streaming_token_metrics(
366
399
  # completion_usage
367
400
  if complete_response.get("choices"):
368
401
  completion_content = ""
369
- model_name = complete_response.get("model") or None
402
+ # setting the default model_name as gpt-4. As this uses the embedding "cl100k_base" that
403
+ # is used by most of the other model.
404
+ model_name = complete_response.get("model") or "gpt-4"
370
405
 
371
406
  for choice in complete_response.get("choices"):
372
407
  if choice.get("message") and choice.get("message").get("content"):
@@ -398,6 +433,16 @@ def _set_streaming_token_metrics(
398
433
 
399
434
 
400
435
  class ChatStream(ObjectProxy):
436
+ _span = None
437
+ _instance = None
438
+ _token_counter = None
439
+ _choice_counter = None
440
+ _duration_histogram = None
441
+ _streaming_time_to_first_token = None
442
+ _streaming_time_to_generate = None
443
+ _start_time = None
444
+ _request_kwargs = None
445
+
401
446
  def __init__(
402
447
  self,
403
448
  span,
@@ -534,3 +579,166 @@ class ChatStream(ObjectProxy):
534
579
 
535
580
  self._span.set_status(Status(StatusCode.OK))
536
581
  self._span.end()
582
+
583
+
584
+ # Backward compatibility with OpenAI v0
585
+
586
+
587
+ @dont_throw
588
+ def _build_from_streaming_response(
589
+ span,
590
+ response,
591
+ instance=None,
592
+ token_counter=None,
593
+ choice_counter=None,
594
+ duration_histogram=None,
595
+ streaming_time_to_first_token=None,
596
+ streaming_time_to_generate=None,
597
+ start_time=None,
598
+ request_kwargs=None,
599
+ ):
600
+ complete_response = {"choices": [], "model": ""}
601
+
602
+ first_token = True
603
+ time_of_first_token = start_time # will be updated when first token is received
604
+
605
+ for item in response:
606
+ span.add_event(name="llm.content.completion.chunk")
607
+
608
+ item_to_yield = item
609
+
610
+ if first_token and streaming_time_to_first_token:
611
+ time_of_first_token = time.time()
612
+ streaming_time_to_first_token.record(time_of_first_token - start_time)
613
+ first_token = False
614
+
615
+ _accumulate_stream_items(item, complete_response)
616
+
617
+ yield item_to_yield
618
+
619
+ shared_attributes = {
620
+ "gen_ai.response.model": complete_response.get("model") or None,
621
+ "server.address": _get_openai_base_url(instance),
622
+ "stream": True,
623
+ }
624
+
625
+ if not is_azure_openai(instance):
626
+ _set_streaming_token_metrics(
627
+ request_kwargs, complete_response, span, token_counter, shared_attributes
628
+ )
629
+
630
+ # choice metrics
631
+ if choice_counter and complete_response.get("choices"):
632
+ _set_choice_counter_metrics(
633
+ choice_counter, complete_response.get("choices"), shared_attributes
634
+ )
635
+
636
+ # duration metrics
637
+ if start_time and isinstance(start_time, (float, int)):
638
+ duration = time.time() - start_time
639
+ else:
640
+ duration = None
641
+ if duration and isinstance(duration, (float, int)) and duration_histogram:
642
+ duration_histogram.record(duration, attributes=shared_attributes)
643
+ if streaming_time_to_generate and time_of_first_token:
644
+ streaming_time_to_generate.record(time.time() - time_of_first_token)
645
+
646
+ _set_response_attributes(span, complete_response)
647
+
648
+ if should_send_prompts():
649
+ _set_completions(span, complete_response.get("choices"))
650
+
651
+ span.set_status(Status(StatusCode.OK))
652
+ span.end()
653
+
654
+
655
+ @dont_throw
656
+ async def _abuild_from_streaming_response(
657
+ span,
658
+ response,
659
+ instance=None,
660
+ token_counter=None,
661
+ choice_counter=None,
662
+ duration_histogram=None,
663
+ streaming_time_to_first_token=None,
664
+ streaming_time_to_generate=None,
665
+ start_time=None,
666
+ request_kwargs=None,
667
+ ):
668
+ complete_response = {"choices": [], "model": ""}
669
+
670
+ first_token = True
671
+ time_of_first_token = start_time # will be updated when first token is received
672
+
673
+ async for item in response:
674
+ span.add_event(name="llm.content.completion.chunk")
675
+
676
+ item_to_yield = item
677
+
678
+ if first_token and streaming_time_to_first_token:
679
+ time_of_first_token = time.time()
680
+ streaming_time_to_first_token.record(time_of_first_token - start_time)
681
+ first_token = False
682
+
683
+ _accumulate_stream_items(item, complete_response)
684
+
685
+ yield item_to_yield
686
+
687
+ shared_attributes = {
688
+ "gen_ai.response.model": complete_response.get("model") or None,
689
+ "server.address": _get_openai_base_url(instance),
690
+ "stream": True,
691
+ }
692
+
693
+ if not is_azure_openai(instance):
694
+ _set_streaming_token_metrics(
695
+ request_kwargs, complete_response, span, token_counter, shared_attributes
696
+ )
697
+
698
+ # choice metrics
699
+ if choice_counter and complete_response.get("choices"):
700
+ _set_choice_counter_metrics(
701
+ choice_counter, complete_response.get("choices"), shared_attributes
702
+ )
703
+
704
+ # duration metrics
705
+ if start_time and isinstance(start_time, (float, int)):
706
+ duration = time.time() - start_time
707
+ else:
708
+ duration = None
709
+ if duration and isinstance(duration, (float, int)) and duration_histogram:
710
+ duration_histogram.record(duration, attributes=shared_attributes)
711
+ if streaming_time_to_generate and time_of_first_token:
712
+ streaming_time_to_generate.record(time.time() - time_of_first_token)
713
+
714
+ _set_response_attributes(span, complete_response)
715
+
716
+ if should_send_prompts():
717
+ _set_completions(span, complete_response.get("choices"))
718
+
719
+ span.set_status(Status(StatusCode.OK))
720
+ span.end()
721
+
722
+
723
+ def _accumulate_stream_items(item, complete_response):
724
+ if is_openai_v1():
725
+ item = model_as_dict(item)
726
+
727
+ complete_response["model"] = item.get("model")
728
+
729
+ for choice in item.get("choices"):
730
+ index = choice.get("index")
731
+ if len(complete_response.get("choices")) <= index:
732
+ complete_response["choices"].append(
733
+ {"index": index, "message": {"content": "", "role": ""}}
734
+ )
735
+ complete_choice = complete_response.get("choices")[index]
736
+ if choice.get("finish_reason"):
737
+ complete_choice["finish_reason"] = choice.get("finish_reason")
738
+
739
+ delta = choice.get("delta")
740
+
741
+ if delta and delta.get("content"):
742
+ complete_choice["message"]["content"] += delta.get("content")
743
+ if delta and delta.get("role"):
744
+ complete_choice["message"]["role"] = delta.get("role")
@@ -1 +1 @@
1
- __version__ = "0.18.0"
1
+ __version__ = "0.18.2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: opentelemetry-instrumentation-openai
3
- Version: 0.18.0
3
+ Version: 0.18.2
4
4
  Summary: OpenTelemetry OpenAI instrumentation
5
5
  Home-page: https://github.com/traceloop/openllmetry/tree/main/packages/opentelemetry-instrumentation-openai
6
6
  License: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  opentelemetry/instrumentation/openai/__init__.py,sha256=xl3Kvqry9glVhu8VtdknfUE9FpXQ7KWAFqtVlpjE-40,1344
2
- opentelemetry/instrumentation/openai/shared/__init__.py,sha256=eBVYPw2Zh28p9wak6Ncba3jSt-bdIgJjzJrad6snCxo,7359
3
- opentelemetry/instrumentation/openai/shared/chat_wrappers.py,sha256=Gq65KbByZnKvcvqWE65_04eaq-XJ2LIHdfxIuq1AiSI,16608
2
+ opentelemetry/instrumentation/openai/shared/__init__.py,sha256=5kEyVhz2YDHvuq2SDQOsDhtjbG7R7GCn793oLq2_J_k,7490
3
+ opentelemetry/instrumentation/openai/shared/chat_wrappers.py,sha256=6gJUpjp1tq3eYviTVTRNq5RcF4yduhI6yAKJBSEGToI,23344
4
4
  opentelemetry/instrumentation/openai/shared/completion_wrappers.py,sha256=-JHfgyxic5I3Wr3Uc_L-U7ztDVFcyovtF37tNLtaW3s,6604
5
5
  opentelemetry/instrumentation/openai/shared/config.py,sha256=5uekQEnmYo1o6tsTD2IGc-cVmHUo5KmUC4pOVdrFFNk,102
6
6
  opentelemetry/instrumentation/openai/shared/embeddings_wrappers.py,sha256=6I6I98T1a5Np0ZjcGZQbId4ZyEmMI6o9wVm8qoRpO9o,6595
@@ -10,8 +10,8 @@ opentelemetry/instrumentation/openai/v0/__init__.py,sha256=ngmmYyfTwRQSjTZAvNpBI
10
10
  opentelemetry/instrumentation/openai/v1/__init__.py,sha256=6XHk11JhkpZixgMDsjb0b-efd8LlBTG0jjMCyf0fOSo,8652
11
11
  opentelemetry/instrumentation/openai/v1/assistant_wrappers.py,sha256=T6Vtdp1fAZdcYjGiTMZwkn4F4DgsltD4p4xLEFW-GhI,5874
12
12
  opentelemetry/instrumentation/openai/v1/event_handler_wrapper.py,sha256=SAzYoun2yyOloofyOWtxpm8E2M9TL3Nm8TgKdNyXHuY,2779
13
- opentelemetry/instrumentation/openai/version.py,sha256=0EHw4xygmgkGSyfwNfEoMlQyN0uHxjHtlSFF79s6120,23
14
- opentelemetry_instrumentation_openai-0.18.0.dist-info/METADATA,sha256=WkE3L2VnQwU5Bu68MPgqgP98uDrwu2LOR6vKlD4RpPg,2255
15
- opentelemetry_instrumentation_openai-0.18.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
16
- opentelemetry_instrumentation_openai-0.18.0.dist-info/entry_points.txt,sha256=vTBfiX5yXji5YHikuJHEOoBZ1TFdPQ1EI4ctd2pZSeE,93
17
- opentelemetry_instrumentation_openai-0.18.0.dist-info/RECORD,,
13
+ opentelemetry/instrumentation/openai/version.py,sha256=GYySpgpz2Cs3F3nz_H9h8KIG60jkP6f1a--08qCTJCQ,23
14
+ opentelemetry_instrumentation_openai-0.18.2.dist-info/METADATA,sha256=8_mfPUYiS7Wxf5kBberCnJ4KEm4VjYtk7ESz_tXiSZ0,2255
15
+ opentelemetry_instrumentation_openai-0.18.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
16
+ opentelemetry_instrumentation_openai-0.18.2.dist-info/entry_points.txt,sha256=vTBfiX5yXji5YHikuJHEOoBZ1TFdPQ1EI4ctd2pZSeE,93
17
+ opentelemetry_instrumentation_openai-0.18.2.dist-info/RECORD,,