Move item occurrences to canonical namespace
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
Extract meaningful Dungeons & Dragons item and currency occurrences: discoveries and changes
|
Extract meaningful Dungeons & Dragons item and currency occurrences: discoveries and changes
|
||||||
in party possession established by the transcript. This is an event history,
|
in party possession established by the transcript. This is an occurrence history,
|
||||||
not an inventory or ledger: do not calculate balances, resolve item identity
|
not an inventory or ledger: do not calculate balances, resolve item identity
|
||||||
across records, or infer ownership that the transcript does not establish.
|
across records, or infer ownership that the transcript does not establish.
|
||||||
|
|
||||||
@@ -18,13 +18,13 @@ when the transcript explicitly describes it being physically destroyed or
|
|||||||
expended as a non-payment component. Use `transferred` only when possession
|
expended as a non-payment component. Use `transferred` only when possession
|
||||||
moves between two distinct named party members.
|
moves between two distinct named party members.
|
||||||
|
|
||||||
Return both `from` and `to` for every event, using `null` when a holder does not
|
Return both `from` and `to` for every occurrence, using `null` when a holder does not
|
||||||
apply. For `discovered`, set both holders to `null`. For `acquired`, set `from`
|
apply. For `discovered`, set both holders to `null`. For `acquired`, set `from`
|
||||||
to `null` and provide `to`; for `lost` and `consumed`, provide `from` and set
|
to `null` and provide `to`; for `lost` and `consumed`, provide `from` and set
|
||||||
`to` to `null`; and for `transferred`, provide both holders. Use `party` only
|
`to` to `null`; and for `transferred`, provide both holders. Use `party` only
|
||||||
for collective or unresolved party possession, never for either side of a
|
for collective or unresolved party possession, never for either side of a
|
||||||
transfer. Do not emit a transfer for a gift, sale, or payment outside the party.
|
transfer. Do not emit a transfer for a gift, sale, or payment outside the party.
|
||||||
|
|
||||||
Ordinary non-depleting use is not an event. Do not infer acquisition from a
|
Ordinary non-depleting use is not an occurrence. Do not infer acquisition from a
|
||||||
discovery, or discovery from an acquisition: emit both only when each is
|
discovery, or discovery from an acquisition: emit both only when each is
|
||||||
independently established.
|
independently established.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
id: dnd.item_events
|
id: dnd.item_occurrences
|
||||||
version: "v1"
|
version: "v1"
|
||||||
default_profile: dnd-extraction
|
default_profile: dnd-extraction
|
||||||
inputs:
|
inputs:
|
||||||
@@ -404,15 +404,15 @@ generated bindings.
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Input | **seriatim** |
|
| Input | **seriatim** |
|
||||||
| Chunk | **generic**, **dnd/scenes** |
|
| Chunk | **generic**, **dnd/scenes** |
|
||||||
| Extract | **dnd/spells**, **dnd/npc-registry**, **dnd/combat-turns**, **dnd/item-events**, **dnd/item-registry**, **dnd/npc-occurrences**, **dnd/scene-descriptions**, **dnd/enemy-events**, **dnd/location-registry**, **dnd/location-occurrences** |
|
| Extract | **dnd/spells**, **dnd/npc-registry**, **dnd/combat-turns**, **dnd/item-occurrences**, **dnd/item-registry**, **dnd/npc-occurrences**, **dnd/scene-descriptions**, **dnd/enemy-events**, **dnd/location-registry**, **dnd/location-occurrences** |
|
||||||
| Merge | **appendorder** |
|
| Merge | **appendorder** |
|
||||||
| Normalize | **noop**, **dnd/spells**, **dnd/npc-registry**, **dnd/combat-turns**, **dnd/item-events**, **dnd/item-registry**, **dnd/npc-occurrences**, **dnd/scene-descriptions**, **dnd/enemy-events**, **dnd/location-registry**, **dnd/location-occurrences** |
|
| Normalize | **noop**, **dnd/spells**, **dnd/npc-registry**, **dnd/combat-turns**, **dnd/item-occurrences**, **dnd/item-registry**, **dnd/npc-occurrences**, **dnd/scene-descriptions**, **dnd/enemy-events**, **dnd/location-registry**, **dnd/location-occurrences** |
|
||||||
| Output | **json** |
|
| Output | **json** |
|
||||||
|
|
||||||
`dnd/location-registry` and `dnd/item-registry` extraction and normalization
|
`dnd/location-registry` and `dnd/item-registry` extraction and normalization
|
||||||
are `llm_backed`; their normalizers may use the pipeline's selected LLM profile
|
are `llm_backed`; their normalizers may use the pipeline's selected LLM profile
|
||||||
for bounded duplicate proposals. `dnd/location-occurrences` and
|
for bounded duplicate proposals. `dnd/location-occurrences` and
|
||||||
`dnd/item-events` extraction are `llm_backed`, while their normalizers are
|
`dnd/item-occurrences` extraction are `llm_backed`, while their normalizers are
|
||||||
`deterministic`. The complete example binds each registry in an earlier step
|
`deterministic`. The complete example binds each registry in an earlier step
|
||||||
before its occurrence consumer.
|
before its occurrence consumer.
|
||||||
|
|
||||||
@@ -421,7 +421,7 @@ The D&D artifact contracts define each emitted schema:
|
|||||||
[NPC registry](integrations/dnd-npc-registry-artifacts.md),
|
[NPC registry](integrations/dnd-npc-registry-artifacts.md),
|
||||||
[NPC occurrences](integrations/dnd-npc-occurrence-artifacts.md),
|
[NPC occurrences](integrations/dnd-npc-occurrence-artifacts.md),
|
||||||
[combat turns](integrations/dnd-combat-turn-artifacts.md),
|
[combat turns](integrations/dnd-combat-turn-artifacts.md),
|
||||||
[item events](integrations/dnd-item-event-artifacts.md),
|
[item occurrences](integrations/dnd-item-occurrence-artifacts.md),
|
||||||
[item registry](integrations/dnd-item-registry-artifacts.md),
|
[item registry](integrations/dnd-item-registry-artifacts.md),
|
||||||
[scene descriptions](integrations/dnd-scene-description-artifacts.md), and
|
[scene descriptions](integrations/dnd-scene-description-artifacts.md), and
|
||||||
[enemy events](integrations/dnd-enemy-event-artifacts.md),
|
[enemy events](integrations/dnd-enemy-event-artifacts.md),
|
||||||
@@ -438,7 +438,7 @@ Available validator keys are:
|
|||||||
| Spells | **extract/dnd/spells/shape**, **extract/dnd/spells/catalog**, **extract/dnd/spells/source_refs**, **extract/dnd/spells/source_relatedness** |
|
| Spells | **extract/dnd/spells/shape**, **extract/dnd/spells/catalog**, **extract/dnd/spells/source_refs**, **extract/dnd/spells/source_relatedness** |
|
||||||
| NPC registry | **extract/dnd/npc-registry/shape**, **extract/dnd/npc-registry/source_refs**, **extract/dnd/npc-registry/source_relatedness**, **normalize/dnd/npc-registry/identity** |
|
| NPC registry | **extract/dnd/npc-registry/shape**, **extract/dnd/npc-registry/source_refs**, **extract/dnd/npc-registry/source_relatedness**, **normalize/dnd/npc-registry/identity** |
|
||||||
| Combat turns | **extract/dnd/combat-turns/shape**, **extract/dnd/combat-turns/source_refs**, **extract/dnd/combat-turns/source_relatedness**, **normalize/dnd/combat-turns/invariants** |
|
| Combat turns | **extract/dnd/combat-turns/shape**, **extract/dnd/combat-turns/source_refs**, **extract/dnd/combat-turns/source_relatedness**, **normalize/dnd/combat-turns/invariants** |
|
||||||
| Item events | **extract/dnd/item-events/shape**, **extract/dnd/item-events/registry**, **extract/dnd/item-events/source_refs**, **extract/dnd/item-events/source_relatedness**, **normalize/dnd/item-events/invariants** |
|
| Item occurrences | **extract/dnd/item-occurrences/shape**, **extract/dnd/item-occurrences/registry**, **extract/dnd/item-occurrences/source_refs**, **extract/dnd/item-occurrences/source_relatedness**, **normalize/dnd/item-occurrences/invariants** |
|
||||||
| Item registry | **extract/dnd/item-registry/shape**, **extract/dnd/item-registry/source_refs**, **extract/dnd/item-registry/source_relatedness**, **normalize/dnd/item-registry/identity** |
|
| Item registry | **extract/dnd/item-registry/shape**, **extract/dnd/item-registry/source_refs**, **extract/dnd/item-registry/source_relatedness**, **normalize/dnd/item-registry/identity** |
|
||||||
| NPC occurrences | **extract/dnd/npc-occurrences/shape**, **extract/dnd/npc-occurrences/registry**, **extract/dnd/npc-occurrences/source_refs**, **extract/dnd/npc-occurrences/source_relatedness**, **normalize/dnd/npc-occurrences/invariants** |
|
| NPC occurrences | **extract/dnd/npc-occurrences/shape**, **extract/dnd/npc-occurrences/registry**, **extract/dnd/npc-occurrences/source_refs**, **extract/dnd/npc-occurrences/source_relatedness**, **normalize/dnd/npc-occurrences/invariants** |
|
||||||
| Scene descriptions | **extract/dnd/scene-descriptions/shape**, **extract/dnd/scene-descriptions/source_refs**, **extract/dnd/scene-descriptions/source_relatedness**, **normalize/dnd/scene-descriptions/invariants** |
|
| Scene descriptions | **extract/dnd/scene-descriptions/shape**, **extract/dnd/scene-descriptions/source_refs**, **extract/dnd/scene-descriptions/source_relatedness**, **normalize/dnd/scene-descriptions/invariants** |
|
||||||
@@ -455,7 +455,7 @@ same at both stages.
|
|||||||
| Spells | generic/valid_json, extract/dnd/spells/shape, extract/dnd/spells/catalog, extract/dnd/spells/source_refs, generic/valid_json_schema, extract/dnd/spells/source_relatedness | Same as extract |
|
| Spells | generic/valid_json, extract/dnd/spells/shape, extract/dnd/spells/catalog, extract/dnd/spells/source_refs, generic/valid_json_schema, extract/dnd/spells/source_relatedness | Same as extract |
|
||||||
| NPC registry | generic/valid_json, extract/dnd/npc-registry/shape, extract/dnd/npc-registry/source_refs, generic/valid_json_schema, extract/dnd/npc-registry/source_relatedness | generic/valid_json, extract/dnd/npc-registry/shape, normalize/dnd/npc-registry/identity, extract/dnd/npc-registry/source_refs, generic/valid_json_schema, extract/dnd/npc-registry/source_relatedness |
|
| NPC registry | generic/valid_json, extract/dnd/npc-registry/shape, extract/dnd/npc-registry/source_refs, generic/valid_json_schema, extract/dnd/npc-registry/source_relatedness | generic/valid_json, extract/dnd/npc-registry/shape, normalize/dnd/npc-registry/identity, extract/dnd/npc-registry/source_refs, generic/valid_json_schema, extract/dnd/npc-registry/source_relatedness |
|
||||||
| Combat turns | generic/valid_json, extract/dnd/combat-turns/shape, extract/dnd/combat-turns/source_refs, generic/valid_json_schema, extract/dnd/combat-turns/source_relatedness | generic/valid_json, extract/dnd/combat-turns/shape, normalize/dnd/combat-turns/invariants, extract/dnd/combat-turns/source_refs, generic/valid_json_schema, extract/dnd/combat-turns/source_relatedness |
|
| Combat turns | generic/valid_json, extract/dnd/combat-turns/shape, extract/dnd/combat-turns/source_refs, generic/valid_json_schema, extract/dnd/combat-turns/source_relatedness | generic/valid_json, extract/dnd/combat-turns/shape, normalize/dnd/combat-turns/invariants, extract/dnd/combat-turns/source_refs, generic/valid_json_schema, extract/dnd/combat-turns/source_relatedness |
|
||||||
| Item events | generic/valid_json, extract/dnd/item-events/shape, extract/dnd/item-events/registry, extract/dnd/item-events/source_refs, generic/valid_json_schema, extract/dnd/item-events/source_relatedness | generic/valid_json, extract/dnd/item-events/shape, extract/dnd/item-events/registry, normalize/dnd/item-events/invariants, extract/dnd/item-events/source_refs, generic/valid_json_schema, extract/dnd/item-events/source_relatedness |
|
| Item occurrences | generic/valid_json, extract/dnd/item-occurrences/shape, extract/dnd/item-occurrences/registry, extract/dnd/item-occurrences/source_refs, generic/valid_json_schema, extract/dnd/item-occurrences/source_relatedness | generic/valid_json, extract/dnd/item-occurrences/shape, extract/dnd/item-occurrences/registry, normalize/dnd/item-occurrences/invariants, extract/dnd/item-occurrences/source_refs, generic/valid_json_schema, extract/dnd/item-occurrences/source_relatedness |
|
||||||
| Item registry | generic/valid_json, extract/dnd/item-registry/shape, extract/dnd/item-registry/source_refs, generic/valid_json_schema, extract/dnd/item-registry/source_relatedness | generic/valid_json, extract/dnd/item-registry/shape, normalize/dnd/item-registry/identity, extract/dnd/item-registry/source_refs, generic/valid_json_schema, extract/dnd/item-registry/source_relatedness |
|
| Item registry | generic/valid_json, extract/dnd/item-registry/shape, extract/dnd/item-registry/source_refs, generic/valid_json_schema, extract/dnd/item-registry/source_relatedness | generic/valid_json, extract/dnd/item-registry/shape, normalize/dnd/item-registry/identity, extract/dnd/item-registry/source_refs, generic/valid_json_schema, extract/dnd/item-registry/source_relatedness |
|
||||||
| NPC occurrences | generic/valid_json, extract/dnd/npc-occurrences/shape, extract/dnd/npc-occurrences/registry, extract/dnd/npc-occurrences/source_refs, generic/valid_json_schema, extract/dnd/npc-occurrences/source_relatedness | generic/valid_json, extract/dnd/npc-occurrences/shape, extract/dnd/npc-occurrences/registry, normalize/dnd/npc-occurrences/invariants, extract/dnd/npc-occurrences/source_refs, generic/valid_json_schema, extract/dnd/npc-occurrences/source_relatedness |
|
| NPC occurrences | generic/valid_json, extract/dnd/npc-occurrences/shape, extract/dnd/npc-occurrences/registry, extract/dnd/npc-occurrences/source_refs, generic/valid_json_schema, extract/dnd/npc-occurrences/source_relatedness | generic/valid_json, extract/dnd/npc-occurrences/shape, extract/dnd/npc-occurrences/registry, normalize/dnd/npc-occurrences/invariants, extract/dnd/npc-occurrences/source_refs, generic/valid_json_schema, extract/dnd/npc-occurrences/source_relatedness |
|
||||||
| Scene descriptions | generic/valid_json, extract/dnd/scene-descriptions/shape, extract/dnd/scene-descriptions/source_refs, generic/valid_json_schema, extract/dnd/scene-descriptions/source_relatedness | generic/valid_json, extract/dnd/scene-descriptions/shape, normalize/dnd/scene-descriptions/invariants, extract/dnd/scene-descriptions/source_refs, generic/valid_json_schema, extract/dnd/scene-descriptions/source_relatedness |
|
| Scene descriptions | generic/valid_json, extract/dnd/scene-descriptions/shape, extract/dnd/scene-descriptions/source_refs, generic/valid_json_schema, extract/dnd/scene-descriptions/source_relatedness | generic/valid_json, extract/dnd/scene-descriptions/shape, normalize/dnd/scene-descriptions/invariants, extract/dnd/scene-descriptions/source_refs, generic/valid_json_schema, extract/dnd/scene-descriptions/source_relatedness |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# D&D Item-Occurrence Artifact
|
# D&D Item-Occurrence Artifact
|
||||||
|
|
||||||
`dnd/item-events` currently produces this source-grounded item and currency
|
`dnd/item-occurrences` currently produces this source-grounded item and currency
|
||||||
occurrence list. It records discoveries and possession changes, not an
|
occurrence list. It records discoveries and possession changes, not an
|
||||||
inventory, balance, or ledger.
|
inventory, balance, or ledger.
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ than infer a lane schema from its name. The current D&D payload contracts are
|
|||||||
[spells](dnd-spell-artifacts.md), [NPC registry](dnd-npc-registry-artifacts.md),
|
[spells](dnd-spell-artifacts.md), [NPC registry](dnd-npc-registry-artifacts.md),
|
||||||
[NPC occurrences](dnd-npc-occurrence-artifacts.md),
|
[NPC occurrences](dnd-npc-occurrence-artifacts.md),
|
||||||
[combat turns](dnd-combat-turn-artifacts.md),
|
[combat turns](dnd-combat-turn-artifacts.md),
|
||||||
[item events](dnd-item-event-artifacts.md),
|
[item occurrences](dnd-item-occurrence-artifacts.md),
|
||||||
[scene descriptions](dnd-scene-description-artifacts.md), and
|
[scene descriptions](dnd-scene-description-artifacts.md), and
|
||||||
[enemy events](dnd-enemy-event-artifacts.md),
|
[enemy events](dnd-enemy-event-artifacts.md),
|
||||||
[location registry](dnd-location-registry-artifacts.md), and
|
[location registry](dnd-location-registry-artifacts.md), and
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ does not repeat their JSON shapes or schemas.
|
|||||||
| Spells | [spell artifacts](../integrations/dnd-spell-artifacts.md) |
|
| Spells | [spell artifacts](../integrations/dnd-spell-artifacts.md) |
|
||||||
| NPC registry | [NPC registry artifacts](../integrations/dnd-npc-registry-artifacts.md) |
|
| NPC registry | [NPC registry artifacts](../integrations/dnd-npc-registry-artifacts.md) |
|
||||||
| Combat turns | [combat-turn artifacts](../integrations/dnd-combat-turn-artifacts.md) |
|
| Combat turns | [combat-turn artifacts](../integrations/dnd-combat-turn-artifacts.md) |
|
||||||
| Item events | [item-event artifacts](../integrations/dnd-item-event-artifacts.md) |
|
| Item occurrences | [item-occurrence artifacts](../integrations/dnd-item-occurrence-artifacts.md) |
|
||||||
| Item registry | [item-registry artifacts](../integrations/dnd-item-registry-artifacts.md) |
|
| Item registry | [item-registry artifacts](../integrations/dnd-item-registry-artifacts.md) |
|
||||||
| NPC occurrences | [NPC-occurrence artifacts](../integrations/dnd-npc-occurrence-artifacts.md) |
|
| NPC occurrences | [NPC-occurrence artifacts](../integrations/dnd-npc-occurrence-artifacts.md) |
|
||||||
| Scene descriptions | [scene-description artifacts](../integrations/dnd-scene-description-artifacts.md) |
|
| Scene descriptions | [scene-description artifacts](../integrations/dnd-scene-description-artifacts.md) |
|
||||||
@@ -123,7 +123,7 @@ the same comparison identity within one scene-scoped result. Normalization may
|
|||||||
combine results from distinct scenes, so it intentionally does not apply that
|
combine results from distinct scenes, so it intentionally does not apply that
|
||||||
rule. Configuration owns the exact validator key and chain position.
|
rule. Configuration owns the exact validator key and chain position.
|
||||||
|
|
||||||
Normalizers are deterministic for spells, combat turns, item events, NPC
|
Normalizers are deterministic for spells, combat turns, item occurrences, NPC
|
||||||
occurrences, scene descriptions, enemy events, and location occurrences. They canonicalize display
|
occurrences, scene descriptions, enemy events, and location occurrences. They canonicalize display
|
||||||
values and evidence, use source-document order for stable output, and issue
|
values and evidence, use source-document order for stable output, and issue
|
||||||
bounded warnings for changes or collapsed duplicates. The NPC and location
|
bounded warnings for changes or collapsed duplicates. The NPC and location
|
||||||
@@ -163,7 +163,7 @@ shared helper changes.
|
|||||||
| 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. |
|
||||||
| NPCs | Does not consume an NPC registry. Its normalizer is the LLM-assisted reconciliation exception described above. |
|
| NPCs | Does not consume an NPC registry. 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 events | Uses campaign context for disambiguation but has no NPC-registry or scene-description dependency. |
|
| Item occurrences | Uses campaign context for disambiguation but has no NPC-registry or scene-description dependency. |
|
||||||
| 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. |
|
||||||
| 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. |
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ pipelines:
|
|||||||
enabled: true
|
enabled: true
|
||||||
window_units: 3
|
window_units: 3
|
||||||
lanes:
|
lanes:
|
||||||
- item-events
|
- item-occurrences
|
||||||
- item-registry
|
- item-registry
|
||||||
- locations
|
- locations
|
||||||
- location-occurrences
|
- location-occurrences
|
||||||
@@ -97,12 +97,12 @@ pipelines:
|
|||||||
step: describe-session
|
step: describe-session
|
||||||
lane: item-registry
|
lane: item-registry
|
||||||
artifacts:
|
artifacts:
|
||||||
item-events:
|
item-occurrences:
|
||||||
extract:
|
extract:
|
||||||
module: dnd/item-events
|
module: dnd/item-occurrences
|
||||||
retries: 2
|
retries: 2
|
||||||
merge: appendorder
|
merge: appendorder
|
||||||
normalize: dnd/item-events
|
normalize: dnd/item-occurrences
|
||||||
spells:
|
spells:
|
||||||
extract:
|
extract:
|
||||||
module: dnd/spells
|
module: dnd/spells
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
locationcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/locationregistry"
|
locationcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/locationregistry"
|
||||||
combat "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
combat "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
||||||
enemyevents "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
enemyevents "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
||||||
itemevents "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemevents"
|
itemoccurrences "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemoccurrences"
|
||||||
itemregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemregistry"
|
itemregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemregistry"
|
||||||
locationoccurrences "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationoccurrences"
|
locationoccurrences "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationoccurrences"
|
||||||
locations "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationregistry"
|
locations "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationregistry"
|
||||||
@@ -259,7 +259,7 @@ func (client *enemyEventLLMClient) CompleteStructured(ctx context.Context, reque
|
|||||||
content = []byte(`{"items":[]}`)
|
content = []byte(`{"items":[]}`)
|
||||||
case itemregistrynormalize.PromptID:
|
case itemregistrynormalize.PromptID:
|
||||||
content = []byte(`{"duplicate_groups":[]}`)
|
content = []byte(`{"duplicate_groups":[]}`)
|
||||||
case itemevents.PromptID:
|
case itemoccurrences.PromptID:
|
||||||
content = []byte(`{"occurrences":[]}`)
|
content = []byte(`{"occurrences":[]}`)
|
||||||
case combat.PromptID:
|
case combat.PromptID:
|
||||||
content = []byte(`{"combat_turns":[{"actor":"Kesh","turn_kind":"turn","source_refs":[{"start_unit_id":8,"end_unit_id":8}]}]}`)
|
content = []byte(`{"combat_turns":[{"actor":"Kesh","turn_kind":"turn","source_refs":[{"start_unit_id":8,"end_unit_id":8}]}]}`)
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func TestMaintainedExamplesLoadResolveAndList(t *testing.T) {
|
|||||||
t.Fatalf("materialize maintained example references for %q: %v", pipelineID, err)
|
t.Fatalf("materialize maintained example references for %q: %v", pipelineID, err)
|
||||||
}
|
}
|
||||||
if example.name == "complete" {
|
if example.name == "complete" {
|
||||||
if got := exampleStepLaneIDs(materialized); strings.Join(got, "|") != "describe-session:item-registry,locations,npc_registry,scene-descriptions|extract-events:combat-turns,item-events,location-occurrences,npc-occurrences,spells|track-enemies:enemy-events" {
|
if got := exampleStepLaneIDs(materialized); strings.Join(got, "|") != "describe-session:item-registry,locations,npc_registry,scene-descriptions|extract-events:combat-turns,item-occurrences,location-occurrences,npc-occurrences,spells|track-enemies:enemy-events" {
|
||||||
t.Fatalf("complete example steps and lanes = %v, want the documented D&D extractor composition", got)
|
t.Fatalf("complete example steps and lanes = %v, want the documented D&D extractor composition", got)
|
||||||
}
|
}
|
||||||
locationLane := referenceContractLane(t, materialized, "locations")
|
locationLane := referenceContractLane(t, materialized, "locations")
|
||||||
@@ -88,13 +88,13 @@ func TestMaintainedExamplesLoadResolveAndList(t *testing.T) {
|
|||||||
len(spellLane.NormalizeReferences.ReferenceSet.Slots["spell_catalog"].Items) != 1 {
|
len(spellLane.NormalizeReferences.ReferenceSet.Slots["spell_catalog"].Items) != 1 {
|
||||||
t.Fatalf("complete example spell catalog reference was not materialized: %#v", spellLane)
|
t.Fatalf("complete example spell catalog reference was not materialized: %#v", spellLane)
|
||||||
}
|
}
|
||||||
itemEventLane := referenceContractLane(t, materialized, "item-events")
|
itemOccurrenceLane := referenceContractLane(t, materialized, "item-occurrences")
|
||||||
for _, references := range []pipeline.ResolvedReferenceTarget{itemEventLane.ExtractReferences, itemEventLane.NormalizeReferences} {
|
for _, references := range []pipeline.ResolvedReferenceTarget{itemOccurrenceLane.ExtractReferences, itemOccurrenceLane.NormalizeReferences} {
|
||||||
if _, found := references.ReferenceSet.Slots["npc_registry"]; found {
|
if _, found := references.ReferenceSet.Slots["npc_registry"]; found {
|
||||||
t.Fatalf("item event lane unexpectedly depends on generated NPCs: %#v", itemEventLane)
|
t.Fatalf("item occurrence lane unexpectedly depends on generated NPCs: %#v", itemOccurrenceLane)
|
||||||
}
|
}
|
||||||
if _, found := references.ReferenceSet.Slots["scene_descriptions"]; found {
|
if _, found := references.ReferenceSet.Slots["scene_descriptions"]; found {
|
||||||
t.Fatalf("item event lane unexpectedly depends on generated scene descriptions: %#v", itemEventLane)
|
t.Fatalf("item occurrence lane unexpectedly depends on generated scene descriptions: %#v", itemOccurrenceLane)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enemyEventLane := referenceContractLane(t, materialized, "enemy-events")
|
enemyEventLane := referenceContractLane(t, materialized, "enemy-events")
|
||||||
|
|||||||
@@ -29,16 +29,16 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/chunk/scenes"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/chunk/scenes"
|
||||||
combatcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/combatturns"
|
combatcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/combatturns"
|
||||||
enemyeventcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/enemyevents"
|
enemyeventcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/enemyevents"
|
||||||
itemeventcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/itemevents"
|
itemoccurrencecodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/itemoccurrences"
|
||||||
spellcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/spells"
|
spellcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/spells"
|
||||||
combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
||||||
enemyeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
enemyeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
||||||
itemeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemevents"
|
itemoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemoccurrences"
|
||||||
itemregistryextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemregistry"
|
itemregistryextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemregistry"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/spells"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/spells"
|
||||||
combatnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/combatturns"
|
combatnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/combatturns"
|
||||||
enemyeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/enemyevents"
|
enemyeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/enemyevents"
|
||||||
itemeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemevents"
|
itemoccurrencenormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemoccurrences"
|
||||||
itemregistrynormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemregistry"
|
itemregistrynormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemregistry"
|
||||||
spellnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/spells"
|
spellnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/spells"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/generic/normalize/noop"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/generic/normalize/noop"
|
||||||
@@ -51,9 +51,9 @@ func TestProductionCatalogCoversMaintainedConfigurations(t *testing.T) {
|
|||||||
|
|
||||||
assertProductionContains(t, "inputs", registries.Inputs.RegisteredKeys(), []string{"seriatim"})
|
assertProductionContains(t, "inputs", registries.Inputs.RegisteredKeys(), []string{"seriatim"})
|
||||||
assertProductionContains(t, "chunkers", registries.Chunkers.RegisteredKeys(), []string{"dnd/scenes", "generic"})
|
assertProductionContains(t, "chunkers", registries.Chunkers.RegisteredKeys(), []string{"dnd/scenes", "generic"})
|
||||||
assertProductionContains(t, "extractors", registries.Extractors.RegisteredKeys(), []string{"dnd/spells", "dnd/npc-registry", combatextract.Key, itemeventextract.Key, enemyeventextract.Key})
|
assertProductionContains(t, "extractors", registries.Extractors.RegisteredKeys(), []string{"dnd/spells", "dnd/npc-registry", combatextract.Key, itemoccurrenceextract.Key, enemyeventextract.Key})
|
||||||
assertProductionContains(t, "mergers", registries.Mergers.RegisteredKeys(), []string{"appendorder"})
|
assertProductionContains(t, "mergers", registries.Mergers.RegisteredKeys(), []string{"appendorder"})
|
||||||
assertProductionContains(t, "normalizers", registries.Normalizers.RegisteredKeys(), []string{"noop", spellnormalize.Key, "dnd/npc-registry", combatnormalize.Key, itemeventnormalize.Key, enemyeventnormalize.Key})
|
assertProductionContains(t, "normalizers", registries.Normalizers.RegisteredKeys(), []string{"noop", spellnormalize.Key, "dnd/npc-registry", combatnormalize.Key, itemoccurrencenormalize.Key, enemyeventnormalize.Key})
|
||||||
assertProductionContains(t, "outputs", registries.Outputs.RegisteredKeys(), []string{"json"})
|
assertProductionContains(t, "outputs", registries.Outputs.RegisteredKeys(), []string{"json"})
|
||||||
assertProductionContains(t, "validators", registries.Validators.RegisteredKeys(), []string{
|
assertProductionContains(t, "validators", registries.Validators.RegisteredKeys(), []string{
|
||||||
"extract/dnd/spells/catalog",
|
"extract/dnd/spells/catalog",
|
||||||
@@ -64,10 +64,10 @@ func TestProductionCatalogCoversMaintainedConfigurations(t *testing.T) {
|
|||||||
"extract/dnd/combat-turns/source_refs",
|
"extract/dnd/combat-turns/source_refs",
|
||||||
"extract/dnd/combat-turns/source_relatedness",
|
"extract/dnd/combat-turns/source_relatedness",
|
||||||
"normalize/dnd/combat-turns/invariants",
|
"normalize/dnd/combat-turns/invariants",
|
||||||
"extract/dnd/item-events/shape",
|
"extract/dnd/item-occurrences/shape",
|
||||||
"extract/dnd/item-events/source_refs",
|
"extract/dnd/item-occurrences/source_refs",
|
||||||
"extract/dnd/item-events/source_relatedness",
|
"extract/dnd/item-occurrences/source_relatedness",
|
||||||
"normalize/dnd/item-events/invariants",
|
"normalize/dnd/item-occurrences/invariants",
|
||||||
"extract/dnd/enemy-events/shape",
|
"extract/dnd/enemy-events/shape",
|
||||||
"extract/dnd/enemy-events/engagements",
|
"extract/dnd/enemy-events/engagements",
|
||||||
"extract/dnd/enemy-events/source_refs",
|
"extract/dnd/enemy-events/source_refs",
|
||||||
@@ -83,7 +83,7 @@ func TestProductionCatalogCoversMaintainedConfigurations(t *testing.T) {
|
|||||||
assertProductionContains(t, "normalizer variants", registries.Normalizers.RegisteredArtifactKinds(pipeline.DefaultNormalizeModule), []contracts.ArtifactKind{dnd.SpellListKind, dnd.NPCRegistryKind, dnd.CombatTurnListKind, dnd.ItemOccurrenceListKind, dnd.EnemyEventListKind})
|
assertProductionContains(t, "normalizer variants", registries.Normalizers.RegisteredArtifactKinds(pipeline.DefaultNormalizeModule), []contracts.ArtifactKind{dnd.SpellListKind, dnd.NPCRegistryKind, dnd.CombatTurnListKind, dnd.ItemOccurrenceListKind, dnd.EnemyEventListKind})
|
||||||
assertProductionContains(t, "spell normalizer variants", registries.Normalizers.RegisteredArtifactKinds(spellnormalize.Key), []contracts.ArtifactKind{dnd.SpellListKind})
|
assertProductionContains(t, "spell normalizer variants", registries.Normalizers.RegisteredArtifactKinds(spellnormalize.Key), []contracts.ArtifactKind{dnd.SpellListKind})
|
||||||
assertProductionContains(t, "combat normalizer variants", registries.Normalizers.RegisteredArtifactKinds(combatnormalize.Key), []contracts.ArtifactKind{dnd.CombatTurnListKind})
|
assertProductionContains(t, "combat normalizer variants", registries.Normalizers.RegisteredArtifactKinds(combatnormalize.Key), []contracts.ArtifactKind{dnd.CombatTurnListKind})
|
||||||
assertProductionContains(t, "item event normalizer variants", registries.Normalizers.RegisteredArtifactKinds(itemeventnormalize.Key), []contracts.ArtifactKind{dnd.ItemOccurrenceListKind})
|
assertProductionContains(t, "item occurrence normalizer variants", registries.Normalizers.RegisteredArtifactKinds(itemoccurrencenormalize.Key), []contracts.ArtifactKind{dnd.ItemOccurrenceListKind})
|
||||||
assertProductionContains(t, "enemy event normalizer variants", registries.Normalizers.RegisteredArtifactKinds(enemyeventnormalize.Key), []contracts.ArtifactKind{dnd.EnemyEventListKind})
|
assertProductionContains(t, "enemy event normalizer variants", registries.Normalizers.RegisteredArtifactKinds(enemyeventnormalize.Key), []contracts.ArtifactKind{dnd.EnemyEventListKind})
|
||||||
|
|
||||||
wantChain := []pipeline.ModuleBinding{
|
wantChain := []pipeline.ModuleBinding{
|
||||||
@@ -121,28 +121,28 @@ func TestProductionCatalogCoversMaintainedConfigurations(t *testing.T) {
|
|||||||
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, combatnormalize.Key); !reflect.DeepEqual(got, combatNormalizeChain) {
|
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, combatnormalize.Key); !reflect.DeepEqual(got, combatNormalizeChain) {
|
||||||
t.Fatalf("combat normalize validator chain = %#v, want %#v", got, combatNormalizeChain)
|
t.Fatalf("combat normalize validator chain = %#v, want %#v", got, combatNormalizeChain)
|
||||||
}
|
}
|
||||||
itemEventExtractChain := []pipeline.ModuleBinding{
|
itemOccurrenceExtractChain := []pipeline.ModuleBinding{
|
||||||
pipeline.Binding("generic/valid_json"),
|
pipeline.Binding("generic/valid_json"),
|
||||||
pipeline.Binding("extract/dnd/item-events/shape"),
|
pipeline.Binding("extract/dnd/item-occurrences/shape"),
|
||||||
pipeline.Binding("extract/dnd/item-events/registry"),
|
pipeline.Binding("extract/dnd/item-occurrences/registry"),
|
||||||
pipeline.Binding("extract/dnd/item-events/source_refs"),
|
pipeline.Binding("extract/dnd/item-occurrences/source_refs"),
|
||||||
pipeline.Binding("generic/valid_json_schema"),
|
pipeline.Binding("generic/valid_json_schema"),
|
||||||
pipeline.Binding("extract/dnd/item-events/source_relatedness"),
|
pipeline.Binding("extract/dnd/item-occurrences/source_relatedness"),
|
||||||
}
|
}
|
||||||
itemEventNormalizeChain := []pipeline.ModuleBinding{
|
itemOccurrenceNormalizeChain := []pipeline.ModuleBinding{
|
||||||
pipeline.Binding("generic/valid_json"),
|
pipeline.Binding("generic/valid_json"),
|
||||||
pipeline.Binding("extract/dnd/item-events/shape"),
|
pipeline.Binding("extract/dnd/item-occurrences/shape"),
|
||||||
pipeline.Binding("extract/dnd/item-events/registry"),
|
pipeline.Binding("extract/dnd/item-occurrences/registry"),
|
||||||
pipeline.Binding("normalize/dnd/item-events/invariants"),
|
pipeline.Binding("normalize/dnd/item-occurrences/invariants"),
|
||||||
pipeline.Binding("extract/dnd/item-events/source_refs"),
|
pipeline.Binding("extract/dnd/item-occurrences/source_refs"),
|
||||||
pipeline.Binding("generic/valid_json_schema"),
|
pipeline.Binding("generic/valid_json_schema"),
|
||||||
pipeline.Binding("extract/dnd/item-events/source_relatedness"),
|
pipeline.Binding("extract/dnd/item-occurrences/source_relatedness"),
|
||||||
}
|
}
|
||||||
if got := registries.ValidatorChains.Validators(pipeline.StageExtract, itemeventextract.Key); !reflect.DeepEqual(got, itemEventExtractChain) {
|
if got := registries.ValidatorChains.Validators(pipeline.StageExtract, itemoccurrenceextract.Key); !reflect.DeepEqual(got, itemOccurrenceExtractChain) {
|
||||||
t.Fatalf("item event extract validator chain = %#v, want %#v", got, itemEventExtractChain)
|
t.Fatalf("item occurrence extract validator chain = %#v, want %#v", got, itemOccurrenceExtractChain)
|
||||||
}
|
}
|
||||||
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, itemeventnormalize.Key); !reflect.DeepEqual(got, itemEventNormalizeChain) {
|
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, itemoccurrencenormalize.Key); !reflect.DeepEqual(got, itemOccurrenceNormalizeChain) {
|
||||||
t.Fatalf("item event normalize validator chain = %#v, want %#v", got, itemEventNormalizeChain)
|
t.Fatalf("item occurrence normalize validator chain = %#v, want %#v", got, itemOccurrenceNormalizeChain)
|
||||||
}
|
}
|
||||||
enemyEventExtractChain := []pipeline.ModuleBinding{
|
enemyEventExtractChain := []pipeline.ModuleBinding{
|
||||||
pipeline.Binding("generic/valid_json"),
|
pipeline.Binding("generic/valid_json"),
|
||||||
@@ -168,7 +168,7 @@ func TestProductionCatalogCoversMaintainedConfigurations(t *testing.T) {
|
|||||||
{stage: pipeline.StageExtract, key: "dnd/spells", want: contracts.ExecutionClassLLMBacked},
|
{stage: pipeline.StageExtract, key: "dnd/spells", want: contracts.ExecutionClassLLMBacked},
|
||||||
{stage: pipeline.StageExtract, key: "dnd/npc-registry", want: contracts.ExecutionClassLLMBacked},
|
{stage: pipeline.StageExtract, key: "dnd/npc-registry", want: contracts.ExecutionClassLLMBacked},
|
||||||
{stage: pipeline.StageExtract, key: "dnd/combat-turns", want: contracts.ExecutionClassLLMBacked},
|
{stage: pipeline.StageExtract, key: "dnd/combat-turns", want: contracts.ExecutionClassLLMBacked},
|
||||||
{stage: pipeline.StageExtract, key: "dnd/item-events", want: contracts.ExecutionClassLLMBacked},
|
{stage: pipeline.StageExtract, key: "dnd/item-occurrences", want: contracts.ExecutionClassLLMBacked},
|
||||||
{stage: pipeline.StageExtract, key: "dnd/npc-occurrences", want: contracts.ExecutionClassLLMBacked},
|
{stage: pipeline.StageExtract, key: "dnd/npc-occurrences", want: contracts.ExecutionClassLLMBacked},
|
||||||
{stage: pipeline.StageExtract, key: "dnd/scene-descriptions", want: contracts.ExecutionClassLLMBacked},
|
{stage: pipeline.StageExtract, key: "dnd/scene-descriptions", want: contracts.ExecutionClassLLMBacked},
|
||||||
{stage: pipeline.StageExtract, key: enemyeventextract.Key, want: contracts.ExecutionClassLLMBacked},
|
{stage: pipeline.StageExtract, key: enemyeventextract.Key, want: contracts.ExecutionClassLLMBacked},
|
||||||
@@ -177,7 +177,7 @@ func TestProductionCatalogCoversMaintainedConfigurations(t *testing.T) {
|
|||||||
{stage: pipeline.StageNormalize, key: "dnd/spells", want: contracts.ExecutionClassDeterministic},
|
{stage: pipeline.StageNormalize, key: "dnd/spells", want: contracts.ExecutionClassDeterministic},
|
||||||
{stage: pipeline.StageNormalize, key: "dnd/npc-registry", want: contracts.ExecutionClassLLMBacked},
|
{stage: pipeline.StageNormalize, key: "dnd/npc-registry", want: contracts.ExecutionClassLLMBacked},
|
||||||
{stage: pipeline.StageNormalize, key: "dnd/combat-turns", want: contracts.ExecutionClassDeterministic},
|
{stage: pipeline.StageNormalize, key: "dnd/combat-turns", want: contracts.ExecutionClassDeterministic},
|
||||||
{stage: pipeline.StageNormalize, key: "dnd/item-events", want: contracts.ExecutionClassDeterministic},
|
{stage: pipeline.StageNormalize, key: "dnd/item-occurrences", want: contracts.ExecutionClassDeterministic},
|
||||||
{stage: pipeline.StageNormalize, key: "dnd/npc-occurrences", want: contracts.ExecutionClassDeterministic},
|
{stage: pipeline.StageNormalize, key: "dnd/npc-occurrences", want: contracts.ExecutionClassDeterministic},
|
||||||
{stage: pipeline.StageNormalize, key: "dnd/scene-descriptions", want: contracts.ExecutionClassDeterministic},
|
{stage: pipeline.StageNormalize, key: "dnd/scene-descriptions", want: contracts.ExecutionClassDeterministic},
|
||||||
{stage: pipeline.StageNormalize, key: enemyeventnormalize.Key, want: contracts.ExecutionClassDeterministic},
|
{stage: pipeline.StageNormalize, key: enemyeventnormalize.Key, want: contracts.ExecutionClassDeterministic},
|
||||||
@@ -200,9 +200,9 @@ func TestProductionCatalogCoversMaintainedConfigurations(t *testing.T) {
|
|||||||
if !ok || combatCodecSpec.Kind != dnd.CombatTurnListKind || combatCodecSpec.Schema.ID != combatcodec.SchemaID {
|
if !ok || combatCodecSpec.Kind != dnd.CombatTurnListKind || combatCodecSpec.Schema.ID != combatcodec.SchemaID {
|
||||||
t.Fatalf("combat codec spec = %#v, ok=%t, want typed D&D combat codec", combatCodecSpec, ok)
|
t.Fatalf("combat codec spec = %#v, ok=%t, want typed D&D combat codec", combatCodecSpec, ok)
|
||||||
}
|
}
|
||||||
itemEventCodecSpec, ok := catalog.ArtifactCodecs.Spec(dnd.ItemOccurrenceListKind)
|
itemOccurrenceCodecSpec, ok := catalog.ArtifactCodecs.Spec(dnd.ItemOccurrenceListKind)
|
||||||
if !ok || itemEventCodecSpec.Kind != dnd.ItemOccurrenceListKind || itemEventCodecSpec.Schema.ID != itemeventcodec.SchemaID {
|
if !ok || itemOccurrenceCodecSpec.Kind != dnd.ItemOccurrenceListKind || itemOccurrenceCodecSpec.Schema.ID != itemoccurrencecodec.SchemaID {
|
||||||
t.Fatalf("item event codec spec = %#v, ok=%t, want typed D&D item-event codec", itemEventCodecSpec, ok)
|
t.Fatalf("item occurrence codec spec = %#v, ok=%t, want typed D&D item-occurrence codec", itemOccurrenceCodecSpec, ok)
|
||||||
}
|
}
|
||||||
enemyEventCodecSpec, ok := catalog.ArtifactCodecs.Spec(dnd.EnemyEventListKind)
|
enemyEventCodecSpec, ok := catalog.ArtifactCodecs.Spec(dnd.EnemyEventListKind)
|
||||||
if !ok || enemyEventCodecSpec.Kind != dnd.EnemyEventListKind || enemyEventCodecSpec.Schema.ID != enemyeventcodec.SchemaID {
|
if !ok || enemyEventCodecSpec.Kind != dnd.EnemyEventListKind || enemyEventCodecSpec.Schema.ID != enemyeventcodec.SchemaID {
|
||||||
@@ -783,8 +783,8 @@ func TestProductionSceneRunRecordsAnnotationFreeChunkPlanAndProvenance(t *testin
|
|||||||
if len(warnings.Warnings) != 0 {
|
if len(warnings.Warnings) != 0 {
|
||||||
t.Fatalf("warnings = %#v, want none", warnings.Warnings)
|
t.Fatalf("warnings = %#v, want none", warnings.Warnings)
|
||||||
}
|
}
|
||||||
if len(fake.requestsFor(scenes.PromptID)) != 1 || len(fake.requestsFor(spells.PromptID)) != 1 || len(fake.requestsFor(itemeventextract.PromptID)) != 1 {
|
if len(fake.requestsFor(scenes.PromptID)) != 1 || len(fake.requestsFor(spells.PromptID)) != 1 || len(fake.requestsFor(itemoccurrenceextract.PromptID)) != 1 {
|
||||||
t.Fatalf("fake prompt requests = %#v, want one scene, spell, and item-event request", fake.requestPrompts())
|
t.Fatalf("fake prompt requests = %#v, want one scene, spell, and item-occurrence request", fake.requestPrompts())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -935,8 +935,8 @@ pipelines:
|
|||||||
artifacts:
|
artifacts:
|
||||||
spells:
|
spells:
|
||||||
extract: dnd/spells
|
extract: dnd/spells
|
||||||
item-events:
|
item-occurrences:
|
||||||
extract: dnd/item-events
|
extract: dnd/item-occurrences
|
||||||
`, outputRoot, filepath.Join(filepath.Dir(outputRoot), "debug"), chunkModule)
|
`, outputRoot, filepath.Join(filepath.Dir(outputRoot), "debug"), chunkModule)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1013,7 +1013,7 @@ func (client *productionFakeLLMClient) CompleteStructured(ctx context.Context, r
|
|||||||
} else {
|
} else {
|
||||||
content = []byte(`{"spell_casts":[{"caster":"Aria","spell":"Cure Wounds","source_refs":[{"start_unit_id":1,"end_unit_id":1}]}]}`)
|
content = []byte(`{"spell_casts":[{"caster":"Aria","spell":"Cure Wounds","source_refs":[{"start_unit_id":1,"end_unit_id":1}]}]}`)
|
||||||
}
|
}
|
||||||
case itemeventextract.PromptID:
|
case itemoccurrenceextract.PromptID:
|
||||||
content = []byte(`{"occurrences":[]}`)
|
content = []byte(`{"occurrences":[]}`)
|
||||||
case itemregistryextract.PromptID:
|
case itemregistryextract.PromptID:
|
||||||
content = []byte(`{"items":[]}`)
|
content = []byte(`{"items":[]}`)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Package itemevents encodes durable D&D item-event artifacts.
|
// Package itemoccurrences encodes durable D&D item-occurrence artifacts.
|
||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
@@ -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/modules/dnd"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/candidatejson"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/candidatejson"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/itemevents"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/itemoccurrences"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -86,35 +86,35 @@ func (c *Codec) DecodeCandidate(content []byte) (dnd.ItemOccurrenceList, error)
|
|||||||
|
|
||||||
func validate(value dnd.ItemOccurrenceList) error {
|
func validate(value dnd.ItemOccurrenceList) error {
|
||||||
if value.Occurrences == nil {
|
if value.Occurrences == nil {
|
||||||
return fmt.Errorf("events must be present")
|
return fmt.Errorf("occurrences must be present")
|
||||||
}
|
}
|
||||||
for index, event := range value.Occurrences {
|
for index, occurrence := range value.Occurrences {
|
||||||
prefix := fmt.Sprintf("occurrences[%d]", index)
|
prefix := fmt.Sprintf("occurrences[%d]", index)
|
||||||
if strings.TrimSpace(event.ItemID) == "" {
|
if strings.TrimSpace(occurrence.ItemID) == "" {
|
||||||
return fmt.Errorf("%s.item_id must not be empty", prefix)
|
return fmt.Errorf("%s.item_id must not be empty", prefix)
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(event.Name) == "" {
|
if strings.TrimSpace(occurrence.Name) == "" {
|
||||||
return fmt.Errorf("%s.name must not be empty", prefix)
|
return fmt.Errorf("%s.name must not be empty", prefix)
|
||||||
}
|
}
|
||||||
if !itemevents.SupportedKind(event.Kind) {
|
if !itemoccurrences.SupportedKind(occurrence.Kind) {
|
||||||
return fmt.Errorf("%s.kind must be supported", prefix)
|
return fmt.Errorf("%s.kind must be supported", prefix)
|
||||||
}
|
}
|
||||||
if event.From != "" && strings.TrimSpace(event.From) == "" {
|
if occurrence.From != "" && strings.TrimSpace(occurrence.From) == "" {
|
||||||
return fmt.Errorf("%s.from must not be empty when present", prefix)
|
return fmt.Errorf("%s.from must not be empty when present", prefix)
|
||||||
}
|
}
|
||||||
if event.To != "" && strings.TrimSpace(event.To) == "" {
|
if occurrence.To != "" && strings.TrimSpace(occurrence.To) == "" {
|
||||||
return fmt.Errorf("%s.to must not be empty when present", prefix)
|
return fmt.Errorf("%s.to must not be empty when present", prefix)
|
||||||
}
|
}
|
||||||
if !itemevents.ValidHolderCombination(event.Kind, event.From, event.To) {
|
if !itemoccurrences.ValidHolderCombination(occurrence.Kind, occurrence.From, occurrence.To) {
|
||||||
return fmt.Errorf("%s holders are incompatible with %q", prefix, event.Kind)
|
return fmt.Errorf("%s holders are incompatible with %q", prefix, occurrence.Kind)
|
||||||
}
|
}
|
||||||
if event.Quantity != nil && *event.Quantity < 1 {
|
if occurrence.Quantity != nil && *occurrence.Quantity < 1 {
|
||||||
return fmt.Errorf("%s.quantity must be positive when present", prefix)
|
return fmt.Errorf("%s.quantity must be positive when present", prefix)
|
||||||
}
|
}
|
||||||
if len(event.SourceRefs) == 0 {
|
if len(occurrence.SourceRefs) == 0 {
|
||||||
return fmt.Errorf("%s.source_refs must contain at least one reference", prefix)
|
return fmt.Errorf("%s.source_refs must contain at least one reference", prefix)
|
||||||
}
|
}
|
||||||
for refIndex, ref := range event.SourceRefs {
|
for refIndex, ref := range occurrence.SourceRefs {
|
||||||
refPrefix := fmt.Sprintf("%s.source_refs[%d]", prefix, refIndex)
|
refPrefix := fmt.Sprintf("%s.source_refs[%d]", prefix, refIndex)
|
||||||
if strings.TrimSpace(ref.SourceID) == "" {
|
if strings.TrimSpace(ref.SourceID) == "" {
|
||||||
return fmt.Errorf("%s.source_id must not be empty", refPrefix)
|
return fmt.Errorf("%s.source_id must not be empty", refPrefix)
|
||||||
@@ -135,14 +135,14 @@ func cloneList(value dnd.ItemOccurrenceList) dnd.ItemOccurrenceList {
|
|||||||
return dnd.ItemOccurrenceList{}
|
return dnd.ItemOccurrenceList{}
|
||||||
}
|
}
|
||||||
cloned := dnd.ItemOccurrenceList{Occurrences: make([]dnd.ItemOccurrence, len(value.Occurrences))}
|
cloned := dnd.ItemOccurrenceList{Occurrences: make([]dnd.ItemOccurrence, len(value.Occurrences))}
|
||||||
for index, event := range value.Occurrences {
|
for index, occurrence := range value.Occurrences {
|
||||||
cloned.Occurrences[index] = event
|
cloned.Occurrences[index] = occurrence
|
||||||
if event.Quantity != nil {
|
if occurrence.Quantity != nil {
|
||||||
quantity := *event.Quantity
|
quantity := *occurrence.Quantity
|
||||||
cloned.Occurrences[index].Quantity = &quantity
|
cloned.Occurrences[index].Quantity = &quantity
|
||||||
}
|
}
|
||||||
if event.SourceRefs != nil {
|
if occurrence.SourceRefs != nil {
|
||||||
cloned.Occurrences[index].SourceRefs = append([]source.SourceRef(nil), event.SourceRefs...)
|
cloned.Occurrences[index].SourceRefs = append([]source.SourceRef(nil), occurrence.SourceRefs...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cloned
|
return cloned
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -93,10 +93,10 @@ func TestCodecRejectsStrictJSONAndApprovedBoundaries(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"malformed", `{`, "decode dnd item occurrence list"},
|
{"malformed", `{`, "decode dnd item occurrence list"},
|
||||||
{"unknown top level", `{"occurrences":[],"unexpected":true}`, "unknown field"},
|
{"unknown top level", `{"occurrences":[],"unexpected":true}`, "unknown field"},
|
||||||
{"unknown event field", strings.Replace(validJSON, `"to":"party"`, `"to":"party","unexpected":true`, 1), "unknown field"},
|
{"unknown occurrence field", strings.Replace(validJSON, `"to":"party"`, `"to":"party","unexpected":true`, 1), "unknown field"},
|
||||||
{"unknown reference field", strings.Replace(validJSON, `"end_unit_id":1`, `"end_unit_id":1,"unexpected":true`, 1), "unknown field"},
|
{"unknown reference field", strings.Replace(validJSON, `"end_unit_id":1`, `"end_unit_id":1,"unexpected":true`, 1), "unknown field"},
|
||||||
{"trailing", `{"occurrences":[]} {}`, "multiple JSON values"},
|
{"trailing", `{"occurrences":[]} {}`, "multiple JSON values"},
|
||||||
{"missing list", `{}`, "events must be present"},
|
{"missing list", `{}`, "occurrences must be present"},
|
||||||
{"zero quantity", strings.Replace(validJSON, `"to":"party"`, `"quantity":0,"to":"party"`, 1), "quantity must be positive"},
|
{"zero quantity", strings.Replace(validJSON, `"to":"party"`, `"quantity":0,"to":"party"`, 1), "quantity must be positive"},
|
||||||
{"negative quantity", strings.Replace(validJSON, `"to":"party"`, `"quantity":-1,"to":"party"`, 1), "quantity must be positive"},
|
{"negative quantity", strings.Replace(validJSON, `"to":"party"`, `"quantity":-1,"to":"party"`, 1), "quantity must be positive"},
|
||||||
{"party transfer", strings.Replace(validJSON, `"kind":"acquired","to":"party"`, `"kind":"transferred","from":"party","to":"Borin"`, 1), "holders are incompatible"},
|
{"party transfer", strings.Replace(validJSON, `"kind":"acquired","to":"party"`, `"kind":"transferred","from":"party","to":"Borin"`, 1), "holders are incompatible"},
|
||||||
@@ -125,7 +125,7 @@ func TestCodecDeepCopiesBoundaryValuesAndMetadata(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if decoded.Occurrences[1].Quantity == value.Occurrences[1].Quantity || &decoded.Occurrences[1].SourceRefs[0] == &value.Occurrences[1].SourceRefs[0] {
|
if decoded.Occurrences[1].Quantity == value.Occurrences[1].Quantity || &decoded.Occurrences[1].SourceRefs[0] == &value.Occurrences[1].SourceRefs[0] {
|
||||||
t.Fatal("DecodeCandidate() retained caller-owned event fields")
|
t.Fatal("DecodeCandidate() retained caller-owned occurrence fields")
|
||||||
}
|
}
|
||||||
*decoded.Occurrences[1].Quantity = 99
|
*decoded.Occurrences[1].Quantity = 99
|
||||||
decoded.Occurrences[1].SourceRefs[0].SourceID = "changed"
|
decoded.Occurrences[1].SourceRefs[0].SourceID = "changed"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
@@ -42,12 +42,12 @@ func canonicalizeResponse(response *extractionResponse, order shared.SourceRefOr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func canonicalizeItemOccurrence(event *itemOccurrenceResponse, order shared.SourceRefOrder, sourceID string) (int, bool) {
|
func canonicalizeItemOccurrence(occurrence *itemOccurrenceResponse, order shared.SourceRefOrder, sourceID string) (int, bool) {
|
||||||
if event == nil {
|
if occurrence == nil {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
refs := order.Canonicalize(itemOccurrenceSourceRefs(event.SourceRefs, sourceID))
|
refs := order.Canonicalize(itemOccurrenceSourceRefs(occurrence.SourceRefs, sourceID))
|
||||||
event.SourceRefs = itemOccurrenceResponseRefs(refs)
|
occurrence.SourceRefs = itemOccurrenceResponseRefs(refs)
|
||||||
return order.EarliestValid(refs)
|
return order.EarliestValid(refs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,19 +56,19 @@ func canonicalItemOccurrenceList(response extractionResponse, sourceID string, r
|
|||||||
return dnd.ItemOccurrenceList{}
|
return dnd.ItemOccurrenceList{}
|
||||||
}
|
}
|
||||||
occurrences := make([]dnd.ItemOccurrence, 0, len(response.Occurrences))
|
occurrences := make([]dnd.ItemOccurrence, 0, len(response.Occurrences))
|
||||||
for _, event := range response.Occurrences {
|
for _, occurrence := range response.Occurrences {
|
||||||
item, found := registry.LookupID(event.ItemID)
|
item, found := registry.LookupID(occurrence.ItemID)
|
||||||
if !found || item.Name != event.Name {
|
if !found || item.Name != occurrence.Name {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
occurrences = append(occurrences, dnd.ItemOccurrence{
|
occurrences = append(occurrences, dnd.ItemOccurrence{
|
||||||
ItemID: event.ItemID,
|
ItemID: occurrence.ItemID,
|
||||||
Name: event.Name,
|
Name: occurrence.Name,
|
||||||
Kind: dnd.ItemOccurrenceKind(event.Kind),
|
Kind: dnd.ItemOccurrenceKind(occurrence.Kind),
|
||||||
Quantity: cloneQuantity(event.Quantity),
|
Quantity: cloneQuantity(occurrence.Quantity),
|
||||||
From: event.From,
|
From: occurrence.From,
|
||||||
To: event.To,
|
To: occurrence.To,
|
||||||
SourceRefs: itemOccurrenceSourceRefs(event.SourceRefs, sourceID),
|
SourceRefs: itemOccurrenceSourceRefs(occurrence.SourceRefs, sourceID),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return dnd.ItemOccurrenceList{Occurrences: occurrences}
|
return dnd.ItemOccurrenceList{Occurrences: occurrences}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// Package itemevents extracts source-grounded D&D item occurrences.
|
// Package itemoccurrences extracts source-grounded D&D item occurrences.
|
||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -13,14 +13,14 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Key = "dnd/item-events"
|
const Key = "dnd/item-occurrences"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ItemRegistryReferenceSlot = itemregistry.ReferenceSlot
|
ItemRegistryReferenceSlot = itemregistry.ReferenceSlot
|
||||||
ItemRegistryMaxBytes = itemregistry.MaxBytes
|
ItemRegistryMaxBytes = itemregistry.MaxBytes
|
||||||
)
|
)
|
||||||
|
|
||||||
const mappingPolicy = "dnd.item_events.extract_mapping.v1"
|
const mappingPolicy = "dnd.item_occurrences.extract_mapping.v1"
|
||||||
|
|
||||||
var requiredCapabilities = []string{
|
var requiredCapabilities = []string{
|
||||||
"chunks",
|
"chunks",
|
||||||
@@ -28,7 +28,7 @@ var requiredCapabilities = []string{
|
|||||||
}
|
}
|
||||||
|
|
||||||
var providedCapabilities = []string{
|
var providedCapabilities = []string{
|
||||||
"dnd.item_events",
|
"dnd.item_occurrences",
|
||||||
}
|
}
|
||||||
|
|
||||||
var referenceSlotDescriptions = shared.ReferenceSlotDescriptions{
|
var referenceSlotDescriptions = shared.ReferenceSlotDescriptions{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
type extractionResponse struct {
|
type extractionResponse struct {
|
||||||
Occurrences []itemOccurrenceResponse `json:"occurrences"`
|
Occurrences []itemOccurrenceResponse `json:"occurrences"`
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
const promptAssetRoot = "assets/prompts"
|
const promptAssetRoot = "assets/prompts"
|
||||||
|
|
||||||
var promptAssetManifest = shared.PromptAssetManifest{
|
var promptAssetManifest = shared.PromptAssetManifest{
|
||||||
ModuleDir: "dnd.item_events",
|
ModuleDir: "dnd.item_occurrences",
|
||||||
ModuleFiles: []promptfs.ModulePromptFile{
|
ModuleFiles: []promptfs.ModulePromptFile{
|
||||||
{Name: "prompt.yaml", Path: "prompts/prompt.yaml"},
|
{Name: "prompt.yaml", Path: "prompts/prompt.yaml"},
|
||||||
{Name: "instructions.md", Path: "prompts/instructions.md"},
|
{Name: "instructions.md", Path: "prompts/instructions.md"},
|
||||||
@@ -30,9 +30,9 @@ var promptAssetManifest = shared.PromptAssetManifest{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func moduleAssetFS() (fs.FS, error) {
|
func moduleAssetFS() (fs.FS, error) {
|
||||||
assets, err := fs.Sub(rootassets.FS(), "dnd/item-events")
|
assets, err := fs.Sub(rootassets.FS(), "dnd/item-occurrences")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("scope item-event assets: %w", err)
|
return nil, fmt.Errorf("scope item-occurrence assets: %w", err)
|
||||||
}
|
}
|
||||||
return assets, nil
|
return assets, nil
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ func RegisterPromptAssets(registry *llm.AssetRegistry) error {
|
|||||||
}
|
}
|
||||||
schemas, err := fs.Sub(assets, "schemas")
|
schemas, err := fs.Sub(assets, "schemas")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("scope item-event schemas: %w", err)
|
return fmt.Errorf("scope item-occurrence schemas: %w", err)
|
||||||
}
|
}
|
||||||
return registry.RegisterSchemaFS(schemas, ".")
|
return registry.RegisterSchemaFS(schemas, ".")
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -21,17 +21,17 @@ func TestPromptAssetsPrepareItemOccurrencePrompt(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
options = append(options, promptkit.WithProfiles(promptkit.OpenAICompatibleProfile(promptkit.OpenAICompatibleProfileConfig{
|
options = append(options, promptkit.WithProfiles(promptkit.OpenAICompatibleProfile(promptkit.OpenAICompatibleProfileConfig{
|
||||||
ID: "item-events-test-profile", Endpoint: "http://127.0.0.1:1/v1", Model: "item-events-test-model",
|
ID: "item-occurrences-test-profile", Endpoint: "http://127.0.0.1:1/v1", Model: "item-occurrences-test-model",
|
||||||
})))
|
})))
|
||||||
engine, err := promptkit.NewEngine(promptkit.Config{Timeout: time.Second}, options...)
|
engine, err := promptkit.NewEngine(promptkit.Config{Timeout: time.Second}, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{
|
prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{
|
||||||
PromptID: PromptID, PromptVersion: SchemaVersion, ProfileID: "item-events-test-profile",
|
PromptID: PromptID, PromptVersion: SchemaVersion, ProfileID: "item-occurrences-test-profile",
|
||||||
Inputs: map[string]promptkit.ArtifactRef{
|
Inputs: map[string]promptkit.ArtifactRef{
|
||||||
"transcript": promptkit.InlineWithURI("file:///session.json", `{"units":[{"sentinel":"item-event-transcript"}]}`),
|
"transcript": promptkit.InlineWithURI("file:///session.json", `{"units":[{"sentinel":"item-occurrence-transcript"}]}`),
|
||||||
"players": promptkit.Inline("item-event-player"),
|
"players": promptkit.Inline("item-occurrence-player"),
|
||||||
"party": promptkit.Inline(" "),
|
"party": promptkit.Inline(" "),
|
||||||
"glossary": promptkit.Inline(" "),
|
"glossary": promptkit.Inline(" "),
|
||||||
"item_registry": promptkit.Inline(`{"items":[{"id":"item:sha256:test","name":"Torch"}]}`),
|
"item_registry": promptkit.Inline(`{"items":[{"id":"item:sha256:test","name":"Torch"}]}`),
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import "gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
|
import "gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PromptID = "dnd.item_events"
|
PromptID = "dnd.item_occurrences"
|
||||||
ResponseSchemaKey = llm.ResponseSchemaKey("dnd_item_occurrences_llm")
|
ResponseSchemaKey = llm.ResponseSchemaKey("dnd_item_occurrences_llm")
|
||||||
ResponseSchemaID = "notarius.dnd.item_occurrences.llm"
|
ResponseSchemaID = "notarius.dnd.item_occurrences.llm"
|
||||||
ResponseSchemaName = "notarius_dnd_item_occurrences_llm_v1"
|
ResponseSchemaName = "notarius_dnd_item_occurrences_llm_v1"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -39,11 +39,11 @@ func TestResponseSchemaIsStrictlyStructuralAndPrivate(t *testing.T) {
|
|||||||
value map[string]any
|
value map[string]any
|
||||||
}{
|
}{
|
||||||
{"missing occurrences", map[string]any{}},
|
{"missing occurrences", map[string]any{}},
|
||||||
{"missing event name", map[string]any{"occurrences": []any{withoutField(responseEvent(), "name")}}},
|
{"missing occurrence name", map[string]any{"occurrences": []any{withoutField(responseOccurrence(), "name")}}},
|
||||||
{"missing nullable field", map[string]any{"occurrences": []any{withoutField(responseEvent(), "quantity")}}},
|
{"missing nullable field", map[string]any{"occurrences": []any{withoutField(responseOccurrence(), "quantity")}}},
|
||||||
{"unknown event field", map[string]any{"occurrences": []any{withField(responseEvent(), "extra", true)}}},
|
{"unknown occurrence field", map[string]any{"occurrences": []any{withField(responseOccurrence(), "extra", true)}}},
|
||||||
{"unknown reference field", map[string]any{"occurrences": []any{withField(responseEvent(), "source_refs", []any{map[string]any{"start_segment": 1, "end_segment": 1, "extra": true}})}}},
|
{"unknown reference field", map[string]any{"occurrences": []any{withField(responseOccurrence(), "source_refs", []any{map[string]any{"start_segment": 1, "end_segment": 1, "extra": true}})}}},
|
||||||
{"noninteger range", map[string]any{"occurrences": []any{withField(responseEvent(), "source_refs", []any{map[string]any{"start_segment": 1.5, "end_segment": 1}})}}},
|
{"noninteger range", map[string]any{"occurrences": []any{withField(responseOccurrence(), "source_refs", []any{map[string]any{"start_segment": 1.5, "end_segment": 1}})}}},
|
||||||
} {
|
} {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
content, err := json.Marshal(test.value)
|
content, err := json.Marshal(test.value)
|
||||||
@@ -66,7 +66,7 @@ func TestResponseSchemaIsStrictlyStructuralAndPrivate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func responseEvent() map[string]any {
|
func responseOccurrence() map[string]any {
|
||||||
return map[string]any{
|
return map[string]any{
|
||||||
"item_id": "item:sha256:test", "name": "Ring", "kind": "acquired", "quantity": nil, "from": nil, "to": "party", "source_refs": []any{},
|
"item_id": "item:sha256:test", "name": "Ring", "kind": "acquired", "quantity": nil, "from": nil, "to": "party", "source_refs": []any{},
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -26,7 +26,7 @@ func extractionRequest() contracts.TypedExtractionRequest {
|
|||||||
Chunk: chunk,
|
Chunk: chunk,
|
||||||
SourceInput: contracts.NewLLMInputMaterial("source", chunk.MediaType, chunk.Content, "sha256:chunk", "file:///session-alpha.json"),
|
SourceInput: contracts.NewLLMInputMaterial("source", chunk.MediaType, chunk.Content, "sha256:chunk", "file:///session-alpha.json"),
|
||||||
SessionID: "session-123",
|
SessionID: "session-123",
|
||||||
LLMProfile: "profile-item-events",
|
LLMProfile: "profile-item-occurrences",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Package itemevents owns canonical ordering and durable domain rules for D&D
|
// Package itemoccurrences owns canonical ordering and durable domain rules for D&D
|
||||||
// item-event artifacts.
|
// item-occurrence artifacts.
|
||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -90,7 +90,7 @@ func ValidSourceRefs(index source.DocumentIndex, refs []source.SourceRef) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Less defines the canonical event order. Invalid source references remain
|
// Less defines the canonical occurrence order. Invalid source references remain
|
||||||
// comparable through SourceRefOrder's literal fallback so malformed candidates
|
// comparable through SourceRefOrder's literal fallback so malformed candidates
|
||||||
// are still safe to sort and diagnose.
|
// are still safe to sort and diagnose.
|
||||||
func Less(order shared.SourceRefOrder, left, right dnd.ItemOccurrence) bool {
|
func Less(order shared.SourceRefOrder, left, right dnd.ItemOccurrence) bool {
|
||||||
@@ -126,7 +126,7 @@ func Less(order shared.SourceRefOrder, left, right dnd.ItemOccurrence) bool {
|
|||||||
return sourceRefsLess(order, order.Canonicalize(left.SourceRefs), order.Canonicalize(right.SourceRefs))
|
return sourceRefsLess(order, order.Canonicalize(left.SourceRefs), order.Canonicalize(right.SourceRefs))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExactEqual reports whether events are exact duplicates after their display
|
// ExactEqual reports whether occurrences are exact duplicates after their display
|
||||||
// fields and evidence have been canonicalized for the supplied source order.
|
// fields and evidence have been canonicalized for the supplied source order.
|
||||||
func ExactEqual(order shared.SourceRefOrder, left, right dnd.ItemOccurrence) bool {
|
func ExactEqual(order shared.SourceRefOrder, left, right dnd.ItemOccurrence) bool {
|
||||||
if left.ItemID != right.ItemID || DisplayValue(left.Name) != DisplayValue(right.Name) || left.Kind != right.Kind ||
|
if left.ItemID != right.ItemID || DisplayValue(left.Name) != DisplayValue(right.Name) || left.Kind != right.Kind ||
|
||||||
@@ -142,21 +142,21 @@ func ExactEqual(order shared.SourceRefOrder, left, right dnd.ItemOccurrence) boo
|
|||||||
|
|
||||||
// ExactIdentity returns a collision-safe duplicate key after display and
|
// ExactIdentity returns a collision-safe duplicate key after display and
|
||||||
// evidence canonicalization. It is intended for callers that have already
|
// evidence canonicalization. It is intended for callers that have already
|
||||||
// decided the event is eligible for duplicate handling.
|
// decided the occurrence is eligible for duplicate handling.
|
||||||
func ExactIdentity(order shared.SourceRefOrder, event dnd.ItemOccurrence) string {
|
func ExactIdentity(order shared.SourceRefOrder, occurrence dnd.ItemOccurrence) string {
|
||||||
var key strings.Builder
|
var key strings.Builder
|
||||||
writeKeyString(&key, event.ItemID)
|
writeKeyString(&key, occurrence.ItemID)
|
||||||
writeKeyString(&key, DisplayValue(event.Name))
|
writeKeyString(&key, DisplayValue(occurrence.Name))
|
||||||
writeKeyString(&key, string(event.Kind))
|
writeKeyString(&key, string(occurrence.Kind))
|
||||||
writeKeyString(&key, DisplayValue(event.From))
|
writeKeyString(&key, DisplayValue(occurrence.From))
|
||||||
writeKeyString(&key, DisplayValue(event.To))
|
writeKeyString(&key, DisplayValue(occurrence.To))
|
||||||
if event.Quantity == nil {
|
if occurrence.Quantity == nil {
|
||||||
key.WriteByte('0')
|
key.WriteByte('0')
|
||||||
} else {
|
} else {
|
||||||
key.WriteByte('1')
|
key.WriteByte('1')
|
||||||
writeKeyInt(&key, *event.Quantity)
|
writeKeyInt(&key, *occurrence.Quantity)
|
||||||
}
|
}
|
||||||
for _, ref := range order.Canonicalize(event.SourceRefs) {
|
for _, ref := range order.Canonicalize(occurrence.SourceRefs) {
|
||||||
writeKeyString(&key, ref.SourceID)
|
writeKeyString(&key, ref.SourceID)
|
||||||
writeKeyInt(&key, ref.StartUnitID)
|
writeKeyInt(&key, ref.StartUnitID)
|
||||||
writeKeyInt(&key, ref.EndUnitID)
|
writeKeyInt(&key, ref.EndUnitID)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
@@ -136,13 +136,13 @@ func TestSourceReferenceHelpers(t *testing.T) {
|
|||||||
|
|
||||||
func TestLessSortsMalformedReferencesDeterministically(t *testing.T) {
|
func TestLessSortsMalformedReferencesDeterministically(t *testing.T) {
|
||||||
order := testOrder()
|
order := testOrder()
|
||||||
events := []dnd.ItemOccurrence{{ItemID: "item", Name: "A", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 999, EndUnitID: 999}}},
|
occurrences := []dnd.ItemOccurrence{{ItemID: "item", Name: "A", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 999, EndUnitID: 999}}},
|
||||||
{ItemID: "item", Name: "A", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: "other", StartUnitID: 1, EndUnitID: 1}}},
|
{ItemID: "item", Name: "A", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: "other", StartUnitID: 1, EndUnitID: 1}}},
|
||||||
{ItemID: "item", Name: "A", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 10, EndUnitID: 10}}},
|
{ItemID: "item", Name: "A", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 10, EndUnitID: 10}}},
|
||||||
}
|
}
|
||||||
sort.SliceStable(events, func(left, right int) bool { return Less(order, events[left], events[right]) })
|
sort.SliceStable(occurrences, func(left, right int) bool { return Less(order, occurrences[left], occurrences[right]) })
|
||||||
if events[0].SourceRefs[0].StartUnitID != 10 || events[1].SourceRefs[0].SourceID != "other" || events[2].SourceRefs[0].StartUnitID != 999 {
|
if occurrences[0].SourceRefs[0].StartUnitID != 10 || occurrences[1].SourceRefs[0].SourceID != "other" || occurrences[2].SourceRefs[0].StartUnitID != 999 {
|
||||||
t.Fatalf("canonical sort = %#v", events)
|
t.Fatalf("canonical sort = %#v", occurrences)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,28 +150,31 @@ func testOrder() shared.SourceRefOrder {
|
|||||||
return shared.NewSourceRefOrder(&source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 10}, {ID: 20}, {ID: 30}}})
|
return shared.NewSourceRefOrder(&source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 10}, {ID: 20}, {ID: 30}}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func withName(event dnd.ItemOccurrence, value string) dnd.ItemOccurrence {
|
func withName(occurrence dnd.ItemOccurrence, value string) dnd.ItemOccurrence {
|
||||||
event.Name = value
|
occurrence.Name = value
|
||||||
return event
|
return occurrence
|
||||||
}
|
}
|
||||||
func withKind(event dnd.ItemOccurrence, value dnd.ItemOccurrenceKind) dnd.ItemOccurrence {
|
func withKind(occurrence dnd.ItemOccurrence, value dnd.ItemOccurrenceKind) dnd.ItemOccurrence {
|
||||||
event.Kind = value
|
occurrence.Kind = value
|
||||||
return event
|
return occurrence
|
||||||
}
|
}
|
||||||
func withFrom(event dnd.ItemOccurrence, value string) dnd.ItemOccurrence {
|
func withFrom(occurrence dnd.ItemOccurrence, value string) dnd.ItemOccurrence {
|
||||||
event.From = value
|
occurrence.From = value
|
||||||
return event
|
return occurrence
|
||||||
}
|
}
|
||||||
func withTo(event dnd.ItemOccurrence, value string) dnd.ItemOccurrence {
|
func withTo(occurrence dnd.ItemOccurrence, value string) dnd.ItemOccurrence {
|
||||||
event.To = value
|
occurrence.To = value
|
||||||
return event
|
return occurrence
|
||||||
}
|
}
|
||||||
func withoutTo(event dnd.ItemOccurrence) dnd.ItemOccurrence { event.To = ""; return event }
|
func withoutTo(occurrence dnd.ItemOccurrence) dnd.ItemOccurrence {
|
||||||
func withQuantity(event dnd.ItemOccurrence, value *int) dnd.ItemOccurrence {
|
occurrence.To = ""
|
||||||
event.Quantity = value
|
return occurrence
|
||||||
return event
|
|
||||||
}
|
}
|
||||||
func withRefs(event dnd.ItemOccurrence, value []source.SourceRef) dnd.ItemOccurrence {
|
func withQuantity(occurrence dnd.ItemOccurrence, value *int) dnd.ItemOccurrence {
|
||||||
event.SourceRefs = value
|
occurrence.Quantity = value
|
||||||
return event
|
return occurrence
|
||||||
|
}
|
||||||
|
func withRefs(occurrence dnd.ItemOccurrence, value []source.SourceRef) dnd.ItemOccurrence {
|
||||||
|
occurrence.SourceRefs = value
|
||||||
|
return occurrence
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// Package itemevents normalizes merged D&D item-event candidates.
|
// Package itemoccurrences normalizes merged D&D item-occurrence candidates.
|
||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -10,15 +10,15 @@ 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"
|
||||||
itemeventmodel "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/itemevents"
|
itemoccurrencemodel "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/itemoccurrences"
|
||||||
itemregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/items/registry"
|
itemregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/items/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"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Key = "dnd/item-events"
|
Key = "dnd/item-occurrences"
|
||||||
normalizationPolicy = "dnd.item_events.normalize.v1"
|
normalizationPolicy = "dnd.item_occurrences.normalize.v1"
|
||||||
NormalizationPolicy = normalizationPolicy
|
NormalizationPolicy = normalizationPolicy
|
||||||
|
|
||||||
ReasonCodeNameCanonicalized = "item_occurrence_name_canonicalized"
|
ReasonCodeNameCanonicalized = "item_occurrence_name_canonicalized"
|
||||||
@@ -111,7 +111,7 @@ func (n *Normalizer) Normalize(ctx context.Context, req contracts.TypedNormalize
|
|||||||
}
|
}
|
||||||
|
|
||||||
type normalizedRecord struct {
|
type normalizedRecord struct {
|
||||||
event dnd.ItemOccurrence
|
occurrence dnd.ItemOccurrence
|
||||||
inputIndex int
|
inputIndex int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,44 +122,44 @@ func normalizeList(input dnd.ItemOccurrenceList, index source.DocumentIndex, ord
|
|||||||
|
|
||||||
records := make([]normalizedRecord, len(input.Occurrences))
|
records := make([]normalizedRecord, len(input.Occurrences))
|
||||||
warnings := make([]contracts.Warning, 0)
|
warnings := make([]contracts.Warning, 0)
|
||||||
for index, inputEvent := range input.Occurrences {
|
for index, inputOccurrence := range input.Occurrences {
|
||||||
event, changedFields, found, refsChanged := normalizeEvent(inputEvent, order, registry)
|
occurrence, changedFields, found, refsChanged := normalizeOccurrence(inputOccurrence, order, registry)
|
||||||
records[index] = normalizedRecord{event: event, inputIndex: index}
|
records[index] = normalizedRecord{occurrence: occurrence, inputIndex: index}
|
||||||
if len(changedFields) != 0 {
|
if len(changedFields) != 0 {
|
||||||
warnings = append(warnings, contracts.Warning{
|
warnings = append(warnings, contracts.Warning{
|
||||||
Scope: eventScope(index),
|
Scope: occurrenceScope(index),
|
||||||
ReasonCode: ReasonCodeNameCanonicalized,
|
ReasonCode: ReasonCodeNameCanonicalized,
|
||||||
Message: fmt.Sprintf("input index %d: normalized display whitespace in %s", index,
|
Message: fmt.Sprintf("input index %d: normalized display whitespace in %s", index,
|
||||||
diagnostics.Aggregate("fields", changedFields)),
|
diagnostics.Aggregate("fields", changedFields)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if found && inputEvent.Name != event.Name {
|
if found && inputOccurrence.Name != occurrence.Name {
|
||||||
warnings = append(warnings, contracts.Warning{Scope: eventScope(index), ReasonCode: ReasonCodeNameCanonicalized,
|
warnings = append(warnings, contracts.Warning{Scope: occurrenceScope(index), ReasonCode: ReasonCodeNameCanonicalized,
|
||||||
Message: fmt.Sprintf("input index %d: item name canonicalized from %s to %s", index, diagnostics.Quote(inputEvent.Name), diagnostics.Quote(event.Name))})
|
Message: fmt.Sprintf("input index %d: item name canonicalized from %s to %s", index, diagnostics.Quote(inputOccurrence.Name), diagnostics.Quote(occurrence.Name))})
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
warnings = append(warnings, contracts.Warning{Scope: eventScope(index), ReasonCode: ReasonCodeUnknownItemID,
|
warnings = append(warnings, contracts.Warning{Scope: occurrenceScope(index), ReasonCode: ReasonCodeUnknownItemID,
|
||||||
Message: fmt.Sprintf("input index %d: item ID %s is not in the supplied registry", index, diagnostics.Quote(inputEvent.ItemID))})
|
Message: fmt.Sprintf("input index %d: item ID %s is not in the supplied registry", index, diagnostics.Quote(inputOccurrence.ItemID))})
|
||||||
}
|
}
|
||||||
if refsChanged {
|
if refsChanged {
|
||||||
warnings = append(warnings, contracts.Warning{
|
warnings = append(warnings, contracts.Warning{
|
||||||
Scope: eventScope(index),
|
Scope: occurrenceScope(index),
|
||||||
ReasonCode: ReasonCodeSourceRefsNormalized,
|
ReasonCode: ReasonCodeSourceRefsNormalized,
|
||||||
Message: fmt.Sprintf("input index %d: source references normalized (original count %d, final count %d)",
|
Message: fmt.Sprintf("input index %d: source references normalized (original count %d, final count %d)",
|
||||||
index, len(inputEvent.SourceRefs), len(event.SourceRefs)),
|
index, len(inputOccurrence.SourceRefs), len(occurrence.SourceRefs)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.SliceStable(records, func(left, right int) bool {
|
sort.SliceStable(records, func(left, right int) bool {
|
||||||
return itemeventmodel.Less(order, records[left].event, records[right].event)
|
return itemoccurrencemodel.Less(order, records[left].occurrence, records[right].occurrence)
|
||||||
})
|
})
|
||||||
for position, record := range records {
|
for position, record := range records {
|
||||||
if position == record.inputIndex {
|
if position == record.inputIndex {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
warnings = append(warnings, contracts.Warning{
|
warnings = append(warnings, contracts.Warning{
|
||||||
Scope: eventScope(record.inputIndex),
|
Scope: occurrenceScope(record.inputIndex),
|
||||||
ReasonCode: ReasonCodeOccurrencesReordered,
|
ReasonCode: ReasonCodeOccurrencesReordered,
|
||||||
Message: fmt.Sprintf("input index %d moved to normalized position %d", record.inputIndex, position),
|
Message: fmt.Sprintf("input index %d moved to normalized position %d", record.inputIndex, position),
|
||||||
})
|
})
|
||||||
@@ -170,8 +170,8 @@ func normalizeList(input dnd.ItemOccurrenceList, index source.DocumentIndex, ord
|
|||||||
return dnd.ItemOccurrenceList{Occurrences: output}, diagnostics.LimitWarnings(warnings, "item_occurrences", ReasonCodeWarningsOmitted)
|
return dnd.ItemOccurrenceList{Occurrences: output}, diagnostics.LimitWarnings(warnings, "item_occurrences", ReasonCodeWarningsOmitted)
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeEvent(input dnd.ItemOccurrence, order shared.SourceRefOrder, registry *itemregistry.Registry) (dnd.ItemOccurrence, []string, bool, bool) {
|
func normalizeOccurrence(input dnd.ItemOccurrence, order shared.SourceRefOrder, registry *itemregistry.Registry) (dnd.ItemOccurrence, []string, bool, bool) {
|
||||||
output := cloneEvent(input)
|
output := cloneOccurrence(input)
|
||||||
canonical, found := registry.LookupID(input.ItemID)
|
canonical, found := registry.LookupID(input.ItemID)
|
||||||
if found {
|
if found {
|
||||||
output.Name = canonical.Name
|
output.Name = canonical.Name
|
||||||
@@ -184,17 +184,17 @@ func normalizeEvent(input dnd.ItemOccurrence, order shared.SourceRefOrder, regis
|
|||||||
{name: "from", value: &output.From},
|
{name: "from", value: &output.From},
|
||||||
{name: "to", value: &output.To},
|
{name: "to", value: &output.To},
|
||||||
} {
|
} {
|
||||||
trimmed := itemeventmodel.DisplayValue(*field.value)
|
trimmed := itemoccurrencemodel.DisplayValue(*field.value)
|
||||||
if *field.value != trimmed {
|
if *field.value != trimmed {
|
||||||
*field.value = trimmed
|
*field.value = trimmed
|
||||||
changedFields = append(changedFields, field.name)
|
changedFields = append(changedFields, field.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output.SourceRefs = order.Canonicalize(input.SourceRefs)
|
output.SourceRefs = order.Canonicalize(input.SourceRefs)
|
||||||
return output, changedFields, found, !itemeventmodel.SourceRefsEqual(input.SourceRefs, output.SourceRefs)
|
return output, changedFields, found, !itemoccurrencemodel.SourceRefsEqual(input.SourceRefs, output.SourceRefs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cloneEvent(input dnd.ItemOccurrence) dnd.ItemOccurrence {
|
func cloneOccurrence(input dnd.ItemOccurrence) dnd.ItemOccurrence {
|
||||||
output := input
|
output := input
|
||||||
if input.Quantity != nil {
|
if input.Quantity != nil {
|
||||||
quantity := *input.Quantity
|
quantity := *input.Quantity
|
||||||
@@ -220,11 +220,11 @@ func collapseDuplicates(records []normalizedRecord, index source.DocumentIndex,
|
|||||||
groups := make([]duplicateGroup, 0)
|
groups := make([]duplicateGroup, 0)
|
||||||
groupByKey := make(map[string]int)
|
groupByKey := make(map[string]int)
|
||||||
for recordIndex, record := range records {
|
for recordIndex, record := range records {
|
||||||
if !itemeventmodel.ValidSourceRefs(index, record.event.SourceRefs) {
|
if !itemoccurrencemodel.ValidSourceRefs(index, record.occurrence.SourceRefs) {
|
||||||
keep[recordIndex] = true
|
keep[recordIndex] = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
key := itemeventmodel.ExactIdentity(order, record.event)
|
key := itemoccurrencemodel.ExactIdentity(order, record.occurrence)
|
||||||
groupIndex, exists := groupByKey[key]
|
groupIndex, exists := groupByKey[key]
|
||||||
if !exists {
|
if !exists {
|
||||||
groupByKey[key] = len(groups)
|
groupByKey[key] = len(groups)
|
||||||
@@ -238,7 +238,7 @@ func collapseDuplicates(records []normalizedRecord, index source.DocumentIndex,
|
|||||||
output := make([]dnd.ItemOccurrence, 0, len(records))
|
output := make([]dnd.ItemOccurrence, 0, len(records))
|
||||||
for recordIndex, record := range records {
|
for recordIndex, record := range records {
|
||||||
if keep[recordIndex] {
|
if keep[recordIndex] {
|
||||||
output = append(output, cloneEvent(record.event))
|
output = append(output, cloneOccurrence(record.occurrence))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
warnings := make([]contracts.Warning, 0)
|
warnings := make([]contracts.Warning, 0)
|
||||||
@@ -256,14 +256,14 @@ func duplicateWarning(retainedIndex int, removed []int) contracts.Warning {
|
|||||||
issues[index] = fmt.Sprintf("removed input index %d", removedIndex)
|
issues[index] = fmt.Sprintf("removed input index %d", removedIndex)
|
||||||
}
|
}
|
||||||
return contracts.Warning{
|
return contracts.Warning{
|
||||||
Scope: eventScope(retainedIndex),
|
Scope: occurrenceScope(retainedIndex),
|
||||||
ReasonCode: ReasonCodeDuplicateCollapsed,
|
ReasonCode: ReasonCodeDuplicateCollapsed,
|
||||||
Message: diagnostics.Aggregate(
|
Message: diagnostics.Aggregate(
|
||||||
fmt.Sprintf("duplicate item occurrence collapsed; retained input index %d", retainedIndex), issues),
|
fmt.Sprintf("duplicate item occurrence collapsed; retained input index %d", retainedIndex), issues),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func eventScope(index int) string { return fmt.Sprintf("occurrences[%d]", index) }
|
func occurrenceScope(index int) string { return fmt.Sprintf("occurrences[%d]", index) }
|
||||||
|
|
||||||
func ModuleSpec() pipeline.ModuleSpec {
|
func ModuleSpec() pipeline.ModuleSpec {
|
||||||
return pipeline.ModuleSpec{
|
return pipeline.ModuleSpec{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package itemevents
|
package itemoccurrences
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
||||||
combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
||||||
enemyeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
enemyeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
||||||
itemeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemevents"
|
itemoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemoccurrences"
|
||||||
itemregistryextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemregistry"
|
itemregistryextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemregistry"
|
||||||
locationoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationoccurrences"
|
locationoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationoccurrences"
|
||||||
locationextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationregistry"
|
locationextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationregistry"
|
||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
spellextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/spells"
|
spellextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/spells"
|
||||||
combatnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/combatturns"
|
combatnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/combatturns"
|
||||||
enemyeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/enemyevents"
|
enemyeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/enemyevents"
|
||||||
itemeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemevents"
|
itemoccurrencenormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemoccurrences"
|
||||||
itemregistrynormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemregistry"
|
itemregistrynormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemregistry"
|
||||||
locationoccurrencenormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/locationoccurrences"
|
locationoccurrencenormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/locationoccurrences"
|
||||||
locationnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/locationregistry"
|
locationnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/locationregistry"
|
||||||
@@ -31,11 +31,11 @@ import (
|
|||||||
enemyeventshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/enemyevents/shape"
|
enemyeventshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/enemyevents/shape"
|
||||||
enemyeventrefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/enemyevents/source_refs"
|
enemyeventrefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/enemyevents/source_refs"
|
||||||
enemyeventrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/enemyevents/source_relatedness"
|
enemyeventrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/enemyevents/source_relatedness"
|
||||||
itemeventinvariants "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/invariants"
|
itemoccurrenceinvariants "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/invariants"
|
||||||
itemeventregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/registry"
|
itemoccurrenceregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/registry"
|
||||||
itemeventshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/shape"
|
itemoccurrenceshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/shape"
|
||||||
itemeventrefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/source_refs"
|
itemoccurrencerefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/source_refs"
|
||||||
itemeventrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/source_relatedness"
|
itemoccurrencerelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/source_relatedness"
|
||||||
itemidentity "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemregistry/identity"
|
itemidentity "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemregistry/identity"
|
||||||
itemshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemregistry/shape"
|
itemshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemregistry/shape"
|
||||||
itemrefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemregistry/source_refs"
|
itemrefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemregistry/source_refs"
|
||||||
@@ -185,29 +185,29 @@ func registerDefaultChains(registry *pipeline.ValidatorChainRegistry) error {
|
|||||||
{name: "item occurrences validator chain", register: func() error {
|
{name: "item occurrences validator chain", register: func() error {
|
||||||
return registry.Register(pipeline.ValidatorChainMapping{
|
return registry.Register(pipeline.ValidatorChainMapping{
|
||||||
Stage: pipeline.StageExtract,
|
Stage: pipeline.StageExtract,
|
||||||
Module: itemeventextract.Key,
|
Module: itemoccurrenceextract.Key,
|
||||||
Validators: []pipeline.ModuleBinding{
|
Validators: []pipeline.ModuleBinding{
|
||||||
pipeline.Binding(validjson.Key),
|
pipeline.Binding(validjson.Key),
|
||||||
pipeline.Binding(itemeventshape.Key),
|
pipeline.Binding(itemoccurrenceshape.Key),
|
||||||
pipeline.Binding(itemeventregistry.Key),
|
pipeline.Binding(itemoccurrenceregistry.Key),
|
||||||
pipeline.Binding(itemeventrefs.Key),
|
pipeline.Binding(itemoccurrencerefs.Key),
|
||||||
pipeline.Binding(validjsonschema.Key),
|
pipeline.Binding(validjsonschema.Key),
|
||||||
pipeline.Binding(itemeventrelatedness.Key),
|
pipeline.Binding(itemoccurrencerelatedness.Key),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}},
|
}},
|
||||||
{name: "item occurrences normalize validator chain", register: func() error {
|
{name: "item occurrences normalize validator chain", register: func() error {
|
||||||
return registry.Register(pipeline.ValidatorChainMapping{
|
return registry.Register(pipeline.ValidatorChainMapping{
|
||||||
Stage: pipeline.StageNormalize,
|
Stage: pipeline.StageNormalize,
|
||||||
Module: itemeventnormalize.Key,
|
Module: itemoccurrencenormalize.Key,
|
||||||
Validators: []pipeline.ModuleBinding{
|
Validators: []pipeline.ModuleBinding{
|
||||||
pipeline.Binding(validjson.Key),
|
pipeline.Binding(validjson.Key),
|
||||||
pipeline.Binding(itemeventshape.Key),
|
pipeline.Binding(itemoccurrenceshape.Key),
|
||||||
pipeline.Binding(itemeventregistry.Key),
|
pipeline.Binding(itemoccurrenceregistry.Key),
|
||||||
pipeline.Binding(itemeventinvariants.Key),
|
pipeline.Binding(itemoccurrenceinvariants.Key),
|
||||||
pipeline.Binding(itemeventrefs.Key),
|
pipeline.Binding(itemoccurrencerefs.Key),
|
||||||
pipeline.Binding(validjsonschema.Key),
|
pipeline.Binding(validjsonschema.Key),
|
||||||
pipeline.Binding(itemeventrelatedness.Key),
|
pipeline.Binding(itemoccurrencerelatedness.Key),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}},
|
}},
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/chunk/scenes"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/chunk/scenes"
|
||||||
combatcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/combatturns"
|
combatcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/combatturns"
|
||||||
enemyeventcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/enemyevents"
|
enemyeventcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/enemyevents"
|
||||||
itemeventcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/itemevents"
|
itemoccurrencecodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/itemoccurrences"
|
||||||
itemregistrycodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/itemregistry"
|
itemregistrycodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/itemregistry"
|
||||||
locationoccurrencecodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/locationoccurrences"
|
locationoccurrencecodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/locationoccurrences"
|
||||||
locationcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/locationregistry"
|
locationcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/locationregistry"
|
||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
spellcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/spells"
|
spellcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/spells"
|
||||||
combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
||||||
enemyeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
enemyeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
||||||
itemeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemevents"
|
itemoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemoccurrences"
|
||||||
itemregistryextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemregistry"
|
itemregistryextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemregistry"
|
||||||
locationoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationoccurrences"
|
locationoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationoccurrences"
|
||||||
locationextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationregistry"
|
locationextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationregistry"
|
||||||
@@ -27,7 +27,7 @@ import (
|
|||||||
spellextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/spells"
|
spellextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/spells"
|
||||||
combatnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/combatturns"
|
combatnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/combatturns"
|
||||||
enemyeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/enemyevents"
|
enemyeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/enemyevents"
|
||||||
itemeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemevents"
|
itemoccurrencenormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemoccurrences"
|
||||||
itemregistrynormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemregistry"
|
itemregistrynormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemregistry"
|
||||||
locationoccurrencenormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/locationoccurrences"
|
locationoccurrencenormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/locationoccurrences"
|
||||||
locationnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/locationregistry"
|
locationnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/locationregistry"
|
||||||
@@ -47,7 +47,9 @@ func registerModules(registries pipeline.Registries) error {
|
|||||||
{name: "npc registry codec", register: func() error { return pipeline.RegisterArtifactCodec(registries.ArtifactCodecs, npccodec.New()) }},
|
{name: "npc registry codec", register: func() error { return pipeline.RegisterArtifactCodec(registries.ArtifactCodecs, npccodec.New()) }},
|
||||||
{name: "combat turns codec", register: func() error { return pipeline.RegisterArtifactCodec(registries.ArtifactCodecs, combatcodec.New()) }},
|
{name: "combat turns codec", register: func() error { return pipeline.RegisterArtifactCodec(registries.ArtifactCodecs, combatcodec.New()) }},
|
||||||
{name: "enemy events codec", register: func() error { return pipeline.RegisterArtifactCodec(registries.ArtifactCodecs, enemyeventcodec.New()) }},
|
{name: "enemy events codec", register: func() error { return pipeline.RegisterArtifactCodec(registries.ArtifactCodecs, enemyeventcodec.New()) }},
|
||||||
{name: "item occurrences codec", register: func() error { return pipeline.RegisterArtifactCodec(registries.ArtifactCodecs, itemeventcodec.New()) }},
|
{name: "item occurrences codec", register: func() error {
|
||||||
|
return pipeline.RegisterArtifactCodec(registries.ArtifactCodecs, itemoccurrencecodec.New())
|
||||||
|
}},
|
||||||
{name: "item registry codec", register: func() error {
|
{name: "item registry codec", register: func() error {
|
||||||
return pipeline.RegisterArtifactCodec(registries.ArtifactCodecs, itemregistrycodec.New())
|
return pipeline.RegisterArtifactCodec(registries.ArtifactCodecs, itemregistrycodec.New())
|
||||||
}},
|
}},
|
||||||
@@ -64,7 +66,7 @@ func registerModules(registries pipeline.Registries) error {
|
|||||||
{name: "npc registry extractor", register: func() error { return npcextract.Register(registries.Extractors) }},
|
{name: "npc registry extractor", register: func() error { return npcextract.Register(registries.Extractors) }},
|
||||||
{name: "combat turns extractor", register: func() error { return combatextract.Register(registries.Extractors) }},
|
{name: "combat turns extractor", register: func() error { return combatextract.Register(registries.Extractors) }},
|
||||||
{name: "enemy events extractor", register: func() error { return enemyeventextract.Register(registries.Extractors) }},
|
{name: "enemy events extractor", register: func() error { return enemyeventextract.Register(registries.Extractors) }},
|
||||||
{name: "item occurrences extractor", register: func() error { return itemeventextract.Register(registries.Extractors) }},
|
{name: "item occurrences extractor", register: func() error { return itemoccurrenceextract.Register(registries.Extractors) }},
|
||||||
{name: "item registry extractor", register: func() error { return itemregistryextract.Register(registries.Extractors) }},
|
{name: "item registry extractor", register: func() error { return itemregistryextract.Register(registries.Extractors) }},
|
||||||
{name: "npc occurrences extractor", register: func() error { return occurrenceextract.Register(registries.Extractors) }},
|
{name: "npc occurrences extractor", register: func() error { return occurrenceextract.Register(registries.Extractors) }},
|
||||||
{name: "scene descriptions extractor", register: func() error { return scenedescriptionextract.Register(registries.Extractors) }},
|
{name: "scene descriptions extractor", register: func() error { return scenedescriptionextract.Register(registries.Extractors) }},
|
||||||
@@ -104,7 +106,7 @@ func registerModules(registries pipeline.Registries) error {
|
|||||||
{name: "npc registry normalizer", register: func() error { return npcnormalize.Register(registries.Normalizers) }},
|
{name: "npc registry normalizer", register: func() error { return npcnormalize.Register(registries.Normalizers) }},
|
||||||
{name: "combat turns normalizer", register: func() error { return combatnormalize.Register(registries.Normalizers) }},
|
{name: "combat turns normalizer", register: func() error { return combatnormalize.Register(registries.Normalizers) }},
|
||||||
{name: "enemy events normalizer", register: func() error { return enemyeventnormalize.Register(registries.Normalizers) }},
|
{name: "enemy events normalizer", register: func() error { return enemyeventnormalize.Register(registries.Normalizers) }},
|
||||||
{name: "item occurrences normalizer", register: func() error { return itemeventnormalize.Register(registries.Normalizers) }},
|
{name: "item occurrences normalizer", register: func() error { return itemoccurrencenormalize.Register(registries.Normalizers) }},
|
||||||
{name: "item registry normalizer", register: func() error { return itemregistrynormalize.Register(registries.Normalizers) }},
|
{name: "item registry normalizer", register: func() error { return itemregistrynormalize.Register(registries.Normalizers) }},
|
||||||
{name: "npc occurrences normalizer", register: func() error { return occurrencenormalize.Register(registries.Normalizers) }},
|
{name: "npc occurrences normalizer", register: func() error { return occurrencenormalize.Register(registries.Normalizers) }},
|
||||||
{name: "scene descriptions normalizer", register: func() error { return scenedescriptionnormalize.Register(registries.Normalizers) }},
|
{name: "scene descriptions normalizer", register: func() error { return scenedescriptionnormalize.Register(registries.Normalizers) }},
|
||||||
@@ -152,7 +154,7 @@ func registerPromptAssets(assets *llm.AssetRegistry) error {
|
|||||||
{name: "npc normalization prompt assets", register: func() error { return npcnormalize.RegisterPromptAssets(assets) }},
|
{name: "npc normalization prompt assets", register: func() error { return npcnormalize.RegisterPromptAssets(assets) }},
|
||||||
{name: "combat turns prompt assets", register: func() error { return combatextract.RegisterPromptAssets(assets) }},
|
{name: "combat turns prompt assets", register: func() error { return combatextract.RegisterPromptAssets(assets) }},
|
||||||
{name: "enemy events prompt assets", register: func() error { return enemyeventextract.RegisterPromptAssets(assets) }},
|
{name: "enemy events prompt assets", register: func() error { return enemyeventextract.RegisterPromptAssets(assets) }},
|
||||||
{name: "item occurrences prompt assets", register: func() error { return itemeventextract.RegisterPromptAssets(assets) }},
|
{name: "item occurrences prompt assets", register: func() error { return itemoccurrenceextract.RegisterPromptAssets(assets) }},
|
||||||
{name: "item registry prompt assets", register: func() error { return itemregistryextract.RegisterPromptAssets(assets) }},
|
{name: "item registry prompt assets", register: func() error { return itemregistryextract.RegisterPromptAssets(assets) }},
|
||||||
{name: "item registry normalization prompt assets", register: func() error { return itemregistrynormalize.RegisterPromptAssets(assets) }},
|
{name: "item registry normalization prompt assets", register: func() error { return itemregistrynormalize.RegisterPromptAssets(assets) }},
|
||||||
{name: "npc occurrences prompt assets", register: func() error { return occurrenceextract.RegisterPromptAssets(assets) }},
|
{name: "npc occurrences prompt assets", register: func() error { return occurrenceextract.RegisterPromptAssets(assets) }},
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
|
||||||
combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
||||||
enemyeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
enemyeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
||||||
itemeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemevents"
|
itemoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemoccurrences"
|
||||||
itemregistryextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemregistry"
|
itemregistryextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemregistry"
|
||||||
locationoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationoccurrences"
|
locationoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationoccurrences"
|
||||||
locationextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationregistry"
|
locationextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationregistry"
|
||||||
@@ -51,7 +51,7 @@ func TestExtractionPromptComposition(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{name: "npcs", promptID: npcextract.PromptID, promptVersion: npcextract.SchemaVersion, inputs: commonInputs, suffixGroups: [][]string{{evidenceSentinel}}},
|
{name: "npcs", promptID: npcextract.PromptID, promptVersion: npcextract.SchemaVersion, inputs: commonInputs, suffixGroups: [][]string{{evidenceSentinel}}},
|
||||||
{name: "locations", promptID: locationextract.PromptID, promptVersion: locationextract.SchemaVersion, inputs: commonInputs, suffixGroups: [][]string{{evidenceSentinel}}},
|
{name: "locations", promptID: locationextract.PromptID, promptVersion: locationextract.SchemaVersion, inputs: commonInputs, suffixGroups: [][]string{{evidenceSentinel}}},
|
||||||
{name: "item occurrences", promptID: itemeventextract.PromptID, promptVersion: itemeventextract.SchemaVersion, inputs: withPromptInputs(commonInputs, map[string]promptkit.ArtifactRef{
|
{name: "item occurrences", promptID: itemoccurrenceextract.PromptID, promptVersion: itemoccurrenceextract.SchemaVersion, inputs: withPromptInputs(commonInputs, map[string]promptkit.ArtifactRef{
|
||||||
"item_registry": promptkit.Inline(`{"items":[{"id":"item-registry-sentinel","name":"Torch"}]}`),
|
"item_registry": promptkit.Inline(`{"items":[{"id":"item-registry-sentinel","name":"Torch"}]}`),
|
||||||
}), suffixGroups: [][]string{{evidenceSentinel}, {"item-registry-sentinel"}}},
|
}), suffixGroups: [][]string{{evidenceSentinel}, {"item-registry-sentinel"}}},
|
||||||
{name: "item registry", promptID: itemregistryextract.PromptID, promptVersion: itemregistryextract.SchemaVersion, inputs: commonInputs, suffixGroups: [][]string{{evidenceSentinel}}},
|
{name: "item registry", promptID: itemregistryextract.PromptID, promptVersion: itemregistryextract.SchemaVersion, inputs: commonInputs, suffixGroups: [][]string{{evidenceSentinel}}},
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
||||||
combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
||||||
enemyeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
enemyeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
||||||
itemeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemevents"
|
itemoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemoccurrences"
|
||||||
itemregistryextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemregistry"
|
itemregistryextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemregistry"
|
||||||
locationoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationoccurrences"
|
locationoccurrenceextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationoccurrences"
|
||||||
locationextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationregistry"
|
locationextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/locationregistry"
|
||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/spells"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/spells"
|
||||||
combatnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/combatturns"
|
combatnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/combatturns"
|
||||||
enemyeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/enemyevents"
|
enemyeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/enemyevents"
|
||||||
itemeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemevents"
|
itemoccurrencenormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemoccurrences"
|
||||||
itemregistrynormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemregistry"
|
itemregistrynormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemregistry"
|
||||||
locationoccurrencenormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/locationoccurrences"
|
locationoccurrencenormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/locationoccurrences"
|
||||||
locationnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/locationregistry"
|
locationnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/locationregistry"
|
||||||
@@ -53,7 +53,7 @@ func TestRegisterAddsDNDFamily(t *testing.T) {
|
|||||||
"dnd.npc_registry/prompt.yaml",
|
"dnd.npc_registry/prompt.yaml",
|
||||||
"dnd.combat_turns/prompt.yaml",
|
"dnd.combat_turns/prompt.yaml",
|
||||||
"dnd.enemy_events/prompt.yaml",
|
"dnd.enemy_events/prompt.yaml",
|
||||||
"dnd.item_events/prompt.yaml",
|
"dnd.item_occurrences/prompt.yaml",
|
||||||
"dnd.item_registry/prompt.yaml",
|
"dnd.item_registry/prompt.yaml",
|
||||||
"dnd.item_registry.normalize/prompt.yaml",
|
"dnd.item_registry.normalize/prompt.yaml",
|
||||||
"dnd.npc_occurrences/prompt.yaml",
|
"dnd.npc_occurrences/prompt.yaml",
|
||||||
@@ -86,8 +86,8 @@ func TestRegisterAddsDNDFamily(t *testing.T) {
|
|||||||
t.Fatalf("entity reconciliation schema asset = %v, want registered shared schema", err)
|
t.Fatalf("entity reconciliation schema asset = %v, want registered shared schema", err)
|
||||||
}
|
}
|
||||||
assertContainsKeys(t, "chunkers", registries.Chunkers.RegisteredKeys(), []string{"dnd/scenes"})
|
assertContainsKeys(t, "chunkers", registries.Chunkers.RegisteredKeys(), []string{"dnd/scenes"})
|
||||||
assertContainsKeys(t, "extractors", registries.Extractors.RegisteredKeys(), []string{"dnd/spells", npcextract.Key, combatextract.Key, enemyeventextract.Key, itemeventextract.Key, itemregistryextract.Key, occurrenceextract.Key, scenedescriptionextract.Key, locationextract.Key, locationoccurrenceextract.Key})
|
assertContainsKeys(t, "extractors", registries.Extractors.RegisteredKeys(), []string{"dnd/spells", npcextract.Key, combatextract.Key, enemyeventextract.Key, itemoccurrenceextract.Key, itemregistryextract.Key, occurrenceextract.Key, scenedescriptionextract.Key, locationextract.Key, locationoccurrenceextract.Key})
|
||||||
assertContainsKeys(t, "normalizers", registries.Normalizers.RegisteredKeys(), []string{spellnormalize.Key, npcnormalize.Key, combatnormalize.Key, enemyeventnormalize.Key, itemeventnormalize.Key, itemregistrynormalize.Key, occurrencenormalize.Key, scenedescriptionnormalize.Key, locationnormalize.Key, locationoccurrencenormalize.Key, pipeline.DefaultNormalizeModule})
|
assertContainsKeys(t, "normalizers", registries.Normalizers.RegisteredKeys(), []string{spellnormalize.Key, npcnormalize.Key, combatnormalize.Key, enemyeventnormalize.Key, itemoccurrencenormalize.Key, itemregistrynormalize.Key, occurrencenormalize.Key, scenedescriptionnormalize.Key, locationnormalize.Key, locationoccurrencenormalize.Key, pipeline.DefaultNormalizeModule})
|
||||||
assertContainsArtifactKinds(t, registries.ArtifactCodecs.RegisteredKinds(), []contracts.ArtifactKind{dnd.SpellListKind, dnd.NPCRegistryKind, dnd.CombatTurnListKind, dnd.EnemyEventListKind, dnd.ItemOccurrenceListKind, dnd.ItemRegistryKind, dnd.NPCOccurrenceListKind, dnd.SceneDescriptionListKind, dnd.LocationRegistryKind, dnd.LocationOccurrenceListKind})
|
assertContainsArtifactKinds(t, registries.ArtifactCodecs.RegisteredKinds(), []contracts.ArtifactKind{dnd.SpellListKind, dnd.NPCRegistryKind, dnd.CombatTurnListKind, dnd.EnemyEventListKind, dnd.ItemOccurrenceListKind, dnd.ItemRegistryKind, dnd.NPCOccurrenceListKind, dnd.SceneDescriptionListKind, dnd.LocationRegistryKind, dnd.LocationOccurrenceListKind})
|
||||||
assertContainsArtifactKinds(t, registries.ArtifactEvidence.RegisteredKinds(), []contracts.ArtifactKind{dnd.SpellListKind, dnd.NPCRegistryKind, dnd.CombatTurnListKind, dnd.EnemyEventListKind, dnd.ItemOccurrenceListKind, dnd.ItemRegistryKind, dnd.NPCOccurrenceListKind, dnd.SceneDescriptionListKind, dnd.LocationRegistryKind, dnd.LocationOccurrenceListKind})
|
assertContainsArtifactKinds(t, registries.ArtifactEvidence.RegisteredKinds(), []contracts.ArtifactKind{dnd.SpellListKind, dnd.NPCRegistryKind, dnd.CombatTurnListKind, dnd.EnemyEventListKind, dnd.ItemOccurrenceListKind, dnd.ItemRegistryKind, dnd.NPCOccurrenceListKind, dnd.SceneDescriptionListKind, dnd.LocationRegistryKind, dnd.LocationOccurrenceListKind})
|
||||||
assertContainsArtifactKinds(t, registries.Mergers.RegisteredArtifactKinds(pipeline.DefaultMergeModule), []contracts.ArtifactKind{dnd.SpellListKind, dnd.NPCRegistryKind, dnd.CombatTurnListKind, dnd.EnemyEventListKind, dnd.ItemOccurrenceListKind, dnd.ItemRegistryKind, dnd.NPCOccurrenceListKind, dnd.SceneDescriptionListKind, dnd.LocationRegistryKind, dnd.LocationOccurrenceListKind})
|
assertContainsArtifactKinds(t, registries.Mergers.RegisteredArtifactKinds(pipeline.DefaultMergeModule), []contracts.ArtifactKind{dnd.SpellListKind, dnd.NPCRegistryKind, dnd.CombatTurnListKind, dnd.EnemyEventListKind, dnd.ItemOccurrenceListKind, dnd.ItemRegistryKind, dnd.NPCOccurrenceListKind, dnd.SceneDescriptionListKind, dnd.LocationRegistryKind, dnd.LocationOccurrenceListKind})
|
||||||
@@ -95,7 +95,7 @@ func TestRegisterAddsDNDFamily(t *testing.T) {
|
|||||||
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(npcnormalize.Key), []contracts.ArtifactKind{dnd.NPCRegistryKind})
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(npcnormalize.Key), []contracts.ArtifactKind{dnd.NPCRegistryKind})
|
||||||
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(combatnormalize.Key), []contracts.ArtifactKind{dnd.CombatTurnListKind})
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(combatnormalize.Key), []contracts.ArtifactKind{dnd.CombatTurnListKind})
|
||||||
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(enemyeventnormalize.Key), []contracts.ArtifactKind{dnd.EnemyEventListKind})
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(enemyeventnormalize.Key), []contracts.ArtifactKind{dnd.EnemyEventListKind})
|
||||||
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(itemeventnormalize.Key), []contracts.ArtifactKind{dnd.ItemOccurrenceListKind})
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(itemoccurrencenormalize.Key), []contracts.ArtifactKind{dnd.ItemOccurrenceListKind})
|
||||||
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(itemregistrynormalize.Key), []contracts.ArtifactKind{dnd.ItemRegistryKind})
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(itemregistrynormalize.Key), []contracts.ArtifactKind{dnd.ItemRegistryKind})
|
||||||
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(occurrencenormalize.Key), []contracts.ArtifactKind{dnd.NPCOccurrenceListKind})
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(occurrencenormalize.Key), []contracts.ArtifactKind{dnd.NPCOccurrenceListKind})
|
||||||
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(scenedescriptionnormalize.Key), []contracts.ArtifactKind{dnd.SceneDescriptionListKind})
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(scenedescriptionnormalize.Key), []contracts.ArtifactKind{dnd.SceneDescriptionListKind})
|
||||||
@@ -119,10 +119,10 @@ func TestRegisterAddsDNDFamily(t *testing.T) {
|
|||||||
"extract/dnd/enemy-events/source_refs",
|
"extract/dnd/enemy-events/source_refs",
|
||||||
"extract/dnd/enemy-events/source_relatedness",
|
"extract/dnd/enemy-events/source_relatedness",
|
||||||
"normalize/dnd/enemy-events/invariants",
|
"normalize/dnd/enemy-events/invariants",
|
||||||
"extract/dnd/item-events/shape",
|
"extract/dnd/item-occurrences/shape",
|
||||||
"extract/dnd/item-events/source_refs",
|
"extract/dnd/item-occurrences/source_refs",
|
||||||
"extract/dnd/item-events/source_relatedness",
|
"extract/dnd/item-occurrences/source_relatedness",
|
||||||
"normalize/dnd/item-events/invariants",
|
"normalize/dnd/item-occurrences/invariants",
|
||||||
"extract/dnd/item-registry/shape", "normalize/dnd/item-registry/identity", "extract/dnd/item-registry/source_refs", "extract/dnd/item-registry/source_relatedness",
|
"extract/dnd/item-registry/shape", "normalize/dnd/item-registry/identity", "extract/dnd/item-registry/source_refs", "extract/dnd/item-registry/source_relatedness",
|
||||||
"extract/dnd/npc-occurrences/shape",
|
"extract/dnd/npc-occurrences/shape",
|
||||||
"extract/dnd/npc-occurrences/registry",
|
"extract/dnd/npc-occurrences/registry",
|
||||||
@@ -232,25 +232,25 @@ func TestRegisterAddsDNDFamily(t *testing.T) {
|
|||||||
}
|
}
|
||||||
itemOccurrenceExtractChain := []pipeline.ModuleBinding{
|
itemOccurrenceExtractChain := []pipeline.ModuleBinding{
|
||||||
pipeline.Binding("generic/valid_json"),
|
pipeline.Binding("generic/valid_json"),
|
||||||
pipeline.Binding("extract/dnd/item-events/shape"),
|
pipeline.Binding("extract/dnd/item-occurrences/shape"),
|
||||||
pipeline.Binding("extract/dnd/item-events/registry"),
|
pipeline.Binding("extract/dnd/item-occurrences/registry"),
|
||||||
pipeline.Binding("extract/dnd/item-events/source_refs"),
|
pipeline.Binding("extract/dnd/item-occurrences/source_refs"),
|
||||||
pipeline.Binding("generic/valid_json_schema"),
|
pipeline.Binding("generic/valid_json_schema"),
|
||||||
pipeline.Binding("extract/dnd/item-events/source_relatedness"),
|
pipeline.Binding("extract/dnd/item-occurrences/source_relatedness"),
|
||||||
}
|
}
|
||||||
itemOccurrenceNormalizeChain := []pipeline.ModuleBinding{
|
itemOccurrenceNormalizeChain := []pipeline.ModuleBinding{
|
||||||
pipeline.Binding("generic/valid_json"),
|
pipeline.Binding("generic/valid_json"),
|
||||||
pipeline.Binding("extract/dnd/item-events/shape"),
|
pipeline.Binding("extract/dnd/item-occurrences/shape"),
|
||||||
pipeline.Binding("extract/dnd/item-events/registry"),
|
pipeline.Binding("extract/dnd/item-occurrences/registry"),
|
||||||
pipeline.Binding("normalize/dnd/item-events/invariants"),
|
pipeline.Binding("normalize/dnd/item-occurrences/invariants"),
|
||||||
pipeline.Binding("extract/dnd/item-events/source_refs"),
|
pipeline.Binding("extract/dnd/item-occurrences/source_refs"),
|
||||||
pipeline.Binding("generic/valid_json_schema"),
|
pipeline.Binding("generic/valid_json_schema"),
|
||||||
pipeline.Binding("extract/dnd/item-events/source_relatedness"),
|
pipeline.Binding("extract/dnd/item-occurrences/source_relatedness"),
|
||||||
}
|
}
|
||||||
if got := registries.ValidatorChains.Validators(pipeline.StageExtract, itemeventextract.Key); !reflect.DeepEqual(got, itemOccurrenceExtractChain) {
|
if got := registries.ValidatorChains.Validators(pipeline.StageExtract, itemoccurrenceextract.Key); !reflect.DeepEqual(got, itemOccurrenceExtractChain) {
|
||||||
t.Fatalf("item occurrence extract validator chain = %#v, want %#v", got, itemOccurrenceExtractChain)
|
t.Fatalf("item occurrence extract validator chain = %#v, want %#v", got, itemOccurrenceExtractChain)
|
||||||
}
|
}
|
||||||
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, itemeventnormalize.Key); !reflect.DeepEqual(got, itemOccurrenceNormalizeChain) {
|
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, itemoccurrencenormalize.Key); !reflect.DeepEqual(got, itemOccurrenceNormalizeChain) {
|
||||||
t.Fatalf("item occurrence normalize validator chain = %#v, want %#v", got, itemOccurrenceNormalizeChain)
|
t.Fatalf("item occurrence normalize validator chain = %#v, want %#v", got, itemOccurrenceNormalizeChain)
|
||||||
}
|
}
|
||||||
itemRegistryExtractChain := []pipeline.ModuleBinding{
|
itemRegistryExtractChain := []pipeline.ModuleBinding{
|
||||||
@@ -370,8 +370,8 @@ func TestRegisterAddsDNDFamily(t *testing.T) {
|
|||||||
if !npcRegistrySlot.Required || !enemyNormalizeRegistrySlot.Required || npcRegistrySlot.MaxBytes != enemyNormalizeRegistrySlot.MaxBytes || !reflect.DeepEqual(npcRegistrySlot.AcceptedMediaTypes, enemyNormalizeRegistrySlot.AcceptedMediaTypes) || !reflect.DeepEqual(npcRegistrySlot.AcceptedArtifactKinds, enemyNormalizeRegistrySlot.AcceptedArtifactKinds) {
|
if !npcRegistrySlot.Required || !enemyNormalizeRegistrySlot.Required || npcRegistrySlot.MaxBytes != enemyNormalizeRegistrySlot.MaxBytes || !reflect.DeepEqual(npcRegistrySlot.AcceptedMediaTypes, enemyNormalizeRegistrySlot.AcceptedMediaTypes) || !reflect.DeepEqual(npcRegistrySlot.AcceptedArtifactKinds, enemyNormalizeRegistrySlot.AcceptedArtifactKinds) {
|
||||||
t.Fatalf("enemy-event NPC registry slots disagree: %#v / %#v", enemyEventExtractSpec.ReferenceSlots, enemyEventNormalizeSpec.ReferenceSlots)
|
t.Fatalf("enemy-event NPC registry slots disagree: %#v / %#v", enemyEventExtractSpec.ReferenceSlots, enemyEventNormalizeSpec.ReferenceSlots)
|
||||||
}
|
}
|
||||||
itemOccurrenceExtractSpec, itemOccurrenceExtractOK := registries.Extractors.Spec(itemeventextract.Key)
|
itemOccurrenceExtractSpec, itemOccurrenceExtractOK := registries.Extractors.Spec(itemoccurrenceextract.Key)
|
||||||
itemOccurrenceNormalizeSpec, itemOccurrenceNormalizeOK := registries.Normalizers.Spec(itemeventnormalize.Key)
|
itemOccurrenceNormalizeSpec, itemOccurrenceNormalizeOK := registries.Normalizers.Spec(itemoccurrencenormalize.Key)
|
||||||
if !itemOccurrenceExtractOK || itemOccurrenceExtractSpec.ArtifactKind != dnd.ItemOccurrenceListKind || !itemOccurrenceNormalizeOK || itemOccurrenceNormalizeSpec.ArtifactKind != dnd.ItemOccurrenceListKind || itemOccurrenceNormalizeSpec.Stage != pipeline.StageNormalize {
|
if !itemOccurrenceExtractOK || itemOccurrenceExtractSpec.ArtifactKind != dnd.ItemOccurrenceListKind || !itemOccurrenceNormalizeOK || itemOccurrenceNormalizeSpec.ArtifactKind != dnd.ItemOccurrenceListKind || itemOccurrenceNormalizeSpec.Stage != pipeline.StageNormalize {
|
||||||
t.Fatalf("item occurrence specs = %#v / %#v, present = %t / %t", itemOccurrenceExtractSpec, itemOccurrenceNormalizeSpec, itemOccurrenceExtractOK, itemOccurrenceNormalizeOK)
|
t.Fatalf("item occurrence specs = %#v / %#v, present = %t / %t", itemOccurrenceExtractSpec, itemOccurrenceNormalizeSpec, itemOccurrenceExtractOK, itemOccurrenceNormalizeOK)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import (
|
|||||||
enemyeventshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/enemyevents/shape"
|
enemyeventshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/enemyevents/shape"
|
||||||
enemyeventrefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/enemyevents/source_refs"
|
enemyeventrefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/enemyevents/source_refs"
|
||||||
enemyeventrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/enemyevents/source_relatedness"
|
enemyeventrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/enemyevents/source_relatedness"
|
||||||
itemeventinvariants "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/invariants"
|
itemoccurrenceinvariants "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/invariants"
|
||||||
itemeventregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/registry"
|
itemoccurrenceregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/registry"
|
||||||
itemeventshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/shape"
|
itemoccurrenceshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/shape"
|
||||||
itemeventrefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/source_refs"
|
itemoccurrencerefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/source_refs"
|
||||||
itemeventrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/source_relatedness"
|
itemoccurrencerelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/source_relatedness"
|
||||||
itemidentity "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemregistry/identity"
|
itemidentity "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemregistry/identity"
|
||||||
itemshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemregistry/shape"
|
itemshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemregistry/shape"
|
||||||
itemrefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemregistry/source_refs"
|
itemrefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemregistry/source_refs"
|
||||||
@@ -70,11 +70,11 @@ func registerValidators(registries pipeline.Registries) error {
|
|||||||
{name: "enemy event source references validator", register: func() error { return enemyeventrefs.Register(registries.Validators) }},
|
{name: "enemy event source references validator", register: func() error { return enemyeventrefs.Register(registries.Validators) }},
|
||||||
{name: "enemy event source relatedness validator", register: func() error { return enemyeventrelatedness.Register(registries.Validators) }},
|
{name: "enemy event source relatedness validator", register: func() error { return enemyeventrelatedness.Register(registries.Validators) }},
|
||||||
{name: "enemy event normalized invariants validator", register: func() error { return enemyeventinvariants.Register(registries.Validators) }},
|
{name: "enemy event normalized invariants validator", register: func() error { return enemyeventinvariants.Register(registries.Validators) }},
|
||||||
{name: "item occurrence shape validator", register: func() error { return itemeventshape.Register(registries.Validators) }},
|
{name: "item occurrence shape validator", register: func() error { return itemoccurrenceshape.Register(registries.Validators) }},
|
||||||
{name: "item occurrence registry validator", register: func() error { return itemeventregistry.Register(registries.Validators) }},
|
{name: "item occurrence registry validator", register: func() error { return itemoccurrenceregistry.Register(registries.Validators) }},
|
||||||
{name: "item occurrence source references validator", register: func() error { return itemeventrefs.Register(registries.Validators) }},
|
{name: "item occurrence source references validator", register: func() error { return itemoccurrencerefs.Register(registries.Validators) }},
|
||||||
{name: "item occurrence source relatedness validator", register: func() error { return itemeventrelatedness.Register(registries.Validators) }},
|
{name: "item occurrence source relatedness validator", register: func() error { return itemoccurrencerelatedness.Register(registries.Validators) }},
|
||||||
{name: "item occurrence normalized invariants validator", register: func() error { return itemeventinvariants.Register(registries.Validators) }},
|
{name: "item occurrence normalized invariants validator", register: func() error { return itemoccurrenceinvariants.Register(registries.Validators) }},
|
||||||
{name: "item shape validator", register: func() error { return itemshape.Register(registries.Validators) }},
|
{name: "item shape validator", register: func() error { return itemshape.Register(registries.Validators) }},
|
||||||
{name: "item identity validator", register: func() error { return itemidentity.Register(registries.Validators) }},
|
{name: "item identity validator", register: func() error { return itemidentity.Register(registries.Validators) }},
|
||||||
{name: "item source references validator", register: func() error { return itemrefs.Register(registries.Validators) }},
|
{name: "item source references validator", register: func() error { return itemrefs.Register(registries.Validators) }},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Package invariants validates normalized D&D item-event artifacts.
|
// Package invariants validates normalized D&D item-occurrence artifacts.
|
||||||
package invariants
|
package invariants
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -9,16 +9,16 @@ 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"
|
||||||
itemeventmodel "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/itemevents"
|
itemoccurrencemodel "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/itemoccurrences"
|
||||||
"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"
|
||||||
itemeventshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/shape"
|
itemoccurrenceshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/shape"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Key = "normalize/dnd/item-events/invariants"
|
Key = "normalize/dnd/item-occurrences/invariants"
|
||||||
ReasonCode = "invalid_normalized_item_event_invariants"
|
ReasonCode = "invalid_normalized_item_occurrence_invariants"
|
||||||
policy = "dnd.item_events.normalize_invariants.v1"
|
policy = "dnd.item_occurrences.normalize_invariants.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct{}
|
type Options struct{}
|
||||||
@@ -43,10 +43,10 @@ func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationReq
|
|||||||
return contracts.ValidationResult{Approved: true}, nil
|
return contracts.ValidationResult{Approved: true}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks only invariants introduced by item-event normalization.
|
// Validate checks only invariants introduced by item-occurrence normalization.
|
||||||
// Shape and source-reference failures remain owned by their earlier validators.
|
// Shape and source-reference failures remain owned by their earlier validators.
|
||||||
func Validate(doc *source.SourceDocument, value dnd.ItemOccurrenceList) error {
|
func Validate(doc *source.SourceDocument, value dnd.ItemOccurrenceList) error {
|
||||||
if itemeventshape.Validate(value) != nil {
|
if itemoccurrenceshape.Validate(value) != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
index := source.NewDocumentIndex(doc)
|
index := source.NewDocumentIndex(doc)
|
||||||
@@ -63,44 +63,44 @@ func Validate(doc *source.SourceDocument, value dnd.ItemOccurrenceList) error {
|
|||||||
func issuesFor(order shared.SourceRefOrder, value dnd.ItemOccurrenceList) []string {
|
func issuesFor(order shared.SourceRefOrder, value dnd.ItemOccurrenceList) []string {
|
||||||
issues := make([]string, 0)
|
issues := make([]string, 0)
|
||||||
seenIdentity := make(map[string]int)
|
seenIdentity := make(map[string]int)
|
||||||
for eventIndex, event := range value.Occurrences {
|
for occurrenceIndex, occurrence := range value.Occurrences {
|
||||||
prefix := fmt.Sprintf("occurrences[%d]", eventIndex)
|
prefix := fmt.Sprintf("occurrences[%d]", occurrenceIndex)
|
||||||
if event.Name != itemeventmodel.DisplayValue(event.Name) {
|
if occurrence.Name != itemoccurrencemodel.DisplayValue(occurrence.Name) {
|
||||||
issues = append(issues, prefix+".name is not display-normalized")
|
issues = append(issues, prefix+".name is not display-normalized")
|
||||||
}
|
}
|
||||||
if event.From != itemeventmodel.DisplayValue(event.From) {
|
if occurrence.From != itemoccurrencemodel.DisplayValue(occurrence.From) {
|
||||||
issues = append(issues, prefix+".from is not display-normalized")
|
issues = append(issues, prefix+".from is not display-normalized")
|
||||||
}
|
}
|
||||||
if event.To != itemeventmodel.DisplayValue(event.To) {
|
if occurrence.To != itemoccurrencemodel.DisplayValue(occurrence.To) {
|
||||||
issues = append(issues, prefix+".to is not display-normalized")
|
issues = append(issues, prefix+".to is not display-normalized")
|
||||||
}
|
}
|
||||||
for refIndex := 1; refIndex < len(event.SourceRefs); refIndex++ {
|
for refIndex := 1; refIndex < len(occurrence.SourceRefs); refIndex++ {
|
||||||
previous := event.SourceRefs[refIndex-1]
|
previous := occurrence.SourceRefs[refIndex-1]
|
||||||
current := event.SourceRefs[refIndex]
|
current := occurrence.SourceRefs[refIndex]
|
||||||
if order.Less(current, previous) {
|
if order.Less(current, previous) {
|
||||||
issues = append(issues, fmt.Sprintf("%s.source_refs are not in canonical order at index %d", prefix, refIndex))
|
issues = append(issues, fmt.Sprintf("%s.source_refs are not in canonical order at index %d", prefix, refIndex))
|
||||||
} else if current == previous {
|
} else if current == previous {
|
||||||
issues = append(issues, fmt.Sprintf("%s.source_refs[%d] duplicates the previous reference", prefix, refIndex))
|
issues = append(issues, fmt.Sprintf("%s.source_refs[%d] duplicates the previous reference", prefix, refIndex))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key := itemeventmodel.ExactIdentity(order, event)
|
key := itemoccurrencemodel.ExactIdentity(order, occurrence)
|
||||||
if previous, exists := seenIdentity[key]; exists {
|
if previous, exists := seenIdentity[key]; exists {
|
||||||
issues = append(issues, fmt.Sprintf("%s duplicates item occurrence %d under normalized identity", prefix, previous))
|
issues = append(issues, fmt.Sprintf("%s duplicates item occurrence %d under normalized identity", prefix, previous))
|
||||||
} else {
|
} else {
|
||||||
seenIdentity[key] = eventIndex
|
seenIdentity[key] = occurrenceIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for eventIndex := 1; eventIndex < len(value.Occurrences); eventIndex++ {
|
for occurrenceIndex := 1; occurrenceIndex < len(value.Occurrences); occurrenceIndex++ {
|
||||||
if itemeventmodel.Less(order, value.Occurrences[eventIndex], value.Occurrences[eventIndex-1]) {
|
if itemoccurrencemodel.Less(order, value.Occurrences[occurrenceIndex], value.Occurrences[occurrenceIndex-1]) {
|
||||||
issues = append(issues, fmt.Sprintf("occurrences[%d] is out of canonical order", eventIndex))
|
issues = append(issues, fmt.Sprintf("occurrences[%d] is out of canonical order", occurrenceIndex))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return issues
|
return issues
|
||||||
}
|
}
|
||||||
|
|
||||||
func sourceRefsValid(index source.DocumentIndex, value dnd.ItemOccurrenceList) bool {
|
func sourceRefsValid(index source.DocumentIndex, value dnd.ItemOccurrenceList) bool {
|
||||||
for _, event := range value.Occurrences {
|
for _, occurrence := range value.Occurrences {
|
||||||
if !itemeventmodel.ValidSourceRefs(index, event.SourceRefs) {
|
if !itemoccurrencemodel.ValidSourceRefs(index, occurrence.SourceRefs) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ func TestValidateRejectsOwnedNormalizedInvariants(t *testing.T) {
|
|||||||
{name: "list order", mutate: func(value *dnd.ItemOccurrenceList) {
|
{name: "list order", mutate: func(value *dnd.ItemOccurrenceList) {
|
||||||
value.Occurrences = []dnd.ItemOccurrence{value.Occurrences[1], value.Occurrences[0]}
|
value.Occurrences = []dnd.ItemOccurrence{value.Occurrences[1], value.Occurrences[0]}
|
||||||
}, want: "out of canonical order"},
|
}, want: "out of canonical order"},
|
||||||
{name: "duplicate event", mutate: func(value *dnd.ItemOccurrenceList) {
|
{name: "duplicate occurrence", mutate: func(value *dnd.ItemOccurrenceList) {
|
||||||
value.Occurrences = append(value.Occurrences, value.Occurrences[0])
|
value.Occurrences = append(value.Occurrences, value.Occurrences[0])
|
||||||
}, want: "duplicates item occurrence"},
|
}, want: "duplicates item occurrence"},
|
||||||
}
|
}
|
||||||
@@ -10,11 +10,11 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
||||||
itemregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/items/registry"
|
itemregistry "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/items/registry"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared/diagnostics"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared/diagnostics"
|
||||||
occurrenceshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/shape"
|
occurrenceshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/shape"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Key = "extract/dnd/item-events/registry"
|
Key = "extract/dnd/item-occurrences/registry"
|
||||||
ReasonCode = "invalid_item_occurrence_registry"
|
ReasonCode = "invalid_item_occurrence_registry"
|
||||||
policy = "dnd.item_occurrences.validator.registry.v1"
|
policy = "dnd.item_occurrences.validator.registry.v1"
|
||||||
)
|
)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Package shape validates required D&D item-event candidate fields.
|
// Package shape validates required D&D item-occurrence candidate fields.
|
||||||
package shape
|
package shape
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -9,14 +9,14 @@ 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"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/itemevents"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/itemoccurrences"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared/diagnostics"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared/diagnostics"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Key = "extract/dnd/item-events/shape"
|
Key = "extract/dnd/item-occurrences/shape"
|
||||||
ReasonCode = "invalid_item_event_shape"
|
ReasonCode = "invalid_item_occurrence_shape"
|
||||||
policy = "dnd.item_events.shape.v1"
|
policy = "dnd.item_occurrences.shape.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct{}
|
type Options struct{}
|
||||||
@@ -41,7 +41,7 @@ func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationReq
|
|||||||
return contracts.ValidationResult{Approved: true}, nil
|
return contracts.ValidationResult{Approved: true}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate returns one bounded error for every owned item-event shape issue.
|
// Validate returns one bounded error for every owned item-occurrence shape issue.
|
||||||
func Validate(value dnd.ItemOccurrenceList) error {
|
func Validate(value dnd.ItemOccurrenceList) error {
|
||||||
issues := issuesFor(value)
|
issues := issuesFor(value)
|
||||||
if len(issues) == 0 {
|
if len(issues) == 0 {
|
||||||
@@ -55,23 +55,23 @@ func issuesFor(value dnd.ItemOccurrenceList) []string {
|
|||||||
return []string{"occurrences must be present"}
|
return []string{"occurrences must be present"}
|
||||||
}
|
}
|
||||||
issues := make([]string, 0)
|
issues := make([]string, 0)
|
||||||
for index, event := range value.Occurrences {
|
for index, occurrence := range value.Occurrences {
|
||||||
prefix := fmt.Sprintf("occurrences[%d]", index)
|
prefix := fmt.Sprintf("occurrences[%d]", index)
|
||||||
if strings.TrimSpace(event.ItemID) == "" {
|
if strings.TrimSpace(occurrence.ItemID) == "" {
|
||||||
issues = append(issues, prefix+".item_id must not be empty: "+diagnostics.Quote(event.ItemID))
|
issues = append(issues, prefix+".item_id must not be empty: "+diagnostics.Quote(occurrence.ItemID))
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(event.Name) == "" {
|
if strings.TrimSpace(occurrence.Name) == "" {
|
||||||
issues = append(issues, prefix+".name must not be empty: "+diagnostics.Quote(event.Name))
|
issues = append(issues, prefix+".name must not be empty: "+diagnostics.Quote(occurrence.Name))
|
||||||
}
|
}
|
||||||
if !itemevents.SupportedKind(event.Kind) {
|
if !itemoccurrences.SupportedKind(occurrence.Kind) {
|
||||||
issues = append(issues, prefix+".kind is unsupported: "+diagnostics.Quote(string(event.Kind)))
|
issues = append(issues, prefix+".kind is unsupported: "+diagnostics.Quote(string(occurrence.Kind)))
|
||||||
} else if !itemevents.ValidHolderCombination(event.Kind, event.From, event.To) {
|
} else if !itemoccurrences.ValidHolderCombination(occurrence.Kind, occurrence.From, occurrence.To) {
|
||||||
issues = append(issues, prefix+".from and .to are incompatible with "+diagnostics.Quote(string(event.Kind)))
|
issues = append(issues, prefix+".from and .to are incompatible with "+diagnostics.Quote(string(occurrence.Kind)))
|
||||||
}
|
}
|
||||||
if event.Quantity != nil && *event.Quantity < 1 {
|
if occurrence.Quantity != nil && *occurrence.Quantity < 1 {
|
||||||
issues = append(issues, prefix+".quantity must be positive when present")
|
issues = append(issues, prefix+".quantity must be positive when present")
|
||||||
}
|
}
|
||||||
if len(event.SourceRefs) == 0 {
|
if len(occurrence.SourceRefs) == 0 {
|
||||||
issues = append(issues, prefix+".source_refs must contain at least one reference")
|
issues = append(issues, prefix+".source_refs must contain at least one reference")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestValidatorAcceptsEveryEventRuleAndNormalizableWhitespace(t *testing.T) {
|
func TestValidatorAcceptsEveryOccurrenceRuleAndNormalizableWhitespace(t *testing.T) {
|
||||||
quantity := 1
|
quantity := 1
|
||||||
value := dnd.ItemOccurrenceList{Occurrences: []dnd.ItemOccurrence{{ItemID: "item", Name: " Hidden Cache ", Kind: dnd.ItemOccurrenceKindDiscovered, From: " ", To: " ", SourceRefs: refs(1, 1)},
|
value := dnd.ItemOccurrenceList{Occurrences: []dnd.ItemOccurrence{{ItemID: "item", Name: " Hidden Cache ", Kind: dnd.ItemOccurrenceKindDiscovered, From: " ", To: " ", SourceRefs: refs(1, 1)},
|
||||||
{ItemID: "item", Name: "Gold Pieces", Kind: dnd.ItemOccurrenceKindAcquired, Quantity: &quantity, To: " party ", SourceRefs: refs(2, 2)},
|
{ItemID: "item", Name: "Gold Pieces", Kind: dnd.ItemOccurrenceKindAcquired, Quantity: &quantity, To: " party ", SourceRefs: refs(2, 2)},
|
||||||
@@ -40,7 +40,7 @@ func TestValidatorRejectsOwnedSemanticBoundaries(t *testing.T) {
|
|||||||
value dnd.ItemOccurrenceList
|
value dnd.ItemOccurrenceList
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{"missing events", dnd.ItemOccurrenceList{}, "occurrences must be present"},
|
{"missing occurrences", dnd.ItemOccurrenceList{}, "occurrences must be present"},
|
||||||
{"blank name", listWith(dnd.ItemOccurrence{ItemID: "item", Name: " \t", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: refs(1, 1)}), "name must not be empty"},
|
{"blank name", listWith(dnd.ItemOccurrence{ItemID: "item", Name: " \t", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: refs(1, 1)}), "name must not be empty"},
|
||||||
{"unsupported kind", listWith(dnd.ItemOccurrence{ItemID: "item", Name: "Ring", Kind: "unknown", SourceRefs: refs(1, 1)}), "kind is unsupported"},
|
{"unsupported kind", listWith(dnd.ItemOccurrence{ItemID: "item", Name: "Ring", Kind: "unknown", SourceRefs: refs(1, 1)}), "kind is unsupported"},
|
||||||
{"discovered holder", listWith(dnd.ItemOccurrence{ItemID: "item", Name: "Ring", Kind: dnd.ItemOccurrenceKindDiscovered, From: "Aria", SourceRefs: refs(1, 1)}), "incompatible"},
|
{"discovered holder", listWith(dnd.ItemOccurrence{ItemID: "item", Name: "Ring", Kind: dnd.ItemOccurrenceKindDiscovered, From: "Aria", SourceRefs: refs(1, 1)}), "incompatible"},
|
||||||
@@ -99,8 +99,8 @@ func validList() dnd.ItemOccurrenceList {
|
|||||||
return dnd.ItemOccurrenceList{Occurrences: []dnd.ItemOccurrence{{ItemID: "item", Name: "Ring", Kind: dnd.ItemOccurrenceKindAcquired, To: "party", SourceRefs: refs(1, 1)}}}
|
return dnd.ItemOccurrenceList{Occurrences: []dnd.ItemOccurrence{{ItemID: "item", Name: "Ring", Kind: dnd.ItemOccurrenceKindAcquired, To: "party", SourceRefs: refs(1, 1)}}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func listWith(event dnd.ItemOccurrence) dnd.ItemOccurrenceList {
|
func listWith(occurrence dnd.ItemOccurrence) dnd.ItemOccurrenceList {
|
||||||
return dnd.ItemOccurrenceList{Occurrences: []dnd.ItemOccurrence{event}}
|
return dnd.ItemOccurrenceList{Occurrences: []dnd.ItemOccurrence{occurrence}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func refs(start, end int) []source.SourceRef {
|
func refs(start, end int) []source.SourceRef {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Package sourcerefs validates D&D item-event transcript evidence.
|
// Package sourcerefs validates D&D item-occurrence transcript evidence.
|
||||||
package sourcerefs
|
package sourcerefs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -10,13 +10,13 @@ import (
|
|||||||
"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"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared/diagnostics"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared/diagnostics"
|
||||||
itemeventshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/shape"
|
itemoccurrenceshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/shape"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Key = "extract/dnd/item-events/source_refs"
|
Key = "extract/dnd/item-occurrences/source_refs"
|
||||||
ReasonCode = "invalid_item_event_source_references"
|
ReasonCode = "invalid_item_occurrence_source_references"
|
||||||
policy = "dnd.item_events.source_refs.v1"
|
policy = "dnd.item_occurrences.source_refs.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct{}
|
type Options struct{}
|
||||||
@@ -38,7 +38,7 @@ func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationReq
|
|||||||
if req.Stage == string(pipeline.StageExtract) && req.Chunk == nil {
|
if req.Stage == string(pipeline.StageExtract) && req.Chunk == nil {
|
||||||
return contracts.ValidationResult{}, fmt.Errorf("item occurrence source-reference validator requires the current extraction chunk")
|
return contracts.ValidationResult{}, fmt.Errorf("item occurrence source-reference validator requires the current extraction chunk")
|
||||||
}
|
}
|
||||||
if itemeventshape.Validate(req.Value) != nil {
|
if itemoccurrenceshape.Validate(req.Value) != nil {
|
||||||
return contracts.ValidationResult{Approved: true}, nil
|
return contracts.ValidationResult{Approved: true}, nil
|
||||||
}
|
}
|
||||||
index := source.NewDocumentIndex(req.Source)
|
index := source.NewDocumentIndex(req.Source)
|
||||||
@@ -47,14 +47,14 @@ func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationReq
|
|||||||
coverage = newChunkCoverage(req.Chunk)
|
coverage = newChunkCoverage(req.Chunk)
|
||||||
}
|
}
|
||||||
issues := make([]string, 0)
|
issues := make([]string, 0)
|
||||||
for eventIndex, event := range req.Value.Occurrences {
|
for occurrenceIndex, occurrence := range req.Value.Occurrences {
|
||||||
for refIndex, ref := range event.SourceRefs {
|
for refIndex, ref := range occurrence.SourceRefs {
|
||||||
if err := index.ValidateRef(ref); err != nil {
|
if err := index.ValidateRef(ref); err != nil {
|
||||||
issues = append(issues, fmt.Sprintf("occurrences[%d].source_refs[%d]: %s", eventIndex, refIndex, diagnostics.Truncate(err.Error())))
|
issues = append(issues, fmt.Sprintf("occurrences[%d].source_refs[%d]: %s", occurrenceIndex, refIndex, diagnostics.Truncate(err.Error())))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if coverage != nil && !coverage.contains(req.Source, ref) {
|
if coverage != nil && !coverage.contains(req.Source, ref) {
|
||||||
issues = append(issues, fmt.Sprintf("occurrences[%d].source_refs[%d]: source reference is outside the current extraction chunk", eventIndex, refIndex))
|
issues = append(issues, fmt.Sprintf("occurrences[%d].source_refs[%d]: source reference is outside the current extraction chunk", occurrenceIndex, refIndex))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Package sourcerelatedness warns when item-event evidence does not mention its item.
|
// Package sourcerelatedness warns when item-occurrence evidence does not mention its item.
|
||||||
package sourcerelatedness
|
package sourcerelatedness
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -10,14 +10,14 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
||||||
"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"
|
||||||
itemeventshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/shape"
|
itemoccurrenceshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemoccurrences/shape"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Key = "extract/dnd/item-events/source_relatedness"
|
Key = "extract/dnd/item-occurrences/source_relatedness"
|
||||||
WarningReasonCode = "item_event_source_unrelated"
|
WarningReasonCode = "item_occurrence_source_unrelated"
|
||||||
OmittedReasonCode = "item_event_relatedness_warnings_omitted"
|
OmittedReasonCode = "item_occurrence_relatedness_warnings_omitted"
|
||||||
policy = "dnd.item_events.source_relatedness.v1"
|
policy = "dnd.item_occurrences.source_relatedness.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct{}
|
type Options struct{}
|
||||||
@@ -38,7 +38,7 @@ func (v *Validator) CheckpointFingerprints() []pipeline.CheckpointFingerprint {
|
|||||||
// Validate performs advisory source grounding only. Shape and range failures
|
// Validate performs advisory source grounding only. Shape and range failures
|
||||||
// are intentionally left to their blocking owners.
|
// are intentionally left to their blocking owners.
|
||||||
func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationRequest[dnd.ItemOccurrenceList]) (contracts.ValidationResult, error) {
|
func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationRequest[dnd.ItemOccurrenceList]) (contracts.ValidationResult, error) {
|
||||||
if itemeventshape.Validate(req.Value) != nil {
|
if itemoccurrenceshape.Validate(req.Value) != nil {
|
||||||
return contracts.ValidationResult{Approved: true}, nil
|
return contracts.ValidationResult{Approved: true}, nil
|
||||||
}
|
}
|
||||||
resolver, err := shared.NewCitationResolver(req.Source)
|
resolver, err := shared.NewCitationResolver(req.Source)
|
||||||
@@ -47,20 +47,20 @@ func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationReq
|
|||||||
}
|
}
|
||||||
|
|
||||||
warnings := make([]contracts.Warning, 0)
|
warnings := make([]contracts.Warning, 0)
|
||||||
for index, event := range req.Value.Occurrences {
|
for index, occurrence := range req.Value.Occurrences {
|
||||||
citedText, err := resolver.CitedText(event.SourceRefs)
|
citedText, err := resolver.CitedText(occurrence.SourceRefs)
|
||||||
if err != nil || shared.ContainsTokenSequence(citedText, event.Name) {
|
if err != nil || shared.ContainsTokenSequence(citedText, occurrence.Name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
warnings = append(warnings, contracts.Warning{
|
warnings = append(warnings, contracts.Warning{
|
||||||
Scope: fmt.Sprintf("occurrences[%d]", index),
|
Scope: fmt.Sprintf("occurrences[%d]", index),
|
||||||
ReasonCode: WarningReasonCode,
|
ReasonCode: WarningReasonCode,
|
||||||
Message: fmt.Sprintf("item occurrence name %s was not found in cited source text", diagnostics.Quote(event.Name)),
|
Message: fmt.Sprintf("item occurrence name %s was not found in cited source text", diagnostics.Quote(occurrence.Name)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return contracts.ValidationResult{
|
return contracts.ValidationResult{
|
||||||
Approved: true,
|
Approved: true,
|
||||||
Warnings: diagnostics.LimitWarnings(warnings, "item_events", OmittedReasonCode),
|
Warnings: diagnostics.LimitWarnings(warnings, "item_occurrences", OmittedReasonCode),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,13 +49,13 @@ func TestValidatorDefersMalformedAndUnreadableEvidence(t *testing.T) {
|
|||||||
|
|
||||||
func TestValidatorBoundsWarningsAndRegistersPolicy(t *testing.T) {
|
func TestValidatorBoundsWarningsAndRegistersPolicy(t *testing.T) {
|
||||||
count := diagnostics.MaxWarnings + 5
|
count := diagnostics.MaxWarnings + 5
|
||||||
events := make([]dnd.ItemOccurrence, count)
|
occurrences := make([]dnd.ItemOccurrence, count)
|
||||||
for index := range events {
|
for index := range occurrences {
|
||||||
events[index] = dnd.ItemOccurrence{ItemID: "item", Name: "missing item", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}}
|
occurrences[index] = dnd.ItemOccurrence{ItemID: "item", Name: "missing item", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}}
|
||||||
}
|
}
|
||||||
result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.ItemOccurrenceList]{
|
result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.ItemOccurrenceList]{
|
||||||
Source: &source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 1, Text: "nothing useful"}}},
|
Source: &source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 1, Text: "nothing useful"}}},
|
||||||
Value: dnd.ItemOccurrenceList{Occurrences: events},
|
Value: dnd.ItemOccurrenceList{Occurrences: occurrences},
|
||||||
})
|
})
|
||||||
if err != nil || !result.Approved || len(result.Warnings) != diagnostics.MaxWarnings || result.Warnings[len(result.Warnings)-1].ReasonCode != OmittedReasonCode {
|
if err != nil || !result.Approved || len(result.Warnings) != diagnostics.MaxWarnings || result.Warnings[len(result.Warnings)-1].ReasonCode != OmittedReasonCode {
|
||||||
t.Fatalf("Validate() = %#v, %v", result, err)
|
t.Fatalf("Validate() = %#v, %v", result, err)
|
||||||
@@ -30,9 +30,9 @@ pipelines:
|
|||||||
lane: registry
|
lane: registry
|
||||||
artifacts:
|
artifacts:
|
||||||
occurrences:
|
occurrences:
|
||||||
extract: dnd/item-events
|
extract: dnd/item-occurrences
|
||||||
merge: appendorder
|
merge: appendorder
|
||||||
normalize: dnd/item-events
|
normalize: dnd/item-occurrences
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -51,7 +51,7 @@ pipelines:
|
|||||||
- id: record-occurrences
|
- id: record-occurrences
|
||||||
artifacts:
|
artifacts:
|
||||||
occurrences:
|
occurrences:
|
||||||
extract: dnd/item-events
|
extract: dnd/item-occurrences
|
||||||
`, "record-occurrences", "record-occurrences", 1))
|
`, "record-occurrences", "record-occurrences", 1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"id": "item-events-session",
|
"id": "item-occurrences-session",
|
||||||
"title": "Synthetic D&D item-event session"
|
"title": "Synthetic D&D item-occurrence session"
|
||||||
},
|
},
|
||||||
"segments": [
|
"segments": [
|
||||||
{"id": 1, "start": 0, "end": 4, "speaker": "DM", "text": "An ancient coin rests in the chest."},
|
{"id": 1, "start": 0, "end": 4, "speaker": "DM", "text": "An ancient coin rests in the chest."},
|
||||||
Reference in New Issue
Block a user