feat: add a deny list for entities in Anonymizer - #243
Conversation
Greptile SummaryThe PR adds a normalized entity-label denylist and propagates it through detection, rewrite latent-entity handling, evaluation, result metadata, measurement records, documentation, and tests.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains; the current code fixes the previously reported permissive-coverage, explicit-label normalization, and latent-entity denylist issues. Important Files Changed
|
|
/nvskills-ci |
6a153c2 to
e3b99da
Compare
|
/nvskills-ci |
Adds entity_label_denylist: list[str] | None to the Detect model, with the same normalisation (strip, lowercase, deduplicate) as entity_labels. A model_validator warns at config construction time when entity_labels and entity_label_denylist share labels that would never be detected. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Applies the denylist at two points: - _resolve_detection_labels: subtracts denied labels before they reach GLiNER and the LLM augmenter/validator prompts - _materialize_final_entities: safety-net filter that drops any entity whose label is in the denylist from COL_FINAL_ENTITIES Threads entity_label_denylist through detect_and_validate_entities, _build_detection_spec, identify_latent_entities, and run on EntityDetectionWorkflow, and wires it from Anonymizer._run_internal via config.detect.entity_label_denylist. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…ion paths Adds tests for build_detection_config and build_detection_builder_for_seed confirming that denied labels are subtracted from the GLiNER label list in the serialized workflow config, so external runtimes see the same effective label set as the in-process path. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…abel set When the denylist subtracts all labels from the effective detection set, _resolve_detection_labels now emits a warning instead of silently passing an empty list to GLiNER (which returns no detections, not the default set). Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…peline Stores entity_label_denylist on AnonymizerResult and PreviewResult so evaluate() can scope its judges to the same label set that was active during detection. Changes: - AnonymizerResult / PreviewResult: add entity_label_denylist field - Anonymizer.evaluate(): extract entity_label_denylist from the result and pass it to EntityCoverageWorkflow (rewrite path) and ReplacementWorkflow.evaluate() (replace path) - EntityCoverageWorkflow: accept entity_label_denylist, pass to _filter_out_of_scope_entities in postprocess - _filter_out_of_scope_entities: exclude entities whose label is in the denylist so the judge does not penalise the output for not anonymizing denied labels - ReplacementWorkflow.evaluate(): thread entity_label_denylist through to EntityCoverageWorkflow Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…filter Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: nvskills-svc-account <svc-nvskills-signing@nvidia.com>
fd66bbd to
7f87728
Compare
| so both paths run exactly the same workflow. | ||
| """ | ||
| labels = _resolve_detection_labels(entity_labels) | ||
| labels = _resolve_detection_labels(entity_labels, set(entity_label_denylist) if entity_label_denylist else None) |
There was a problem hiding this comment.
[P1] Filter denied augmented labels before resolving overlaps
In permissive mode, the augmenter can still return denied labels. These labels enter overlap resolution before the final deny-list filter, so a denied span can replace an allowed span and then be removed, leaving the allowed PII undetected.
Please filter denied augmented entities before merging them, and add a regression test confirming an overlapping allowed span survives.
There was a problem hiding this comment.
Yes, this is critical to fix. Do note that #238 will change the mechanism for overlap resolution in resolve_overlaps, but regardless of the resolution mechanism we should filter out undesired entity labels after.
|
The param name |
| raise InvalidConfigError(str(exc)) from exc | ||
|
|
||
| entity_labels = getattr(output, "entity_labels", None) | ||
| entity_label_denylist = getattr(output, "entity_label_denylist", None) |
There was a problem hiding this comment.
We should preserve the existing result constructor contract.
These result classes are public, so their constructor parameters and attributes form part of the user-facing API. Adding the new field before data_summary changes the positional signature, even though positional construction is probably uncommon.
Could we place the new optional field after data_summary to preserve the existing parameter order? Longer term, it may be worth grouping evaluation provenance into a dedicated context object, but replacing the existing fields would require a separately planned API change.
Summary
Adds
entity_label_denylistto theDetectconfiguration, allowing users to explicitly exclude specific entity label types from detection. Denied labels are removed from detector scope, filtered from final explicit and latent entity outputs, and excluded from entity-coverage scoring.Changes
Config
entity_label_denylist: list[str] | Nonefield toDetectwith the same normalisation asentity_labels(strip, lowercase, deduplicate, reject empty)entity_labelsandentity_label_denylistshare labels that would never be detected.Detection Engine
_resolve_detection_labels)_materialize_final_entitiesto drop denied labels from final entity output regardless of LLM outputentity_label_denylistthrough all detection pipeline methods:run,detect_and_validate_entities,_build_detection_spec,identify_latent_entitiesbuild_detection_configandbuild_detection_builder_for_seedEvaluation
entity_label_denylistfield toAnonymizerResultandPreviewResultsoevaluate()has access to the full detection scopeentity_label_denylistthroughevaluate()toEntityCoverageWorkflowandReplacementWorkflow.evaluate()Telemetry
entity_label_denylistto_detect_config_metadataso it is captured in measurement records alongsideentity_labelsTests
_effective_entity_labelshelper, coverage judge postprocess filter, coverage prompt scopingentity_label_denylistcaptured correctly when set and absentDocs
Detectfields reference table and added "Excluding labels with a deny list" section indetection.mdentity_label_denylistto the judge scoping list inevaluation.mdentity_label_denylistsection and corrected thedata_summarysoft-exclusion claim inchoosing-a-strategy.mdentity_label_denylistas the recommended fix for systematically noisy label types introubleshooting.mdskills/anonymizer/SKILL.mdwith denylist behavior and precedence ruleType of Change
Contributor Checklist
fix: handle empty entity listskills/anonymizer/SKILL.mdupdated if neededValidation
Documentation and Artifacts
make docs-buildpasses locallymake convert-notebookscloses issue #164