Adopt registry-backed NPC occurrence artifacts

This commit is contained in:
2026-08-05 18:55:58 +00:00
parent 3f4a1f2647
commit 5e2ccffc0f
42 changed files with 676 additions and 528 deletions

View File

@@ -36,7 +36,7 @@ func TestNPCInteractionPipelineUsesAcceptedRegistryAndCurrentEvidence(t *testing
}
request := client.requestFor(t, interactionextract.PromptID)
wantRegistry := `{"npcs":[{"name":"Mira Thorn"},{"name":"Hooded Guard"}]}`
wantRegistry := `{"npcs":[{"id":"` + identity.DeriveID("Mira Thorn") + `","name":"Mira Thorn"},{"id":"` + identity.DeriveID("Hooded Guard") + `","name":"Hooded Guard"}]}`
if got := string(request.Inputs["npc_registry"].Content); got != wantRegistry {
t.Fatalf("interaction registry input = %s, want names-only projection %s", got, wantRegistry)
}
@@ -52,25 +52,25 @@ func TestNPCInteractionPipelineUsesAcceptedRegistryAndCurrentEvidence(t *testing
if err != nil {
t.Fatalf("Decode(interaction output) error = %v", err)
}
if len(interactions.Interactions) != 2 {
if len(interactions.Occurrences) != 2 {
t.Fatalf("interactions = %#v, want two occurrences", interactions)
}
first, second := interactions.Interactions[0], interactions.Interactions[1]
first, second := interactions.Occurrences[0], interactions.Occurrences[1]
if first.Name != "Mira Thorn" || string(first.Kind) != "dialogue" || second.Name != "Hooded Guard" || string(second.Kind) != "noncombat_presence" {
t.Fatalf("interactions = %#v, want canonical names, kinds, and source chronology", interactions)
}
assertInteractionEvidence(t, first.SourceRefs)
assertInteractionEvidence(t, second.SourceRefs)
if first.SourceRefs[0].StartUnitID >= second.SourceRefs[0].StartUnitID {
t.Fatalf("interaction chronology = %#v, want source order", interactions.Interactions)
t.Fatalf("interaction chronology = %#v, want source order", interactions.Occurrences)
}
var durable map[string]json.RawMessage
if err := json.Unmarshal(serialized.Artifact.Content, &durable); err != nil {
t.Fatalf("unmarshal durable interaction payload: %v", err)
}
if len(durable) != 1 || durable["interactions"] == nil {
t.Fatalf("durable interaction payload = %#v, want only interactions", durable)
if len(durable) != 1 || durable["occurrences"] == nil {
t.Fatalf("durable interaction payload = %#v, want only occurrences", durable)
}
}
@@ -113,8 +113,8 @@ func TestSemanticNPCNormalizationCrossesOrderedRegistryHandoff(t *testing.T) {
t.Fatalf("interaction output step = %q, want ordered downstream step", interactionOutput.StepID)
}
registryRequest := client.requestFor(t, interactionextract.PromptID)
if got := string(registryRequest.Inputs["npc_registry"].Content); got != `{"npcs":[{"name":"Mira Thorn"}]}` {
t.Fatalf("downstream registry = %s, want one canonical names-only NPC", got)
if got := string(registryRequest.Inputs["npc_registry"].Content); got != `{"npcs":[{"id":"`+identity.DeriveID("Mira Thorn")+`","name":"Mira Thorn"}]}` {
t.Fatalf("downstream registry = %s, want one canonical NPC identity", got)
}
manifestContent, err := json.Marshal(output.Manifest)
if err != nil {
@@ -228,9 +228,9 @@ func (client *npcInteractionLLMClient) CompleteStructured(ctx context.Context, r
case npcnormalize.PromptID:
payload = map[string]any{"duplicate_groups": []any{}}
case interactionextract.PromptID:
payload = map[string]any{"interactions": []any{
map[string]any{"name": "Hooded Guard", "kind": "noncombat_presence", "source_refs": []any{map[string]int{"start_unit_id": 3, "end_unit_id": 3}}},
map[string]any{"name": " mira thorn ", "kind": "dialogue", "source_refs": []any{map[string]int{"start_unit_id": 2, "end_unit_id": 2}}},
payload = map[string]any{"occurrences": []any{
map[string]any{"npc_id": identity.DeriveID("Hooded Guard"), "name": "Hooded Guard", "kind": "noncombat_presence", "source_refs": []any{map[string]int{"start_unit_id": 3, "end_unit_id": 3}}},
map[string]any{"npc_id": identity.DeriveID("Mira Thorn"), "name": "Mira Thorn", "kind": "dialogue", "source_refs": []any{map[string]int{"start_unit_id": 2, "end_unit_id": 2}}},
}}
default:
return contracts.StructuredCompletionResponse{}, fmt.Errorf("unexpected interaction prompt %q", request.PromptID)
@@ -291,7 +291,7 @@ func (client *semanticNPCInteractionClient) CompleteStructured(_ context.Context
case npcnormalize.PromptID:
payload = map[string]any{"duplicate_groups": []any{map[string]any{"members": []string{"candidate-000001", "candidate-000002"}, "canonical": "candidate-000001"}}}
case interactionextract.PromptID:
payload = map[string]any{"interactions": []any{map[string]any{"name": "Mira Thorn", "kind": "dialogue", "source_refs": []any{map[string]int{"start_unit_id": 1, "end_unit_id": 1}}}}}
payload = map[string]any{"occurrences": []any{map[string]any{"npc_id": identity.DeriveID("Mira Thorn"), "name": "Mira Thorn", "kind": "dialogue", "source_refs": []any{map[string]int{"start_unit_id": 1, "end_unit_id": 1}}}}}
default:
return contracts.StructuredCompletionResponse{}, fmt.Errorf("unexpected semantic pipeline prompt %q", request.PromptID)
}