openlit 1.34.19__py3-none-any.whl → 1.34.22__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.
openlit/__helpers.py CHANGED
@@ -402,3 +402,43 @@ def record_embedding_metrics(metrics, gen_ai_operation, gen_ai_system, server_ad
402
402
  metrics["genai_requests"].add(1, attributes)
403
403
  metrics["genai_prompt_tokens"].add(input_tokens, attributes)
404
404
  metrics["genai_cost"].record(cost, attributes)
405
+
406
+ def record_audio_metrics(metrics, gen_ai_operation, gen_ai_system, server_address, server_port,
407
+ request_model, response_model, environment, application_name, start_time, end_time, cost):
408
+ """
409
+ Record audio-specific metrics for the operation.
410
+ """
411
+
412
+ attributes = create_metrics_attributes(
413
+ operation=gen_ai_operation,
414
+ system=gen_ai_system,
415
+ server_address=server_address,
416
+ server_port=server_port,
417
+ request_model=request_model,
418
+ response_model=response_model,
419
+ service_name=application_name,
420
+ deployment_environment=environment,
421
+ )
422
+ metrics["genai_client_operation_duration"].record(end_time - start_time, attributes)
423
+ metrics["genai_requests"].add(1, attributes)
424
+ metrics["genai_cost"].record(cost, attributes)
425
+
426
+ def record_image_metrics(metrics, gen_ai_operation, gen_ai_system, server_address, server_port,
427
+ request_model, response_model, environment, application_name, start_time, end_time, cost):
428
+ """
429
+ Record image-specific metrics for the operation.
430
+ """
431
+
432
+ attributes = create_metrics_attributes(
433
+ operation=gen_ai_operation,
434
+ system=gen_ai_system,
435
+ server_address=server_address,
436
+ server_port=server_port,
437
+ request_model=request_model,
438
+ response_model=response_model,
439
+ service_name=application_name,
440
+ deployment_environment=environment,
441
+ )
442
+ metrics["genai_client_operation_duration"].record(end_time - start_time, attributes)
443
+ metrics["genai_requests"].add(1, attributes)
444
+ metrics["genai_cost"].record(cost, attributes)
@@ -1,4 +1,3 @@
1
- # pylint: disable=useless-return, bad-staticmethod-argument, disable=duplicate-code
2
1
  """Initializer of Auto Instrumentation of AWS Bedrock Functions"""
3
2
 
4
3
  from typing import Collection
@@ -6,37 +5,43 @@ import importlib.metadata
6
5
  from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
7
6
  from wrapt import wrap_function_wrapper
8
7
 
9
- from openlit.instrumentation.bedrock.bedrock import converse
8
+ from openlit.instrumentation.bedrock.bedrock import converse, converse_stream
10
9
 
11
10
  _instruments = ("boto3 >= 1.34.138",)
12
11
 
13
12
  class BedrockInstrumentor(BaseInstrumentor):
14
13
  """
15
- An instrumentor for AWS Bedrock's client library.
14
+ An instrumentor for AWS Bedrock client library.
16
15
  """
17
16
 
18
17
  def instrumentation_dependencies(self) -> Collection[str]:
19
18
  return _instruments
20
19
 
21
20
  def _instrument(self, **kwargs):
22
- application_name = kwargs.get("application_name", "default_application")
23
- environment = kwargs.get("environment", "default_environment")
21
+ version = importlib.metadata.version("boto3")
22
+ environment = kwargs.get("environment", "default")
23
+ application_name = kwargs.get("application_name", "default")
24
24
  tracer = kwargs.get("tracer")
25
- event_provider = kwargs.get('event_provider')
26
- metrics = kwargs.get("metrics_dict")
27
25
  pricing_info = kwargs.get("pricing_info", {})
28
26
  capture_message_content = kwargs.get("capture_message_content", False)
27
+ metrics = kwargs.get("metrics_dict")
29
28
  disable_metrics = kwargs.get("disable_metrics")
30
- version = importlib.metadata.version("boto3")
31
29
 
32
- #sync
30
+ # sync
31
+ wrap_function_wrapper(
32
+ "botocore.client",
33
+ "ClientCreator.create_client",
34
+ converse(version, environment, application_name, tracer, pricing_info,
35
+ capture_message_content, metrics, disable_metrics),
36
+ )
37
+
38
+ # streaming
33
39
  wrap_function_wrapper(
34
- "botocore.client",
35
- "ClientCreator.create_client",
36
- converse(version, environment, application_name,
37
- tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
40
+ "botocore.client",
41
+ "ClientCreator.create_client",
42
+ converse_stream(version, environment, application_name, tracer, pricing_info,
43
+ capture_message_content, metrics, disable_metrics),
38
44
  )
39
45
 
40
46
  def _uninstrument(self, **kwargs):
41
- # Proper uninstrumentation logic to revert patched methods
42
47
  pass
@@ -2,64 +2,65 @@
2
2
  Module for monitoring Amazon Bedrock API calls.
3
3
  """
4
4
 
5
- import logging
6
5
  import time
7
6
  from opentelemetry.trace import SpanKind
8
7
  from openlit.__helpers import (
8
+ handle_exception,
9
9
  set_server_address_and_port
10
10
  )
11
11
  from openlit.instrumentation.bedrock.utils import (
12
+ process_chunk,
12
13
  process_chat_response,
14
+ process_streaming_chat_response,
13
15
  )
14
16
  from openlit.semcov import SemanticConvention
15
17
 
16
- # Initialize logger for logging potential issues and operations
17
- logger = logging.getLogger(__name__)
18
-
19
- def converse(version, environment, application_name, tracer, event_provider,
20
- pricing_info, capture_message_content, metrics, disable_metrics):
18
+ def converse(version, environment, application_name, tracer, pricing_info, capture_message_content, metrics, disable_metrics):
21
19
  """
22
- Generates a telemetry wrapper for GenAI function call
20
+ Generates a telemetry wrapper for AWS Bedrock converse calls.
23
21
  """
24
22
 
25
23
  def wrapper(wrapped, instance, args, kwargs):
26
24
  """
27
- Wraps the GenAI function call.
25
+ Wraps the ClientCreator.create_client call.
28
26
  """
29
27
 
30
28
  def converse_wrapper(original_method, *method_args, **method_kwargs):
31
-
32
29
  """
33
- Wraps the GenAI function call.
30
+ Wraps the individual converse method call.
34
31
  """
35
32
 
36
- server_address, server_port = set_server_address_and_port(instance, 'aws.amazon.com', 443)
37
- request_model = method_kwargs.get('modelId', 'amazon.titan-text-express-v1')
33
+ server_address, server_port = set_server_address_and_port(instance, "aws.amazon.com", 443)
34
+ request_model = method_kwargs.get("modelId", "amazon.titan-text-express-v1")
38
35
 
39
- span_name = f'{SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT} {request_model}'
36
+ span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT} {request_model}"
40
37
 
41
38
  with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
42
39
  start_time = time.time()
43
40
  response = original_method(*method_args, **method_kwargs)
44
- llm_config = method_kwargs.get('inferenceConfig', {})
45
- response = process_chat_response(
46
- response=response,
47
- request_model=request_model,
48
- pricing_info=pricing_info,
49
- server_port=server_port,
50
- server_address=server_address,
51
- environment=environment,
52
- application_name=application_name,
53
- metrics=metrics,
54
- event_provider=event_provider,
55
- start_time=start_time,
56
- span=span,
57
- capture_message_content=capture_message_content,
58
- disable_metrics=disable_metrics,
59
- version=version,
60
- llm_config=llm_config,
61
- **method_kwargs
62
- )
41
+ llm_config = method_kwargs.get("inferenceConfig", {})
42
+
43
+ try:
44
+ response = process_chat_response(
45
+ response=response,
46
+ request_model=request_model,
47
+ pricing_info=pricing_info,
48
+ server_port=server_port,
49
+ server_address=server_address,
50
+ environment=environment,
51
+ application_name=application_name,
52
+ metrics=metrics,
53
+ start_time=start_time,
54
+ span=span,
55
+ capture_message_content=capture_message_content,
56
+ disable_metrics=disable_metrics,
57
+ version=version,
58
+ llm_config=llm_config,
59
+ **method_kwargs
60
+ )
61
+
62
+ except Exception as e:
63
+ handle_exception(span, e)
63
64
 
64
65
  return response
65
66
 
@@ -67,10 +68,143 @@ def converse(version, environment, application_name, tracer, event_provider,
67
68
  client = wrapped(*args, **kwargs)
68
69
 
69
70
  # Replace the original method with the instrumented one
70
- if kwargs.get('service_name') == 'bedrock-runtime':
71
+ if kwargs.get("service_name") == "bedrock-runtime":
71
72
  original_invoke_model = client.converse
72
- client.converse = lambda *args, **kwargs: converse_wrapper(original_invoke_model,
73
- *args, **kwargs)
73
+ client.converse = lambda *args, **kwargs: converse_wrapper(original_invoke_model, *args, **kwargs)
74
+
75
+ return client
76
+
77
+ return wrapper
78
+
79
+ def converse_stream(version, environment, application_name, tracer, pricing_info, capture_message_content, metrics, disable_metrics):
80
+ """
81
+ Generates a telemetry wrapper for AWS Bedrock converse_stream calls.
82
+ """
83
+
84
+ class TracedSyncStream:
85
+ """
86
+ Wrapper for streaming responses to collect telemetry.
87
+ """
88
+
89
+ def __init__(
90
+ self,
91
+ wrapped_response,
92
+ span,
93
+ span_name,
94
+ kwargs,
95
+ server_address,
96
+ server_port,
97
+ **args,
98
+ ):
99
+ self.__wrapped_response = wrapped_response
100
+ # Extract the actual stream iterator from the response
101
+ if isinstance(wrapped_response, dict) and "stream" in wrapped_response:
102
+ self.__wrapped_stream = iter(wrapped_response["stream"])
103
+ else:
104
+ self.__wrapped_stream = iter(wrapped_response)
105
+
106
+ self._span = span
107
+ self._span_name = span_name
108
+ self._llmresponse = ""
109
+ self._response_id = ""
110
+ self._response_model = ""
111
+ self._finish_reason = ""
112
+ self._tools = None
113
+ self._input_tokens = 0
114
+ self._output_tokens = 0
115
+
116
+ self._args = args
117
+ self._kwargs = kwargs
118
+ self._start_time = time.time()
119
+ self._end_time = None
120
+ self._timestamps = []
121
+ self._ttft = 0
122
+ self._tbt = 0
123
+ self._server_address = server_address
124
+ self._server_port = server_port
125
+
126
+ def __enter__(self):
127
+ if hasattr(self.__wrapped_stream, "__enter__"):
128
+ self.__wrapped_stream.__enter__()
129
+ return self
130
+
131
+ def __exit__(self, exc_type, exc_value, traceback):
132
+ if hasattr(self.__wrapped_stream, "__exit__"):
133
+ self.__wrapped_stream.__exit__(exc_type, exc_value, traceback)
134
+
135
+ def __iter__(self):
136
+ return self
137
+
138
+ def __getattr__(self, name):
139
+ """Delegate attribute access to the wrapped response."""
140
+ return getattr(self.__wrapped_response, name)
141
+
142
+ def get(self, key, default=None):
143
+ """Delegate get method to the wrapped response if its a dict."""
144
+ if isinstance(self.__wrapped_response, dict):
145
+ return self.__wrapped_response.get(key, default)
146
+ return getattr(self.__wrapped_response, key, default)
147
+
148
+ def __getitem__(self, key):
149
+ """Delegate item access to the wrapped response if its a dict."""
150
+ if isinstance(self.__wrapped_response, dict):
151
+ return self.__wrapped_response[key]
152
+ return getattr(self.__wrapped_response, key)
153
+
154
+ def __next__(self):
155
+ try:
156
+ chunk = next(self.__wrapped_stream)
157
+ process_chunk(self, chunk)
158
+ return chunk
159
+ except StopIteration:
160
+ try:
161
+ llm_config = self._kwargs.get("inferenceConfig", {})
162
+ with tracer.start_as_current_span(self._span_name, kind=SpanKind.CLIENT) as self._span:
163
+ process_streaming_chat_response(
164
+ self,
165
+ pricing_info=pricing_info,
166
+ environment=environment,
167
+ application_name=application_name,
168
+ metrics=metrics,
169
+ capture_message_content=capture_message_content,
170
+ disable_metrics=disable_metrics,
171
+ version=version,
172
+ llm_config=llm_config
173
+ )
174
+
175
+ except Exception as e:
176
+ handle_exception(self._span, e)
177
+
178
+ raise
179
+
180
+ def wrapper(wrapped, instance, args, kwargs):
181
+ """
182
+ Wraps the ClientCreator.create_client call.
183
+ """
184
+
185
+ def converse_stream_wrapper(original_method, *method_args, **method_kwargs):
186
+ """
187
+ Wraps the individual converse_stream method call.
188
+ """
189
+
190
+ server_address, server_port = set_server_address_and_port(instance, "aws.amazon.com", 443)
191
+ request_model = method_kwargs.get("modelId", "amazon.titan-text-express-v1")
192
+
193
+ span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT} {request_model}"
194
+
195
+ # Get the streaming response
196
+ stream_response = original_method(*method_args, **method_kwargs)
197
+ span = tracer.start_span(span_name, kind=SpanKind.CLIENT)
198
+
199
+ return TracedSyncStream(stream_response, span, span_name, method_kwargs, server_address, server_port)
200
+
201
+ # Get the original client instance from the wrapper
202
+ client = wrapped(*args, **kwargs)
203
+
204
+ # Replace the original method with the instrumented one
205
+ if kwargs.get("service_name") == "bedrock-runtime":
206
+ original_stream_model = client.converse_stream
207
+ client.converse_stream = lambda *args, **kwargs: converse_stream_wrapper(original_stream_model, *args, **kwargs)
74
208
 
75
209
  return client
76
210