Clarify NPC mention eligibility
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
Extract Dungeons & Dragons NPC occurrence occurrences from the supplied
|
Extract Dungeons & Dragons NPC occurrences from the supplied
|
||||||
transcript. Include an occurrence only when the transcript establishes one
|
transcript. Include an occurrence only when the transcript establishes one
|
||||||
supplied NPC, one occurrence kind, and a coherent passage supporting both.
|
supplied NPC, one occurrence kind, and a coherent passage supporting both.
|
||||||
Use the exact `npc_id` and matching `name` pair from the supplied NPC registry;
|
Use the exact `npc_id` and matching `name` pair from the supplied NPC registry;
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
Extract the individually identifiable Dungeons & Dragons non-player characters
|
Extract the individually identifiable Dungeons & Dragons non-player characters
|
||||||
established by the provided transcript.
|
established by the provided transcript.
|
||||||
|
|
||||||
Include an in-world non-PC participant only when the transcript gives it a
|
Include an in-world non-PC only when the transcript factually establishes a
|
||||||
proper name or a stable, individually distinguishing title or alias.
|
proper name or a stable, individually distinguishing title or alias. A factual
|
||||||
|
third-party mention establishes that identity even when the NPC is not
|
||||||
|
physically present, does not speak, and takes no direct action in this chunk.
|
||||||
|
Record only the NPC identity and the transcript evidence that establishes it;
|
||||||
|
do not infer or classify a separate occurrence.
|
||||||
|
|
||||||
Exclude human players, transcript speakers, and the GM as out-of-world people;
|
Exclude human players, transcript speakers, and the GM as out-of-world people;
|
||||||
player characters identified by the player or party references; incidental or
|
player characters identified by the player or party references; names used only
|
||||||
hypothetical name drops; corrected transcription mistakes; anonymous or generic
|
in hypothetical, speculative, or imagined examples; corrected transcription
|
||||||
roles; indistinguishable crowds or groups; invented descriptive labels; and
|
mistakes; anonymous or generic roles; indistinguishable crowds or groups;
|
||||||
temporary summoned creatures or spell effects without a persistent individual
|
invented descriptive labels; and temporary summoned creatures or spell effects
|
||||||
identity.
|
without a persistent individual identity.
|
||||||
|
|
||||||
Preserve observed display spelling. Do not invent a label for an anonymous
|
Preserve observed display spelling. Do not invent a label for an anonymous
|
||||||
creature, crowd, or generic role.
|
creature, crowd, or generic role.
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ The required normalized [NPC registry artifact](dnd-npc-registry-artifacts.md) r
|
|||||||
the exact `{npc_id, name}` pair. Unknown IDs and names that do not match their
|
the exact `{npc_id, name}` pair. Unknown IDs and names that do not match their
|
||||||
ID are rejected; normalization does not repair names by similarity. Registry
|
ID are rejected; normalization does not repair names by similarity. Registry
|
||||||
references are provenance only and never replace an occurrence's own evidence.
|
references are provenance only and never replace an occurrence's own evidence.
|
||||||
|
The registry may include an identity established by a factual third-party
|
||||||
|
mention; that provenance alone does not create a `mentioned` occurrence. Each
|
||||||
|
occurrence remains a separately cited fact in the current transcript.
|
||||||
Normalization validates the exact pair, orders and
|
Normalization validates the exact pair, orders and
|
||||||
deduplicates exact source references, then orders occurrences by valid source
|
deduplicates exact source references, then orders occurrences by valid source
|
||||||
chronology, NPC comparison identity, display name, kind, and reference sequence.
|
chronology, NPC comparison identity, display name, kind, and reference sequence.
|
||||||
|
|||||||
@@ -68,10 +68,16 @@ descriptor.
|
|||||||
## Scope and consumers
|
## Scope and consumers
|
||||||
|
|
||||||
Only individually identifiable NPC names with transcript evidence belong in
|
Only individually identifiable NPC names with transcript evidence belong in
|
||||||
this artifact. Groups, generic roles, invented labels, and descriptive
|
this artifact. A factual third-party mention can establish an identity even if
|
||||||
enrichment are excluded. Its source references prove registry provenance; they
|
the NPC is not present, speaking, or acting in the cited passage. Names used
|
||||||
do not become evidence for a spell, occurrence, combat, or enemy-event
|
only in hypothetical, speculative, or imagined examples are excluded, as are
|
||||||
occurrence.
|
groups, generic roles, invented labels, and descriptive enrichment. Its source
|
||||||
|
references prove registry provenance; they do not become evidence for a spell,
|
||||||
|
occurrence, combat, or enemy-event occurrence.
|
||||||
|
|
||||||
|
Registry evidence establishes an identity, not an [NPC occurrence](dnd-npc-occurrence-artifacts.md).
|
||||||
|
That later artifact independently records any current-transcript occurrence
|
||||||
|
with its own cited evidence and category.
|
||||||
|
|
||||||
This registry can ground actor or caster names in the [spell](dnd-spell-artifacts.md)
|
This registry can ground actor or caster names in the [spell](dnd-spell-artifacts.md)
|
||||||
and [combat-turn](dnd-combat-turn-artifacts.md) artifacts. It is required to
|
and [combat-turn](dnd-combat-turn-artifacts.md) artifacts. It is required to
|
||||||
|
|||||||
@@ -168,11 +168,11 @@ checkpoint fingerprint.
|
|||||||
| Lane | Intentional behavior |
|
| Lane | Intentional behavior |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Spells | May use a spell-catalog overlay and optional NPC grounding; the catalog validator supplies domain-specific semantic checks. |
|
| Spells | May use a spell-catalog overlay and optional NPC grounding; the catalog validator supplies domain-specific semantic checks. |
|
||||||
| NPC registry | Does not consume an NPC registry. Its normalizer is the LLM-assisted reconciliation exception described above. |
|
| NPC registry | Establishes transcript-grounded NPC identities, including factual third-party mentions, without assigning occurrence categories. It does not consume an NPC registry, and its normalizer is the LLM-assisted reconciliation exception described above. |
|
||||||
| Combat turns | Requires a scene-description artifact. It calls the LLM only for an exact `combat` classification; exact non-combat classifications return an accepted empty result, while missing or mismatched classifications return an empty result with a bounded warning. Optional NPC grounding never becomes evidence. |
|
| Combat turns | Requires a scene-description artifact. It calls the LLM only for an exact `combat` classification; exact non-combat classifications return an accepted empty result, while missing or mismatched classifications return an empty result with a bounded warning. Optional NPC grounding never becomes evidence. |
|
||||||
| Item occurrences | Requires the normalized item registry for exact ID/name grounding at extraction and normalization. Campaign context may disambiguate, but the registry never becomes occurrence evidence. |
|
| Item occurrences | Requires the normalized item registry for exact ID/name grounding at extraction and normalization. Campaign context may disambiguate, but the registry never becomes occurrence evidence. |
|
||||||
| Item registry | Produces source-grounded item types and unique designations. Its LLM-assisted reconciliation is proposal-only, preserves distinct currency denominations and item types, and does not create per-instance identities. |
|
| Item registry | Produces source-grounded item types and unique designations. Its LLM-assisted reconciliation is proposal-only, preserves distinct currency denominations and item types, and does not create per-instance identities. |
|
||||||
| NPC occurrences | Requires the normalized NPC registry at extraction and normalization, using it for canonical actor grounding only. |
|
| NPC occurrences | Requires the normalized NPC registry at extraction and normalization, using it for canonical actor grounding only. It separately emits cited current-transcript occurrence facts, including `mentioned`, rather than deriving them from registry provenance. |
|
||||||
| Scene descriptions | Produces the classifications consumed by combat routing; it does not consume an NPC registry or provide evidence for combat artifacts. |
|
| Scene descriptions | Produces the classifications consumed by combat routing; it does not consume an NPC registry or provide evidence for combat artifacts. |
|
||||||
| Enemy events | Requires NPC, scene-description, combat-turn, and NPC-occurrence artifacts. It calls the LLM only for an exact `combat` classification, records ordered observations rather than terminal state, and normalizes recognized names through the NPC registry while preserving grounded collective labels. |
|
| Enemy events | Requires NPC, scene-description, combat-turn, and NPC-occurrence artifacts. It calls the LLM only for an exact `combat` classification, records ordered observations rather than terminal state, and normalizes recognized names through the NPC registry while preserving grounded collective labels. |
|
||||||
| Location registry | Produces a source-anchored, session-scoped registry from stable proper names or unique in-world designations. Its LLM-assisted reconciliation is proposal-only and never collapses same-name places without validated identity and evidence rules. |
|
| Location registry | Produces a source-anchored, session-scoped registry from stable proper names or unique in-world designations. Its LLM-assisted reconciliation is proposal-only and never collapses same-name places without validated identity and evidence rules. |
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ func groundingReferences(t *testing.T, enemy string, sceneKind dnd.SceneKind) co
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
interactionContent, err := occurrencecodec.New().Encode(dnd.NPCOccurrenceList{Occurrences: []dnd.NPCOccurrence{
|
occurrenceContent, err := occurrencecodec.New().Encode(dnd.NPCOccurrenceList{Occurrences: []dnd.NPCOccurrence{
|
||||||
{NPCID: identity.DeriveID(enemy), Name: enemy, Kind: dnd.NPCOccurrenceKindCombatOpponent, SourceRefs: []source.SourceRef{{SourceID: "combat-session", StartUnitID: 1, EndUnitID: 1}}},
|
{NPCID: identity.DeriveID(enemy), Name: enemy, Kind: dnd.NPCOccurrenceKindCombatOpponent, SourceRefs: []source.SourceRef{{SourceID: "combat-session", StartUnitID: 1, EndUnitID: 1}}},
|
||||||
{NPCID: identity.DeriveID("Aria"), Name: "Aria", Kind: dnd.NPCOccurrenceKindCombatAlly, SourceRefs: []source.SourceRef{{SourceID: "combat-session", StartUnitID: 2, EndUnitID: 2}}},
|
{NPCID: identity.DeriveID("Aria"), Name: "Aria", Kind: dnd.NPCOccurrenceKindCombatAlly, SourceRefs: []source.SourceRef{{SourceID: "combat-session", StartUnitID: 2, EndUnitID: 2}}},
|
||||||
}})
|
}})
|
||||||
@@ -231,7 +231,7 @@ func groundingReferences(t *testing.T, enemy string, sceneKind dnd.SceneKind) co
|
|||||||
NPCRegistryReferenceSlot: {Items: []contracts.ReferenceItem{newReferenceItem(NPCRegistryReferenceSlot, npcContent)}},
|
NPCRegistryReferenceSlot: {Items: []contracts.ReferenceItem{newReferenceItem(NPCRegistryReferenceSlot, npcContent)}},
|
||||||
SceneDescriptionReferenceSlot: {Items: []contracts.ReferenceItem{newReferenceItem(SceneDescriptionReferenceSlot, sceneContent)}},
|
SceneDescriptionReferenceSlot: {Items: []contracts.ReferenceItem{newReferenceItem(SceneDescriptionReferenceSlot, sceneContent)}},
|
||||||
CombatTurnReferenceSlot: {Items: []contracts.ReferenceItem{newReferenceItem(CombatTurnReferenceSlot, turnContent)}},
|
CombatTurnReferenceSlot: {Items: []contracts.ReferenceItem{newReferenceItem(CombatTurnReferenceSlot, turnContent)}},
|
||||||
NPCOccurrenceReferenceSlot: {Items: []contracts.ReferenceItem{newReferenceItem(NPCOccurrenceReferenceSlot, interactionContent)}},
|
NPCOccurrenceReferenceSlot: {Items: []contracts.ReferenceItem{newReferenceItem(NPCOccurrenceReferenceSlot, occurrenceContent)}},
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
||||||
interactionmodel "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/npcoccurrences"
|
occurrencemodel "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/npcoccurrences"
|
||||||
npcregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/npcs/registry"
|
npcregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/npcs/registry"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared/diagnostics"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared/diagnostics"
|
||||||
@@ -107,7 +107,7 @@ func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationReq
|
|||||||
|
|
||||||
func allSourceRefsValid(index source.DocumentIndex, value dnd.NPCOccurrenceList) bool {
|
func allSourceRefsValid(index source.DocumentIndex, value dnd.NPCOccurrenceList) bool {
|
||||||
for _, occurrence := range value.Occurrences {
|
for _, occurrence := range value.Occurrences {
|
||||||
if !interactionmodel.ValidSourceRefs(index, occurrence.SourceRefs) {
|
if !occurrencemodel.ValidSourceRefs(index, occurrence.SourceRefs) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,13 +136,13 @@ func issuesFor(order shared.SourceRefOrder, value dnd.NPCOccurrenceList, npcRegi
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !sort.SliceIsSorted(value.Occurrences, func(left, right int) bool {
|
if !sort.SliceIsSorted(value.Occurrences, func(left, right int) bool {
|
||||||
return interactionmodel.Less(order, value.Occurrences[left], value.Occurrences[right])
|
return occurrencemodel.Less(order, value.Occurrences[left], value.Occurrences[right])
|
||||||
}) {
|
}) {
|
||||||
issues = append(issues, "occurrences are not in canonical order")
|
issues = append(issues, "occurrences are not in canonical order")
|
||||||
}
|
}
|
||||||
seen := make(map[string]int)
|
seen := make(map[string]int)
|
||||||
for index, occurrence := range value.Occurrences {
|
for index, occurrence := range value.Occurrences {
|
||||||
key := interactionmodel.ExactIdentity(occurrence)
|
key := occurrencemodel.ExactIdentity(occurrence)
|
||||||
if previous, ok := seen[key]; ok {
|
if previous, ok := seen[key]; ok {
|
||||||
issues = append(issues, fmt.Sprintf("occurrences[%d] duplicates occurrence %d", index, previous))
|
issues = append(issues, fmt.Sprintf("occurrences[%d] duplicates occurrence %d", index, previous))
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user