feat(llama-index): add loongsuite-instrumentation-llama-index (#18) - #273
RichardoMrMu wants to merge 32 commits into
Conversation
…-index/CHANGELOG.md
…-index/pyproject.toml
…-index/test-requirements.txt
…-index/src/opentelemetry/instrumentation/llama_index/package.py
…-index/src/opentelemetry/instrumentation/llama_index/version.py
…-index/src/opentelemetry/instrumentation/llama_index/__init__.py
…-index/tests/conftest.py
…-index/tests/test_instrumentor.py
…-index/tests/__init__.py
…loongsuite_instrumentation_llama_index.py
…format) CI precommit failed on the new package: E402 in tests/conftest.py (imports intentionally after sys.path setup + pytest_configure), PLC0415 for lazy imports in src, and I001/format. Follow the existing per-file-ignores convention used by every other loongsuite instrumentation package (PLC0415 for the package, E402+F811 for tests), and apply ruff import sorting + formatting. No behavior change; 15 tests still pass.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Structured prediction classification, active-span teardown, hierarchy verification, and CI registration need correction.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 3
Open (4)
What changed in this PR
Adds automatic OpenTelemetry instrumentation for LlamaIndex via its native dispatcher.
Changes:
- Maps LlamaIndex spans and events to GenAI telemetry.
- Adds lifecycle, hierarchy, classification, and embedding tests.
- Registers packaging and bootstrap metadata.
| File | Description |
|---|---|
pyproject.toml |
Adds Ruff exceptions. |
loongsuite-distro/.../loongsuite_instrumentation_llama_index.py |
Adds bootstrap registry metadata. |
.../tests/test_instrumentor.py |
Tests spans and lifecycle. |
.../tests/conftest.py |
Configures test tracing. |
.../tests/__init__.py |
Initializes tests. |
.../test-requirements.txt |
Adds test dependencies. |
.../version.py |
Defines package version. |
.../package.py |
Defines instrument dependency. |
.../llama_index/__init__.py |
Implements instrumentation. |
.../README.md |
Documents usage. |
.../pyproject.toml |
Defines package metadata. |
.../LICENSE |
Adds Apache license. |
.../CHANGELOG.md |
Records initial release. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| "instrumentation-loongsuite/loongsuite-instrumentation-llama-index/**/*.py" = ["PLC0415"] | ||
| "instrumentation-loongsuite/loongsuite-instrumentation-llama-index/tests/**/*.py" = ["E402", "F811"] |
|
Thanks for adding LlamaIndex support! Could we use the shared GenAI util in this LoongSuite repository, following the Hermes instrumentation as a reference, so semantic conventions and content-capture controls can be managed consistently? Also, span types should reflect the actual operation. A class name containing |
…agent-vs-tool span kinds (alibaba#18)
…e, drain-safe uninstrument, exact parent id
|
Thanks for the review, @sipercai — both points are addressed, and I pushed the changes plus tests. 1. Use the shared GenAI util for semconv + content-capture (following hermes)
2. Span types must reflect the real operation (not AGENT-by-classname)
Also picked up the Copilot findings in the same pass:
All 28 unit tests pass locally and |
|
Thanks for the update! The span classification fixes look good. For shared-util integration, please follow Hermes and use this repository’s Please also add the missing GenAI util dependency—clean installation currently fails on import—and delegate content emission to the handler so |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Span shutdown and concurrent teardown can strand spans or contexts, and output telemetry and content-capture documentation are inaccurate.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 5
Open (6)
Race between stopped check and span registration strands spans · New Live spans omitted from shutdown drain registry · New Cross-context cleanup leaves ended span attached · New Chat input incorrectly reported as generated output · New New package missing from tox-loongsuite configuration GenAI content capture documentation contradicts implementation · New
| if self._stopped(): | ||
| # Uninstrument in progress: create no new spans, but leave | ||
| # already-open ones for exit/drop to finish. | ||
| return None |
| with self._lock(): | ||
| spans[id_] = span | ||
| self._tokens()[id_] = token | ||
| return None |
| if token is not None: | ||
| try: | ||
| context_api.detach(token) | ||
| except Exception: # pragma: no cover - defensive | ||
| pass |
| # completion end carries a plain response string | ||
| if capture: | ||
| messages_out = getattr(event, "messages", None) | ||
| if messages_out and _GEN_AI_OUTPUT_MESSAGES not in ( | ||
| span.attributes or {} | ||
| ): | ||
| js = _messages_to_json(messages_out) | ||
| if js: | ||
| span.set_attribute(_GEN_AI_OUTPUT_MESSAGES, js) |
| Message text is captured on span attributes by default. To suppress | ||
| `gen_ai.input.messages` / `gen_ai.output.messages` while keeping the | ||
| structural spans and token metrics: | ||
|
|
||
| ```bash | ||
| export OTEL_INSTRUMENTATION_LLAMA_INDEX_CAPTURE_CONTENT=false | ||
| ``` |


What
Adds a new instrumentation package
loongsuite-instrumentation-llama-index, providing automatic OpenTelemetry instrumentation for LlamaIndex (llama-index-core).Closes #18 (roadmap #36 — "Add instrumentation for llama-index",
contribution welcome).How
Rather than monkey-patching call sites, this attaches to LlamaIndex's native instrumentation dispatcher (
llama_index.core.instrumentation). LlamaIndex already emits a span/event stream through a rootDispatcher, assigning each instrumented call a spanid_and aparent_span_idthat reflects the logical call tree (e.g.query→retrieve/synthesize,chat→complete). The package registers aBaseSpanHandler+BaseEventHandleron that dispatcher and re-projects the stream onto OpenTelemetry spans following the ARMS gen-ai semantic conventions, consumingparent_span_iddirectly so the OTel trace preserves LlamaIndex's own parent/child structure.Span-kind mapping (
gen_ai.span.kind): LLM / EMBEDDING / RETRIEVER / RERANKER / TASK (synthesis) / CHAIN (query engine) / AGENT (chat engine, agent run). Classification is method-first so class names embedding a misleading keyword (e.g.RetrieverQueryEngine.query) are classified correctly (CHAIN, not RETRIEVER). LLM/embedding events fold request model, messages and provider token usage onto the span. Content capture can be disabled withOTEL_INSTRUMENTATION_LLAMA_INDEX_CAPTURE_CONTENT=false.Testing
tests/test_instrumentor.pydrives the realllama-index-coredispatcher with in-processMockLLM/MockEmbeddingand asserts on spans exported to anInMemorySpanExporter(no network, no credentials). Each GREEN assertion is paired with a RED baseline:chatproduces an LLM span with correctgen_ai.*attributes;chat→completeshare onetrace_idwithcompletenested underchat(provingparent_span_idmapping); embedding produces an EMBEDDING span.uninstrument(), a subsequent chat produces zero spans.15 tests pass (
pytest), onllama-index-core 0.14.25+opentelemetry-sdk.Notes
pyproject.toml, entry point, CHANGELOG and bootstrap-registry entry follow the existingloongsuite-instrumentation-*conventions (e.g. terminus2).