opentelemetry-instrumentation-llamaindex 0.26.5__py3-none-any.whl → 0.28.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.

Potentially problematic release.


This version of opentelemetry-instrumentation-llamaindex might be problematic. Click here for more details.

@@ -1,6 +1,7 @@
1
1
  """OpenTelemetry LlamaIndex instrumentation"""
2
2
 
3
3
  import logging
4
+ from importlib.metadata import version as import_version
4
5
  from typing import Collection
5
6
 
6
7
  from opentelemetry.instrumentation.llamaindex.config import Config
@@ -33,6 +34,7 @@ from opentelemetry.instrumentation.llamaindex.query_pipeline_instrumentor import
33
34
  QueryPipelineInstrumentor,
34
35
  )
35
36
  from opentelemetry.instrumentation.llamaindex.version import __version__
37
+ from opentelemetry.instrumentation.llamaindex.dispatcher_wrapper import instrument_with_dispatcher
36
38
 
37
39
  logger = logging.getLogger(__name__)
38
40
 
@@ -53,14 +55,17 @@ class LlamaIndexInstrumentor(BaseInstrumentor):
53
55
  tracer_provider = kwargs.get("tracer_provider")
54
56
  tracer = get_tracer(__name__, __version__, tracer_provider)
55
57
 
56
- RetrieverQueryEngineInstrumentor(tracer).instrument()
57
- BaseRetrieverInstrumentor(tracer).instrument()
58
- BaseSynthesizerInstrumentor(tracer).instrument()
59
- BaseEmbeddingInstrumentor(tracer).instrument()
60
- CustomLLMInstrumentor(tracer).instrument()
61
- QueryPipelineInstrumentor(tracer).instrument()
62
- BaseAgentInstrumentor(tracer).instrument()
63
- BaseToolInstrumentor(tracer).instrument()
58
+ if import_version("llama-index") >= "0.10.20":
59
+ instrument_with_dispatcher(tracer)
60
+ else:
61
+ RetrieverQueryEngineInstrumentor(tracer).instrument()
62
+ BaseRetrieverInstrumentor(tracer).instrument()
63
+ BaseSynthesizerInstrumentor(tracer).instrument()
64
+ BaseEmbeddingInstrumentor(tracer).instrument()
65
+ CustomLLMInstrumentor(tracer).instrument()
66
+ QueryPipelineInstrumentor(tracer).instrument()
67
+ BaseAgentInstrumentor(tracer).instrument()
68
+ BaseToolInstrumentor(tracer).instrument()
64
69
 
65
70
  def _uninstrument(self, **kwargs):
66
71
  pass
@@ -0,0 +1,271 @@
1
+ import inspect
2
+ import json
3
+ import re
4
+ from typing import Any, Dict, Optional
5
+ from dataclasses import dataclass
6
+
7
+ from llama_index.core.bridge.pydantic import PrivateAttr
8
+ from llama_index.core.base.llms.types import MessageRole
9
+ from llama_index.core.instrumentation import get_dispatcher
10
+ from llama_index.core.instrumentation.events import BaseEvent
11
+ from llama_index.core.instrumentation.events.agent import AgentToolCallEvent
12
+ from llama_index.core.instrumentation.events.embedding import EmbeddingStartEvent
13
+ from llama_index.core.instrumentation.events.llm import (
14
+ LLMChatEndEvent,
15
+ LLMChatStartEvent,
16
+ LLMPredictEndEvent,
17
+ )
18
+ from llama_index.core.instrumentation.events.rerank import ReRankStartEvent
19
+ from llama_index.core.instrumentation.event_handlers import BaseEventHandler
20
+ from llama_index.core.instrumentation.span_handlers import BaseSpanHandler
21
+ from opentelemetry import context as context_api
22
+ from opentelemetry.instrumentation.llamaindex.utils import (
23
+ JSONEncoder,
24
+ dont_throw,
25
+ should_send_prompts,
26
+ )
27
+ from opentelemetry.semconv_ai import (
28
+ SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION_KEY,
29
+ LLMRequestTypeValues,
30
+ SpanAttributes,
31
+ TraceloopSpanKindValues,
32
+ )
33
+ from opentelemetry.trace import get_current_span, set_span_in_context, Tracer
34
+ from opentelemetry.trace.span import Span
35
+
36
+
37
+ LLAMA_INDEX_REGEX = re.compile(r"^([a-zA-Z]+)\.")
38
+
39
+
40
+ def instrument_with_dispatcher(tracer: Tracer):
41
+ dispatcher = get_dispatcher()
42
+ openll_span_handler = OpenLLSpanHandler(tracer)
43
+ dispatcher.add_span_handler(openll_span_handler)
44
+ dispatcher.add_event_handler(OpenLLEventHandler(openll_span_handler))
45
+
46
+
47
+ @dont_throw
48
+ def _set_llm_chat_request(event, span) -> None:
49
+ model_dict = event.model_dict
50
+ span.set_attribute(SpanAttributes.LLM_REQUEST_MODEL, model_dict.get("model"))
51
+ span.set_attribute(
52
+ SpanAttributes.LLM_REQUEST_TEMPERATURE, model_dict.get("temperature")
53
+ )
54
+ if should_send_prompts():
55
+ for idx, message in enumerate(event.messages):
56
+ span.set_attribute(
57
+ f"{SpanAttributes.LLM_PROMPTS}.{idx}.role", message.role.value
58
+ )
59
+ span.set_attribute(
60
+ f"{SpanAttributes.LLM_PROMPTS}.{idx}.content", message.content
61
+ )
62
+
63
+
64
+ @dont_throw
65
+ def _set_llm_chat_response(event, span) -> None:
66
+ response = event.response
67
+ if should_send_prompts():
68
+ for idx, message in enumerate(event.messages):
69
+ span.set_attribute(
70
+ f"{SpanAttributes.LLM_PROMPTS}.{idx}.role", message.role.value
71
+ )
72
+ span.set_attribute(
73
+ f"{SpanAttributes.LLM_PROMPTS}.{idx}.content", message.content
74
+ )
75
+ span.set_attribute(
76
+ f"{SpanAttributes.LLM_COMPLETIONS}.0.role",
77
+ response.message.role.value,
78
+ )
79
+ span.set_attribute(
80
+ f"{SpanAttributes.LLM_COMPLETIONS}.0.content",
81
+ response.message.content,
82
+ )
83
+ if not (raw := response.raw):
84
+ return
85
+ span.set_attribute(
86
+ SpanAttributes.LLM_RESPONSE_MODEL,
87
+ raw.get("model") if "model" in raw else raw.model, # raw can be Any, not just ChatCompletion
88
+ )
89
+ if usage := raw.get("usage") if "usage" in raw else raw.usage:
90
+ span.set_attribute(
91
+ SpanAttributes.LLM_USAGE_COMPLETION_TOKENS, usage.completion_tokens
92
+ )
93
+ span.set_attribute(
94
+ SpanAttributes.LLM_USAGE_PROMPT_TOKENS, usage.prompt_tokens
95
+ )
96
+ span.set_attribute(
97
+ SpanAttributes.LLM_USAGE_TOTAL_TOKENS, usage.total_tokens
98
+ )
99
+ if choices := raw.choices:
100
+ span.set_attribute(
101
+ SpanAttributes.LLM_RESPONSE_FINISH_REASON, choices[0].finish_reason
102
+ )
103
+
104
+
105
+ @dont_throw
106
+ def _set_llm_predict_response(event, span) -> None:
107
+ if should_send_prompts():
108
+ span.set_attribute(
109
+ f"{SpanAttributes.LLM_COMPLETIONS}.role",
110
+ MessageRole.ASSISTANT.value,
111
+ )
112
+ span.set_attribute(
113
+ f"{SpanAttributes.LLM_COMPLETIONS}.content",
114
+ event.output,
115
+ )
116
+
117
+
118
+ @dont_throw
119
+ def _set_embedding(event, span) -> None:
120
+ model_dict = event.model_dict
121
+ span.set_attribute(
122
+ f"{LLMRequestTypeValues.EMBEDDING.value}.model_name",
123
+ model_dict.get("model_name"),
124
+ )
125
+
126
+
127
+ @dont_throw
128
+ def _set_rerank(event, span) -> None:
129
+ span.set_attribute(
130
+ f"{LLMRequestTypeValues.RERANK.value}.model_name",
131
+ event.model_name,
132
+ )
133
+ span.set_attribute(
134
+ f"{LLMRequestTypeValues.RERANK.value}.top_n",
135
+ event.top_n,
136
+ )
137
+ if should_send_prompts():
138
+ span.set_attribute(
139
+ f"{LLMRequestTypeValues.RERANK.value}.query",
140
+ event.query.query_str,
141
+ )
142
+
143
+
144
+ @dont_throw
145
+ def _set_tool(event, span) -> None:
146
+ span.set_attribute("tool.name", event.tool.name)
147
+ span.set_attribute("tool.description", event.tool.description)
148
+ span.set_attribute("tool.arguments", event.arguments)
149
+
150
+
151
+ @dataclass
152
+ class SpanHolder:
153
+ span: Span
154
+ token: Any
155
+ context: context_api.context.Context
156
+
157
+
158
+ class OpenLLSpanHandler(BaseSpanHandler[SpanHolder]):
159
+ _tracer: Tracer = PrivateAttr()
160
+
161
+ def __init__(self, tracer: Tracer):
162
+ super().__init__()
163
+ self._tracer = tracer
164
+
165
+ def new_span(
166
+ self,
167
+ id_: str,
168
+ bound_args: inspect.BoundArguments,
169
+ instance: Optional[Any] = None,
170
+ parent_span_id: Optional[str] = None,
171
+ tags: Optional[Dict[str, Any]] = None,
172
+ **kwargs: Any,
173
+ ) -> Optional[SpanHolder]:
174
+ """Create a span."""
175
+ parent = self.open_spans.get(parent_span_id)
176
+ kind = (
177
+ TraceloopSpanKindValues.TASK.value
178
+ if parent
179
+ else TraceloopSpanKindValues.WORKFLOW.value
180
+ )
181
+ # Take the class name from id_ where id_ is e.g.
182
+ # 'SentenceSplitter.split_text_metadata_aware-a2f2a780-2fa6-4682-a88e-80dc1f1ebe6a'
183
+ class_name = LLAMA_INDEX_REGEX.match(id_).groups()[0]
184
+ span_name = f"{class_name}.{kind}"
185
+ span = self._tracer.start_span(
186
+ span_name,
187
+ context=parent.context if parent else None,
188
+ )
189
+ current_context = set_span_in_context(
190
+ span, context=parent.context if parent else None
191
+ )
192
+ current_context = context_api.set_value(
193
+ SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION_KEY,
194
+ True,
195
+ current_context,
196
+ )
197
+ token = context_api.attach(current_context)
198
+ span.set_attribute(SpanAttributes.TRACELOOP_SPAN_KIND, kind)
199
+ span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, span_name)
200
+ try:
201
+ if should_send_prompts():
202
+ span.set_attribute(
203
+ SpanAttributes.TRACELOOP_ENTITY_INPUT,
204
+ json.dumps(bound_args.arguments, cls=JSONEncoder)
205
+ )
206
+ except Exception:
207
+ pass
208
+
209
+ return SpanHolder(span, token, current_context)
210
+
211
+ def prepare_to_exit_span(
212
+ self, id_: str, result: Optional[Any] = None, **kwargs
213
+ ) -> SpanHolder:
214
+ """Logic for preparing to drop a span."""
215
+ span_holder = self.open_spans[id_]
216
+ # I know it's messy, but the typing of result is messy and couldn't find a better way
217
+ # to get a dictionary I can then use to remove keys
218
+ try:
219
+ serialized_output = json.dumps(result, cls=JSONEncoder)
220
+ # we need to remove some keys like source_nodes as they can be very large
221
+ output = json.loads(serialized_output)
222
+ if "source_nodes" in output:
223
+ del output["source_nodes"]
224
+ if should_send_prompts():
225
+ span_holder.span.set_attribute(
226
+ SpanAttributes.TRACELOOP_ENTITY_OUTPUT,
227
+ json.dumps(output, cls=JSONEncoder),
228
+ )
229
+ except Exception:
230
+ pass
231
+
232
+ span_holder.span.end()
233
+ context_api.detach(span_holder.token)
234
+ with self.lock:
235
+ self.completed_spans += [span_holder]
236
+ return span_holder
237
+
238
+ def prepare_to_drop_span(
239
+ self, id_: str, err: Optional[Exception], **kwargs
240
+ ) -> Optional[SpanHolder]:
241
+ """Logic for dropping a span."""
242
+ if id_ in self.open_spans:
243
+ with self.lock:
244
+ span_holder = self.open_spans[id_]
245
+ self.dropped_spans += [span_holder]
246
+ return span_holder
247
+ return None
248
+
249
+
250
+ class OpenLLEventHandler(BaseEventHandler):
251
+ _span_handler: OpenLLSpanHandler = PrivateAttr()
252
+
253
+ def __init__(self, span_handler: OpenLLSpanHandler):
254
+ super().__init__()
255
+ self._span_handler = span_handler
256
+
257
+ def handle(self, event: BaseEvent, **kwargs) -> Any:
258
+ span = get_current_span()
259
+ # use case with class_pattern if support for 3.9 is dropped
260
+ if isinstance(event, LLMChatStartEvent):
261
+ _set_llm_chat_request(event, span)
262
+ elif isinstance(event, LLMChatEndEvent):
263
+ _set_llm_chat_response(event, span)
264
+ elif isinstance(event, LLMPredictEndEvent):
265
+ _set_llm_predict_response(event, span)
266
+ elif isinstance(event, EmbeddingStartEvent):
267
+ _set_embedding(event, span)
268
+ elif isinstance(event, ReRankStartEvent):
269
+ _set_rerank(event, span)
270
+ elif isinstance(event, AgentToolCallEvent):
271
+ _set_tool(event, span)
@@ -1 +1 @@
1
- __version__ = "0.26.5"
1
+ __version__ = "0.28.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: opentelemetry-instrumentation-llamaindex
3
- Version: 0.26.5
3
+ Version: 0.28.0
4
4
  Summary: OpenTelemetry LlamaIndex instrumentation
5
5
  Home-page: https://github.com/traceloop/openllmetry/tree/main/packages/opentelemetry-instrumentation-llamaindex
6
6
  License: Apache-2.0
@@ -1,4 +1,4 @@
1
- opentelemetry/instrumentation/llamaindex/__init__.py,sha256=xVgDrVrDSVLC93OAk9hIvzBybBoKCtquGSV2-2Y_P5E,2318
1
+ opentelemetry/instrumentation/llamaindex/__init__.py,sha256=m5ycp9c8Zt5gT-G5QkUC-0bWb0wGiTsjxAKHSZUuSs8,2622
2
2
  opentelemetry/instrumentation/llamaindex/base_agent_instrumentor.py,sha256=WvPuSECoouFQ3MzItWDcnWVQXULjskfLxE-zyH5HrTg,2645
3
3
  opentelemetry/instrumentation/llamaindex/base_embedding_instrumentor.py,sha256=SsbAhMtIiC5Djrc32LOkCMxETJlz4653aeC1x0uXm4g,2207
4
4
  opentelemetry/instrumentation/llamaindex/base_retriever_instrumentor.py,sha256=aHuH7VNz6D1fWsc0jXV3U3vbqG-Mo20mhHIHcfJbuzo,2346
@@ -6,11 +6,12 @@ opentelemetry/instrumentation/llamaindex/base_synthesizer_instrumentor.py,sha256
6
6
  opentelemetry/instrumentation/llamaindex/base_tool_instrumentor.py,sha256=mdPai098XOqra-BnfdN3amn9WFX06FEf7N9mVqZcJ_c,2758
7
7
  opentelemetry/instrumentation/llamaindex/config.py,sha256=CtypZov_ytI9nSrfN9lWnjcufbAR9sfkXRA0OstDEUw,42
8
8
  opentelemetry/instrumentation/llamaindex/custom_llm_instrumentor.py,sha256=3YIcVdVGrWOYpPzZCyXnXyi-g1AyAQQDhD1XGLgyc_Q,5950
9
+ opentelemetry/instrumentation/llamaindex/dispatcher_wrapper.py,sha256=Hc8mZRXDjxQgEz8t2h_xVti6aGRVePo2QW8nMxlJMi8,9395
9
10
  opentelemetry/instrumentation/llamaindex/query_pipeline_instrumentor.py,sha256=PfUens1GisvbU98TLXEJ8_ALWGhnbOdsQkMwhFom8ZA,2496
10
11
  opentelemetry/instrumentation/llamaindex/retriever_query_engine_instrumentor.py,sha256=OtQ7uZckFtzq9mzqSlKDhvO-Uffl99axuZ2TJXCqDRQ,2627
11
12
  opentelemetry/instrumentation/llamaindex/utils.py,sha256=7NfuSbIf5Uohxo79AUM_gB-8RQtxgUO5glCWzXHeueQ,2349
12
- opentelemetry/instrumentation/llamaindex/version.py,sha256=Q5QQA3kVN0Uxvuxo_emQJjjelCa7wzxU2n7kQ-gW-aM,23
13
- opentelemetry_instrumentation_llamaindex-0.26.5.dist-info/METADATA,sha256=UNC_TlPB04wQ5n2-tHizRl6VNxXibO8EcEMI9rtRhUU,2285
14
- opentelemetry_instrumentation_llamaindex-0.26.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
15
- opentelemetry_instrumentation_llamaindex-0.26.5.dist-info/entry_points.txt,sha256=gtV40W4oFCp6VNvgowTKa0zQjfIrvfdlYflgGdSsA5A,106
16
- opentelemetry_instrumentation_llamaindex-0.26.5.dist-info/RECORD,,
13
+ opentelemetry/instrumentation/llamaindex/version.py,sha256=MRQGtOXBhcDKeeNOL0LiB-cllo6kfd8_KGJOvaDp0XQ,23
14
+ opentelemetry_instrumentation_llamaindex-0.28.0.dist-info/METADATA,sha256=mc-_i-VdFHtreNaMD56Y0n-fzaSOUBn_Xf0ijUCiI1c,2285
15
+ opentelemetry_instrumentation_llamaindex-0.28.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
16
+ opentelemetry_instrumentation_llamaindex-0.28.0.dist-info/entry_points.txt,sha256=gtV40W4oFCp6VNvgowTKa0zQjfIrvfdlYflgGdSsA5A,106
17
+ opentelemetry_instrumentation_llamaindex-0.28.0.dist-info/RECORD,,