Ground NPC occurrences by canonical names

This commit is contained in:
2026-08-08 14:37:54 +00:00
parent 516af12916
commit ece1bca460
18 changed files with 1088 additions and 98 deletions

View File

@@ -14,7 +14,7 @@ import (
const (
Key = "dnd/npc-occurrences"
mappingPolicy = "dnd.npc_occurrences.extract_mapping.v2"
mappingPolicy = "dnd.npc_occurrences.extract_mapping.v3"
)
const (
@@ -132,7 +132,7 @@ func (e *Extractor) CheckpointFingerprints() []pipeline.CheckpointFingerprint {
{Name: "prompt", Value: e.promptSHA},
{Name: "response_schema", Value: e.responseSchemaSHA},
{Name: "mapping_policy", Value: mappingPolicy},
{Name: "npc_registry", Value: seeded.IdentityPromptInput().Digest},
{Name: "npc_registry", Value: seeded.IdentityDigest()},
}
}
@@ -158,7 +158,7 @@ func (e *Extractor) Extract(ctx context.Context, req contracts.TypedExtractionRe
var response extractionResponse
inputs := shared.PromptInputs(sourceInput, req.References)
inputs[NPCRegistryReferenceSlot] = npcRegistry.IdentityPromptInput()
inputs[NPCRegistryReferenceSlot] = npcRegistry.PromptInput()
if _, err := e.llm.CompleteStructured(ctx, contracts.StructuredCompletionRequest{
StageName: Key,
PromptID: PromptID,
@@ -170,26 +170,13 @@ func (e *Extractor) Extract(ctx context.Context, req contracts.TypedExtractionRe
return contracts.TypedExtractionResult[dnd.NPCOccurrenceList]{}, extractorErrorf("complete structured output: %w", err)
}
canonicalizeResponse(&response, order, req.Source.ID)
value := canonicalOccurrenceList(response, req.Source.ID)
if err := validateRegistryPairs(value, npcRegistry); err != nil {
return contracts.TypedExtractionResult[dnd.NPCOccurrenceList]{}, extractorErrorf("validate NPC registry pairs: %w", err)
value, err := canonicalOccurrenceList(response, req.Source.ID, npcRegistry)
if err != nil {
return contracts.TypedExtractionResult[dnd.NPCOccurrenceList]{}, extractorErrorf("resolve NPC names against registry: %w", err)
}
return contracts.TypedExtractionResult[dnd.NPCOccurrenceList]{Value: value}, nil
}
func validateRegistryPairs(value dnd.NPCOccurrenceList, registry *npcregistry.Registry) error {
for index, occurrence := range value.Occurrences {
canonical, ok := registry.LookupID(occurrence.NPCID)
if !ok {
return fmt.Errorf("occurrences[%d].npc_id is not in the NPC registry", index)
}
if occurrence.Name != canonical.Name {
return fmt.Errorf("occurrences[%d].name does not match npc_id", index)
}
}
return nil
}
func ModuleSpec() pipeline.ModuleSpec {
return pipeline.ModuleSpec{
Key: Key,