Improve D&D validation reliability
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
|
||||
frameworkdiagnostics "gitea.maximumdirect.net/eric/notarius/internal/framework/diagnostics"
|
||||
)
|
||||
@@ -25,6 +26,79 @@ type Finding struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
// Corrections collects domain-owned model instructions and contextual record
|
||||
// descriptions without coupling them to operator-facing diagnostics. Rules are
|
||||
// emitted before affected records so the bounded result remains useful even
|
||||
// when a large candidate exceeds the display budget.
|
||||
type Corrections struct {
|
||||
groups []correctionGroup
|
||||
indexes map[string]int
|
||||
}
|
||||
|
||||
type correctionGroup struct {
|
||||
rule string
|
||||
records []string
|
||||
recordsSeen map[string]struct{}
|
||||
}
|
||||
|
||||
// Add records one semantic rule and, when non-empty, one contextual record to
|
||||
// which it applies. key is local grouping state and is never rendered.
|
||||
func (c *Corrections) Add(key, rule, record string) {
|
||||
if c.indexes == nil {
|
||||
c.indexes = make(map[string]int)
|
||||
}
|
||||
index, ok := c.indexes[key]
|
||||
if !ok {
|
||||
index = len(c.groups)
|
||||
c.indexes[key] = index
|
||||
c.groups = append(c.groups, correctionGroup{rule: rule, recordsSeen: make(map[string]struct{})})
|
||||
}
|
||||
if record == "" {
|
||||
return
|
||||
}
|
||||
group := &c.groups[index]
|
||||
if _, seen := group.recordsSeen[record]; seen {
|
||||
return
|
||||
}
|
||||
group.recordsSeen[record] = struct{}{}
|
||||
group.records = append(group.records, record)
|
||||
}
|
||||
|
||||
// Guidance returns one bounded correction request. It deliberately renders
|
||||
// neither grouping keys nor operator diagnostics.
|
||||
func (c Corrections) Guidance(prefix string) string {
|
||||
issues := make([]string, 0, len(c.groups)*2)
|
||||
for _, group := range c.groups {
|
||||
issues = append(issues, group.rule)
|
||||
}
|
||||
for _, group := range c.groups {
|
||||
issues = append(issues, group.records...)
|
||||
}
|
||||
return Aggregate(prefix, issues)
|
||||
}
|
||||
|
||||
// SourceRange describes cited transcript positions without exposing source
|
||||
// identities or application entity IDs.
|
||||
func SourceRange(refs []source.SourceRef) string {
|
||||
if len(refs) == 0 {
|
||||
return "without a cited source range"
|
||||
}
|
||||
description := SourceRefRange(refs[0])
|
||||
if len(refs) > 1 {
|
||||
description += fmt.Sprintf(" (first of %d cited ranges)", len(refs))
|
||||
}
|
||||
return description
|
||||
}
|
||||
|
||||
// SourceRefRange describes one transcript range without exposing its source
|
||||
// identity.
|
||||
func SourceRefRange(ref source.SourceRef) string {
|
||||
if ref.StartUnitID == ref.EndUnitID {
|
||||
return "at source unit " + strconv.Itoa(ref.StartUnitID)
|
||||
}
|
||||
return fmt.Sprintf("at source units %d-%d", ref.StartUnitID, ref.EndUnitID)
|
||||
}
|
||||
|
||||
// DataQualityResult converts accepted source-quality findings into bounded,
|
||||
// locally grouped advisories. These findings do not indicate process
|
||||
// degradation.
|
||||
|
||||
Reference in New Issue
Block a user