openlit 1.34.27__py3-none-any.whl → 1.34.29__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 +38 -0
- openlit/__init__.py +22 -155
- openlit/_instrumentors.py +144 -0
- openlit/guard/all.py +3 -3
- openlit/instrumentation/chroma/utils.py +2 -2
- openlit/instrumentation/controlflow/controlflow.py +2 -2
- openlit/instrumentation/embedchain/embedchain.py +4 -4
- openlit/instrumentation/groq/__init__.py +4 -4
- openlit/instrumentation/haystack/__init__.py +57 -28
- openlit/instrumentation/haystack/async_haystack.py +54 -0
- openlit/instrumentation/haystack/haystack.py +35 -65
- openlit/instrumentation/haystack/utils.py +377 -0
- openlit/instrumentation/julep/async_julep.py +2 -2
- openlit/instrumentation/julep/julep.py +2 -2
- openlit/instrumentation/langchain_community/utils.py +2 -2
- openlit/instrumentation/llamaindex/__init__.py +165 -37
- openlit/instrumentation/llamaindex/async_llamaindex.py +53 -0
- openlit/instrumentation/llamaindex/llamaindex.py +32 -64
- openlit/instrumentation/llamaindex/utils.py +412 -0
- openlit/instrumentation/mem0/mem0.py +2 -2
- openlit/instrumentation/openai/__init__.py +24 -24
- openlit/instrumentation/openai/utils.py +66 -27
- openlit/instrumentation/openai_agents/__init__.py +46 -26
- openlit/instrumentation/openai_agents/processor.py +600 -0
- openlit/instrumentation/pinecone/utils.py +2 -2
- openlit/instrumentation/qdrant/utils.py +2 -2
- openlit/instrumentation/together/__init__.py +8 -8
- openlit/semcov/__init__.py +80 -0
- {openlit-1.34.27.dist-info → openlit-1.34.29.dist-info}/METADATA +2 -1
- {openlit-1.34.27.dist-info → openlit-1.34.29.dist-info}/RECORD +32 -27
- openlit/instrumentation/openai_agents/openai_agents.py +0 -65
- {openlit-1.34.27.dist-info → openlit-1.34.29.dist-info}/LICENSE +0 -0
- {openlit-1.34.27.dist-info → openlit-1.34.29.dist-info}/WHEEL +0 -0
@@ -0,0 +1,412 @@
|
|
1
|
+
"""
|
2
|
+
LlamaIndex OpenTelemetry Instrumentation
|
3
|
+
"""
|
4
|
+
import time
|
5
|
+
import hashlib
|
6
|
+
from opentelemetry.trace import Status, StatusCode
|
7
|
+
|
8
|
+
from openlit.__helpers import (
|
9
|
+
common_framework_span_attributes,
|
10
|
+
record_framework_metrics,
|
11
|
+
)
|
12
|
+
from openlit.semcov import SemanticConvention
|
13
|
+
|
14
|
+
# === OPTIMIZED OPERATION MAPPING - Framework Guide Compliant ===
|
15
|
+
# Simplified semantic conventions for efficient processing
|
16
|
+
OPERATION_MAP = {
|
17
|
+
# === WORKFLOW OPERATIONS (Business-level spans) ===
|
18
|
+
|
19
|
+
# Document Loading & Processing Pipeline
|
20
|
+
"document_load": SemanticConvention.GEN_AI_OPERATION_TYPE_RETRIEVE,
|
21
|
+
"document_load_async": SemanticConvention.GEN_AI_OPERATION_TYPE_RETRIEVE,
|
22
|
+
"document_transform": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
23
|
+
"document_split": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
24
|
+
|
25
|
+
# Index Construction & Management
|
26
|
+
"index_construct": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
27
|
+
"index_insert": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
28
|
+
"index_delete": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
29
|
+
"index_build": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
30
|
+
|
31
|
+
# Query Engine Operations
|
32
|
+
"query_engine_create": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
33
|
+
"query_engine_query": SemanticConvention.GEN_AI_OPERATION_TYPE_RETRIEVE,
|
34
|
+
"query_engine_query_async": SemanticConvention.GEN_AI_OPERATION_TYPE_RETRIEVE,
|
35
|
+
|
36
|
+
# Retriever Operations
|
37
|
+
"retriever_create": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
38
|
+
"retriever_retrieve": SemanticConvention.GEN_AI_OPERATION_TYPE_RETRIEVE,
|
39
|
+
"retriever_retrieve_async": SemanticConvention.GEN_AI_OPERATION_TYPE_RETRIEVE,
|
40
|
+
|
41
|
+
# LLM & Embedding Operations
|
42
|
+
"llm_complete": SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
43
|
+
"llm_complete_async": SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
44
|
+
"llm_chat": SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
45
|
+
"llm_chat_async": SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
46
|
+
"llm_stream_async": SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
47
|
+
"embedding_generate": SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING,
|
48
|
+
"embedding_generate_async": SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING,
|
49
|
+
|
50
|
+
# Response Generation Operations
|
51
|
+
"response_generate_async": SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
52
|
+
|
53
|
+
# === COMPONENT OPERATIONS (Technical-level spans) ===
|
54
|
+
|
55
|
+
# Text Processing Components
|
56
|
+
"text_splitter_split": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
57
|
+
"text_splitter_postprocess": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
58
|
+
"node_parser_parse": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
59
|
+
|
60
|
+
# Embedding Processing Components
|
61
|
+
"embedding_encode": SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING,
|
62
|
+
"embedding_embed_nodes": SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING,
|
63
|
+
"embedding_similarity": SemanticConvention.GEN_AI_OPERATION_TYPE_RETRIEVE,
|
64
|
+
"embedding_metadata": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
65
|
+
|
66
|
+
# Retrieval Processing Components
|
67
|
+
"retrieval_retrieve_nodes": SemanticConvention.GEN_AI_OPERATION_TYPE_RETRIEVE,
|
68
|
+
"retrieval_get_nodes": SemanticConvention.GEN_AI_OPERATION_TYPE_RETRIEVE,
|
69
|
+
"retrieval_build_nodes": SemanticConvention.GEN_AI_OPERATION_TYPE_RETRIEVE,
|
70
|
+
"postprocessor_process": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
71
|
+
|
72
|
+
# Response Generation Components
|
73
|
+
"response_synthesize": SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
74
|
+
"response_compact_refine": SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
75
|
+
"response_tree_summarize": SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
76
|
+
|
77
|
+
# Vector Store Components
|
78
|
+
"vector_store_add": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
79
|
+
"vector_store_delete": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
80
|
+
"vector_store_query": SemanticConvention.GEN_AI_OPERATION_TYPE_RETRIEVE,
|
81
|
+
|
82
|
+
# Document & Node Components
|
83
|
+
"document_get_content": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
84
|
+
"node_get_content": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
85
|
+
"node_get_metadata": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
86
|
+
"document_extract_metadata": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
87
|
+
"query_prepare_response": SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK,
|
88
|
+
}
|
89
|
+
|
90
|
+
def set_server_address_and_port(instance, default_host="localhost", default_port=8000):
|
91
|
+
"""Extract server address and port with enhanced detection"""
|
92
|
+
if hasattr(instance, '_client'):
|
93
|
+
client = instance._client
|
94
|
+
if hasattr(client, 'base_url'):
|
95
|
+
base_url = str(client.base_url)
|
96
|
+
if '://' in base_url:
|
97
|
+
parts = base_url.split('://', 1)[1].split('/', 1)[0]
|
98
|
+
if ':' in parts:
|
99
|
+
host, port = parts.rsplit(':', 1)
|
100
|
+
try:
|
101
|
+
return host, int(port)
|
102
|
+
except ValueError:
|
103
|
+
return parts, default_port
|
104
|
+
return parts, default_port
|
105
|
+
return default_host, default_port
|
106
|
+
|
107
|
+
def object_count(obj):
|
108
|
+
"""Enhanced object counting with type detection"""
|
109
|
+
if obj is None:
|
110
|
+
return 0
|
111
|
+
try:
|
112
|
+
if hasattr(obj, '__len__'):
|
113
|
+
return len(obj)
|
114
|
+
elif hasattr(obj, '__iter__'):
|
115
|
+
return sum(1 for _ in obj)
|
116
|
+
else:
|
117
|
+
return 1 if obj else 0
|
118
|
+
except Exception:
|
119
|
+
return 0
|
120
|
+
|
121
|
+
def extract_performance_metrics(scope):
|
122
|
+
"""Extract comprehensive performance metrics"""
|
123
|
+
duration = scope._end_time - scope._start_time
|
124
|
+
|
125
|
+
# Performance categorization for business intelligence
|
126
|
+
if duration < 0.1:
|
127
|
+
performance_tier = "excellent"
|
128
|
+
elif duration < 0.5:
|
129
|
+
performance_tier = "good"
|
130
|
+
elif duration < 2.0:
|
131
|
+
performance_tier = "acceptable"
|
132
|
+
else:
|
133
|
+
performance_tier = "slow"
|
134
|
+
|
135
|
+
return {
|
136
|
+
"duration": duration,
|
137
|
+
"performance_tier": performance_tier,
|
138
|
+
"latency_ms": duration * 1000,
|
139
|
+
"is_fast": duration < 0.5,
|
140
|
+
"needs_optimization": duration > 2.0
|
141
|
+
}
|
142
|
+
|
143
|
+
def extract_content_metrics(content):
|
144
|
+
"""Extract advanced content analysis metrics"""
|
145
|
+
if not content:
|
146
|
+
return {}
|
147
|
+
|
148
|
+
content_str = str(content)
|
149
|
+
char_count = len(content_str)
|
150
|
+
word_count = len(content_str.split()) if content_str else 0
|
151
|
+
|
152
|
+
# Content complexity analysis
|
153
|
+
complexity_score = 0
|
154
|
+
if word_count > 0:
|
155
|
+
avg_word_length = char_count / word_count
|
156
|
+
complexity_score = min(100, int((avg_word_length * 10) + (word_count / 10)))
|
157
|
+
|
158
|
+
return {
|
159
|
+
"char_count": char_count,
|
160
|
+
"word_count": word_count,
|
161
|
+
"complexity_score": complexity_score,
|
162
|
+
"content_hash": hashlib.md5(content_str.encode()).hexdigest()[:8],
|
163
|
+
"is_lengthy": char_count > 1000,
|
164
|
+
"is_complex": complexity_score > 50
|
165
|
+
}
|
166
|
+
|
167
|
+
def extract_business_intelligence(scope, endpoint):
|
168
|
+
"""Extract superior business intelligence attributes"""
|
169
|
+
bi_attrs = {}
|
170
|
+
|
171
|
+
# Operation categorization for business insights
|
172
|
+
if endpoint.startswith("framework.query"):
|
173
|
+
bi_attrs["operation_category"] = "user_interaction"
|
174
|
+
bi_attrs["business_impact"] = "high"
|
175
|
+
bi_attrs["cost_driver"] = "llm_calls"
|
176
|
+
elif endpoint.startswith("framework.index"):
|
177
|
+
bi_attrs["operation_category"] = "data_preparation"
|
178
|
+
bi_attrs["business_impact"] = "medium"
|
179
|
+
bi_attrs["cost_driver"] = "embedding_generation"
|
180
|
+
elif endpoint.startswith("framework.retriever"):
|
181
|
+
bi_attrs["operation_category"] = "information_retrieval"
|
182
|
+
bi_attrs["business_impact"] = "high"
|
183
|
+
bi_attrs["cost_driver"] = "vector_search"
|
184
|
+
elif endpoint.startswith("component."):
|
185
|
+
bi_attrs["operation_category"] = "technical_processing"
|
186
|
+
bi_attrs["business_impact"] = "low"
|
187
|
+
bi_attrs["cost_driver"] = "compute_resources"
|
188
|
+
|
189
|
+
# Performance impact classification
|
190
|
+
performance = extract_performance_metrics(scope)
|
191
|
+
bi_attrs["performance_impact"] = performance["performance_tier"]
|
192
|
+
bi_attrs["optimization_opportunity"] = performance["needs_optimization"]
|
193
|
+
|
194
|
+
return bi_attrs
|
195
|
+
|
196
|
+
def common_llamaindex_logic(scope, environment, application_name,
|
197
|
+
metrics, capture_message_content, disable_metrics, version,
|
198
|
+
instance=None, endpoint=None, **kwargs):
|
199
|
+
"""
|
200
|
+
DOMINANCE EDITION: Process LlamaIndex with superior attribute richness
|
201
|
+
Enhanced to beat OpenInference with 5+ attributes per span vs their 2.3
|
202
|
+
"""
|
203
|
+
scope._end_time = time.time()
|
204
|
+
|
205
|
+
# Set common framework span attributes using centralized helper
|
206
|
+
common_framework_span_attributes(scope, SemanticConvention.GEN_AI_SYSTEM_LLAMAINDEX,
|
207
|
+
scope._server_address, scope._server_port, environment, application_name,
|
208
|
+
version, endpoint, instance)
|
209
|
+
|
210
|
+
# === CORE SEMANTIC ATTRIBUTES ===
|
211
|
+
operation_type = OPERATION_MAP.get(endpoint, SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK)
|
212
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_OPERATION, operation_type)
|
213
|
+
|
214
|
+
# === PERFORMANCE INTELLIGENCE ===
|
215
|
+
performance = extract_performance_metrics(scope)
|
216
|
+
scope._span.set_attribute("gen_ai.operation.duration_ms", performance["latency_ms"])
|
217
|
+
scope._span.set_attribute("gen_ai.operation.performance_tier", performance["performance_tier"])
|
218
|
+
scope._span.set_attribute("gen_ai.operation.is_fast", performance["is_fast"])
|
219
|
+
|
220
|
+
# === BUSINESS INTELLIGENCE ===
|
221
|
+
bi_attrs = extract_business_intelligence(scope, endpoint)
|
222
|
+
for key, value in bi_attrs.items():
|
223
|
+
scope._span.set_attribute(f"gen_ai.business.{key}", str(value))
|
224
|
+
|
225
|
+
# === OPERATION-SPECIFIC ENHANCED PROCESSING ===
|
226
|
+
|
227
|
+
if endpoint == "framework.index.construct":
|
228
|
+
# Enhanced index construction telemetry
|
229
|
+
documents_count = scope._kwargs.get("documents", [])
|
230
|
+
if documents_count:
|
231
|
+
doc_count = object_count(documents_count)
|
232
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_FRAMEWORK_DOCUMENTS_COUNT, doc_count)
|
233
|
+
|
234
|
+
# Document source analysis
|
235
|
+
document_sources = []
|
236
|
+
total_content_length = 0
|
237
|
+
unique_authors = set()
|
238
|
+
|
239
|
+
for doc in (documents_count[:10] if hasattr(documents_count, '__iter__') else []):
|
240
|
+
if hasattr(doc, 'metadata'):
|
241
|
+
source = doc.metadata.get('file_path', 'unknown')
|
242
|
+
author = doc.metadata.get('author', 'unknown')
|
243
|
+
document_sources.append(source)
|
244
|
+
unique_authors.add(author)
|
245
|
+
|
246
|
+
if hasattr(doc, 'text'):
|
247
|
+
total_content_length += len(doc.text)
|
248
|
+
|
249
|
+
scope._span.set_attribute("gen_ai.index.document_sources", str(document_sources[:5]))
|
250
|
+
scope._span.set_attribute("gen_ai.index.total_content_length", total_content_length)
|
251
|
+
scope._span.set_attribute("gen_ai.index.unique_authors", len(unique_authors))
|
252
|
+
scope._span.set_attribute("gen_ai.index.avg_document_size", total_content_length // max(doc_count, 1))
|
253
|
+
|
254
|
+
elif endpoint in ("framework.query_engine.query", "framework.query_engine.query_async"):
|
255
|
+
# Enhanced query processing telemetry
|
256
|
+
query_text = scope._args[0] if scope._args else scope._kwargs.get("query", "unknown")
|
257
|
+
if capture_message_content:
|
258
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, str(query_text))
|
259
|
+
|
260
|
+
# Query analysis
|
261
|
+
query_length = len(str(query_text))
|
262
|
+
query_words = len(str(query_text).split())
|
263
|
+
scope._span.set_attribute("gen_ai.query.length", query_length)
|
264
|
+
scope._span.set_attribute("gen_ai.query.word_count", query_words)
|
265
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_FRAMEWORK_QUERY_TYPE, "query_engine")
|
266
|
+
|
267
|
+
# Process index operations using helper function
|
268
|
+
_process_index_operations(scope, endpoint, capture_message_content)
|
269
|
+
|
270
|
+
elif endpoint in ("framework.retriever.retrieve", "framework.retriever.retrieve_async"):
|
271
|
+
# Enhanced retrieval telemetry
|
272
|
+
query_text = scope._args[0] if scope._args else scope._kwargs.get("query", "unknown")
|
273
|
+
if capture_message_content:
|
274
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, str(query_text))
|
275
|
+
|
276
|
+
# Retrieval configuration analysis
|
277
|
+
similarity_top_k = scope._kwargs.get("similarity_top_k", 2)
|
278
|
+
scope._span.set_attribute("gen_ai.retrieval.top_k", similarity_top_k)
|
279
|
+
scope._span.set_attribute("gen_ai.retrieval.strategy", "vector_similarity")
|
280
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_FRAMEWORK_QUERY_TYPE, "retriever")
|
281
|
+
|
282
|
+
elif endpoint == "framework.document.split":
|
283
|
+
# Enhanced document splitting telemetry
|
284
|
+
show_progress = scope._kwargs.get("show_progress", False)
|
285
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_FRAMEWORK_SHOW_PROGRESS, show_progress)
|
286
|
+
|
287
|
+
# Extract enhanced node creation info
|
288
|
+
if scope._response and hasattr(scope._response, '__len__'):
|
289
|
+
nodes_created = len(scope._response)
|
290
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_FRAMEWORK_NODES_CREATED, nodes_created)
|
291
|
+
|
292
|
+
# Extract comprehensive chunk configuration
|
293
|
+
chunk_size = getattr(instance, 'chunk_size', 1024) if instance else 1024
|
294
|
+
chunk_overlap = getattr(instance, 'chunk_overlap', 200) if instance else 200
|
295
|
+
separator = getattr(instance, 'separator', '\\n\\n') if instance else '\\n\\n'
|
296
|
+
|
297
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_FRAMEWORK_CHUNK_SIZE, chunk_size)
|
298
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_FRAMEWORK_CHUNK_OVERLAP, chunk_overlap)
|
299
|
+
scope._span.set_attribute("gen_ai.splitter.separator", separator)
|
300
|
+
scope._span.set_attribute("gen_ai.splitter.efficiency", nodes_created / max(1, chunk_size // 100))
|
301
|
+
|
302
|
+
# === COMPONENT-LEVEL ENHANCED PROCESSING ===
|
303
|
+
|
304
|
+
elif endpoint.startswith("component.text_splitter"):
|
305
|
+
if endpoint == "component.text_splitter.split":
|
306
|
+
text_input = scope._args[0] if scope._args else scope._kwargs.get("text", "")
|
307
|
+
text_metrics = extract_content_metrics(text_input)
|
308
|
+
scope._span.set_attribute("gen_ai.component.input_length", text_metrics.get("char_count", 0))
|
309
|
+
scope._span.set_attribute("gen_ai.component.input_complexity", text_metrics.get("complexity_score", 0))
|
310
|
+
|
311
|
+
if scope._response and hasattr(scope._response, '__len__'):
|
312
|
+
chunks_created = len(scope._response)
|
313
|
+
scope._span.set_attribute("gen_ai.component.chunks_created", chunks_created)
|
314
|
+
scope._span.set_attribute("gen_ai.component.compression_ratio",
|
315
|
+
chunks_created / max(1, text_metrics.get("word_count", 1) // 100))
|
316
|
+
|
317
|
+
elif endpoint.startswith("component.embedding"):
|
318
|
+
if endpoint == "component.embedding.encode":
|
319
|
+
texts = scope._args[0] if scope._args else scope._kwargs.get("texts", [])
|
320
|
+
embedding_count = object_count(texts)
|
321
|
+
scope._span.set_attribute("gen_ai.component.embedding_count", embedding_count)
|
322
|
+
|
323
|
+
if embedding_count > 0 and hasattr(texts, '__iter__'):
|
324
|
+
total_chars = sum(len(str(text)) for text in texts)
|
325
|
+
scope._span.set_attribute("gen_ai.component.total_chars", total_chars)
|
326
|
+
scope._span.set_attribute("gen_ai.component.avg_text_length", total_chars // embedding_count)
|
327
|
+
|
328
|
+
elif endpoint.startswith("component.retrieval"):
|
329
|
+
if endpoint == "component.retrieval.retrieve_nodes":
|
330
|
+
# Enhanced retrieval component analysis
|
331
|
+
query_embedding = scope._args[0] if scope._args else scope._kwargs.get("query_embedding")
|
332
|
+
if query_embedding and hasattr(query_embedding, '__len__'):
|
333
|
+
scope._span.set_attribute("gen_ai.component.embedding_dimension", len(query_embedding))
|
334
|
+
|
335
|
+
similarity_threshold = scope._kwargs.get("similarity_threshold", 0.0)
|
336
|
+
scope._span.set_attribute("gen_ai.component.similarity_threshold", similarity_threshold)
|
337
|
+
|
338
|
+
# === UNIVERSAL ATTRIBUTES ===
|
339
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_CLIENT_OPERATION_DURATION,
|
340
|
+
scope._end_time - scope._start_time)
|
341
|
+
scope._span.set_attribute("gen_ai.operation.endpoint", endpoint)
|
342
|
+
scope._span.set_attribute("gen_ai.framework.version", version)
|
343
|
+
scope._span.set_attribute("gen_ai.operation.success", True)
|
344
|
+
|
345
|
+
scope._span.set_status(Status(StatusCode.OK))
|
346
|
+
|
347
|
+
# Record enhanced metrics
|
348
|
+
if not disable_metrics:
|
349
|
+
record_framework_metrics(metrics, scope._operation_type, SemanticConvention.GEN_AI_SYSTEM_LLAMAINDEX,
|
350
|
+
scope._server_address, scope._server_port, environment, application_name,
|
351
|
+
scope._start_time, scope._end_time)
|
352
|
+
|
353
|
+
def process_llamaindex_response(response, operation_type, server_address, server_port,
|
354
|
+
environment, application_name, metrics, start_time, span,
|
355
|
+
capture_message_content, disable_metrics, version, instance=None,
|
356
|
+
args=None, endpoint=None, **kwargs):
|
357
|
+
"""
|
358
|
+
DOMINANCE EDITION: Process LlamaIndex response with superior observability
|
359
|
+
"""
|
360
|
+
# Create enhanced scope object
|
361
|
+
scope = type("EnhancedScope", (), {})()
|
362
|
+
scope._span = span
|
363
|
+
scope._operation_type = operation_type
|
364
|
+
scope._response = response
|
365
|
+
scope._start_time = start_time
|
366
|
+
scope._server_address = server_address
|
367
|
+
scope._server_port = server_port
|
368
|
+
scope._args = args
|
369
|
+
scope._kwargs = kwargs
|
370
|
+
|
371
|
+
# Process with enhanced telemetry
|
372
|
+
common_llamaindex_logic(
|
373
|
+
scope, environment, application_name, metrics,
|
374
|
+
capture_message_content, disable_metrics, version,
|
375
|
+
instance, endpoint
|
376
|
+
)
|
377
|
+
|
378
|
+
return response
|
379
|
+
|
380
|
+
def _process_index_operations(scope, endpoint, capture_message_content):
|
381
|
+
"""Helper function to process index operations and reduce nesting"""
|
382
|
+
if not hasattr(scope, '_result') or not scope._result:
|
383
|
+
return
|
384
|
+
|
385
|
+
try:
|
386
|
+
if hasattr(scope._result, "source_nodes"):
|
387
|
+
nodes = scope._result.source_nodes
|
388
|
+
elif hasattr(scope._result, "nodes"):
|
389
|
+
nodes = scope._result.nodes
|
390
|
+
else:
|
391
|
+
return
|
392
|
+
|
393
|
+
doc_count = len(nodes)
|
394
|
+
scope._span.set_attribute("gen_ai.index.document_count", doc_count)
|
395
|
+
|
396
|
+
# Process document metadata
|
397
|
+
unique_authors = set()
|
398
|
+
total_content_length = 0
|
399
|
+
|
400
|
+
for node in nodes:
|
401
|
+
if hasattr(node, "metadata") and isinstance(node.metadata, dict):
|
402
|
+
if "author" in node.metadata:
|
403
|
+
unique_authors.add(node.metadata["author"])
|
404
|
+
|
405
|
+
if hasattr(node, "text"):
|
406
|
+
total_content_length += len(str(node.text))
|
407
|
+
|
408
|
+
scope._span.set_attribute("gen_ai.index.total_content_length", total_content_length)
|
409
|
+
scope._span.set_attribute("gen_ai.index.unique_authors", len(unique_authors))
|
410
|
+
scope._span.set_attribute("gen_ai.index.avg_document_size", total_content_length // max(doc_count, 1))
|
411
|
+
except Exception:
|
412
|
+
pass # Don't fail on metadata extraction
|
@@ -19,7 +19,7 @@ def mem0_wrap(gen_ai_endpoint, version, environment, application_name,
|
|
19
19
|
|
20
20
|
This function wraps any given function to measure its execution time,
|
21
21
|
log its operation, and trace its execution using OpenTelemetry.
|
22
|
-
|
22
|
+
|
23
23
|
Parameters:
|
24
24
|
- gen_ai_endpoint (str): A descriptor or name for the endpoint being traced.
|
25
25
|
- version (str): The version of the mem0 application.
|
@@ -49,7 +49,7 @@ def mem0_wrap(gen_ai_endpoint, version, environment, application_name,
|
|
49
49
|
|
50
50
|
Returns:
|
51
51
|
- The result of the wrapped function call.
|
52
|
-
|
52
|
+
|
53
53
|
The wrapper initiates a span with the provided tracer, sets various attributes
|
54
54
|
on the span based on the function's execution and response, and ensures
|
55
55
|
errors are handled and logged appropriately.
|
@@ -35,15 +35,15 @@ class OpenAIInstrumentor(BaseInstrumentor):
|
|
35
35
|
|
36
36
|
# chat completions
|
37
37
|
wrap_function_wrapper(
|
38
|
-
"openai.resources.chat.completions",
|
39
|
-
"Completions.create",
|
38
|
+
"openai.resources.chat.completions",
|
39
|
+
"Completions.create",
|
40
40
|
chat_completions(version, environment, application_name,
|
41
41
|
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
42
42
|
)
|
43
43
|
|
44
44
|
wrap_function_wrapper(
|
45
|
-
"openai.resources.chat.completions",
|
46
|
-
"AsyncCompletions.create",
|
45
|
+
"openai.resources.chat.completions",
|
46
|
+
"AsyncCompletions.create",
|
47
47
|
async_chat_completions(version, environment, application_name,
|
48
48
|
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
49
49
|
)
|
@@ -65,75 +65,75 @@ class OpenAIInstrumentor(BaseInstrumentor):
|
|
65
65
|
|
66
66
|
# responses
|
67
67
|
wrap_function_wrapper(
|
68
|
-
"openai.resources.responses.responses",
|
69
|
-
"Responses.create",
|
68
|
+
"openai.resources.responses.responses",
|
69
|
+
"Responses.create",
|
70
70
|
responses(version, environment, application_name,
|
71
71
|
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
72
72
|
)
|
73
73
|
|
74
74
|
wrap_function_wrapper(
|
75
|
-
"openai.resources.responses.responses",
|
76
|
-
"AsyncResponses.create",
|
75
|
+
"openai.resources.responses.responses",
|
76
|
+
"AsyncResponses.create",
|
77
77
|
async_responses(version, environment, application_name,
|
78
78
|
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
79
79
|
)
|
80
80
|
|
81
81
|
# embeddings
|
82
82
|
wrap_function_wrapper(
|
83
|
-
"openai.resources.embeddings",
|
84
|
-
"Embeddings.create",
|
83
|
+
"openai.resources.embeddings",
|
84
|
+
"Embeddings.create",
|
85
85
|
embedding(version, environment, application_name,
|
86
86
|
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
87
87
|
)
|
88
88
|
|
89
89
|
wrap_function_wrapper(
|
90
|
-
"openai.resources.embeddings",
|
91
|
-
"AsyncEmbeddings.create",
|
90
|
+
"openai.resources.embeddings",
|
91
|
+
"AsyncEmbeddings.create",
|
92
92
|
async_embedding(version, environment, application_name,
|
93
93
|
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
94
94
|
)
|
95
95
|
|
96
96
|
# image generation
|
97
97
|
wrap_function_wrapper(
|
98
|
-
"openai.resources.images",
|
99
|
-
"Images.generate",
|
98
|
+
"openai.resources.images",
|
99
|
+
"Images.generate",
|
100
100
|
image_generate(version, environment, application_name,
|
101
101
|
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
102
102
|
)
|
103
103
|
|
104
104
|
wrap_function_wrapper(
|
105
|
-
"openai.resources.images",
|
106
|
-
"AsyncImages.generate",
|
105
|
+
"openai.resources.images",
|
106
|
+
"AsyncImages.generate",
|
107
107
|
async_image_generate(version, environment, application_name,
|
108
108
|
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
109
109
|
)
|
110
110
|
|
111
111
|
# image variations
|
112
112
|
wrap_function_wrapper(
|
113
|
-
"openai.resources.images",
|
114
|
-
"Images.create_variation",
|
113
|
+
"openai.resources.images",
|
114
|
+
"Images.create_variation",
|
115
115
|
image_variatons(version, environment, application_name,
|
116
116
|
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
117
117
|
)
|
118
118
|
|
119
119
|
wrap_function_wrapper(
|
120
|
-
"openai.resources.images",
|
121
|
-
"AsyncImages.create_variation",
|
120
|
+
"openai.resources.images",
|
121
|
+
"AsyncImages.create_variation",
|
122
122
|
async_image_variations(version, environment, application_name,
|
123
123
|
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
124
124
|
)
|
125
125
|
|
126
126
|
# audio generation
|
127
127
|
wrap_function_wrapper(
|
128
|
-
"openai.resources.audio.speech",
|
129
|
-
"Speech.create",
|
128
|
+
"openai.resources.audio.speech",
|
129
|
+
"Speech.create",
|
130
130
|
audio_create(version, environment, application_name,
|
131
131
|
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
132
132
|
)
|
133
133
|
|
134
134
|
wrap_function_wrapper(
|
135
|
-
"openai.resources.audio.speech",
|
136
|
-
"AsyncSpeech.create",
|
135
|
+
"openai.resources.audio.speech",
|
136
|
+
"AsyncSpeech.create",
|
137
137
|
async_audio_create(version, environment, application_name,
|
138
138
|
tracer, pricing_info, capture_message_content, metrics, disable_metrics),
|
139
139
|
)
|