refactor(telemetry): migrate application spans to plugin hooks#1289
Open
ajbozarth wants to merge 1 commit into
Open
refactor(telemetry): migrate application spans to plugin hooks#1289ajbozarth wants to merge 1 commit into
ajbozarth wants to merge 1 commit into
Conversation
This was referenced Jun 18, 2026
Action span emission moves to ComponentTracingPlugin on component_* hooks. Session and start_session spans emit directly from stdlib via typed helpers in tracing.py — session lifecycle crosses _run_async_in_thread Tasks, which OTel Token pairing can't span across hooks. Side fix: _run_async_in_thread now propagates calling-thread contextvars into the new Task (approved with @jakelorocco), so children inside async work nest under user-thread spans. Delivers the docs-promised session > action > chat trace tree for the first time. Closes generative-computing#1048. Assisted-by: Claude Code Signed-off-by: Alex Bozarth <ajbozart@us.ibm.com>
ab9c6b5 to
c870348
Compare
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.
Pull Request
Issue
Fixes #1048. Phase 2 of #444.
Description
Action span emission moves from inline
trace_applicationcalls instdlib/functional.pyto a newComponentTracingPluginsubscribed tocomponent_*hooks. Session andstart_sessionspans stay in stdlib but route through clean typed helpers intracing.pyinstead of the deprecatedtrace_applicationsurface. Hooks fit spans whose lifecycle lives inside one async function (one Task); session lifecycle crosses multiple_run_async_in_threadcalls and OTel Token attach/detach is task-affine, so direct emission is the right shape there.Reviewer call-outs:
_run_async_in_threadnow propagates the calling thread's contextvars into the new Task. One-way, no copy-back. Without it,asyncio.run_coroutine_threadsafestarts the new Task with empty contextvars, so contextvar-backed state on the user thread (OTel active span, Mellea'ssession_id/request_id/model_id/sampling_iteration, log context) is invisible inside Mellea's async work — including thesession > action > chathierarchy that the docs have promised since feat: instrument telemetry #355. With this PR, that hierarchy actually forms in real usage for the first time. ~6 lines inevent_loop_helper.py; aligns Mellea with howasyncio.create_taskalready inherits Context within a thread.Span renames.
session_context→session;aact→action.session_context's suffix was redundant, andaactnamed a Mellea internal function rather than the operation. Trace-tooling consumers filtering on the old names need to update.Scope-narrowing change vs. the original issue. Issue feat: session and act spans via plugin hooks #1048 asked for session and act spans via plugin hooks. Action lands as planned (
ComponentTracingPlugin); session does not. Session lifecycle crosses multiple_run_async_in_threadcalls (sync__enter__, syncm.act(...), sync__exit__/cleanup()), each scheduled as a separate Task with its owncontextvars.Context. OTelToken.resetis bound to the originating Context, so a span attached in one hook's Task can't be detached in another. Session spans now emit directly fromMelleaSession.__enter__/__exit__andstart_session()via typed helpers intracing.py. TheSESSION_*hooks still fire as observation points for non-tracing plugins.Component payload
component_id. Adds a UUID correlation field onComponentPreExecutePayload/ComponentPostSuccessPayload/ComponentPostErrorPayload, matching the existinggeneration_idpattern on generation payloads. Plugin authors subscribing tocomponent_*hooks gain a correlation key.Bug fix from refactor(telemetry)!: move backend tracing onto plugin/hook pattern #1181 review.
mellea.responseis now gated onMELLEA_TRACES_CONTENT/OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT. Pre-this-PR it was captured unconditionally; this is the first real caller ofis_content_tracing_enabled()(per @planetf1's refactor(telemetry)!: move backend tracing onto plugin/hook pattern #1181 review).Streaming out of scope.
mellea/stdlib/streaming.pywas added in PR feat(stdlib): add streaming event types, events() iterator, and OTEL bridge (#902) #1095 mid-epic, after the original Phase 2 plan was set, and it imports the deprecatedtrace_applicationsurface. Migrating it is tracked as a follow-up Phase 2 sub-issue feat(telemetry): migrate stream_with_chunking orchestration span to plugin hooks #1290. The four deprecated public helpers (trace_application,set_span_attribute,set_span_error,set_span_status_error) stay intracing.pyuntil streaming migrates off them.Docs touched only where existing claims went stale. Span-hierarchy diagrams updated for the renames; one stale Phase-1 entry corrected (
mellea.backendis a string identifier, not a class name). Broader docs cleanup is tracked in docs: add Telemtry example for getting full traces #945.Bug fix:
mellea.sampling_successnow reflects the actual flag. Pre-this-PR the attribute wasbool(sampling_result.result)— i.e. truthiness of the chosenModelOutputThunk, which isTruewhenever sampling produced any non-None output even if no requirement passed. Post-this-PR it readssampling_result.success, the flag the field name has always promised. Traces where sampling exhausted retries without satisfying requirements will now correctly showmellea.sampling_success=false.BREAKING CHANGE: Span names renamed:
session_context→sessionaact→actionTrace-tooling consumers filtering on the old names need to update.
Testing
Attribution
Adding a new component, requirement, sampling strategy, or tool?
If your PR adds or modifies one of the types below, check the matching box. A checklist of type-specific review items will be posted as a comment.
NOTE: Please ensure you have an issue that has been acknowledged by a core contributor and routed you to open a pull request against this repository. Otherwise, please open an issue before continuing with this pull request.