Make shared prompt filesystem composition generic

This commit is contained in:
2026-07-06 17:10:59 +00:00
parent 223f3751e8
commit 68b426cdb0
5 changed files with 112 additions and 42 deletions

View File

@@ -18,14 +18,18 @@ type ModulePromptFile struct {
Path string
}
// ModulePromptFS builds a prompt filesystem for a module directory from
// module-owned prompt files plus common D&D shared prompt files under the
// module's sharedassets subdirectory.
func ModulePromptFS(moduleDir string, moduleFS fs.FS, files []ModulePromptFile) (fs.FS, error) {
return modulePromptFS(moduleDir, moduleFS, files, embeddedAssets)
// SharedPromptFile maps a caller-owned shared prompt file into a module's
// Scriptorium-visible sharedassets prompt subdirectory.
type SharedPromptFile struct {
Name string
FS fs.FS
Path string
}
func modulePromptFS(moduleDir string, moduleFS fs.FS, files []ModulePromptFile, sharedFS fs.FS) (fs.FS, error) {
// ModulePromptFS builds a prompt filesystem for a module directory from
// module-owned prompt files plus caller-provided shared prompt files under the
// module's sharedassets subdirectory.
func ModulePromptFS(moduleDir string, moduleFS fs.FS, files []ModulePromptFile, sharedFiles ...SharedPromptFile) (fs.FS, error) {
cleanModuleDir, err := cleanPromptPath(moduleDir)
if err != nil {
return nil, fmt.Errorf("module prompt directory: %w", err)
@@ -33,10 +37,7 @@ func modulePromptFS(moduleDir string, moduleFS fs.FS, files []ModulePromptFile,
if moduleFS == nil {
return nil, fmt.Errorf("module prompt filesystem must not be nil")
}
if sharedFS == nil {
return nil, fmt.Errorf("shared prompt filesystem must not be nil")
}
assets := make(promptMapFS, len(files)+len(commonDNDPromptFiles))
assets := make(promptMapFS, len(files)+len(sharedFiles))
for _, file := range files {
name, err := cleanPromptPath(file.Name)
if err != nil {
@@ -56,22 +57,30 @@ func modulePromptFS(moduleDir string, moduleFS fs.FS, files []ModulePromptFile,
assets["assets/prompts/"+cleanModuleDir+"/"+name] = append([]byte(nil), data...)
}
for _, name := range commonDNDPromptFiles {
data, err := fs.ReadFile(sharedFS, "assets/prompts/"+name)
for _, file := range sharedFiles {
name, err := cleanPromptPath(file.Name)
if err != nil {
return nil, fmt.Errorf("read shared prompt asset %s: %w", name, err)
return nil, fmt.Errorf("shared prompt file name %q: %w", file.Name, err)
}
if strings.Contains(name, "/") {
return nil, fmt.Errorf("shared prompt file name %q must not contain path separators", file.Name)
}
if file.FS == nil {
return nil, fmt.Errorf("shared prompt file %q filesystem must not be nil", file.Name)
}
filePath, err := cleanPromptPath(file.Path)
if err != nil {
return nil, fmt.Errorf("shared prompt file path %q: %w", file.Path, err)
}
data, err := fs.ReadFile(file.FS, filePath)
if err != nil {
return nil, fmt.Errorf("read shared prompt asset %s: %w", filePath, err)
}
assets["assets/prompts/"+cleanModuleDir+"/sharedassets/"+name] = append([]byte(nil), data...)
}
return assets, nil
}
var commonDNDPromptFiles = []string{
"common-dnd-system.md",
"common-dnd-transcript.md",
"common-dnd-references.md",
}
type promptMapFS map[string][]byte
func (m promptMapFS) Open(name string) (fs.File, error) {