feat: first-class OpenTelemetry export (one span per boundary) - #33
Merged
Conversation
Chronicle already bootstrapped OTel and mapped envelope fields to OpenInference attributes, but nothing turned a recorded run into spans. Add instrument_otel: each recorded Envelope becomes an OpenTelemetry span with OpenInference semantic conventions (LLM/tool span kind, input/output, model, token counts), nested by the run's parent linkage, so recorded runs land in Phoenix or any OTel backend. - chronicle/otel.py: instrument_otel(tracer=None, session=None) wires a session on_record hook that emits + ends a span per crossing, linking children to their parent span; envelope_span_attributes() maps one Envelope to OpenInference attrs. Error envelopes set span status ERROR. Returns an uninstrument callable. - session.py: new on_record hook fired with the full Envelope after each record. - __init__: instrument_otel / envelope_span_attributes exposed lazily via module __getattr__, so `import chronicle` never imports opentelemetry (base install needs neither the OTel SDK nor OpenInference). Deps ship in the existing [phoenix] extra. Tests (tests/test_otel.py): one span per crossing with OpenInference attrs, parent nesting, tool name, model + token attributes, ERROR status on failure, uninstrument, and that importing chronicle does not import opentelemetry (subprocess). Full suite green; ruff 0.15.22 clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Tisha Chawla <tisha.chawla2020@vitalum.ac.in>
# Conflicts: # chronicle/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Chronicle already bootstrapped OTel and had an envelope→attributes mapper, but nothing turned a recorded run into spans. This makes OpenTelemetry first-class: each recorded boundary crossing becomes a span in Phoenix or any OTel backend.
What
input.value/output.value,llm.model_name,llm.token_count.*,tool.name, pluschronicle.*ids.Design
chronicle/otel.py:instrument_otel(tracer=None, session=None)wires a newsession.on_recordhook that emits and ends a span per crossing;envelope_span_attributes()maps one Envelope to OpenInference attributes.session.py: newon_recordhook fired with the full Envelope after each record (additive; defaultNone).__init__:instrument_otel/envelope_span_attributesexposed lazily via module__getattr__, soimport chroniclenever imports opentelemetry. The base install needs neither the OTel SDK nor OpenInference; they come with the existing[phoenix]extra.Tests (
tests/test_otel.py, in-memory exporter)One span per crossing with OpenInference attrs; parent nesting; tool name; model + token-count mapping; ERROR status on a failing boundary; uninstrument stops spans; and a subprocess test proving
import chronicledoes not import opentelemetry. Full suite green; ruff 0.15.22 clean. No em/en dashes.