openlit 1.33.9__py3-none-any.whl → 1.33.11__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 (113) hide show
  1. openlit/__helpers.py +78 -0
  2. openlit/__init__.py +41 -13
  3. openlit/instrumentation/ag2/__init__.py +9 -10
  4. openlit/instrumentation/ag2/ag2.py +134 -69
  5. openlit/instrumentation/ai21/__init__.py +6 -5
  6. openlit/instrumentation/ai21/ai21.py +71 -534
  7. openlit/instrumentation/ai21/async_ai21.py +71 -534
  8. openlit/instrumentation/ai21/utils.py +407 -0
  9. openlit/instrumentation/anthropic/__init__.py +3 -3
  10. openlit/instrumentation/anthropic/anthropic.py +5 -5
  11. openlit/instrumentation/anthropic/async_anthropic.py +5 -5
  12. openlit/instrumentation/assemblyai/__init__.py +2 -2
  13. openlit/instrumentation/assemblyai/assemblyai.py +3 -3
  14. openlit/instrumentation/astra/__init__.py +25 -25
  15. openlit/instrumentation/astra/astra.py +7 -7
  16. openlit/instrumentation/astra/async_astra.py +7 -7
  17. openlit/instrumentation/azure_ai_inference/__init__.py +5 -5
  18. openlit/instrumentation/azure_ai_inference/async_azure_ai_inference.py +11 -11
  19. openlit/instrumentation/azure_ai_inference/azure_ai_inference.py +11 -11
  20. openlit/instrumentation/bedrock/__init__.py +2 -2
  21. openlit/instrumentation/bedrock/bedrock.py +3 -3
  22. openlit/instrumentation/chroma/__init__.py +9 -9
  23. openlit/instrumentation/chroma/chroma.py +7 -7
  24. openlit/instrumentation/cohere/__init__.py +7 -7
  25. openlit/instrumentation/cohere/async_cohere.py +10 -10
  26. openlit/instrumentation/cohere/cohere.py +11 -11
  27. openlit/instrumentation/controlflow/__init__.py +4 -4
  28. openlit/instrumentation/controlflow/controlflow.py +5 -5
  29. openlit/instrumentation/crawl4ai/__init__.py +3 -3
  30. openlit/instrumentation/crawl4ai/async_crawl4ai.py +5 -5
  31. openlit/instrumentation/crawl4ai/crawl4ai.py +5 -5
  32. openlit/instrumentation/crewai/__init__.py +3 -3
  33. openlit/instrumentation/crewai/crewai.py +6 -4
  34. openlit/instrumentation/dynamiq/__init__.py +5 -5
  35. openlit/instrumentation/dynamiq/dynamiq.py +5 -5
  36. openlit/instrumentation/elevenlabs/__init__.py +5 -5
  37. openlit/instrumentation/elevenlabs/async_elevenlabs.py +4 -5
  38. openlit/instrumentation/elevenlabs/elevenlabs.py +4 -5
  39. openlit/instrumentation/embedchain/__init__.py +2 -2
  40. openlit/instrumentation/embedchain/embedchain.py +9 -9
  41. openlit/instrumentation/firecrawl/__init__.py +3 -3
  42. openlit/instrumentation/firecrawl/firecrawl.py +5 -5
  43. openlit/instrumentation/google_ai_studio/__init__.py +3 -3
  44. openlit/instrumentation/google_ai_studio/async_google_ai_studio.py +3 -3
  45. openlit/instrumentation/google_ai_studio/google_ai_studio.py +3 -3
  46. openlit/instrumentation/gpt4all/__init__.py +5 -5
  47. openlit/instrumentation/gpt4all/gpt4all.py +350 -225
  48. openlit/instrumentation/gpu/__init__.py +5 -5
  49. openlit/instrumentation/groq/__init__.py +5 -5
  50. openlit/instrumentation/groq/async_groq.py +359 -243
  51. openlit/instrumentation/groq/groq.py +359 -243
  52. openlit/instrumentation/haystack/__init__.py +2 -2
  53. openlit/instrumentation/haystack/haystack.py +5 -5
  54. openlit/instrumentation/julep/__init__.py +7 -7
  55. openlit/instrumentation/julep/async_julep.py +6 -6
  56. openlit/instrumentation/julep/julep.py +6 -6
  57. openlit/instrumentation/langchain/__init__.py +15 -9
  58. openlit/instrumentation/langchain/async_langchain.py +388 -0
  59. openlit/instrumentation/langchain/langchain.py +110 -497
  60. openlit/instrumentation/letta/__init__.py +7 -7
  61. openlit/instrumentation/letta/letta.py +10 -8
  62. openlit/instrumentation/litellm/__init__.py +9 -10
  63. openlit/instrumentation/litellm/async_litellm.py +321 -250
  64. openlit/instrumentation/litellm/litellm.py +319 -248
  65. openlit/instrumentation/llamaindex/__init__.py +2 -2
  66. openlit/instrumentation/llamaindex/llamaindex.py +5 -5
  67. openlit/instrumentation/mem0/__init__.py +2 -2
  68. openlit/instrumentation/mem0/mem0.py +5 -5
  69. openlit/instrumentation/milvus/__init__.py +2 -2
  70. openlit/instrumentation/milvus/milvus.py +7 -7
  71. openlit/instrumentation/mistral/__init__.py +13 -13
  72. openlit/instrumentation/mistral/async_mistral.py +426 -253
  73. openlit/instrumentation/mistral/mistral.py +424 -250
  74. openlit/instrumentation/multion/__init__.py +7 -7
  75. openlit/instrumentation/multion/async_multion.py +9 -7
  76. openlit/instrumentation/multion/multion.py +9 -7
  77. openlit/instrumentation/ollama/__init__.py +19 -39
  78. openlit/instrumentation/ollama/async_ollama.py +137 -563
  79. openlit/instrumentation/ollama/ollama.py +136 -563
  80. openlit/instrumentation/ollama/utils.py +333 -0
  81. openlit/instrumentation/openai/__init__.py +11 -11
  82. openlit/instrumentation/openai/async_openai.py +25 -27
  83. openlit/instrumentation/openai/openai.py +25 -27
  84. openlit/instrumentation/phidata/__init__.py +2 -2
  85. openlit/instrumentation/phidata/phidata.py +6 -4
  86. openlit/instrumentation/pinecone/__init__.py +6 -6
  87. openlit/instrumentation/pinecone/pinecone.py +7 -7
  88. openlit/instrumentation/premai/__init__.py +5 -5
  89. openlit/instrumentation/premai/premai.py +268 -219
  90. openlit/instrumentation/qdrant/__init__.py +2 -2
  91. openlit/instrumentation/qdrant/async_qdrant.py +7 -7
  92. openlit/instrumentation/qdrant/qdrant.py +7 -7
  93. openlit/instrumentation/reka/__init__.py +5 -5
  94. openlit/instrumentation/reka/async_reka.py +93 -55
  95. openlit/instrumentation/reka/reka.py +93 -55
  96. openlit/instrumentation/together/__init__.py +9 -9
  97. openlit/instrumentation/together/async_together.py +284 -242
  98. openlit/instrumentation/together/together.py +284 -242
  99. openlit/instrumentation/transformers/__init__.py +3 -3
  100. openlit/instrumentation/transformers/transformers.py +79 -48
  101. openlit/instrumentation/vertexai/__init__.py +19 -69
  102. openlit/instrumentation/vertexai/async_vertexai.py +333 -990
  103. openlit/instrumentation/vertexai/vertexai.py +333 -990
  104. openlit/instrumentation/vllm/__init__.py +3 -3
  105. openlit/instrumentation/vllm/vllm.py +65 -35
  106. openlit/otel/events.py +85 -0
  107. openlit/otel/tracing.py +3 -13
  108. openlit/semcov/__init__.py +16 -4
  109. {openlit-1.33.9.dist-info → openlit-1.33.11.dist-info}/METADATA +2 -2
  110. openlit-1.33.11.dist-info/RECORD +125 -0
  111. openlit-1.33.9.dist-info/RECORD +0 -121
  112. {openlit-1.33.9.dist-info → openlit-1.33.11.dist-info}/LICENSE +0 -0
  113. {openlit-1.33.9.dist-info → openlit-1.33.11.dist-info}/WHEEL +0 -0
@@ -4,9 +4,17 @@ Module for monitoring Langchain applications.
4
4
  """
5
5
 
6
6
  import logging
7
+ import time
7
8
  from opentelemetry.trace import SpanKind, Status, StatusCode
8
- from opentelemetry.sdk.resources import TELEMETRY_SDK_NAME
9
- from openlit.__helpers import handle_exception, get_chat_model_cost, general_tokens
9
+ from opentelemetry.sdk.resources import SERVICE_NAME, TELEMETRY_SDK_NAME, DEPLOYMENT_ENVIRONMENT
10
+ from openlit.__helpers import (
11
+ get_chat_model_cost,
12
+ handle_exception,
13
+ general_tokens,
14
+ calculate_ttft,
15
+ calculate_tbt,
16
+ create_metrics_attributes,
17
+ )
10
18
  from openlit.semcov import SemanticConvetion
11
19
 
12
20
  # Initialize logger for logging potential issues and operations
@@ -33,7 +41,7 @@ def get_attribute_from_instance_or_kwargs(instance, attribute_name, default=-1):
33
41
  return default
34
42
 
35
43
  def general_wrap(gen_ai_endpoint, version, environment, application_name,
36
- tracer, pricing_info, trace_content, metrics, disable_metrics):
44
+ tracer, pricing_info, capture_message_content, metrics, disable_metrics):
37
45
  """
38
46
  Creates a wrapper around a function call to trace and log its execution metrics.
39
47
 
@@ -47,7 +55,7 @@ def general_wrap(gen_ai_endpoint, version, environment, application_name,
47
55
  - application_name (str): Name of the Langchain application.
48
56
  - tracer (opentelemetry.trace.Tracer): The tracer object used for OpenTelemetry tracing.
49
57
  - pricing_info (dict): Information about the pricing for internal metrics (currently not used).
50
- - trace_content (bool): Flag indicating whether to trace the content of the response.
58
+ - capture_message_content (bool): Flag indicating whether to trace the content of the response.
51
59
 
52
60
  Returns:
53
61
  - function: A higher-order function that takes a function 'wrapped' and returns
@@ -83,11 +91,11 @@ def general_wrap(gen_ai_endpoint, version, environment, application_name,
83
91
  SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN)
84
92
  span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
85
93
  gen_ai_endpoint)
86
- span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
94
+ span.set_attribute(DEPLOYMENT_ENVIRONMENT,
87
95
  environment)
88
96
  span.set_attribute(SemanticConvetion.GEN_AI_OPERATION,
89
97
  SemanticConvetion.GEN_AI_OPERATION_TYPE_FRAMEWORK)
90
- span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
98
+ span.set_attribute(SERVICE_NAME,
91
99
  application_name)
92
100
  span.set_attribute(SemanticConvetion.GEN_AI_RETRIEVAL_SOURCE,
93
101
  response[0].metadata["source"])
@@ -106,7 +114,7 @@ def general_wrap(gen_ai_endpoint, version, environment, application_name,
106
114
  return wrapper
107
115
 
108
116
  def hub(gen_ai_endpoint, version, environment, application_name, tracer,
109
- pricing_info, trace_content, metrics, disable_metrics):
117
+ pricing_info, capture_message_content, metrics, disable_metrics):
110
118
  """
111
119
  Creates a wrapper around Langchain hub operations for tracing and logging.
112
120
 
@@ -121,7 +129,7 @@ def hub(gen_ai_endpoint, version, environment, application_name, tracer,
121
129
  - application_name (str): Name of the Langchain application.
122
130
  - tracer (opentelemetry.trace.Tracer): The tracer for OpenTelemetry tracing.
123
131
  - pricing_info (dict): Pricing information for the operation (not currently used).
124
- - trace_content (bool): Indicates if the content of the response should be traced.
132
+ - capture_message_content (bool): Indicates if the content of the response should be traced.
125
133
 
126
134
  Returns:
127
135
  - function: A new function that wraps the original hub operation call with added
@@ -157,11 +165,11 @@ def hub(gen_ai_endpoint, version, environment, application_name, tracer,
157
165
  SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN)
158
166
  span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
159
167
  gen_ai_endpoint)
160
- span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
168
+ span.set_attribute(DEPLOYMENT_ENVIRONMENT,
161
169
  environment)
162
170
  span.set_attribute(SemanticConvetion.GEN_AI_OPERATION,
163
171
  SemanticConvetion.GEN_AI_OPERATION_TYPE_FRAMEWORK)
164
- span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
172
+ span.set_attribute(SERVICE_NAME,
165
173
  application_name)
166
174
  span.set_attribute(SemanticConvetion.GEN_AI_HUB_OWNER,
167
175
  response.metadata["lc_hub_owner"])
@@ -180,149 +188,8 @@ def hub(gen_ai_endpoint, version, environment, application_name, tracer,
180
188
 
181
189
  return wrapper
182
190
 
183
-
184
- def allm(gen_ai_endpoint, version, environment, application_name,
185
- tracer, pricing_info, trace_content, metrics, disable_metrics):
186
- """
187
- Creates a wrapper around a function call to trace and log its execution metrics.
188
-
189
- This function wraps any given function to measure its execution time,
190
- log its operation, and trace its execution using OpenTelemetry.
191
-
192
- Parameters:
193
- - gen_ai_endpoint (str): A descriptor or name for the endpoint being traced.
194
- - version (str): The version of the Langchain application.
195
- - environment (str): The deployment environment (e.g., 'production', 'development').
196
- - application_name (str): Name of the Langchain application.
197
- - tracer (opentelemetry.trace.Tracer): The tracer object used for OpenTelemetry tracing.
198
- - pricing_info (dict): Information about the pricing for internal metrics (currently not used).
199
- - trace_content (bool): Flag indicating whether to trace the content of the response.
200
-
201
- Returns:
202
- - function: A higher-order function that takes a function 'wrapped' and returns
203
- a new function that wraps 'wrapped' with additional tracing and logging.
204
- """
205
-
206
- async def wrapper(wrapped, instance, args, kwargs):
207
- """
208
- An inner wrapper function that executes the wrapped function, measures execution
209
- time, and records trace data using OpenTelemetry.
210
-
211
- Parameters:
212
- - wrapped (Callable): The original function that this wrapper will execute.
213
- - instance (object): The instance to which the wrapped function belongs. This
214
- is used for instance methods. For static and classmethods,
215
- this may be None.
216
- - args (tuple): Positional arguments passed to the wrapped function.
217
- - kwargs (dict): Keyword arguments passed to the wrapped function.
218
-
219
- Returns:
220
- - The result of the wrapped function call.
221
-
222
- The wrapper initiates a span with the provided tracer, sets various attributes
223
- on the span based on the function's execution and response, and ensures
224
- errors are handled and logged appropriately.
225
- """
226
- with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
227
- response = await wrapped(*args, **kwargs)
228
-
229
- try:
230
- if args:
231
- prompt = str(args[0]) if args[0] is not None else ""
232
- else:
233
- prompt = ""
234
- input_tokens = general_tokens(prompt)
235
- output_tokens = general_tokens(response)
236
-
237
- # Calculate cost of the operation
238
- cost = get_chat_model_cost(
239
- str(get_attribute_from_instance_or_kwargs(instance, 'model')),
240
- pricing_info, input_tokens, output_tokens
241
- )
242
-
243
- span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
244
- span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
245
- SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN)
246
- span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
247
- gen_ai_endpoint)
248
- span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
249
- environment)
250
- span.set_attribute(SemanticConvetion.GEN_AI_OPERATION,
251
- SemanticConvetion.GEN_AI_OPERATION_TYPE_FRAMEWORK)
252
- span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
253
- application_name)
254
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
255
- str(get_attribute_from_instance_or_kwargs(instance, 'model')))
256
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TEMPERATURE,
257
- str(get_attribute_from_instance_or_kwargs(instance, 'temperature')))
258
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_K,
259
- str(get_attribute_from_instance_or_kwargs(instance, 'top_k')))
260
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_P,
261
- str(get_attribute_from_instance_or_kwargs(instance, 'top_p')))
262
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
263
- False)
264
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_INPUT_TOKENS,
265
- input_tokens)
266
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_OUTPUT_TOKENS,
267
- output_tokens)
268
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
269
- input_tokens + output_tokens)
270
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
271
- cost)
272
- if trace_content:
273
- span.add_event(
274
- name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
275
- attributes={
276
- SemanticConvetion.GEN_AI_CONTENT_PROMPT: prompt,
277
- },
278
- )
279
- span.add_event(
280
- name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
281
- attributes={
282
- SemanticConvetion.GEN_AI_CONTENT_COMPLETION: response,
283
- },
284
- )
285
-
286
- span.set_status(Status(StatusCode.OK))
287
-
288
- if disable_metrics is False:
289
- attributes = {
290
- TELEMETRY_SDK_NAME:
291
- "openlit",
292
- SemanticConvetion.GEN_AI_APPLICATION_NAME:
293
- application_name,
294
- SemanticConvetion.GEN_AI_SYSTEM:
295
- SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN,
296
- SemanticConvetion.GEN_AI_ENVIRONMENT:
297
- environment,
298
- SemanticConvetion.GEN_AI_OPERATION:
299
- SemanticConvetion.GEN_AI_OPERATION_TYPE_CHAT,
300
- SemanticConvetion.GEN_AI_REQUEST_MODEL:
301
- str(get_attribute_from_instance_or_kwargs(instance, 'model'))
302
- }
303
-
304
- metrics["genai_requests"].add(1, attributes)
305
- metrics["genai_total_tokens"].add(
306
- input_tokens + output_tokens, attributes
307
- )
308
- metrics["genai_completion_tokens"].add(output_tokens, attributes)
309
- metrics["genai_prompt_tokens"].add(input_tokens, attributes)
310
- metrics["genai_cost"].record(cost, attributes)
311
-
312
- # Return original response
313
- return response
314
-
315
- except Exception as e:
316
- handle_exception(span, e)
317
- logger.error("Error in trace creation: %s", e)
318
-
319
- # Return original response
320
- return response
321
-
322
- return wrapper
323
-
324
- def llm(gen_ai_endpoint, version, environment, application_name,
325
- tracer, pricing_info, trace_content, metrics, disable_metrics):
191
+ def chat(gen_ai_endpoint, version, environment, application_name,
192
+ tracer, pricing_info, capture_message_content, metrics, disable_metrics):
326
193
  """
327
194
  Creates a wrapper around a function call to trace and log its execution metrics.
328
195
 
@@ -330,13 +197,12 @@ def llm(gen_ai_endpoint, version, environment, application_name,
330
197
  log its operation, and trace its execution using OpenTelemetry.
331
198
 
332
199
  Parameters:
333
- - gen_ai_endpoint (str): A descriptor or name for the endpoint being traced.
334
200
  - version (str): The version of the Langchain application.
335
201
  - environment (str): The deployment environment (e.g., 'production', 'development').
336
202
  - application_name (str): Name of the Langchain application.
337
203
  - tracer (opentelemetry.trace.Tracer): The tracer object used for OpenTelemetry tracing.
338
204
  - pricing_info (dict): Information about the pricing for internal metrics (currently not used).
339
- - trace_content (bool): Flag indicating whether to trace the content of the response.
205
+ - capture_message_content (bool): Flag indicating whether to trace the content of the response.
340
206
 
341
207
  Returns:
342
208
  - function: A higher-order function that takes a function 'wrapped' and returns
@@ -363,366 +229,111 @@ def llm(gen_ai_endpoint, version, environment, application_name,
363
229
  on the span based on the function's execution and response, and ensures
364
230
  errors are handled and logged appropriately.
365
231
  """
366
- with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
367
- response = wrapped(*args, **kwargs)
368
-
369
- try:
370
- if args:
371
- prompt = str(args[0]) if args[0] is not None else ""
372
- else:
373
- prompt = ""
374
- input_tokens = general_tokens(prompt)
375
- output_tokens = general_tokens(response)
376
-
377
- # Calculate cost of the operation
378
- cost = get_chat_model_cost(
379
- str(get_attribute_from_instance_or_kwargs(instance, 'model')),
380
- pricing_info, input_tokens, output_tokens
381
- )
382
-
383
- span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
384
- span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
385
- SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN)
386
- span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
387
- gen_ai_endpoint)
388
- span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
389
- environment)
390
- span.set_attribute(SemanticConvetion.GEN_AI_OPERATION,
391
- SemanticConvetion.GEN_AI_OPERATION_TYPE_FRAMEWORK)
392
- span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
393
- application_name)
394
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
395
- str(get_attribute_from_instance_or_kwargs(instance, 'model')))
396
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TEMPERATURE,
397
- str(get_attribute_from_instance_or_kwargs(instance, 'temperature')))
398
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_K,
399
- str(get_attribute_from_instance_or_kwargs(instance, 'top_k')))
400
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_P,
401
- str(get_attribute_from_instance_or_kwargs(instance, 'top_p')))
402
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
403
- False)
404
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_INPUT_TOKENS,
405
- input_tokens)
406
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_OUTPUT_TOKENS,
407
- output_tokens)
408
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
409
- input_tokens + output_tokens)
410
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
411
- cost)
412
- if trace_content:
413
- span.add_event(
414
- name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
415
- attributes={
416
- SemanticConvetion.GEN_AI_CONTENT_PROMPT: prompt,
417
- },
418
- )
419
- span.add_event(
420
- name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
421
- attributes={
422
- SemanticConvetion.GEN_AI_CONTENT_COMPLETION: response,
423
- },
424
- )
425
-
426
- span.set_status(Status(StatusCode.OK))
427
-
428
- if disable_metrics is False:
429
- attributes = {
430
- TELEMETRY_SDK_NAME:
431
- "openlit",
432
- SemanticConvetion.GEN_AI_APPLICATION_NAME:
433
- application_name,
434
- SemanticConvetion.GEN_AI_SYSTEM:
435
- SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN,
436
- SemanticConvetion.GEN_AI_ENVIRONMENT:
437
- environment,
438
- SemanticConvetion.GEN_AI_OPERATION:
439
- SemanticConvetion.GEN_AI_OPERATION_TYPE_CHAT,
440
- SemanticConvetion.GEN_AI_REQUEST_MODEL:
441
- str(get_attribute_from_instance_or_kwargs(instance, 'model'))
442
- }
443
-
444
- metrics["genai_requests"].add(1, attributes)
445
- metrics["genai_total_tokens"].add(
446
- input_tokens + output_tokens, attributes
447
- )
448
- metrics["genai_completion_tokens"].add(output_tokens, attributes)
449
- metrics["genai_prompt_tokens"].add(input_tokens, attributes)
450
- metrics["genai_cost"].record(cost, attributes)
451
-
452
- # Return original response
453
- return response
454
-
455
- except Exception as e:
456
- handle_exception(span, e)
457
- logger.error("Error in trace creation: %s", e)
458
-
459
- # Return original response
460
- return response
461
-
462
- return wrapper
463
-
464
- def chat(gen_ai_endpoint, version, environment, application_name,
465
- tracer, pricing_info, trace_content, metrics, disable_metrics):
466
- """
467
- Creates a wrapper around a function call to trace and log its execution metrics.
468
-
469
- This function wraps any given function to measure its execution time,
470
- log its operation, and trace its execution using OpenTelemetry.
471
-
472
- Parameters:
473
- - gen_ai_endpoint (str): A descriptor or name for the endpoint being traced.
474
- - version (str): The version of the Langchain application.
475
- - environment (str): The deployment environment (e.g., 'production', 'development').
476
- - application_name (str): Name of the Langchain application.
477
- - tracer (opentelemetry.trace.Tracer): The tracer object used for OpenTelemetry tracing.
478
- - pricing_info (dict): Information about the pricing for internal metrics (currently not used).
479
- - trace_content (bool): Flag indicating whether to trace the content of the response.
480
232
 
481
- Returns:
482
- - function: A higher-order function that takes a function 'wrapped' and returns
483
- a new function that wraps 'wrapped' with additional tracing and logging.
484
- """
233
+ server_address, server_port = "NOT_FOUND", "NOT_FOUND"
485
234
 
486
- def wrapper(wrapped, instance, args, kwargs):
487
- """
488
- An inner wrapper function that executes the wrapped function, measures execution
489
- time, and records trace data using OpenTelemetry.
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"
490
243
 
491
- Parameters:
492
- - wrapped (Callable): The original function that this wrapper will execute.
493
- - instance (object): The instance to which the wrapped function belongs. This
494
- is used for instance methods. For static and classmethods,
495
- this may be None.
496
- - args (tuple): Positional arguments passed to the wrapped function.
497
- - kwargs (dict): Keyword arguments passed to the wrapped function.
244
+ span_name = f"{SemanticConvetion.GEN_AI_OPERATION_TYPE_CHAT} {request_model}"
498
245
 
499
- Returns:
500
- - The result of the wrapped function call.
501
-
502
- The wrapper initiates a span with the provided tracer, sets various attributes
503
- on the span based on the function's execution and response, and ensures
504
- errors are handled and logged appropriately.
505
- """
506
- with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
246
+ with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
247
+ start_time = time.time()
507
248
  response = wrapped(*args, **kwargs)
249
+ end_time = time.time()
508
250
 
509
251
  try:
510
- prompt = ""
511
- if hasattr(response, 'usage_metadata') and response.usage_metadata:
512
- token_usage = response.usage_metadata
513
- input_tokens = token_usage.get("input_tokens", 0)
514
- output_tokens = token_usage.get("output_tokens", 0)
515
- if hasattr(instance, "model_id"):
516
- model = instance.model_id
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"]
517
264
  else:
518
- model = instance.model_name
519
- prompt = "" if isinstance(args[0], list) else args[0]
520
- else:
521
- if not isinstance(response, dict) or "output_text" not in response:
522
- return response
523
- # Fallback: Calculate tokens manually if response_metadata is missing
524
- model = "gpt-4o-mini" # Fallback model
525
- input_texts = [
526
- doc.page_content for doc in response.get("input_documents", [])
527
- if isinstance(doc.page_content, str)
528
- ]
529
- input_tokens = sum(general_tokens(text) for text in input_texts)
530
- output_text = response.get("output_text", "")
531
- output_tokens = general_tokens(output_text)
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))
532
283
 
533
284
  # Calculate cost of the operation
534
285
  cost = get_chat_model_cost(
535
- model,
286
+ request_model,
536
287
  pricing_info, input_tokens, output_tokens
537
288
  )
538
289
 
290
+ try:
291
+ llm_response = response.content
292
+ except AttributeError:
293
+ llm_response = response
294
+
295
+ # Set base span attribues (OTel Semconv)
539
296
  span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
540
- span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
541
- SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN)
542
- span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
543
- gen_ai_endpoint)
544
- span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
545
- environment)
546
297
  span.set_attribute(SemanticConvetion.GEN_AI_OPERATION,
547
298
  SemanticConvetion.GEN_AI_OPERATION_TYPE_CHAT)
548
- span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
549
- application_name)
299
+ span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
300
+ SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN)
550
301
  span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
551
- model)
302
+ request_model)
303
+ span.set_attribute(SemanticConvetion.GEN_AI_RESPONSE_MODEL,
304
+ request_model)
552
305
  span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TEMPERATURE,
553
306
  str(getattr(instance, 'temperature', 1)))
554
307
  span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_K,
555
308
  str(getattr(instance, 'top_k', 1)))
556
309
  span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_P,
557
310
  str(getattr(instance, 'top_p', 1)))
558
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
559
- False)
560
311
  span.set_attribute(SemanticConvetion.GEN_AI_USAGE_INPUT_TOKENS,
561
312
  input_tokens)
562
313
  span.set_attribute(SemanticConvetion.GEN_AI_USAGE_OUTPUT_TOKENS,
563
314
  output_tokens)
564
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
565
- input_tokens + output_tokens)
566
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
567
- cost)
568
- if trace_content:
569
- span.add_event(
570
- name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
571
- attributes={
572
- SemanticConvetion.GEN_AI_CONTENT_PROMPT: prompt,
573
- },
574
- )
575
- completion_content = getattr(response, 'content', "")
576
- span.add_event(
577
- name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
578
- attributes={
579
- SemanticConvetion.GEN_AI_CONTENT_COMPLETION: completion_content,
580
- },
581
- )
582
-
583
- span.set_status(Status(StatusCode.OK))
584
-
585
- if disable_metrics is False:
586
- attributes = {
587
- TELEMETRY_SDK_NAME:
588
- "openlit",
589
- SemanticConvetion.GEN_AI_APPLICATION_NAME:
590
- application_name,
591
- SemanticConvetion.GEN_AI_SYSTEM:
592
- SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN,
593
- SemanticConvetion.GEN_AI_ENVIRONMENT:
594
- environment,
595
- SemanticConvetion.GEN_AI_OPERATION:
596
- SemanticConvetion.GEN_AI_OPERATION_TYPE_CHAT,
597
- SemanticConvetion.GEN_AI_REQUEST_MODEL:
598
- model
599
- }
600
-
601
- metrics["genai_requests"].add(1, attributes)
602
- metrics["genai_total_tokens"].add(
603
- input_tokens + output_tokens, attributes
604
- )
605
- metrics["genai_completion_tokens"].add(output_tokens, attributes)
606
- metrics["genai_prompt_tokens"].add(input_tokens, attributes)
607
- metrics["genai_cost"].record(cost, attributes)
608
-
609
- # Return original response
610
- return response
611
-
612
- except Exception as e:
613
- handle_exception(span, e)
614
- logger.error("Error in trace creation: %s", e)
615
-
616
- # Return original response
617
- return response
618
-
619
- return wrapper
620
-
621
- def achat(gen_ai_endpoint, version, environment, application_name,
622
- tracer, pricing_info, trace_content, metrics, disable_metrics):
623
- """
624
- Creates a wrapper around a function call to trace and log its execution metrics.
625
-
626
- This function wraps any given function to measure its execution time,
627
- log its operation, and trace its execution using OpenTelemetry.
628
-
629
- Parameters:
630
- - gen_ai_endpoint (str): A descriptor or name for the endpoint being traced.
631
- - version (str): The version of the Langchain application.
632
- - environment (str): The deployment environment (e.g., 'production', 'development').
633
- - application_name (str): Name of the Langchain application.
634
- - tracer (opentelemetry.trace.Tracer): The tracer object used for OpenTelemetry tracing.
635
- - pricing_info (dict): Information about the pricing for internal metrics (currently not used).
636
- - trace_content (bool): Flag indicating whether to trace the content of the response.
637
-
638
- Returns:
639
- - function: A higher-order function that takes a function 'wrapped' and returns
640
- a new function that wraps 'wrapped' with additional tracing and logging.
641
- """
642
-
643
- async def wrapper(wrapped, instance, args, kwargs):
644
- """
645
- An inner wrapper function that executes the wrapped function, measures execution
646
- time, and records trace data using OpenTelemetry.
647
-
648
- Parameters:
649
- - wrapped (Callable): The original function that this wrapper will execute.
650
- - instance (object): The instance to which the wrapped function belongs. This
651
- is used for instance methods. For static and classmethods,
652
- this may be None.
653
- - args (tuple): Positional arguments passed to the wrapped function.
654
- - kwargs (dict): Keyword arguments passed to the wrapped function.
655
-
656
- Returns:
657
- - The result of the wrapped function call.
658
-
659
- The wrapper initiates a span with the provided tracer, sets various attributes
660
- on the span based on the function's execution and response, and ensures
661
- errors are handled and logged appropriately.
662
- """
663
- with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
664
- response = await wrapped(*args, **kwargs)
665
-
666
- try:
667
- prompt = ""
668
- if hasattr(response, 'usage_metadata') and response.usage_metadata:
669
- token_usage = response.usage_metadata
670
- input_tokens = token_usage.get("input_tokens", 0)
671
- output_tokens = token_usage.get("output_tokens", 0)
672
- if hasattr(instance, "model_id"):
673
- model = instance.model_id
674
- else:
675
- model = instance.model_name
676
- prompt = "" if isinstance(args[0], list) else args[0]
677
- else:
678
- if not isinstance(response, dict) or "output_text" not in response:
679
- return response
680
- # Fallback: Calculate tokens manually if response_metadata is missing
681
- model = "gpt-4o-mini" # Fallback model
682
- input_texts = [
683
- doc.page_content for doc in response.get("input_documents", [])
684
- if isinstance(doc.page_content, str)
685
- ]
686
- input_tokens = sum(general_tokens(text) for text in input_texts)
687
- output_text = response.get("output_text", "")
688
- output_tokens = general_tokens(output_text)
689
-
690
- # Calculate cost of the operation
691
- cost = get_chat_model_cost(
692
- model,
693
- pricing_info, input_tokens, output_tokens
694
- )
695
-
696
- span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
697
- span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
698
- SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN)
699
- span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
700
- gen_ai_endpoint)
701
- span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
702
- environment)
703
- span.set_attribute(SemanticConvetion.GEN_AI_OPERATION,
704
- SemanticConvetion.GEN_AI_OPERATION_TYPE_CHAT)
705
- span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
315
+ span.set_attribute(SemanticConvetion.SERVER_ADDRESS,
316
+ server_address)
317
+ span.set_attribute(SemanticConvetion.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,
706
324
  application_name)
707
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
708
- model)
709
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TEMPERATURE,
710
- str(getattr(instance, 'temperature',1)))
711
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_K,
712
- str(getattr(instance, 'top_k',1)))
713
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_P,
714
- str(getattr(instance, 'top_p',1)))
715
325
  span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
716
326
  False)
717
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_INPUT_TOKENS,
718
- input_tokens)
719
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_OUTPUT_TOKENS,
720
- output_tokens)
721
327
  span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
722
328
  input_tokens + output_tokens)
723
329
  span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
724
330
  cost)
725
- if trace_content:
331
+ span.set_attribute(SemanticConvetion.GEN_AI_SERVER_TTFT,
332
+ end_time - start_time)
333
+ span.set_attribute(SemanticConvetion.GEN_AI_SDK_VERSION,
334
+ version)
335
+
336
+ if capture_message_content:
726
337
  span.add_event(
727
338
  name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
728
339
  attributes={
@@ -732,32 +343,34 @@ def achat(gen_ai_endpoint, version, environment, application_name,
732
343
  span.add_event(
733
344
  name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
734
345
  attributes={
735
- SemanticConvetion.GEN_AI_CONTENT_COMPLETION: response.content,
346
+ SemanticConvetion.GEN_AI_CONTENT_COMPLETION: llm_response,
736
347
  },
737
348
  )
738
349
 
739
350
  span.set_status(Status(StatusCode.OK))
740
351
 
741
352
  if disable_metrics is False:
742
- attributes = {
743
- TELEMETRY_SDK_NAME:
744
- "openlit",
745
- SemanticConvetion.GEN_AI_APPLICATION_NAME:
746
- application_name,
747
- SemanticConvetion.GEN_AI_SYSTEM:
748
- SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN,
749
- SemanticConvetion.GEN_AI_ENVIRONMENT:
750
- environment,
751
- SemanticConvetion.GEN_AI_OPERATION:
752
- SemanticConvetion.GEN_AI_OPERATION_TYPE_CHAT,
753
- SemanticConvetion.GEN_AI_REQUEST_MODEL:
754
- model
755
- }
353
+ attributes = create_metrics_attributes(
354
+ service_name=application_name,
355
+ deployment_environment=environment,
356
+ operation=SemanticConvetion.GEN_AI_OPERATION_TYPE_CHAT,
357
+ system=SemanticConvetion.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
+ )
756
363
 
757
- metrics["genai_requests"].add(1, attributes)
758
- metrics["genai_total_tokens"].add(
364
+ metrics["genai_client_usage_tokens"].record(
759
365
  input_tokens + output_tokens, attributes
760
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)
761
374
  metrics["genai_completion_tokens"].add(output_tokens, attributes)
762
375
  metrics["genai_prompt_tokens"].add(input_tokens, attributes)
763
376
  metrics["genai_cost"].record(cost, attributes)