openlit 1.30.1__tar.gz → 1.30.3__tar.gz

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 (83) hide show
  1. {openlit-1.30.1 → openlit-1.30.3}/PKG-INFO +3 -2
  2. {openlit-1.30.1 → openlit-1.30.3}/README.md +2 -1
  3. {openlit-1.30.1 → openlit-1.30.3}/pyproject.toml +1 -1
  4. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/__init__.py +2 -2
  5. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/evals/utils.py +5 -2
  6. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/guard/utils.py +5 -1
  7. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/azure_ai_inference/async_azure_ai_inference.py +0 -1
  8. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/azure_ai_inference/azure_ai_inference.py +0 -1
  9. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/google_ai_studio/google_ai_studio.py +0 -1
  10. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/litellm/__init__.py +16 -2
  11. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/litellm/async_litellm.py +115 -0
  12. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/litellm/litellm.py +115 -0
  13. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/openai/async_openai.py +8 -6
  14. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/openai/openai.py +8 -6
  15. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/otel/metrics.py +6 -3
  16. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/otel/tracing.py +5 -2
  17. {openlit-1.30.1 → openlit-1.30.3}/LICENSE +0 -0
  18. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/__helpers.py +0 -0
  19. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/evals/__init__.py +0 -0
  20. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/evals/all.py +0 -0
  21. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/evals/bias_detection.py +0 -0
  22. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/evals/hallucination.py +0 -0
  23. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/evals/toxicity.py +0 -0
  24. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/guard/__init__.py +0 -0
  25. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/guard/all.py +0 -0
  26. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/guard/prompt_injection.py +0 -0
  27. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/guard/restrict_topic.py +0 -0
  28. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/guard/sensitive_topic.py +0 -0
  29. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/anthropic/__init__.py +0 -0
  30. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/anthropic/anthropic.py +0 -0
  31. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/anthropic/async_anthropic.py +0 -0
  32. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/azure_ai_inference/__init__.py +0 -0
  33. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/bedrock/__init__.py +0 -0
  34. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/bedrock/bedrock.py +0 -0
  35. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/chroma/__init__.py +0 -0
  36. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/chroma/chroma.py +0 -0
  37. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/cohere/__init__.py +0 -0
  38. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/cohere/cohere.py +0 -0
  39. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/crewai/__init__.py +0 -0
  40. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/crewai/crewai.py +0 -0
  41. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/elevenlabs/__init__.py +0 -0
  42. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/elevenlabs/async_elevenlabs.py +0 -0
  43. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/elevenlabs/elevenlabs.py +0 -0
  44. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/embedchain/__init__.py +0 -0
  45. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/embedchain/embedchain.py +0 -0
  46. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/google_ai_studio/__init__.py +0 -0
  47. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/google_ai_studio/async_google_ai_studio.py +0 -0
  48. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/gpt4all/__init__.py +0 -0
  49. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/gpt4all/gpt4all.py +0 -0
  50. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/gpu/__init__.py +0 -0
  51. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/groq/__init__.py +0 -0
  52. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/groq/async_groq.py +0 -0
  53. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/groq/groq.py +0 -0
  54. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/haystack/__init__.py +0 -0
  55. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/haystack/haystack.py +0 -0
  56. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/langchain/__init__.py +0 -0
  57. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/langchain/langchain.py +0 -0
  58. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/llamaindex/__init__.py +0 -0
  59. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/llamaindex/llamaindex.py +0 -0
  60. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/milvus/__init__.py +0 -0
  61. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/milvus/milvus.py +0 -0
  62. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/mistral/__init__.py +0 -0
  63. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/mistral/async_mistral.py +0 -0
  64. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/mistral/mistral.py +0 -0
  65. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/ollama/__init__.py +0 -0
  66. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/ollama/async_ollama.py +0 -0
  67. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/ollama/ollama.py +0 -0
  68. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/openai/__init__.py +0 -0
  69. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/openai/async_azure_openai.py +0 -0
  70. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/openai/azure_openai.py +0 -0
  71. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/pinecone/__init__.py +0 -0
  72. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/pinecone/pinecone.py +0 -0
  73. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/qdrant/__init__.py +0 -0
  74. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/qdrant/async_qdrant.py +0 -0
  75. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/qdrant/qdrant.py +0 -0
  76. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/transformers/__init__.py +0 -0
  77. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/transformers/transformers.py +0 -0
  78. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/vertexai/__init__.py +0 -0
  79. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/vertexai/async_vertexai.py +0 -0
  80. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/vertexai/vertexai.py +0 -0
  81. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/vllm/__init__.py +0 -0
  82. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/instrumentation/vllm/vllm.py +0 -0
  83. {openlit-1.30.1 → openlit-1.30.3}/src/openlit/semcov/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openlit
3
- Version: 1.30.1
3
+ Version: 1.30.3
4
4
  Summary: OpenTelemetry-native Auto instrumentation library for monitoring LLM Applications and GPUs, facilitating the integration of observability into your GenAI-driven projects
5
5
  Home-page: https://github.com/openlit/openlit/tree/main/openlit/python
6
6
  Keywords: OpenTelemetry,otel,otlp,llm,tracing,openai,anthropic,claude,cohere,llm monitoring,observability,monitoring,gpt,Generative AI,chatGPT,gpu
@@ -72,7 +72,7 @@ This project proudly follows and maintains the [Semantic Conventions](https://gi
72
72
  | [✅ Cohere](https://docs.openlit.io/latest/integrations/cohere) | | [✅ EmbedChain](https://docs.openlit.io/latest/integrations/embedchain) | |
73
73
  | [✅ Mistral](https://docs.openlit.io/latest/integrations/mistral) | | [✅ Guardrails](https://docs.openlit.io/latest/integrations/guardrails) | |
74
74
  | [✅ Azure OpenAI](https://docs.openlit.io/latest/integrations/azure-openai) | | [✅ CrewAI](https://docs.openlit.io/latest/integrations/crewai) | |
75
- | [✅ Azure AI Inference](https://docs.openlit.io/latest/integrations/azure-ai-inference) | |
75
+ | [✅ Azure AI Inference](https://docs.openlit.io/latest/integrations/azure-ai-inference) | | [✅ DSPy](https://docs.openlit.io/latest/integrations/dspy) | |
76
76
  | [✅ GitHub AI Models](https://docs.openlit.io/latest/integrations/github-models) | | | |
77
77
  | [✅ HuggingFace Transformers](https://docs.openlit.io/latest/integrations/huggingface) | | | |
78
78
  | [✅ Amazon Bedrock](https://docs.openlit.io/latest/integrations/bedrock) | | | |
@@ -84,6 +84,7 @@ This project proudly follows and maintains the [Semantic Conventions](https://gi
84
84
  | [✅ Google AI Studio](https://docs.openlit.io/latest/integrations/google-ai-studio) | | | |
85
85
  | [✅ NVIDIA NIM](https://docs.openlit.io/latest/integrations/nvidia-nim) | | | |
86
86
 
87
+
87
88
  ## Supported Destinations
88
89
  - [✅ OpenTelemetry Collector](https://docs.openlit.io/latest/connections/otelcol)
89
90
  - [✅ Prometheus + Tempo](https://docs.openlit.io/latest/connections/prometheus-tempo)
@@ -41,7 +41,7 @@ This project proudly follows and maintains the [Semantic Conventions](https://gi
41
41
  | [✅ Cohere](https://docs.openlit.io/latest/integrations/cohere) | | [✅ EmbedChain](https://docs.openlit.io/latest/integrations/embedchain) | |
42
42
  | [✅ Mistral](https://docs.openlit.io/latest/integrations/mistral) | | [✅ Guardrails](https://docs.openlit.io/latest/integrations/guardrails) | |
43
43
  | [✅ Azure OpenAI](https://docs.openlit.io/latest/integrations/azure-openai) | | [✅ CrewAI](https://docs.openlit.io/latest/integrations/crewai) | |
44
- | [✅ Azure AI Inference](https://docs.openlit.io/latest/integrations/azure-ai-inference) | |
44
+ | [✅ Azure AI Inference](https://docs.openlit.io/latest/integrations/azure-ai-inference) | | [✅ DSPy](https://docs.openlit.io/latest/integrations/dspy) | |
45
45
  | [✅ GitHub AI Models](https://docs.openlit.io/latest/integrations/github-models) | | | |
46
46
  | [✅ HuggingFace Transformers](https://docs.openlit.io/latest/integrations/huggingface) | | | |
47
47
  | [✅ Amazon Bedrock](https://docs.openlit.io/latest/integrations/bedrock) | | | |
@@ -53,6 +53,7 @@ This project proudly follows and maintains the [Semantic Conventions](https://gi
53
53
  | [✅ Google AI Studio](https://docs.openlit.io/latest/integrations/google-ai-studio) | | | |
54
54
  | [✅ NVIDIA NIM](https://docs.openlit.io/latest/integrations/nvidia-nim) | | | |
55
55
 
56
+
56
57
  ## Supported Destinations
57
58
  - [✅ OpenTelemetry Collector](https://docs.openlit.io/latest/connections/otelcol)
58
59
  - [✅ Prometheus + Tempo](https://docs.openlit.io/latest/connections/prometheus-tempo)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "openlit"
3
- version = "1.30.1"
3
+ version = "1.30.3"
4
4
  description = "OpenTelemetry-native Auto instrumentation library for monitoring LLM Applications and GPUs, facilitating the integration of observability into your GenAI-driven projects"
5
5
  authors = ["OpenLIT"]
6
6
  repository = "https://github.com/openlit/openlit/tree/main/openlit/python"
@@ -379,7 +379,7 @@ def get_prompt(url=None, name=None, api_key=None, prompt_id=None,
379
379
  # Return the JSON response
380
380
  return response.json()
381
381
  except requests.RequestException as error:
382
- print(f"Error fetching prompt: {error}")
382
+ logger.error("Error fetching prompt: '%s'", error)
383
383
  return None
384
384
 
385
385
  def get_secrets(url=None, api_key=None, key=None, tags=None, should_set_env=None):
@@ -437,7 +437,7 @@ def get_secrets(url=None, api_key=None, key=None, tags=None, should_set_env=None
437
437
  os.environ[token] = str(value)
438
438
  return vault_response
439
439
  except requests.RequestException as error:
440
- print(f"Error fetching secrets: {error}")
440
+ logger.error("Error fetching secrets: '%s'", error)
441
441
  return None
442
442
 
443
443
  def trace(wrapped):
@@ -3,15 +3,18 @@
3
3
 
4
4
  import json
5
5
  import os
6
+ import logging
6
7
  from typing import Optional, Tuple, List
7
8
  from pydantic import BaseModel
8
-
9
9
  from opentelemetry.metrics import get_meter
10
10
  from opentelemetry.sdk.resources import TELEMETRY_SDK_NAME
11
11
  from anthropic import Anthropic
12
12
  from openai import OpenAI
13
13
  from openlit.semcov import SemanticConvetion
14
14
 
15
+ # Initialize logger for logging potential issues and operations
16
+ logger = logging.getLogger(__name__)
17
+
15
18
  class JsonOutput(BaseModel):
16
19
  """
17
20
  A model representing the structure of JSON output for prompt injection detection.
@@ -216,7 +219,7 @@ def parse_llm_response(response) -> JsonOutput:
216
219
 
217
220
  return JsonOutput(**data)
218
221
  except (json.JSONDecodeError, TypeError) as e:
219
- print(f"Error parsing LLM response: {e}")
222
+ logger.error("Error parsing LLM response: '%s'", e)
220
223
  return JsonOutput(score=0, classification="none", explanation="none",
221
224
  verdict="no", evaluation="none")
222
225
 
@@ -4,6 +4,7 @@
4
4
  import re
5
5
  import json
6
6
  import os
7
+ import logging
7
8
  from typing import Optional, Tuple
8
9
  from pydantic import BaseModel
9
10
  from opentelemetry.metrics import get_meter
@@ -12,6 +13,9 @@ from anthropic import Anthropic
12
13
  from openai import OpenAI
13
14
  from openlit.semcov import SemanticConvetion
14
15
 
16
+ # Initialize logger for logging potential issues and operations
17
+ logger = logging.getLogger(__name__)
18
+
15
19
  class JsonOutput(BaseModel):
16
20
  """
17
21
  A model representing the structure of JSON output for prompt injection detection.
@@ -158,7 +162,7 @@ def parse_llm_response(response) -> JsonOutput:
158
162
 
159
163
  return JsonOutput(**data)
160
164
  except (json.JSONDecodeError, TypeError) as e:
161
- print(f"Error parsing LLM response: {e}")
165
+ logger.error("Error parsing LLM response: '%s'", e)
162
166
  return JsonOutput(score=0, classification="none", explanation="none",
163
167
  verdict="none", guard="none")
164
168
 
@@ -195,7 +195,6 @@ def async_complete(gen_ai_endpoint, version, environment, application_name,
195
195
  with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
196
196
  response = await wrapped(*args, **kwargs)
197
197
 
198
- # print(instance._system_instruction.__dict__["_pb"].parts[0].text)
199
198
  try:
200
199
  # Format 'messages' into a single string
201
200
  message_prompt = kwargs.get("messages", "")
@@ -195,7 +195,6 @@ def complete(gen_ai_endpoint, version, environment, application_name,
195
195
  with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
196
196
  response = wrapped(*args, **kwargs)
197
197
 
198
- # print(instance._system_instruction.__dict__["_pb"].parts[0].text)
199
198
  try:
200
199
  # Format 'messages' into a single string
201
200
  message_prompt = kwargs.get("messages", "")
@@ -164,7 +164,6 @@ def generate(gen_ai_endpoint, version, environment, application_name,
164
164
  with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
165
165
  response = wrapped(*args, **kwargs)
166
166
 
167
- # print(instance._system_instruction.__dict__["_pb"].parts[0].text)
168
167
  try:
169
168
  prompt = ""
170
169
  for arg in args:
@@ -7,10 +7,10 @@ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
7
7
  from wrapt import wrap_function_wrapper
8
8
 
9
9
  from openlit.instrumentation.litellm.litellm import (
10
- completion
10
+ completion, embedding
11
11
  )
12
12
  from openlit.instrumentation.litellm.async_litellm import (
13
- acompletion
13
+ acompletion, aembedding
14
14
  )
15
15
 
16
16
  _instruments = ("litellm >= 1.52.6",)
@@ -48,6 +48,20 @@ class LiteLLMInstrumentor(BaseInstrumentor):
48
48
  tracer, pricing_info, trace_content, metrics, disable_metrics),
49
49
  )
50
50
 
51
+ wrap_function_wrapper(
52
+ "litellm",
53
+ "embedding",
54
+ embedding("litellm.embedding", version, environment, application_name,
55
+ tracer, pricing_info, trace_content, metrics, disable_metrics),
56
+ )
57
+
58
+ wrap_function_wrapper(
59
+ "litellm",
60
+ "aembedding",
61
+ aembedding("litellm.embedding", version, environment, application_name,
62
+ tracer, pricing_info, trace_content, metrics, disable_metrics),
63
+ )
64
+
51
65
 
52
66
  def _uninstrument(self, **kwargs):
53
67
  # Proper uninstrumentation logic to revert patched methods
@@ -8,6 +8,7 @@ from opentelemetry.trace import SpanKind, Status, StatusCode
8
8
  from opentelemetry.sdk.resources import TELEMETRY_SDK_NAME
9
9
  from openlit.__helpers import (
10
10
  get_chat_model_cost,
11
+ get_embed_model_cost,
11
12
  openai_tokens,
12
13
  handle_exception,
13
14
  response_as_dict,
@@ -404,3 +405,117 @@ def acompletion(gen_ai_endpoint, version, environment, application_name,
404
405
  return response
405
406
 
406
407
  return wrapper
408
+
409
+ def aembedding(gen_ai_endpoint, version, environment, application_name,
410
+ tracer, pricing_info, trace_content, metrics, disable_metrics):
411
+ """
412
+ Generates a telemetry wrapper for embeddings to collect metrics.
413
+
414
+ Args:
415
+ gen_ai_endpoint: Endpoint identifier for logging and tracing.
416
+ version: Version of the monitoring package.
417
+ environment: Deployment environment (e.g., production, staging).
418
+ application_name: Name of the application using the OpenAI API.
419
+ tracer: OpenTelemetry tracer for creating spans.
420
+ pricing_info: Information used for calculating the cost of OpenAI usage.
421
+ trace_content: Flag indicating whether to trace the actual content.
422
+
423
+ Returns:
424
+ A function that wraps the embeddings method to add telemetry.
425
+ """
426
+
427
+ async def wrapper(wrapped, instance, args, kwargs):
428
+ """
429
+ Wraps the 'embeddings' API call to add telemetry.
430
+
431
+ This collects metrics such as execution time, cost, and token usage, and handles errors
432
+ gracefully, adding details to the trace for observability.
433
+
434
+ Args:
435
+ wrapped: The original 'embeddings' method to be wrapped.
436
+ instance: The instance of the class where the original method is defined.
437
+ args: Positional arguments for the 'embeddings' method.
438
+ kwargs: Keyword arguments for the 'embeddings' method.
439
+
440
+ Returns:
441
+ The response from the original 'embeddings' method.
442
+ """
443
+
444
+ with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
445
+ response = await wrapped(*args, **kwargs)
446
+ response_dict = response_as_dict(response)
447
+ try:
448
+ # Calculate cost of the operation
449
+ cost = get_embed_model_cost(kwargs.get("model", "text-embedding-ada-002"),
450
+ pricing_info, response_dict.get('usage').get('prompt_tokens'))
451
+
452
+ # Set Span attributes
453
+ span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
454
+ span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
455
+ SemanticConvetion.GEN_AI_SYSTEM_OPENAI)
456
+ span.set_attribute(SemanticConvetion.GEN_AI_TYPE,
457
+ SemanticConvetion.GEN_AI_TYPE_EMBEDDING)
458
+ span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
459
+ gen_ai_endpoint)
460
+ span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
461
+ environment)
462
+ span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
463
+ application_name)
464
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
465
+ kwargs.get("model", "text-embedding-ada-002"))
466
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_EMBEDDING_FORMAT,
467
+ kwargs.get("encoding_format", "float"))
468
+ # span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_EMBEDDING_DIMENSION,
469
+ # kwargs.get("dimensions", "null"))
470
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_USER,
471
+ kwargs.get("user", ""))
472
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
473
+ response_dict.get('usage').get('prompt_tokens'))
474
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
475
+ response_dict.get('usage').get('total_tokens'))
476
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
477
+ cost)
478
+ if trace_content:
479
+ span.add_event(
480
+ name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
481
+ attributes={
482
+ SemanticConvetion.GEN_AI_CONTENT_PROMPT: kwargs.get("input", ""),
483
+ },
484
+ )
485
+
486
+ span.set_status(Status(StatusCode.OK))
487
+
488
+ if disable_metrics is False:
489
+ attributes = {
490
+ TELEMETRY_SDK_NAME:
491
+ "openlit",
492
+ SemanticConvetion.GEN_AI_APPLICATION_NAME:
493
+ application_name,
494
+ SemanticConvetion.GEN_AI_SYSTEM:
495
+ SemanticConvetion.GEN_AI_SYSTEM_OPENAI,
496
+ SemanticConvetion.GEN_AI_ENVIRONMENT:
497
+ environment,
498
+ SemanticConvetion.GEN_AI_TYPE:
499
+ SemanticConvetion.GEN_AI_TYPE_EMBEDDING,
500
+ SemanticConvetion.GEN_AI_REQUEST_MODEL:
501
+ kwargs.get("model", "text-embedding-ada-002")
502
+ }
503
+
504
+ metrics["genai_requests"].add(1, attributes)
505
+ metrics["genai_total_tokens"].add(
506
+ response_dict.get('usage').get('total_tokens'), attributes)
507
+ metrics["genai_prompt_tokens"].add(
508
+ response_dict.get('usage').get('prompt_tokens'), attributes)
509
+ metrics["genai_cost"].record(cost, attributes)
510
+
511
+ # Return original response
512
+ return response
513
+
514
+ except Exception as e:
515
+ handle_exception(span, e)
516
+ logger.error("Error in trace creation: %s", e)
517
+
518
+ # Return original response
519
+ return response
520
+
521
+ return wrapper
@@ -8,6 +8,7 @@ from opentelemetry.trace import SpanKind, Status, StatusCode
8
8
  from opentelemetry.sdk.resources import TELEMETRY_SDK_NAME
9
9
  from openlit.__helpers import (
10
10
  get_chat_model_cost,
11
+ get_embed_model_cost,
11
12
  openai_tokens,
12
13
  handle_exception,
13
14
  response_as_dict,
@@ -404,3 +405,117 @@ def completion(gen_ai_endpoint, version, environment, application_name,
404
405
  return response
405
406
 
406
407
  return wrapper
408
+
409
+ def embedding(gen_ai_endpoint, version, environment, application_name,
410
+ tracer, pricing_info, trace_content, metrics, disable_metrics):
411
+ """
412
+ Generates a telemetry wrapper for embeddings to collect metrics.
413
+
414
+ Args:
415
+ gen_ai_endpoint: Endpoint identifier for logging and tracing.
416
+ version: Version of the monitoring package.
417
+ environment: Deployment environment (e.g., production, staging).
418
+ application_name: Name of the application using the OpenAI API.
419
+ tracer: OpenTelemetry tracer for creating spans.
420
+ pricing_info: Information used for calculating the cost of OpenAI usage.
421
+ trace_content: Flag indicating whether to trace the actual content.
422
+
423
+ Returns:
424
+ A function that wraps the embeddings method to add telemetry.
425
+ """
426
+
427
+ def wrapper(wrapped, instance, args, kwargs):
428
+ """
429
+ Wraps the 'embeddings' API call to add telemetry.
430
+
431
+ This collects metrics such as execution time, cost, and token usage, and handles errors
432
+ gracefully, adding details to the trace for observability.
433
+
434
+ Args:
435
+ wrapped: The original 'embeddings' method to be wrapped.
436
+ instance: The instance of the class where the original method is defined.
437
+ args: Positional arguments for the 'embeddings' method.
438
+ kwargs: Keyword arguments for the 'embeddings' method.
439
+
440
+ Returns:
441
+ The response from the original 'embeddings' method.
442
+ """
443
+
444
+ with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
445
+ response = wrapped(*args, **kwargs)
446
+ response_dict = response_as_dict(response)
447
+ try:
448
+ # Calculate cost of the operation
449
+ cost = get_embed_model_cost(kwargs.get("model", "text-embedding-ada-002"),
450
+ pricing_info, response_dict.get('usage').get('prompt_tokens'))
451
+
452
+ # Set Span attributes
453
+ span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
454
+ span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
455
+ SemanticConvetion.GEN_AI_SYSTEM_OPENAI)
456
+ span.set_attribute(SemanticConvetion.GEN_AI_TYPE,
457
+ SemanticConvetion.GEN_AI_TYPE_EMBEDDING)
458
+ span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
459
+ gen_ai_endpoint)
460
+ span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
461
+ environment)
462
+ span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
463
+ application_name)
464
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
465
+ kwargs.get("model", "text-embedding-ada-002"))
466
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_EMBEDDING_FORMAT,
467
+ kwargs.get("encoding_format", "float"))
468
+ # span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_EMBEDDING_DIMENSION,
469
+ # kwargs.get("dimensions", "null"))
470
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_USER,
471
+ kwargs.get("user", ""))
472
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
473
+ response_dict.get('usage').get('prompt_tokens'))
474
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
475
+ response_dict.get('usage').get('total_tokens'))
476
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
477
+ cost)
478
+ if trace_content:
479
+ span.add_event(
480
+ name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
481
+ attributes={
482
+ SemanticConvetion.GEN_AI_CONTENT_PROMPT: kwargs.get("input", ""),
483
+ },
484
+ )
485
+
486
+ span.set_status(Status(StatusCode.OK))
487
+
488
+ if disable_metrics is False:
489
+ attributes = {
490
+ TELEMETRY_SDK_NAME:
491
+ "openlit",
492
+ SemanticConvetion.GEN_AI_APPLICATION_NAME:
493
+ application_name,
494
+ SemanticConvetion.GEN_AI_SYSTEM:
495
+ SemanticConvetion.GEN_AI_SYSTEM_OPENAI,
496
+ SemanticConvetion.GEN_AI_ENVIRONMENT:
497
+ environment,
498
+ SemanticConvetion.GEN_AI_TYPE:
499
+ SemanticConvetion.GEN_AI_TYPE_EMBEDDING,
500
+ SemanticConvetion.GEN_AI_REQUEST_MODEL:
501
+ kwargs.get("model", "text-embedding-ada-002")
502
+ }
503
+
504
+ metrics["genai_requests"].add(1, attributes)
505
+ metrics["genai_total_tokens"].add(
506
+ response_dict.get('usage').get('total_tokens'), attributes)
507
+ metrics["genai_prompt_tokens"].add(
508
+ response_dict.get('usage').get('prompt_tokens'), attributes)
509
+ metrics["genai_cost"].record(cost, attributes)
510
+
511
+ # Return original response
512
+ return response
513
+
514
+ except Exception as e:
515
+ handle_exception(span, e)
516
+ logger.error("Error in trace creation: %s", e)
517
+
518
+ # Return original response
519
+ return response
520
+
521
+ return wrapper
@@ -446,11 +446,11 @@ def async_embedding(gen_ai_endpoint, version, environment, application_name,
446
446
 
447
447
  with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
448
448
  response = await wrapped(*args, **kwargs)
449
-
449
+ response_dict = response_as_dict(response)
450
450
  try:
451
451
  # Calculate cost of the operation
452
452
  cost = get_embed_model_cost(kwargs.get("model", "text-embedding-ada-002"),
453
- pricing_info, response.usage.prompt_tokens)
453
+ pricing_info, response_dict.get('usage').get('prompt_tokens'))
454
454
 
455
455
  # Set Span attributes
456
456
  span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
@@ -473,9 +473,9 @@ def async_embedding(gen_ai_endpoint, version, environment, application_name,
473
473
  span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_USER,
474
474
  kwargs.get("user", ""))
475
475
  span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
476
- response.usage.prompt_tokens)
476
+ response_dict.get('usage').get('prompt_tokens'))
477
477
  span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
478
- response.usage.total_tokens)
478
+ response_dict.get('usage').get('total_tokens'))
479
479
  span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
480
480
  cost)
481
481
  if trace_content:
@@ -505,8 +505,10 @@ def async_embedding(gen_ai_endpoint, version, environment, application_name,
505
505
  }
506
506
 
507
507
  metrics["genai_requests"].add(1, attributes)
508
- metrics["genai_total_tokens"].add(response.usage.total_tokens, attributes)
509
- metrics["genai_prompt_tokens"].add(response.usage.prompt_tokens, attributes)
508
+ metrics["genai_total_tokens"].add(
509
+ response_dict.get('usage').get('total_tokens'), attributes)
510
+ metrics["genai_prompt_tokens"].add(
511
+ response_dict.get('usage').get('prompt_tokens'), attributes)
510
512
  metrics["genai_cost"].record(cost, attributes)
511
513
 
512
514
  # Return original response
@@ -446,11 +446,11 @@ def embedding(gen_ai_endpoint, version, environment, application_name,
446
446
 
447
447
  with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
448
448
  response = wrapped(*args, **kwargs)
449
-
449
+ response_dict = response_as_dict(response)
450
450
  try:
451
451
  # Calculate cost of the operation
452
452
  cost = get_embed_model_cost(kwargs.get("model", "text-embedding-ada-002"),
453
- pricing_info, response.usage.prompt_tokens)
453
+ pricing_info, response_dict.get('usage').get('prompt_tokens'))
454
454
 
455
455
  # Set Span attributes
456
456
  span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
@@ -473,9 +473,9 @@ def embedding(gen_ai_endpoint, version, environment, application_name,
473
473
  span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_USER,
474
474
  kwargs.get("user", ""))
475
475
  span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
476
- response.usage.prompt_tokens)
476
+ response_dict.get('usage').get('prompt_tokens'))
477
477
  span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
478
- response.usage.total_tokens)
478
+ response_dict.get('usage').get('total_tokens'))
479
479
  span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
480
480
  cost)
481
481
  if trace_content:
@@ -505,8 +505,10 @@ def embedding(gen_ai_endpoint, version, environment, application_name,
505
505
  }
506
506
 
507
507
  metrics["genai_requests"].add(1, attributes)
508
- metrics["genai_total_tokens"].add(response.usage.total_tokens, attributes)
509
- metrics["genai_prompt_tokens"].add(response.usage.prompt_tokens, attributes)
508
+ metrics["genai_total_tokens"].add(
509
+ response_dict.get('usage').get('total_tokens'), attributes)
510
+ metrics["genai_prompt_tokens"].add(
511
+ response_dict.get('usage').get('prompt_tokens'), attributes)
510
512
  metrics["genai_cost"].record(cost, attributes)
511
513
 
512
514
  # Return original response
@@ -1,4 +1,4 @@
1
- # pylint: disable=duplicate-code, line-too-long
1
+ # pylint: disable=duplicate-code, line-too-long, ungrouped-imports
2
2
  """
3
3
  Setups up OpenTelemetry Meter
4
4
  """
@@ -8,10 +8,13 @@ from opentelemetry.sdk.metrics import MeterProvider
8
8
  from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader, ConsoleMetricExporter
9
9
  from opentelemetry.sdk.resources import SERVICE_NAME, TELEMETRY_SDK_NAME, DEPLOYMENT_ENVIRONMENT
10
10
  from opentelemetry.sdk.resources import Resource
11
- from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
12
-
13
11
  from openlit.semcov import SemanticConvetion
14
12
 
13
+ if os.environ.get("OTEL_EXPORTER_OTLP_PROTOCOL") == "grpc":
14
+ from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
15
+ else:
16
+ from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
17
+
15
18
  # Global flag to check if the meter provider initialization is complete.
16
19
  METER_SET = False
17
20
 
@@ -1,4 +1,4 @@
1
- # pylint: disable=duplicate-code, line-too-long
1
+ # pylint: disable=duplicate-code, line-too-long, ungrouped-imports
2
2
  """
3
3
  Setups up OpenTelemetry tracer
4
4
  """
@@ -10,8 +10,11 @@ from opentelemetry.sdk.resources import Resource
10
10
  from opentelemetry.sdk.trace import TracerProvider
11
11
  from opentelemetry.sdk.trace.export import BatchSpanProcessor, SimpleSpanProcessor
12
12
  from opentelemetry.sdk.trace.export import ConsoleSpanExporter
13
- from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
14
13
 
14
+ if os.environ.get("OTEL_EXPORTER_OTLP_PROTOCOL") == "grpc":
15
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
16
+ else:
17
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
15
18
 
16
19
  # Global flag to check if the tracer provider initialization is complete.
17
20
  TRACER_SET = False
File without changes