Repository navigation
feat(evaluations): export each offline row as one trace - #170
Open
ccschmitz-launchdarkly wants to merge 3 commits into
Open
ccschmitz-launchdarkly wants to merge 3 commits into
ccschmitz-launchdarkly wants to merge 3 commits into
Conversation
ccschmitz-launchdarkly
marked this pull request as ready for review
October 22, 2026 21:15
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4b9e771. Configure here.
EvaluationsModule.run opens a launchdarkly.dataset_row root per row on the tracer provider init_client registered, then parents the generation, its tool calls, and each criterion's evaluate span beneath it. Every span shares the row's UUID5 gen_ai.conversation.id and carries launchdarkly.evaluation_run.id / .row_index. A provider handler traces its own call. For a plain function the runner writes the chat and execute_tool spans itself, only when the handler opened no SDK span. Content follows the existing capture_content gate. Core conversation.py gains two feature-neutral primitives, stamp_span_attributes and count_sdk_spans; everything evaluation-specific lives in evaluations/spans.py.
ccschmitz-launchdarkly
force-pushed
the
ccschmitz/offline-eval-conversation-spans-17b3
branch
from
October 23, 2026 16:53
4b9e771 to
5d02ea7
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lush in a finally When the active tracer provider lacks ConversationIdSpanProcessor, the SDK span count stays 0 even when the handler traced itself, so the runner wrote its own chat and tool spans as well. Both observer.count == 0 sites now also require _sdk_processor_is_active() (lifecycle.py). A test with a foreign TracerProvider checks each chat span is written once. Ending the row spans now runs in a try whose finally flushes the SDK events and the tracer provider. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Pushed f684f98 (merge of origin/main) and fa2e25e:
Trade-off: under a foreign provider, a handler that traces nothing gets no runner-written chat span. Needs a human reviewer. |
3 tasks done
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.

EvaluationsModule.runnow exports each dataset row as one trace, on the tracer providerinit_clientalready registers. The$ld:ai:offline-evals:generationand:criterionevents are unchanged.Spec: launchdarkly/ai-sdks-monorepo#60 (
TESTING.md§8.10). Frontend: launchdarkly/gonfalon#74117.What each row exports
gen_aioperation, so the conversation view treats it as an envelope. Its status is ERROR when the handler raised.chatandexecute_toolspans itself. It does so only when the handler opened no SDK span, so nothing is traced twice.evaluatespan.capture_contentgate, which is off by default. There is no separate evaluation switch.Where the code lives
Core
conversation.pygains two primitives that don't know about evaluations:stamp_span_attributes(write-if-absent attributes on SDK spans in a block) andcount_sdk_spans. Everything evaluation-specific is inevaluations/spans.py.Verified on staging
Project
default, envai-o-11-y, with a plain-function handler and thelookup_capitaltool. Each run produced one trace with the five spans above andpassed=1.enable-evaluation-run-conversation-spansis still off in that environment, so the run page shows the synthetic waterfall. The trace links show the exported spans.Known gaps
generation_incompletecriterion event for a failed row. §8.8.6 tracks that separately.gen_ai.system_instructionsas its raw JSON part array.EvaluationsModule.runnow exports each dataset row as one trace, on the tracer providerinit_clientalready registers. The$ld:ai:offline-evals:generationand:criterionevents are unchanged.Spec: launchdarkly/ai-sdks-monorepo#60 (
TESTING.md§8.10). Frontend: launchdarkly/gonfalon#74117.What each row exports
gen_aioperation, so the conversation view treats it as an envelope. Its status is ERROR when the handler raised.chatandexecute_toolspans itself. It does so only when the handler opened no SDK span, so nothing is traced twice.evaluatespan.capture_contentgate, which is off by default. There is no separate evaluation switch.Where the code lives
Core
conversation.pygains two primitives that don't know about evaluations:stamp_span_attributes(write-if-absent attributes on SDK spans in a block) andcount_sdk_spans. Everything evaluation-specific is inevaluations/spans.py.Verified on staging
Project
default, envai-o-11-y, with a plain-function handler and thelookup_capitaltool. Each run produced one trace with the five spans above andpassed=1.Trace search defaults to the last 15 minutes, which no longer covers these runs. The trace links set
startDateandendDateand do not setrelativeTime. Each window is the root span, padded by 15 minutes on either side. The run page's conversation query omits its own window and lets the backend resolve the span range, so those links do not carry a time bound.2026-10-22T21:10:38.640Z,1600467ns. The tree, model, usage, tool name, and score are present. No content attributes.2026-10-22T21:10:38.740Z,2044814ns. Input and output messages, tool arguments and results, the judge prompt and completion, and the explanation are all present.enable-evaluation-run-conversation-spansis still off in that environment, so the run page shows the synthetic waterfall. The trace links show the exported spans. Preview is gonfalon#74117 atc497293cd.Known gaps
generation_incompletecriterion event for a failed row. §8.8.6 tracks that separately.gen_ai.system_instructionsas its raw JSON part array.