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