Conversation
When the replacement-generator LLM drops a requested (value, label) pair, _filter_replacement_map_to_input_entities now appends a collision-safe placeholder via _collision_safe_synthetic and emits a PII-free WARNING. That keeps applied_span_count == targeted_span_count so rewrite readiness is not lost to a single omission. Fixes NVIDIA-NeMo#278 Signed-off-by: Dundy Pasupuleti <dundysm@gmail.com>
Linked Issue CheckIssue #278 has not been triaged yet. A maintainer needs to review You can continue working on the PR in the meantime. When the issue is |
|
ready for review. @asteier2026 @binaryaaron @lipikaramaswamy @andreatnvidia @NVIDIA-NeMo/anonymizer-maintainers fill lives only in |
|
| "synthetic": _collision_safe_synthetic( | ||
| label, | ||
| index=synthetic_collision_labels[label], | ||
| protected_original_values=protected_original_values, | ||
| ), | ||
| } | ||
| ) |
There was a problem hiding this comment.
Placeholder values can collide
Labels such as foo-bar and foo_bar normalize to the same placeholder token, but their counters remain separate, so omitted entries can receive the same synthetic value. A generated placeholder can also match an accepted LLM synthetic because the helper checks only original values. This violates the workflow's requirement that every entity have a different replacement and can reduce output quality. Please track all occupied synthetic values when generating placeholders and cover both collision cases with tests.
There was a problem hiding this comment.
hardened in 9537e86: occupied_values now seeds from protected originals and grows with every accepted llm synthetic and minted placeholder; _collision_safe_synthetic skips any occupied value (not only originals). placeholder index counters are keyed by normalized label token so foo-bar / foo_bar share one FOO_BAR sequence. added unit tests for the cross-label token clash and for an accepted llm synthetic equal to the would-be placeholder.
Track occupied values (protected originals plus accepted LLM synthetics and minted placeholders) when minting [SUBSTITUTE_*] fills. Key the placeholder index counter by normalized label token so labels that collapse to the same token cannot emit duplicate synthetics. Signed-off-by: Dundy Pasupuleti <dundysm@gmail.com>
Summary
Fixes #278.
When
LlmReplaceWorkflow.generate_map_onlyasks the replacement-generator LLM for a full map, entity-dense rows can occasionally drop one(value, label)pair. After #246,_prepare_rewrite_tagged_textfail-closes unlessapplied_span_count == targeted_span_count, so a single omission blanks the whole rewrite for that record.This change keeps the fix inside
_filter_replacement_map_to_input_entities:allowed_pairs - filled_pairs_collision_safe_synthetic(...)for each remaining pair (stable(label, original)order)filled_by_label=...) matching the collision-repair styleunfilled_by_labeltelemetry before the fill so LLM miss rates remain visibleShared filter path also covers Substitute: a missing synthetic no longer leaves the original value in place.
Intentionally scoped away from draft #198 umbrella rewrite work and #179 windowed
generate_map_only.Test plan
uv run pytest tests/engine/test_llm_replace_workflow.py -q(13 passed)uv run pytest tests/engine/test_rewrite_generation.py tests/engine/test_combined_rewrite_workflow.py -qmake format-checkcc @asteier2026 @binaryaaron @lipikaramaswamy @andreatnvidia @NVIDIA-NeMo/anonymizer-maintainers