paid-python 0.3.6__py3-none-any.whl → 0.4.1__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.
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/__init__.py +6 -6
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/__init__.py +2 -2
- opentelemetry/instrumentation/openai/shared/audio_wrappers.py +247 -0
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/chat_wrappers.py +5 -5
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/completion_wrappers.py +5 -5
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/embeddings_wrappers.py +5 -5
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/event_emitter.py +2 -2
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/image_gen_wrappers.py +3 -3
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/utils.py +24 -1
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/v0/__init__.py +6 -6
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/v1/__init__.py +45 -9
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/v1/assistant_wrappers.py +6 -6
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/v1/event_handler_wrapper.py +4 -4
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/v1/responses_wrappers.py +186 -69
- opentelemetry/instrumentation/openai/version.py +1 -0
- paid/client.py +23 -2
- paid/tracing/__init__.py +2 -1
- paid/tracing/autoinstrumentation.py +1 -2
- paid/tracing/tracing.py +16 -0
- {paid_python-0.3.6.dist-info → paid_python-0.4.1.dist-info}/METADATA +9 -1
- {paid_python-0.3.6.dist-info → paid_python-0.4.1.dist-info}/RECORD +26 -28
- paid/_vendor/__init__.py +0 -0
- paid/_vendor/opentelemetry/__init__.py +0 -0
- paid/_vendor/opentelemetry/instrumentation/__init__.py +0 -0
- paid/_vendor/opentelemetry/instrumentation/openai/version.py +0 -1
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/config.py +0 -0
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/event_models.py +0 -0
- {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/span_utils.py +0 -0
- {paid_python-0.3.6.dist-info → paid_python-0.4.1.dist-info}/LICENSE +0 -0
- {paid_python-0.3.6.dist-info → paid_python-0.4.1.dist-info}/WHEEL +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from typing import Callable, Collection, Optional
|
|
2
2
|
|
|
3
3
|
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|
4
|
-
from
|
|
5
|
-
from
|
|
4
|
+
from opentelemetry.instrumentation.openai.shared.config import Config
|
|
5
|
+
from opentelemetry.instrumentation.openai.utils import is_openai_v1
|
|
6
6
|
from typing_extensions import Coroutine
|
|
7
7
|
|
|
8
8
|
_instruments = ("openai >= 0.27.0",)
|
|
@@ -35,20 +35,20 @@ class OpenAIInstrumentor(BaseInstrumentor):
|
|
|
35
35
|
|
|
36
36
|
def _instrument(self, **kwargs):
|
|
37
37
|
if is_openai_v1():
|
|
38
|
-
from
|
|
38
|
+
from opentelemetry.instrumentation.openai.v1 import OpenAIV1Instrumentor
|
|
39
39
|
|
|
40
40
|
OpenAIV1Instrumentor().instrument(**kwargs)
|
|
41
41
|
else:
|
|
42
|
-
from
|
|
42
|
+
from opentelemetry.instrumentation.openai.v0 import OpenAIV0Instrumentor
|
|
43
43
|
|
|
44
44
|
OpenAIV0Instrumentor().instrument(**kwargs)
|
|
45
45
|
|
|
46
46
|
def _uninstrument(self, **kwargs):
|
|
47
47
|
if is_openai_v1():
|
|
48
|
-
from
|
|
48
|
+
from opentelemetry.instrumentation.openai.v1 import OpenAIV1Instrumentor
|
|
49
49
|
|
|
50
50
|
OpenAIV1Instrumentor().uninstrument(**kwargs)
|
|
51
51
|
else:
|
|
52
|
-
from
|
|
52
|
+
from opentelemetry.instrumentation.openai.v0 import OpenAIV0Instrumentor
|
|
53
53
|
|
|
54
54
|
OpenAIV0Instrumentor().uninstrument(**kwargs)
|
|
@@ -5,8 +5,8 @@ import openai
|
|
|
5
5
|
import pydantic
|
|
6
6
|
from importlib.metadata import version
|
|
7
7
|
|
|
8
|
-
from
|
|
9
|
-
from
|
|
8
|
+
from opentelemetry.instrumentation.openai.shared.config import Config
|
|
9
|
+
from opentelemetry.instrumentation.openai.utils import (
|
|
10
10
|
dont_throw,
|
|
11
11
|
is_openai_v1,
|
|
12
12
|
)
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from opentelemetry import context as context_api
|
|
5
|
+
from opentelemetry.instrumentation.openai.shared import (
|
|
6
|
+
_set_client_attributes,
|
|
7
|
+
_set_request_attributes,
|
|
8
|
+
_set_response_attributes,
|
|
9
|
+
_set_span_attribute,
|
|
10
|
+
metric_shared_attributes,
|
|
11
|
+
model_as_dict,
|
|
12
|
+
)
|
|
13
|
+
from opentelemetry.instrumentation.openai.utils import (
|
|
14
|
+
_with_audio_telemetry_wrapper,
|
|
15
|
+
dont_throw,
|
|
16
|
+
is_openai_v1,
|
|
17
|
+
start_as_current_span_async,
|
|
18
|
+
)
|
|
19
|
+
from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY
|
|
20
|
+
from opentelemetry.metrics import Counter, Histogram
|
|
21
|
+
from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
|
|
22
|
+
from opentelemetry.semconv_ai import (
|
|
23
|
+
SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION_KEY,
|
|
24
|
+
SpanAttributes,
|
|
25
|
+
)
|
|
26
|
+
from opentelemetry.trace import SpanKind, Status, StatusCode
|
|
27
|
+
|
|
28
|
+
SPAN_NAME = "openai.audio.transcriptions"
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _get_audio_duration(file):
|
|
34
|
+
"""
|
|
35
|
+
Extract audio duration from file object.
|
|
36
|
+
Returns duration in seconds, or None if unable to determine.
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
# Try to get duration from common audio libraries
|
|
40
|
+
# First check if it's a file-like object with a name attribute
|
|
41
|
+
if hasattr(file, "name"):
|
|
42
|
+
file_path = file.name
|
|
43
|
+
elif isinstance(file, (str, bytes)):
|
|
44
|
+
# If it's a path string or bytes
|
|
45
|
+
return None
|
|
46
|
+
else:
|
|
47
|
+
# If it's a file-like object without name, we can't easily determine duration
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
# Try mutagen (supports many formats)
|
|
51
|
+
try:
|
|
52
|
+
from mutagen import File as MutagenFile
|
|
53
|
+
|
|
54
|
+
audio = MutagenFile(file_path)
|
|
55
|
+
if audio and hasattr(audio.info, "length"):
|
|
56
|
+
return audio.info.length
|
|
57
|
+
except (ImportError, Exception):
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.debug(f"Unable to extract audio duration: {e}")
|
|
62
|
+
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@_with_audio_telemetry_wrapper
|
|
67
|
+
def transcription_wrapper(
|
|
68
|
+
tracer,
|
|
69
|
+
duration_histogram: Histogram,
|
|
70
|
+
exception_counter: Counter,
|
|
71
|
+
wrapped,
|
|
72
|
+
instance,
|
|
73
|
+
args,
|
|
74
|
+
kwargs,
|
|
75
|
+
):
|
|
76
|
+
if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY) or context_api.get_value(
|
|
77
|
+
SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION_KEY
|
|
78
|
+
):
|
|
79
|
+
return wrapped(*args, **kwargs)
|
|
80
|
+
|
|
81
|
+
with tracer.start_as_current_span(
|
|
82
|
+
name=SPAN_NAME,
|
|
83
|
+
kind=SpanKind.CLIENT,
|
|
84
|
+
) as span:
|
|
85
|
+
_handle_request(span, kwargs, instance)
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
# record time for duration
|
|
89
|
+
start_time = time.time()
|
|
90
|
+
response = wrapped(*args, **kwargs)
|
|
91
|
+
end_time = time.time()
|
|
92
|
+
except Exception as e: # pylint: disable=broad-except
|
|
93
|
+
end_time = time.time()
|
|
94
|
+
duration = end_time - start_time if "start_time" in locals() else 0
|
|
95
|
+
attributes = {
|
|
96
|
+
"error.type": e.__class__.__name__,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# if there are legal duration, record it
|
|
100
|
+
if duration > 0 and duration_histogram:
|
|
101
|
+
duration_histogram.record(duration, attributes=attributes)
|
|
102
|
+
if exception_counter:
|
|
103
|
+
exception_counter.add(1, attributes=attributes)
|
|
104
|
+
|
|
105
|
+
span.set_attribute(ERROR_TYPE, e.__class__.__name__)
|
|
106
|
+
span.record_exception(e)
|
|
107
|
+
span.set_status(Status(StatusCode.ERROR, str(e)))
|
|
108
|
+
span.end()
|
|
109
|
+
|
|
110
|
+
raise
|
|
111
|
+
|
|
112
|
+
duration = end_time - start_time
|
|
113
|
+
|
|
114
|
+
_handle_response(
|
|
115
|
+
response,
|
|
116
|
+
span,
|
|
117
|
+
instance,
|
|
118
|
+
duration_histogram,
|
|
119
|
+
duration,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return response
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@_with_audio_telemetry_wrapper
|
|
126
|
+
async def atranscription_wrapper(
|
|
127
|
+
tracer,
|
|
128
|
+
duration_histogram: Histogram,
|
|
129
|
+
exception_counter: Counter,
|
|
130
|
+
wrapped,
|
|
131
|
+
instance,
|
|
132
|
+
args,
|
|
133
|
+
kwargs,
|
|
134
|
+
):
|
|
135
|
+
if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY) or context_api.get_value(
|
|
136
|
+
SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION_KEY
|
|
137
|
+
):
|
|
138
|
+
return await wrapped(*args, **kwargs)
|
|
139
|
+
|
|
140
|
+
async with start_as_current_span_async(
|
|
141
|
+
tracer=tracer,
|
|
142
|
+
name=SPAN_NAME,
|
|
143
|
+
kind=SpanKind.CLIENT,
|
|
144
|
+
) as span:
|
|
145
|
+
_handle_request(span, kwargs, instance)
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
# record time for duration
|
|
149
|
+
start_time = time.time()
|
|
150
|
+
response = await wrapped(*args, **kwargs)
|
|
151
|
+
end_time = time.time()
|
|
152
|
+
except Exception as e: # pylint: disable=broad-except
|
|
153
|
+
end_time = time.time()
|
|
154
|
+
duration = end_time - start_time if "start_time" in locals() else 0
|
|
155
|
+
attributes = {
|
|
156
|
+
"error.type": e.__class__.__name__,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# if there are legal duration, record it
|
|
160
|
+
if duration > 0 and duration_histogram:
|
|
161
|
+
duration_histogram.record(duration, attributes=attributes)
|
|
162
|
+
if exception_counter:
|
|
163
|
+
exception_counter.add(1, attributes=attributes)
|
|
164
|
+
|
|
165
|
+
span.set_attribute(ERROR_TYPE, e.__class__.__name__)
|
|
166
|
+
span.record_exception(e)
|
|
167
|
+
span.set_status(Status(StatusCode.ERROR, str(e)))
|
|
168
|
+
span.end()
|
|
169
|
+
|
|
170
|
+
raise
|
|
171
|
+
|
|
172
|
+
duration = end_time - start_time
|
|
173
|
+
|
|
174
|
+
_handle_response(
|
|
175
|
+
response,
|
|
176
|
+
span,
|
|
177
|
+
instance,
|
|
178
|
+
duration_histogram,
|
|
179
|
+
duration,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return response
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@dont_throw
|
|
186
|
+
def _handle_request(span, kwargs, instance):
|
|
187
|
+
_set_request_attributes(span, kwargs, instance)
|
|
188
|
+
_set_client_attributes(span, instance)
|
|
189
|
+
|
|
190
|
+
# Extract and set audio duration
|
|
191
|
+
file_param = kwargs.get("file")
|
|
192
|
+
if file_param:
|
|
193
|
+
audio_duration = _get_audio_duration(file_param)
|
|
194
|
+
if audio_duration is not None:
|
|
195
|
+
# _set_span_attribute(
|
|
196
|
+
# span, SpanAttributes.LLM_OPENAI_AUDIO_INPUT_DURATION_SECONDS, audio_duration
|
|
197
|
+
# )
|
|
198
|
+
# TODO(Ata): come back here later when semconv is published
|
|
199
|
+
_set_span_attribute(
|
|
200
|
+
span, 'gen_ai.openai.audio.input.duration_seconds', audio_duration
|
|
201
|
+
)
|
|
202
|
+
else:
|
|
203
|
+
print("REMOVE ME : ATA-DBG : COULD NOT READ AUDIO FILE WITH MUTAGEN")
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@dont_throw
|
|
207
|
+
def _handle_response(
|
|
208
|
+
response,
|
|
209
|
+
span,
|
|
210
|
+
instance=None,
|
|
211
|
+
duration_histogram=None,
|
|
212
|
+
duration=None,
|
|
213
|
+
):
|
|
214
|
+
if is_openai_v1():
|
|
215
|
+
response_dict = model_as_dict(response)
|
|
216
|
+
else:
|
|
217
|
+
response_dict = response
|
|
218
|
+
|
|
219
|
+
# metrics record
|
|
220
|
+
_set_transcription_metrics(
|
|
221
|
+
instance,
|
|
222
|
+
duration_histogram,
|
|
223
|
+
response_dict,
|
|
224
|
+
duration,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# span attributes
|
|
228
|
+
_set_response_attributes(span, response_dict)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _set_transcription_metrics(
|
|
232
|
+
instance,
|
|
233
|
+
duration_histogram,
|
|
234
|
+
response_dict,
|
|
235
|
+
duration,
|
|
236
|
+
):
|
|
237
|
+
from opentelemetry.instrumentation.openai.shared import _get_openai_base_url
|
|
238
|
+
|
|
239
|
+
shared_attributes = metric_shared_attributes(
|
|
240
|
+
response_model=response_dict.get("model") or None,
|
|
241
|
+
operation="audio.transcriptions",
|
|
242
|
+
server_address=_get_openai_base_url(instance),
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# duration metrics
|
|
246
|
+
if duration and isinstance(duration, (float, int)) and duration_histogram:
|
|
247
|
+
duration_histogram.record(duration, attributes=shared_attributes)
|
|
@@ -8,7 +8,7 @@ from typing import List, Optional, Union
|
|
|
8
8
|
|
|
9
9
|
from opentelemetry import context as context_api
|
|
10
10
|
import pydantic
|
|
11
|
-
from
|
|
11
|
+
from opentelemetry.instrumentation.openai.shared import (
|
|
12
12
|
OPENAI_LLM_USAGE_TOKEN_TYPES,
|
|
13
13
|
_get_openai_base_url,
|
|
14
14
|
_set_client_attributes,
|
|
@@ -24,14 +24,14 @@ from paid._vendor.opentelemetry.instrumentation.openai.shared import (
|
|
|
24
24
|
propagate_trace_context,
|
|
25
25
|
set_tools_attributes,
|
|
26
26
|
)
|
|
27
|
-
from
|
|
28
|
-
from
|
|
29
|
-
from
|
|
27
|
+
from opentelemetry.instrumentation.openai.shared.config import Config
|
|
28
|
+
from opentelemetry.instrumentation.openai.shared.event_emitter import emit_event
|
|
29
|
+
from opentelemetry.instrumentation.openai.shared.event_models import (
|
|
30
30
|
ChoiceEvent,
|
|
31
31
|
MessageEvent,
|
|
32
32
|
ToolCall,
|
|
33
33
|
)
|
|
34
|
-
from
|
|
34
|
+
from opentelemetry.instrumentation.openai.utils import (
|
|
35
35
|
_with_chat_telemetry_wrapper,
|
|
36
36
|
dont_throw,
|
|
37
37
|
is_openai_v1,
|
{paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/completion_wrappers.py
RENAMED
|
@@ -2,7 +2,7 @@ import logging
|
|
|
2
2
|
|
|
3
3
|
from opentelemetry import context as context_api
|
|
4
4
|
from opentelemetry import trace
|
|
5
|
-
from
|
|
5
|
+
from opentelemetry.instrumentation.openai.shared import (
|
|
6
6
|
_set_client_attributes,
|
|
7
7
|
_set_functions_attributes,
|
|
8
8
|
_set_request_attributes,
|
|
@@ -13,14 +13,14 @@ from paid._vendor.opentelemetry.instrumentation.openai.shared import (
|
|
|
13
13
|
model_as_dict,
|
|
14
14
|
propagate_trace_context,
|
|
15
15
|
)
|
|
16
|
-
from
|
|
16
|
+
from opentelemetry.instrumentation.openai.shared.config import Config
|
|
17
17
|
from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
|
|
18
|
-
from
|
|
19
|
-
from
|
|
18
|
+
from opentelemetry.instrumentation.openai.shared.event_emitter import emit_event
|
|
19
|
+
from opentelemetry.instrumentation.openai.shared.event_models import (
|
|
20
20
|
ChoiceEvent,
|
|
21
21
|
MessageEvent,
|
|
22
22
|
)
|
|
23
|
-
from
|
|
23
|
+
from opentelemetry.instrumentation.openai.utils import (
|
|
24
24
|
_with_tracer_wrapper,
|
|
25
25
|
dont_throw,
|
|
26
26
|
is_openai_v1,
|
{paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/embeddings_wrappers.py
RENAMED
|
@@ -3,7 +3,7 @@ import time
|
|
|
3
3
|
from collections.abc import Iterable
|
|
4
4
|
|
|
5
5
|
from opentelemetry import context as context_api
|
|
6
|
-
from
|
|
6
|
+
from opentelemetry.instrumentation.openai.shared import (
|
|
7
7
|
OPENAI_LLM_USAGE_TOKEN_TYPES,
|
|
8
8
|
_get_openai_base_url,
|
|
9
9
|
_set_client_attributes,
|
|
@@ -15,13 +15,13 @@ from paid._vendor.opentelemetry.instrumentation.openai.shared import (
|
|
|
15
15
|
model_as_dict,
|
|
16
16
|
propagate_trace_context,
|
|
17
17
|
)
|
|
18
|
-
from
|
|
19
|
-
from
|
|
20
|
-
from
|
|
18
|
+
from opentelemetry.instrumentation.openai.shared.config import Config
|
|
19
|
+
from opentelemetry.instrumentation.openai.shared.event_emitter import emit_event
|
|
20
|
+
from opentelemetry.instrumentation.openai.shared.event_models import (
|
|
21
21
|
ChoiceEvent,
|
|
22
22
|
MessageEvent,
|
|
23
23
|
)
|
|
24
|
-
from
|
|
24
|
+
from opentelemetry.instrumentation.openai.utils import (
|
|
25
25
|
_with_embeddings_telemetry_wrapper,
|
|
26
26
|
dont_throw,
|
|
27
27
|
is_openai_v1,
|
|
@@ -3,11 +3,11 @@ from enum import Enum
|
|
|
3
3
|
from typing import Union
|
|
4
4
|
|
|
5
5
|
from opentelemetry._logs import LogRecord
|
|
6
|
-
from
|
|
6
|
+
from opentelemetry.instrumentation.openai.shared.event_models import (
|
|
7
7
|
ChoiceEvent,
|
|
8
8
|
MessageEvent,
|
|
9
9
|
)
|
|
10
|
-
from
|
|
10
|
+
from opentelemetry.instrumentation.openai.utils import (
|
|
11
11
|
should_emit_events,
|
|
12
12
|
should_send_prompts,
|
|
13
13
|
)
|
{paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/image_gen_wrappers.py
RENAMED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import time
|
|
2
2
|
|
|
3
3
|
from opentelemetry import context as context_api
|
|
4
|
-
from
|
|
5
|
-
from
|
|
4
|
+
from opentelemetry.instrumentation.openai import is_openai_v1
|
|
5
|
+
from opentelemetry.instrumentation.openai.shared import (
|
|
6
6
|
_get_openai_base_url,
|
|
7
7
|
metric_shared_attributes,
|
|
8
8
|
model_as_dict,
|
|
9
9
|
)
|
|
10
|
-
from
|
|
10
|
+
from opentelemetry.instrumentation.openai.utils import (
|
|
11
11
|
_with_image_gen_metric_wrapper,
|
|
12
12
|
)
|
|
13
13
|
from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY
|
|
@@ -9,7 +9,7 @@ from packaging import version as pkg_version
|
|
|
9
9
|
|
|
10
10
|
from opentelemetry import context as context_api
|
|
11
11
|
from opentelemetry._logs import Logger
|
|
12
|
-
from
|
|
12
|
+
from opentelemetry.instrumentation.openai.shared.config import Config
|
|
13
13
|
|
|
14
14
|
import openai
|
|
15
15
|
|
|
@@ -83,6 +83,29 @@ def _with_embeddings_telemetry_wrapper(func):
|
|
|
83
83
|
return _with_embeddings_telemetry
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
def _with_audio_telemetry_wrapper(func):
|
|
87
|
+
"""Wrapper to convert the audio wrapper function into the expected format for wrapt."""
|
|
88
|
+
def _with_audio_telemetry(
|
|
89
|
+
tracer,
|
|
90
|
+
duration_histogram,
|
|
91
|
+
exception_counter,
|
|
92
|
+
):
|
|
93
|
+
def wrapper(wrapped, instance, args, kwargs):
|
|
94
|
+
return func(
|
|
95
|
+
tracer,
|
|
96
|
+
duration_histogram,
|
|
97
|
+
exception_counter,
|
|
98
|
+
wrapped,
|
|
99
|
+
instance,
|
|
100
|
+
args,
|
|
101
|
+
kwargs,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return wrapper
|
|
105
|
+
|
|
106
|
+
return _with_audio_telemetry
|
|
107
|
+
|
|
108
|
+
|
|
86
109
|
def _with_chat_telemetry_wrapper(func):
|
|
87
110
|
def _with_chat_telemetry(
|
|
88
111
|
tracer,
|
|
@@ -2,21 +2,21 @@ from typing import Collection
|
|
|
2
2
|
|
|
3
3
|
from opentelemetry._logs import get_logger
|
|
4
4
|
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|
5
|
-
from
|
|
5
|
+
from opentelemetry.instrumentation.openai.shared.chat_wrappers import (
|
|
6
6
|
achat_wrapper,
|
|
7
7
|
chat_wrapper,
|
|
8
8
|
)
|
|
9
|
-
from
|
|
9
|
+
from opentelemetry.instrumentation.openai.shared.completion_wrappers import (
|
|
10
10
|
acompletion_wrapper,
|
|
11
11
|
completion_wrapper,
|
|
12
12
|
)
|
|
13
|
-
from
|
|
14
|
-
from
|
|
13
|
+
from opentelemetry.instrumentation.openai.shared.config import Config
|
|
14
|
+
from opentelemetry.instrumentation.openai.shared.embeddings_wrappers import (
|
|
15
15
|
aembeddings_wrapper,
|
|
16
16
|
embeddings_wrapper,
|
|
17
17
|
)
|
|
18
|
-
from
|
|
19
|
-
from
|
|
18
|
+
from opentelemetry.instrumentation.openai.utils import is_metrics_enabled
|
|
19
|
+
from opentelemetry.instrumentation.openai.version import __version__
|
|
20
20
|
from opentelemetry.instrumentation.utils import unwrap
|
|
21
21
|
from opentelemetry.metrics import get_meter
|
|
22
22
|
from opentelemetry.semconv._incubating.metrics import gen_ai_metrics as GenAIMetrics
|
|
@@ -2,24 +2,28 @@ from typing import Collection
|
|
|
2
2
|
|
|
3
3
|
from opentelemetry._logs import get_logger
|
|
4
4
|
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|
5
|
-
from
|
|
5
|
+
from opentelemetry.instrumentation.openai.shared.chat_wrappers import (
|
|
6
6
|
achat_wrapper,
|
|
7
7
|
chat_wrapper,
|
|
8
8
|
)
|
|
9
|
-
from
|
|
9
|
+
from opentelemetry.instrumentation.openai.shared.completion_wrappers import (
|
|
10
10
|
acompletion_wrapper,
|
|
11
11
|
completion_wrapper,
|
|
12
12
|
)
|
|
13
|
-
from
|
|
14
|
-
from
|
|
13
|
+
from opentelemetry.instrumentation.openai.shared.config import Config
|
|
14
|
+
from opentelemetry.instrumentation.openai.shared.embeddings_wrappers import (
|
|
15
15
|
aembeddings_wrapper,
|
|
16
16
|
embeddings_wrapper,
|
|
17
17
|
)
|
|
18
|
-
from
|
|
18
|
+
from opentelemetry.instrumentation.openai.shared.image_gen_wrappers import (
|
|
19
19
|
image_gen_metrics_wrapper,
|
|
20
20
|
)
|
|
21
|
-
from
|
|
22
|
-
|
|
21
|
+
from opentelemetry.instrumentation.openai.shared.audio_wrappers import (
|
|
22
|
+
atranscription_wrapper,
|
|
23
|
+
transcription_wrapper,
|
|
24
|
+
)
|
|
25
|
+
from opentelemetry.instrumentation.openai.utils import is_metrics_enabled
|
|
26
|
+
from opentelemetry.instrumentation.openai.v1.assistant_wrappers import (
|
|
23
27
|
assistants_create_wrapper,
|
|
24
28
|
messages_list_wrapper,
|
|
25
29
|
runs_create_and_stream_wrapper,
|
|
@@ -27,14 +31,14 @@ from paid._vendor.opentelemetry.instrumentation.openai.v1.assistant_wrappers imp
|
|
|
27
31
|
runs_retrieve_wrapper,
|
|
28
32
|
)
|
|
29
33
|
|
|
30
|
-
from
|
|
34
|
+
from opentelemetry.instrumentation.openai.v1.responses_wrappers import (
|
|
31
35
|
async_responses_cancel_wrapper,
|
|
32
36
|
async_responses_get_or_create_wrapper,
|
|
33
37
|
responses_cancel_wrapper,
|
|
34
38
|
responses_get_or_create_wrapper,
|
|
35
39
|
)
|
|
36
40
|
|
|
37
|
-
from
|
|
41
|
+
from opentelemetry.instrumentation.openai.version import __version__
|
|
38
42
|
from opentelemetry.instrumentation.utils import unwrap
|
|
39
43
|
from opentelemetry.metrics import get_meter
|
|
40
44
|
from opentelemetry.semconv._incubating.metrics import gen_ai_metrics as GenAIMetrics
|
|
@@ -247,6 +251,36 @@ class OpenAIV1Instrumentor(BaseInstrumentor):
|
|
|
247
251
|
image_gen_metrics_wrapper(duration_histogram, image_gen_exception_counter),
|
|
248
252
|
)
|
|
249
253
|
|
|
254
|
+
if is_metrics_enabled():
|
|
255
|
+
audio_transcription_exception_counter = meter.create_counter(
|
|
256
|
+
# name=Meters.LLM_AUDIO_TRANSCRIPTIONS_EXCEPTIONS, # TODO(Ata): come back here later when semconv is published
|
|
257
|
+
name='llm.openai.audio.transcriptions.exceptions',
|
|
258
|
+
unit="time",
|
|
259
|
+
description="Number of exceptions occurred during audio transcriptions operation",
|
|
260
|
+
)
|
|
261
|
+
else:
|
|
262
|
+
audio_transcription_exception_counter = None
|
|
263
|
+
|
|
264
|
+
wrap_function_wrapper(
|
|
265
|
+
"openai.resources.audio.transcriptions",
|
|
266
|
+
"Transcriptions.create",
|
|
267
|
+
transcription_wrapper(
|
|
268
|
+
tracer,
|
|
269
|
+
duration_histogram,
|
|
270
|
+
audio_transcription_exception_counter,
|
|
271
|
+
),
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
wrap_function_wrapper(
|
|
275
|
+
"openai.resources.audio.transcriptions",
|
|
276
|
+
"AsyncTranscriptions.create",
|
|
277
|
+
atranscription_wrapper(
|
|
278
|
+
tracer,
|
|
279
|
+
duration_histogram,
|
|
280
|
+
audio_transcription_exception_counter,
|
|
281
|
+
),
|
|
282
|
+
)
|
|
283
|
+
|
|
250
284
|
# Beta APIs may not be available consistently in all versions
|
|
251
285
|
self._try_wrap(
|
|
252
286
|
"openai.resources.beta.assistants",
|
|
@@ -338,6 +372,8 @@ class OpenAIV1Instrumentor(BaseInstrumentor):
|
|
|
338
372
|
unwrap("openai.resources.completions", "AsyncCompletions.create")
|
|
339
373
|
unwrap("openai.resources.embeddings", "AsyncEmbeddings.create")
|
|
340
374
|
unwrap("openai.resources.images", "Images.generate")
|
|
375
|
+
unwrap("openai.resources.audio.transcriptions", "Transcriptions.create")
|
|
376
|
+
unwrap("openai.resources.audio.transcriptions", "AsyncTranscriptions.create")
|
|
341
377
|
|
|
342
378
|
# Beta APIs may not be available consistently in all versions
|
|
343
379
|
try:
|
{paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/v1/assistant_wrappers.py
RENAMED
|
@@ -3,17 +3,17 @@ import time
|
|
|
3
3
|
|
|
4
4
|
from opentelemetry import context as context_api
|
|
5
5
|
from opentelemetry import trace
|
|
6
|
-
from
|
|
6
|
+
from opentelemetry.instrumentation.openai.shared import (
|
|
7
7
|
_set_span_attribute,
|
|
8
8
|
model_as_dict,
|
|
9
9
|
)
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from
|
|
10
|
+
from opentelemetry.instrumentation.openai.shared.config import Config
|
|
11
|
+
from opentelemetry.instrumentation.openai.shared.event_emitter import emit_event
|
|
12
|
+
from opentelemetry.instrumentation.openai.shared.event_models import (
|
|
13
13
|
ChoiceEvent,
|
|
14
14
|
MessageEvent,
|
|
15
15
|
)
|
|
16
|
-
from
|
|
16
|
+
from opentelemetry.instrumentation.openai.utils import (
|
|
17
17
|
_with_tracer_wrapper,
|
|
18
18
|
dont_throw,
|
|
19
19
|
should_emit_events,
|
|
@@ -309,7 +309,7 @@ def runs_create_and_stream_wrapper(tracer, wrapped, instance, args, kwargs):
|
|
|
309
309
|
span, f"{GenAIAttributes.GEN_AI_PROMPT}.{i}.content", instructions
|
|
310
310
|
)
|
|
311
311
|
|
|
312
|
-
from
|
|
312
|
+
from opentelemetry.instrumentation.openai.v1.event_handler_wrapper import (
|
|
313
313
|
EventHandleWrapper,
|
|
314
314
|
)
|
|
315
315
|
|
{paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/v1/event_handler_wrapper.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
3
|
-
from
|
|
4
|
-
from
|
|
1
|
+
from opentelemetry.instrumentation.openai.shared import _set_span_attribute
|
|
2
|
+
from opentelemetry.instrumentation.openai.shared.event_emitter import emit_event
|
|
3
|
+
from opentelemetry.instrumentation.openai.shared.event_models import ChoiceEvent
|
|
4
|
+
from opentelemetry.instrumentation.openai.utils import should_emit_events
|
|
5
5
|
from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
|
|
6
6
|
from opentelemetry.semconv._incubating.attributes import (
|
|
7
7
|
gen_ai_attributes as GenAIAttributes,
|