Files

110 lines
3.8 KiB
Go
Raw Permalink Normal View History

2026-07-17 05:05:48 +00:00
package shared
2026-07-06 01:17:13 +00:00
import (
2026-07-21 14:13:11 +00:00
"fmt"
"io/fs"
2026-07-21 14:13:11 +00:00
"strings"
2026-07-06 01:17:13 +00:00
2026-08-05 00:37:19 +00:00
rootassets "gitea.maximumdirect.net/eric/notarius/assets"
2026-07-06 01:17:13 +00:00
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
2026-07-17 05:05:48 +00:00
"gitea.maximumdirect.net/eric/notarius/internal/framework/promptfs"
2026-07-06 01:17:13 +00:00
)
2026-07-21 14:13:11 +00:00
// PromptAssetManifest is the ordered set of assets that make up one prompt.
// Module files are addressed in the owning module filesystem. SharedFiles use
// names in sharedPromptPaths, while ExternalSharedFiles are explicit
// caller-owned descriptors. Both kinds are mounted beneath sharedassets.
2026-07-21 14:13:11 +00:00
type PromptAssetManifest struct {
ModuleDir string
ModuleFiles []promptfs.ModulePromptFile
SharedFiles []string
ExternalSharedFiles []promptfs.SharedPromptFile
}
2026-07-21 14:13:11 +00:00
var sharedPromptPaths = map[string]string{
2026-08-09 16:57:44 +00:00
"common-dnd-system.md": "prompts/common-dnd-system.md",
"common-dnd-extraction-evidence.md": "prompts/common-dnd-extraction-evidence.md",
"common-dnd-identity.md": "prompts/common-dnd-identity.md",
"common-dnd-transcript-full.md": "prompts/common-dnd-transcript-full.md",
"common-dnd-transcript-chunk.md": "prompts/common-dnd-transcript-chunk.md",
2026-08-28 00:13:44 +00:00
"common-dnd-scene-combat-policy.md": "prompts/common-dnd-scene-combat-policy.md",
2026-08-09 16:57:44 +00:00
"common-dnd-references.md": "prompts/common-dnd-references.md",
"common-dnd-npc-registry.md": "prompts/common-dnd-npc-registry.md",
2026-08-05 00:37:19 +00:00
}
func sharedAssetFS() (fs.FS, error) {
assets, err := fs.Sub(rootassets.FS(), "dnd/shared")
if err != nil {
return nil, fmt.Errorf("scope shared D&D prompt assets: %w", err)
}
return assets, nil
}
2026-07-21 14:13:11 +00:00
func (manifest PromptAssetManifest) PromptFS(moduleFS fs.FS) (fs.FS, error) {
sharedFiles, err := manifest.sharedPromptFiles()
2026-07-21 14:13:11 +00:00
if err != nil {
return nil, err
2026-07-06 01:17:13 +00:00
}
2026-07-21 14:13:11 +00:00
moduleFiles := append([]promptfs.ModulePromptFile(nil), manifest.ModuleFiles...)
return promptfs.ModulePromptFS(manifest.ModuleDir, moduleFS, moduleFiles, sharedFiles...)
2026-07-06 01:17:13 +00:00
}
2026-07-21 14:13:11 +00:00
func (manifest PromptAssetManifest) Hash(moduleFS fs.FS) (string, error) {
sharedFiles, err := manifest.sharedPromptFiles()
2026-07-21 14:13:11 +00:00
if err != nil {
return "", err
}
moduleFiles := append([]promptfs.ModulePromptFile(nil), manifest.ModuleFiles...)
if _, err := promptfs.ModulePromptFS(manifest.ModuleDir, moduleFS, moduleFiles, sharedFiles...); err != nil {
return "", err
}
2026-07-21 14:13:11 +00:00
parts := make([]llm.AssetHashPart, 0, len(manifest.ModuleFiles)+len(sharedFiles))
for _, file := range manifest.ModuleFiles {
parts = append(parts, llm.AssetHashPart{FS: moduleFS, Path: file.Path})
2026-07-06 01:17:13 +00:00
}
2026-07-21 14:13:11 +00:00
for _, file := range sharedFiles {
parts = append(parts, llm.AssetHashPart{FS: file.FS, Path: file.Path})
}
return llm.HashAssets(parts)
2026-07-06 01:17:13 +00:00
}
func (manifest PromptAssetManifest) sharedPromptFiles() ([]promptfs.SharedPromptFile, error) {
files, err := resolveSharedPromptFiles(manifest.SharedFiles)
if err != nil {
return nil, err
}
return append(files, manifest.ExternalSharedFiles...), nil
}
2026-07-21 14:13:11 +00:00
func resolveSharedPromptFiles(names []string) ([]promptfs.SharedPromptFile, error) {
2026-08-05 00:37:19 +00:00
assets, err := sharedAssetFS()
if err != nil {
return nil, err
}
2026-07-21 14:13:11 +00:00
files := make([]promptfs.SharedPromptFile, 0, len(names))
seen := make(map[string]struct{}, len(names))
for _, name := range names {
if name == "" {
return nil, fmt.Errorf("shared prompt asset name must not be empty")
}
if strings.ContainsAny(name, `/\\`) {
return nil, fmt.Errorf("shared prompt asset name %q must not contain path separators", name)
}
if _, ok := seen[name]; ok {
return nil, fmt.Errorf("duplicate shared prompt asset name %q", name)
}
path, ok := sharedPromptPaths[name]
if !ok {
return nil, fmt.Errorf("unknown shared prompt asset name %q", name)
}
seen[name] = struct{}{}
files = append(files, promptfs.SharedPromptFile{
Name: name,
2026-08-05 00:37:19 +00:00
FS: assets,
2026-07-21 14:13:11 +00:00
Path: path,
})
}
return files, nil
}