openlit 1.18.2__py3-none-any.whl → 1.20.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
openlit/__init__.py CHANGED
@@ -33,6 +33,7 @@ from openlit.instrumentation.ollama import OllamaInstrumentor
33
33
  from openlit.instrumentation.gpt4all import GPT4AllInstrumentor
34
34
  from openlit.instrumentation.elevenlabs import ElevenLabsInstrumentor
35
35
  from openlit.instrumentation.vllm import VLLMInstrumentor
36
+ from openlit.instrumentation.google_ai_studio import GoogleAIStudioInstrumentor
36
37
  from openlit.instrumentation.langchain import LangChainInstrumentor
37
38
  from openlit.instrumentation.llamaindex import LlamaIndexInstrumentor
38
39
  from openlit.instrumentation.haystack import HaystackInstrumentor
@@ -196,6 +197,7 @@ def init(environment="default", application_name="default", tracer=None, otlp_en
196
197
  "gpt4all": "gpt4all",
197
198
  "elevenlabs": "elevenlabs",
198
199
  "vllm": "vllm",
200
+ "google-ai-studio": "google.generativeai",
199
201
  "langchain": "langchain",
200
202
  "llama_index": "llama_index",
201
203
  "haystack": "haystack",
@@ -273,6 +275,7 @@ def init(environment="default", application_name="default", tracer=None, otlp_en
273
275
  "gpt4all": GPT4AllInstrumentor(),
274
276
  "elevenlabs": ElevenLabsInstrumentor(),
275
277
  "vllm": VLLMInstrumentor(),
278
+ "google-ai-studio": GoogleAIStudioInstrumentor(),
276
279
  "langchain": LangChainInstrumentor(),
277
280
  "llama_index": LlamaIndexInstrumentor(),
278
281
  "haystack": HaystackInstrumentor(),
@@ -0,0 +1,56 @@
1
+ # pylint: disable=useless-return, bad-staticmethod-argument, disable=duplicate-code
2
+ """Initializer of Auto Instrumentation of Google AI Studio Functions"""
3
+
4
+ from typing import Collection
5
+ import importlib.metadata
6
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
7
+ from wrapt import wrap_function_wrapper
8
+
9
+ from openlit.instrumentation.google_ai_studio.google_ai_studio import (
10
+ generate
11
+ )
12
+
13
+ from openlit.instrumentation.google_ai_studio.async_google_ai_studio import (
14
+ async_generate
15
+ )
16
+
17
+ _instruments = ("google-generativeai >= 0.2.0",)
18
+
19
+ class GoogleAIStudioInstrumentor(BaseInstrumentor):
20
+ """
21
+ An instrumentor for google-generativeai's client library.
22
+ """
23
+
24
+ def instrumentation_dependencies(self) -> Collection[str]:
25
+ return _instruments
26
+
27
+ def _instrument(self, **kwargs):
28
+ application_name = kwargs.get("application_name", "default_application")
29
+ environment = kwargs.get("environment", "default_environment")
30
+ tracer = kwargs.get("tracer")
31
+ metrics = kwargs.get("metrics_dict")
32
+ pricing_info = kwargs.get("pricing_info", {})
33
+ trace_content = kwargs.get("trace_content", False)
34
+ disable_metrics = kwargs.get("disable_metrics")
35
+ version = importlib.metadata.version("ollama")
36
+
37
+ # sync generate
38
+ wrap_function_wrapper(
39
+ "google.generativeai.generative_models",
40
+ "GenerativeModel.generate_content",
41
+ generate("google_ai_studio.generate_content", version, environment, application_name,
42
+ tracer, pricing_info, trace_content, metrics, disable_metrics),
43
+ )
44
+
45
+ # async generate
46
+ wrap_function_wrapper(
47
+ "google.generativeai.generative_models",
48
+ "GenerativeModel.generate_content_async",
49
+ async_generate("google_ai_studio.generate_content", version, environment,
50
+ application_name, tracer, pricing_info, trace_content, metrics,
51
+ disable_metrics),
52
+ )
53
+
54
+ def _uninstrument(self, **kwargs):
55
+ # Proper uninstrumentation logic to revert patched methods
56
+ pass
@@ -0,0 +1,259 @@
1
+ # pylint: disable=duplicate-code, broad-exception-caught, too-many-statements, unused-argument, possibly-used-before-assignment, protected-access
2
+ """
3
+ Module for monitoring Ollama API calls.
4
+ """
5
+
6
+ import logging
7
+ from opentelemetry.trace import SpanKind, Status, StatusCode
8
+ from opentelemetry.sdk.resources import TELEMETRY_SDK_NAME
9
+ from openlit.__helpers import (
10
+ handle_exception,
11
+ get_chat_model_cost,
12
+ )
13
+ from openlit.semcov import SemanticConvetion
14
+
15
+ # Initialize logger for logging potential issues and operations
16
+ logger = logging.getLogger(__name__)
17
+
18
+ def async_generate(gen_ai_endpoint, version, environment, application_name,
19
+ tracer, pricing_info, trace_content, metrics, disable_metrics):
20
+ """
21
+ Generates a telemetry wrapper for chat to collect metrics.
22
+
23
+ Args:
24
+ gen_ai_endpoint: Endpoint identifier for logging and tracing.
25
+ version: Version of the monitoring package.
26
+ environment: Deployment environment (e.g., production, staging).
27
+ application_name: Name of the application using the Ollama API.
28
+ tracer: OpenTelemetry tracer for creating spans.
29
+ pricing_info: Information used for calculating the cost of Ollama usage.
30
+ trace_content: Flag indicating whether to trace the actual content.
31
+
32
+ Returns:
33
+ A function that wraps the chat method to add telemetry.
34
+ """
35
+
36
+ async def wrapper(wrapped, instance, args, kwargs):
37
+ """
38
+ Wraps the 'chat' API call to add telemetry.
39
+
40
+ This collects metrics such as execution time, cost, and token usage, and handles errors
41
+ gracefully, adding details to the trace for observability.
42
+
43
+ Args:
44
+ wrapped: The original 'chat' method to be wrapped.
45
+ instance: The instance of the class where the original method is defined.
46
+ args: Positional arguments for the 'chat' method.
47
+ kwargs: Keyword arguments for the 'chat' method.
48
+
49
+ Returns:
50
+ The response from the original 'chat' method.
51
+ """
52
+ # pylint: disable=no-else-return
53
+ if kwargs.get('stream', False) is True:
54
+ # Special handling for streaming response to accommodate the nature of data flow
55
+ async def stream_generator():
56
+ with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
57
+ # Placeholder for aggregating streaming response
58
+ llmresponse = ""
59
+
60
+ # Loop through streaming events capturing relevant details
61
+ async for chunk in await wrapped(*args, **kwargs):
62
+ # Collect message IDs and aggregated response from events
63
+ content = chunk.text
64
+ if content:
65
+ llmresponse += content
66
+
67
+ input_tokens = chunk.usage_metadata.prompt_token_count
68
+ output_tokens = chunk.usage_metadata.candidates_token_count
69
+ yield chunk
70
+
71
+ # Handling exception ensure observability without disrupting operation
72
+ try:
73
+ prompt = ""
74
+ for arg in args:
75
+ if isinstance(arg, str):
76
+ prompt = f"{prompt}{arg}\n"
77
+ elif isinstance(arg, list):
78
+ for subarg in arg:
79
+ prompt = f"{prompt}{subarg}\n"
80
+ if hasattr(instance, "_model_id"):
81
+ model = instance._model_id
82
+ if hasattr(instance, "_model_name"):
83
+ model = instance._model_name.replace("publishers/google/models/", "")
84
+
85
+ total_tokens = input_tokens + output_tokens
86
+ # Calculate cost of the operation
87
+ cost = get_chat_model_cost(kwargs.get("model", "gpt-3.5-turbo"),
88
+ pricing_info, input_tokens,
89
+ output_tokens)
90
+
91
+ # Set Span attributes
92
+ span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
93
+ span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
94
+ SemanticConvetion.GEN_AI_SYSTEM_GOOGLE_AI_STUDIO)
95
+ span.set_attribute(SemanticConvetion.GEN_AI_TYPE,
96
+ SemanticConvetion.GEN_AI_TYPE_CHAT)
97
+ span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
98
+ gen_ai_endpoint)
99
+ span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
100
+ environment)
101
+ span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
102
+ application_name)
103
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
104
+ model)
105
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
106
+ True)
107
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
108
+ input_tokens)
109
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COMPLETION_TOKENS,
110
+ output_tokens)
111
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
112
+ total_tokens)
113
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
114
+ cost)
115
+ if trace_content:
116
+ span.add_event(
117
+ name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
118
+ attributes={
119
+ SemanticConvetion.GEN_AI_CONTENT_PROMPT: prompt,
120
+ },
121
+ )
122
+ span.add_event(
123
+ name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
124
+ attributes={
125
+ SemanticConvetion.GEN_AI_CONTENT_COMPLETION: llmresponse,
126
+ },
127
+ )
128
+
129
+ span.set_status(Status(StatusCode.OK))
130
+
131
+ if disable_metrics is False:
132
+ attributes = {
133
+ TELEMETRY_SDK_NAME:
134
+ "openlit",
135
+ SemanticConvetion.GEN_AI_APPLICATION_NAME:
136
+ application_name,
137
+ SemanticConvetion.GEN_AI_SYSTEM:
138
+ SemanticConvetion.GEN_AI_SYSTEM_GOOGLE_AI_STUDIO,
139
+ SemanticConvetion.GEN_AI_ENVIRONMENT:
140
+ environment,
141
+ SemanticConvetion.GEN_AI_TYPE:
142
+ SemanticConvetion.GEN_AI_TYPE_CHAT,
143
+ SemanticConvetion.GEN_AI_REQUEST_MODEL:
144
+ model
145
+ }
146
+
147
+ metrics["genai_requests"].add(1, attributes)
148
+ metrics["genai_total_tokens"].add(
149
+ total_tokens, attributes
150
+ )
151
+ metrics["genai_completion_tokens"].add(output_tokens, attributes)
152
+ metrics["genai_prompt_tokens"].add(input_tokens, attributes)
153
+ metrics["genai_cost"].record(cost, attributes)
154
+
155
+ except Exception as e:
156
+ handle_exception(span, e)
157
+ logger.error("Error in trace creation: %s", e)
158
+
159
+ return stream_generator()
160
+ else:
161
+ # pylint: disable=line-too-long
162
+ with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
163
+ response = await wrapped(*args, **kwargs)
164
+
165
+ try:
166
+ prompt = ""
167
+ for arg in args:
168
+ if isinstance(arg, str):
169
+ prompt = f"{prompt}{arg}\n"
170
+ elif isinstance(arg, list):
171
+ for subarg in arg:
172
+ prompt = f"{prompt}{subarg}\n"
173
+ if hasattr(instance, "_model_id"):
174
+ model = instance._model_id
175
+ if hasattr(instance, "_model_name"):
176
+ model = instance._model_name.replace("publishers/google/models/", "")
177
+
178
+ # Set base span attribues
179
+ span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
180
+ span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
181
+ SemanticConvetion.GEN_AI_SYSTEM_GOOGLE_AI_STUDIO)
182
+ span.set_attribute(SemanticConvetion.GEN_AI_TYPE,
183
+ SemanticConvetion.GEN_AI_TYPE_CHAT)
184
+ span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
185
+ gen_ai_endpoint)
186
+ span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
187
+ environment)
188
+ span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
189
+ application_name)
190
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
191
+ model)
192
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
193
+ False)
194
+
195
+ if trace_content:
196
+ span.add_event(
197
+ name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
198
+ attributes={
199
+ SemanticConvetion.GEN_AI_CONTENT_PROMPT: prompt,
200
+ },
201
+ )
202
+ span.add_event(
203
+ name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
204
+ attributes={
205
+ SemanticConvetion.GEN_AI_CONTENT_COMPLETION: response.text,
206
+ },
207
+ )
208
+
209
+ prompt_tokens = response.usage_metadata.prompt_token_count
210
+ completion_tokens = response.usage_metadata.candidates_token_count
211
+ total_tokens = response.usage_metadata.total_token_count
212
+ # Calculate cost of the operation
213
+ cost = get_chat_model_cost(kwargs.get("model", "llama3"),
214
+ pricing_info, prompt_tokens, completion_tokens)
215
+
216
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
217
+ prompt_tokens)
218
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COMPLETION_TOKENS,
219
+ completion_tokens)
220
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
221
+ total_tokens)
222
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
223
+ cost)
224
+
225
+ span.set_status(Status(StatusCode.OK))
226
+
227
+ if disable_metrics is False:
228
+ attributes = {
229
+ TELEMETRY_SDK_NAME:
230
+ "openlit",
231
+ SemanticConvetion.GEN_AI_APPLICATION_NAME:
232
+ application_name,
233
+ SemanticConvetion.GEN_AI_SYSTEM:
234
+ SemanticConvetion.GEN_AI_SYSTEM_OLLAMA,
235
+ SemanticConvetion.GEN_AI_ENVIRONMENT:
236
+ environment,
237
+ SemanticConvetion.GEN_AI_TYPE:
238
+ SemanticConvetion.GEN_AI_TYPE_CHAT,
239
+ SemanticConvetion.GEN_AI_REQUEST_MODEL:
240
+ kwargs.get("model", "llama3")
241
+ }
242
+
243
+ metrics["genai_requests"].add(1, attributes)
244
+ metrics["genai_total_tokens"].add(total_tokens, attributes)
245
+ metrics["genai_completion_tokens"].add(completion_tokens, attributes)
246
+ metrics["genai_prompt_tokens"].add(prompt_tokens, attributes)
247
+ metrics["genai_cost"].record(cost, attributes)
248
+
249
+ # Return original response
250
+ return response
251
+
252
+ except Exception as e:
253
+ handle_exception(span, e)
254
+ logger.error("Error in trace creation: %s", e)
255
+
256
+ # Return original response
257
+ return response
258
+
259
+ return wrapper
@@ -0,0 +1,261 @@
1
+ # pylint: disable=duplicate-code, broad-exception-caught, too-many-statements, unused-argument, possibly-used-before-assignment, protected-access
2
+ """
3
+ Module for monitoring Ollama API calls.
4
+ """
5
+
6
+ import logging
7
+ from opentelemetry.trace import SpanKind, Status, StatusCode
8
+ from opentelemetry.sdk.resources import TELEMETRY_SDK_NAME
9
+ from openlit.__helpers import (
10
+ handle_exception,
11
+ get_chat_model_cost,
12
+ )
13
+ from openlit.semcov import SemanticConvetion
14
+
15
+ # Initialize logger for logging potential issues and operations
16
+ logger = logging.getLogger(__name__)
17
+
18
+ def generate(gen_ai_endpoint, version, environment, application_name,
19
+ tracer, pricing_info, trace_content, metrics, disable_metrics):
20
+ """
21
+ Generates a telemetry wrapper for chat to collect metrics.
22
+
23
+ Args:
24
+ gen_ai_endpoint: Endpoint identifier for logging and tracing.
25
+ version: Version of the monitoring package.
26
+ environment: Deployment environment (e.g., production, staging).
27
+ application_name: Name of the application using the Ollama API.
28
+ tracer: OpenTelemetry tracer for creating spans.
29
+ pricing_info: Information used for calculating the cost of Ollama usage.
30
+ trace_content: Flag indicating whether to trace the actual content.
31
+
32
+ Returns:
33
+ A function that wraps the chat method to add telemetry.
34
+ """
35
+
36
+ def wrapper(wrapped, instance, args, kwargs):
37
+ """
38
+ Wraps the 'chat' API call to add telemetry.
39
+
40
+ This collects metrics such as execution time, cost, and token usage, and handles errors
41
+ gracefully, adding details to the trace for observability.
42
+
43
+ Args:
44
+ wrapped: The original 'chat' method to be wrapped.
45
+ instance: The instance of the class where the original method is defined.
46
+ args: Positional arguments for the 'chat' method.
47
+ kwargs: Keyword arguments for the 'chat' method.
48
+
49
+ Returns:
50
+ The response from the original 'chat' method.
51
+ """
52
+ # pylint: disable=no-else-return
53
+ if kwargs.get("stream", False) is True:
54
+ # Special handling for streaming response to accommodate the nature of data flow
55
+ def stream_generator():
56
+ with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
57
+ # Placeholder for aggregating streaming response
58
+ llmresponse = ""
59
+
60
+ # Loop through streaming events capturing relevant details
61
+ for chunk in wrapped(*args, **kwargs):
62
+ # Collect message IDs and aggregated response from events
63
+ content = chunk.text
64
+ if content:
65
+ llmresponse += content
66
+
67
+ input_tokens = chunk.usage_metadata.prompt_token_count
68
+ output_tokens = chunk.usage_metadata.candidates_token_count
69
+ yield chunk
70
+
71
+ # Handling exception ensure observability without disrupting operation
72
+ try:
73
+ prompt = ""
74
+ for arg in args:
75
+ if isinstance(arg, str):
76
+ prompt = f"{prompt}{arg}\n"
77
+ elif isinstance(arg, list):
78
+ for subarg in arg:
79
+ prompt = f"{prompt}{subarg}\n"
80
+ if hasattr(instance, "_model_id"):
81
+ model = instance._model_id
82
+ if hasattr(instance, "_model_name"):
83
+ model = instance._model_name.replace("publishers/google/models/", "")
84
+
85
+ total_tokens = input_tokens + output_tokens
86
+ # Calculate cost of the operation
87
+ cost = get_chat_model_cost(kwargs.get("model", "gpt-3.5-turbo"),
88
+ pricing_info, input_tokens,
89
+ output_tokens)
90
+
91
+ # Set Span attributes
92
+ span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
93
+ span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
94
+ SemanticConvetion.GEN_AI_SYSTEM_GOOGLE_AI_STUDIO)
95
+ span.set_attribute(SemanticConvetion.GEN_AI_TYPE,
96
+ SemanticConvetion.GEN_AI_TYPE_CHAT)
97
+ span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
98
+ gen_ai_endpoint)
99
+ span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
100
+ environment)
101
+ span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
102
+ application_name)
103
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
104
+ model)
105
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
106
+ True)
107
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
108
+ input_tokens)
109
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COMPLETION_TOKENS,
110
+ output_tokens)
111
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
112
+ total_tokens)
113
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
114
+ cost)
115
+ if trace_content:
116
+ span.add_event(
117
+ name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
118
+ attributes={
119
+ SemanticConvetion.GEN_AI_CONTENT_PROMPT: prompt,
120
+ },
121
+ )
122
+ span.add_event(
123
+ name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
124
+ attributes={
125
+ SemanticConvetion.GEN_AI_CONTENT_COMPLETION: llmresponse,
126
+ },
127
+ )
128
+
129
+ span.set_status(Status(StatusCode.OK))
130
+
131
+ if disable_metrics is False:
132
+ attributes = {
133
+ TELEMETRY_SDK_NAME:
134
+ "openlit",
135
+ SemanticConvetion.GEN_AI_APPLICATION_NAME:
136
+ application_name,
137
+ SemanticConvetion.GEN_AI_SYSTEM:
138
+ SemanticConvetion.GEN_AI_SYSTEM_GOOGLE_AI_STUDIO,
139
+ SemanticConvetion.GEN_AI_ENVIRONMENT:
140
+ environment,
141
+ SemanticConvetion.GEN_AI_TYPE:
142
+ SemanticConvetion.GEN_AI_TYPE_CHAT,
143
+ SemanticConvetion.GEN_AI_REQUEST_MODEL:
144
+ model
145
+ }
146
+
147
+ metrics["genai_requests"].add(1, attributes)
148
+ metrics["genai_total_tokens"].add(
149
+ total_tokens, attributes
150
+ )
151
+ metrics["genai_completion_tokens"].add(output_tokens, attributes)
152
+ metrics["genai_prompt_tokens"].add(input_tokens, attributes)
153
+ metrics["genai_cost"].record(cost, attributes)
154
+
155
+ except Exception as e:
156
+ handle_exception(span, e)
157
+ logger.error("Error in trace creation: %s", e)
158
+
159
+ return stream_generator()
160
+ else:
161
+ # pylint: disable=line-too-long
162
+ with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
163
+ response = wrapped(*args, **kwargs)
164
+
165
+ # print(instance._system_instruction.__dict__["_pb"].parts[0].text)
166
+ try:
167
+ prompt = ""
168
+ for arg in args:
169
+ if isinstance(arg, str):
170
+ prompt = f"{prompt}{arg}\n"
171
+ elif isinstance(arg, list):
172
+ for subarg in arg:
173
+ prompt = f"{prompt}{subarg}\n"
174
+
175
+ if hasattr(instance, "_model_id"):
176
+ model = instance._model_id
177
+ if hasattr(instance, "_model_name"):
178
+ model = instance._model_name.replace("publishers/google/models/", "")
179
+
180
+ # Set base span attribues
181
+ span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
182
+ span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
183
+ SemanticConvetion.GEN_AI_SYSTEM_GOOGLE_AI_STUDIO)
184
+ span.set_attribute(SemanticConvetion.GEN_AI_TYPE,
185
+ SemanticConvetion.GEN_AI_TYPE_CHAT)
186
+ span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
187
+ gen_ai_endpoint)
188
+ span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
189
+ environment)
190
+ span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
191
+ application_name)
192
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
193
+ model)
194
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
195
+ False)
196
+
197
+ if trace_content:
198
+ span.add_event(
199
+ name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
200
+ attributes={
201
+ SemanticConvetion.GEN_AI_CONTENT_PROMPT: prompt,
202
+ },
203
+ )
204
+ span.add_event(
205
+ name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
206
+ attributes={
207
+ SemanticConvetion.GEN_AI_CONTENT_COMPLETION: response.text,
208
+ },
209
+ )
210
+
211
+ prompt_tokens = response.usage_metadata.prompt_token_count
212
+ completion_tokens = response.usage_metadata.candidates_token_count
213
+ total_tokens = response.usage_metadata.total_token_count
214
+ # Calculate cost of the operation
215
+ cost = get_chat_model_cost(kwargs.get("model", "llama3"),
216
+ pricing_info, prompt_tokens, completion_tokens)
217
+
218
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
219
+ prompt_tokens)
220
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COMPLETION_TOKENS,
221
+ completion_tokens)
222
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
223
+ total_tokens)
224
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
225
+ cost)
226
+
227
+ span.set_status(Status(StatusCode.OK))
228
+
229
+ if disable_metrics is False:
230
+ attributes = {
231
+ TELEMETRY_SDK_NAME:
232
+ "openlit",
233
+ SemanticConvetion.GEN_AI_APPLICATION_NAME:
234
+ application_name,
235
+ SemanticConvetion.GEN_AI_SYSTEM:
236
+ SemanticConvetion.GEN_AI_SYSTEM_OLLAMA,
237
+ SemanticConvetion.GEN_AI_ENVIRONMENT:
238
+ environment,
239
+ SemanticConvetion.GEN_AI_TYPE:
240
+ SemanticConvetion.GEN_AI_TYPE_CHAT,
241
+ SemanticConvetion.GEN_AI_REQUEST_MODEL:
242
+ kwargs.get("model", "llama3")
243
+ }
244
+
245
+ metrics["genai_requests"].add(1, attributes)
246
+ metrics["genai_total_tokens"].add(total_tokens, attributes)
247
+ metrics["genai_completion_tokens"].add(completion_tokens, attributes)
248
+ metrics["genai_prompt_tokens"].add(prompt_tokens, attributes)
249
+ metrics["genai_cost"].record(cost, attributes)
250
+
251
+ # Return original response
252
+ return response
253
+
254
+ except Exception as e:
255
+ handle_exception(span, e)
256
+ logger.error("Error in trace creation: %s", e)
257
+
258
+ # Return original response
259
+ return response
260
+
261
+ return wrapper
@@ -5,7 +5,14 @@ import importlib.metadata
5
5
  from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
6
6
  from wrapt import wrap_function_wrapper
7
7
 
8
- from openlit.instrumentation.langchain.langchain import general_wrap, hub, llm, allm
8
+ from openlit.instrumentation.langchain.langchain import (
9
+ general_wrap,
10
+ hub,
11
+ llm,
12
+ allm,
13
+ chat,
14
+ achat
15
+ )
9
16
 
10
17
  _instruments = ("langchain >= 0.1.20",)
11
18
 
@@ -52,6 +59,18 @@ WRAPPED_METHODS = [
52
59
  "endpoint": "langchain.llm",
53
60
  "wrapper": allm,
54
61
  },
62
+ {
63
+ "package": "langchain_core.language_models.chat_models",
64
+ "object": "BaseChatModel.invoke",
65
+ "endpoint": "langchain.chat_models",
66
+ "wrapper": chat,
67
+ },
68
+ {
69
+ "package": "langchain_core.language_models.chat_models",
70
+ "object": "BaseChatModel.ainvoke",
71
+ "endpoint": "langchain.chat_models",
72
+ "wrapper": achat,
73
+ },
55
74
  ]
56
75
 
57
76
  class LangChainInstrumentor(BaseInstrumentor):
@@ -66,6 +85,8 @@ class LangChainInstrumentor(BaseInstrumentor):
66
85
  tracer = kwargs.get("tracer")
67
86
  pricing_info = kwargs.get("pricing_info")
68
87
  trace_content = kwargs.get("trace_content")
88
+ metrics = kwargs.get("metrics_dict")
89
+ disable_metrics = kwargs.get("disable_metrics")
69
90
  version = importlib.metadata.version("langchain")
70
91
 
71
92
  for wrapped_method in WRAPPED_METHODS:
@@ -77,7 +98,7 @@ class LangChainInstrumentor(BaseInstrumentor):
77
98
  wrap_package,
78
99
  wrap_object,
79
100
  wrapper(gen_ai_endpoint, version, environment, application_name,
80
- tracer, pricing_info, trace_content),
101
+ tracer, pricing_info, trace_content, metrics, disable_metrics),
81
102
  )
82
103
 
83
104
  @staticmethod
@@ -1,4 +1,4 @@
1
- # pylint: disable=duplicate-code, broad-exception-caught, too-many-statements, unused-argument
1
+ # pylint: disable=duplicate-code, broad-exception-caught, too-many-statements, unused-argument, unused-import
2
2
  """
3
3
  Module for monitoring Langchain applications.
4
4
  """
@@ -6,14 +6,14 @@ Module for monitoring Langchain applications.
6
6
  import logging
7
7
  from opentelemetry.trace import SpanKind, Status, StatusCode
8
8
  from opentelemetry.sdk.resources import TELEMETRY_SDK_NAME
9
- from openlit.__helpers import handle_exception
9
+ from openlit.__helpers import handle_exception, get_chat_model_cost, general_tokens
10
10
  from openlit.semcov import SemanticConvetion
11
11
 
12
12
  # Initialize logger for logging potential issues and operations
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
15
  def general_wrap(gen_ai_endpoint, version, environment, application_name,
16
- tracer, pricing_info, trace_content):
16
+ tracer, pricing_info, trace_content, metrics, disable_metrics):
17
17
  """
18
18
  Creates a wrapper around a function call to trace and log its execution metrics.
19
19
 
@@ -86,7 +86,7 @@ def general_wrap(gen_ai_endpoint, version, environment, application_name,
86
86
  return wrapper
87
87
 
88
88
  def hub(gen_ai_endpoint, version, environment, application_name, tracer,
89
- pricing_info, trace_content):
89
+ pricing_info, trace_content, metrics, disable_metrics):
90
90
  """
91
91
  Creates a wrapper around Langchain hub operations for tracing and logging.
92
92
 
@@ -162,7 +162,7 @@ def hub(gen_ai_endpoint, version, environment, application_name, tracer,
162
162
 
163
163
 
164
164
  def allm(gen_ai_endpoint, version, environment, application_name,
165
- tracer, pricing_info, trace_content):
165
+ tracer, pricing_info, trace_content, metrics, disable_metrics):
166
166
  """
167
167
  Creates a wrapper around a function call to trace and log its execution metrics.
168
168
 
@@ -207,6 +207,16 @@ def allm(gen_ai_endpoint, version, environment, application_name,
207
207
  response = await wrapped(*args, **kwargs)
208
208
 
209
209
  try:
210
+ prompt = args[0] or ""
211
+ # input_tokens = general_tokens(prompt)
212
+ # output_tokens = general_tokens(response)
213
+
214
+ # # Calculate cost of the operation
215
+ # cost = get_chat_model_cost(
216
+ # str(getattr(instance, 'model')),
217
+ # pricing_info, input_tokens, output_tokens
218
+ # )
219
+
210
220
  span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
211
221
  span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
212
222
  SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN)
@@ -226,13 +236,56 @@ def allm(gen_ai_endpoint, version, environment, application_name,
226
236
  str(getattr(instance, 'top_k')))
227
237
  span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_P,
228
238
  str(getattr(instance, 'top_p')))
239
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
240
+ False)
241
+ # span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
242
+ # input_tokens)
243
+ # span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COMPLETION_TOKENS,
244
+ # output_tokens)
245
+ # span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
246
+ # input_tokens + output_tokens)
247
+ # span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
248
+ # cost)
229
249
  if trace_content:
230
- span.set_attribute(SemanticConvetion.GEN_AI_CONTENT_PROMPT,
231
- args[0])
232
- span.set_attribute(SemanticConvetion.GEN_AI_CONTENT_COMPLETION,
233
- response)
250
+ span.add_event(
251
+ name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
252
+ attributes={
253
+ SemanticConvetion.GEN_AI_CONTENT_PROMPT: prompt,
254
+ },
255
+ )
256
+ span.add_event(
257
+ name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
258
+ attributes={
259
+ SemanticConvetion.GEN_AI_CONTENT_COMPLETION: response,
260
+ },
261
+ )
262
+
234
263
  span.set_status(Status(StatusCode.OK))
235
264
 
265
+ # if disable_metrics is False:
266
+ # attributes = {
267
+ # TELEMETRY_SDK_NAME:
268
+ # "openlit",
269
+ # SemanticConvetion.GEN_AI_APPLICATION_NAME:
270
+ # application_name,
271
+ # SemanticConvetion.GEN_AI_SYSTEM:
272
+ # SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN,
273
+ # SemanticConvetion.GEN_AI_ENVIRONMENT:
274
+ # environment,
275
+ # SemanticConvetion.GEN_AI_TYPE:
276
+ # SemanticConvetion.GEN_AI_TYPE_CHAT,
277
+ # SemanticConvetion.GEN_AI_REQUEST_MODEL:
278
+ # str(getattr(instance, 'model'))
279
+ # }
280
+
281
+ # metrics["genai_requests"].add(1, attributes)
282
+ # metrics["genai_total_tokens"].add(
283
+ # input_tokens + output_tokens, attributes
284
+ # )
285
+ # metrics["genai_completion_tokens"].add(output_tokens, attributes)
286
+ # metrics["genai_prompt_tokens"].add(input_tokens, attributes)
287
+ # metrics["genai_cost"].record(cost, attributes)
288
+
236
289
  # Return original response
237
290
  return response
238
291
 
@@ -246,7 +299,7 @@ def allm(gen_ai_endpoint, version, environment, application_name,
246
299
  return wrapper
247
300
 
248
301
  def llm(gen_ai_endpoint, version, environment, application_name,
249
- tracer, pricing_info, trace_content):
302
+ tracer, pricing_info, trace_content, metrics, disable_metrics):
250
303
  """
251
304
  Creates a wrapper around a function call to trace and log its execution metrics.
252
305
 
@@ -291,6 +344,16 @@ def llm(gen_ai_endpoint, version, environment, application_name,
291
344
  response = wrapped(*args, **kwargs)
292
345
 
293
346
  try:
347
+ prompt = args[0] or ""
348
+ # input_tokens = general_tokens(prompt)
349
+ # output_tokens = general_tokens(response)
350
+
351
+ # # Calculate cost of the operation
352
+ # cost = get_chat_model_cost(
353
+ # str(getattr(instance, 'model')),
354
+ # pricing_info, input_tokens, output_tokens
355
+ # )
356
+
294
357
  span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
295
358
  span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
296
359
  SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN)
@@ -310,13 +373,332 @@ def llm(gen_ai_endpoint, version, environment, application_name,
310
373
  str(getattr(instance, 'top_k')))
311
374
  span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_P,
312
375
  str(getattr(instance, 'top_p')))
376
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
377
+ False)
378
+ # span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
379
+ # input_tokens)
380
+ # span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COMPLETION_TOKENS,
381
+ # output_tokens)
382
+ # span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
383
+ # input_tokens + output_tokens)
384
+ # span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
385
+ # cost)
386
+ if trace_content:
387
+ span.add_event(
388
+ name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
389
+ attributes={
390
+ SemanticConvetion.GEN_AI_CONTENT_PROMPT: prompt,
391
+ },
392
+ )
393
+ span.add_event(
394
+ name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
395
+ attributes={
396
+ SemanticConvetion.GEN_AI_CONTENT_COMPLETION: response,
397
+ },
398
+ )
399
+
400
+ span.set_status(Status(StatusCode.OK))
401
+
402
+ # if disable_metrics is False:
403
+ # attributes = {
404
+ # TELEMETRY_SDK_NAME:
405
+ # "openlit",
406
+ # SemanticConvetion.GEN_AI_APPLICATION_NAME:
407
+ # application_name,
408
+ # SemanticConvetion.GEN_AI_SYSTEM:
409
+ # SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN,
410
+ # SemanticConvetion.GEN_AI_ENVIRONMENT:
411
+ # environment,
412
+ # SemanticConvetion.GEN_AI_TYPE:
413
+ # SemanticConvetion.GEN_AI_TYPE_CHAT,
414
+ # SemanticConvetion.GEN_AI_REQUEST_MODEL:
415
+ # str(getattr(instance, 'model'))
416
+ # }
417
+
418
+ # metrics["genai_requests"].add(1, attributes)
419
+ # metrics["genai_total_tokens"].add(
420
+ # input_tokens + output_tokens, attributes
421
+ # )
422
+ # metrics["genai_completion_tokens"].add(output_tokens, attributes)
423
+ # metrics["genai_prompt_tokens"].add(input_tokens, attributes)
424
+ # metrics["genai_cost"].record(cost, attributes)
425
+
426
+ # Return original response
427
+ return response
428
+
429
+ except Exception as e:
430
+ handle_exception(span, e)
431
+ logger.error("Error in trace creation: %s", e)
432
+
433
+ # Return original response
434
+ return response
435
+
436
+ return wrapper
437
+
438
+ def chat(gen_ai_endpoint, version, environment, application_name,
439
+ tracer, pricing_info, trace_content, metrics, disable_metrics):
440
+ """
441
+ Creates a wrapper around a function call to trace and log its execution metrics.
442
+
443
+ This function wraps any given function to measure its execution time,
444
+ log its operation, and trace its execution using OpenTelemetry.
445
+
446
+ Parameters:
447
+ - gen_ai_endpoint (str): A descriptor or name for the endpoint being traced.
448
+ - version (str): The version of the Langchain application.
449
+ - environment (str): The deployment environment (e.g., 'production', 'development').
450
+ - application_name (str): Name of the Langchain application.
451
+ - tracer (opentelemetry.trace.Tracer): The tracer object used for OpenTelemetry tracing.
452
+ - pricing_info (dict): Information about the pricing for internal metrics (currently not used).
453
+ - trace_content (bool): Flag indicating whether to trace the content of the response.
454
+
455
+ Returns:
456
+ - function: A higher-order function that takes a function 'wrapped' and returns
457
+ a new function that wraps 'wrapped' with additional tracing and logging.
458
+ """
459
+
460
+ def wrapper(wrapped, instance, args, kwargs):
461
+ """
462
+ An inner wrapper function that executes the wrapped function, measures execution
463
+ time, and records trace data using OpenTelemetry.
464
+
465
+ Parameters:
466
+ - wrapped (Callable): The original function that this wrapper will execute.
467
+ - instance (object): The instance to which the wrapped function belongs. This
468
+ is used for instance methods. For static and classmethods,
469
+ this may be None.
470
+ - args (tuple): Positional arguments passed to the wrapped function.
471
+ - kwargs (dict): Keyword arguments passed to the wrapped function.
472
+
473
+ Returns:
474
+ - The result of the wrapped function call.
475
+
476
+ The wrapper initiates a span with the provided tracer, sets various attributes
477
+ on the span based on the function's execution and response, and ensures
478
+ errors are handled and logged appropriately.
479
+ """
480
+ with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
481
+ response = wrapped(*args, **kwargs)
482
+
483
+ try:
484
+ input_tokens = response.response_metadata["prompt_eval_count"] or 0
485
+ output_tokens = response.response_metadata["eval_count"] or 0
486
+
487
+ # Calculate cost of the operation
488
+ cost = get_chat_model_cost(
489
+ str(getattr(instance, 'model')),
490
+ pricing_info, input_tokens, output_tokens
491
+ )
492
+
493
+ span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
494
+ span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
495
+ SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN)
496
+ span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
497
+ gen_ai_endpoint)
498
+ span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
499
+ environment)
500
+ span.set_attribute(SemanticConvetion.GEN_AI_TYPE,
501
+ SemanticConvetion.GEN_AI_TYPE_CHAT)
502
+ span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
503
+ application_name)
504
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
505
+ str(getattr(instance, 'model')))
506
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TEMPERATURE,
507
+ str(getattr(instance, 'temperature')))
508
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_K,
509
+ str(getattr(instance, 'top_k')))
510
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_P,
511
+ str(getattr(instance, 'top_p')))
512
+ span.set_attribute(SemanticConvetion.GEN_AI_RESPONSE_FINISH_REASON,
513
+ [response.response_metadata["done_reason"]])
514
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
515
+ False)
516
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
517
+ input_tokens)
518
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COMPLETION_TOKENS,
519
+ output_tokens)
520
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
521
+ input_tokens + output_tokens)
522
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
523
+ cost)
313
524
  if trace_content:
314
- span.set_attribute(SemanticConvetion.GEN_AI_CONTENT_PROMPT,
315
- args[0])
316
- span.set_attribute(SemanticConvetion.GEN_AI_CONTENT_COMPLETION,
317
- response)
525
+ span.add_event(
526
+ name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
527
+ attributes={
528
+ SemanticConvetion.GEN_AI_CONTENT_PROMPT: args[0],
529
+ },
530
+ )
531
+ span.add_event(
532
+ name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
533
+ attributes={
534
+ SemanticConvetion.GEN_AI_CONTENT_COMPLETION: response.content,
535
+ },
536
+ )
537
+
318
538
  span.set_status(Status(StatusCode.OK))
319
539
 
540
+ if disable_metrics is False:
541
+ attributes = {
542
+ TELEMETRY_SDK_NAME:
543
+ "openlit",
544
+ SemanticConvetion.GEN_AI_APPLICATION_NAME:
545
+ application_name,
546
+ SemanticConvetion.GEN_AI_SYSTEM:
547
+ SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN,
548
+ SemanticConvetion.GEN_AI_ENVIRONMENT:
549
+ environment,
550
+ SemanticConvetion.GEN_AI_TYPE:
551
+ SemanticConvetion.GEN_AI_TYPE_CHAT,
552
+ SemanticConvetion.GEN_AI_REQUEST_MODEL:
553
+ str(getattr(instance, 'model'))
554
+ }
555
+
556
+ metrics["genai_requests"].add(1, attributes)
557
+ metrics["genai_total_tokens"].add(
558
+ input_tokens + output_tokens, attributes
559
+ )
560
+ metrics["genai_completion_tokens"].add(output_tokens, attributes)
561
+ metrics["genai_prompt_tokens"].add(input_tokens, attributes)
562
+ metrics["genai_cost"].record(cost, attributes)
563
+
564
+ # Return original response
565
+ return response
566
+
567
+ except Exception as e:
568
+ handle_exception(span, e)
569
+ logger.error("Error in trace creation: %s", e)
570
+
571
+ # Return original response
572
+ return response
573
+
574
+ return wrapper
575
+
576
+ def achat(gen_ai_endpoint, version, environment, application_name,
577
+ tracer, pricing_info, trace_content, metrics, disable_metrics):
578
+ """
579
+ Creates a wrapper around a function call to trace and log its execution metrics.
580
+
581
+ This function wraps any given function to measure its execution time,
582
+ log its operation, and trace its execution using OpenTelemetry.
583
+
584
+ Parameters:
585
+ - gen_ai_endpoint (str): A descriptor or name for the endpoint being traced.
586
+ - version (str): The version of the Langchain application.
587
+ - environment (str): The deployment environment (e.g., 'production', 'development').
588
+ - application_name (str): Name of the Langchain application.
589
+ - tracer (opentelemetry.trace.Tracer): The tracer object used for OpenTelemetry tracing.
590
+ - pricing_info (dict): Information about the pricing for internal metrics (currently not used).
591
+ - trace_content (bool): Flag indicating whether to trace the content of the response.
592
+
593
+ Returns:
594
+ - function: A higher-order function that takes a function 'wrapped' and returns
595
+ a new function that wraps 'wrapped' with additional tracing and logging.
596
+ """
597
+
598
+ async def wrapper(wrapped, instance, args, kwargs):
599
+ """
600
+ An inner wrapper function that executes the wrapped function, measures execution
601
+ time, and records trace data using OpenTelemetry.
602
+
603
+ Parameters:
604
+ - wrapped (Callable): The original function that this wrapper will execute.
605
+ - instance (object): The instance to which the wrapped function belongs. This
606
+ is used for instance methods. For static and classmethods,
607
+ this may be None.
608
+ - args (tuple): Positional arguments passed to the wrapped function.
609
+ - kwargs (dict): Keyword arguments passed to the wrapped function.
610
+
611
+ Returns:
612
+ - The result of the wrapped function call.
613
+
614
+ The wrapper initiates a span with the provided tracer, sets various attributes
615
+ on the span based on the function's execution and response, and ensures
616
+ errors are handled and logged appropriately.
617
+ """
618
+ with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
619
+ response = await wrapped(*args, **kwargs)
620
+
621
+ try:
622
+ input_tokens = response.response_metadata["prompt_eval_count"] or 0
623
+ output_tokens = response.response_metadata["eval_count"] or 0
624
+
625
+ # Calculate cost of the operation
626
+ cost = get_chat_model_cost(
627
+ str(getattr(instance, 'model')),
628
+ pricing_info, input_tokens, output_tokens
629
+ )
630
+
631
+ span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
632
+ span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
633
+ SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN)
634
+ span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
635
+ gen_ai_endpoint)
636
+ span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
637
+ environment)
638
+ span.set_attribute(SemanticConvetion.GEN_AI_TYPE,
639
+ SemanticConvetion.GEN_AI_TYPE_CHAT)
640
+ span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
641
+ application_name)
642
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
643
+ str(getattr(instance, 'model')))
644
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TEMPERATURE,
645
+ str(getattr(instance, 'temperature')))
646
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_K,
647
+ str(getattr(instance, 'top_k')))
648
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_P,
649
+ str(getattr(instance, 'top_p')))
650
+ span.set_attribute(SemanticConvetion.GEN_AI_RESPONSE_FINISH_REASON,
651
+ [response.response_metadata["done_reason"]])
652
+ span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
653
+ False)
654
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
655
+ input_tokens)
656
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COMPLETION_TOKENS,
657
+ output_tokens)
658
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
659
+ input_tokens + output_tokens)
660
+ span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
661
+ cost)
662
+ if trace_content:
663
+ span.add_event(
664
+ name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
665
+ attributes={
666
+ SemanticConvetion.GEN_AI_CONTENT_PROMPT: args[0],
667
+ },
668
+ )
669
+ span.add_event(
670
+ name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
671
+ attributes={
672
+ SemanticConvetion.GEN_AI_CONTENT_COMPLETION: response.content,
673
+ },
674
+ )
675
+
676
+ span.set_status(Status(StatusCode.OK))
677
+
678
+ if disable_metrics is False:
679
+ attributes = {
680
+ TELEMETRY_SDK_NAME:
681
+ "openlit",
682
+ SemanticConvetion.GEN_AI_APPLICATION_NAME:
683
+ application_name,
684
+ SemanticConvetion.GEN_AI_SYSTEM:
685
+ SemanticConvetion.GEN_AI_SYSTEM_LANGCHAIN,
686
+ SemanticConvetion.GEN_AI_ENVIRONMENT:
687
+ environment,
688
+ SemanticConvetion.GEN_AI_TYPE:
689
+ SemanticConvetion.GEN_AI_TYPE_CHAT,
690
+ SemanticConvetion.GEN_AI_REQUEST_MODEL:
691
+ str(getattr(instance, 'model'))
692
+ }
693
+
694
+ metrics["genai_requests"].add(1, attributes)
695
+ metrics["genai_total_tokens"].add(
696
+ input_tokens + output_tokens, attributes
697
+ )
698
+ metrics["genai_completion_tokens"].add(output_tokens, attributes)
699
+ metrics["genai_prompt_tokens"].add(input_tokens, attributes)
700
+ metrics["genai_cost"].record(cost, attributes)
701
+
320
702
  # Return original response
321
703
  return response
322
704
 
@@ -102,6 +102,7 @@ class SemanticConvetion:
102
102
  GEN_AI_SYSTEM_GPT4ALL = "gpt4all"
103
103
  GEN_AI_SYSTEM_ELEVENLABS = "elevenlabs"
104
104
  GEN_AI_SYSTEM_VLLM = "vLLM"
105
+ GEN_AI_SYSTEM_GOOGLE_AI_STUDIO = "google-ai-studio"
105
106
  GEN_AI_SYSTEM_LANGCHAIN = "langchain"
106
107
  GEN_AI_SYSTEM_LLAMAINDEX = "llama_index"
107
108
  GEN_AI_SYSTEM_HAYSTACK = "haystack"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openlit
3
- Version: 1.18.2
3
+ Version: 1.20.0
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
@@ -60,15 +60,17 @@ This project adheres to the [Semantic Conventions](https://github.com/open-telem
60
60
  | [✅ Ollama](https://docs.openlit.io/latest/integrations/ollama) | [✅ Pinecone](https://docs.openlit.io/latest/integrations/pinecone) | [✅ LiteLLM](https://docs.openlit.io/latest/integrations/litellm) | |
61
61
  | [✅ Anthropic](https://docs.openlit.io/latest/integrations/anthropic) | [✅ Qdrant](https://docs.openlit.io/latest/integrations/qdrant) | [✅ LlamaIndex](https://docs.openlit.io/latest/integrations/llama-index) | |
62
62
  | [✅ GPT4All](https://docs.openlit.io/latest/integrations/gpt4all) | [✅ Milvus](https://docs.openlit.io/latest/integrations/milvus) | [✅ Haystack](https://docs.openlit.io/latest/integrations/haystack) | |
63
- | [✅ Cohere](https://docs.openlit.io/latest/integrations/cohere) | | [✅ EmbedChain](https://docs.openlit.io/latest/integrations/embedchain) | |
64
- | [✅ Mistral](https://docs.openlit.io/latest/integrations/mistral) | | | |
65
- | [✅ Azure OpenAI](https://docs.openlit.io/latest/integrations/azure-openai) | | | |
66
- | [✅ HuggingFace Transformers](https://docs.openlit.io/latest/integrations/huggingface) | | | |
67
- | [✅ Amazon Bedrock](https://docs.openlit.io/latest/integrations/bedrock) | | | |
63
+ | [✅ Cohere](https://docs.openlit.io/latest/integrations/cohere) | | [✅ EmbedChain](https://docs.openlit.io/latest/integrations/embedchain) | |
64
+ | [✅ Mistral](https://docs.openlit.io/latest/integrations/mistral) | | [✅ Guardrails](https://docs.openlit.io/latest/integrations/guardrails) | |
65
+ | [✅ Azure OpenAI](https://docs.openlit.io/latest/integrations/azure-openai) | | | |
66
+ | [✅ HuggingFace Transformers](https://docs.openlit.io/latest/integrations/huggingface) | | | |
67
+ | [✅ Amazon Bedrock](https://docs.openlit.io/latest/integrations/bedrock) | | | |
68
68
  | [✅ Vertex AI](https://docs.openlit.io/latest/integrations/vertexai) | | | |
69
69
  | [✅ Groq](https://docs.openlit.io/latest/integrations/groq) | | | |
70
70
  | [✅ ElevenLabs](https://docs.openlit.io/latest/integrations/elevenlabs) | | | |
71
71
  | [✅ vLLM](https://docs.openlit.io/latest/integrations/vllm) | | | |
72
+ | [✅ OLA Krutrim](https://docs.openlit.io/latest/integrations/krutrim) | | | |
73
+ | [✅ Google AI Studio](https://docs.openlit.io/latest/integrations/google-ai-studio) | | | |
72
74
 
73
75
  ## Supported Destinations
74
76
  - [✅ OpenTelemetry Collector](https://docs.openlit.io/latest/connections/otelcol)
@@ -77,8 +79,10 @@ This project adheres to the [Semantic Conventions](https://github.com/open-telem
77
79
  - [✅ Grafana Cloud](https://docs.openlit.io/latest/connections/grafanacloud)
78
80
  - [✅ New Relic](https://docs.openlit.io/latest/connections/new-relic)
79
81
  - [✅ Elastic](https://docs.openlit.io/latest/connections/elastic)
82
+ - [✅ HyperDX](https://docs.openlit.io/latest/connections/hyperdx)
80
83
  - [✅ DataDog](https://docs.openlit.io/latest/connections/datadog)
81
84
  - [✅ SigNoz](https://docs.openlit.io/latest/connections/signoz)
85
+ - [✅ OneUptime](https://docs.openlit.io/latest/connections/oneuptime)
82
86
  - [✅ Dynatrace](https://docs.openlit.io/latest/connections/dynatrace)
83
87
  - [✅ OpenObserve](https://docs.openlit.io/latest/connections/openobserve)
84
88
  - [✅ Highlight.io](https://docs.openlit.io/latest/connections/highlight)
@@ -1,5 +1,5 @@
1
1
  openlit/__helpers.py,sha256=lrn4PBs9owDudiCY2NBoVbAi7AU_HtUpyOj0oqPBsPY,5545
2
- openlit/__init__.py,sha256=LfU5w-D62u5pY70DdNbv5_DGtqeL1Yb0TlY-l0NAn8I,15103
2
+ openlit/__init__.py,sha256=fqeMFg76VYEX4VdxTn76Fh-0kbrN5_Sk8ahlm7Xlp9k,15296
3
3
  openlit/instrumentation/anthropic/__init__.py,sha256=oaU53BOPyfUKbEzYvLr1DPymDluurSnwo4Hernf2XdU,1955
4
4
  openlit/instrumentation/anthropic/anthropic.py,sha256=y7CEGhKOGHWt8G_5Phr4qPJTfPGRJIAr9Yk6nM3CcvM,16775
5
5
  openlit/instrumentation/anthropic/async_anthropic.py,sha256=Zz1KRKIG9wGn0quOoLvjORC-49IvHQpJ6GBdB-4PfCQ,16816
@@ -14,6 +14,9 @@ openlit/instrumentation/elevenlabs/async_elevenlabs.py,sha256=yMYACh95SFr5EYklKn
14
14
  openlit/instrumentation/elevenlabs/elevenlabs.py,sha256=mFnD7sgT47OxaXJz0Vc1nrNjXEpcGQDj5run3gA48Lw,6089
15
15
  openlit/instrumentation/embedchain/__init__.py,sha256=8TYk1OEbz46yF19dr-gB_x80VZMagU3kJ8-QihPXTeA,1929
16
16
  openlit/instrumentation/embedchain/embedchain.py,sha256=SLlr7qieT3kp4M6OYSRy8FaVCXQ2t3oPyIiE99ioNE4,7892
17
+ openlit/instrumentation/google_ai_studio/__init__.py,sha256=vG4WzaavOiiwI3r5stDMotM5TSBxdcxQhC3W4XjJIG8,2146
18
+ openlit/instrumentation/google_ai_studio/async_google_ai_studio.py,sha256=cEy5FNu92gvdmsKKVDrUcY5LpY4hHzWwJl9yezxN-Zo,13404
19
+ openlit/instrumentation/google_ai_studio/google_ai_studio.py,sha256=mLABvD756GEgVfAjiYsfLYWATT1AxPuWDuElwpX4voo,13453
17
20
  openlit/instrumentation/gpt4all/__init__.py,sha256=-59CP2B3-HGZJ_vC-fI9Dt-0BuQXRhSCWCjnaGeU15Q,1802
18
21
  openlit/instrumentation/gpt4all/gpt4all.py,sha256=dbxqZeuTrv_y6wyDOIEmC8-Dc4iCGgLpj3l5JiodLMI,18787
19
22
  openlit/instrumentation/gpu/__init__.py,sha256=Dj2MLar0DB20-t6W3pfR-3jfR_mwg4SYwhzIrH_n9sU,5596
@@ -22,8 +25,8 @@ openlit/instrumentation/groq/async_groq.py,sha256=myob-d9V66YiNmkFd9rtmMaXjlLiSM
22
25
  openlit/instrumentation/groq/groq.py,sha256=m4gFPbYzjUUIgjXZ0Alu2Zy1HcO5takCFA2XFnkcGVo,19975
23
26
  openlit/instrumentation/haystack/__init__.py,sha256=QK6XxxZUHX8vMv2Crk7rNBOc64iOOBLhJGL_lPlAZ8s,1758
24
27
  openlit/instrumentation/haystack/haystack.py,sha256=oQIZiDhdp3gnJnhYQ1OouJMc9YT0pQ-_31cmNuopa68,3891
25
- openlit/instrumentation/langchain/__init__.py,sha256=19C7YGSF-6u5VlvKkThNS4zZqvxw-fQfRsKufZ9onfk,2881
26
- openlit/instrumentation/langchain/langchain.py,sha256=xGiRb3Z_fY1PU09hfSBFt6ipfYJIQrwFFm5HEDaawOY,16243
28
+ openlit/instrumentation/langchain/__init__.py,sha256=0AI2Dnqw81IcJw3jM--gGkv_HRh2GtosOGJjvOpw7Zk,3431
29
+ openlit/instrumentation/langchain/langchain.py,sha256=7K-m35sS3yTu9IklRo6n9LCcymeg6OyIYKrMzMG_uDQ,35730
27
30
  openlit/instrumentation/llamaindex/__init__.py,sha256=vPtK65G6b-TwJERowVRUVl7f_nBSlFdwPBtpg8dOGos,1977
28
31
  openlit/instrumentation/llamaindex/llamaindex.py,sha256=uiIigbwhonSbJWA7LpgOVI1R4kxxPODS1K5wyHIQ4hM,4048
29
32
  openlit/instrumentation/milvus/__init__.py,sha256=qi1yfmMrvkDtnrN_6toW8qC9BRL78bq7ayWpObJ8Bq4,2961
@@ -52,8 +55,8 @@ openlit/instrumentation/vllm/__init__.py,sha256=OVWalQ1dXvip1DUsjUGaHX4J-2FrSp-T
52
55
  openlit/instrumentation/vllm/vllm.py,sha256=lDzM7F5pgxvh8nKL0dcKB4TD0Mc9wXOWeXOsOGN7Wd8,6527
53
56
  openlit/otel/metrics.py,sha256=O7NoaDz0bY19mqpE4-0PcKwEe-B-iJFRgOCaanAuZAc,4291
54
57
  openlit/otel/tracing.py,sha256=vL1ifMbARPBpqK--yXYsCM6y5dSu5LFIKqkhZXtYmUc,3712
55
- openlit/semcov/__init__.py,sha256=EvoNOKtc7UKwLZ3Gp0-B1zwmeTcAIbx8O7wvAw8wXP4,7498
56
- openlit-1.18.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
57
- openlit-1.18.2.dist-info/METADATA,sha256=eD-PsH7RbUA7EOICMoUHzO_f1g_Sa0b6UcNvHnBwY-8,14347
58
- openlit-1.18.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
59
- openlit-1.18.2.dist-info/RECORD,,
58
+ openlit/semcov/__init__.py,sha256=56daxsJtWFqp87TTT3R5OxSeMH7eOZqb4k-7AELirTI,7554
59
+ openlit-1.20.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
60
+ openlit-1.20.0.dist-info/METADATA,sha256=DvK9Dt8ZvBBvdPc_Fdbi7HxkCu9Kp-Z4IkzCOuynkg4,14934
61
+ openlit-1.20.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
62
+ openlit-1.20.0.dist-info/RECORD,,