Skip to content

Commit 5789819

Browse files
committed
trace: add adapter
1 parent 4a6eb29 commit 5789819

5 files changed

Lines changed: 79 additions & 6 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ requires-python = ">=3.10"
77
dependencies = [
88
"llama-index>=0.12.36",
99
"openinference-instrumentation-llama-index>=4.2.1",
10-
"uipath>=2.0.56",
10+
"uipath>=2.0.59",
1111
]
1212
classifiers = [
1313
"Development Status :: 3 - Alpha",

src/uipath_llamaindex/_cli/_runtime/_runtime.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,24 @@
55

66
from openinference.instrumentation.llama_index import LlamaIndexInstrumentor
77
from opentelemetry import trace
8+
from opentelemetry.sdk.trace import TracerProvider
9+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
810
from uipath import UiPath
911
from uipath._cli._runtime._contracts import (
1012
UiPathBaseRuntime,
1113
UiPathErrorCategory,
1214
UiPathRuntimeResult,
1315
)
14-
from uipath.tracing import wait_for_tracers
16+
from uipath.tracing import LlmOpsHttpExporter, wait_for_tracers
1517

18+
from .._tracing._oteladapter import LlamaIndexAdapter
1619
from ._context import UiPathLlamaIndexRuntimeContext
1720
from ._exception import UiPathLlamaIndexRuntimeError
1821

1922
logger = logging.getLogger(__name__)
2023

2124

25+
2226
class UiPathLlamaIndexRuntime(UiPathBaseRuntime):
2327
"""
2428
A runtime class for hosting UiPath LlamaIndex agents.
@@ -42,6 +46,9 @@ async def execute(self) -> Optional[UiPathRuntimeResult]:
4246
await self.validate()
4347

4448
with suppress(Exception):
49+
trace.set_tracer_provider(TracerProvider())
50+
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(LlamaIndexAdapter(LlmOpsHttpExporter()))) # type: ignore
51+
4552
LlamaIndexInstrumentor().instrument(
4653
tracer_provider=trace.get_tracer_provider()
4754
)

src/uipath_llamaindex/_cli/_tracing/__init__.py

Whitespace-only changes.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import json
2+
import logging
3+
from typing import Sequence
4+
5+
from opentelemetry.sdk.trace import ReadableSpan
6+
from opentelemetry.sdk.trace.export import (
7+
SpanExporter,
8+
SpanExportResult,
9+
)
10+
11+
logger = logging.getLogger(__name__)
12+
13+
class LlamaIndexAdapter(SpanExporter):
14+
"""A simple wrapper for SpanExporters that allows for customization."""
15+
16+
# Mapping of old attribute names to new attribute names or (new name, function)
17+
ATTRIBUTE_MAPPING = {
18+
"input.value": ("input", lambda s: json.loads(s)),
19+
"output.value": ("output", lambda s: json.loads(s)),
20+
"llm.model_name": "model",
21+
}
22+
23+
def __init__(self, wrapped_exporter: SpanExporter):
24+
"""Initialize with the exporter to wrap.
25+
26+
Args:
27+
wrapped_exporter: The underlying SpanExporter to wrap
28+
"""
29+
super().__init__()
30+
self.wrapped_exporter = wrapped_exporter
31+
32+
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
33+
"""Export spans, with a hook to transform them first."""
34+
transformed_spans = [self.transform_span(span) for span in spans]
35+
return self.wrapped_exporter.export(transformed_spans)
36+
37+
def transform_span(self, span: ReadableSpan) -> ReadableSpan:
38+
"""Override this method to transform spans before export.
39+
40+
Args:
41+
span: The original span to transform
42+
43+
Returns:
44+
The transformed span
45+
"""
46+
for old_key, mapping in self.ATTRIBUTE_MAPPING.items():
47+
if old_key in span.attributes:
48+
if isinstance(mapping, tuple):
49+
new_key, func = mapping
50+
try:
51+
span.attributes[new_key] = func(span.attributes[old_key])
52+
except Exception:
53+
span.attributes[new_key] = span.attributes[old_key]
54+
else:
55+
new_key = mapping
56+
span.attributes[new_key] = span.attributes[old_key]
57+
del span.attributes[old_key]
58+
return span
59+
60+
def force_flush(self, timeout_millis: int = 30000) -> bool:
61+
"""Pass through to the wrapped exporter."""
62+
return self.wrapped_exporter.force_flush(timeout_millis)
63+
64+
def shutdown(self) -> None:
65+
"""Pass through to the wrapped exporter."""
66+
return self.wrapped_exporter.shutdown()

uv.lock

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

0 commit comments

Comments
 (0)