diff --git a/internal/modules/dnd/register/chains.go b/internal/modules/dnd/register/chains.go new file mode 100644 index 0000000..e6c053e --- /dev/null +++ b/internal/modules/dnd/register/chains.go @@ -0,0 +1,112 @@ +package register + +import ( + "gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline" + combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns" + npcextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/npcs" + spellextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/spells" + combatnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/combatturns" + npcnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/npcs" + spellnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/spells" + combatinvariants "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/combatturns/invariants" + combatshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/combatturns/shape" + combatsourcerefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/combatturns/source_refs" + combatrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/combatturns/source_relatedness" + npcidentity "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/npcs/identity" + npcshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/npcs/shape" + npcsourcerefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/npcs/source_refs" + npcrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/npcs/source_relatedness" + spellcatalog "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/catalog" + spellshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/shape" + spellsourcerefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/source_refs" + spellrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/source_relatedness" + validjson "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/validate/valid_json" + validjsonschema "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/validate/valid_json_schema" +) + +func registerDefaultChains(registry *pipeline.ValidatorChainRegistry) error { + return runRegistrations([]registration{ + {name: "spells validator chain", register: func() error { + return registry.Register(pipeline.ValidatorChainMapping{ + Stage: pipeline.StageExtract, + Module: spellextract.Key, + Validators: []pipeline.ModuleBinding{ + pipeline.Binding(validjson.Key), + pipeline.Binding(validjsonschema.Key), + pipeline.Binding(spellshape.Key), + pipeline.Binding(spellcatalog.Key), + pipeline.Binding(spellsourcerefs.Key), + pipeline.Binding(spellrelatedness.Key), + }, + }) + }}, + {name: "spells normalize validator chain", register: func() error { + return registry.Register(pipeline.ValidatorChainMapping{ + Stage: pipeline.StageNormalize, + Module: spellnormalize.Key, + Validators: []pipeline.ModuleBinding{ + pipeline.Binding(validjson.Key), + pipeline.Binding(validjsonschema.Key), + pipeline.Binding(spellshape.Key), + pipeline.Binding(spellcatalog.Key), + pipeline.Binding(spellsourcerefs.Key), + pipeline.Binding(spellrelatedness.Key), + }, + }) + }}, + {name: "npcs validator chain", register: func() error { + return registry.Register(pipeline.ValidatorChainMapping{ + Stage: pipeline.StageExtract, + Module: npcextract.Key, + Validators: []pipeline.ModuleBinding{ + pipeline.Binding(validjson.Key), + pipeline.Binding(validjsonschema.Key), + pipeline.Binding(npcshape.Key), + pipeline.Binding(npcsourcerefs.Key), + pipeline.Binding(npcrelatedness.Key), + }, + }) + }}, + {name: "npcs normalize validator chain", register: func() error { + return registry.Register(pipeline.ValidatorChainMapping{ + Stage: pipeline.StageNormalize, + Module: npcnormalize.Key, + Validators: []pipeline.ModuleBinding{ + pipeline.Binding(validjson.Key), + pipeline.Binding(validjsonschema.Key), + pipeline.Binding(npcshape.Key), + pipeline.Binding(npcidentity.Key), + pipeline.Binding(npcsourcerefs.Key), + pipeline.Binding(npcrelatedness.Key), + }, + }) + }}, + {name: "combat turns validator chain", register: func() error { + return registry.Register(pipeline.ValidatorChainMapping{ + Stage: pipeline.StageExtract, + Module: combatextract.Key, + Validators: []pipeline.ModuleBinding{ + pipeline.Binding(validjson.Key), + pipeline.Binding(validjsonschema.Key), + pipeline.Binding(combatshape.Key), + pipeline.Binding(combatsourcerefs.Key), + pipeline.Binding(combatrelatedness.Key), + }, + }) + }}, + {name: "combat turns normalize validator chain", register: func() error { + return registry.Register(pipeline.ValidatorChainMapping{ + Stage: pipeline.StageNormalize, + Module: combatnormalize.Key, + Validators: []pipeline.ModuleBinding{ + pipeline.Binding(validjson.Key), + pipeline.Binding(validjsonschema.Key), + pipeline.Binding(combatshape.Key), + pipeline.Binding(combatinvariants.Key), + pipeline.Binding(combatsourcerefs.Key), + pipeline.Binding(combatrelatedness.Key), + }, + }) + }}, + }) +} diff --git a/internal/modules/dnd/register/merge.go b/internal/modules/dnd/register/merge.go new file mode 100644 index 0000000..e9265ae --- /dev/null +++ b/internal/modules/dnd/register/merge.go @@ -0,0 +1,83 @@ +package register + +import ( + "gitea.maximumdirect.net/eric/notarius/internal/core/source" + "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd" +) + +func appendSpellLists(values []dnd.SpellList) (dnd.SpellList, error) { + count := 0 + for _, value := range values { + count += len(value.SpellCasts) + } + combined := dnd.SpellList{SpellCasts: make([]dnd.SpellCast, 0, count)} + for _, value := range values { + combined.SpellCasts = append(combined.SpellCasts, value.SpellCasts...) + } + return combined, nil +} + +func appendNPCLists(values []dnd.NPCList) (dnd.NPCList, error) { + count := 0 + present := false + for _, value := range values { + if value.NPCs != nil { + present = true + } + count += len(value.NPCs) + } + if !present { + return dnd.NPCList{}, nil + } + combined := dnd.NPCList{NPCs: make([]dnd.NPC, 0, count)} + for _, value := range values { + combined.NPCs = append(combined.NPCs, value.NPCs...) + } + return combined, nil +} + +func appendCombatTurnLists(values []dnd.CombatTurnList) (dnd.CombatTurnList, error) { + count := 0 + present := false + for _, value := range values { + if value.CombatTurns != nil { + present = true + } + count += len(value.CombatTurns) + } + if !present { + return dnd.CombatTurnList{}, nil + } + combined := dnd.CombatTurnList{CombatTurns: make([]dnd.CombatTurn, 0, count)} + for _, value := range values { + for _, turn := range value.CombatTurns { + combined.CombatTurns = append(combined.CombatTurns, cloneCombatTurn(turn)) + } + } + return combined, nil +} + +func cloneCombatTurn(value dnd.CombatTurn) dnd.CombatTurn { + clone := value + if value.Round != nil { + round := *value.Round + clone.Round = &round + } + if value.Actions != nil { + clone.Actions = make([]dnd.CombatAction, len(value.Actions)) + for index, action := range value.Actions { + clone.Actions[index] = action + if action.Targets != nil { + clone.Actions[index].Targets = append([]string(nil), action.Targets...) + } + if action.Resolution != nil { + resolution := *action.Resolution + clone.Actions[index].Resolution = &resolution + } + } + } + if value.SourceRefs != nil { + clone.SourceRefs = append([]source.SourceRef(nil), value.SourceRefs...) + } + return clone +} diff --git a/internal/modules/dnd/register/modules.go b/internal/modules/dnd/register/modules.go new file mode 100644 index 0000000..2ac216c --- /dev/null +++ b/internal/modules/dnd/register/modules.go @@ -0,0 +1,62 @@ +package register + +import ( + "gitea.maximumdirect.net/eric/notarius/internal/framework/llm" + "gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline" + "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd" + "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/chunk/scenes" + combatcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/combatturns" + npccodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/npcs" + spellcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/spells" + combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns" + npcextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/npcs" + spellextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/spells" + combatnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/combatturns" + npcnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/npcs" + spellnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/spells" + "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/merge/appendorder" + "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/normalize/noop" +) + +func registerModules(registries pipeline.Registries) error { + codec := spellcodec.New() + return runRegistrations([]registration{ + {name: "spells codec", register: func() error { return pipeline.RegisterArtifactCodec(registries.ArtifactCodecs, codec) }}, + {name: "npcs 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: "scenes chunker", register: func() error { return scenes.Register(registries.Chunkers) }}, + {name: "spells extractor", register: func() error { return spellextract.Register(registries.Extractors) }}, + {name: "npcs extractor", register: func() error { return npcextract.Register(registries.Extractors) }}, + {name: "combat turns extractor", register: func() error { return combatextract.Register(registries.Extractors) }}, + {name: "spell-list appendorder merger", register: func() error { + return appendorder.RegisterTyped(registries.Mergers, dnd.SpellListKind, appendSpellLists) + }}, + {name: "npc-list appendorder merger", register: func() error { + return appendorder.RegisterTyped(registries.Mergers, dnd.NPCListKind, appendNPCLists) + }}, + {name: "combat-turn-list appendorder merger", register: func() error { + return appendorder.RegisterTyped(registries.Mergers, dnd.CombatTurnListKind, appendCombatTurnLists) + }}, + {name: "spells normalizer", register: func() error { return spellnormalize.Register(registries.Normalizers) }}, + {name: "npcs normalizer", register: func() error { return npcnormalize.Register(registries.Normalizers) }}, + {name: "combat turns normalizer", register: func() error { return combatnormalize.Register(registries.Normalizers) }}, + {name: "spell-list noop normalizer", register: func() error { + return noop.RegisterTyped[dnd.SpellList](registries.Normalizers, dnd.SpellListKind) + }}, + {name: "npc-list noop normalizer", register: func() error { + return noop.RegisterTyped[dnd.NPCList](registries.Normalizers, dnd.NPCListKind) + }}, + {name: "combat-turn-list noop normalizer", register: func() error { + return noop.RegisterTyped[dnd.CombatTurnList](registries.Normalizers, dnd.CombatTurnListKind) + }}, + }) +} + +func registerPromptAssets(assets *llm.AssetRegistry) error { + return runRegistrations([]registration{ + {name: "scenes prompt assets", register: func() error { return scenes.RegisterPromptAssets(assets) }}, + {name: "spells prompt assets", register: func() error { return spellextract.RegisterPromptAssets(assets) }}, + {name: "npcs prompt assets", register: func() error { return npcextract.RegisterPromptAssets(assets) }}, + {name: "combat turns prompt assets", register: func() error { return combatextract.RegisterPromptAssets(assets) }}, + }) +} diff --git a/internal/modules/dnd/register/register.go b/internal/modules/dnd/register/register.go index b066d13..dbb2f80 100644 --- a/internal/modules/dnd/register/register.go +++ b/internal/modules/dnd/register/register.go @@ -4,276 +4,41 @@ package register import ( "fmt" - "gitea.maximumdirect.net/eric/notarius/internal/core/source" "gitea.maximumdirect.net/eric/notarius/internal/framework/llm" "gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline" - "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd" - "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/chunk/scenes" - combatcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/combatturns" - npccodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/npcs" - spellcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/spells" - combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns" - npcextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/npcs" - "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/spells" - combatnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/combatturns" - npcnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/npcs" - spellnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/spells" - combatinvariants "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/combatturns/invariants" - combatshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/combatturns/shape" - combatsourcerefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/combatturns/source_refs" - combatrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/combatturns/source_relatedness" - npcidentity "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/npcs/identity" - npcshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/npcs/shape" - npcsourcerefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/npcs/source_refs" - npcrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/npcs/source_relatedness" - spellcatalog "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/catalog" - spellshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/shape" - spellsourcerefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/source_refs" - spellrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/source_relatedness" - "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/merge/appendorder" - "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/normalize/noop" - alwaysaccept "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/validate/always_accept" - alwaysreject "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/validate/always_reject" - validjson "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/validate/valid_json" - validjsonschema "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/validate/valid_json_schema" ) +type registration struct { + name string + register func() error +} + // Register adds all production D&D modules, validators, policy, and assets. func Register(registries pipeline.Registries, assets *llm.AssetRegistry) error { if err := validateRegistries(registries, assets); err != nil { return err } - codec := spellcodec.New() - registrations := []struct { - name string - register func() error - }{ - {name: "spells codec", register: func() error { return pipeline.RegisterArtifactCodec(registries.ArtifactCodecs, codec) }}, - {name: "npcs 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: "scenes chunker", register: func() error { return scenes.Register(registries.Chunkers) }}, - {name: "spells extractor", register: func() error { return spells.Register(registries.Extractors) }}, - {name: "npcs extractor", register: func() error { return npcextract.Register(registries.Extractors) }}, - {name: "combat turns extractor", register: func() error { return combatextract.Register(registries.Extractors) }}, - {name: "spell-list appendorder merger", register: func() error { - return appendorder.RegisterTyped(registries.Mergers, dnd.SpellListKind, appendSpellLists) - }}, - {name: "npc-list appendorder merger", register: func() error { - return appendorder.RegisterTyped(registries.Mergers, dnd.NPCListKind, appendNPCLists) - }}, - {name: "combat-turn-list appendorder merger", register: func() error { - return appendorder.RegisterTyped(registries.Mergers, dnd.CombatTurnListKind, appendCombatTurnLists) - }}, - {name: "spells normalizer", register: func() error { return spellnormalize.Register(registries.Normalizers) }}, - {name: "npcs normalizer", register: func() error { return npcnormalize.Register(registries.Normalizers) }}, - {name: "combat turns normalizer", register: func() error { return combatnormalize.Register(registries.Normalizers) }}, - {name: "spell-list noop normalizer", register: func() error { return noop.RegisterTyped[dnd.SpellList](registries.Normalizers, dnd.SpellListKind) }}, - {name: "npc-list noop normalizer", register: func() error { return noop.RegisterTyped[dnd.NPCList](registries.Normalizers, dnd.NPCListKind) }}, - {name: "combat-turn-list noop normalizer", register: func() error { - return noop.RegisterTyped[dnd.CombatTurnList](registries.Normalizers, dnd.CombatTurnListKind) - }}, - {name: "spell shape validator", register: func() error { return spellshape.Register(registries.Validators) }}, - {name: "spell catalog validator", register: func() error { return spellcatalog.Register(registries.Validators) }}, - {name: "spell source references validator", register: func() error { return spellsourcerefs.Register(registries.Validators) }}, - {name: "spell source relatedness validator", register: func() error { return spellrelatedness.Register(registries.Validators) }}, - {name: "npc shape validator", register: func() error { return npcshape.Register(registries.Validators) }}, - {name: "npc identity validator", register: func() error { return npcidentity.Register(registries.Validators) }}, - {name: "npc source references validator", register: func() error { return npcsourcerefs.Register(registries.Validators) }}, - {name: "npc source relatedness validator", register: func() error { return npcrelatedness.Register(registries.Validators) }}, - {name: "combat shape validator", register: func() error { return combatshape.Register(registries.Validators) }}, - {name: "combat source references validator", register: func() error { return combatsourcerefs.Register(registries.Validators) }}, - {name: "combat source relatedness validator", register: func() error { return combatrelatedness.Register(registries.Validators) }}, - {name: "combat normalized invariants validator", register: func() error { return combatinvariants.Register(registries.Validators) }}, - {name: "spell-list always accept validator", register: func() error { - return alwaysaccept.RegisterTyped[dnd.SpellList](registries.Validators, dnd.SpellListKind) - }}, - {name: "spell-list always reject validator", register: func() error { - return alwaysreject.RegisterTyped[dnd.SpellList](registries.Validators, dnd.SpellListKind) - }}, - {name: "npc-list always accept validator", register: func() error { - return alwaysaccept.RegisterTyped[dnd.NPCList](registries.Validators, dnd.NPCListKind) - }}, - {name: "npc-list always reject validator", register: func() error { - return alwaysreject.RegisterTyped[dnd.NPCList](registries.Validators, dnd.NPCListKind) - }}, - {name: "combat-turn-list always accept validator", register: func() error { - return alwaysaccept.RegisterTyped[dnd.CombatTurnList](registries.Validators, dnd.CombatTurnListKind) - }}, - {name: "combat-turn-list always reject validator", register: func() error { - return alwaysreject.RegisterTyped[dnd.CombatTurnList](registries.Validators, dnd.CombatTurnListKind) - }}, - {name: "scenes prompt assets", register: func() error { return scenes.RegisterPromptAssets(assets) }}, - {name: "spells prompt assets", register: func() error { return spells.RegisterPromptAssets(assets) }}, - {name: "npcs prompt assets", register: func() error { return npcextract.RegisterPromptAssets(assets) }}, - {name: "combat turns prompt assets", register: func() error { return combatextract.RegisterPromptAssets(assets) }}, + if err := registerModules(registries); err != nil { + return err } + if err := registerValidators(registries); err != nil { + return err + } + if err := registerPromptAssets(assets); err != nil { + return err + } + return registerDefaultChains(registries.ValidatorChains) +} + +func runRegistrations(registrations []registration) error { for _, registration := range registrations { if err := registration.register(); err != nil { return fmt.Errorf("register dnd %s: %w", registration.name, err) } } - if err := registries.ValidatorChains.Register(pipeline.ValidatorChainMapping{ - Stage: pipeline.StageExtract, - Module: spells.Key, - Validators: []pipeline.ModuleBinding{ - pipeline.Binding(validjson.Key), - pipeline.Binding(validjsonschema.Key), - pipeline.Binding(spellshape.Key), - pipeline.Binding(spellcatalog.Key), - pipeline.Binding(spellsourcerefs.Key), - pipeline.Binding(spellrelatedness.Key), - }, - }); err != nil { - return fmt.Errorf("register dnd spells validator chain: %w", err) - } - if err := registries.ValidatorChains.Register(pipeline.ValidatorChainMapping{ - Stage: pipeline.StageNormalize, - Module: spellnormalize.Key, - Validators: []pipeline.ModuleBinding{ - pipeline.Binding(validjson.Key), - pipeline.Binding(validjsonschema.Key), - pipeline.Binding(spellshape.Key), - pipeline.Binding(spellcatalog.Key), - pipeline.Binding(spellsourcerefs.Key), - pipeline.Binding(spellrelatedness.Key), - }, - }); err != nil { - return fmt.Errorf("register dnd spells normalize validator chain: %w", err) - } - if err := registries.ValidatorChains.Register(pipeline.ValidatorChainMapping{ - Stage: pipeline.StageExtract, - Module: npcextract.Key, - Validators: []pipeline.ModuleBinding{ - pipeline.Binding(validjson.Key), - pipeline.Binding(validjsonschema.Key), - pipeline.Binding(npcshape.Key), - pipeline.Binding(npcsourcerefs.Key), - pipeline.Binding(npcrelatedness.Key), - }, - }); err != nil { - return fmt.Errorf("register dnd npcs validator chain: %w", err) - } - if err := registries.ValidatorChains.Register(pipeline.ValidatorChainMapping{ - Stage: pipeline.StageNormalize, - Module: npcnormalize.Key, - Validators: []pipeline.ModuleBinding{ - pipeline.Binding(validjson.Key), - pipeline.Binding(validjsonschema.Key), - pipeline.Binding(npcshape.Key), - pipeline.Binding(npcidentity.Key), - pipeline.Binding(npcsourcerefs.Key), - pipeline.Binding(npcrelatedness.Key), - }, - }); err != nil { - return fmt.Errorf("register dnd npcs normalize validator chain: %w", err) - } - if err := registries.ValidatorChains.Register(pipeline.ValidatorChainMapping{ - Stage: pipeline.StageExtract, - Module: combatextract.Key, - Validators: []pipeline.ModuleBinding{ - pipeline.Binding(validjson.Key), - pipeline.Binding(validjsonschema.Key), - pipeline.Binding(combatshape.Key), - pipeline.Binding(combatsourcerefs.Key), - pipeline.Binding(combatrelatedness.Key), - }, - }); err != nil { - return fmt.Errorf("register dnd combat turns validator chain: %w", err) - } - if err := registries.ValidatorChains.Register(pipeline.ValidatorChainMapping{ - Stage: pipeline.StageNormalize, - Module: combatnormalize.Key, - Validators: []pipeline.ModuleBinding{ - pipeline.Binding(validjson.Key), - pipeline.Binding(validjsonschema.Key), - pipeline.Binding(combatshape.Key), - pipeline.Binding(combatinvariants.Key), - pipeline.Binding(combatsourcerefs.Key), - pipeline.Binding(combatrelatedness.Key), - }, - }); err != nil { - return fmt.Errorf("register dnd combat turns normalize validator chain: %w", err) - } return nil } -func appendSpellLists(values []dnd.SpellList) (dnd.SpellList, error) { - count := 0 - for _, value := range values { - count += len(value.SpellCasts) - } - combined := dnd.SpellList{SpellCasts: make([]dnd.SpellCast, 0, count)} - for _, value := range values { - combined.SpellCasts = append(combined.SpellCasts, value.SpellCasts...) - } - return combined, nil -} - -func appendNPCLists(values []dnd.NPCList) (dnd.NPCList, error) { - count := 0 - present := false - for _, value := range values { - if value.NPCs != nil { - present = true - } - count += len(value.NPCs) - } - if !present { - return dnd.NPCList{}, nil - } - combined := dnd.NPCList{NPCs: make([]dnd.NPC, 0, count)} - for _, value := range values { - combined.NPCs = append(combined.NPCs, value.NPCs...) - } - return combined, nil -} - -func appendCombatTurnLists(values []dnd.CombatTurnList) (dnd.CombatTurnList, error) { - count := 0 - present := false - for _, value := range values { - if value.CombatTurns != nil { - present = true - } - count += len(value.CombatTurns) - } - if !present { - return dnd.CombatTurnList{}, nil - } - combined := dnd.CombatTurnList{CombatTurns: make([]dnd.CombatTurn, 0, count)} - for _, value := range values { - for _, turn := range value.CombatTurns { - combined.CombatTurns = append(combined.CombatTurns, cloneCombatTurn(turn)) - } - } - return combined, nil -} - -func cloneCombatTurn(value dnd.CombatTurn) dnd.CombatTurn { - clone := value - if value.Round != nil { - round := *value.Round - clone.Round = &round - } - if value.Actions != nil { - clone.Actions = make([]dnd.CombatAction, len(value.Actions)) - for index, action := range value.Actions { - clone.Actions[index] = action - if action.Targets != nil { - clone.Actions[index].Targets = append([]string(nil), action.Targets...) - } - if action.Resolution != nil { - resolution := *action.Resolution - clone.Actions[index].Resolution = &resolution - } - } - } - if value.SourceRefs != nil { - clone.SourceRefs = append([]source.SourceRef(nil), value.SourceRefs...) - } - return clone -} - func validateRegistries(registries pipeline.Registries, assets *llm.AssetRegistry) error { switch { case registries.Chunkers == nil: diff --git a/internal/modules/dnd/register/validators.go b/internal/modules/dnd/register/validators.go new file mode 100644 index 0000000..9bf11aa --- /dev/null +++ b/internal/modules/dnd/register/validators.go @@ -0,0 +1,55 @@ +package register + +import ( + "gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline" + "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd" + combatinvariants "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/combatturns/invariants" + combatshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/combatturns/shape" + combatsourcerefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/combatturns/source_refs" + combatrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/combatturns/source_relatedness" + npcidentity "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/npcs/identity" + npcshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/npcs/shape" + npcsourcerefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/npcs/source_refs" + npcrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/npcs/source_relatedness" + spellcatalog "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/catalog" + spellshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/shape" + spellsourcerefs "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/source_refs" + spellrelatedness "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/source_relatedness" + alwaysaccept "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/validate/always_accept" + alwaysreject "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/validate/always_reject" +) + +func registerValidators(registries pipeline.Registries) error { + return runRegistrations([]registration{ + {name: "spell shape validator", register: func() error { return spellshape.Register(registries.Validators) }}, + {name: "spell catalog validator", register: func() error { return spellcatalog.Register(registries.Validators) }}, + {name: "spell source references validator", register: func() error { return spellsourcerefs.Register(registries.Validators) }}, + {name: "spell source relatedness validator", register: func() error { return spellrelatedness.Register(registries.Validators) }}, + {name: "npc shape validator", register: func() error { return npcshape.Register(registries.Validators) }}, + {name: "npc identity validator", register: func() error { return npcidentity.Register(registries.Validators) }}, + {name: "npc source references validator", register: func() error { return npcsourcerefs.Register(registries.Validators) }}, + {name: "npc source relatedness validator", register: func() error { return npcrelatedness.Register(registries.Validators) }}, + {name: "combat shape validator", register: func() error { return combatshape.Register(registries.Validators) }}, + {name: "combat source references validator", register: func() error { return combatsourcerefs.Register(registries.Validators) }}, + {name: "combat source relatedness validator", register: func() error { return combatrelatedness.Register(registries.Validators) }}, + {name: "combat normalized invariants validator", register: func() error { return combatinvariants.Register(registries.Validators) }}, + {name: "spell-list always accept validator", register: func() error { + return alwaysaccept.RegisterTyped[dnd.SpellList](registries.Validators, dnd.SpellListKind) + }}, + {name: "spell-list always reject validator", register: func() error { + return alwaysreject.RegisterTyped[dnd.SpellList](registries.Validators, dnd.SpellListKind) + }}, + {name: "npc-list always accept validator", register: func() error { + return alwaysaccept.RegisterTyped[dnd.NPCList](registries.Validators, dnd.NPCListKind) + }}, + {name: "npc-list always reject validator", register: func() error { + return alwaysreject.RegisterTyped[dnd.NPCList](registries.Validators, dnd.NPCListKind) + }}, + {name: "combat-turn-list always accept validator", register: func() error { + return alwaysaccept.RegisterTyped[dnd.CombatTurnList](registries.Validators, dnd.CombatTurnListKind) + }}, + {name: "combat-turn-list always reject validator", register: func() error { + return alwaysreject.RegisterTyped[dnd.CombatTurnList](registries.Validators, dnd.CombatTurnListKind) + }}, + }) +}