openlit 1.34.20__py3-none-any.whl → 1.34.23__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.
@@ -1,4 +1,3 @@
1
- # pylint: disable=useless-return, bad-staticmethod-argument, disable=duplicate-code
2
1
  """Initializer of Auto Instrumentation of LangChain Functions"""
3
2
  from typing import Collection
4
3
  import importlib.metadata
@@ -6,41 +5,17 @@ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
6
5
  from wrapt import wrap_function_wrapper
7
6
 
8
7
  from openlit.instrumentation.langchain.langchain import (
9
- general_wrap,
10
8
  hub,
11
9
  chat
12
10
  )
13
11
  from openlit.instrumentation.langchain.async_langchain import (
12
+ async_hub,
14
13
  async_chat
15
14
  )
16
15
 
17
16
  _instruments = ("langchain >= 0.1.20",)
18
17
 
19
18
  WRAPPED_METHODS = [
20
- {
21
- "package": "langchain_community.document_loaders.base",
22
- "object": "BaseLoader.load",
23
- "endpoint": "langchain.retrieve.load",
24
- "wrapper": general_wrap,
25
- },
26
- {
27
- "package": "langchain_community.document_loaders.base",
28
- "object": "BaseLoader.aload",
29
- "endpoint": "langchain.retrieve.load",
30
- "wrapper": general_wrap,
31
- },
32
- {
33
- "package": "langchain_text_splitters.base",
34
- "object": "TextSplitter.split_documents",
35
- "endpoint": "langchain.retrieve.split_documents",
36
- "wrapper": general_wrap,
37
- },
38
- {
39
- "package": "langchain_text_splitters.base",
40
- "object": "TextSplitter.create_documents",
41
- "endpoint": "langchain.retrieve.create_documents",
42
- "wrapper": general_wrap,
43
- },
44
19
  {
45
20
  "package": "langchain.hub",
46
21
  "object": "pull",
@@ -79,27 +54,29 @@ WRAPPED_METHODS = [
79
54
  },
80
55
  {
81
56
  "package": "langchain.chains.base",
82
- "object": "Chain.invoke",
57
+ "object": "Chain.ainvoke",
83
58
  "endpoint": "langchain.chain.invoke",
84
59
  "wrapper": async_chat,
85
60
  }
86
61
  ]
87
62
 
88
63
  class LangChainInstrumentor(BaseInstrumentor):
89
- """An instrumentor for Cohere's client library."""
64
+ """
65
+ An instrumentor for LangChain client library.
66
+ """
90
67
 
91
68
  def instrumentation_dependencies(self) -> Collection[str]:
92
69
  return _instruments
93
70
 
94
71
  def _instrument(self, **kwargs):
95
- application_name = kwargs.get("application_name")
96
- environment = kwargs.get("environment")
72
+ version = importlib.metadata.version("langchain")
73
+ environment = kwargs.get("environment", "default")
74
+ application_name = kwargs.get("application_name", "default")
97
75
  tracer = kwargs.get("tracer")
98
- pricing_info = kwargs.get("pricing_info")
99
- capture_message_content = kwargs.get("capture_message_content")
76
+ pricing_info = kwargs.get("pricing_info", {})
77
+ capture_message_content = kwargs.get("capture_message_content", False)
100
78
  metrics = kwargs.get("metrics_dict")
101
79
  disable_metrics = kwargs.get("disable_metrics")
102
- version = importlib.metadata.version("langchain")
103
80
 
104
81
  for wrapped_method in WRAPPED_METHODS:
105
82
  wrap_package = wrapped_method.get("package")
@@ -110,9 +87,8 @@ class LangChainInstrumentor(BaseInstrumentor):
110
87
  wrap_package,
111
88
  wrap_object,
112
89
  wrapper(gen_ai_endpoint, version, environment, application_name,
113
- tracer, pricing_info, capture_message_content, metrics, disable_metrics),
90
+ tracer, pricing_info, capture_message_content, metrics, disable_metrics),
114
91
  )
115
92
 
116
- @staticmethod
117
93
  def _uninstrument(self, **kwargs):
118
94
  pass
@@ -1,245 +1,68 @@
1
- # pylint: disable=duplicate-code, broad-exception-caught, too-many-statements, unused-argument, unused-import, too-many-function-args
2
1
  """
3
- Module for monitoring Langchain applications.
2
+ Module for monitoring LangChain API calls.
4
3
  """
5
4
 
6
- import logging
7
5
  import time
8
- from opentelemetry.trace import SpanKind, Status, StatusCode
9
- from opentelemetry.sdk.resources import SERVICE_NAME, TELEMETRY_SDK_NAME, DEPLOYMENT_ENVIRONMENT
6
+ from opentelemetry.trace import SpanKind
10
7
  from openlit.__helpers import (
11
- get_chat_model_cost,
12
8
  handle_exception,
13
- general_tokens,
14
- calculate_ttft,
15
- calculate_tbt,
16
- create_metrics_attributes,
9
+ set_server_address_and_port
10
+ )
11
+ from openlit.instrumentation.langchain.utils import (
12
+ get_model_from_instance,
13
+ process_chat_response,
14
+ process_hub_response,
17
15
  )
18
16
  from openlit.semcov import SemanticConvention
19
17
 
20
- # Initialize logger for logging potential issues and operations
21
- logger = logging.getLogger(__name__)
22
-
23
- def get_attribute_from_instance_or_kwargs(instance, attribute_name, default=-1):
24
- """Return attribute from instance or kwargs"""
25
- # Attempt to retrieve model_kwargs from the instance
26
- model_kwargs = getattr(instance, 'model_kwargs', None)
27
-
28
- # Check for attribute in model_kwargs if it exists
29
- if model_kwargs and attribute_name in model_kwargs:
30
- return model_kwargs[attribute_name]
31
-
32
- # Attempt to get the attribute directly from the instance
33
- try:
34
- return getattr(instance, attribute_name)
35
- except AttributeError:
36
- # Special handling for 'model' attribute to consider 'model_id'
37
- if attribute_name == 'model':
38
- return getattr(instance, 'model_id', 'default_model_id')
39
-
40
- # Default if the attribute isn't found in model_kwargs or the instance
41
- return default
42
-
43
- def async_general_wrap(gen_ai_endpoint, version, environment, application_name,
44
- tracer, pricing_info, capture_message_content, metrics, disable_metrics):
45
- """
46
- Creates a wrapper around a function call to trace and log its execution metrics.
47
-
48
- This function wraps any given function to measure its execution time,
49
- log its operation, and trace its execution using OpenTelemetry.
50
-
51
- Parameters:
52
- - gen_ai_endpoint (str): A descriptor or name for the endpoint being traced.
53
- - version (str): The version of the Langchain application.
54
- - environment (str): The deployment environment (e.g., 'production', 'development').
55
- - application_name (str): Name of the Langchain application.
56
- - tracer (opentelemetry.trace.Tracer): The tracer object used for OpenTelemetry tracing.
57
- - pricing_info (dict): Information about the pricing for internal metrics (currently not used).
58
- - capture_message_content (bool): Flag indicating whether to trace the content of the response.
59
-
60
- Returns:
61
- - function: A higher-order function that takes a function 'wrapped' and returns
62
- a new function that wraps 'wrapped' with additional tracing and logging.
63
- """
64
-
65
- async def wrapper(wrapped, instance, args, kwargs):
66
- """
67
- An inner wrapper function that executes the wrapped function, measures execution
68
- time, and records trace data using OpenTelemetry.
69
-
70
- Parameters:
71
- - wrapped (Callable): The original function that this wrapper will execute.
72
- - instance (object): The instance to which the wrapped function belongs. This
73
- is used for instance methods. For static and classmethods,
74
- this may be None.
75
- - args (tuple): Positional arguments passed to the wrapped function.
76
- - kwargs (dict): Keyword arguments passed to the wrapped function.
77
-
78
- Returns:
79
- - The result of the wrapped function call.
80
-
81
- The wrapper initiates a span with the provided tracer, sets various attributes
82
- on the span based on the function's execution and response, and ensures
83
- errors are handled and logged appropriately.
84
- """
85
- with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
86
- response = await wrapped(*args, **kwargs)
87
-
88
- try:
89
- span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
90
- span.set_attribute(SemanticConvention.GEN_AI_SYSTEM,
91
- SemanticConvention.GEN_AI_SYSTEM_LANGCHAIN)
92
- span.set_attribute(SemanticConvention.GEN_AI_ENDPOINT,
93
- gen_ai_endpoint)
94
- span.set_attribute(DEPLOYMENT_ENVIRONMENT,
95
- environment)
96
- span.set_attribute(SemanticConvention.GEN_AI_OPERATION,
97
- SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK)
98
- span.set_attribute(SERVICE_NAME,
99
- application_name)
100
- span.set_attribute(SemanticConvention.GEN_AI_RETRIEVAL_SOURCE,
101
- response[0].metadata["source"])
102
- span.set_status(Status(StatusCode.OK))
103
-
104
- # Return original response
105
- return response
106
-
107
- except Exception as e:
108
- handle_exception(span, e)
109
- logger.error("Error in trace creation: %s", e)
110
-
111
- # Return original response
112
- return response
113
-
114
- return wrapper
115
-
116
18
  def async_hub(gen_ai_endpoint, version, environment, application_name, tracer,
117
- pricing_info, capture_message_content, metrics, disable_metrics):
19
+ pricing_info, capture_message_content, metrics, disable_metrics):
118
20
  """
119
- Creates a wrapper around Langchain hub operations for tracing and logging.
120
-
121
- Similar to `general_wrap`, this function focuses on wrapping functions involved
122
- in interacting with the Langchain hub, adding specific metadata relevant to
123
- hub operations to the span attributes.
124
-
125
- Parameters:
126
- - gen_ai_endpoint (str): A descriptor or name for the Langchain hub endpoint.
127
- - version (str): The version of the Langchain application.
128
- - environment (str): The deployment environment, such as 'production' or 'development'.
129
- - application_name (str): Name of the Langchain application.
130
- - tracer (opentelemetry.trace.Tracer): The tracer for OpenTelemetry tracing.
131
- - pricing_info (dict): Pricing information for the operation (not currently used).
132
- - capture_message_content (bool): Indicates if the content of the response should be traced.
133
-
134
- Returns:
135
- - function: A new function that wraps the original hub operation call with added
136
- logging, tracing, and metric calculation functionalities.
21
+ Generates a telemetry wrapper for LangChain async hub operations.
137
22
  """
138
23
 
139
24
  async def wrapper(wrapped, instance, args, kwargs):
140
25
  """
141
- An inner wrapper specifically designed for Langchain hub operations,
142
- providing tracing, logging, and execution metrics.
143
-
144
- Parameters:
145
- - wrapped (Callable): The original hub operation function to be executed.
146
- - instance (object): The instance of the class where the hub operation
147
- method is defined. May be None for static or class methods.
148
- - args (tuple): Positional arguments to pass to the hub operation function.
149
- - kwargs (dict): Keyword arguments to pass to the hub operation function.
150
-
151
- Returns:
152
- - The result of executing the hub operation function.
153
-
154
- This wrapper captures additional metadata relevant to Langchain hub operations,
155
- creating spans with specific attributes and metrics that reflect the nature of
156
- each hub call.
26
+ Wraps the LangChain async hub operation call.
157
27
  """
158
28
 
159
- with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
29
+ server_address, server_port = set_server_address_and_port(instance, "langchain.com", 443)
30
+
31
+ with tracer.start_as_current_span(gen_ai_endpoint, kind=SpanKind.CLIENT) as span:
160
32
  response = await wrapped(*args, **kwargs)
161
33
 
162
34
  try:
163
- span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
164
- span.set_attribute(SemanticConvention.GEN_AI_SYSTEM,
165
- SemanticConvention.GEN_AI_SYSTEM_LANGCHAIN)
166
- span.set_attribute(SemanticConvention.GEN_AI_ENDPOINT,
167
- gen_ai_endpoint)
168
- span.set_attribute(DEPLOYMENT_ENVIRONMENT,
169
- environment)
170
- span.set_attribute(SemanticConvention.GEN_AI_OPERATION,
171
- SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK)
172
- span.set_attribute(SERVICE_NAME,
173
- application_name)
174
- span.set_attribute(SemanticConvention.GEN_AI_HUB_OWNER,
175
- response.metadata["lc_hub_owner"])
176
- span.set_attribute(SemanticConvention.GEN_AI_HUB_REPO,
177
- response.metadata["lc_hub_repo"])
178
- span.set_status(Status(StatusCode.OK))
179
-
180
- return response
35
+ response = process_hub_response(
36
+ response=response,
37
+ gen_ai_endpoint=gen_ai_endpoint,
38
+ server_port=server_port,
39
+ server_address=server_address,
40
+ environment=environment,
41
+ application_name=application_name,
42
+ span=span,
43
+ version=version
44
+ )
181
45
 
182
46
  except Exception as e:
183
47
  handle_exception(span, e)
184
- logger.error("Error in trace creation: %s", e)
185
48
 
186
- # Return original response
187
- return response
49
+ return response
188
50
 
189
51
  return wrapper
190
52
 
191
53
  def async_chat(gen_ai_endpoint, version, environment, application_name,
192
- tracer, pricing_info, capture_message_content, metrics, disable_metrics):
54
+ tracer, pricing_info, capture_message_content, metrics, disable_metrics):
193
55
  """
194
- Creates a wrapper around a function call to trace and log its execution metrics.
195
-
196
- This function wraps any given function to measure its execution time,
197
- log its operation, and trace its execution using OpenTelemetry.
198
-
199
- Parameters:
200
- - version (str): The version of the Langchain application.
201
- - environment (str): The deployment environment (e.g., 'production', 'development').
202
- - application_name (str): Name of the Langchain application.
203
- - tracer (opentelemetry.trace.Tracer): The tracer object used for OpenTelemetry tracing.
204
- - pricing_info (dict): Information about the pricing for internal metrics (currently not used).
205
- - capture_message_content (bool): Flag indicating whether to trace the content of the response.
206
-
207
- Returns:
208
- - function: A higher-order function that takes a function 'wrapped' and returns
209
- a new function that wraps 'wrapped' with additional tracing and logging.
56
+ Generates a telemetry wrapper for LangChain async chat operations.
210
57
  """
211
58
 
212
59
  async def wrapper(wrapped, instance, args, kwargs):
213
60
  """
214
- An inner wrapper function that executes the wrapped function, measures execution
215
- time, and records trace data using OpenTelemetry.
216
-
217
- Parameters:
218
- - wrapped (Callable): The original function that this wrapper will execute.
219
- - instance (object): The instance to which the wrapped function belongs. This
220
- is used for instance methods. For static and classmethods,
221
- this may be None.
222
- - args (tuple): Positional arguments passed to the wrapped function.
223
- - kwargs (dict): Keyword arguments passed to the wrapped function.
224
-
225
- Returns:
226
- - The result of the wrapped function call.
227
-
228
- The wrapper initiates a span with the provided tracer, sets various attributes
229
- on the span based on the function's execution and response, and ensures
230
- errors are handled and logged appropriately.
61
+ Wraps the LangChain async chat operation call.
231
62
  """
232
63
 
233
- server_address, server_port = "NOT_FOUND", "NOT_FOUND"
234
-
235
- if hasattr(instance, "model_id"):
236
- request_model = instance.model_id
237
- elif hasattr(instance, "model"):
238
- request_model = instance.model
239
- elif hasattr(instance, "model_name"):
240
- request_model = instance.model_name
241
- else:
242
- request_model = "NOT_FOUND"
64
+ server_address, server_port = set_server_address_and_port(instance, "langchain.com", 443)
65
+ request_model = get_model_from_instance(instance)
243
66
 
244
67
  span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT} {request_model}"
245
68
 
@@ -249,140 +72,31 @@ def async_chat(gen_ai_endpoint, version, environment, application_name,
249
72
  end_time = time.time()
250
73
 
251
74
  try:
252
- # Format 'messages' into a single string
253
- message_prompt = kwargs.get("messages", "") or args[0]
254
- formatted_messages = []
255
-
256
- for message in message_prompt:
257
- # Handle the case where message is a tuple
258
- if isinstance(message, tuple) and len(message) == 2:
259
- role, content = message
260
- # Handle the case where message is a dictionary
261
- elif isinstance(message, dict):
262
- role = message["role"]
263
- content = message["content"]
264
- else:
265
- continue
266
-
267
- # Check if the content is a list
268
- if isinstance(content, list):
269
- content_str = ", ".join(
270
- f'{item["type"]}: {item["text"] if "text" in item else item["image_url"]}'
271
- if "type" in item else f'text: {item["text"]}'
272
- for item in content
273
- )
274
- formatted_messages.append(f"{role}: {content_str}")
275
- else:
276
- formatted_messages.append(f"{role}: {content}")
277
-
278
- # Join all formatted messages with newline
279
- prompt = "\n".join(formatted_messages)
280
-
281
- input_tokens = general_tokens(str(prompt))
282
- output_tokens = general_tokens(str(response))
283
-
284
- # Calculate cost of the operation
285
- cost = get_chat_model_cost(
286
- request_model,
287
- pricing_info, input_tokens, output_tokens
75
+ # Add instance to kwargs for processing
76
+ kwargs["instance"] = instance
77
+
78
+ response = process_chat_response(
79
+ response=response,
80
+ request_model=request_model,
81
+ pricing_info=pricing_info,
82
+ server_port=server_port,
83
+ server_address=server_address,
84
+ environment=environment,
85
+ application_name=application_name,
86
+ metrics=metrics,
87
+ start_time=start_time,
88
+ end_time=end_time,
89
+ span=span,
90
+ capture_message_content=capture_message_content,
91
+ disable_metrics=disable_metrics,
92
+ version=version,
93
+ args=args,
94
+ **kwargs
288
95
  )
289
96
 
290
- try:
291
- llm_response = response.content
292
- except AttributeError:
293
- llm_response = response
294
-
295
- # Set base span attribues (OTel Semconv)
296
- span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
297
- span.set_attribute(SemanticConvention.GEN_AI_OPERATION,
298
- SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT)
299
- span.set_attribute(SemanticConvention.GEN_AI_SYSTEM,
300
- SemanticConvention.GEN_AI_SYSTEM_LANGCHAIN)
301
- span.set_attribute(SemanticConvention.GEN_AI_REQUEST_MODEL,
302
- request_model)
303
- span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_MODEL,
304
- request_model)
305
- span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TEMPERATURE,
306
- str(getattr(instance, 'temperature', 1)))
307
- span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TOP_K,
308
- str(getattr(instance, 'top_k', 1)))
309
- span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TOP_P,
310
- str(getattr(instance, 'top_p', 1)))
311
- span.set_attribute(SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS,
312
- input_tokens)
313
- span.set_attribute(SemanticConvention.GEN_AI_USAGE_OUTPUT_TOKENS,
314
- output_tokens)
315
- span.set_attribute(SemanticConvention.SERVER_ADDRESS,
316
- server_address)
317
- span.set_attribute(SemanticConvention.SERVER_PORT,
318
- server_port)
319
-
320
- # Set base span attribues (Extras)
321
- span.set_attribute(DEPLOYMENT_ENVIRONMENT,
322
- environment)
323
- span.set_attribute(SERVICE_NAME,
324
- application_name)
325
- span.set_attribute(SemanticConvention.GEN_AI_REQUEST_IS_STREAM,
326
- False)
327
- span.set_attribute(SemanticConvention.GEN_AI_USAGE_TOTAL_TOKENS,
328
- input_tokens + output_tokens)
329
- span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST,
330
- cost)
331
- span.set_attribute(SemanticConvention.GEN_AI_SERVER_TTFT,
332
- end_time - start_time)
333
- span.set_attribute(SemanticConvention.GEN_AI_SDK_VERSION,
334
- version)
335
-
336
- if capture_message_content:
337
- span.add_event(
338
- name=SemanticConvention.GEN_AI_CONTENT_PROMPT_EVENT,
339
- attributes={
340
- SemanticConvention.GEN_AI_CONTENT_PROMPT: prompt,
341
- },
342
- )
343
- span.add_event(
344
- name=SemanticConvention.GEN_AI_CONTENT_COMPLETION_EVENT,
345
- attributes={
346
- SemanticConvention.GEN_AI_CONTENT_COMPLETION: llm_response,
347
- },
348
- )
349
-
350
- span.set_status(Status(StatusCode.OK))
351
-
352
- if disable_metrics is False:
353
- attributes = create_metrics_attributes(
354
- service_name=application_name,
355
- deployment_environment=environment,
356
- operation=SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
357
- system=SemanticConvention.GEN_AI_SYSTEM_LANGCHAIN,
358
- request_model=request_model,
359
- server_address=server_address,
360
- server_port=server_port,
361
- response_model=request_model,
362
- )
363
-
364
- metrics["genai_client_usage_tokens"].record(
365
- input_tokens + output_tokens, attributes
366
- )
367
- metrics["genai_client_operation_duration"].record(
368
- end_time - start_time, attributes
369
- )
370
- metrics["genai_server_ttft"].record(
371
- end_time - start_time, attributes
372
- )
373
- metrics["genai_requests"].add(1, attributes)
374
- metrics["genai_completion_tokens"].add(output_tokens, attributes)
375
- metrics["genai_prompt_tokens"].add(input_tokens, attributes)
376
- metrics["genai_cost"].record(cost, attributes)
377
-
378
- # Return original response
379
- return response
380
-
381
97
  except Exception as e:
382
98
  handle_exception(span, e)
383
- logger.error("Error in trace creation: %s", e)
384
99
 
385
- # Return original response
386
- return response
100
+ return response
387
101
 
388
102
  return wrapper