Order spell and NPC extraction by document position

This commit is contained in:
2026-07-24 14:29:19 +00:00
parent 83fde83a58
commit 9bbf2535dd
8 changed files with 138 additions and 111 deletions

View File

@@ -284,6 +284,57 @@ func TestExtractOrdersAndDeduplicatesEvidence(t *testing.T) {
}
}
func TestExtractUsesDocumentOrderForReferencesAndSpellCasts(t *testing.T) {
client := &fakeSpellsLLMClient{response: extractionResponse{SpellCasts: []spellCastResponse{
{Caster: "Later", Spell: "Fire Bolt", SourceRefs: responseSourceRefs(10, 10)},
{Caster: "Earlier", Spell: "Cure Wounds", SourceRefs: []spellSourceRefResponse{
{StartUnitID: 10, EndUnitID: 10},
{StartUnitID: 30, EndUnitID: 30},
{StartUnitID: 30, EndUnitID: 30},
{StartUnitID: 999, EndUnitID: 0},
}},
{Caster: "Unavailable", Spell: "Healing Word", SourceRefs: []spellSourceRefResponse{{StartUnitID: 999, EndUnitID: 0}}},
}}}
req := extractionRequest()
req.Source.Units = []source.SourceUnit{{ID: 30}, {ID: 10}}
req.Chunk.Units = append([]source.SourceUnit(nil), req.Source.Units...)
req.Chunk.Ref = source.SourceRef{SourceID: req.Source.ID, StartUnitID: 30, EndUnitID: 10}
result, err := newExtractor(t, client).Extract(context.Background(), req)
if err != nil {
t.Fatalf("Extract() error = %v", err)
}
if got := []string{result.Value.SpellCasts[0].Spell, result.Value.SpellCasts[1].Spell, result.Value.SpellCasts[2].Spell}; !reflect.DeepEqual(got, []string{"Cure Wounds", "Fire Bolt", "Healing Word"}) {
t.Fatalf("spell order = %#v, want document chronology followed by invalid evidence", got)
}
refs := result.Value.SpellCasts[0].SourceRefs
if got := []int{refs[0].StartUnitID, refs[1].StartUnitID, refs[2].StartUnitID}; !reflect.DeepEqual(got, []int{30, 10, 999}) {
t.Fatalf("source refs = %#v, want document order with exact duplicate removed", refs)
}
refs[0].StartUnitID = 777
for _, spell := range client.response.SpellCasts {
for _, ref := range spell.SourceRefs {
if ref.StartUnitID == 777 {
t.Fatal("result source references alias the model response")
}
}
}
}
func TestExtractPreservesStableSpellOrderForEqualEvidence(t *testing.T) {
client := &fakeSpellsLLMClient{response: extractionResponse{SpellCasts: []spellCastResponse{
{Caster: "First", Spell: "Cure Wounds", SourceRefs: responseSourceRefs(2, 2)},
{Caster: "Second", Spell: "Fire Bolt", SourceRefs: responseSourceRefs(2, 2)},
}}}
result, err := newExtractor(t, client).Extract(context.Background(), extractionRequest())
if err != nil {
t.Fatalf("Extract() error = %v", err)
}
if got := []string{result.Value.SpellCasts[0].Caster, result.Value.SpellCasts[1].Caster}; !reflect.DeepEqual(got, []string{"First", "Second"}) {
t.Fatalf("equal-evidence order = %#v, want stable response order", got)
}
}
func TestExtractPreservesInvalidEvidenceForValidators(t *testing.T) {
client := &fakeSpellsLLMClient{response: extractionResponse{SpellCasts: []spellCastResponse{{
Caster: "Aria", Spell: "Cure Wounds",