From 3e456ec4d496ad9c87cd2f0ccc37f6bf5473d8fe Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Mon, 6 Jul 2026 01:32:27 +0000 Subject: [PATCH] Update D&D prompt definitions to shallow asset paths --- internal/framework/llm/asset_registry_test.go | 31 +++++++++++++++++++ .../dnd/scenes/assets/prompts/dnd.scenes.yaml | 4 +-- .../modules/chunk/dnd/scenes/prompt_fs.go | 29 ++++++++++++++--- .../chunk/dnd/scenes/scriptorium_assets.go | 15 ++++++++- .../dnd/spells/assets/prompts/dnd.spells.yaml | 4 +-- .../modules/extract/dnd/spells/prompt_fs.go | 29 ++++++++++++++--- .../extract/dnd/spells/scriptorium_assets.go | 15 ++++++++- 7 files changed, 111 insertions(+), 16 deletions(-) diff --git a/internal/framework/llm/asset_registry_test.go b/internal/framework/llm/asset_registry_test.go index 183ebbe..34f6aa2 100644 --- a/internal/framework/llm/asset_registry_test.go +++ b/internal/framework/llm/asset_registry_test.go @@ -98,6 +98,37 @@ func TestAssetRegistryRejectsDuplicateAssetPaths(t *testing.T) { } } +func TestAssetRegistryCombinesNamespacedPromptSources(t *testing.T) { + registry := NewAssetRegistry() + mustRegisterPromptFS(t, registry, fstest.MapFS{ + "dnd.spells/dnd.spells.yaml": {Data: []byte(validPromptYAML("schema.json"))}, + "dnd.spells/task.md": {Data: []byte("spell task")}, + "dnd.spells/instructions.md": {Data: []byte("spell instructions")}, + }, ".") + mustRegisterPromptFS(t, registry, fstest.MapFS{ + "dnd.scenes/dnd.scenes.yaml": {Data: []byte(validPromptYAML("schema.json"))}, + "dnd.scenes/task.md": {Data: []byte("scene task")}, + "dnd.scenes/instructions.md": {Data: []byte("scene instructions")}, + }, ".") + + fsys, err := registry.PromptFS() + if err != nil { + t.Fatalf("PromptFS() error = %v, want nil", err) + } + for _, name := range []string{ + "dnd.spells/dnd.spells.yaml", + "dnd.spells/task.md", + "dnd.spells/instructions.md", + "dnd.scenes/dnd.scenes.yaml", + "dnd.scenes/task.md", + "dnd.scenes/instructions.md", + } { + if _, err := fsys.Open(name); err != nil { + t.Fatalf("PromptFS().Open(%q) error = %v, want nil", name, err) + } + } +} + func TestHashAssetsOmitsRawAssetContent(t *testing.T) { hash, err := HashAssets([]AssetHashPart{{ FS: fstest.MapFS{"prompt.md": {Data: []byte("secret prompt text")}}, diff --git a/internal/modules/chunk/dnd/scenes/assets/prompts/dnd.scenes.yaml b/internal/modules/chunk/dnd/scenes/assets/prompts/dnd.scenes.yaml index 9b4437b..82ce625 100644 --- a/internal/modules/chunk/dnd/scenes/assets/prompts/dnd.scenes.yaml +++ b/internal/modules/chunk/dnd/scenes/assets/prompts/dnd.scenes.yaml @@ -23,9 +23,9 @@ messages: cache_control: type: ephemeral - role: user - content_file: ./dnd/scenes/task.md + content_file: ./task.md - role: user - content_file: ./dnd/scenes/instructions.md + content_file: ./instructions.md output: format: json validation_mode: json_schema diff --git a/internal/modules/chunk/dnd/scenes/prompt_fs.go b/internal/modules/chunk/dnd/scenes/prompt_fs.go index 94a10eb..d849e6b 100644 --- a/internal/modules/chunk/dnd/scenes/prompt_fs.go +++ b/internal/modules/chunk/dnd/scenes/prompt_fs.go @@ -11,12 +11,31 @@ import ( "time" ) -func modulePromptFS() (fs.FS, error) { - return promptMapFSFromEmbedded(map[string]string{ - "assets/prompts/dnd.scenes.yaml": "assets/prompts/dnd.scenes.yaml", - "assets/prompts/dnd/scenes/task.md": "assets/prompts/task.md", - "assets/prompts/dnd/scenes/instructions.md": "assets/prompts/instructions.md", +func modulePromptFS(shared fs.FS) (fs.FS, error) { + assetFS, err := promptMapFSFromEmbedded(map[string]string{ + "assets/prompts/dnd.scenes/dnd.scenes.yaml": "assets/prompts/dnd.scenes.yaml", + "assets/prompts/dnd.scenes/task.md": "assets/prompts/task.md", + "assets/prompts/dnd.scenes/instructions.md": "assets/prompts/instructions.md", }) + if err != nil { + return nil, err + } + assets := assetFS.(promptMapFS) + if shared == nil { + return assets, nil + } + for _, name := range []string{ + "common-dnd-system.md", + "common-dnd-transcript.md", + "common-dnd-references.md", + } { + data, err := fs.ReadFile(shared, name) + if err != nil { + return nil, fmt.Errorf("read shared prompt asset %s: %w", name, err) + } + assets["assets/prompts/dnd.scenes/"+name] = append([]byte(nil), data...) + } + return assets, nil } func promptMapFSFromEmbedded(files map[string]string) (fs.FS, error) { diff --git a/internal/modules/chunk/dnd/scenes/scriptorium_assets.go b/internal/modules/chunk/dnd/scenes/scriptorium_assets.go index f4cd5e8..6783c98 100644 --- a/internal/modules/chunk/dnd/scenes/scriptorium_assets.go +++ b/internal/modules/chunk/dnd/scenes/scriptorium_assets.go @@ -3,6 +3,7 @@ package scenes import ( "bytes" "fmt" + "io/fs" "sort" "sync" @@ -14,7 +15,11 @@ import ( const scriptoriumPromptRoot = "assets/prompts" func RegisterPromptAssets(registry *llm.AssetRegistry) error { - promptFS, err := modulePromptFS() + sharedPromptFS, err := promptSharedFS() + if err != nil { + return fmt.Errorf("prepare shared prompt assets: %w", err) + } + promptFS, err := modulePromptFS(sharedPromptFS) if err != nil { return fmt.Errorf("prepare scene prompt assets: %w", err) } @@ -24,6 +29,14 @@ func RegisterPromptAssets(registry *llm.AssetRegistry) error { return registry.RegisterSchemaFS(embeddedAssets, "assets/schemas") } +func promptSharedFS() (fs.FS, error) { + registry := llm.NewAssetRegistry() + if err := sharedassets.Register(registry); err != nil { + return nil, err + } + return registry.PromptFS() +} + func promptInputs(req contracts.ChunkRequest) contracts.LLMInputSet { return contracts.LLMInputSet{ "transcript": transcriptPromptInput(req.SourceInput), diff --git a/internal/modules/extract/dnd/spells/assets/prompts/dnd.spells.yaml b/internal/modules/extract/dnd/spells/assets/prompts/dnd.spells.yaml index 5fae15e..6cab237 100644 --- a/internal/modules/extract/dnd/spells/assets/prompts/dnd.spells.yaml +++ b/internal/modules/extract/dnd/spells/assets/prompts/dnd.spells.yaml @@ -23,9 +23,9 @@ messages: cache_control: type: ephemeral - role: user - content_file: ./dnd/spells/task.md + content_file: ./task.md - role: user - content_file: ./dnd/spells/instructions.md + content_file: ./instructions.md output: format: json validation_mode: json_schema diff --git a/internal/modules/extract/dnd/spells/prompt_fs.go b/internal/modules/extract/dnd/spells/prompt_fs.go index 7253a26..a4db50f 100644 --- a/internal/modules/extract/dnd/spells/prompt_fs.go +++ b/internal/modules/extract/dnd/spells/prompt_fs.go @@ -11,12 +11,31 @@ import ( "time" ) -func modulePromptFS() (fs.FS, error) { - return promptMapFSFromEmbedded(map[string]string{ - "assets/prompts/dnd.spells.yaml": "assets/prompts/dnd.spells.yaml", - "assets/prompts/dnd/spells/task.md": "assets/prompts/task.md", - "assets/prompts/dnd/spells/instructions.md": "assets/prompts/instructions.md", +func modulePromptFS(shared fs.FS) (fs.FS, error) { + assetFS, err := promptMapFSFromEmbedded(map[string]string{ + "assets/prompts/dnd.spells/dnd.spells.yaml": "assets/prompts/dnd.spells.yaml", + "assets/prompts/dnd.spells/task.md": "assets/prompts/task.md", + "assets/prompts/dnd.spells/instructions.md": "assets/prompts/instructions.md", }) + if err != nil { + return nil, err + } + assets := assetFS.(promptMapFS) + if shared == nil { + return assets, nil + } + for _, name := range []string{ + "common-dnd-system.md", + "common-dnd-transcript.md", + "common-dnd-references.md", + } { + data, err := fs.ReadFile(shared, name) + if err != nil { + return nil, fmt.Errorf("read shared prompt asset %s: %w", name, err) + } + assets["assets/prompts/dnd.spells/"+name] = append([]byte(nil), data...) + } + return assets, nil } func promptMapFSFromEmbedded(files map[string]string) (fs.FS, error) { diff --git a/internal/modules/extract/dnd/spells/scriptorium_assets.go b/internal/modules/extract/dnd/spells/scriptorium_assets.go index 9d394a8..1ce4f83 100644 --- a/internal/modules/extract/dnd/spells/scriptorium_assets.go +++ b/internal/modules/extract/dnd/spells/scriptorium_assets.go @@ -3,6 +3,7 @@ package spells import ( "bytes" "fmt" + "io/fs" "sort" "sync" @@ -14,7 +15,11 @@ import ( const scriptoriumPromptRoot = "assets/prompts" func RegisterPromptAssets(registry *llm.AssetRegistry) error { - promptFS, err := modulePromptFS() + sharedPromptFS, err := promptSharedFS() + if err != nil { + return fmt.Errorf("prepare shared prompt assets: %w", err) + } + promptFS, err := modulePromptFS(sharedPromptFS) if err != nil { return fmt.Errorf("prepare spell prompt assets: %w", err) } @@ -24,6 +29,14 @@ func RegisterPromptAssets(registry *llm.AssetRegistry) error { return registry.RegisterSchemaFS(embeddedAssets, "assets/schemas") } +func promptSharedFS() (fs.FS, error) { + registry := llm.NewAssetRegistry() + if err := sharedassets.Register(registry); err != nil { + return nil, err + } + return registry.PromptFS() +} + func promptInputs(req contracts.ExtractionRequest) contracts.LLMInputSet { return contracts.LLMInputSet{ "transcript": transcriptPromptInput(req.SourceInput),