Repository navigation
refactor(replace): run detection and replacement as one invocation - #342
Merged
binaryaaron merged 9 commits intoOct 29, 2026
Conversation
Add run_replace_pipeline: an invocation-scoped detect step calls the detector once and returns each target's mentions, then the Substitute map step (when used), replace and verify-patches run per target. A row the detector drops fails at detect and blocks the later steps. Output rows are the detection frame's rows, and the failures are the detector's followed by the map step's. ReplacementWorkflow.run_with_detection exposes it, and ReplacementResult carries the detection result and its elapsed time. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
Replace mode now calls ReplacementWorkflow.run_with_detection with a detect callable, so detection runs as the first step of the call that replaces. Rewrite still runs detection and then its runner. The detection and replacement log lines keep their text and counts. Interface tests that stub the replace runner now stub run_with_detection through a shared helper that calls the detector it is given and returns the stubbed result. Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
…vocation pipeline The single invocation builds each row's target from the input frame's text before detection runs, so a null input became the string 'None' or 'nan' in the replaced column. The replaced cell for a row whose detected text is missing now takes the detection frame's own stringification, as the two-call path did. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
A detector may return a frame that does not map one row per input row: a duplicated row with no source_positions, or a repeated or out-of-range position list. The single invocation raised 'Workflow result is missing source correspondence' inside the detect step. A result now maps to targets only when it names one distinct input position per returned row, and positions the detector did not report are trusted only when each returned row's text equals the input row's text at that position. Otherwise every target fails at detect and the runner replaces over the detection frame as returned, through the same run() the two-call path used. The detector still runs once and the map step at most once. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
…ll path A caller's ReplacementWorkflow subclass may override run(). The single-invocation path called run_with_detection instead, so the override was never reached. When the configured runner's class overrides run(), Replace mode now detects, logs the replacement line and calls self._replace_runner.run() with the detection frame, as before. A runner that does not override run() stays on the single invocation. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
…de the engine The replace_method annotation uses the strategy types the module already imports, and the unaligned-result check goes through a row-partitioning helper, so the module no longer imports from the config strategy union or the interface package. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
…zales/chore/graph-sdk-detect-replace-one-invocation 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
…zales/chore/graph-sdk-detect-replace-one-invocation 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
Base automatically changed from
aagonzales/chore/graph-sdk-substitute-one-invocation
to
aagonzales/graph-sdk/feat/main
October 29, 2026 21:15
binaryaaron
deleted the
aagonzales/chore/graph-sdk-detect-replace-one-invocation
branch
October 29, 2026 21:15
Collaborator
Author
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.
In Replace mode, one graph invocation now runs detection and replacement: an invocation-scoped
detectstep produces each row's mentions, then (for Substitute) thesubstitute-mapstep, thenreplaceandverify-patchesper row. Output, failed records, errors and log lines match the base; detection's own code, Rewrite, detect-only andevaluate()are unchanged, and no public name is added.This branch stacks on the Substitute single-invocation PR and is rebased onto its base branch when the stack moves.
What changes
engine/replace/route.py).run_replace_pipelinelowers the input frame to one target per row and declares(detect, [substitute-map], replace, verify-patches). Thedetectstep calls the detector once over the whole frame, aligns each returned row to its input row, and returns that row's accepted mentions; a row the detector drops fails atdetectand blocks the later steps.verify-patchesbinds to thedetectoutput by port name, so the kernel is unchanged. Output rows are the detection frame's rows for the targets that reached the end, followed by the replacement columns as before. The Substitute map step is shared with the existing Substitute route.detect, the map step does not run, and replacement runs over the detection frame as returned, throughReplacementWorkflow.run, as before. When positions are not reported, a returned row counts as aligned only if its text equals the input row's text.engine/replace/replace_runner.py).ReplacementWorkflow.run_with_detectionruns the pipeline;ReplacementResultgains optionaldetectionanddetection_elapsedfields so the facade can report detection as before.ReplacementWorkflow.runstays for its other callers.interface/anonymizer.py). Replace mode callsrun_with_detection. A caller-supplied replace runner whose class overridesrunkeeps the previous path: detection, then thatrun. The Rewrite branch behaves as before; its detection call moved inside the branch with the same arguments and timing points.engine/row_partitioning.py.try_resolve_source_positionsreturnsNoneinstead of raising when a result cannot be aligned to its source.AGENTS.mdshows the single invocation;docs/concepts/graph-sdk.mdgains one sentence.ReplacementWorkflow.runto drive Replace mode now stubrun_with_detectionthrough a shared helper intests/conftest.py; six assertions name the new method instead ofrun, with the same conditions.Net non-blank, non-comment code lines under
src/: +173 (route.py+103,replace_runner.py+32,anonymizer.py+30,row_partitioning.py+8).Known differences
ReplacementWorkflow.runstage, because that method is no longer called; detection's own stage is still recorded. A runner that overridesrun, and the rerouted case above, still record it.ValueError, now before detection runs instead of after.Checks
make checkpasses.make test: 1,557 passed.test_wandb_snapshot_rejects_fifo_socket_and_devicefails in the test environment withAF_UNIX path too long, as on the base.uv run --group dev python -m tests.spec_programs.run_all: 75 pass, 8 retired, 0 fail.run, ordinary Redact and Substitute runs) prints status, errors, both result frames cell by cell, failed records and call counts; the output at the base and at the head is identical.f4f2b73d) on pandas 2 and 3, over all cases: no blocking difference beyond the differences the base already has.Main merged (5155d6d)
This branch now includes main at 5155d6d through its base branch, including the Rewrite join change described on the remove-old-sdk PR.
Main merged (f294dc1)
This branch now also includes main at f294dc1 (#328, recovery from incomplete replacements in Rewrite) through its base branch.
🤖 Generated with Claude Code