Enable correction-aware semantic reconciliation

This commit is contained in:
2026-08-27 00:08:05 +00:00
parent a26d6ed042
commit 04ba87e174
14 changed files with 195 additions and 51 deletions

View File

@@ -119,7 +119,7 @@ func (n *Normalizer) Normalize(ctx context.Context, req contracts.TypedNormalize
}
reconciliation, err := n.engine.Reconcile(ctx, semanticreconcile.Request{
StageName: Key, Source: req.Source, Candidates: candidates,
ProfileID: req.LLMProfile, SessionID: req.SessionID, StructuredOutputRepairAttempts: req.StructuredOutputRepairAttempts,
ProfileID: req.LLMProfile, SessionID: req.SessionID, StructuredOutputRepairAttempts: req.StructuredOutputRepairAttempts, Correction: req.Correction,
})
if err != nil {
return contracts.TypedNormalizeResult[dnd.ItemRegistry]{}, normalizerErrorf("reconcile semantic duplicates: %w", err)
@@ -144,7 +144,7 @@ func (n *Normalizer) Normalize(ctx context.Context, req contracts.TypedNormalize
warnings = append(warnings, semanticWarnings...)
discardedGroups := reconciliation.DiscardedGroupCount() + rejectedGroups
if discardedGroups == 0 {
return contracts.TypedNormalizeResult[dnd.ItemRegistry]{Value: recordList(applied), Warnings: limitWarnings(warnings)}, nil
return contracts.TypedNormalizeResult[dnd.ItemRegistry]{Value: recordList(applied), Warnings: limitWarnings(warnings), ModelCandidate: reconciliation.ModelCandidate()}, nil
}
return retryResult(recordList(applied), warnings, reconciliation, rejectedGroups), nil
}
@@ -162,7 +162,7 @@ func retryResult(value dnd.ItemRegistry, warnings []contracts.Warning, reconcili
details = append(details, "currency may only be consolidated with aliases of one denomination")
}
discardedGroups := reconciliation.DiscardedGroupCount() + rejectedGroups
return contracts.TypedNormalizeResult[dnd.ItemRegistry]{Value: value, Warnings: limitWarningsForRetry(warnings), Retry: &contracts.NormalizeRetry{
return contracts.TypedNormalizeResult[dnd.ItemRegistry]{Value: value, Warnings: limitWarningsForRetry(warnings), ModelCandidate: reconciliation.ModelCandidate(), Retry: &contracts.NormalizeRetry{
ReasonCode: ReasonCodeItemSemanticProposalInvalid, Message: diagnostics.Aggregate("semantic proposal requires retry", details),
FallbackWarnings: []contracts.Warning{semanticFallbackWarning(discardedGroups)},
}}
@@ -334,7 +334,7 @@ func duplicateWarning(retainedIndex int, removed []int) contracts.Warning {
func itemScope(index int) string { return fmt.Sprintf("items[%d]", index) }
func ModuleSpec() pipeline.ModuleSpec {
return pipeline.ModuleSpec{Key: Key, Stage: pipeline.StageNormalize, ExecutionClass: contracts.ExecutionClassLLMBacked, Requires: append([]string(nil), requiredCapabilities...), Provides: append([]string(nil), providedCapabilities...), ArtifactKind: dnd.ItemRegistryKind}
return pipeline.ModuleSpec{Key: Key, Stage: pipeline.StageNormalize, ExecutionClass: contracts.ExecutionClassLLMBacked, CorrectionProtocol: contracts.CorrectionProtocolSingleResponseV1, Requires: append([]string(nil), requiredCapabilities...), Provides: append([]string(nil), providedCapabilities...), ArtifactKind: dnd.ItemRegistryKind}
}
func Register(registry *pipeline.NormalizerRegistry) error {
return pipeline.RegisterNormalizerBuilder(registry, ModuleSpec(), validateOptions, func(request pipeline.BuildRequest) (contracts.Normalizer[dnd.ItemRegistry], error) {