Adopt registry-backed NPC occurrence artifacts
This commit is contained in:
@@ -32,7 +32,9 @@ type Registry struct {
|
||||
digest string
|
||||
projectionDigest string
|
||||
promptInput contracts.LLMInputMaterial
|
||||
identityInput contracts.LLMInputMaterial
|
||||
lookupByKey map[string]int
|
||||
lookupByID map[string]int
|
||||
}
|
||||
|
||||
// Resolver selects and memoizes immutable NPC registry views.
|
||||
@@ -109,7 +111,9 @@ func emptyRegistry() *Registry {
|
||||
canonical: append([]byte(nil), content...),
|
||||
projectionDigest: projectionDigest,
|
||||
promptInput: contracts.NewLLMInputMaterial(ReferenceSlot, npccodec.MediaType, content, projectionDigest, ""),
|
||||
identityInput: contracts.NewLLMInputMaterial(ReferenceSlot, npccodec.MediaType, content, projectionDigest, ""),
|
||||
lookupByKey: map[string]int{},
|
||||
lookupByID: map[string]int{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,8 +133,10 @@ func loadRegistry(referenceContent []byte) (*Registry, error) {
|
||||
|
||||
list := cloneNPCRegistry(value)
|
||||
lookupByKey := make(map[string]int, len(list.NPCs))
|
||||
lookupByID := make(map[string]int, len(list.NPCs))
|
||||
for index, npc := range list.NPCs {
|
||||
lookupByKey[identity.ComparisonKey(npc.Name)] = index
|
||||
lookupByID[npc.ID] = index
|
||||
}
|
||||
digest := semanticDigest(content)
|
||||
projection, err := nameProjection(list)
|
||||
@@ -138,6 +144,11 @@ func loadRegistry(referenceContent []byte) (*Registry, error) {
|
||||
return nil, fmt.Errorf("encode NPC name projection: %w", err)
|
||||
}
|
||||
projectionDigest := semanticDigest(projection)
|
||||
identityProjection, err := identityProjection(list)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("encode NPC identity projection: %w", err)
|
||||
}
|
||||
identityProjectionDigest := semanticDigest(identityProjection)
|
||||
return &Registry{
|
||||
bound: true,
|
||||
list: list,
|
||||
@@ -146,6 +157,8 @@ func loadRegistry(referenceContent []byte) (*Registry, error) {
|
||||
projectionDigest: projectionDigest,
|
||||
promptInput: contracts.NewLLMInputMaterial(ReferenceSlot, npccodec.MediaType, projection, projectionDigest, ""),
|
||||
lookupByKey: lookupByKey,
|
||||
identityInput: contracts.NewLLMInputMaterial(ReferenceSlot, npccodec.MediaType, identityProjection, identityProjectionDigest, ""),
|
||||
lookupByID: lookupByID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -211,6 +224,15 @@ func (r *Registry) PromptInput() contracts.LLMInputMaterial {
|
||||
return r.promptInput.Clone()
|
||||
}
|
||||
|
||||
// IdentityPromptInput returns the ordered ID/name projection for consumers
|
||||
// that must bind output records to exact registry identities.
|
||||
func (r *Registry) IdentityPromptInput() contracts.LLMInputMaterial {
|
||||
if r == nil {
|
||||
return contracts.LLMInputMaterial{}
|
||||
}
|
||||
return r.identityInput.Clone()
|
||||
}
|
||||
|
||||
// Lookup returns the canonical NPC for an exact canonical-name match under the
|
||||
// NPC identity comparison policy.
|
||||
func (r *Registry) Lookup(value string) (dnd.NPC, bool) {
|
||||
@@ -224,6 +246,18 @@ func (r *Registry) Lookup(value string) (dnd.NPC, bool) {
|
||||
return cloneNPC(r.list.NPCs[index]), true
|
||||
}
|
||||
|
||||
// LookupID returns the canonical NPC for an exact durable ID.
|
||||
func (r *Registry) LookupID(value string) (dnd.NPC, bool) {
|
||||
if r == nil {
|
||||
return dnd.NPC{}, false
|
||||
}
|
||||
index, ok := r.lookupByID[value]
|
||||
if !ok {
|
||||
return dnd.NPC{}, false
|
||||
}
|
||||
return cloneNPC(r.list.NPCs[index]), true
|
||||
}
|
||||
|
||||
func semanticDigest(content []byte) string {
|
||||
sum := sha256.Sum256(content)
|
||||
return "sha256:" + hex.EncodeToString(sum[:])
|
||||
@@ -233,10 +267,19 @@ type projectedNPC struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type identityProjectedNPC struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type projectedNPCRegistry struct {
|
||||
NPCs []projectedNPC `json:"npcs"`
|
||||
}
|
||||
|
||||
type identityProjectedNPCRegistry struct {
|
||||
NPCs []identityProjectedNPC `json:"npcs"`
|
||||
}
|
||||
|
||||
func nameProjection(list dnd.NPCRegistry) ([]byte, error) {
|
||||
projection := projectedNPCRegistry{NPCs: make([]projectedNPC, len(list.NPCs))}
|
||||
for index, npc := range list.NPCs {
|
||||
@@ -245,6 +288,14 @@ func nameProjection(list dnd.NPCRegistry) ([]byte, error) {
|
||||
return json.Marshal(projection)
|
||||
}
|
||||
|
||||
func identityProjection(list dnd.NPCRegistry) ([]byte, error) {
|
||||
projection := identityProjectedNPCRegistry{NPCs: make([]identityProjectedNPC, len(list.NPCs))}
|
||||
for index, npc := range list.NPCs {
|
||||
projection.NPCs[index] = identityProjectedNPC{ID: npc.ID, Name: npc.Name}
|
||||
}
|
||||
return json.Marshal(projection)
|
||||
}
|
||||
|
||||
func formatIdentityIssues(issues []identity.Issue) string {
|
||||
parts := make([]string, len(issues))
|
||||
for index, issue := range issues {
|
||||
|
||||
Reference in New Issue
Block a user