fix: honor dataset pipeline trace scope in the Python runner - #379
Abhijeet Prasad (AbhiPrasad) wants to merge 3 commits into
Conversation
`scope: "trace"` was silently TypeScript-only. The TS runner threads the source scope into `transformArgsForCandidate`, but the Python runner never read it: `transform_args_for_candidate` always took the span path, so a trace-scoped Python pipeline received `input`/`output`/`expected`/`metadata` as `None` (and, via the SDK's positional fallback in `call_user_fn`, a single-parameter transform received `None` instead of the trace). The Python runner now resolves the merged source scope once per transform batch and passes only `trace` for trace scope, matching the TS runner byte-for-byte. The TS runner's `hydrateDiscoveryRefs` takes the already-merged source so the two stay diffable. Adds one runner smoke test covering both scopes against a stub braintrust package, reusing the spawn/assert helpers with the existing TypeScript runner test, and documents the scope contract in the README. An unknown scope value is left to the `PipelineScope` enum, which already rejects it while parsing the inspect response. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Latest downloadable build artifacts for this PR commit
Available artifact names
|
Stephen Belanger (Qard)
left a comment
There was a problem hiding this comment.
Generally LGTM. Just one small, non-blocking nit.
| ); | ||
| } | ||
|
|
||
| fn write_fake_python_braintrust_package(root: &Path) -> PathBuf { |
There was a problem hiding this comment.
There's a lot of Python fragments in here. Might be cleaner to just make a directory with all those files so they can be edited as plain files with proper syntax highlighting and not this awkward line formatting. Can just do include_str!("file.py") to load those in-place so you don't need to think about embedding the actual directory hierarchy into the Rust binary and can still just do the plain fs::write(...) calls here.
There was a problem hiding this comment.
Done in 06b69b2 — the stub braintrust package and the scope-probe pipeline are now plain files under src/datasets/pipeline-test-fixtures/:
src/datasets/pipeline-test-fixtures/
├── braintrust/{__init__,dataset_pipeline,framework,logger,trace}.py
└── scope_probe_pipeline.py
Loaded via include_str!, with the fs::write(...) calls kept — the five stub modules collapse into one (name, source) table, so write_fake_python_braintrust_package is now a 9-line loop. That drops 116 lines of embedded string from pipeline.rs.
The TypeScript test above it still has its fake braintrust package and pipeline inline. Same argument applies and it's mechanical — happy to move those too, just didn't want to grow this PR's diff into code it otherwise doesn't touch. Say the word.
There was a problem hiding this comment.
TypeScript fixtures extracted too, in 32f9a66. Fixtures are now grouped by runtime:
src/datasets/pipeline-test-fixtures/
├── node/
│ ├── braintrust/{package.json,index.cjs,index.mjs}
│ └── json_attachment_pipeline.ts
└── python/
├── braintrust/{__init__,dataset_pipeline,framework,logger,trace}.py
└── scope_probe_pipeline.py
write_fake_node_braintrust_package mirrors the Python one — a (name, source) table of include_str!s and one fs::write loop. The setup in typescript_runner_defers_json_attachments_during_transform goes from ~120 lines of embedded string to four.
Across both commits pipeline.rs is down 249 lines of raw-string fixture. The fixture .ts/.cjs/.mjs files are picked up by prettier and oxlint (both pass); tsconfig.json only includes scripts/**, so the pipeline fixture isn't typechecked — it wasn't before either, since it was a string.
cargo test --bin bt: 854 passed, 0 failed.
Addresses review feedback: the stub braintrust package and the scope-probe pipeline are plain .py files under src/datasets/pipeline-test-fixtures/, loaded with include_str! and written out as before, so they can be edited with syntax highlighting instead of as embedded raw strings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Same treatment as the Python fixtures: the stub braintrust npm package and
the JSON-attachment pipeline move out of embedded raw strings into plain
files, loaded with include_str!. Fixtures are now grouped by runtime under
src/datasets/pipeline-test-fixtures/{python,node}/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Problem
source.scope: "trace"was silently TypeScript-only.The TS runner threads the source scope into
transformArgsForCandidate, but the Python runner never read it —transform_args_for_candidatealways took the span path. Trace-scoped discovery emits refs without a span row id (src/sync/discovery.rs:226), so a trace-scoped Python pipeline gotinput/output/expected/metadataallNone, with no warning.The Python SDK documents the opposite:
DatasetPipelineTransformArgsmarks those four as "for span-scoped transforms" andtraceas "always available".Fix
scripts/dataset-pipeline-runner.pyresolves the merged source scope once per transform batch and passes onlytracefor trace scope, matching the TS runner.hydrate_discovery_refsnow takes the already-merged source instead of re-merging it, andscripts/dataset-pipeline-runner.tsgets the same shape so the two runners stay diffable — that divergence is the mechanism that produced this bug.An unknown scope value is left to the
PipelineScopeenum, which already rejects it while parsing the inspect response.Verification
Against the real
braintrustPyPI SDK (realDatasetPipeline+ realcall_user_fndispatch; only login/LocalTracestubbed):transform(**kwargs)['expected','input','metadata','output','trace'](4 bogusNones)['trace']transform(t)t=None— the SDK's positional fallback handed itinputt= the traceBoth runners now emit byte-identical JSON for the same trace-scope pipeline and ref.
New smoke test
python_runner_passes_scoped_transform_argscovers both scopes against a stub braintrust package, reusing spawn/assert helpers with the existing TypeScript runner test. Against the pre-fix runner it fails withleft: ["expected","input","metadata","output","trace"], right: ["trace"].cargo test --bin bt: 854 passed, 0 failed.cargo fmt,tsc,oxlint,prettierclean.Note
cargo clippy --all-features -- -D warningsfails onsrc/main.rs:193(large_enum_variantforTrace(CLIArgs<bt_daemon::TraceArgs>)). Pre-existing, from thebt-daemondependency, untouched here.🤖 Generated with Claude Code
Co-authored by StarfolkAI (@starfolkai)[bot]