Simplify enemy event grounding ownership
This commit is contained in:
@@ -16,6 +16,8 @@ import (
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
||||
combatturncodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/combatturns"
|
||||
interactioncodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/npcinteractions"
|
||||
npccodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/npcs"
|
||||
scenecodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/scenedescriptions"
|
||||
npcregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/npcs/registry"
|
||||
sceneregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/scenedescriptions/registry"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
|
||||
@@ -27,6 +29,7 @@ const (
|
||||
CombatTurnReferenceSlot = "combat_turns"
|
||||
NPCInteractionReferenceSlot = "npc_interactions"
|
||||
ReferenceMaxBytes = 1048576
|
||||
promptProjectionMediaType = "application/json"
|
||||
)
|
||||
|
||||
var referenceSlotDescriptions = shared.ReferenceSlotDescriptions{
|
||||
@@ -43,7 +46,7 @@ func referenceSlots() []contracts.ReferenceSlot {
|
||||
Name: NPCRegistryReferenceSlot,
|
||||
Description: "Required normalized NPC registry used only for enemy-subject grounding, never as event evidence.",
|
||||
Required: true,
|
||||
AcceptedMediaTypes: []string{combatturncodec.MediaType},
|
||||
AcceptedMediaTypes: []string{npccodec.MediaType},
|
||||
AcceptedArtifactKinds: []contracts.ArtifactKind{dnd.NPCListKind},
|
||||
MaxBytes: ReferenceMaxBytes,
|
||||
},
|
||||
@@ -51,7 +54,7 @@ func referenceSlots() []contracts.ReferenceSlot {
|
||||
Name: SceneDescriptionReferenceSlot,
|
||||
Description: "Required scene descriptions used only to determine exact combat eligibility, never as event evidence.",
|
||||
Required: true,
|
||||
AcceptedMediaTypes: []string{combatturncodec.MediaType},
|
||||
AcceptedMediaTypes: []string{scenecodec.MediaType},
|
||||
AcceptedArtifactKinds: []contracts.ArtifactKind{dnd.SceneDescriptionListKind},
|
||||
MaxBytes: ReferenceMaxBytes,
|
||||
},
|
||||
@@ -88,10 +91,9 @@ type groundingResolver struct {
|
||||
}
|
||||
|
||||
type grounding struct {
|
||||
npcInput contracts.LLMInputMaterial
|
||||
combatTurnInput contracts.LLMInputMaterial
|
||||
npcInteractionInput contracts.LLMInputMaterial
|
||||
sceneEligibilityView *sceneregistry.Registry
|
||||
npcInput contracts.LLMInputMaterial
|
||||
combatTurnInput contracts.LLMInputMaterial
|
||||
npcInteractionInput contracts.LLMInputMaterial
|
||||
}
|
||||
|
||||
func newGroundingResolver(references contracts.ReferenceSet) (*groundingResolver, error) {
|
||||
@@ -130,10 +132,6 @@ func (r *groundingResolver) Resolve(references contracts.ReferenceSet) (groundin
|
||||
if !npcs.Bound() {
|
||||
return grounding{}, fmt.Errorf("NPC registry reference is required")
|
||||
}
|
||||
scenes, err := r.resolveScenes(references)
|
||||
if err != nil {
|
||||
return grounding{}, err
|
||||
}
|
||||
combatTurns, err := resolveInput(references, CombatTurnReferenceSlot, r.combatTurns, prepareCombatTurnInput)
|
||||
if err != nil {
|
||||
return grounding{}, err
|
||||
@@ -143,10 +141,9 @@ func (r *groundingResolver) Resolve(references contracts.ReferenceSet) (groundin
|
||||
return grounding{}, err
|
||||
}
|
||||
return grounding{
|
||||
npcInput: npcs.PromptInput(),
|
||||
combatTurnInput: combatTurns,
|
||||
npcInteractionInput: npcInteractions,
|
||||
sceneEligibilityView: scenes,
|
||||
npcInput: npcs.PromptInput(),
|
||||
combatTurnInput: combatTurns,
|
||||
npcInteractionInput: npcInteractions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -197,10 +194,6 @@ func (g grounding) PromptInputs() contracts.LLMInputSet {
|
||||
}
|
||||
}
|
||||
|
||||
func (g grounding) SceneMatch(chunk *source.Chunk) sceneregistry.ChunkMatch {
|
||||
return g.sceneEligibilityView.Match(chunk)
|
||||
}
|
||||
|
||||
func prepareCombatTurnInput(references contracts.ReferenceSet) (*contracts.LLMInputMaterial, error) {
|
||||
item, ok, err := referenceItem(references, CombatTurnReferenceSlot, combatturncodec.MediaType)
|
||||
if err != nil || !ok {
|
||||
@@ -295,6 +288,6 @@ func projectNPCInteractions(interactions []dnd.NPCInteraction) []npcInteractionP
|
||||
|
||||
func newPromptInput(name string, content []byte) *contracts.LLMInputMaterial {
|
||||
sum := sha256.Sum256(content)
|
||||
material := contracts.NewLLMInputMaterial(name, combatturncodec.MediaType, content, "sha256:"+hex.EncodeToString(sum[:]), "")
|
||||
material := contracts.NewLLMInputMaterial(name, promptProjectionMediaType, content, "sha256:"+hex.EncodeToString(sum[:]), "")
|
||||
return &material
|
||||
}
|
||||
|
||||
@@ -104,15 +104,27 @@ func TestGroundingResolvesGeneratedReferencesAndSceneEligibility(t *testing.T) {
|
||||
if got := string(resolved.PromptInputs()[NPCRegistryReferenceSlot].Content); got != `{"npcs":[{"name":"Grimjaw"}]}` {
|
||||
t.Fatalf("generated NPC projection = %s", got)
|
||||
}
|
||||
if resolved.SceneMatch(combatChunk()) != (sceneregistry.ChunkMatch{State: sceneregistry.MatchExact, Kind: dnd.SceneKindNarrative}) {
|
||||
t.Fatalf("generated scene match = %#v", resolved.SceneMatch(combatChunk()))
|
||||
match, err := resolver.SceneMatch(generated, combatChunk())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resolved.SceneMatch(&source.Chunk{ID: "other", Ref: combatChunk().Ref}).State != sceneregistry.MatchMissing {
|
||||
if match != (sceneregistry.ChunkMatch{State: sceneregistry.MatchExact, Kind: dnd.SceneKindNarrative}) {
|
||||
t.Fatalf("generated scene match = %#v", match)
|
||||
}
|
||||
match, err = resolver.SceneMatch(generated, &source.Chunk{ID: "other", Ref: combatChunk().Ref})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if match.State != sceneregistry.MatchMissing {
|
||||
t.Fatal("missing scene was not reported")
|
||||
}
|
||||
mismatched := combatChunk()
|
||||
mismatched.Ref.EndUnitID++
|
||||
if resolved.SceneMatch(mismatched).State != sceneregistry.MatchMismatched {
|
||||
match, err = resolver.SceneMatch(generated, mismatched)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if match.State != sceneregistry.MatchMismatched {
|
||||
t.Fatal("mismatched scene was not reported")
|
||||
}
|
||||
|
||||
@@ -123,8 +135,12 @@ func TestGroundingResolvesGeneratedReferencesAndSceneEligibility(t *testing.T) {
|
||||
if got := string(static.PromptInputs()[NPCRegistryReferenceSlot].Content); got != `{"npcs":[{"name":"Ashfang"}]}` {
|
||||
t.Fatalf("static NPC projection changed after generated resolution: %s", got)
|
||||
}
|
||||
if static.SceneMatch(combatChunk()) != (sceneregistry.ChunkMatch{State: sceneregistry.MatchExact, Kind: dnd.SceneKindCombat}) {
|
||||
t.Fatalf("static scene match = %#v", static.SceneMatch(combatChunk()))
|
||||
match, err = resolver.SceneMatch(contracts.ReferenceSet{}, combatChunk())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if match != (sceneregistry.ChunkMatch{State: sceneregistry.MatchExact, Kind: dnd.SceneKindCombat}) {
|
||||
t.Fatalf("static scene match = %#v", match)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,8 +160,13 @@ func TestGroundingRejectsMissingAndInvalidReferences(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := resolver.Resolve(contracts.ReferenceSet{}); err == nil || !strings.Contains(err.Error(), test.want) {
|
||||
t.Fatalf("Resolve() error = %v, want missing %q reference", err, test.slot)
|
||||
if test.slot == SceneDescriptionReferenceSlot {
|
||||
_, err = resolver.SceneMatch(contracts.ReferenceSet{}, combatChunk())
|
||||
} else {
|
||||
_, err = resolver.Resolve(contracts.ReferenceSet{})
|
||||
}
|
||||
if err == nil || !strings.Contains(err.Error(), test.want) {
|
||||
t.Fatalf("grounding error = %v, want missing %q reference", err, test.slot)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user