Make D&D prompt assets provider neutral

This commit is contained in:
2026-07-28 16:42:41 +00:00
parent 8e04ef9e2b
commit 7c569a3d8c
25 changed files with 103 additions and 103 deletions

View File

@@ -2,9 +2,9 @@
## Status
In progress. The dependency, framework adapter, and version 4 PromptKit
configuration migration are implemented; module asset terminology and remaining
provenance alignment are still planned.
In progress. The dependency, framework adapter, version 4 PromptKit
configuration migration, and provider-neutral module prompt-asset support are
implemented; remaining provenance alignment is still planned.
## Objective

View File

@@ -56,7 +56,7 @@ func (c *Chunker) ReferenceSlots() []contracts.ReferenceSlot {
}
func (c *Chunker) ManifestMetadata() map[string]any {
promptSHA, err := scriptoriumPromptMetadata()
promptSHA, err := promptAssetMetadata()
if err != nil {
promptSHA = ""
}

View File

@@ -9,7 +9,7 @@ import (
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
)
const scriptoriumPromptRoot = "assets/prompts"
const promptAssetRoot = "assets/prompts"
var promptAssetManifest = shared.PromptAssetManifest{
ModuleDir: "dnd.scenes",
@@ -30,21 +30,21 @@ func RegisterPromptAssets(registry *llm.AssetRegistry) error {
if err != nil {
return fmt.Errorf("prepare scene prompt assets: %w", err)
}
if err := registry.RegisterPromptFS(promptFS, scriptoriumPromptRoot); err != nil {
if err := registry.RegisterPromptFS(promptFS, promptAssetRoot); err != nil {
return err
}
return registry.RegisterSchemaFS(embeddedAssets, "assets/schemas")
}
func scriptoriumPromptMetadata() (string, error) {
scriptoriumPromptHashOnce.Do(func() {
scriptoriumPromptHash, scriptoriumPromptHashErr = promptAssetManifest.Hash(embeddedAssets)
func promptAssetMetadata() (string, error) {
promptAssetHashOnce.Do(func() {
promptAssetHash, promptAssetHashErr = promptAssetManifest.Hash(embeddedAssets)
})
return scriptoriumPromptHash, scriptoriumPromptHashErr
return promptAssetHash, promptAssetHashErr
}
var (
scriptoriumPromptHashOnce sync.Once
scriptoriumPromptHash string
scriptoriumPromptHashErr error
promptAssetHashOnce sync.Once
promptAssetHash string
promptAssetHashErr error
)

View File

@@ -11,7 +11,7 @@ import (
"gitea.maximumdirect.net/eric/promptkit"
)
func TestScriptoriumPromptPreparesTranscriptAndTaskMessages(t *testing.T) {
func TestPromptAssetsPrepareTranscriptAndTaskMessages(t *testing.T) {
transcript := []byte(`{"sentinel":"scene-transcript"}`)
prepared := prepareScenesPrompt(t, transcript, "scene-players", "scene-party", "scene-glossary")
@@ -52,7 +52,7 @@ func TestScriptoriumPromptPreparesTranscriptAndTaskMessages(t *testing.T) {
}
}
func TestScriptoriumPromptDiagnosticsOmitRawMaterials(t *testing.T) {
func TestPromptAssetDiagnosticsOmitRawMaterials(t *testing.T) {
transcript := []byte(`{"secret":"source text"}`)
prepared := prepareScenesPrompt(t, transcript, "private player note", "private party note", "private glossary note")
metadata := newChunker(t, &fakeScenesLLMClient{}).ManifestMetadata()
@@ -100,7 +100,7 @@ func prepareScenesPrompt(t *testing.T, transcript []byte, players string, party
if err := RegisterPromptAssets(registry); err != nil {
t.Fatalf("register scene prompt assets: %v", err)
}
engine := newScenesScriptoriumEngine(t, registry)
engine := newScenesPromptEngine(t, registry)
prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{
PromptID: PromptID,
PromptVersion: ResponseSchemaVersion,
@@ -118,7 +118,7 @@ func prepareScenesPrompt(t *testing.T, transcript []byte, players string, party
return prepared
}
func newScenesScriptoriumEngine(t *testing.T, registry *llm.AssetRegistry) *promptkit.Engine {
func newScenesPromptEngine(t *testing.T, registry *llm.AssetRegistry) *promptkit.Engine {
t.Helper()
options, err := registry.PromptKitOptions()
if err != nil {

View File

@@ -96,7 +96,7 @@ func New(llmClient contracts.StructuredLLMClient, _ Options, references ...contr
if err != nil {
return nil, extractorErrorf("prepare scene eligibility: %w", err)
}
promptSHA, err := scriptoriumPromptMetadata()
promptSHA, err := promptAssetMetadata()
if err != nil {
return nil, extractorErrorf("load prompt metadata: %w", err)
}

View File

@@ -9,7 +9,7 @@ import (
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
)
const scriptoriumPromptRoot = "assets/prompts"
const promptAssetRoot = "assets/prompts"
var promptAssetManifest = shared.PromptAssetManifest{
ModuleDir: "dnd.combat_turns",
@@ -33,21 +33,21 @@ func RegisterPromptAssets(registry *llm.AssetRegistry) error {
if err != nil {
return fmt.Errorf("prepare combat-turn prompt assets: %w", err)
}
if err := registry.RegisterPromptFS(promptFS, scriptoriumPromptRoot); err != nil {
if err := registry.RegisterPromptFS(promptFS, promptAssetRoot); err != nil {
return err
}
return registry.RegisterSchemaFS(embeddedAssets, "assets/schemas")
}
func scriptoriumPromptMetadata() (string, error) {
scriptoriumPromptHashOnce.Do(func() {
scriptoriumPromptHash, scriptoriumPromptHashErr = promptAssetManifest.Hash(embeddedAssets)
func promptAssetMetadata() (string, error) {
promptAssetHashOnce.Do(func() {
promptAssetHash, promptAssetHashErr = promptAssetManifest.Hash(embeddedAssets)
})
return scriptoriumPromptHash, scriptoriumPromptHashErr
return promptAssetHash, promptAssetHashErr
}
var (
scriptoriumPromptHashOnce sync.Once
scriptoriumPromptHash string
scriptoriumPromptHashErr error
promptAssetHashOnce sync.Once
promptAssetHash string
promptAssetHashErr error
)

View File

@@ -23,13 +23,13 @@ func TestRegisterPromptAssetsAndPrepareCombatPrompt(t *testing.T) {
if _, err := fs.ReadFile(schemaFS, "dnd_combat_turns_llm.v1.json"); err != nil {
t.Fatalf("response schema asset: %v", err)
}
hash, err := scriptoriumPromptMetadata()
hash, err := promptAssetMetadata()
if err != nil || !strings.HasPrefix(hash, "sha256:") {
t.Fatalf("scriptoriumPromptMetadata() = %q, %v; want digest", hash, err)
t.Fatalf("promptAssetMetadata() = %q, %v; want digest", hash, err)
}
}
func TestScriptoriumPromptPreparesRequiredInputs(t *testing.T) {
func TestPromptAssetsPrepareRequiredInputs(t *testing.T) {
registry := llm.NewAssetRegistry()
if err := RegisterPromptAssets(registry); err != nil {
t.Fatalf("RegisterPromptAssets() error = %v, want nil", err)

View File

@@ -54,7 +54,7 @@ func New(llmClient contracts.StructuredLLMClient, _ Options, references ...contr
if len(references) > 1 {
return nil, extractorErrorf("at most one reference set may be supplied")
}
promptSHA, err := scriptoriumPromptMetadata()
promptSHA, err := promptAssetMetadata()
if err != nil {
return nil, extractorErrorf("load prompt metadata: %w", err)
}

View File

@@ -9,7 +9,7 @@ import (
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
)
const scriptoriumPromptRoot = "assets/prompts"
const promptAssetRoot = "assets/prompts"
var promptAssetManifest = shared.PromptAssetManifest{
ModuleDir: "dnd.item_events",
@@ -32,21 +32,21 @@ func RegisterPromptAssets(registry *llm.AssetRegistry) error {
if err != nil {
return fmt.Errorf("prepare item event prompt assets: %w", err)
}
if err := registry.RegisterPromptFS(promptFS, scriptoriumPromptRoot); err != nil {
if err := registry.RegisterPromptFS(promptFS, promptAssetRoot); err != nil {
return err
}
return registry.RegisterSchemaFS(embeddedAssets, "assets/schemas")
}
func scriptoriumPromptMetadata() (string, error) {
scriptoriumPromptHashOnce.Do(func() {
scriptoriumPromptHash, scriptoriumPromptHashErr = promptAssetManifest.Hash(embeddedAssets)
func promptAssetMetadata() (string, error) {
promptAssetHashOnce.Do(func() {
promptAssetHash, promptAssetHashErr = promptAssetManifest.Hash(embeddedAssets)
})
return scriptoriumPromptHash, scriptoriumPromptHashErr
return promptAssetHash, promptAssetHashErr
}
var (
scriptoriumPromptHashOnce sync.Once
scriptoriumPromptHash string
scriptoriumPromptHashErr error
promptAssetHashOnce sync.Once
promptAssetHash string
promptAssetHashErr error
)

View File

@@ -46,9 +46,9 @@ func TestPromptAssetsPrepareItemEventPrompt(t *testing.T) {
}
func TestPromptAssetsDoNotLeakIntoMetadata(t *testing.T) {
hash, err := scriptoriumPromptMetadata()
hash, err := promptAssetMetadata()
if err != nil || !strings.HasPrefix(hash, "sha256:") {
t.Fatalf("scriptoriumPromptMetadata() = %q, %v", hash, err)
t.Fatalf("promptAssetMetadata() = %q, %v", hash, err)
}
metadata := newExtractor(t, &fakeItemEventsLLMClient{}).ManifestMetadata()
payload, err := json.Marshal(metadata)

View File

@@ -80,7 +80,7 @@ func New(llmClient contracts.StructuredLLMClient, _ Options, references ...contr
if err != nil {
return nil, extractorErrorf("prepare NPC registry prompt input: %w", err)
}
promptSHA, err := scriptoriumPromptMetadata()
promptSHA, err := promptAssetMetadata()
if err != nil {
return nil, extractorErrorf("load prompt metadata: %w", err)
}

View File

@@ -9,7 +9,7 @@ import (
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
)
const scriptoriumPromptRoot = "assets/prompts"
const promptAssetRoot = "assets/prompts"
var promptAssetManifest = shared.PromptAssetManifest{
ModuleDir: "dnd.npc_interactions",
@@ -33,21 +33,21 @@ func RegisterPromptAssets(registry *llm.AssetRegistry) error {
if err != nil {
return fmt.Errorf("prepare NPC-interaction prompt assets: %w", err)
}
if err := registry.RegisterPromptFS(promptFS, scriptoriumPromptRoot); err != nil {
if err := registry.RegisterPromptFS(promptFS, promptAssetRoot); err != nil {
return err
}
return registry.RegisterSchemaFS(embeddedAssets, "assets/schemas")
}
func scriptoriumPromptMetadata() (string, error) {
scriptoriumPromptHashOnce.Do(func() {
scriptoriumPromptHash, scriptoriumPromptHashErr = promptAssetManifest.Hash(embeddedAssets)
func promptAssetMetadata() (string, error) {
promptAssetHashOnce.Do(func() {
promptAssetHash, promptAssetHashErr = promptAssetManifest.Hash(embeddedAssets)
})
return scriptoriumPromptHash, scriptoriumPromptHashErr
return promptAssetHash, promptAssetHashErr
}
var (
scriptoriumPromptHashOnce sync.Once
scriptoriumPromptHash string
scriptoriumPromptHashErr error
promptAssetHashOnce sync.Once
promptAssetHash string
promptAssetHashErr error
)

View File

@@ -54,9 +54,9 @@ func TestRegisterPromptAssetsAndPrepareInteractionPrompt(t *testing.T) {
}
func TestPromptMetadataDoesNotExposeAssetContent(t *testing.T) {
hash, err := scriptoriumPromptMetadata()
hash, err := promptAssetMetadata()
if err != nil || !strings.HasPrefix(hash, "sha256:") {
t.Fatalf("scriptoriumPromptMetadata() = %q, %v", hash, err)
t.Fatalf("promptAssetMetadata() = %q, %v", hash, err)
}
metadata := newExtractor(t, &fakeInteractionsLLMClient{}).ManifestMetadata()
for _, forbidden := range []string{"common-dnd-system", "dnd_npc_interactions_llm.v1.json"} {

View File

@@ -54,7 +54,7 @@ func New(llmClient contracts.StructuredLLMClient, _ Options, references ...contr
if len(references) > 1 {
return nil, extractorErrorf("at most one reference set may be supplied")
}
promptSHA, err := scriptoriumPromptMetadata()
promptSHA, err := promptAssetMetadata()
if err != nil {
return nil, extractorErrorf("load prompt metadata: %w", err)
}

View File

@@ -9,7 +9,7 @@ import (
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
)
const scriptoriumPromptRoot = "assets/prompts"
const promptAssetRoot = "assets/prompts"
var promptAssetManifest = shared.PromptAssetManifest{
ModuleDir: "dnd.npcs",
@@ -32,21 +32,21 @@ func RegisterPromptAssets(registry *llm.AssetRegistry) error {
if err != nil {
return fmt.Errorf("prepare NPC prompt assets: %w", err)
}
if err := registry.RegisterPromptFS(promptFS, scriptoriumPromptRoot); err != nil {
if err := registry.RegisterPromptFS(promptFS, promptAssetRoot); err != nil {
return err
}
return registry.RegisterSchemaFS(embeddedAssets, "assets/schemas")
}
func scriptoriumPromptMetadata() (string, error) {
scriptoriumPromptHashOnce.Do(func() {
scriptoriumPromptHash, scriptoriumPromptHashErr = promptAssetManifest.Hash(embeddedAssets)
func promptAssetMetadata() (string, error) {
promptAssetHashOnce.Do(func() {
promptAssetHash, promptAssetHashErr = promptAssetManifest.Hash(embeddedAssets)
})
return scriptoriumPromptHash, scriptoriumPromptHashErr
return promptAssetHash, promptAssetHashErr
}
var (
scriptoriumPromptHashOnce sync.Once
scriptoriumPromptHash string
scriptoriumPromptHashErr error
promptAssetHashOnce sync.Once
promptAssetHash string
promptAssetHashErr error
)

View File

@@ -45,9 +45,9 @@ func TestRegisterPromptAssetsAndPrepareNPCPrompt(t *testing.T) {
}
func TestPromptMetadataAndDiagnosticsDoNotContainRawAssets(t *testing.T) {
hash, err := scriptoriumPromptMetadata()
hash, err := promptAssetMetadata()
if err != nil || !strings.HasPrefix(hash, "sha256:") {
t.Fatalf("scriptoriumPromptMetadata() = %q, %v; want hash", hash, err)
t.Fatalf("promptAssetMetadata() = %q, %v; want hash", hash, err)
}
metadata := newExtractor(t, &fakeNPCsLLMClient{}).ManifestMetadata()
payload, err := json.Marshal(metadata)

View File

@@ -59,7 +59,7 @@ func New(llmClient contracts.StructuredLLMClient, _ Options, references ...contr
if len(references) > 1 {
return nil, extractorErrorf("at most one reference set may be supplied")
}
promptSHA, err := scriptoriumPromptMetadata()
promptSHA, err := promptAssetMetadata()
if err != nil {
return nil, extractorErrorf("load prompt metadata: %w", err)
}

View File

@@ -9,7 +9,7 @@ import (
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
)
const scriptoriumPromptRoot = "assets/prompts"
const promptAssetRoot = "assets/prompts"
var promptAssetManifest = shared.PromptAssetManifest{
ModuleDir: "dnd.scene_descriptions",
@@ -31,21 +31,21 @@ func RegisterPromptAssets(registry *llm.AssetRegistry) error {
if err != nil {
return fmt.Errorf("prepare scene-description prompt assets: %w", err)
}
if err := registry.RegisterPromptFS(promptFS, scriptoriumPromptRoot); err != nil {
if err := registry.RegisterPromptFS(promptFS, promptAssetRoot); err != nil {
return err
}
return registry.RegisterSchemaFS(embeddedAssets, "assets/schemas")
}
func scriptoriumPromptMetadata() (string, error) {
scriptoriumPromptHashOnce.Do(func() {
scriptoriumPromptHash, scriptoriumPromptHashErr = promptAssetManifest.Hash(embeddedAssets)
func promptAssetMetadata() (string, error) {
promptAssetHashOnce.Do(func() {
promptAssetHash, promptAssetHashErr = promptAssetManifest.Hash(embeddedAssets)
})
return scriptoriumPromptHash, scriptoriumPromptHashErr
return promptAssetHash, promptAssetHashErr
}
var (
scriptoriumPromptHashOnce sync.Once
scriptoriumPromptHash string
scriptoriumPromptHashErr error
promptAssetHashOnce sync.Once
promptAssetHash string
promptAssetHashErr error
)

View File

@@ -45,9 +45,9 @@ func TestRegisterPromptAssetsPreparesSceneDescriptionPrompt(t *testing.T) {
}
func TestPromptMetadataAndDiagnosticsDoNotContainRawAssets(t *testing.T) {
hash, err := scriptoriumPromptMetadata()
hash, err := promptAssetMetadata()
if err != nil || !strings.HasPrefix(hash, "sha256:") {
t.Fatalf("scriptoriumPromptMetadata() = %q, %v; want hash", hash, err)
t.Fatalf("promptAssetMetadata() = %q, %v; want hash", hash, err)
}
payload, err := json.Marshal(newExtractor(t, &fakeSceneDescriptionsLLMClient{}).ManifestMetadata())
if err != nil {

View File

@@ -94,7 +94,7 @@ func New(llmClient contracts.StructuredLLMClient, _ Options, references ...contr
if err != nil {
return nil, extractorErrorf("prepare NPC registry prompt input: %w", err)
}
promptSHA, err := scriptoriumPromptMetadata()
promptSHA, err := promptAssetMetadata()
if err != nil {
return nil, extractorErrorf("load prompt metadata: %w", err)
}

View File

@@ -9,7 +9,7 @@ import (
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
)
const scriptoriumPromptRoot = "assets/prompts"
const promptAssetRoot = "assets/prompts"
var promptAssetManifest = shared.PromptAssetManifest{
ModuleDir: "dnd.spells",
@@ -34,21 +34,21 @@ func RegisterPromptAssets(registry *llm.AssetRegistry) error {
if err != nil {
return fmt.Errorf("prepare spell prompt assets: %w", err)
}
if err := registry.RegisterPromptFS(promptFS, scriptoriumPromptRoot); err != nil {
if err := registry.RegisterPromptFS(promptFS, promptAssetRoot); err != nil {
return err
}
return registry.RegisterSchemaFS(embeddedAssets, "assets/schemas")
}
func scriptoriumPromptMetadata() (string, error) {
scriptoriumPromptHashOnce.Do(func() {
scriptoriumPromptHash, scriptoriumPromptHashErr = promptAssetManifest.Hash(embeddedAssets)
func promptAssetMetadata() (string, error) {
promptAssetHashOnce.Do(func() {
promptAssetHash, promptAssetHashErr = promptAssetManifest.Hash(embeddedAssets)
})
return scriptoriumPromptHash, scriptoriumPromptHashErr
return promptAssetHash, promptAssetHashErr
}
var (
scriptoriumPromptHashOnce sync.Once
scriptoriumPromptHash string
scriptoriumPromptHashErr error
promptAssetHashOnce sync.Once
promptAssetHash string
promptAssetHashErr error
)

View File

@@ -11,7 +11,7 @@ import (
"gitea.maximumdirect.net/eric/promptkit"
)
func TestScriptoriumPromptPreparesSpellPrompt(t *testing.T) {
func TestPromptAssetsPrepareSpellPrompt(t *testing.T) {
transcript := []byte(`{"id":"session-1","segments":[{"id":"u1","text":"Mira casts shield."}]}`)
prepared := prepareSpellsPrompt(t, transcript, "Dana: Mira", "Mira: wizard", "Shield: abjuration")
@@ -23,7 +23,7 @@ func TestScriptoriumPromptPreparesSpellPrompt(t *testing.T) {
}
}
func TestScriptoriumPromptPreparesWithMissingOptionalReferences(t *testing.T) {
func TestPromptAssetsPrepareWithMissingOptionalReferences(t *testing.T) {
transcript := []byte(`{"id":"session-1","segments":[]}`)
prepared := prepareSpellsPrompt(t, transcript, " ", " ", " ")
@@ -35,7 +35,7 @@ func TestScriptoriumPromptPreparesWithMissingOptionalReferences(t *testing.T) {
t.Fatalf("reference message did not render empty optional reference placeholders")
}
func TestScriptoriumPromptDiagnosticsOmitRawMaterials(t *testing.T) {
func TestPromptAssetDiagnosticsOmitRawMaterials(t *testing.T) {
transcript := []byte(`{"secret":"source text"}`)
reference := "private party note"
prepared := prepareSpellsPrompt(t, transcript, "private player note", reference, " ")

View File

@@ -53,7 +53,7 @@ func New(llmClient contracts.StructuredLLMClient, _ Options) (*Normalizer, error
if llmClient == nil {
return nil, normalizerErrorf("LLM client must not be nil")
}
promptSHA, err := scriptoriumPromptMetadata()
promptSHA, err := promptAssetMetadata()
if err != nil {
return nil, normalizerErrorf("load prompt metadata: %w", err)
}

View File

@@ -9,7 +9,7 @@ import (
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
)
const scriptoriumPromptRoot = "assets/prompts"
const promptAssetRoot = "assets/prompts"
var promptAssetManifest = shared.PromptAssetManifest{
ModuleDir: PromptID,
@@ -30,21 +30,21 @@ func RegisterPromptAssets(registry *llm.AssetRegistry) error {
if err != nil {
return fmt.Errorf("prepare NPC normalization prompt assets: %w", err)
}
if err := registry.RegisterPromptFS(promptFS, scriptoriumPromptRoot); err != nil {
if err := registry.RegisterPromptFS(promptFS, promptAssetRoot); err != nil {
return err
}
return registry.RegisterSchemaFS(embeddedAssets, "assets/schemas")
}
func scriptoriumPromptMetadata() (string, error) {
scriptoriumPromptHashOnce.Do(func() {
scriptoriumPromptHash, scriptoriumPromptHashErr = promptAssetManifest.Hash(embeddedAssets)
func promptAssetMetadata() (string, error) {
promptAssetHashOnce.Do(func() {
promptAssetHash, promptAssetHashErr = promptAssetManifest.Hash(embeddedAssets)
})
return scriptoriumPromptHash, scriptoriumPromptHashErr
return promptAssetHash, promptAssetHashErr
}
var (
scriptoriumPromptHashOnce sync.Once
scriptoriumPromptHash string
scriptoriumPromptHashErr error
promptAssetHashOnce sync.Once
promptAssetHash string
promptAssetHashErr error
)

View File

@@ -15,8 +15,8 @@ func TestRegisterPromptAssetsPreparesNormalizationPrompt(t *testing.T) {
if want := []string{"common-dnd-system.md", "common-dnd-transcript.md"}; !reflect.DeepEqual(promptAssetManifest.SharedFiles, want) {
t.Fatalf("shared prompt assets = %#v, want %#v", promptAssetManifest.SharedFiles, want)
}
if promptHash, err := scriptoriumPromptMetadata(); err != nil || promptHash == "" {
t.Fatalf("scriptoriumPromptMetadata() = %q, %v; want prompt fingerprint", promptHash, err)
if promptHash, err := promptAssetMetadata(); err != nil || promptHash == "" {
t.Fatalf("promptAssetMetadata() = %q, %v; want prompt fingerprint", promptHash, err)
}
registry := llm.NewAssetRegistry()
if err := RegisterPromptAssets(registry); err != nil {