feat(classification): enforce valid class vocabulary with re-prompt/retry (#356)#371
Merged
Merged
Conversation
…etry (#356) Page-level classification (multimodalPageLevelClassification) now validates the model's predicted class against the configured class vocabulary and re-prompts the model on out-of-vocabulary predictions, retrying up to a configurable limit. Because classification runs at temperature=0, the retry appends a correction message listing the allowed classes (a single-turn re-prompt) rather than re-sending an identical request. When retries are exhausted the page is assigned a configurable fallback class and flagged with a validation_error in metadata; the document keeps processing (no hard failure). Three new classification config keys control it (on by default): - enforceValidClasses (default true) - maxValidationRetries (default 2) - invalidClassFallback (default unclassified) Behavior change on upgrade: enforcement is on by default, so out-of-vocabulary predictions that previously passed through unchanged are now corrected or coerced to the fallback. Set enforceValidClasses: false to restore legacy "warn and use as-is" behavior. - Add fields + validators to ClassificationConfig - Add defaults to base-classification.yaml (inherited by config_library samples) - Add ConfigSchema entries to patterns/unified/template.yaml for the Config UI - Add validation/retry loop and _build_validation_retry_content helper - Add unit tests (retry-then-valid, exhausted-fallback, valid-first, legacy, metering aggregation) and config-model default/parsing tests - Add demo notebook notebooks/misc/classification-valid-class-enforcement.ipynb - Update user docs, developer README, and CHANGELOG Holistic packet classification is not covered by this loop yet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Summary
Closes #356.
Page-level classification (
multimodalPageLevelClassification) can return a class label that is not in the configured vocabulary — e.g. predictingreceiptwhen onlyinvoice,w2, andcheckare valid. This is especially common with smaller/cheaper models. Previously the code detected this but only logged a warning and used the invalid label anyway.This PR adds a deterministic validation + retry guardrail:
temperature=0, re-sending an identical request would return the identical wrong answer, so the correction is load-bearing — this is a single-turn re-prompt, not a blind retry.)maxValidationRetries.invalidClassFallback(defaultunclassified) and flag the page with avalidation_errorin classification metadata. The document keeps processing — no hard failure.New config keys (under
classification:)enforceValidClassestruefalse= legacy "warn and use as-is".maxValidationRetries20disables retries.invalidClassFallbackunclassifiedAll three are editable in the Configuration UI.
Enforcement is on by default, so an out-of-vocabulary prediction that previously passed through unchanged is now corrected or coerced to
unclassified. SetenforceValidClasses: falseto restore the prior behavior. This was a deliberate decision (stronger correctness guarantee out of the box); calling it out here for reviewer sign-off.Design notes
textbasedHolisticClassification) is not covered by this loop yet — noted as a follow-up.Changes
_build_validation_retry_contenthelper inclassify_page_bedrockClassificationConfigbase-classification.yaml(inherited byconfig_librarysamples via merge)ConfigSchemaentries inpatterns/unified/template.yamlwithdependsOnwiringnotebooks/misc/classification-valid-class-enforcement.ipynb(deterministic mock-driven demo of all 3 scenarios)docs/classification.md), developer README, CHANGELOG with migration noteTesting
cd lib/idp_common_pkg && pytest tests/unit/classification tests/unit/config/test_config_models.py→ 84 passed, 1 skippedruff checkandruff format --checkclean on all changed files🤖 Generated with Claude Code