Skip to content

Commit c284db2

Browse files
feat(llamaindex): support llamaparse instrumentation (#3103)
Co-authored-by: Nir Gazit <[email protected]>
1 parent a84a347 commit c284db2

File tree

13 files changed

+1972
-189
lines changed

13 files changed

+1972
-189
lines changed

packages/opentelemetry-instrumentation-chromadb/poetry.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/opentelemetry-instrumentation-cohere/poetry.lock

Lines changed: 5 additions & 43 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/opentelemetry-instrumentation-llamaindex/opentelemetry/instrumentation/llamaindex/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
from opentelemetry.instrumentation.llamaindex.dispatcher_wrapper import (
2929
instrument_with_dispatcher,
3030
)
31+
from opentelemetry.instrumentation.llamaindex.llamaparse_instrumentor import (
32+
LlamaParseInstrumentor,
33+
)
3134
from opentelemetry.instrumentation.llamaindex.query_pipeline_instrumentor import (
3235
QueryPipelineInstrumentor,
3336
)
@@ -90,6 +93,12 @@ def apply_instrumentation(name, **kwargs):
9093
BaseAgentInstrumentor(tracer).instrument()
9194
BaseToolInstrumentor(tracer).instrument()
9295

96+
# LlamaParse instrumentation doesn't work for all versions
97+
try:
98+
LlamaParseInstrumentor(tracer).instrument()
99+
except Exception:
100+
pass
101+
93102

94103
class LlamaIndexInstrumentorCore(BaseInstrumentor):
95104
"""An instrumentor for core LlamaIndex SDK."""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
from wrapt import wrap_function_wrapper
2+
3+
from opentelemetry.instrumentation.llamaindex.utils import (
4+
_with_tracer_wrapper,
5+
process_request,
6+
process_response,
7+
start_as_current_span_async,
8+
)
9+
from opentelemetry.semconv_ai import SpanAttributes, TraceloopSpanKindValues
10+
11+
MODULE_NAME = "llama_parse"
12+
CLASS_NAME = "LlamaParse"
13+
TASK_NAME = "llamaparse"
14+
15+
16+
class LlamaParseInstrumentor:
17+
def __init__(self, tracer):
18+
self._tracer = tracer
19+
20+
def instrument(self):
21+
methods_to_wrap = [
22+
("load_data", load_data_wrapper),
23+
("aload_data", aload_data_wrapper),
24+
("get_json_result", get_json_wrapper),
25+
("aget_json", aget_json_wrapper),
26+
("get_images", get_images_wrapper),
27+
("aget_images", aget_images_wrapper),
28+
("get_charts", get_charts_wrapper),
29+
("aget_charts", aget_charts_wrapper),
30+
]
31+
32+
for method_name, wrapper_func in methods_to_wrap:
33+
try:
34+
wrap_function_wrapper(
35+
MODULE_NAME,
36+
f"{CLASS_NAME}.{method_name}",
37+
wrapper_func(self._tracer),
38+
)
39+
except AttributeError:
40+
# Method doesn't exist, skip it
41+
continue
42+
43+
44+
@_with_tracer_wrapper
45+
def get_json_wrapper(tracer, wrapped, instance, args, kwargs):
46+
with tracer.start_as_current_span(f"{TASK_NAME}.task") as span:
47+
span.set_attribute(
48+
SpanAttributes.TRACELOOP_SPAN_KIND,
49+
TraceloopSpanKindValues.TASK.value,
50+
)
51+
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, TASK_NAME)
52+
53+
process_request(span, args, kwargs)
54+
result = wrapped(*args, **kwargs)
55+
process_response(span, result)
56+
return result
57+
58+
59+
@_with_tracer_wrapper
60+
async def aget_json_wrapper(tracer, wrapped, instance, args, kwargs):
61+
async with start_as_current_span_async(
62+
tracer=tracer, name=f"{TASK_NAME}.task"
63+
) as span:
64+
span.set_attribute(
65+
SpanAttributes.TRACELOOP_SPAN_KIND,
66+
TraceloopSpanKindValues.TASK.value,
67+
)
68+
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, TASK_NAME)
69+
70+
process_request(span, args, kwargs)
71+
result = await wrapped(*args, **kwargs)
72+
process_response(span, result)
73+
return result
74+
75+
76+
@_with_tracer_wrapper
77+
def get_images_wrapper(tracer, wrapped, instance, args, kwargs):
78+
with tracer.start_as_current_span(f"{TASK_NAME}.task") as span:
79+
span.set_attribute(
80+
SpanAttributes.TRACELOOP_SPAN_KIND,
81+
TraceloopSpanKindValues.TASK.value,
82+
)
83+
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, TASK_NAME)
84+
85+
process_request(span, args, kwargs)
86+
result = wrapped(*args, **kwargs)
87+
process_response(span, result)
88+
return result
89+
90+
91+
@_with_tracer_wrapper
92+
async def aget_images_wrapper(tracer, wrapped, instance, args, kwargs):
93+
async with start_as_current_span_async(
94+
tracer=tracer, name=f"{TASK_NAME}.task"
95+
) as span:
96+
span.set_attribute(
97+
SpanAttributes.TRACELOOP_SPAN_KIND,
98+
TraceloopSpanKindValues.TASK.value,
99+
)
100+
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, TASK_NAME)
101+
102+
process_request(span, args, kwargs)
103+
result = await wrapped(*args, **kwargs)
104+
process_response(span, result)
105+
return result
106+
107+
108+
@_with_tracer_wrapper
109+
def get_charts_wrapper(tracer, wrapped, instance, args, kwargs):
110+
with tracer.start_as_current_span(f"{TASK_NAME}.task") as span:
111+
span.set_attribute(
112+
SpanAttributes.TRACELOOP_SPAN_KIND,
113+
TraceloopSpanKindValues.TASK.value,
114+
)
115+
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, TASK_NAME)
116+
117+
process_request(span, args, kwargs)
118+
result = wrapped(*args, **kwargs)
119+
process_response(span, result)
120+
return result
121+
122+
123+
@_with_tracer_wrapper
124+
async def aget_charts_wrapper(tracer, wrapped, instance, args, kwargs):
125+
async with start_as_current_span_async(
126+
tracer=tracer, name=f"{TASK_NAME}.task"
127+
) as span:
128+
span.set_attribute(
129+
SpanAttributes.TRACELOOP_SPAN_KIND,
130+
TraceloopSpanKindValues.TASK.value,
131+
)
132+
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, TASK_NAME)
133+
134+
process_request(span, args, kwargs)
135+
result = await wrapped(*args, **kwargs)
136+
process_response(span, result)
137+
return result
138+
139+
140+
@_with_tracer_wrapper
141+
def load_data_wrapper(tracer, wrapped, instance, args, kwargs):
142+
with tracer.start_as_current_span(f"{TASK_NAME}.task") as span:
143+
span.set_attribute(
144+
SpanAttributes.TRACELOOP_SPAN_KIND,
145+
TraceloopSpanKindValues.TASK.value,
146+
)
147+
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, TASK_NAME)
148+
149+
process_request(span, args, kwargs)
150+
result = wrapped(*args, **kwargs)
151+
process_response(span, result)
152+
return result
153+
154+
155+
@_with_tracer_wrapper
156+
async def aload_data_wrapper(tracer, wrapped, instance, args, kwargs):
157+
async with start_as_current_span_async(
158+
tracer=tracer, name=f"{TASK_NAME}.task"
159+
) as span:
160+
span.set_attribute(
161+
SpanAttributes.TRACELOOP_SPAN_KIND,
162+
TraceloopSpanKindValues.TASK.value,
163+
)
164+
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, TASK_NAME)
165+
166+
process_request(span, args, kwargs)
167+
result = await wrapped(*args, **kwargs)
168+
process_response(span, result)
169+
return result

0 commit comments

Comments
 (0)