opentelemetry-instrumentation-openai 0.21.5__tar.gz → 0.22.1__tar.gz
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.
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/PKG-INFO +2 -2
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/opentelemetry/instrumentation/openai/shared/__init__.py +12 -12
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/opentelemetry/instrumentation/openai/shared/chat_wrappers.py +37 -22
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/opentelemetry/instrumentation/openai/shared/embeddings_wrappers.py +1 -1
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/opentelemetry/instrumentation/openai/shared/image_gen_wrappers.py +2 -4
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/opentelemetry/instrumentation/openai/utils.py +4 -1
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/opentelemetry/instrumentation/openai/v0/__init__.py +16 -23
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/opentelemetry/instrumentation/openai/v1/__init__.py +20 -34
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/opentelemetry/instrumentation/openai/v1/assistant_wrappers.py +16 -5
- opentelemetry_instrumentation_openai-0.22.1/opentelemetry/instrumentation/openai/version.py +1 -0
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/pyproject.toml +6 -6
- opentelemetry_instrumentation_openai-0.21.5/opentelemetry/instrumentation/openai/version.py +0 -1
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/README.md +0 -0
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/opentelemetry/instrumentation/openai/__init__.py +0 -0
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/opentelemetry/instrumentation/openai/shared/completion_wrappers.py +0 -0
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/opentelemetry/instrumentation/openai/shared/config.py +0 -0
- {opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/opentelemetry/instrumentation/openai/v1/event_handler_wrapper.py +0 -0
{opentelemetry_instrumentation_openai-0.21.5 → opentelemetry_instrumentation_openai-0.22.1}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: opentelemetry-instrumentation-openai
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.22.1
|
|
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
|
|
@@ -17,7 +17,7 @@ Provides-Extra: instruments
|
|
|
17
17
|
Requires-Dist: opentelemetry-api (>=1.25.0,<2.0.0)
|
|
18
18
|
Requires-Dist: opentelemetry-instrumentation (>=0.46b0,<0.47)
|
|
19
19
|
Requires-Dist: opentelemetry-semantic-conventions (>=0.46b0,<0.47)
|
|
20
|
-
Requires-Dist: opentelemetry-semantic-conventions-ai (==0.
|
|
20
|
+
Requires-Dist: opentelemetry-semantic-conventions-ai (==0.3.1)
|
|
21
21
|
Requires-Dist: tiktoken (>=0.6.0,<1)
|
|
22
22
|
Project-URL: Repository, https://github.com/traceloop/openllmetry/tree/main/packages/opentelemetry-instrumentation-openai
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
@@ -15,10 +15,6 @@ from opentelemetry.instrumentation.openai.utils import (
|
|
|
15
15
|
should_record_stream_token_usage,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
-
OPENAI_API_VERSION = "openai.api_version"
|
|
19
|
-
OPENAI_API_BASE = "openai.api_base"
|
|
20
|
-
OPENAI_API_TYPE = "openai.api_type"
|
|
21
|
-
|
|
22
18
|
OPENAI_LLM_USAGE_TOKEN_TYPES = ["prompt_tokens", "completion_tokens"]
|
|
23
19
|
|
|
24
20
|
# tiktoken encodings map for different model, key is model_name, value is tiktoken encoding
|
|
@@ -49,10 +45,12 @@ def _set_client_attributes(span, instance):
|
|
|
49
45
|
|
|
50
46
|
client = instance._client # pylint: disable=protected-access
|
|
51
47
|
if isinstance(client, (openai.AsyncOpenAI, openai.OpenAI)):
|
|
52
|
-
_set_span_attribute(
|
|
48
|
+
_set_span_attribute(
|
|
49
|
+
span, SpanAttributes.LLM_OPENAI_API_BASE, str(client.base_url)
|
|
50
|
+
)
|
|
53
51
|
if isinstance(client, (openai.AsyncAzureOpenAI, openai.AzureOpenAI)):
|
|
54
52
|
_set_span_attribute(
|
|
55
|
-
span,
|
|
53
|
+
span, SpanAttributes.LLM_OPENAI_API_VERSION, client._api_version
|
|
56
54
|
) # pylint: disable=protected-access
|
|
57
55
|
|
|
58
56
|
|
|
@@ -65,9 +63,9 @@ def _set_api_attributes(span):
|
|
|
65
63
|
|
|
66
64
|
base_url = openai.base_url if hasattr(openai, "base_url") else openai.api_base
|
|
67
65
|
|
|
68
|
-
_set_span_attribute(span,
|
|
69
|
-
_set_span_attribute(span,
|
|
70
|
-
_set_span_attribute(span,
|
|
66
|
+
_set_span_attribute(span, SpanAttributes.LLM_OPENAI_API_BASE, base_url)
|
|
67
|
+
_set_span_attribute(span, SpanAttributes.LLM_OPENAI_API_TYPE, openai.api_type)
|
|
68
|
+
_set_span_attribute(span, SpanAttributes.LLM_OPENAI_API_VERSION, openai.api_version)
|
|
71
69
|
|
|
72
70
|
return
|
|
73
71
|
|
|
@@ -142,7 +140,9 @@ def _set_response_attributes(span, response):
|
|
|
142
140
|
_set_span_attribute(span, SpanAttributes.LLM_RESPONSE_MODEL, response.get("model"))
|
|
143
141
|
|
|
144
142
|
_set_span_attribute(
|
|
145
|
-
span,
|
|
143
|
+
span,
|
|
144
|
+
SpanAttributes.LLM_OPENAI_RESPONSE_SYSTEM_FINGERPRINT,
|
|
145
|
+
response.get("system_fingerprint"),
|
|
146
146
|
)
|
|
147
147
|
|
|
148
148
|
usage = response.get("usage")
|
|
@@ -260,8 +260,8 @@ def _metric_shared_attributes(
|
|
|
260
260
|
response_model: str, operation: str, server_address: str, is_streaming: bool = False
|
|
261
261
|
):
|
|
262
262
|
return {
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
SpanAttributes.LLM_SYSTEM: "openai",
|
|
264
|
+
SpanAttributes.LLM_RESPONSE_MODEL: response_model,
|
|
265
265
|
"gen_ai.operation.name": operation,
|
|
266
266
|
"server.address": server_address,
|
|
267
267
|
"stream": is_streaming,
|
|
@@ -296,8 +296,8 @@ def _set_choice_counter_metrics(choice_counter, choices, shared_attributes):
|
|
|
296
296
|
for choice in choices:
|
|
297
297
|
attributes_with_reason = {**shared_attributes}
|
|
298
298
|
if choice.get("finish_reason"):
|
|
299
|
-
attributes_with_reason[
|
|
300
|
-
"finish_reason"
|
|
299
|
+
attributes_with_reason[SpanAttributes.LLM_RESPONSE_FINISH_REASON] = (
|
|
300
|
+
choice.get("finish_reason")
|
|
301
301
|
)
|
|
302
302
|
choice_counter.add(1, attributes=attributes_with_reason)
|
|
303
303
|
|
|
@@ -307,7 +307,7 @@ def _set_token_counter_metrics(token_counter, usage, shared_attributes):
|
|
|
307
307
|
if name in OPENAI_LLM_USAGE_TOKEN_TYPES:
|
|
308
308
|
attributes_with_token_type = {
|
|
309
309
|
**shared_attributes,
|
|
310
|
-
|
|
310
|
+
SpanAttributes.LLM_TOKEN_TYPE: _token_type(name),
|
|
311
311
|
}
|
|
312
312
|
token_counter.record(val, attributes=attributes_with_token_type)
|
|
313
313
|
|
|
@@ -424,14 +424,14 @@ def _set_streaming_token_metrics(
|
|
|
424
424
|
if type(prompt_usage) is int and prompt_usage >= 0:
|
|
425
425
|
attributes_with_token_type = {
|
|
426
426
|
**shared_attributes,
|
|
427
|
-
|
|
427
|
+
SpanAttributes.LLM_TOKEN_TYPE: "input",
|
|
428
428
|
}
|
|
429
429
|
token_counter.record(prompt_usage, attributes=attributes_with_token_type)
|
|
430
430
|
|
|
431
431
|
if type(completion_usage) is int and completion_usage >= 0:
|
|
432
432
|
attributes_with_token_type = {
|
|
433
433
|
**shared_attributes,
|
|
434
|
-
|
|
434
|
+
SpanAttributes.LLM_TOKEN_TYPE: "output",
|
|
435
435
|
}
|
|
436
436
|
token_counter.record(
|
|
437
437
|
completion_usage, attributes=attributes_with_token_type
|
|
@@ -520,7 +520,7 @@ class ChatStream(ObjectProxy):
|
|
|
520
520
|
return chunk
|
|
521
521
|
|
|
522
522
|
def _process_item(self, item):
|
|
523
|
-
self._span.add_event(name="
|
|
523
|
+
self._span.add_event(name=f"{SpanAttributes.LLM_CONTENT_COMPLETION_CHUNK}")
|
|
524
524
|
|
|
525
525
|
if self._first_token and self._streaming_time_to_first_token:
|
|
526
526
|
self._time_of_first_token = time.time()
|
|
@@ -551,6 +551,23 @@ class ChatStream(ObjectProxy):
|
|
|
551
551
|
complete_choice["message"]["content"] += delta.get("content")
|
|
552
552
|
if delta and delta.get("role"):
|
|
553
553
|
complete_choice["message"]["role"] = delta.get("role")
|
|
554
|
+
if delta and delta.get("tool_calls"):
|
|
555
|
+
tool_calls = delta.get("tool_calls")
|
|
556
|
+
if not isinstance(tool_calls, list) or len(tool_calls) == 0:
|
|
557
|
+
continue
|
|
558
|
+
|
|
559
|
+
if not complete_choice["message"].get("tool_calls"):
|
|
560
|
+
complete_choice["message"]["tool_calls"] = [
|
|
561
|
+
{"function": {"name": "", "arguments": ""}}
|
|
562
|
+
]
|
|
563
|
+
|
|
564
|
+
tool_call = tool_calls[0]
|
|
565
|
+
function = complete_choice["message"]["tool_calls"][0]["function"]
|
|
566
|
+
|
|
567
|
+
if tool_call.get("function") and tool_call["function"].get("name"):
|
|
568
|
+
function["name"] += tool_call["function"]["name"]
|
|
569
|
+
if tool_call.get("function") and tool_call["function"].get("arguments"):
|
|
570
|
+
function["arguments"] += tool_call["function"]["arguments"]
|
|
554
571
|
|
|
555
572
|
def _shared_attributes(self):
|
|
556
573
|
return _metric_shared_attributes(
|
|
@@ -564,14 +581,13 @@ class ChatStream(ObjectProxy):
|
|
|
564
581
|
|
|
565
582
|
@dont_throw
|
|
566
583
|
def _close_span(self):
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
)
|
|
584
|
+
_set_streaming_token_metrics(
|
|
585
|
+
self._request_kwargs,
|
|
586
|
+
self._complete_response,
|
|
587
|
+
self._span,
|
|
588
|
+
self._token_counter,
|
|
589
|
+
self._shared_attributes(),
|
|
590
|
+
)
|
|
575
591
|
|
|
576
592
|
# choice metrics
|
|
577
593
|
if self._choice_counter and self._complete_response.get("choices"):
|
|
@@ -627,7 +643,7 @@ def _build_from_streaming_response(
|
|
|
627
643
|
time_of_first_token = start_time # will be updated when first token is received
|
|
628
644
|
|
|
629
645
|
for item in response:
|
|
630
|
-
span.add_event(name="
|
|
646
|
+
span.add_event(name=f"{SpanAttributes.LLM_CONTENT_COMPLETION_CHUNK}")
|
|
631
647
|
|
|
632
648
|
item_to_yield = item
|
|
633
649
|
|
|
@@ -641,15 +657,14 @@ def _build_from_streaming_response(
|
|
|
641
657
|
yield item_to_yield
|
|
642
658
|
|
|
643
659
|
shared_attributes = {
|
|
644
|
-
|
|
660
|
+
SpanAttributes.LLM_RESPONSE_MODEL: complete_response.get("model") or None,
|
|
645
661
|
"server.address": _get_openai_base_url(instance),
|
|
646
662
|
"stream": True,
|
|
647
663
|
}
|
|
648
664
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
)
|
|
665
|
+
_set_streaming_token_metrics(
|
|
666
|
+
request_kwargs, complete_response, span, token_counter, shared_attributes
|
|
667
|
+
)
|
|
653
668
|
|
|
654
669
|
# choice metrics
|
|
655
670
|
if choice_counter and complete_response.get("choices"):
|
|
@@ -695,7 +710,7 @@ async def _abuild_from_streaming_response(
|
|
|
695
710
|
time_of_first_token = start_time # will be updated when first token is received
|
|
696
711
|
|
|
697
712
|
async for item in response:
|
|
698
|
-
span.add_event(name="
|
|
713
|
+
span.add_event(name=f"{SpanAttributes.LLM_CONTENT_COMPLETION_CHUNK}")
|
|
699
714
|
|
|
700
715
|
item_to_yield = item
|
|
701
716
|
|
|
@@ -709,7 +724,7 @@ async def _abuild_from_streaming_response(
|
|
|
709
724
|
yield item_to_yield
|
|
710
725
|
|
|
711
726
|
shared_attributes = {
|
|
712
|
-
|
|
727
|
+
SpanAttributes.LLM_RESPONSE_MODEL: complete_response.get("model") or None,
|
|
713
728
|
"server.address": _get_openai_base_url(instance),
|
|
714
729
|
"stream": True,
|
|
715
730
|
}
|
|
@@ -203,7 +203,7 @@ def _set_embeddings_metrics(
|
|
|
203
203
|
if name in OPENAI_LLM_USAGE_TOKEN_TYPES:
|
|
204
204
|
attributes_with_token_type = {
|
|
205
205
|
**shared_attributes,
|
|
206
|
-
|
|
206
|
+
SpanAttributes.LLM_TOKEN_TYPE: _token_type(name),
|
|
207
207
|
}
|
|
208
208
|
token_counter.record(val, attributes=attributes_with_token_type)
|
|
209
209
|
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import time
|
|
2
2
|
|
|
3
3
|
from opentelemetry import context as context_api
|
|
4
|
-
from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY
|
|
5
|
-
|
|
6
|
-
from opentelemetry.metrics import Counter, Histogram
|
|
7
|
-
|
|
8
4
|
from opentelemetry.instrumentation.openai import is_openai_v1
|
|
9
5
|
from opentelemetry.instrumentation.openai.shared import (
|
|
10
6
|
_get_openai_base_url,
|
|
@@ -14,6 +10,8 @@ from opentelemetry.instrumentation.openai.shared import (
|
|
|
14
10
|
from opentelemetry.instrumentation.openai.utils import (
|
|
15
11
|
_with_image_gen_metric_wrapper,
|
|
16
12
|
)
|
|
13
|
+
from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY
|
|
14
|
+
from opentelemetry.metrics import Counter, Histogram
|
|
17
15
|
|
|
18
16
|
|
|
19
17
|
@_with_image_gen_metric_wrapper
|
|
@@ -2,6 +2,7 @@ from importlib.metadata import version
|
|
|
2
2
|
from contextlib import asynccontextmanager
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
|
+
import traceback
|
|
5
6
|
|
|
6
7
|
import openai
|
|
7
8
|
from opentelemetry.instrumentation.openai.shared.config import Config
|
|
@@ -124,7 +125,9 @@ def dont_throw(func):
|
|
|
124
125
|
return func(*args, **kwargs)
|
|
125
126
|
except Exception as e:
|
|
126
127
|
logger.debug(
|
|
127
|
-
"OpenLLMetry failed to trace in %s, error: %s",
|
|
128
|
+
"OpenLLMetry failed to trace in %s, error: %s",
|
|
129
|
+
func.__name__,
|
|
130
|
+
traceback.format_exc(),
|
|
128
131
|
)
|
|
129
132
|
if Config.exception_logger:
|
|
130
133
|
Config.exception_logger(e)
|
|
@@ -19,6 +19,7 @@ from opentelemetry.instrumentation.openai.shared.embeddings_wrappers import (
|
|
|
19
19
|
)
|
|
20
20
|
from opentelemetry.instrumentation.openai.utils import is_metrics_enabled
|
|
21
21
|
from opentelemetry.instrumentation.openai.version import __version__
|
|
22
|
+
from opentelemetry.semconv.ai import Meters
|
|
22
23
|
|
|
23
24
|
_instruments = ("openai >= 0.27.0", "openai < 1.0.0")
|
|
24
25
|
|
|
@@ -36,36 +37,36 @@ class OpenAIV0Instrumentor(BaseInstrumentor):
|
|
|
36
37
|
|
|
37
38
|
if is_metrics_enabled():
|
|
38
39
|
tokens_histogram = meter.create_histogram(
|
|
39
|
-
name=
|
|
40
|
+
name=Meters.LLM_TOKEN_USAGE,
|
|
40
41
|
unit="token",
|
|
41
42
|
description="Measures number of input and output tokens used",
|
|
42
43
|
)
|
|
43
44
|
|
|
44
45
|
chat_choice_counter = meter.create_counter(
|
|
45
|
-
name=
|
|
46
|
+
name=Meters.LLM_GENERATION_CHOICES,
|
|
46
47
|
unit="choice",
|
|
47
48
|
description="Number of choices returned by chat completions call",
|
|
48
49
|
)
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
name=
|
|
51
|
+
duration_histogram = meter.create_histogram(
|
|
52
|
+
name=Meters.LLM_OPERATION_DURATION,
|
|
52
53
|
unit="s",
|
|
53
54
|
description="GenAI operation duration",
|
|
54
55
|
)
|
|
55
56
|
|
|
56
57
|
chat_exception_counter = meter.create_counter(
|
|
57
|
-
name=
|
|
58
|
+
name=Meters.LLM_COMPLETIONS_EXCEPTIONS,
|
|
58
59
|
unit="time",
|
|
59
60
|
description="Number of exceptions occurred during chat completions",
|
|
60
61
|
)
|
|
61
62
|
|
|
62
63
|
streaming_time_to_first_token = meter.create_histogram(
|
|
63
|
-
name=
|
|
64
|
+
name=Meters.LLM_STREAMING_TIME_TO_FIRST_TOKEN,
|
|
64
65
|
unit="s",
|
|
65
66
|
description="Time to first token in streaming chat completions",
|
|
66
67
|
)
|
|
67
68
|
streaming_time_to_generate = meter.create_histogram(
|
|
68
|
-
name=
|
|
69
|
+
name=Meters.LLM_STREAMING_TIME_TO_GENERATE,
|
|
69
70
|
unit="s",
|
|
70
71
|
description="Time between first token and completion in streaming chat completions",
|
|
71
72
|
)
|
|
@@ -73,7 +74,7 @@ class OpenAIV0Instrumentor(BaseInstrumentor):
|
|
|
73
74
|
(
|
|
74
75
|
tokens_histogram,
|
|
75
76
|
chat_choice_counter,
|
|
76
|
-
|
|
77
|
+
duration_histogram,
|
|
77
78
|
chat_exception_counter,
|
|
78
79
|
streaming_time_to_first_token,
|
|
79
80
|
streaming_time_to_generate,
|
|
@@ -81,19 +82,12 @@ class OpenAIV0Instrumentor(BaseInstrumentor):
|
|
|
81
82
|
|
|
82
83
|
if is_metrics_enabled():
|
|
83
84
|
embeddings_vector_size_counter = meter.create_counter(
|
|
84
|
-
name=
|
|
85
|
+
name=Meters.LLM_EMBEDDINGS_VECTOR_SIZE,
|
|
85
86
|
unit="element",
|
|
86
87
|
description="he size of returned vector",
|
|
87
88
|
)
|
|
88
|
-
|
|
89
|
-
embeddings_duration_histogram = meter.create_histogram(
|
|
90
|
-
name="llm.openai.embeddings.duration",
|
|
91
|
-
unit="s",
|
|
92
|
-
description="Duration of embeddings operation",
|
|
93
|
-
)
|
|
94
|
-
|
|
95
89
|
embeddings_exception_counter = meter.create_counter(
|
|
96
|
-
name=
|
|
90
|
+
name=Meters.LLM_EMBEDDINGS_EXCEPTIONS,
|
|
97
91
|
unit="time",
|
|
98
92
|
description="Number of exceptions occurred during embeddings operation",
|
|
99
93
|
)
|
|
@@ -101,9 +95,8 @@ class OpenAIV0Instrumentor(BaseInstrumentor):
|
|
|
101
95
|
(
|
|
102
96
|
tokens_histogram,
|
|
103
97
|
embeddings_vector_size_counter,
|
|
104
|
-
embeddings_duration_histogram,
|
|
105
98
|
embeddings_exception_counter,
|
|
106
|
-
) = (None, None, None
|
|
99
|
+
) = (None, None, None)
|
|
107
100
|
|
|
108
101
|
wrap_function_wrapper("openai", "Completion.create", completion_wrapper(tracer))
|
|
109
102
|
wrap_function_wrapper(
|
|
@@ -116,7 +109,7 @@ class OpenAIV0Instrumentor(BaseInstrumentor):
|
|
|
116
109
|
tracer,
|
|
117
110
|
tokens_histogram,
|
|
118
111
|
chat_choice_counter,
|
|
119
|
-
|
|
112
|
+
duration_histogram,
|
|
120
113
|
chat_exception_counter,
|
|
121
114
|
streaming_time_to_first_token,
|
|
122
115
|
streaming_time_to_generate,
|
|
@@ -129,7 +122,7 @@ class OpenAIV0Instrumentor(BaseInstrumentor):
|
|
|
129
122
|
tracer,
|
|
130
123
|
tokens_histogram,
|
|
131
124
|
chat_choice_counter,
|
|
132
|
-
|
|
125
|
+
duration_histogram,
|
|
133
126
|
chat_exception_counter,
|
|
134
127
|
streaming_time_to_first_token,
|
|
135
128
|
streaming_time_to_generate,
|
|
@@ -142,7 +135,7 @@ class OpenAIV0Instrumentor(BaseInstrumentor):
|
|
|
142
135
|
tracer,
|
|
143
136
|
tokens_histogram,
|
|
144
137
|
embeddings_vector_size_counter,
|
|
145
|
-
|
|
138
|
+
duration_histogram,
|
|
146
139
|
embeddings_exception_counter,
|
|
147
140
|
),
|
|
148
141
|
)
|
|
@@ -153,7 +146,7 @@ class OpenAIV0Instrumentor(BaseInstrumentor):
|
|
|
153
146
|
tracer,
|
|
154
147
|
tokens_histogram,
|
|
155
148
|
embeddings_vector_size_counter,
|
|
156
|
-
|
|
149
|
+
duration_histogram,
|
|
157
150
|
embeddings_exception_counter,
|
|
158
151
|
),
|
|
159
152
|
)
|
|
@@ -33,6 +33,8 @@ from opentelemetry.instrumentation.openai.v1.assistant_wrappers import (
|
|
|
33
33
|
from opentelemetry.instrumentation.openai.utils import is_metrics_enabled
|
|
34
34
|
from opentelemetry.instrumentation.openai.version import __version__
|
|
35
35
|
|
|
36
|
+
from opentelemetry.semconv.ai import Meters
|
|
37
|
+
|
|
36
38
|
_instruments = ("openai >= 1.0.0",)
|
|
37
39
|
|
|
38
40
|
|
|
@@ -50,36 +52,36 @@ class OpenAIV1Instrumentor(BaseInstrumentor):
|
|
|
50
52
|
|
|
51
53
|
if is_metrics_enabled():
|
|
52
54
|
tokens_histogram = meter.create_histogram(
|
|
53
|
-
name=
|
|
55
|
+
name=Meters.LLM_TOKEN_USAGE,
|
|
54
56
|
unit="token",
|
|
55
57
|
description="Measures number of input and output tokens used",
|
|
56
58
|
)
|
|
57
59
|
|
|
58
60
|
chat_choice_counter = meter.create_counter(
|
|
59
|
-
name=
|
|
61
|
+
name=Meters.LLM_GENERATION_CHOICES,
|
|
60
62
|
unit="choice",
|
|
61
63
|
description="Number of choices returned by chat completions call",
|
|
62
64
|
)
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
name=
|
|
66
|
+
duration_histogram = meter.create_histogram(
|
|
67
|
+
name=Meters.LLM_OPERATION_DURATION,
|
|
66
68
|
unit="s",
|
|
67
69
|
description="GenAI operation duration",
|
|
68
70
|
)
|
|
69
71
|
|
|
70
72
|
chat_exception_counter = meter.create_counter(
|
|
71
|
-
name=
|
|
73
|
+
name=Meters.LLM_COMPLETIONS_EXCEPTIONS,
|
|
72
74
|
unit="time",
|
|
73
75
|
description="Number of exceptions occurred during chat completions",
|
|
74
76
|
)
|
|
75
77
|
|
|
76
78
|
streaming_time_to_first_token = meter.create_histogram(
|
|
77
|
-
name=
|
|
79
|
+
name=Meters.LLM_STREAMING_TIME_TO_FIRST_TOKEN,
|
|
78
80
|
unit="s",
|
|
79
81
|
description="Time to first token in streaming chat completions",
|
|
80
82
|
)
|
|
81
83
|
streaming_time_to_generate = meter.create_histogram(
|
|
82
|
-
name=
|
|
84
|
+
name=Meters.LLM_STREAMING_TIME_TO_GENERATE,
|
|
83
85
|
unit="s",
|
|
84
86
|
description="Time between first token and completion in streaming chat completions",
|
|
85
87
|
)
|
|
@@ -87,7 +89,7 @@ class OpenAIV1Instrumentor(BaseInstrumentor):
|
|
|
87
89
|
(
|
|
88
90
|
tokens_histogram,
|
|
89
91
|
chat_choice_counter,
|
|
90
|
-
|
|
92
|
+
duration_histogram,
|
|
91
93
|
chat_exception_counter,
|
|
92
94
|
streaming_time_to_first_token,
|
|
93
95
|
streaming_time_to_generate,
|
|
@@ -100,7 +102,7 @@ class OpenAIV1Instrumentor(BaseInstrumentor):
|
|
|
100
102
|
tracer,
|
|
101
103
|
tokens_histogram,
|
|
102
104
|
chat_choice_counter,
|
|
103
|
-
|
|
105
|
+
duration_histogram,
|
|
104
106
|
chat_exception_counter,
|
|
105
107
|
streaming_time_to_first_token,
|
|
106
108
|
streaming_time_to_generate,
|
|
@@ -115,19 +117,12 @@ class OpenAIV1Instrumentor(BaseInstrumentor):
|
|
|
115
117
|
|
|
116
118
|
if is_metrics_enabled():
|
|
117
119
|
embeddings_vector_size_counter = meter.create_counter(
|
|
118
|
-
name=
|
|
120
|
+
name=Meters.LLM_EMBEDDINGS_VECTOR_SIZE,
|
|
119
121
|
unit="element",
|
|
120
122
|
description="he size of returned vector",
|
|
121
123
|
)
|
|
122
|
-
|
|
123
|
-
embeddings_duration_histogram = meter.create_histogram(
|
|
124
|
-
name="llm.openai.embeddings.duration",
|
|
125
|
-
unit="s",
|
|
126
|
-
description="Duration of embeddings operation",
|
|
127
|
-
)
|
|
128
|
-
|
|
129
124
|
embeddings_exception_counter = meter.create_counter(
|
|
130
|
-
name=
|
|
125
|
+
name=Meters.LLM_EMBEDDINGS_EXCEPTIONS,
|
|
131
126
|
unit="time",
|
|
132
127
|
description="Number of exceptions occurred during embeddings operation",
|
|
133
128
|
)
|
|
@@ -135,9 +130,8 @@ class OpenAIV1Instrumentor(BaseInstrumentor):
|
|
|
135
130
|
(
|
|
136
131
|
tokens_histogram,
|
|
137
132
|
embeddings_vector_size_counter,
|
|
138
|
-
embeddings_duration_histogram,
|
|
139
133
|
embeddings_exception_counter,
|
|
140
|
-
) = (None, None, None
|
|
134
|
+
) = (None, None, None)
|
|
141
135
|
|
|
142
136
|
wrap_function_wrapper(
|
|
143
137
|
"openai.resources.embeddings",
|
|
@@ -146,7 +140,7 @@ class OpenAIV1Instrumentor(BaseInstrumentor):
|
|
|
146
140
|
tracer,
|
|
147
141
|
tokens_histogram,
|
|
148
142
|
embeddings_vector_size_counter,
|
|
149
|
-
|
|
143
|
+
duration_histogram,
|
|
150
144
|
embeddings_exception_counter,
|
|
151
145
|
),
|
|
152
146
|
)
|
|
@@ -158,7 +152,7 @@ class OpenAIV1Instrumentor(BaseInstrumentor):
|
|
|
158
152
|
tracer,
|
|
159
153
|
tokens_histogram,
|
|
160
154
|
chat_choice_counter,
|
|
161
|
-
|
|
155
|
+
duration_histogram,
|
|
162
156
|
chat_exception_counter,
|
|
163
157
|
streaming_time_to_first_token,
|
|
164
158
|
streaming_time_to_generate,
|
|
@@ -176,32 +170,24 @@ class OpenAIV1Instrumentor(BaseInstrumentor):
|
|
|
176
170
|
tracer,
|
|
177
171
|
tokens_histogram,
|
|
178
172
|
embeddings_vector_size_counter,
|
|
179
|
-
|
|
173
|
+
duration_histogram,
|
|
180
174
|
embeddings_exception_counter,
|
|
181
175
|
),
|
|
182
176
|
)
|
|
183
177
|
|
|
184
178
|
if is_metrics_enabled():
|
|
185
|
-
image_gen_duration_histogram = meter.create_histogram(
|
|
186
|
-
name="llm.openai.image_generations.duration",
|
|
187
|
-
unit="s",
|
|
188
|
-
description="Duration of image generations operation",
|
|
189
|
-
)
|
|
190
|
-
|
|
191
179
|
image_gen_exception_counter = meter.create_counter(
|
|
192
|
-
name=
|
|
180
|
+
name=Meters.LLM_IMAGE_GENERATIONS_EXCEPTIONS,
|
|
193
181
|
unit="time",
|
|
194
182
|
description="Number of exceptions occurred during image generations operation",
|
|
195
183
|
)
|
|
196
184
|
else:
|
|
197
|
-
|
|
185
|
+
image_gen_exception_counter = None
|
|
198
186
|
|
|
199
187
|
wrap_function_wrapper(
|
|
200
188
|
"openai.resources.images",
|
|
201
189
|
"Images.generate",
|
|
202
|
-
image_gen_metrics_wrapper(
|
|
203
|
-
image_gen_duration_histogram, image_gen_exception_counter
|
|
204
|
-
),
|
|
190
|
+
image_gen_metrics_wrapper(duration_histogram, image_gen_exception_counter),
|
|
205
191
|
)
|
|
206
192
|
|
|
207
193
|
# Beta APIs may not be available consistently in all versions
|
|
@@ -10,9 +10,12 @@ from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY
|
|
|
10
10
|
|
|
11
11
|
from opentelemetry.semconv.ai import SpanAttributes, LLMRequestTypeValues
|
|
12
12
|
|
|
13
|
-
from opentelemetry.instrumentation.openai.utils import _with_tracer_wrapper
|
|
13
|
+
from opentelemetry.instrumentation.openai.utils import _with_tracer_wrapper, dont_throw
|
|
14
14
|
from opentelemetry.instrumentation.openai.shared.config import Config
|
|
15
15
|
|
|
16
|
+
from openai._legacy_response import LegacyAPIResponse
|
|
17
|
+
from openai.types.beta.threads.run import Run
|
|
18
|
+
|
|
16
19
|
logger = logging.getLogger(__name__)
|
|
17
20
|
|
|
18
21
|
assistants = {}
|
|
@@ -55,15 +58,23 @@ def runs_create_wrapper(tracer, wrapped, instance, args, kwargs):
|
|
|
55
58
|
|
|
56
59
|
@_with_tracer_wrapper
|
|
57
60
|
def runs_retrieve_wrapper(tracer, wrapped, instance, args, kwargs):
|
|
61
|
+
@dont_throw
|
|
62
|
+
def process_response(response):
|
|
63
|
+
if type(response) is LegacyAPIResponse:
|
|
64
|
+
parsed_response = response.parse()
|
|
65
|
+
else:
|
|
66
|
+
parsed_response = response
|
|
67
|
+
assert type(parsed_response) is Run
|
|
68
|
+
|
|
69
|
+
if parsed_response.id in runs:
|
|
70
|
+
runs[thread_id]["end_time"] = time.time_ns()
|
|
71
|
+
|
|
58
72
|
if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY):
|
|
59
73
|
return wrapped(*args, **kwargs)
|
|
60
74
|
|
|
61
75
|
thread_id = kwargs.get("thread_id")
|
|
62
|
-
|
|
63
76
|
response = wrapped(*args, **kwargs)
|
|
64
|
-
|
|
65
|
-
if response.id in runs:
|
|
66
|
-
runs[thread_id]["end_time"] = time.time_ns()
|
|
77
|
+
process_response(response)
|
|
67
78
|
|
|
68
79
|
return response
|
|
69
80
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.22.1"
|
|
@@ -8,7 +8,7 @@ show_missing = true
|
|
|
8
8
|
|
|
9
9
|
[tool.poetry]
|
|
10
10
|
name = "opentelemetry-instrumentation-openai"
|
|
11
|
-
version = "0.
|
|
11
|
+
version = "0.22.1"
|
|
12
12
|
description = "OpenTelemetry OpenAI instrumentation"
|
|
13
13
|
authors = [
|
|
14
14
|
"Gal Kleinman <gal@traceloop.com>",
|
|
@@ -27,21 +27,21 @@ python = ">=3.9,<4"
|
|
|
27
27
|
opentelemetry-api = "^1.25.0"
|
|
28
28
|
opentelemetry-instrumentation = "^0.46b0"
|
|
29
29
|
opentelemetry-semantic-conventions = "^0.46b0"
|
|
30
|
-
opentelemetry-semantic-conventions-ai = "0.
|
|
30
|
+
opentelemetry-semantic-conventions-ai = "0.3.1"
|
|
31
31
|
tiktoken = ">=0.6.0, <1"
|
|
32
32
|
|
|
33
33
|
[tool.poetry.group.dev.dependencies]
|
|
34
|
-
autopep8 = "2.
|
|
34
|
+
autopep8 = "^2.2.0"
|
|
35
35
|
flake8 = "7.0.0"
|
|
36
36
|
|
|
37
37
|
[tool.poetry.group.test.dependencies]
|
|
38
|
-
pytest = "8.
|
|
38
|
+
pytest = "^8.2.2"
|
|
39
39
|
pytest-sugar = "1.0.0"
|
|
40
40
|
vcrpy = "^6.0.1"
|
|
41
41
|
pytest-recording = "^0.13.1"
|
|
42
|
-
openai = {extras = ["datalib"], version = "^1.
|
|
42
|
+
openai = {extras = ["datalib"], version = "^1.31.1"}
|
|
43
43
|
opentelemetry-sdk = "^1.23.0"
|
|
44
|
-
pytest-asyncio = "^0.23.
|
|
44
|
+
pytest-asyncio = "^0.23.7"
|
|
45
45
|
|
|
46
46
|
[build-system]
|
|
47
47
|
requires = [ "poetry-core" ]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.21.5"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|