opentelemetry-instrumentation-llamaindex 0.26.4__tar.gz → 0.27.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (17) hide show
  1. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/PKG-INFO +2 -2
  2. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/opentelemetry/instrumentation/llamaindex/__init__.py +13 -8
  3. opentelemetry_instrumentation_llamaindex-0.27.0/opentelemetry/instrumentation/llamaindex/dispatcher_wrapper.py +219 -0
  4. opentelemetry_instrumentation_llamaindex-0.27.0/opentelemetry/instrumentation/llamaindex/version.py +1 -0
  5. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/pyproject.toml +5 -5
  6. opentelemetry_instrumentation_llamaindex-0.26.4/opentelemetry/instrumentation/llamaindex/version.py +0 -1
  7. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/README.md +0 -0
  8. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/opentelemetry/instrumentation/llamaindex/base_agent_instrumentor.py +0 -0
  9. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/opentelemetry/instrumentation/llamaindex/base_embedding_instrumentor.py +0 -0
  10. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/opentelemetry/instrumentation/llamaindex/base_retriever_instrumentor.py +0 -0
  11. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/opentelemetry/instrumentation/llamaindex/base_synthesizer_instrumentor.py +0 -0
  12. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/opentelemetry/instrumentation/llamaindex/base_tool_instrumentor.py +0 -0
  13. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/opentelemetry/instrumentation/llamaindex/config.py +0 -0
  14. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/opentelemetry/instrumentation/llamaindex/custom_llm_instrumentor.py +0 -0
  15. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/opentelemetry/instrumentation/llamaindex/query_pipeline_instrumentor.py +0 -0
  16. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/opentelemetry/instrumentation/llamaindex/retriever_query_engine_instrumentor.py +0 -0
  17. {opentelemetry_instrumentation_llamaindex-0.26.4 → opentelemetry_instrumentation_llamaindex-0.27.0}/opentelemetry/instrumentation/llamaindex/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: opentelemetry-instrumentation-llamaindex
3
- Version: 0.26.4
3
+ Version: 0.27.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
@@ -18,7 +18,7 @@ Requires-Dist: inflection (>=0.5.1,<0.6.0)
18
18
  Requires-Dist: opentelemetry-api (>=1.26.0,<2.0.0)
19
19
  Requires-Dist: opentelemetry-instrumentation (>=0.47b0,<0.48)
20
20
  Requires-Dist: opentelemetry-semantic-conventions (>=0.47b0,<0.48)
21
- Requires-Dist: opentelemetry-semantic-conventions-ai (==0.4.0)
21
+ Requires-Dist: opentelemetry-semantic-conventions-ai (==0.4.1)
22
22
  Project-URL: Repository, https://github.com/traceloop/openllmetry/tree/main/packages/opentelemetry-instrumentation-llamaindex
23
23
  Description-Content-Type: text/markdown
24
24
 
@@ -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,219 @@
1
+ import inspect
2
+ import json
3
+ import re
4
+ from typing import Any, 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.llm import (
12
+ LLMChatEndEvent,
13
+ LLMChatStartEvent,
14
+ LLMPredictEndEvent,
15
+ )
16
+ from llama_index.core.instrumentation.event_handlers import BaseEventHandler
17
+ from llama_index.core.instrumentation.span_handlers import BaseSpanHandler
18
+ from opentelemetry import context as context_api
19
+ from opentelemetry.instrumentation.llamaindex.utils import (
20
+ JSONEncoder,
21
+ dont_throw,
22
+ should_send_prompts,
23
+ )
24
+ from opentelemetry.semconv_ai import (
25
+ SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION_KEY,
26
+ SpanAttributes,
27
+ TraceloopSpanKindValues,
28
+ )
29
+ from opentelemetry.trace import get_current_span, set_span_in_context, Tracer
30
+ from opentelemetry.trace.span import Span
31
+
32
+
33
+ LLAMA_INDEX_REGEX = re.compile(r"^([a-zA-Z]+)\.")
34
+
35
+
36
+ def instrument_with_dispatcher(tracer: Tracer):
37
+ dispatcher = get_dispatcher()
38
+ openll_span_handler = OpenLLSpanHandler(tracer)
39
+ dispatcher.add_span_handler(openll_span_handler)
40
+ dispatcher.add_event_handler(OpenLLEventHandler(openll_span_handler))
41
+
42
+
43
+ @dont_throw
44
+ def _set_llm_chat_request(event, span) -> None:
45
+ model_dict = event.model_dict
46
+ span.set_attribute(SpanAttributes.LLM_REQUEST_MODEL, model_dict.get("model"))
47
+ span.set_attribute(
48
+ SpanAttributes.LLM_REQUEST_TEMPERATURE, model_dict.get("temperature")
49
+ )
50
+ if should_send_prompts():
51
+ for idx, message in enumerate(event.messages):
52
+ span.set_attribute(
53
+ f"{SpanAttributes.LLM_PROMPTS}.{idx}.role", message.role.value
54
+ )
55
+ span.set_attribute(
56
+ f"{SpanAttributes.LLM_PROMPTS}.{idx}.content", message.content
57
+ )
58
+
59
+
60
+ @dont_throw
61
+ def _set_llm_chat_response(event, span) -> None:
62
+ if should_send_prompts():
63
+ for idx, message in enumerate(event.messages):
64
+ span.set_attribute(
65
+ f"{SpanAttributes.LLM_PROMPTS}.{idx}.role", message.role.value
66
+ )
67
+ span.set_attribute(
68
+ f"{SpanAttributes.LLM_PROMPTS}.{idx}.content", message.content
69
+ )
70
+ response = event.response
71
+ span.set_attribute(
72
+ f"{SpanAttributes.LLM_COMPLETIONS}.0.role",
73
+ response.message.role.value,
74
+ )
75
+ span.set_attribute(
76
+ f"{SpanAttributes.LLM_COMPLETIONS}.0.content",
77
+ response.message.content,
78
+ )
79
+ if not (raw := response.raw):
80
+ return
81
+ # raw can be Any, not just ChatCompletion
82
+ span.set_attribute(
83
+ SpanAttributes.LLM_RESPONSE_MODEL,
84
+ raw.get("model") if "model" in raw else raw.model,
85
+ )
86
+ if usage := raw.get("usage") if "usage" in raw else raw.usage:
87
+ span.set_attribute(
88
+ SpanAttributes.LLM_USAGE_COMPLETION_TOKENS, usage.completion_tokens
89
+ )
90
+ span.set_attribute(
91
+ SpanAttributes.LLM_USAGE_PROMPT_TOKENS, usage.prompt_tokens
92
+ )
93
+ span.set_attribute(
94
+ SpanAttributes.LLM_USAGE_TOTAL_TOKENS, usage.total_tokens
95
+ )
96
+
97
+
98
+ @dont_throw
99
+ def _set_llm_predict_response(event, span) -> None:
100
+ if should_send_prompts():
101
+ span.set_attribute(
102
+ f"{SpanAttributes.LLM_COMPLETIONS}.role",
103
+ MessageRole.ASSISTANT.value,
104
+ )
105
+ span.set_attribute(
106
+ f"{SpanAttributes.LLM_COMPLETIONS}.content",
107
+ event.output,
108
+ )
109
+
110
+
111
+ @dataclass
112
+ class SpanHolder:
113
+ span: Span
114
+ token: Any
115
+ context: context_api.context.Context
116
+
117
+
118
+ class OpenLLSpanHandler(BaseSpanHandler[SpanHolder]):
119
+ _tracer: Tracer = PrivateAttr()
120
+
121
+ def __init__(self, tracer: Tracer):
122
+ super().__init__()
123
+ self._tracer = tracer
124
+
125
+ def new_span(
126
+ self, id_: str, bound_args: inspect.BoundArguments, parent_span_id: Optional[str], **kwargs
127
+ ) -> Optional[SpanHolder]:
128
+ """Create a span."""
129
+ parent = self.open_spans.get(parent_span_id)
130
+ kind = (
131
+ TraceloopSpanKindValues.TASK.value
132
+ if parent
133
+ else TraceloopSpanKindValues.WORKFLOW.value
134
+ )
135
+ # Take the class name from id_ where id_ is e.g.
136
+ # 'SentenceSplitter.split_text_metadata_aware-a2f2a780-2fa6-4682-a88e-80dc1f1ebe6a'
137
+ class_name = LLAMA_INDEX_REGEX.match(id_).groups()[0]
138
+ span_name = f"{class_name}.{kind}"
139
+ span = self._tracer.start_span(
140
+ span_name,
141
+ context=parent.context if parent else None,
142
+ )
143
+ current_context = set_span_in_context(
144
+ span, context=parent.context if parent else None
145
+ )
146
+ current_context = context_api.set_value(
147
+ SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION_KEY,
148
+ True,
149
+ current_context,
150
+ )
151
+ token = context_api.attach(current_context)
152
+ span.set_attribute(SpanAttributes.TRACELOOP_SPAN_KIND, kind)
153
+ span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, span_name)
154
+ try:
155
+ if should_send_prompts():
156
+ span.set_attribute(
157
+ SpanAttributes.TRACELOOP_ENTITY_INPUT,
158
+ json.dumps(bound_args.arguments, cls=JSONEncoder)
159
+ )
160
+ except Exception:
161
+ pass
162
+
163
+ return SpanHolder(span, token, current_context)
164
+
165
+ def prepare_to_exit_span(
166
+ self, id_: str, result: Optional[Any] = None, **kwargs
167
+ ) -> SpanHolder:
168
+ """Logic for preparing to drop a span."""
169
+ span_holder = self.open_spans[id_]
170
+ # I know it's messy, but the typing of result is messy and couldn't find a better way
171
+ # to get a dictionary I can then use to remove keys
172
+ try:
173
+ serialized_output = json.dumps(result, cls=JSONEncoder)
174
+ # we need to remove some keys like source_nodes as they can be very large
175
+ output = json.loads(serialized_output)
176
+ if "source_nodes" in output:
177
+ del output["source_nodes"]
178
+ if should_send_prompts():
179
+ span_holder.span.set_attribute(
180
+ SpanAttributes.TRACELOOP_ENTITY_OUTPUT,
181
+ json.dumps(output, cls=JSONEncoder),
182
+ )
183
+ except Exception:
184
+ pass
185
+
186
+ span_holder.span.end()
187
+ context_api.detach(span_holder.token)
188
+ with self.lock:
189
+ self.completed_spans += [span_holder]
190
+ return span_holder
191
+
192
+ def prepare_to_drop_span(
193
+ self, id_: str, err: Optional[Exception], **kwargs
194
+ ) -> Optional[SpanHolder]:
195
+ """Logic for dropping a span."""
196
+ if id_ in self.open_spans:
197
+ with self.lock:
198
+ span_holder = self.open_spans[id_]
199
+ self.dropped_spans += [span_holder]
200
+ return span_holder
201
+ return None
202
+
203
+
204
+ class OpenLLEventHandler(BaseEventHandler):
205
+ _span_handler: OpenLLSpanHandler = PrivateAttr()
206
+
207
+ def __init__(self, span_handler: OpenLLSpanHandler):
208
+ super().__init__()
209
+ self._span_handler = span_handler
210
+
211
+ def handle(self, event: BaseEvent, **kwargs) -> Any:
212
+ span = get_current_span()
213
+ # use case with class_pattern if support for 3.9 is dropped
214
+ if isinstance(event, LLMChatStartEvent):
215
+ _set_llm_chat_request(event, span)
216
+ elif isinstance(event, LLMChatEndEvent):
217
+ _set_llm_chat_response(event, span)
218
+ elif isinstance(event, LLMPredictEndEvent):
219
+ _set_llm_predict_response(event, span)
@@ -8,7 +8,7 @@ show_missing = true
8
8
 
9
9
  [tool.poetry]
10
10
  name = "opentelemetry-instrumentation-llamaindex"
11
- version = "0.26.4"
11
+ version = "0.27.0"
12
12
  description = "OpenTelemetry LlamaIndex instrumentation"
13
13
  authors = [
14
14
  "Gal Kleinman <gal@traceloop.com>",
@@ -27,7 +27,7 @@ python = ">=3.9,<4"
27
27
  opentelemetry-api = "^1.26.0"
28
28
  opentelemetry-instrumentation = "^0.47b0"
29
29
  opentelemetry-semantic-conventions = "^0.47b0"
30
- opentelemetry-semantic-conventions-ai = "0.4.0"
30
+ opentelemetry-semantic-conventions-ai = "0.4.1"
31
31
  inflection = "^0.5.1"
32
32
 
33
33
  [tool.poetry.group.dev.dependencies]
@@ -43,9 +43,9 @@ openai = "^1.35.0"
43
43
  opentelemetry-sdk = "^1.23.0"
44
44
  llama-index = "^0.10.46"
45
45
  llama-index-postprocessor-cohere-rerank = "^0.1.7"
46
- opentelemetry-instrumentation-openai = "==0.26.4"
47
- opentelemetry-instrumentation-cohere = "==0.26.4"
48
- opentelemetry-instrumentation-chromadb = "==0.26.4"
46
+ opentelemetry-instrumentation-openai = "==0.27.0"
47
+ opentelemetry-instrumentation-cohere = "==0.27.0"
48
+ opentelemetry-instrumentation-chromadb = "==0.27.0"
49
49
  sqlalchemy = "^2.0.31"
50
50
  llama-index-agent-openai = ">=0.2.7,<0.3.0"
51
51
  llama-index-vector-stores-chroma = "^0.1.9"