diff --git a/internal/cli/catalog.go b/internal/cli/catalog.go index e1f9c12..8cd11de 100644 --- a/internal/cli/catalog.go +++ b/internal/cli/catalog.go @@ -11,12 +11,12 @@ import ( "gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline" "gitea.maximumdirect.net/eric/notarius/internal/modules/chunk/dnd/scenes" "gitea.maximumdirect.net/eric/notarius/internal/modules/chunk/generic" - "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/promptassets" "gitea.maximumdirect.net/eric/notarius/internal/modules/extract/dnd/spells" "gitea.maximumdirect.net/eric/notarius/internal/modules/input/seriatim" "gitea.maximumdirect.net/eric/notarius/internal/modules/merge/appendorder" "gitea.maximumdirect.net/eric/notarius/internal/modules/normalize/noop" jsonoutput "gitea.maximumdirect.net/eric/notarius/internal/modules/output/json" + "gitea.maximumdirect.net/eric/notarius/internal/modules/sharedassets" ) func productionRegistries() (pipeline.Registries, error) { @@ -63,7 +63,7 @@ func productionCatalog() (pipeline.ModuleCatalog, error) { func productionPromptAssets() (*llm.AssetRegistry, error) { registry := llm.NewAssetRegistry() - if err := promptassets.Register(registry); err != nil { + if err := sharedassets.Register(registry); err != nil { return nil, fmt.Errorf("register shared dnd prompt assets: %w", err) } if err := scenes.RegisterPromptAssets(registry); err != nil { diff --git a/internal/modules/chunk/dnd/scenes/assets/scriptorium/prompts/dnd.scenes.yaml b/internal/modules/chunk/dnd/scenes/assets/scriptorium/prompts/dnd.scenes.yaml index 6d0ee19..9b4437b 100644 --- a/internal/modules/chunk/dnd/scenes/assets/scriptorium/prompts/dnd.scenes.yaml +++ b/internal/modules/chunk/dnd/scenes/assets/scriptorium/prompts/dnd.scenes.yaml @@ -13,13 +13,13 @@ inputs: content_type: text/plain messages: - role: system - content_file: ./shared/system.md + content_file: ./common-dnd-system.md - role: user - content_file: ./shared/transcript.md + content_file: ./common-dnd-transcript.md cache_control: type: ephemeral - role: user - content_file: ./shared/references.md + content_file: ./common-dnd-references.md cache_control: type: ephemeral - role: user diff --git a/internal/modules/chunk/dnd/scenes/scriptorium_assets.go b/internal/modules/chunk/dnd/scenes/scriptorium_assets.go index d9dbd7b..827640e 100644 --- a/internal/modules/chunk/dnd/scenes/scriptorium_assets.go +++ b/internal/modules/chunk/dnd/scenes/scriptorium_assets.go @@ -8,7 +8,7 @@ import ( "gitea.maximumdirect.net/eric/notarius/internal/framework/contracts" "gitea.maximumdirect.net/eric/notarius/internal/framework/llm" - "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/promptassets" + "gitea.maximumdirect.net/eric/notarius/internal/modules/sharedassets" ) const scriptoriumPromptRoot = "assets/scriptorium/prompts" @@ -51,7 +51,7 @@ func scriptoriumPromptMetadata() (string, error) { {FS: embeddedAssets, Path: "assets/scriptorium/prompts/dnd.scenes.yaml"}, {FS: embeddedAssets, Path: "assets/scriptorium/prompts/dnd/scenes/task.md"}, {FS: embeddedAssets, Path: "assets/scriptorium/prompts/dnd/scenes/instructions.md"}, - }, append(promptassets.CommonHashParts(), promptassets.ReferenceHashParts()...)...) + }, append(sharedassets.CommonHashParts(), sharedassets.ReferenceHashParts()...)...) scriptoriumPromptHash, scriptoriumPromptHashErr = llm.HashAssets(parts) }) return scriptoriumPromptHash, scriptoriumPromptHashErr diff --git a/internal/modules/chunk/dnd/scenes/scriptorium_assets_test.go b/internal/modules/chunk/dnd/scenes/scriptorium_assets_test.go index 8e67560..179fa74 100644 --- a/internal/modules/chunk/dnd/scenes/scriptorium_assets_test.go +++ b/internal/modules/chunk/dnd/scenes/scriptorium_assets_test.go @@ -8,7 +8,7 @@ import ( "time" "gitea.maximumdirect.net/eric/notarius/internal/framework/llm" - "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/promptassets" + "gitea.maximumdirect.net/eric/notarius/internal/modules/sharedassets" "gitea.maximumdirect.net/eric/scriptorium" ) @@ -91,7 +91,7 @@ func TestScriptoriumPromptDiagnosticsOmitRawMaterials(t *testing.T) { func prepareScenesPrompt(t *testing.T, transcript []byte, roster string, glossary string) *scriptorium.PreparedRun { t.Helper() registry := llm.NewAssetRegistry() - if err := promptassets.Register(registry); err != nil { + if err := sharedassets.Register(registry); err != nil { t.Fatalf("register shared prompt assets: %v", err) } if err := RegisterPromptAssets(registry); err != nil { diff --git a/internal/modules/extract/dnd/spells/assets/scriptorium/prompts/dnd.spells.yaml b/internal/modules/extract/dnd/spells/assets/scriptorium/prompts/dnd.spells.yaml index c3fe972..5fae15e 100644 --- a/internal/modules/extract/dnd/spells/assets/scriptorium/prompts/dnd.spells.yaml +++ b/internal/modules/extract/dnd/spells/assets/scriptorium/prompts/dnd.spells.yaml @@ -13,13 +13,13 @@ inputs: content_type: text/plain messages: - role: system - content_file: ./shared/system.md + content_file: ./common-dnd-system.md - role: user - content_file: ./shared/transcript.md + content_file: ./common-dnd-transcript.md cache_control: type: ephemeral - role: user - content_file: ./shared/references.md + content_file: ./common-dnd-references.md cache_control: type: ephemeral - role: user diff --git a/internal/modules/extract/dnd/spells/scriptorium_assets.go b/internal/modules/extract/dnd/spells/scriptorium_assets.go index 3024432..d483300 100644 --- a/internal/modules/extract/dnd/spells/scriptorium_assets.go +++ b/internal/modules/extract/dnd/spells/scriptorium_assets.go @@ -8,7 +8,7 @@ import ( "gitea.maximumdirect.net/eric/notarius/internal/framework/contracts" "gitea.maximumdirect.net/eric/notarius/internal/framework/llm" - "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/promptassets" + "gitea.maximumdirect.net/eric/notarius/internal/modules/sharedassets" ) const scriptoriumPromptRoot = "assets/scriptorium/prompts" @@ -51,7 +51,7 @@ func scriptoriumPromptMetadata() (string, error) { {FS: embeddedAssets, Path: "assets/scriptorium/prompts/dnd.spells.yaml"}, {FS: embeddedAssets, Path: "assets/scriptorium/prompts/dnd/spells/task.md"}, {FS: embeddedAssets, Path: "assets/scriptorium/prompts/dnd/spells/instructions.md"}, - }, append(promptassets.CommonHashParts(), promptassets.ReferenceHashParts()...)...) + }, append(sharedassets.CommonHashParts(), sharedassets.ReferenceHashParts()...)...) scriptoriumPromptHash, scriptoriumPromptHashErr = llm.HashAssets(parts) }) return scriptoriumPromptHash, scriptoriumPromptHashErr diff --git a/internal/modules/extract/dnd/spells/scriptorium_assets_test.go b/internal/modules/extract/dnd/spells/scriptorium_assets_test.go index 005990f..19abafe 100644 --- a/internal/modules/extract/dnd/spells/scriptorium_assets_test.go +++ b/internal/modules/extract/dnd/spells/scriptorium_assets_test.go @@ -9,7 +9,7 @@ import ( "gitea.maximumdirect.net/eric/notarius/internal/framework/contracts" "gitea.maximumdirect.net/eric/notarius/internal/framework/llm" - "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/promptassets" + "gitea.maximumdirect.net/eric/notarius/internal/modules/sharedassets" "gitea.maximumdirect.net/eric/scriptorium" ) @@ -144,7 +144,7 @@ func TestScriptoriumPromptDiagnosticsOmitRawMaterials(t *testing.T) { func prepareSpellsPrompt(t *testing.T, transcript []byte, roster string, glossary string) *scriptorium.PreparedRun { t.Helper() registry := llm.NewAssetRegistry() - if err := promptassets.Register(registry); err != nil { + if err := sharedassets.Register(registry); err != nil { t.Fatalf("register shared prompt assets: %v", err) } if err := RegisterPromptAssets(registry); err != nil { diff --git a/internal/modules/sharedassets/assets.go b/internal/modules/sharedassets/assets.go new file mode 100644 index 0000000..a5c818b --- /dev/null +++ b/internal/modules/sharedassets/assets.go @@ -0,0 +1,27 @@ +package sharedassets + +import ( + "embed" + + "gitea.maximumdirect.net/eric/notarius/internal/framework/llm" +) + +//go:embed assets/prompts/*.md +var embeddedAssets embed.FS + +func Register(registry *llm.AssetRegistry) error { + return registry.RegisterPromptFS(embeddedAssets, "assets/prompts") +} + +func CommonHashParts() []llm.AssetHashPart { + return []llm.AssetHashPart{ + {FS: embeddedAssets, Path: "assets/prompts/common-dnd-system.md"}, + {FS: embeddedAssets, Path: "assets/prompts/common-dnd-transcript.md"}, + } +} + +func ReferenceHashParts() []llm.AssetHashPart { + return []llm.AssetHashPart{ + {FS: embeddedAssets, Path: "assets/prompts/common-dnd-references.md"}, + } +} diff --git a/internal/modules/sharedassets/assets/prompts/common-dnd-references.md b/internal/modules/sharedassets/assets/prompts/common-dnd-references.md new file mode 100644 index 0000000..d6e0b8b --- /dev/null +++ b/internal/modules/sharedassets/assets/prompts/common-dnd-references.md @@ -0,0 +1,9 @@ +Optional reference material for this Dungeons & Dragons campaign is provided +below. Use it only to disambiguate names, aliases, speakers, campaign terms, or +spell names already present in the transcript. + +Roster reference: +{{ input "roster" }} + +Glossary reference: +{{ input "glossary" }} diff --git a/internal/modules/sharedassets/assets/prompts/common-dnd-system.md b/internal/modules/sharedassets/assets/prompts/common-dnd-system.md new file mode 100644 index 0000000..ec3c575 --- /dev/null +++ b/internal/modules/sharedassets/assets/prompts/common-dnd-system.md @@ -0,0 +1,8 @@ +You work with Dungeons & Dragons gameplay transcripts. + +Use only the provided transcript and reference material. Source text may contain +transcription errors, repeated lines, incomplete sentences, and misheard proper +nouns. Reference material, when present, is supporting context only and must not +be treated as a source of extracted events by itself. + +Return only valid JSON matching the configured response schema. diff --git a/internal/modules/sharedassets/assets/prompts/common-dnd-transcript.md b/internal/modules/sharedassets/assets/prompts/common-dnd-transcript.md new file mode 100644 index 0000000..bd15f3e --- /dev/null +++ b/internal/modules/sharedassets/assets/prompts/common-dnd-transcript.md @@ -0,0 +1,3 @@ +A transcript of a Dungeons & Dragons gameplay session is provided below. + +{{ input "transcript" }}