Consolidate grounded event extraction instructions
This commit is contained in:
@@ -1,14 +1,25 @@
|
|||||||
|
Extract Dungeons & Dragons enemy events from the supplied combat transcript.
|
||||||
|
An `engaged` event requires direct establishment that a subject is actively
|
||||||
|
opposing the party in combat. A `killed`, `fled`, `captured`, or
|
||||||
|
`incapacitated` event requires explicit establishment of that outcome. An
|
||||||
|
outcome may share evidence with an engagement, and a later engagement or
|
||||||
|
outcome for the same subject remains a separate observation. Emit at most one
|
||||||
|
`engaged` observation for the same subject in this combat scene.
|
||||||
|
|
||||||
|
For `killed`, direct death or killing is required. For `fled`, the subject
|
||||||
|
must explicitly escape, retreat, or leave combat to avoid continued engagement.
|
||||||
|
For `captured`, the subject must be explicitly taken prisoner or secured
|
||||||
|
under the party's control. For `incapacitated`, the subject must be explicitly
|
||||||
|
unable to continue acting without being established as killed or captured.
|
||||||
|
|
||||||
|
When the transcript identifies a named NPC, use its normalized registry
|
||||||
|
spelling. A hostile creature without a registry entry is allowed. For unnamed
|
||||||
|
individuals or groups, use only the narrowest transcript-grounded label, such
|
||||||
|
as `Orcs`, `One orc`, or `Remaining orcs`; never invent member names, IDs,
|
||||||
|
or quantities.
|
||||||
|
|
||||||
Exclude party members, allies, neutral observers, mentioned-but-absent enemies,
|
Exclude party members, allies, neutral observers, mentioned-but-absent enemies,
|
||||||
hazards, traps, environmental effects, uncertain allegiance, table talk,
|
hazards, traps, environmental effects, uncertain allegiance, table talk,
|
||||||
planning, hypotheses, recaps outside this passage, and downstream inference.
|
planning, hypotheses, recaps outside this passage, and downstream inference.
|
||||||
|
|
||||||
Do not infer an engagement or outcome from initiative, turn absence, damage,
|
Do not infer an engagement or outcome from initiative, turn absence, damage,
|
||||||
defeat, movement, a scene ending, combat-opponent grounding, or any auxiliary
|
defeat, movement, or a scene ending.
|
||||||
artifact. Auxiliary inputs can guide attention but cannot prove or supply an
|
|
||||||
event. Cite only narrow current-transcript ranges that establish each event.
|
|
||||||
|
|
||||||
Return the `events` array even when no enemy event is established. Every event
|
|
||||||
must contain only `name`, `kind`, and `source_refs`. Use exactly one kind:
|
|
||||||
`engaged`, `killed`, `fled`, `captured`, or `incapacitated`. Each source range
|
|
||||||
uses integer `start_unit_id` and `end_unit_id`; omit `source_id` because
|
|
||||||
Notarius assigns the current source identity.
|
|
||||||
|
|||||||
@@ -42,8 +42,6 @@ messages:
|
|||||||
content_file: ./sharedassets/common-dnd-npcs.md
|
content_file: ./sharedassets/common-dnd-npcs.md
|
||||||
- role: user
|
- role: user
|
||||||
content_file: ./combat-grounding.md
|
content_file: ./combat-grounding.md
|
||||||
- role: user
|
|
||||||
content_file: ./task.md
|
|
||||||
- role: user
|
- role: user
|
||||||
content_file: ./instructions.md
|
content_file: ./instructions.md
|
||||||
cache_control:
|
cache_control:
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
Extract Dungeons & Dragons enemy events from the supplied combat transcript.
|
|
||||||
|
|
||||||
Return an `engaged` event only when the transcript directly establishes that a
|
|
||||||
subject is actively opposing the party in combat. Return `killed`, `fled`,
|
|
||||||
`captured`, or `incapacitated` only when the transcript explicitly establishes
|
|
||||||
that outcome. An outcome may share evidence with an engagement, and a later
|
|
||||||
engagement or outcome for the same subject remains a separate observation.
|
|
||||||
Emit at most one engagement for the same subject in this combat scene.
|
|
||||||
|
|
||||||
For `killed`, direct death or killing is required. For `fled`, the subject must
|
|
||||||
explicitly escape, retreat, or leave combat to avoid continued engagement. For
|
|
||||||
`captured`, the subject must be explicitly taken prisoner or secured under the
|
|
||||||
party's control. For `incapacitated`, the subject must be explicitly unable to
|
|
||||||
continue acting without being established as killed or captured.
|
|
||||||
|
|
||||||
Use a normalized NPC registry spelling when the transcript identifies that
|
|
||||||
named NPC. A hostile creature without a registry entry is allowed. For unnamed
|
|
||||||
individuals or groups, use only the narrowest transcript-grounded label, such
|
|
||||||
as `Orcs`, `One orc`, or `Remaining orcs`; never invent member names, IDs, or
|
|
||||||
quantities.
|
|
||||||
@@ -1,8 +1,24 @@
|
|||||||
Return the occurrences array even when no occurrence is established. Every
|
Extract Dungeons & Dragons location occurrences from the supplied transcript.
|
||||||
record must contain location_id, name, kind, and source_refs. Copy location_id
|
Include an occurrence only when the transcript establishes one supplied
|
||||||
and name from one supplied registry record, and cite only narrow transcript
|
location, one occurrence kind, and a coherent passage supporting both.
|
||||||
ranges that support both that location and its classified occurrence.
|
|
||||||
|
|
||||||
Do not summarize location descriptions, infer a missing registry record, or
|
Use exactly one kind per occurrence:
|
||||||
use registry context as evidence. Omit source_id; Notarius assigns the current
|
|
||||||
transcript source identity.
|
- visited: party members are physically present, arrive, remain, or depart;
|
||||||
|
- planned: the party explicitly proposes, intends, or agrees to future travel;
|
||||||
|
- recalled: the transcript explicitly recounts an earlier party visit; or
|
||||||
|
- mentioned: the location is explicitly referenced without stronger support,
|
||||||
|
including non-actionable speculation or a mere hypothetical reference.
|
||||||
|
|
||||||
|
A mere hypothetical or speculative reference is not planned unless the
|
||||||
|
transcript also establishes an actual proposal, intention, or agreement to
|
||||||
|
travel. When the hypothetical explicitly names a supplied location, it may be
|
||||||
|
mentioned.
|
||||||
|
|
||||||
|
For overlapping support, visited outranks planned, recalled, and mentioned;
|
||||||
|
planned outranks recalled and mentioned; recalled outranks mentioned. A passage
|
||||||
|
may produce multiple records when it independently establishes separate facts,
|
||||||
|
such as recalling an earlier visit while planning a return. Omit inferred,
|
||||||
|
unstated, uncertain, or unsupported places and occurrences. Do not infer a
|
||||||
|
location or occurrence from surrounding events when the transcript does not
|
||||||
|
state it. Do not summarize location descriptions.
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ messages:
|
|||||||
content_file: ./sharedassets/common-dnd-extraction-evidence.md
|
content_file: ./sharedassets/common-dnd-extraction-evidence.md
|
||||||
- role: user
|
- role: user
|
||||||
content_file: ./location-registry.md
|
content_file: ./location-registry.md
|
||||||
- role: user
|
|
||||||
content_file: ./task.md
|
|
||||||
- role: user
|
- role: user
|
||||||
content_file: ./instructions.md
|
content_file: ./instructions.md
|
||||||
cache_control:
|
cache_control:
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
Extract Dungeons & Dragons location occurrences from the supplied transcript.
|
|
||||||
|
|
||||||
Include an occurrence only when the transcript establishes one supplied
|
|
||||||
location, one occurrence kind, and a coherent passage supporting both. Use
|
|
||||||
only the exact ID and name pair from the supplied location registry. Return an
|
|
||||||
empty occurrences array when no supplied location has an evidenced occurrence
|
|
||||||
in this transcript passage.
|
|
||||||
|
|
||||||
Use exactly one kind per occurrence:
|
|
||||||
|
|
||||||
- visited: party members are physically present, arrive, remain, or depart;
|
|
||||||
- planned: the party explicitly proposes, intends, or agrees to future travel;
|
|
||||||
- recalled: the transcript explicitly recounts an earlier party visit; or
|
|
||||||
- mentioned: the location is explicitly referenced without stronger support,
|
|
||||||
including non-actionable speculation or a mere hypothetical reference.
|
|
||||||
|
|
||||||
A mere hypothetical or speculative reference is not planned unless the
|
|
||||||
transcript also establishes an actual proposal, intention, or agreement to
|
|
||||||
travel. When the hypothetical itself explicitly names a supplied registry
|
|
||||||
location, it may be mentioned using the narrow passage that supports that
|
|
||||||
reference.
|
|
||||||
|
|
||||||
For overlapping support, visited outranks planned, recalled, and mentioned;
|
|
||||||
planned outranks recalled and mentioned; recalled outranks mentioned. A passage
|
|
||||||
may produce multiple records when it independently establishes separate facts,
|
|
||||||
such as recalling an earlier visit while planning a return. Omit inferred,
|
|
||||||
unstated, uncertain, or unsupported places and occurrences. Do not infer a
|
|
||||||
location or occurrence from surrounding events when the transcript does not
|
|
||||||
state it.
|
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
Return the interactions array even when no interaction is established. Every
|
Extract Dungeons & Dragons NPC interaction occurrences from the supplied
|
||||||
record must contain name, kind, and source_refs. Cite transcript ranges that
|
transcript. Include an occurrence only when the transcript establishes one
|
||||||
support both the NPC identity and the interaction kind.
|
supplied NPC, one interaction kind, and a coherent passage supporting both.
|
||||||
|
Use only names from the supplied NPC registry.
|
||||||
|
|
||||||
|
Do not summarize, infer relationships, sentiment, factions, motives, aliases,
|
||||||
|
or persistent state. Do not identify player characters, anonymous groups, or
|
||||||
|
invented NPCs. Keep occurrences within this transcript chunk. Split records
|
||||||
|
when an NPC's interaction kind changes, when combat alignment changes, or when
|
||||||
|
an NPC is first mentioned and later becomes present.
|
||||||
|
|
||||||
Use exactly one kind per occurrence:
|
Use exactly one kind per occurrence:
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ messages:
|
|||||||
content_file: ./sharedassets/common-dnd-extraction-evidence.md
|
content_file: ./sharedassets/common-dnd-extraction-evidence.md
|
||||||
- role: user
|
- role: user
|
||||||
content_file: ./sharedassets/common-dnd-npcs.md
|
content_file: ./sharedassets/common-dnd-npcs.md
|
||||||
- role: user
|
|
||||||
content_file: ./task.md
|
|
||||||
- role: user
|
- role: user
|
||||||
content_file: ./instructions.md
|
content_file: ./instructions.md
|
||||||
cache_control:
|
cache_control:
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
Extract Dungeons & Dragons NPC interaction occurrences from the supplied
|
|
||||||
transcript.
|
|
||||||
|
|
||||||
Include an occurrence only when the transcript establishes one supplied NPC,
|
|
||||||
one interaction kind, and a coherent passage supporting both. Use only names
|
|
||||||
from the supplied NPC registry. The registry helps ground identity but never
|
|
||||||
proves that an interaction occurred.
|
|
||||||
|
|
||||||
Do not summarize, infer relationships, sentiment, factions, motives, aliases,
|
|
||||||
or persistent state. Do not identify player characters, anonymous groups, or
|
|
||||||
invented NPCs. Return an empty interactions array when no supplied NPC has an
|
|
||||||
evidenced interaction in this transcript passage.
|
|
||||||
|
|
||||||
Keep occurrences within this transcript chunk. Split records when an NPC's
|
|
||||||
interaction kind changes, when combat alignment changes, or when an NPC is
|
|
||||||
first mentioned and later becomes present.
|
|
||||||
@@ -18,7 +18,6 @@ var promptAssetManifest = shared.PromptAssetManifest{
|
|||||||
ModuleFiles: []promptfs.ModulePromptFile{
|
ModuleFiles: []promptfs.ModulePromptFile{
|
||||||
{Name: "prompt.yaml", Path: "prompts/prompt.yaml"},
|
{Name: "prompt.yaml", Path: "prompts/prompt.yaml"},
|
||||||
{Name: "combat-grounding.md", Path: "prompts/combat-grounding.md"},
|
{Name: "combat-grounding.md", Path: "prompts/combat-grounding.md"},
|
||||||
{Name: "task.md", Path: "prompts/task.md"},
|
|
||||||
{Name: "instructions.md", Path: "prompts/instructions.md"},
|
{Name: "instructions.md", Path: "prompts/instructions.md"},
|
||||||
},
|
},
|
||||||
SharedFiles: []string{
|
SharedFiles: []string{
|
||||||
|
|||||||
@@ -28,21 +28,32 @@ func TestRegisterPromptAssetsAndPrepareEnemyEventPrompt(t *testing.T) {
|
|||||||
if prepared.PromptID != PromptID || prepared.OutputContract.SchemaPath != "dnd_enemy_events_llm.v1.json" || prepared.SelectedProfileID != "dnd-extraction" {
|
if prepared.PromptID != PromptID || prepared.OutputContract.SchemaPath != "dnd_enemy_events_llm.v1.json" || prepared.SelectedProfileID != "dnd-extraction" {
|
||||||
t.Fatalf("prepared prompt = %#v", prepared)
|
t.Fatalf("prepared prompt = %#v", prepared)
|
||||||
}
|
}
|
||||||
|
referenceIndex := renderedMessageIndex(t, prepared.Messages, "enemy-player")
|
||||||
transcriptIndex := renderedMessageIndex(t, prepared.Messages, "enemy-transcript")
|
transcriptIndex := renderedMessageIndex(t, prepared.Messages, "enemy-transcript")
|
||||||
for _, sentinel := range []string{"enemy-npc", "enemy-turn", "enemy-opponent", "Extract Dungeons & Dragons enemy events"} {
|
if transcriptIndex <= referenceIndex {
|
||||||
|
t.Fatalf("transcript message index = %d, want after references index %d", transcriptIndex, referenceIndex)
|
||||||
|
}
|
||||||
|
groundingIndices := make([]int, 0, 3)
|
||||||
|
for _, sentinel := range []string{"enemy-npc", "enemy-turn", "enemy-opponent"} {
|
||||||
if index := renderedMessageIndex(t, prepared.Messages, sentinel); index <= transcriptIndex {
|
if index := renderedMessageIndex(t, prepared.Messages, sentinel); index <= transcriptIndex {
|
||||||
t.Fatalf("message containing %q has index %d, want after transcript index %d", sentinel, index, transcriptIndex)
|
t.Fatalf("message containing %q has index %d, want after transcript index %d", sentinel, index, transcriptIndex)
|
||||||
|
} else {
|
||||||
|
groundingIndices = append(groundingIndices, index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
instructionIndex := renderedMessageIndex(t, prepared.Messages, "Return the `events` array")
|
instructionIndex := renderedMessageIndex(t, prepared.Messages, "Extract Dungeons & Dragons enemy events")
|
||||||
if instructionIndex <= transcriptIndex {
|
for _, index := range groundingIndices {
|
||||||
t.Fatalf("instruction index = %d, want after transcript index %d", instructionIndex, transcriptIndex)
|
if instructionIndex <= index {
|
||||||
|
t.Fatalf("instruction index = %d, want after grounding index %d", instructionIndex, index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if instructionIndex != len(prepared.Messages)-1 {
|
if instructionIndex != len(prepared.Messages)-1 {
|
||||||
t.Fatalf("instruction message index = %d, want final message", instructionIndex)
|
t.Fatalf("instruction message index = %d, want final message", instructionIndex)
|
||||||
}
|
}
|
||||||
if cache := prepared.Messages[instructionIndex].CacheControl; cache == nil || cache.Type != promptkit.CacheControlEphemeral {
|
for _, index := range []int{referenceIndex, transcriptIndex, instructionIndex} {
|
||||||
t.Fatalf("final instruction cache control = %#v", cache)
|
if cache := prepared.Messages[index].CacheControl; cache == nil || cache.Type != promptkit.CacheControlEphemeral {
|
||||||
|
t.Fatalf("message %d cache control = %#v, want ephemeral", index, cache)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ var promptAssetManifest = shared.PromptAssetManifest{
|
|||||||
ModuleFiles: []promptfs.ModulePromptFile{
|
ModuleFiles: []promptfs.ModulePromptFile{
|
||||||
{Name: "prompt.yaml", Path: "prompts/prompt.yaml"},
|
{Name: "prompt.yaml", Path: "prompts/prompt.yaml"},
|
||||||
{Name: "location-registry.md", Path: "prompts/location-registry.md"},
|
{Name: "location-registry.md", Path: "prompts/location-registry.md"},
|
||||||
{Name: "task.md", Path: "prompts/task.md"},
|
|
||||||
{Name: "instructions.md", Path: "prompts/instructions.md"},
|
{Name: "instructions.md", Path: "prompts/instructions.md"},
|
||||||
},
|
},
|
||||||
SharedFiles: []string{
|
SharedFiles: []string{
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ func TestRegisterPromptAssetsPreparesLocationOccurrencePrompt(t *testing.T) {
|
|||||||
prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{
|
prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{
|
||||||
PromptID: PromptID, PromptVersion: SchemaVersion, ProfileID: "location-occurrences-test",
|
PromptID: PromptID, PromptVersion: SchemaVersion, ProfileID: "location-occurrences-test",
|
||||||
Inputs: map[string]promptkit.ArtifactRef{
|
Inputs: map[string]promptkit.ArtifactRef{
|
||||||
"transcript": promptkit.Inline(`{"units":[1]}`), "players": promptkit.Inline(" "), "party": promptkit.Inline(" "), "glossary": promptkit.Inline(" "),
|
"transcript": promptkit.Inline(`{"units":[{"sentinel":"location-occurrence-transcript"}]}`), "players": promptkit.Inline("location-occurrence-player"), "party": promptkit.Inline(" "), "glossary": promptkit.Inline(" "),
|
||||||
"locations": promptkit.Inline(`{"locations":[{"id":"location:sha256:test","name":"The Mill"}]}`),
|
"locations": promptkit.Inline(`{"locations":[{"id":"location:sha256:test","name":"location-occurrence-registry"}]}`),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -40,10 +40,17 @@ func TestRegisterPromptAssetsPreparesLocationOccurrencePrompt(t *testing.T) {
|
|||||||
t.Fatalf("prepared prompt = %#v", prepared)
|
t.Fatalf("prepared prompt = %#v", prepared)
|
||||||
}
|
}
|
||||||
var registryMessage string
|
var registryMessage string
|
||||||
|
referenceIndex := -1
|
||||||
|
transcriptIndex := -1
|
||||||
registryIndex := -1
|
registryIndex := -1
|
||||||
evidenceIndex := -1
|
evidenceIndex := -1
|
||||||
taskIndex := -1
|
|
||||||
for index, message := range prepared.Messages {
|
for index, message := range prepared.Messages {
|
||||||
|
if strings.Contains(message.Content, "location-occurrence-player") {
|
||||||
|
referenceIndex = index
|
||||||
|
}
|
||||||
|
if strings.Contains(message.Content, "location-occurrence-transcript") {
|
||||||
|
transcriptIndex = index
|
||||||
|
}
|
||||||
if strings.Contains(message.Content, "normalized location registry") {
|
if strings.Contains(message.Content, "normalized location registry") {
|
||||||
registryMessage = message.Content
|
registryMessage = message.Content
|
||||||
registryIndex = index
|
registryIndex = index
|
||||||
@@ -51,14 +58,28 @@ func TestRegisterPromptAssetsPreparesLocationOccurrencePrompt(t *testing.T) {
|
|||||||
if strings.Contains(message.Content, "Transcript units are the only evidence") {
|
if strings.Contains(message.Content, "Transcript units are the only evidence") {
|
||||||
evidenceIndex = index
|
evidenceIndex = index
|
||||||
}
|
}
|
||||||
if strings.Contains(message.Content, "Extract Dungeons & Dragons location occurrences") {
|
|
||||||
taskIndex = index
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !strings.Contains(registryMessage, "location:sha256:test") || !strings.Contains(registryMessage, "The Mill") || strings.Contains(registryMessage, "source_refs") {
|
if !strings.Contains(registryMessage, "location:sha256:test") || !strings.Contains(registryMessage, "location-occurrence-registry") || strings.Contains(registryMessage, "source_refs") {
|
||||||
t.Fatalf("rendered prompt did not preserve source-free registry grounding: %s", registryMessage)
|
t.Fatalf("rendered prompt did not preserve source-free registry grounding: %s", registryMessage)
|
||||||
}
|
}
|
||||||
if evidenceIndex < 0 || taskIndex < 0 || registryIndex <= evidenceIndex || registryIndex >= taskIndex {
|
instructionsIndex := len(prepared.Messages) - 1
|
||||||
t.Fatalf("registry prompt placement = evidence %d, registry %d, task %d", evidenceIndex, registryIndex, taskIndex)
|
if referenceIndex < 0 || transcriptIndex <= referenceIndex || evidenceIndex <= transcriptIndex || registryIndex <= evidenceIndex || instructionsIndex <= registryIndex {
|
||||||
|
t.Fatalf("message order = references %d, transcript %d, evidence %d, registry %d, instructions %d; want that order", referenceIndex, transcriptIndex, evidenceIndex, registryIndex, instructionsIndex)
|
||||||
|
}
|
||||||
|
for _, index := range []int{referenceIndex, transcriptIndex, instructionsIndex} {
|
||||||
|
if cache := prepared.Messages[index].CacheControl; cache == nil || cache.Type != promptkit.CacheControlEphemeral {
|
||||||
|
t.Fatalf("message %d cache control = %#v, want ephemeral", index, cache)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for index, message := range prepared.Messages {
|
||||||
|
if index != referenceIndex && strings.Contains(message.Content, "location-occurrence-player") {
|
||||||
|
t.Errorf("message %d unexpectedly rendered campaign-reference input", index)
|
||||||
|
}
|
||||||
|
if index != transcriptIndex && strings.Contains(message.Content, "location-occurrence-transcript") {
|
||||||
|
t.Errorf("message %d unexpectedly rendered transcript input", index)
|
||||||
|
}
|
||||||
|
if index != registryIndex && strings.Contains(message.Content, "location-occurrence-registry") {
|
||||||
|
t.Errorf("message %d unexpectedly rendered registry grounding input", index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ var promptAssetManifest = shared.PromptAssetManifest{
|
|||||||
ModuleDir: "dnd.npc_interactions",
|
ModuleDir: "dnd.npc_interactions",
|
||||||
ModuleFiles: []promptfs.ModulePromptFile{
|
ModuleFiles: []promptfs.ModulePromptFile{
|
||||||
{Name: "prompt.yaml", Path: "prompts/prompt.yaml"},
|
{Name: "prompt.yaml", Path: "prompts/prompt.yaml"},
|
||||||
{Name: "task.md", Path: "prompts/task.md"},
|
|
||||||
{Name: "instructions.md", Path: "prompts/instructions.md"},
|
{Name: "instructions.md", Path: "prompts/instructions.md"},
|
||||||
},
|
},
|
||||||
SharedFiles: []string{
|
SharedFiles: []string{
|
||||||
|
|||||||
@@ -34,15 +34,15 @@ func TestRegisterPromptAssetsAndPrepareInteractionPrompt(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
transcript := `{"units":[1]}`
|
transcript := `{"units":[{"sentinel":"interaction-transcript"}]}`
|
||||||
prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{
|
prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{
|
||||||
PromptID: PromptID, PromptVersion: SchemaVersion, ProfileID: "npc-interactions-test-profile",
|
PromptID: PromptID, PromptVersion: SchemaVersion, ProfileID: "npc-interactions-test-profile",
|
||||||
Inputs: map[string]promptkit.ArtifactRef{
|
Inputs: map[string]promptkit.ArtifactRef{
|
||||||
"transcript": promptkit.InlineWithURI("file:///session.json", transcript),
|
"transcript": promptkit.InlineWithURI("file:///session.json", transcript),
|
||||||
"players": promptkit.Inline("Dana: Mira"),
|
"players": promptkit.Inline("interaction-player"),
|
||||||
"party": promptkit.Inline("Mira: ranger"),
|
"party": promptkit.Inline("Mira: ranger"),
|
||||||
"glossary": promptkit.Inline("Greencloak: title"),
|
"glossary": promptkit.Inline("Greencloak: title"),
|
||||||
"npcs": promptkit.Inline(`{"npcs":[{"name":"Mira Thorn"}]}`),
|
"npcs": promptkit.Inline(`{"npcs":[{"name":"interaction-npc"}]}`),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -51,6 +51,43 @@ func TestRegisterPromptAssetsAndPrepareInteractionPrompt(t *testing.T) {
|
|||||||
if prepared.PromptID != PromptID || prepared.OutputContract.SchemaPath != "dnd_npc_interactions_llm.v1.json" {
|
if prepared.PromptID != PromptID || prepared.OutputContract.SchemaPath != "dnd_npc_interactions_llm.v1.json" {
|
||||||
t.Fatalf("prepared prompt = %#v", prepared)
|
t.Fatalf("prepared prompt = %#v", prepared)
|
||||||
}
|
}
|
||||||
|
if len(prepared.Messages) < 6 {
|
||||||
|
t.Fatalf("prepared messages = %#v, want shared context, NPC grounding, and instructions", prepared.Messages)
|
||||||
|
}
|
||||||
|
referenceIndex := -1
|
||||||
|
transcriptIndex := -1
|
||||||
|
npcIndex := -1
|
||||||
|
for index, message := range prepared.Messages {
|
||||||
|
if strings.Contains(message.Content, "interaction-player") {
|
||||||
|
referenceIndex = index
|
||||||
|
}
|
||||||
|
if strings.Contains(message.Content, "interaction-transcript") {
|
||||||
|
transcriptIndex = index
|
||||||
|
}
|
||||||
|
if strings.Contains(message.Content, "interaction-npc") {
|
||||||
|
npcIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instructionsIndex := len(prepared.Messages) - 1
|
||||||
|
if referenceIndex < 0 || transcriptIndex <= referenceIndex || npcIndex <= transcriptIndex || instructionsIndex <= npcIndex {
|
||||||
|
t.Fatalf("message order = references %d, transcript %d, NPCs %d, instructions %d; want that order", referenceIndex, transcriptIndex, npcIndex, instructionsIndex)
|
||||||
|
}
|
||||||
|
for _, index := range []int{referenceIndex, transcriptIndex, instructionsIndex} {
|
||||||
|
if cache := prepared.Messages[index].CacheControl; cache == nil || cache.Type != promptkit.CacheControlEphemeral {
|
||||||
|
t.Fatalf("message %d cache control = %#v, want ephemeral", index, cache)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for index, message := range prepared.Messages {
|
||||||
|
if index != referenceIndex && strings.Contains(message.Content, "interaction-player") {
|
||||||
|
t.Errorf("message %d unexpectedly rendered campaign-reference input", index)
|
||||||
|
}
|
||||||
|
if index != transcriptIndex && strings.Contains(message.Content, "interaction-transcript") {
|
||||||
|
t.Errorf("message %d unexpectedly rendered transcript input", index)
|
||||||
|
}
|
||||||
|
if index != npcIndex && strings.Contains(message.Content, "interaction-npc") {
|
||||||
|
t.Errorf("message %d unexpectedly rendered NPC grounding input", index)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPromptMetadataDoesNotExposeAssetContent(t *testing.T) {
|
func TestPromptMetadataDoesNotExposeAssetContent(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user