Move D&D spell assets into extractor module
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"embed"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -17,7 +18,6 @@ var embeddedAssets embed.FS
|
||||
const (
|
||||
SourceBuiltin = "builtin"
|
||||
VersionV1 = "v1"
|
||||
DNDSpellsPromptID = "dnd.spells"
|
||||
TestGenericPromptID = "test.generic"
|
||||
)
|
||||
|
||||
@@ -41,21 +41,23 @@ func (m Metadata) DiagnosticsMap() map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
type definition struct {
|
||||
id string
|
||||
version string
|
||||
embeddedDir string
|
||||
systemPath string
|
||||
userPath string
|
||||
// Definition identifies a caller-owned system/user prompt bundle.
|
||||
type Definition struct {
|
||||
PromptID string
|
||||
Version string
|
||||
EmbeddedPath string
|
||||
SystemPath string
|
||||
UserPath string
|
||||
}
|
||||
|
||||
type compiledPrompt struct {
|
||||
// Bundle is a compiled system/user prompt pair.
|
||||
type Bundle struct {
|
||||
systemTmpl *template.Template
|
||||
userTmpl *template.Template
|
||||
metadata Metadata
|
||||
}
|
||||
|
||||
var promptRegistry map[string]compiledPrompt
|
||||
var promptRegistry map[string]*Bundle
|
||||
var sharedHardening string
|
||||
|
||||
func init() {
|
||||
@@ -65,30 +67,23 @@ func init() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defs := []definition{
|
||||
defs := []Definition{
|
||||
{
|
||||
id: DNDSpellsPromptID,
|
||||
version: VersionV1,
|
||||
embeddedDir: "assets/dnd/spells",
|
||||
systemPath: "assets/dnd/spells/system.md",
|
||||
userPath: "assets/dnd/spells/user.md",
|
||||
},
|
||||
{
|
||||
id: TestGenericPromptID,
|
||||
version: VersionV1,
|
||||
embeddedDir: "assets/test/generic",
|
||||
systemPath: "assets/test/generic/system.md",
|
||||
userPath: "assets/test/generic/user.md",
|
||||
PromptID: TestGenericPromptID,
|
||||
Version: VersionV1,
|
||||
EmbeddedPath: "assets/test/generic",
|
||||
SystemPath: "assets/test/generic/system.md",
|
||||
UserPath: "assets/test/generic/user.md",
|
||||
},
|
||||
}
|
||||
|
||||
promptRegistry = make(map[string]compiledPrompt, len(defs))
|
||||
promptRegistry = make(map[string]*Bundle, len(defs))
|
||||
for _, def := range defs {
|
||||
compiled, compileErr := compilePrompt(def)
|
||||
compiled, compileErr := LoadBundle(embeddedAssets, def)
|
||||
if compileErr != nil {
|
||||
panic(compileErr)
|
||||
}
|
||||
promptRegistry[def.id] = compiled
|
||||
promptRegistry[compiled.metadata.PromptID] = compiled
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,51 +133,68 @@ func readAsset(assetPath string) (string, error) {
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
func compilePrompt(def definition) (compiledPrompt, error) {
|
||||
if strings.TrimSpace(def.id) == "" {
|
||||
return compiledPrompt{}, fmt.Errorf("prompt id must not be empty")
|
||||
// LoadBundle compiles a system/user prompt bundle from a caller-owned filesystem.
|
||||
func LoadBundle(fsys fs.FS, def Definition) (*Bundle, error) {
|
||||
promptID := strings.TrimSpace(def.PromptID)
|
||||
version := strings.TrimSpace(def.Version)
|
||||
embeddedPath := strings.TrimSpace(def.EmbeddedPath)
|
||||
systemPath := strings.TrimSpace(def.SystemPath)
|
||||
userPath := strings.TrimSpace(def.UserPath)
|
||||
if promptID == "" {
|
||||
return nil, fmt.Errorf("prompt id must not be empty")
|
||||
}
|
||||
if strings.TrimSpace(def.version) == "" {
|
||||
return compiledPrompt{}, fmt.Errorf("prompt version must not be empty")
|
||||
if version == "" {
|
||||
return nil, fmt.Errorf("prompt version must not be empty")
|
||||
}
|
||||
if strings.TrimSpace(def.embeddedDir) == "" {
|
||||
return compiledPrompt{}, fmt.Errorf("prompt embedded path must not be empty")
|
||||
if embeddedPath == "" {
|
||||
return nil, fmt.Errorf("prompt embedded path must not be empty")
|
||||
}
|
||||
|
||||
systemSource, err := readAsset(def.systemPath)
|
||||
systemSource, err := readPromptAsset(fsys, systemPath)
|
||||
if err != nil {
|
||||
return compiledPrompt{}, err
|
||||
return nil, err
|
||||
}
|
||||
userSource, err := readAsset(def.userPath)
|
||||
userSource, err := readPromptAsset(fsys, userPath)
|
||||
if err != nil {
|
||||
return compiledPrompt{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
funcs := template.FuncMap{
|
||||
"hardening": func() string { return sharedHardening },
|
||||
}
|
||||
systemTmpl, err := template.New(path.Base(def.systemPath)).Option("missingkey=error").Funcs(funcs).Parse(systemSource)
|
||||
systemTmpl, err := template.New(path.Base(systemPath)).Option("missingkey=error").Funcs(funcs).Parse(systemSource)
|
||||
if err != nil {
|
||||
return compiledPrompt{}, fmt.Errorf("parse embedded system prompt %q: %w", def.systemPath, err)
|
||||
return nil, fmt.Errorf("parse embedded system prompt %q: %w", systemPath, err)
|
||||
}
|
||||
userTmpl, err := template.New(path.Base(def.userPath)).Option("missingkey=error").Funcs(funcs).Parse(userSource)
|
||||
userTmpl, err := template.New(path.Base(userPath)).Option("missingkey=error").Funcs(funcs).Parse(userSource)
|
||||
if err != nil {
|
||||
return compiledPrompt{}, fmt.Errorf("parse embedded user prompt %q: %w", def.userPath, err)
|
||||
return nil, fmt.Errorf("parse embedded user prompt %q: %w", userPath, err)
|
||||
}
|
||||
|
||||
hashInput := systemSource + "\n\n" + userSource
|
||||
hash := sha256.Sum256([]byte(hashInput))
|
||||
metadata := Metadata{
|
||||
PromptID: strings.TrimSpace(def.id),
|
||||
PromptVersion: strings.TrimSpace(def.version),
|
||||
PromptID: promptID,
|
||||
PromptVersion: version,
|
||||
PromptSource: SourceBuiltin,
|
||||
EmbeddedPath: strings.TrimSpace(def.embeddedDir),
|
||||
EmbeddedPath: embeddedPath,
|
||||
SHA256: "sha256:" + hex.EncodeToString(hash[:]),
|
||||
}
|
||||
|
||||
return compiledPrompt{
|
||||
return &Bundle{
|
||||
systemTmpl: systemTmpl,
|
||||
userTmpl: userTmpl,
|
||||
metadata: metadata,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func readPromptAsset(fsys fs.FS, assetPath string) (string, error) {
|
||||
if strings.TrimSpace(assetPath) == "" {
|
||||
return "", fmt.Errorf("prompt asset path must not be empty")
|
||||
}
|
||||
content, err := fs.ReadFile(fsys, assetPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("read embedded prompt asset %q: %w", assetPath, err)
|
||||
}
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user