Implement integer source units and chunk payloads
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
Source references must use 1-based integer source-unit numbers from the
|
||||
transcript, where 1 is the first provided source unit.
|
||||
Source references must use integer source-unit IDs from the transcript.
|
||||
|
||||
Return only D&D spell-cast artifacts. For each spell cast, identify the in-world
|
||||
caster, spell name, effect, narrative description, and source references using
|
||||
|
||||
@@ -244,10 +244,14 @@ func (dndSpellsChunker) Chunk(ctx context.Context, req contracts.ChunkRequest) (
|
||||
return contracts.ChunkResult{
|
||||
Chunks: []contracts.SourceChunk{
|
||||
{
|
||||
ID: req.Source.ID + ":chunk:0",
|
||||
SourceID: req.Source.ID,
|
||||
Index: 0,
|
||||
Units: append([]source.SourceUnit(nil), req.Source.Units...),
|
||||
ID: req.Source.ID + ":chunk:0",
|
||||
SourceID: req.Source.ID,
|
||||
Index: 0,
|
||||
StartUnitID: req.Source.Units[0].ID,
|
||||
EndUnitID: req.Source.Units[len(req.Source.Units)-1].ID,
|
||||
Content: []byte(`{"units":[1,2,3]}`),
|
||||
MediaType: "application/json",
|
||||
Units: append([]source.SourceUnit(nil), req.Source.Units...),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
||||
@@ -73,7 +73,7 @@ func TestExtractReturnsSpellCandidateFromStructuredOutput(t *testing.T) {
|
||||
if payload != wantPayload {
|
||||
t.Fatalf("payload = %#v, want %#v", payload, wantPayload)
|
||||
}
|
||||
wantRef := source.SourceRef{SourceID: "session-alpha", StartUnitID: "seg-001", EndUnitID: "seg-002"}
|
||||
wantRef := source.SourceRef{SourceID: "session-alpha", StartUnitID: 1, EndUnitID: 2}
|
||||
if len(candidate.SourceRefs) != 1 || candidate.SourceRefs[0] != wantRef {
|
||||
t.Fatalf("SourceRefs = %#v, want %#v", candidate.SourceRefs, []source.SourceRef{wantRef})
|
||||
}
|
||||
@@ -254,14 +254,14 @@ func TestExtractPreservesResponseOrder(t *testing.T) {
|
||||
Spell: "Cure Wounds",
|
||||
Effect: "Heals.",
|
||||
NarrativeDescription: "First spell.",
|
||||
SourceRefs: responseSourceRefs("session-alpha", "seg-001", "seg-001"),
|
||||
SourceRefs: responseSourceRefs("session-alpha", 1, 1),
|
||||
},
|
||||
{
|
||||
Caster: "Bandit Shaman",
|
||||
Spell: "Fire Bolt",
|
||||
Effect: "Burns.",
|
||||
NarrativeDescription: "Second spell.",
|
||||
SourceRefs: responseSourceRefs("session-alpha", "seg-002", "seg-002"),
|
||||
SourceRefs: responseSourceRefs("session-alpha", 2, 2),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -296,7 +296,7 @@ func TestExtractCopiesCandidateSourceRefs(t *testing.T) {
|
||||
Spell: "Cure Wounds",
|
||||
Effect: "Heals.",
|
||||
NarrativeDescription: "Aria heals.",
|
||||
SourceRefs: responseSourceRefs("session-alpha", "seg-001", "seg-002"),
|
||||
SourceRefs: responseSourceRefs("session-alpha", 1, 2),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -306,10 +306,10 @@ func TestExtractCopiesCandidateSourceRefs(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Extract() error = %v, want nil", err)
|
||||
}
|
||||
client.response.SpellCasts[0].SourceRefs[0].StartUnitID = dnd.UnitRefFromString("mutated")
|
||||
client.response.SpellCasts[0].SourceRefs[0].StartUnitID = dnd.UnitRefFromInt(99)
|
||||
|
||||
if got := result.Candidates[0].SourceRefs[0].StartUnitID; got != "seg-001" {
|
||||
t.Fatalf("candidate source ref start = %q, want copied seg-001", got)
|
||||
if got := result.Candidates[0].SourceRefs[0].StartUnitID; got != 1 {
|
||||
t.Fatalf("candidate source ref start = %d, want copied 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +322,7 @@ func extractionRequestWithClient(client contracts.StructuredLLMClient) contracts
|
||||
return req
|
||||
}
|
||||
|
||||
const spellTranscriptJSON = `{"id":"session-alpha","segments":[{"id":"seg-001","text":"Aria raises her hand and casts Cure Wounds."}]}`
|
||||
const spellTranscriptJSON = `{"id":"session-alpha","segments":[{"id":1,"text":"Aria raises her hand and casts Cure Wounds."}]}`
|
||||
|
||||
func spellSourceInput() contracts.LLMInputMaterial {
|
||||
return contracts.NewLLMInputMaterial("source", "application/json", []byte(spellTranscriptJSON), "sha256:transcript", "file:///session-alpha.json")
|
||||
|
||||
@@ -26,14 +26,14 @@ func TestRunnerProcessesSeriatimInputWithDNDSpellsExtractor(t *testing.T) {
|
||||
Spell: "Cure Wounds",
|
||||
Effect: "Heals an injured ally.",
|
||||
NarrativeDescription: "Aria restores the fighter after the fight.",
|
||||
SourceRefs: responseSourceRefs(expectedDoc.ID, "seg-001", "seg-001"),
|
||||
SourceRefs: responseSourceRefs(expectedDoc.ID, 1, 1),
|
||||
},
|
||||
{
|
||||
Caster: "Borin",
|
||||
Spell: "Fire Bolt",
|
||||
Effect: "Scorches the wight.",
|
||||
NarrativeDescription: "Borin hurls fire at the wight.",
|
||||
SourceRefs: responseSourceRefs(expectedDoc.ID, "seg-003", "seg-003"),
|
||||
SourceRefs: responseSourceRefs(expectedDoc.ID, 3, 3),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -122,7 +122,7 @@ func TestRunnerPassesPartyAndGlossaryReferencesToDNDSpellsPrompt(t *testing.T) {
|
||||
Spell: "Fire Bolt",
|
||||
Effect: "Scorches the wight.",
|
||||
NarrativeDescription: "Borin hurls fire at the wight.",
|
||||
SourceRefs: responseSourceRefs(expectedDoc.ID, "seg-003", "seg-003"),
|
||||
SourceRefs: responseSourceRefs(expectedDoc.ID, 3, 3),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -207,7 +207,7 @@ func TestRunnerRejectsDNDSpellCastWithInvalidSourceRef(t *testing.T) {
|
||||
Spell: "Cure Wounds",
|
||||
Effect: "Heals an injured ally.",
|
||||
NarrativeDescription: "Aria restores the fighter after the fight.",
|
||||
SourceRefs: responseSourceRefs("spell-session", "seg-999", "seg-999"),
|
||||
SourceRefs: responseSourceRefs("spell-session", 999, 999),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -12,11 +12,15 @@ import (
|
||||
func promptExtractionRequest() contracts.ExtractionRequest {
|
||||
doc := promptSourceDocument()
|
||||
chunk := &contracts.SourceChunk{
|
||||
ID: "session-alpha:chunk:0",
|
||||
SourceID: doc.ID,
|
||||
Index: 0,
|
||||
Units: append([]source.SourceUnit(nil), doc.Units...),
|
||||
Metadata: map[string]any{"ignored": "chunk metadata"},
|
||||
ID: "session-alpha:chunk:0",
|
||||
SourceID: doc.ID,
|
||||
Index: 0,
|
||||
StartUnitID: doc.Units[0].ID,
|
||||
EndUnitID: doc.Units[len(doc.Units)-1].ID,
|
||||
Content: []byte(`{"units":[1,2]}`),
|
||||
MediaType: "application/json",
|
||||
Units: append([]source.SourceUnit(nil), doc.Units...),
|
||||
Metadata: map[string]any{"ignored": "chunk metadata"},
|
||||
}
|
||||
return contracts.ExtractionRequest{
|
||||
Source: doc,
|
||||
@@ -32,7 +36,7 @@ func promptSourceDocument() *source.SourceDocument {
|
||||
Digest: "sha256:test",
|
||||
Units: []source.SourceUnit{
|
||||
{
|
||||
ID: "seg-001",
|
||||
ID: 1,
|
||||
Kind: "transcript_segment",
|
||||
Text: "Aria raises her hand and casts Cure Wounds.",
|
||||
Metadata: map[string]any{
|
||||
@@ -43,7 +47,7 @@ func promptSourceDocument() *source.SourceDocument {
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "seg-002",
|
||||
ID: 2,
|
||||
Kind: "transcript_segment",
|
||||
Text: "The fighter's wounds begin to close.",
|
||||
Metadata: map[string]any{"ignored": "not rendered"},
|
||||
@@ -61,12 +65,12 @@ func mustJSON(t *testing.T, value any) string {
|
||||
return string(encoded)
|
||||
}
|
||||
|
||||
func responseSourceRefs(sourceID string, startUnitID string, endUnitID string) []dnd.SourceRefResponse {
|
||||
func responseSourceRefs(sourceID string, startUnitID int, endUnitID int) []dnd.SourceRefResponse {
|
||||
return []dnd.SourceRefResponse{
|
||||
{
|
||||
SourceID: sourceID,
|
||||
StartUnitID: dnd.UnitRefFromString(startUnitID),
|
||||
EndUnitID: dnd.UnitRefFromString(endUnitID),
|
||||
StartUnitID: dnd.UnitRefFromInt(startUnitID),
|
||||
EndUnitID: dnd.UnitRefFromInt(endUnitID),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
},
|
||||
"segments": [
|
||||
{
|
||||
"id": "seg-001",
|
||||
"id": 1,
|
||||
"start": 0,
|
||||
"end": 4,
|
||||
"speaker": "Alice",
|
||||
"text": "Aria raises her holy symbol and casts Cure Wounds."
|
||||
},
|
||||
{
|
||||
"id": "seg-002",
|
||||
"id": 2,
|
||||
"start": 4,
|
||||
"end": 8,
|
||||
"speaker": "DM",
|
||||
"text": "The bandit mage casts Shield as the blow lands."
|
||||
},
|
||||
{
|
||||
"id": "seg-003",
|
||||
"id": 3,
|
||||
"start": 8,
|
||||
"end": 12,
|
||||
"speaker": "Bob",
|
||||
|
||||
@@ -130,22 +130,22 @@ func TestSourceRefValidatorRejectsInvalidRefs(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "unknown source id",
|
||||
ref: source.SourceRef{SourceID: "session-beta", StartUnitID: "seg-001", EndUnitID: "seg-002"},
|
||||
ref: source.SourceRef{SourceID: "session-beta", StartUnitID: 1, EndUnitID: 2},
|
||||
want: "does not match",
|
||||
},
|
||||
{
|
||||
name: "unknown start unit",
|
||||
ref: source.SourceRef{SourceID: "session-alpha", StartUnitID: "seg-999", EndUnitID: "seg-002"},
|
||||
ref: source.SourceRef{SourceID: "session-alpha", StartUnitID: 999, EndUnitID: 2},
|
||||
want: "start_unit_id",
|
||||
},
|
||||
{
|
||||
name: "unknown end unit",
|
||||
ref: source.SourceRef{SourceID: "session-alpha", StartUnitID: "seg-001", EndUnitID: "seg-999"},
|
||||
ref: source.SourceRef{SourceID: "session-alpha", StartUnitID: 1, EndUnitID: 999},
|
||||
want: "end_unit_id",
|
||||
},
|
||||
{
|
||||
name: "reversed unit range",
|
||||
ref: source.SourceRef{SourceID: "session-alpha", StartUnitID: "seg-002", EndUnitID: "seg-001"},
|
||||
ref: source.SourceRef{SourceID: "session-alpha", StartUnitID: 2, EndUnitID: 1},
|
||||
want: "appears after",
|
||||
},
|
||||
}
|
||||
@@ -234,7 +234,7 @@ func validSpellCandidate(index int) artifacts.ArtifactCandidate {
|
||||
Index: index,
|
||||
Payload: spellPayload(validSpellPayload()),
|
||||
SourceRefs: []source.SourceRef{
|
||||
{SourceID: "session-alpha", StartUnitID: "seg-001", EndUnitID: "seg-002"},
|
||||
{SourceID: "session-alpha", StartUnitID: 1, EndUnitID: 2},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user