openlit 1.34.30__py3-none-any.whl → 1.34.31__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openlit/__helpers.py +235 -86
- openlit/__init__.py +16 -13
- openlit/_instrumentors.py +2 -1
- openlit/evals/all.py +50 -21
- openlit/evals/bias_detection.py +47 -20
- openlit/evals/hallucination.py +53 -22
- openlit/evals/toxicity.py +50 -21
- openlit/evals/utils.py +54 -30
- openlit/guard/all.py +61 -19
- openlit/guard/prompt_injection.py +34 -14
- openlit/guard/restrict_topic.py +46 -15
- openlit/guard/sensitive_topic.py +34 -14
- openlit/guard/utils.py +58 -22
- openlit/instrumentation/ag2/__init__.py +24 -8
- openlit/instrumentation/ag2/ag2.py +34 -13
- openlit/instrumentation/ag2/async_ag2.py +34 -13
- openlit/instrumentation/ag2/utils.py +133 -30
- openlit/instrumentation/ai21/__init__.py +43 -14
- openlit/instrumentation/ai21/ai21.py +47 -21
- openlit/instrumentation/ai21/async_ai21.py +47 -21
- openlit/instrumentation/ai21/utils.py +299 -78
- openlit/instrumentation/anthropic/__init__.py +21 -4
- openlit/instrumentation/anthropic/anthropic.py +28 -17
- openlit/instrumentation/anthropic/async_anthropic.py +28 -17
- openlit/instrumentation/anthropic/utils.py +145 -35
- openlit/instrumentation/assemblyai/__init__.py +11 -2
- openlit/instrumentation/assemblyai/assemblyai.py +15 -4
- openlit/instrumentation/assemblyai/utils.py +120 -25
- openlit/instrumentation/astra/__init__.py +43 -10
- openlit/instrumentation/astra/astra.py +28 -5
- openlit/instrumentation/astra/async_astra.py +28 -5
- openlit/instrumentation/astra/utils.py +151 -55
- openlit/instrumentation/azure_ai_inference/__init__.py +43 -10
- openlit/instrumentation/azure_ai_inference/async_azure_ai_inference.py +53 -21
- openlit/instrumentation/azure_ai_inference/azure_ai_inference.py +53 -21
- openlit/instrumentation/azure_ai_inference/utils.py +307 -83
- openlit/instrumentation/bedrock/__init__.py +21 -4
- openlit/instrumentation/bedrock/bedrock.py +63 -25
- openlit/instrumentation/bedrock/utils.py +139 -30
- openlit/instrumentation/chroma/__init__.py +89 -16
- openlit/instrumentation/chroma/chroma.py +28 -6
- openlit/instrumentation/chroma/utils.py +167 -51
- openlit/instrumentation/cohere/__init__.py +63 -18
- openlit/instrumentation/cohere/async_cohere.py +63 -24
- openlit/instrumentation/cohere/cohere.py +63 -24
- openlit/instrumentation/cohere/utils.py +286 -73
- openlit/instrumentation/controlflow/__init__.py +35 -9
- openlit/instrumentation/controlflow/controlflow.py +66 -33
- openlit/instrumentation/crawl4ai/__init__.py +25 -10
- openlit/instrumentation/crawl4ai/async_crawl4ai.py +78 -31
- openlit/instrumentation/crawl4ai/crawl4ai.py +78 -31
- openlit/instrumentation/crewai/__init__.py +40 -15
- openlit/instrumentation/crewai/async_crewai.py +32 -7
- openlit/instrumentation/crewai/crewai.py +32 -7
- openlit/instrumentation/crewai/utils.py +159 -56
- openlit/instrumentation/dynamiq/__init__.py +46 -12
- openlit/instrumentation/dynamiq/dynamiq.py +74 -33
- openlit/instrumentation/elevenlabs/__init__.py +23 -4
- openlit/instrumentation/elevenlabs/async_elevenlabs.py +16 -4
- openlit/instrumentation/elevenlabs/elevenlabs.py +16 -4
- openlit/instrumentation/elevenlabs/utils.py +128 -25
- openlit/instrumentation/embedchain/__init__.py +11 -2
- openlit/instrumentation/embedchain/embedchain.py +68 -35
- openlit/instrumentation/firecrawl/__init__.py +24 -7
- openlit/instrumentation/firecrawl/firecrawl.py +46 -20
- openlit/instrumentation/google_ai_studio/__init__.py +45 -10
- openlit/instrumentation/google_ai_studio/async_google_ai_studio.py +67 -44
- openlit/instrumentation/google_ai_studio/google_ai_studio.py +67 -44
- openlit/instrumentation/google_ai_studio/utils.py +180 -67
- openlit/instrumentation/gpt4all/__init__.py +22 -7
- openlit/instrumentation/gpt4all/gpt4all.py +67 -29
- openlit/instrumentation/gpt4all/utils.py +285 -61
- openlit/instrumentation/gpu/__init__.py +128 -47
- openlit/instrumentation/groq/__init__.py +21 -4
- openlit/instrumentation/groq/async_groq.py +33 -21
- openlit/instrumentation/groq/groq.py +33 -21
- openlit/instrumentation/groq/utils.py +192 -55
- openlit/instrumentation/haystack/__init__.py +70 -24
- openlit/instrumentation/haystack/async_haystack.py +28 -6
- openlit/instrumentation/haystack/haystack.py +28 -6
- openlit/instrumentation/haystack/utils.py +196 -74
- openlit/instrumentation/julep/__init__.py +69 -19
- openlit/instrumentation/julep/async_julep.py +53 -27
- openlit/instrumentation/julep/julep.py +53 -28
- openlit/instrumentation/langchain/__init__.py +74 -63
- openlit/instrumentation/langchain/callback_handler.py +1100 -0
- openlit/instrumentation/langchain_community/__init__.py +13 -2
- openlit/instrumentation/langchain_community/async_langchain_community.py +23 -5
- openlit/instrumentation/langchain_community/langchain_community.py +23 -5
- openlit/instrumentation/langchain_community/utils.py +35 -9
- openlit/instrumentation/letta/__init__.py +68 -15
- openlit/instrumentation/letta/letta.py +99 -54
- openlit/instrumentation/litellm/__init__.py +43 -14
- openlit/instrumentation/litellm/async_litellm.py +51 -26
- openlit/instrumentation/litellm/litellm.py +51 -26
- openlit/instrumentation/litellm/utils.py +304 -102
- openlit/instrumentation/llamaindex/__init__.py +267 -90
- openlit/instrumentation/llamaindex/async_llamaindex.py +28 -6
- openlit/instrumentation/llamaindex/llamaindex.py +28 -6
- openlit/instrumentation/llamaindex/utils.py +204 -91
- openlit/instrumentation/mem0/__init__.py +11 -2
- openlit/instrumentation/mem0/mem0.py +50 -29
- openlit/instrumentation/milvus/__init__.py +10 -2
- openlit/instrumentation/milvus/milvus.py +31 -6
- openlit/instrumentation/milvus/utils.py +166 -67
- openlit/instrumentation/mistral/__init__.py +63 -18
- openlit/instrumentation/mistral/async_mistral.py +63 -24
- openlit/instrumentation/mistral/mistral.py +63 -24
- openlit/instrumentation/mistral/utils.py +277 -69
- openlit/instrumentation/multion/__init__.py +69 -19
- openlit/instrumentation/multion/async_multion.py +57 -26
- openlit/instrumentation/multion/multion.py +57 -26
- openlit/instrumentation/ollama/__init__.py +39 -18
- openlit/instrumentation/ollama/async_ollama.py +57 -26
- openlit/instrumentation/ollama/ollama.py +57 -26
- openlit/instrumentation/ollama/utils.py +226 -50
- openlit/instrumentation/openai/__init__.py +156 -32
- openlit/instrumentation/openai/async_openai.py +147 -67
- openlit/instrumentation/openai/openai.py +150 -67
- openlit/instrumentation/openai/utils.py +657 -185
- openlit/instrumentation/openai_agents/__init__.py +5 -1
- openlit/instrumentation/openai_agents/processor.py +110 -90
- openlit/instrumentation/phidata/__init__.py +13 -5
- openlit/instrumentation/phidata/phidata.py +67 -32
- openlit/instrumentation/pinecone/__init__.py +48 -9
- openlit/instrumentation/pinecone/async_pinecone.py +27 -5
- openlit/instrumentation/pinecone/pinecone.py +27 -5
- openlit/instrumentation/pinecone/utils.py +153 -47
- openlit/instrumentation/premai/__init__.py +22 -7
- openlit/instrumentation/premai/premai.py +51 -26
- openlit/instrumentation/premai/utils.py +246 -59
- openlit/instrumentation/pydantic_ai/__init__.py +49 -22
- openlit/instrumentation/pydantic_ai/pydantic_ai.py +69 -16
- openlit/instrumentation/pydantic_ai/utils.py +89 -24
- openlit/instrumentation/qdrant/__init__.py +19 -4
- openlit/instrumentation/qdrant/async_qdrant.py +33 -7
- openlit/instrumentation/qdrant/qdrant.py +33 -7
- openlit/instrumentation/qdrant/utils.py +228 -93
- openlit/instrumentation/reka/__init__.py +23 -10
- openlit/instrumentation/reka/async_reka.py +17 -11
- openlit/instrumentation/reka/reka.py +17 -11
- openlit/instrumentation/reka/utils.py +138 -36
- openlit/instrumentation/together/__init__.py +44 -12
- openlit/instrumentation/together/async_together.py +50 -27
- openlit/instrumentation/together/together.py +50 -27
- openlit/instrumentation/together/utils.py +301 -71
- openlit/instrumentation/transformers/__init__.py +2 -1
- openlit/instrumentation/transformers/transformers.py +13 -3
- openlit/instrumentation/transformers/utils.py +139 -36
- openlit/instrumentation/vertexai/__init__.py +81 -16
- openlit/instrumentation/vertexai/async_vertexai.py +33 -15
- openlit/instrumentation/vertexai/utils.py +123 -27
- openlit/instrumentation/vertexai/vertexai.py +33 -15
- openlit/instrumentation/vllm/__init__.py +12 -5
- openlit/instrumentation/vllm/utils.py +121 -31
- openlit/instrumentation/vllm/vllm.py +16 -10
- openlit/otel/events.py +35 -10
- openlit/otel/metrics.py +32 -24
- openlit/otel/tracing.py +24 -9
- openlit/semcov/__init__.py +72 -6
- {openlit-1.34.30.dist-info → openlit-1.34.31.dist-info}/METADATA +2 -1
- openlit-1.34.31.dist-info/RECORD +166 -0
- openlit/instrumentation/langchain/async_langchain.py +0 -102
- openlit/instrumentation/langchain/langchain.py +0 -102
- openlit/instrumentation/langchain/utils.py +0 -252
- openlit-1.34.30.dist-info/RECORD +0 -168
- {openlit-1.34.30.dist-info → openlit-1.34.31.dist-info}/LICENSE +0 -0
- {openlit-1.34.30.dist-info → openlit-1.34.31.dist-info}/WHEEL +0 -0
@@ -17,6 +17,7 @@ from openlit.__helpers import (
|
|
17
17
|
)
|
18
18
|
from openlit.semcov import SemanticConvention
|
19
19
|
|
20
|
+
|
20
21
|
def format_content(messages):
|
21
22
|
"""
|
22
23
|
Process a list of messages to extract content.
|
@@ -25,9 +26,15 @@ def format_content(messages):
|
|
25
26
|
formatted_messages = []
|
26
27
|
for message in messages:
|
27
28
|
# Handle different message formats
|
28
|
-
if hasattr(message, "role") and (
|
29
|
+
if hasattr(message, "role") and (
|
30
|
+
hasattr(message, "content") or hasattr(message, "text")
|
31
|
+
):
|
29
32
|
# ChatMessage object (AI21 format)
|
30
|
-
role =
|
33
|
+
role = (
|
34
|
+
str(message.role)
|
35
|
+
if hasattr(message.role, "value")
|
36
|
+
else str(message.role)
|
37
|
+
)
|
31
38
|
content = getattr(message, "content", None) or getattr(message, "text", "")
|
32
39
|
elif isinstance(message, dict):
|
33
40
|
# Dictionary format
|
@@ -36,12 +43,15 @@ def format_content(messages):
|
|
36
43
|
else:
|
37
44
|
# Fallback - try to extract as string
|
38
45
|
role = str(getattr(message, "role", "unknown"))
|
39
|
-
content = str(
|
46
|
+
content = str(
|
47
|
+
getattr(message, "content", "") or getattr(message, "text", "")
|
48
|
+
)
|
40
49
|
|
41
50
|
if isinstance(content, list):
|
42
51
|
content_str = ", ".join(
|
43
|
-
f
|
44
|
-
if "type" in item
|
52
|
+
f"{item['type']}: {item['text'] if 'text' in item else item['image_url']}"
|
53
|
+
if "type" in item
|
54
|
+
else f"text: {item['text']}"
|
45
55
|
for item in content
|
46
56
|
)
|
47
57
|
formatted_messages.append(f"{role}: {content_str}")
|
@@ -50,6 +60,7 @@ def format_content(messages):
|
|
50
60
|
|
51
61
|
return "\n".join(formatted_messages)
|
52
62
|
|
63
|
+
|
53
64
|
def process_chunk(scope, chunk):
|
54
65
|
"""
|
55
66
|
Process a chunk of response data and update state.
|
@@ -66,10 +77,11 @@ def process_chunk(scope, chunk):
|
|
66
77
|
chunked = response_as_dict(chunk)
|
67
78
|
|
68
79
|
# Collect message IDs and aggregated response from events
|
69
|
-
if (
|
70
|
-
|
71
|
-
"
|
72
|
-
|
80
|
+
if (
|
81
|
+
len(chunked.get("choices", [])) > 0
|
82
|
+
and "delta" in chunked.get("choices")[0]
|
83
|
+
and "content" in chunked.get("choices")[0].get("delta", {})
|
84
|
+
):
|
73
85
|
content = chunked.get("choices")[0].get("delta").get("content")
|
74
86
|
if content:
|
75
87
|
scope._llmresponse += content
|
@@ -81,8 +93,18 @@ def process_chunk(scope, chunk):
|
|
81
93
|
scope._finish_reason = chunked.get("choices", [{}])[0].get("finish_reason")
|
82
94
|
scope._end_time = time.time()
|
83
95
|
|
84
|
-
|
85
|
-
|
96
|
+
|
97
|
+
def common_chat_logic(
|
98
|
+
scope,
|
99
|
+
pricing_info,
|
100
|
+
environment,
|
101
|
+
application_name,
|
102
|
+
metrics,
|
103
|
+
capture_message_content,
|
104
|
+
disable_metrics,
|
105
|
+
version,
|
106
|
+
is_stream,
|
107
|
+
):
|
86
108
|
"""
|
87
109
|
Process chat request and generate Telemetry
|
88
110
|
"""
|
@@ -93,44 +115,97 @@ def common_chat_logic(scope, pricing_info, environment, application_name, metric
|
|
93
115
|
prompt = format_content(scope._kwargs.get("messages", []))
|
94
116
|
request_model = scope._kwargs.get("model", "jamba-1.5-mini")
|
95
117
|
|
96
|
-
cost = get_chat_model_cost(
|
118
|
+
cost = get_chat_model_cost(
|
119
|
+
request_model, pricing_info, scope._input_tokens, scope._output_tokens
|
120
|
+
)
|
97
121
|
|
98
122
|
# Common Span Attributes
|
99
|
-
common_span_attributes(
|
100
|
-
|
101
|
-
|
102
|
-
|
123
|
+
common_span_attributes(
|
124
|
+
scope,
|
125
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
126
|
+
SemanticConvention.GEN_AI_SYSTEM_AI21,
|
127
|
+
scope._server_address,
|
128
|
+
scope._server_port,
|
129
|
+
request_model,
|
130
|
+
request_model,
|
131
|
+
environment,
|
132
|
+
application_name,
|
133
|
+
is_stream,
|
134
|
+
scope._tbt,
|
135
|
+
scope._ttft,
|
136
|
+
version,
|
137
|
+
)
|
103
138
|
|
104
139
|
# Span Attributes for Request parameters
|
105
|
-
scope._span.set_attribute(
|
106
|
-
|
107
|
-
|
108
|
-
scope._span.set_attribute(
|
109
|
-
|
110
|
-
|
111
|
-
|
140
|
+
scope._span.set_attribute(
|
141
|
+
SemanticConvention.GEN_AI_REQUEST_SEED, scope._kwargs.get("seed", "")
|
142
|
+
)
|
143
|
+
scope._span.set_attribute(
|
144
|
+
SemanticConvention.GEN_AI_REQUEST_FREQUENCY_PENALTY,
|
145
|
+
scope._kwargs.get("frequency_penalty", 0.0),
|
146
|
+
)
|
147
|
+
scope._span.set_attribute(
|
148
|
+
SemanticConvention.GEN_AI_REQUEST_MAX_TOKENS,
|
149
|
+
scope._kwargs.get("max_tokens", -1),
|
150
|
+
)
|
151
|
+
scope._span.set_attribute(
|
152
|
+
SemanticConvention.GEN_AI_REQUEST_PRESENCE_PENALTY,
|
153
|
+
scope._kwargs.get("presence_penalty", 0.0),
|
154
|
+
)
|
155
|
+
scope._span.set_attribute(
|
156
|
+
SemanticConvention.GEN_AI_REQUEST_STOP_SEQUENCES, scope._kwargs.get("stop", [])
|
157
|
+
)
|
158
|
+
scope._span.set_attribute(
|
159
|
+
SemanticConvention.GEN_AI_REQUEST_TEMPERATURE,
|
160
|
+
scope._kwargs.get("temperature", 0.4),
|
161
|
+
)
|
162
|
+
scope._span.set_attribute(
|
163
|
+
SemanticConvention.GEN_AI_REQUEST_TOP_P, scope._kwargs.get("top_p", 1.0)
|
164
|
+
)
|
112
165
|
|
113
166
|
# Span Attributes for Response parameters
|
114
167
|
scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_ID, scope._response_id)
|
115
|
-
scope._span.set_attribute(
|
116
|
-
|
168
|
+
scope._span.set_attribute(
|
169
|
+
SemanticConvention.GEN_AI_RESPONSE_FINISH_REASON, [scope._finish_reason]
|
170
|
+
)
|
171
|
+
scope._span.set_attribute(
|
172
|
+
SemanticConvention.GEN_AI_OUTPUT_TYPE,
|
173
|
+
"text" if isinstance(scope._llmresponse, str) else "json",
|
174
|
+
)
|
117
175
|
|
118
176
|
# Span Attributes for Cost and Tokens
|
119
|
-
scope._span.set_attribute(
|
120
|
-
|
121
|
-
|
177
|
+
scope._span.set_attribute(
|
178
|
+
SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens
|
179
|
+
)
|
180
|
+
scope._span.set_attribute(
|
181
|
+
SemanticConvention.GEN_AI_USAGE_OUTPUT_TOKENS, scope._output_tokens
|
182
|
+
)
|
183
|
+
scope._span.set_attribute(
|
184
|
+
SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE,
|
185
|
+
scope._input_tokens + scope._output_tokens,
|
186
|
+
)
|
122
187
|
scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
|
123
188
|
|
124
189
|
# Span Attributes for Tools
|
125
190
|
if scope._tools:
|
126
|
-
scope._span.set_attribute(
|
127
|
-
|
128
|
-
|
191
|
+
scope._span.set_attribute(
|
192
|
+
SemanticConvention.GEN_AI_TOOL_NAME,
|
193
|
+
scope._tools.get("function", {}).get("name", ""),
|
194
|
+
)
|
195
|
+
scope._span.set_attribute(
|
196
|
+
SemanticConvention.GEN_AI_TOOL_CALL_ID, str(scope._tools.get("id", ""))
|
197
|
+
)
|
198
|
+
scope._span.set_attribute(
|
199
|
+
SemanticConvention.GEN_AI_TOOL_ARGS,
|
200
|
+
str(scope._tools.get("function", {}).get("arguments", "")),
|
201
|
+
)
|
129
202
|
|
130
203
|
# Span Attributes for Content
|
131
204
|
if capture_message_content:
|
132
205
|
scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, prompt)
|
133
|
-
scope._span.set_attribute(
|
206
|
+
scope._span.set_attribute(
|
207
|
+
SemanticConvention.GEN_AI_CONTENT_COMPLETION, scope._llmresponse
|
208
|
+
)
|
134
209
|
|
135
210
|
# To be removed once the change to span_attributes (from span events) is complete
|
136
211
|
scope._span.add_event(
|
@@ -150,23 +225,69 @@ def common_chat_logic(scope, pricing_info, environment, application_name, metric
|
|
150
225
|
|
151
226
|
# Metrics
|
152
227
|
if not disable_metrics:
|
153
|
-
record_completion_metrics(
|
154
|
-
|
155
|
-
|
156
|
-
|
228
|
+
record_completion_metrics(
|
229
|
+
metrics,
|
230
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
231
|
+
SemanticConvention.GEN_AI_SYSTEM_AI21,
|
232
|
+
scope._server_address,
|
233
|
+
scope._server_port,
|
234
|
+
request_model,
|
235
|
+
request_model,
|
236
|
+
environment,
|
237
|
+
application_name,
|
238
|
+
scope._start_time,
|
239
|
+
scope._end_time,
|
240
|
+
scope._input_tokens,
|
241
|
+
scope._output_tokens,
|
242
|
+
cost,
|
243
|
+
scope._tbt,
|
244
|
+
scope._ttft,
|
245
|
+
)
|
157
246
|
|
158
|
-
|
159
|
-
|
247
|
+
|
248
|
+
def process_streaming_chat_response(
|
249
|
+
scope,
|
250
|
+
pricing_info,
|
251
|
+
environment,
|
252
|
+
application_name,
|
253
|
+
metrics,
|
254
|
+
capture_message_content=False,
|
255
|
+
disable_metrics=False,
|
256
|
+
version="",
|
257
|
+
):
|
160
258
|
"""
|
161
259
|
Process streaming chat request and generate Telemetry
|
162
260
|
"""
|
163
261
|
|
164
|
-
common_chat_logic(
|
165
|
-
|
262
|
+
common_chat_logic(
|
263
|
+
scope,
|
264
|
+
pricing_info,
|
265
|
+
environment,
|
266
|
+
application_name,
|
267
|
+
metrics,
|
268
|
+
capture_message_content,
|
269
|
+
disable_metrics,
|
270
|
+
version,
|
271
|
+
is_stream=True,
|
272
|
+
)
|
273
|
+
|
166
274
|
|
167
|
-
def process_chat_response(
|
168
|
-
|
169
|
-
|
275
|
+
def process_chat_response(
|
276
|
+
response,
|
277
|
+
request_model,
|
278
|
+
pricing_info,
|
279
|
+
server_port,
|
280
|
+
server_address,
|
281
|
+
environment,
|
282
|
+
application_name,
|
283
|
+
metrics,
|
284
|
+
start_time,
|
285
|
+
span,
|
286
|
+
capture_message_content=False,
|
287
|
+
disable_metrics=False,
|
288
|
+
version="1.0.0",
|
289
|
+
**kwargs,
|
290
|
+
):
|
170
291
|
"""
|
171
292
|
Process chat request and generate Telemetry
|
172
293
|
"""
|
@@ -189,21 +310,43 @@ def process_chat_response(response, request_model, pricing_info, server_port, se
|
|
189
310
|
scope._ttft, scope._tbt = scope._end_time - scope._start_time, 0
|
190
311
|
scope._server_address, scope._server_port = server_address, server_port
|
191
312
|
scope._kwargs = kwargs
|
192
|
-
scope._finish_reason = str(
|
313
|
+
scope._finish_reason = str(
|
314
|
+
response_dict.get("choices", [])[0].get("finish_reason", "")
|
315
|
+
)
|
193
316
|
|
194
317
|
# Handle tool calls
|
195
318
|
if scope._kwargs.get("tools"):
|
196
|
-
scope._tools =
|
319
|
+
scope._tools = (
|
320
|
+
response_dict.get("choices", [{}])[0].get("message", {}).get("tool_calls")
|
321
|
+
)
|
197
322
|
else:
|
198
323
|
scope._tools = None
|
199
324
|
|
200
|
-
common_chat_logic(
|
201
|
-
|
325
|
+
common_chat_logic(
|
326
|
+
scope,
|
327
|
+
pricing_info,
|
328
|
+
environment,
|
329
|
+
application_name,
|
330
|
+
metrics,
|
331
|
+
capture_message_content,
|
332
|
+
disable_metrics,
|
333
|
+
version,
|
334
|
+
is_stream=False,
|
335
|
+
)
|
202
336
|
|
203
337
|
return response
|
204
338
|
|
205
|
-
|
206
|
-
|
339
|
+
|
340
|
+
def common_chat_rag_logic(
|
341
|
+
scope,
|
342
|
+
pricing_info,
|
343
|
+
environment,
|
344
|
+
application_name,
|
345
|
+
metrics,
|
346
|
+
capture_message_content,
|
347
|
+
disable_metrics,
|
348
|
+
version,
|
349
|
+
):
|
207
350
|
"""
|
208
351
|
Process RAG chat request and generate Telemetry
|
209
352
|
"""
|
@@ -211,40 +354,82 @@ def common_chat_rag_logic(scope, pricing_info, environment, application_name, me
|
|
211
354
|
prompt = format_content(scope._kwargs.get("messages", []))
|
212
355
|
request_model = scope._kwargs.get("model", "jamba-1.5-mini")
|
213
356
|
|
214
|
-
cost = get_chat_model_cost(
|
357
|
+
cost = get_chat_model_cost(
|
358
|
+
request_model, pricing_info, scope._input_tokens, scope._output_tokens
|
359
|
+
)
|
215
360
|
|
216
361
|
# Common Span Attributes
|
217
|
-
common_span_attributes(
|
218
|
-
|
219
|
-
|
220
|
-
|
362
|
+
common_span_attributes(
|
363
|
+
scope,
|
364
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
365
|
+
SemanticConvention.GEN_AI_SYSTEM_AI21,
|
366
|
+
scope._server_address,
|
367
|
+
scope._server_port,
|
368
|
+
request_model,
|
369
|
+
scope._response_model,
|
370
|
+
environment,
|
371
|
+
application_name,
|
372
|
+
False,
|
373
|
+
scope._tbt,
|
374
|
+
scope._ttft,
|
375
|
+
version,
|
376
|
+
)
|
221
377
|
|
222
378
|
# RAG-specific span attributes
|
223
|
-
scope._span.set_attribute(
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
scope._span.set_attribute(
|
228
|
-
|
229
|
-
|
379
|
+
scope._span.set_attribute(
|
380
|
+
SemanticConvention.GEN_AI_RAG_MAX_SEGMENTS,
|
381
|
+
scope._kwargs.get("max_segments", -1),
|
382
|
+
)
|
383
|
+
scope._span.set_attribute(
|
384
|
+
SemanticConvention.GEN_AI_RAG_STRATEGY,
|
385
|
+
scope._kwargs.get("retrieval_strategy", "segments"),
|
386
|
+
)
|
387
|
+
scope._span.set_attribute(
|
388
|
+
SemanticConvention.GEN_AI_RAG_MAX_NEIGHBORS,
|
389
|
+
scope._kwargs.get("max_neighbors", -1),
|
390
|
+
)
|
391
|
+
scope._span.set_attribute(
|
392
|
+
SemanticConvention.GEN_AI_RAG_FILE_IDS, str(scope._kwargs.get("file_ids", ""))
|
393
|
+
)
|
394
|
+
scope._span.set_attribute(
|
395
|
+
SemanticConvention.GEN_AI_RAG_DOCUMENTS_PATH, scope._kwargs.get("path", "")
|
396
|
+
)
|
397
|
+
scope._span.set_attribute(
|
398
|
+
SemanticConvention.GEN_AI_RAG_SIMILARITY_THRESHOLD,
|
399
|
+
scope._kwargs.get("retrieval_similarity_threshold", -1),
|
400
|
+
)
|
230
401
|
|
231
402
|
# Standard span attributes
|
232
403
|
scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_ID, scope._response_id)
|
233
|
-
scope._span.set_attribute(
|
234
|
-
|
235
|
-
|
236
|
-
|
404
|
+
scope._span.set_attribute(
|
405
|
+
SemanticConvention.GEN_AI_OUTPUT_TYPE,
|
406
|
+
"text" if isinstance(scope._llmresponse, str) else "json",
|
407
|
+
)
|
408
|
+
scope._span.set_attribute(
|
409
|
+
SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens
|
410
|
+
)
|
411
|
+
scope._span.set_attribute(
|
412
|
+
SemanticConvention.GEN_AI_USAGE_OUTPUT_TOKENS, scope._output_tokens
|
413
|
+
)
|
414
|
+
scope._span.set_attribute(
|
415
|
+
SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE,
|
416
|
+
scope._input_tokens + scope._output_tokens,
|
417
|
+
)
|
237
418
|
scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
|
238
419
|
|
239
420
|
# Handle tool calls
|
240
421
|
if scope._kwargs.get("tools"):
|
241
|
-
scope._span.set_attribute(
|
242
|
-
|
422
|
+
scope._span.set_attribute(
|
423
|
+
SemanticConvention.GEN_AI_TOOL_CALLS,
|
424
|
+
str(scope._choices[0].get("message", {}).get("tool_calls", "")),
|
425
|
+
)
|
243
426
|
|
244
427
|
# Content attributes
|
245
428
|
if capture_message_content:
|
246
429
|
scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, prompt)
|
247
|
-
scope._span.set_attribute(
|
430
|
+
scope._span.set_attribute(
|
431
|
+
SemanticConvention.GEN_AI_CONTENT_COMPLETION, scope._llmresponse
|
432
|
+
)
|
248
433
|
|
249
434
|
# To be removed once the change to span_attributes (from span events) is complete
|
250
435
|
scope._span.add_event(
|
@@ -264,14 +449,42 @@ def common_chat_rag_logic(scope, pricing_info, environment, application_name, me
|
|
264
449
|
|
265
450
|
# Metrics
|
266
451
|
if not disable_metrics:
|
267
|
-
record_completion_metrics(
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
452
|
+
record_completion_metrics(
|
453
|
+
metrics,
|
454
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
455
|
+
SemanticConvention.GEN_AI_SYSTEM_AI21,
|
456
|
+
scope._server_address,
|
457
|
+
scope._server_port,
|
458
|
+
request_model,
|
459
|
+
scope._response_model,
|
460
|
+
environment,
|
461
|
+
application_name,
|
462
|
+
scope._start_time,
|
463
|
+
scope._end_time,
|
464
|
+
scope._input_tokens,
|
465
|
+
scope._output_tokens,
|
466
|
+
cost,
|
467
|
+
scope._tbt,
|
468
|
+
scope._ttft,
|
469
|
+
)
|
470
|
+
|
471
|
+
|
472
|
+
def process_chat_rag_response(
|
473
|
+
response,
|
474
|
+
request_model,
|
475
|
+
pricing_info,
|
476
|
+
server_port,
|
477
|
+
server_address,
|
478
|
+
environment,
|
479
|
+
application_name,
|
480
|
+
metrics,
|
481
|
+
start_time,
|
482
|
+
span,
|
483
|
+
capture_message_content=False,
|
484
|
+
disable_metrics=False,
|
485
|
+
version="1.0.0",
|
486
|
+
**kwargs,
|
487
|
+
):
|
275
488
|
"""
|
276
489
|
Process RAG chat request and generate Telemetry
|
277
490
|
"""
|
@@ -311,7 +524,15 @@ def process_chat_rag_response(response, request_model, pricing_info, server_port
|
|
311
524
|
scope._tools = None
|
312
525
|
scope._choices = choices
|
313
526
|
|
314
|
-
common_chat_rag_logic(
|
315
|
-
|
527
|
+
common_chat_rag_logic(
|
528
|
+
scope,
|
529
|
+
pricing_info,
|
530
|
+
environment,
|
531
|
+
application_name,
|
532
|
+
metrics,
|
533
|
+
capture_message_content,
|
534
|
+
disable_metrics,
|
535
|
+
version,
|
536
|
+
)
|
316
537
|
|
317
538
|
return response
|
@@ -10,6 +10,7 @@ from openlit.instrumentation.anthropic.async_anthropic import async_messages
|
|
10
10
|
|
11
11
|
_instruments = ("anthropic >= 0.21.0",)
|
12
12
|
|
13
|
+
|
13
14
|
class AnthropicInstrumentor(BaseInstrumentor):
|
14
15
|
"""
|
15
16
|
An instrumentor for Anthropic's client library.
|
@@ -32,16 +33,32 @@ class AnthropicInstrumentor(BaseInstrumentor):
|
|
32
33
|
wrap_function_wrapper(
|
33
34
|
"anthropic.resources.messages",
|
34
35
|
"Messages.create",
|
35
|
-
messages(
|
36
|
-
|
36
|
+
messages(
|
37
|
+
version,
|
38
|
+
environment,
|
39
|
+
application_name,
|
40
|
+
tracer,
|
41
|
+
pricing_info,
|
42
|
+
capture_message_content,
|
43
|
+
metrics,
|
44
|
+
disable_metrics,
|
45
|
+
),
|
37
46
|
)
|
38
47
|
|
39
48
|
# async
|
40
49
|
wrap_function_wrapper(
|
41
50
|
"anthropic.resources.messages",
|
42
51
|
"AsyncMessages.create",
|
43
|
-
async_messages(
|
44
|
-
|
52
|
+
async_messages(
|
53
|
+
version,
|
54
|
+
environment,
|
55
|
+
application_name,
|
56
|
+
tracer,
|
57
|
+
pricing_info,
|
58
|
+
capture_message_content,
|
59
|
+
metrics,
|
60
|
+
disable_metrics,
|
61
|
+
),
|
45
62
|
)
|
46
63
|
|
47
64
|
def _uninstrument(self, **kwargs):
|
@@ -4,10 +4,7 @@ Module for monitoring Anthropic API calls.
|
|
4
4
|
|
5
5
|
import time
|
6
6
|
from opentelemetry.trace import SpanKind
|
7
|
-
from openlit.__helpers import
|
8
|
-
handle_exception,
|
9
|
-
set_server_address_and_port
|
10
|
-
)
|
7
|
+
from openlit.__helpers import handle_exception, set_server_address_and_port
|
11
8
|
from openlit.instrumentation.anthropic.utils import (
|
12
9
|
process_chunk,
|
13
10
|
process_chat_response,
|
@@ -15,7 +12,17 @@ from openlit.instrumentation.anthropic.utils import (
|
|
15
12
|
)
|
16
13
|
from openlit.semcov import SemanticConvention
|
17
14
|
|
18
|
-
|
15
|
+
|
16
|
+
def messages(
|
17
|
+
version,
|
18
|
+
environment,
|
19
|
+
application_name,
|
20
|
+
tracer,
|
21
|
+
pricing_info,
|
22
|
+
capture_message_content,
|
23
|
+
metrics,
|
24
|
+
disable_metrics,
|
25
|
+
):
|
19
26
|
"""
|
20
27
|
Generates a telemetry wrapper for Anthropic Messages.create calls.
|
21
28
|
"""
|
@@ -26,14 +33,14 @@ def messages(version, environment, application_name, tracer, pricing_info, captu
|
|
26
33
|
"""
|
27
34
|
|
28
35
|
def __init__(
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
36
|
+
self,
|
37
|
+
wrapped,
|
38
|
+
span,
|
39
|
+
span_name,
|
40
|
+
kwargs,
|
41
|
+
server_address,
|
42
|
+
server_port,
|
43
|
+
):
|
37
44
|
self.__wrapped__ = wrapped
|
38
45
|
self._span = span
|
39
46
|
self._span_name = span_name
|
@@ -87,7 +94,7 @@ def messages(version, environment, application_name, tracer, pricing_info, captu
|
|
87
94
|
metrics=metrics,
|
88
95
|
capture_message_content=capture_message_content,
|
89
96
|
disable_metrics=disable_metrics,
|
90
|
-
version=version
|
97
|
+
version=version,
|
91
98
|
)
|
92
99
|
except Exception as e:
|
93
100
|
handle_exception(self._span, e)
|
@@ -99,7 +106,9 @@ def messages(version, environment, application_name, tracer, pricing_info, captu
|
|
99
106
|
"""
|
100
107
|
|
101
108
|
streaming = kwargs.get("stream", False)
|
102
|
-
server_address, server_port = set_server_address_and_port(
|
109
|
+
server_address, server_port = set_server_address_and_port(
|
110
|
+
instance, "api.anthropic.com", 443
|
111
|
+
)
|
103
112
|
request_model = kwargs.get("model", "claude-3-5-sonnet-latest")
|
104
113
|
|
105
114
|
span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT} {request_model}"
|
@@ -109,7 +118,9 @@ def messages(version, environment, application_name, tracer, pricing_info, captu
|
|
109
118
|
awaited_wrapped = wrapped(*args, **kwargs)
|
110
119
|
span = tracer.start_span(span_name, kind=SpanKind.CLIENT)
|
111
120
|
|
112
|
-
return TracedSyncStream(
|
121
|
+
return TracedSyncStream(
|
122
|
+
awaited_wrapped, span, span_name, kwargs, server_address, server_port
|
123
|
+
)
|
113
124
|
|
114
125
|
else:
|
115
126
|
with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
|
@@ -131,7 +142,7 @@ def messages(version, environment, application_name, tracer, pricing_info, captu
|
|
131
142
|
capture_message_content=capture_message_content,
|
132
143
|
disable_metrics=disable_metrics,
|
133
144
|
version=version,
|
134
|
-
**kwargs
|
145
|
+
**kwargs,
|
135
146
|
)
|
136
147
|
|
137
148
|
except Exception as e:
|