paid-python 0.4.0__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.
Files changed (29) hide show
  1. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/__init__.py +6 -6
  2. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/__init__.py +2 -2
  3. opentelemetry/instrumentation/openai/shared/audio_wrappers.py +247 -0
  4. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/chat_wrappers.py +5 -5
  5. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/completion_wrappers.py +5 -5
  6. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/embeddings_wrappers.py +5 -5
  7. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/event_emitter.py +2 -2
  8. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/image_gen_wrappers.py +3 -3
  9. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/utils.py +24 -1
  10. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/v0/__init__.py +6 -6
  11. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/v1/__init__.py +45 -9
  12. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/v1/assistant_wrappers.py +6 -6
  13. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/v1/event_handler_wrapper.py +4 -4
  14. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/v1/responses_wrappers.py +186 -69
  15. opentelemetry/instrumentation/openai/version.py +1 -0
  16. paid/client.py +23 -2
  17. paid/tracing/__init__.py +1 -1
  18. paid/tracing/autoinstrumentation.py +1 -2
  19. {paid_python-0.4.0.dist-info → paid_python-0.4.1.dist-info}/METADATA +1 -1
  20. {paid_python-0.4.0.dist-info → paid_python-0.4.1.dist-info}/RECORD +25 -27
  21. paid/_vendor/__init__.py +0 -0
  22. paid/_vendor/opentelemetry/__init__.py +0 -0
  23. paid/_vendor/opentelemetry/instrumentation/__init__.py +0 -0
  24. paid/_vendor/opentelemetry/instrumentation/openai/version.py +0 -1
  25. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/config.py +0 -0
  26. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/event_models.py +0 -0
  27. {paid/_vendor/opentelemetry → opentelemetry}/instrumentation/openai/shared/span_utils.py +0 -0
  28. {paid_python-0.4.0.dist-info → paid_python-0.4.1.dist-info}/LICENSE +0 -0
  29. {paid_python-0.4.0.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 paid._vendor.opentelemetry.instrumentation.openai.shared.config import Config
5
- from paid._vendor.opentelemetry.instrumentation.openai.utils import is_openai_v1
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 paid._vendor.opentelemetry.instrumentation.openai.v1 import OpenAIV1Instrumentor
38
+ from opentelemetry.instrumentation.openai.v1 import OpenAIV1Instrumentor
39
39
 
40
40
  OpenAIV1Instrumentor().instrument(**kwargs)
41
41
  else:
42
- from paid._vendor.opentelemetry.instrumentation.openai.v0 import OpenAIV0Instrumentor
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 paid._vendor.opentelemetry.instrumentation.openai.v1 import OpenAIV1Instrumentor
48
+ from opentelemetry.instrumentation.openai.v1 import OpenAIV1Instrumentor
49
49
 
50
50
  OpenAIV1Instrumentor().uninstrument(**kwargs)
51
51
  else:
52
- from paid._vendor.opentelemetry.instrumentation.openai.v0 import OpenAIV0Instrumentor
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 paid._vendor.opentelemetry.instrumentation.openai.shared.config import Config
9
- from paid._vendor.opentelemetry.instrumentation.openai.utils import (
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 paid._vendor.opentelemetry.instrumentation.openai.shared import (
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 paid._vendor.opentelemetry.instrumentation.openai.shared.config import Config
28
- from paid._vendor.opentelemetry.instrumentation.openai.shared.event_emitter import emit_event
29
- from paid._vendor.opentelemetry.instrumentation.openai.shared.event_models import (
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 paid._vendor.opentelemetry.instrumentation.openai.utils import (
34
+ from opentelemetry.instrumentation.openai.utils import (
35
35
  _with_chat_telemetry_wrapper,
36
36
  dont_throw,
37
37
  is_openai_v1,
@@ -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 paid._vendor.opentelemetry.instrumentation.openai.shared import (
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 paid._vendor.opentelemetry.instrumentation.openai.shared.config import Config
16
+ from opentelemetry.instrumentation.openai.shared.config import Config
17
17
  from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
18
- from paid._vendor.opentelemetry.instrumentation.openai.shared.event_emitter import emit_event
19
- from paid._vendor.opentelemetry.instrumentation.openai.shared.event_models import (
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 paid._vendor.opentelemetry.instrumentation.openai.utils import (
23
+ from opentelemetry.instrumentation.openai.utils import (
24
24
  _with_tracer_wrapper,
25
25
  dont_throw,
26
26
  is_openai_v1,
@@ -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 paid._vendor.opentelemetry.instrumentation.openai.shared import (
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 paid._vendor.opentelemetry.instrumentation.openai.shared.config import Config
19
- from paid._vendor.opentelemetry.instrumentation.openai.shared.event_emitter import emit_event
20
- from paid._vendor.opentelemetry.instrumentation.openai.shared.event_models import (
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 paid._vendor.opentelemetry.instrumentation.openai.utils import (
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 paid._vendor.opentelemetry.instrumentation.openai.shared.event_models import (
6
+ from opentelemetry.instrumentation.openai.shared.event_models import (
7
7
  ChoiceEvent,
8
8
  MessageEvent,
9
9
  )
10
- from paid._vendor.opentelemetry.instrumentation.openai.utils import (
10
+ from opentelemetry.instrumentation.openai.utils import (
11
11
  should_emit_events,
12
12
  should_send_prompts,
13
13
  )
@@ -1,13 +1,13 @@
1
1
  import time
2
2
 
3
3
  from opentelemetry import context as context_api
4
- from paid._vendor.opentelemetry.instrumentation.openai import is_openai_v1
5
- from paid._vendor.opentelemetry.instrumentation.openai.shared import (
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 paid._vendor.opentelemetry.instrumentation.openai.utils import (
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 paid._vendor.opentelemetry.instrumentation.openai.shared.config import Config
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 paid._vendor.opentelemetry.instrumentation.openai.shared.chat_wrappers import (
5
+ from opentelemetry.instrumentation.openai.shared.chat_wrappers import (
6
6
  achat_wrapper,
7
7
  chat_wrapper,
8
8
  )
9
- from paid._vendor.opentelemetry.instrumentation.openai.shared.completion_wrappers import (
9
+ from opentelemetry.instrumentation.openai.shared.completion_wrappers import (
10
10
  acompletion_wrapper,
11
11
  completion_wrapper,
12
12
  )
13
- from paid._vendor.opentelemetry.instrumentation.openai.shared.config import Config
14
- from paid._vendor.opentelemetry.instrumentation.openai.shared.embeddings_wrappers import (
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 paid._vendor.opentelemetry.instrumentation.openai.utils import is_metrics_enabled
19
- from paid._vendor.opentelemetry.instrumentation.openai.version import __version__
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 paid._vendor.opentelemetry.instrumentation.openai.shared.chat_wrappers import (
5
+ from opentelemetry.instrumentation.openai.shared.chat_wrappers import (
6
6
  achat_wrapper,
7
7
  chat_wrapper,
8
8
  )
9
- from paid._vendor.opentelemetry.instrumentation.openai.shared.completion_wrappers import (
9
+ from opentelemetry.instrumentation.openai.shared.completion_wrappers import (
10
10
  acompletion_wrapper,
11
11
  completion_wrapper,
12
12
  )
13
- from paid._vendor.opentelemetry.instrumentation.openai.shared.config import Config
14
- from paid._vendor.opentelemetry.instrumentation.openai.shared.embeddings_wrappers import (
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 paid._vendor.opentelemetry.instrumentation.openai.shared.image_gen_wrappers import (
18
+ from opentelemetry.instrumentation.openai.shared.image_gen_wrappers import (
19
19
  image_gen_metrics_wrapper,
20
20
  )
21
- from paid._vendor.opentelemetry.instrumentation.openai.utils import is_metrics_enabled
22
- from paid._vendor.opentelemetry.instrumentation.openai.v1.assistant_wrappers import (
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 paid._vendor.opentelemetry.instrumentation.openai.v1.responses_wrappers import (
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 paid._vendor.opentelemetry.instrumentation.openai.version import __version__
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:
@@ -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 paid._vendor.opentelemetry.instrumentation.openai.shared import (
6
+ from opentelemetry.instrumentation.openai.shared import (
7
7
  _set_span_attribute,
8
8
  model_as_dict,
9
9
  )
10
- from paid._vendor.opentelemetry.instrumentation.openai.shared.config import Config
11
- from paid._vendor.opentelemetry.instrumentation.openai.shared.event_emitter import emit_event
12
- from paid._vendor.opentelemetry.instrumentation.openai.shared.event_models import (
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 paid._vendor.opentelemetry.instrumentation.openai.utils import (
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 paid._vendor.opentelemetry.instrumentation.openai.v1.event_handler_wrapper import (
312
+ from opentelemetry.instrumentation.openai.v1.event_handler_wrapper import (
313
313
  EventHandleWrapper,
314
314
  )
315
315
 
@@ -1,7 +1,7 @@
1
- from paid._vendor.opentelemetry.instrumentation.openai.shared import _set_span_attribute
2
- from paid._vendor.opentelemetry.instrumentation.openai.shared.event_emitter import emit_event
3
- from paid._vendor.opentelemetry.instrumentation.openai.shared.event_models import ChoiceEvent
4
- from paid._vendor.opentelemetry.instrumentation.openai.utils import should_emit_events
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,