package registry import ( "encoding/json" "fmt" "gitea.maximumdirect.net/eric/notarius/internal/core/source" "gitea.maximumdirect.net/eric/notarius/internal/framework/contracts" "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd" locationcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/locationregistry" "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/locations/identity" "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared" ) // Selector identifies one location using its canonical name and, when needed, // source-free registry ranges. type Selector struct { Name string `json:"name"` RegistryRefs []RegistryRef `json:"registry_refs"` } // RegistryRef is a source-free range used only for location identity grounding. type RegistryRef struct { StartUnitID int `json:"start_unit_id"` EndUnitID int `json:"end_unit_id"` } // ContextUnit supplies the bounded transcript context for an ambiguous // location's registry ranges. type ContextUnit struct { UnitID int `json:"unit_id"` Text string `json:"text"` } // Grounding is an immutable, operation-scoped location selector projection. // Its prompt input contains no durable IDs or source IDs. type Grounding struct { promptInput contracts.LLMInputMaterial locationsBySelector map[string]dnd.Location } type groundingProjection struct { Locations []groundingProjectionLocation `json:"locations"` } type groundingProjectionLocation struct { Name string `json:"name"` RegistryRefs []RegistryRef `json:"registry_refs"` Context []ContextUnit `json:"context"` } // NewGrounding creates the contextual location projection for one source // document and resolved registry. func NewGrounding(registry *Registry, doc *source.SourceDocument) (*Grounding, error) { if registry == nil { return nil, fmt.Errorf("location registry is required") } if doc == nil { return nil, fmt.Errorf("source document is required") } index := source.NewDocumentIndex(doc) order := shared.NewSourceRefOrderFromIndex(index) locations := registry.Locations() grouped := make(map[string][]int, len(locations)) for locationIndex, location := range locations { key := identity.ComparisonKey(location.Name) if key == "" { return nil, fmt.Errorf("location registry record %d has an empty comparison name", locationIndex) } grouped[key] = append(grouped[key], locationIndex) } projection := groundingProjection{Locations: make([]groundingProjectionLocation, len(locations))} locationsBySelector := make(map[string]dnd.Location, len(locations)) for locationIndex, location := range locations { key := identity.ComparisonKey(location.Name) registryRefs := make([]RegistryRef, 0) context := make([]ContextUnit, 0) if len(grouped[key]) > 1 { var err error registryRefs, context, err = contextualRanges(locationIndex, location.SourceRefs, doc, index, order) if err != nil { return nil, err } } projection.Locations[locationIndex] = groundingProjectionLocation{ Name: location.Name, RegistryRefs: cloneRegistryRefs(registryRefs), Context: cloneContextUnits(context), } selectorKey := contextualSelectorKey(key, registryRefs) if _, exists := locationsBySelector[selectorKey]; exists { return nil, fmt.Errorf("location registry records produce the same contextual selector") } locationsBySelector[selectorKey] = cloneLocation(location) } content, err := json.Marshal(projection) if err != nil { return nil, fmt.Errorf("encode location grounding projection: %w", err) } digest := semanticDigest(content) return &Grounding{ promptInput: contracts.NewLLMInputMaterial(ReferenceSlot, locationcodec.MediaType, content, digest, ""), locationsBySelector: locationsBySelector, }, nil } // PromptInput returns a defensive copy of the contextual projection for the // location_registry prompt slot. func (g *Grounding) PromptInput() contracts.LLMInputMaterial { if g == nil { return contracts.LLMInputMaterial{} } return g.promptInput.Clone() } // Resolve maps a contextual selector to one canonical location. func (g *Grounding) Resolve(selector Selector) (dnd.Location, bool) { if g == nil { return dnd.Location{}, false } key := identity.ComparisonKey(selector.Name) if key == "" { return dnd.Location{}, false } location, ok := g.locationsBySelector[contextualSelectorKey(key, selector.RegistryRefs)] if !ok { return dnd.Location{}, false } return cloneLocation(location), true } func contextualRanges(locationIndex int, refs []source.SourceRef, doc *source.SourceDocument, index source.DocumentIndex, order shared.SourceRefOrder) ([]RegistryRef, []ContextUnit, error) { for _, ref := range refs { if ref.SourceID != doc.ID || index.ValidateRef(ref) != nil { return nil, nil, fmt.Errorf("location registry record %d has an invalid source reference", locationIndex) } } canonical := order.Canonicalize(refs) ranges := make([]RegistryRef, len(canonical)) for refIndex, ref := range canonical { ranges[refIndex] = RegistryRef{StartUnitID: ref.StartUnitID, EndUnitID: ref.EndUnitID} } return ranges, contextUnits(doc, index, canonical), nil } func contextUnits(doc *source.SourceDocument, index source.DocumentIndex, refs []source.SourceRef) []ContextUnit { units := make([]ContextUnit, 0) seen := make(map[int]struct{}) for _, ref := range refs { start, _ := index.Position(ref.StartUnitID) end, _ := index.Position(ref.EndUnitID) for position := start; position <= end; position++ { unit := doc.Units[position] if _, exists := seen[unit.ID]; exists { continue } seen[unit.ID] = struct{}{} units = append(units, ContextUnit{UnitID: unit.ID, Text: unit.Text}) } } return units } func contextualSelectorKey(name string, refs []RegistryRef) string { key, _ := json.Marshal(struct { Name string `json:"name"` RegistryRefs []RegistryRef `json:"registry_refs"` }{Name: name, RegistryRefs: cloneRegistryRefs(refs)}) return string(key) } func cloneRegistryRefs(values []RegistryRef) []RegistryRef { if len(values) == 0 { return []RegistryRef{} } return append([]RegistryRef(nil), values...) } func cloneContextUnits(values []ContextUnit) []ContextUnit { if len(values) == 0 { return []ContextUnit{} } return append([]ContextUnit(nil), values...) }