opentelemetry-instrumentation-llamaindex 0.26.5__tar.gz → 0.28.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.
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/PKG-INFO +1 -1
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/opentelemetry/instrumentation/llamaindex/__init__.py +13 -8
- opentelemetry_instrumentation_llamaindex-0.28.0/opentelemetry/instrumentation/llamaindex/dispatcher_wrapper.py +271 -0
- opentelemetry_instrumentation_llamaindex-0.28.0/opentelemetry/instrumentation/llamaindex/version.py +1 -0
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/pyproject.toml +6 -5
- opentelemetry_instrumentation_llamaindex-0.26.5/opentelemetry/instrumentation/llamaindex/version.py +0 -1
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/README.md +0 -0
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/opentelemetry/instrumentation/llamaindex/base_agent_instrumentor.py +0 -0
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/opentelemetry/instrumentation/llamaindex/base_embedding_instrumentor.py +0 -0
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/opentelemetry/instrumentation/llamaindex/base_retriever_instrumentor.py +0 -0
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/opentelemetry/instrumentation/llamaindex/base_synthesizer_instrumentor.py +0 -0
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/opentelemetry/instrumentation/llamaindex/base_tool_instrumentor.py +0 -0
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/opentelemetry/instrumentation/llamaindex/config.py +0 -0
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/opentelemetry/instrumentation/llamaindex/custom_llm_instrumentor.py +0 -0
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/opentelemetry/instrumentation/llamaindex/query_pipeline_instrumentor.py +0 -0
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.0}/opentelemetry/instrumentation/llamaindex/retriever_query_engine_instrumentor.py +0 -0
- {opentelemetry_instrumentation_llamaindex-0.26.5 → opentelemetry_instrumentation_llamaindex-0.28.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.
|
|
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,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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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)
|
opentelemetry_instrumentation_llamaindex-0.28.0/opentelemetry/instrumentation/llamaindex/version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.28.0"
|
|
@@ -8,7 +8,7 @@ show_missing = true
|
|
|
8
8
|
|
|
9
9
|
[tool.poetry]
|
|
10
10
|
name = "opentelemetry-instrumentation-llamaindex"
|
|
11
|
-
version = "0.
|
|
11
|
+
version = "0.28.0"
|
|
12
12
|
description = "OpenTelemetry LlamaIndex instrumentation"
|
|
13
13
|
authors = [
|
|
14
14
|
"Gal Kleinman <gal@traceloop.com>",
|
|
@@ -41,14 +41,15 @@ pytest-asyncio = "^0.23.7"
|
|
|
41
41
|
chromadb = ">=0.4.22,<0.6.0"
|
|
42
42
|
openai = "^1.35.0"
|
|
43
43
|
opentelemetry-sdk = "^1.23.0"
|
|
44
|
-
llama-index = "^0.10.
|
|
44
|
+
llama-index = "^0.10.59"
|
|
45
45
|
llama-index-postprocessor-cohere-rerank = "^0.1.7"
|
|
46
|
-
opentelemetry-instrumentation-openai = "==0.
|
|
47
|
-
opentelemetry-instrumentation-cohere = "==0.
|
|
48
|
-
opentelemetry-instrumentation-chromadb = "==0.
|
|
46
|
+
opentelemetry-instrumentation-openai = "==0.28.0"
|
|
47
|
+
opentelemetry-instrumentation-cohere = "==0.28.0"
|
|
48
|
+
opentelemetry-instrumentation-chromadb = "==0.28.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"
|
|
52
|
+
llama-index-llms-cohere = "^0.2.0"
|
|
52
53
|
|
|
53
54
|
[build-system]
|
|
54
55
|
requires = ["poetry-core"]
|
opentelemetry_instrumentation_llamaindex-0.26.5/opentelemetry/instrumentation/llamaindex/version.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.26.5"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|