Centralize shared D&D LLM assets
This commit is contained in:
@@ -6,7 +6,7 @@ import (
|
|||||||
"io/fs"
|
"io/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed generic
|
//go:embed dnd generic
|
||||||
var embedded embed.FS
|
var embedded embed.FS
|
||||||
|
|
||||||
// FS returns the embedded read-only asset filesystem.
|
// FS returns the embedded read-only asset filesystem.
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
package register
|
package register
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
|
||||||
|
rootassets "gitea.maximumdirect.net/eric/notarius/assets"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed assets/profiles/*.yaml
|
|
||||||
var embeddedProfileAssets embed.FS
|
|
||||||
|
|
||||||
func registerFallbackProfiles(assets *llm.AssetRegistry) error {
|
func registerFallbackProfiles(assets *llm.AssetRegistry) error {
|
||||||
return assets.RegisterFallbackProfileFS(embeddedProfileAssets, "assets/profiles")
|
profiles, err := fs.Sub(rootassets.FS(), "dnd/profiles")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("scope D&D fallback profiles: %w", err)
|
||||||
|
}
|
||||||
|
return assets.RegisterFallbackProfileFS(profiles, ".")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
package shared
|
package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
rootassets "gitea.maximumdirect.net/eric/notarius/assets"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/promptfs"
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/promptfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed assets/prompts/*.md
|
|
||||||
var embeddedAssets embed.FS
|
|
||||||
|
|
||||||
// PromptAssetManifest is the ordered set of assets that make up one prompt.
|
// PromptAssetManifest is the ordered set of assets that make up one prompt.
|
||||||
// Module files are addressed in the owning module filesystem; shared files use
|
// Module files are addressed in the owning module filesystem; shared files use
|
||||||
// the names in sharedPromptPaths and are mounted beneath sharedassets.
|
// the names in sharedPromptPaths and are mounted beneath sharedassets.
|
||||||
@@ -23,13 +20,21 @@ type PromptAssetManifest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var sharedPromptPaths = map[string]string{
|
var sharedPromptPaths = map[string]string{
|
||||||
"common-dnd-system.md": "assets/prompts/common-dnd-system.md",
|
"common-dnd-system.md": "prompts/common-dnd-system.md",
|
||||||
"common-dnd-extraction-evidence.md": "assets/prompts/common-dnd-extraction-evidence.md",
|
"common-dnd-extraction-evidence.md": "prompts/common-dnd-extraction-evidence.md",
|
||||||
"common-dnd-identity.md": "assets/prompts/common-dnd-identity.md",
|
"common-dnd-identity.md": "prompts/common-dnd-identity.md",
|
||||||
"common-dnd-transcript.md": "assets/prompts/common-dnd-transcript.md",
|
"common-dnd-transcript.md": "prompts/common-dnd-transcript.md",
|
||||||
"common-dnd-references.md": "assets/prompts/common-dnd-references.md",
|
"common-dnd-references.md": "prompts/common-dnd-references.md",
|
||||||
"common-dnd-npcs.md": "assets/prompts/common-dnd-npcs.md",
|
"common-dnd-npcs.md": "prompts/common-dnd-npcs.md",
|
||||||
"common-dnd-entity-reconciliation.md": "assets/prompts/common-dnd-entity-reconciliation.md",
|
"common-dnd-entity-reconciliation.md": "prompts/common-dnd-entity-reconciliation.md",
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
func (manifest PromptAssetManifest) PromptFS(moduleFS fs.FS) (fs.FS, error) {
|
func (manifest PromptAssetManifest) PromptFS(moduleFS fs.FS) (fs.FS, error) {
|
||||||
@@ -57,6 +62,11 @@ func (manifest PromptAssetManifest) Hash(moduleFS fs.FS) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func resolveSharedPromptFiles(names []string) ([]promptfs.SharedPromptFile, error) {
|
func resolveSharedPromptFiles(names []string) ([]promptfs.SharedPromptFile, error) {
|
||||||
|
assets, err := sharedAssetFS()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
files := make([]promptfs.SharedPromptFile, 0, len(names))
|
files := make([]promptfs.SharedPromptFile, 0, len(names))
|
||||||
seen := make(map[string]struct{}, len(names))
|
seen := make(map[string]struct{}, len(names))
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
@@ -76,7 +86,7 @@ func resolveSharedPromptFiles(names []string) ([]promptfs.SharedPromptFile, erro
|
|||||||
seen[name] = struct{}{}
|
seen[name] = struct{}{}
|
||||||
files = append(files, promptfs.SharedPromptFile{
|
files = append(files, promptfs.SharedPromptFile{
|
||||||
Name: name,
|
Name: name,
|
||||||
FS: embeddedAssets,
|
FS: assets,
|
||||||
Path: path,
|
Path: path,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ func TestPromptAssetManifestRejectsMissingModuleFile(t *testing.T) {
|
|||||||
|
|
||||||
func TestPromptAssetManifestRejectsMissingSharedFile(t *testing.T) {
|
func TestPromptAssetManifestRejectsMissingSharedFile(t *testing.T) {
|
||||||
const name = "missing-for-test.md"
|
const name = "missing-for-test.md"
|
||||||
const path = "assets/prompts/missing-for-test.md"
|
const path = "prompts/missing-for-test.md"
|
||||||
previous, existed := sharedPromptPaths[name]
|
previous, existed := sharedPromptPaths[name]
|
||||||
sharedPromptPaths[name] = path
|
sharedPromptPaths[name] = path
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
@@ -157,11 +157,15 @@ func TestPromptAssetManifestHashMatchesManifestParts(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Hash() error = %v, want nil", err)
|
t.Fatalf("Hash() error = %v, want nil", err)
|
||||||
}
|
}
|
||||||
|
sharedFS, err := sharedAssetFS()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("sharedAssetFS() error = %v, want nil", err)
|
||||||
|
}
|
||||||
want, err := llm.HashAssets([]llm.AssetHashPart{
|
want, err := llm.HashAssets([]llm.AssetHashPart{
|
||||||
{FS: moduleFS, Path: "assets/prompts/dnd.test.yaml"},
|
{FS: moduleFS, Path: "assets/prompts/dnd.test.yaml"},
|
||||||
{FS: moduleFS, Path: "assets/prompts/task.md"},
|
{FS: moduleFS, Path: "assets/prompts/task.md"},
|
||||||
{FS: embeddedAssets, Path: "assets/prompts/common-dnd-transcript.md"},
|
{FS: sharedFS, Path: "prompts/common-dnd-transcript.md"},
|
||||||
{FS: embeddedAssets, Path: "assets/prompts/common-dnd-system.md"},
|
{FS: sharedFS, Path: "prompts/common-dnd-system.md"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("HashAssets() error = %v, want nil", err)
|
t.Fatalf("HashAssets() error = %v, want nil", err)
|
||||||
@@ -172,9 +176,9 @@ func TestPromptAssetManifestHashMatchesManifestParts(t *testing.T) {
|
|||||||
withUnused, err := llm.HashAssets([]llm.AssetHashPart{
|
withUnused, err := llm.HashAssets([]llm.AssetHashPart{
|
||||||
{FS: moduleFS, Path: "assets/prompts/dnd.test.yaml"},
|
{FS: moduleFS, Path: "assets/prompts/dnd.test.yaml"},
|
||||||
{FS: moduleFS, Path: "assets/prompts/task.md"},
|
{FS: moduleFS, Path: "assets/prompts/task.md"},
|
||||||
{FS: embeddedAssets, Path: "assets/prompts/common-dnd-transcript.md"},
|
{FS: sharedFS, Path: "prompts/common-dnd-transcript.md"},
|
||||||
{FS: embeddedAssets, Path: "assets/prompts/common-dnd-system.md"},
|
{FS: sharedFS, Path: "prompts/common-dnd-system.md"},
|
||||||
{FS: embeddedAssets, Path: "assets/prompts/common-dnd-npcs.md"},
|
{FS: sharedFS, Path: "prompts/common-dnd-npcs.md"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("HashAssets() with unused asset error = %v, want nil", err)
|
t.Fatalf("HashAssets() with unused asset error = %v, want nil", err)
|
||||||
@@ -195,7 +199,7 @@ func TestSharedPromptDescriptorsReturnFreshCopies(t *testing.T) {
|
|||||||
}
|
}
|
||||||
first[0].Name = "changed.md"
|
first[0].Name = "changed.md"
|
||||||
first[0].Path = "changed.md"
|
first[0].Path = "changed.md"
|
||||||
if reflect.DeepEqual(first, second) || second[0].Name != "common-dnd-system.md" || second[0].Path != "assets/prompts/common-dnd-system.md" {
|
if reflect.DeepEqual(first, second) || second[0].Name != "common-dnd-system.md" || second[0].Path != "prompts/common-dnd-system.md" {
|
||||||
t.Fatalf("resolveSharedPromptFiles() reused descriptor state: first=%#v second=%#v", first, second)
|
t.Fatalf("resolveSharedPromptFiles() reused descriptor state: first=%#v second=%#v", first, second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
package entityreconcile
|
|
||||||
|
|
||||||
import "embed"
|
|
||||||
|
|
||||||
//go:embed assets/schemas/dnd_entity_reconcile_llm.v1.json
|
|
||||||
var embeddedAssets embed.FS
|
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
package entityreconcile
|
package entityreconcile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
|
||||||
|
rootassets "gitea.maximumdirect.net/eric/notarius/assets"
|
||||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -9,13 +13,25 @@ const (
|
|||||||
ResponseSchemaID = "notarius.dnd.entity_reconcile.llm"
|
ResponseSchemaID = "notarius.dnd.entity_reconcile.llm"
|
||||||
ResponseSchemaName = "notarius_dnd_entity_reconcile_llm_v1"
|
ResponseSchemaName = "notarius_dnd_entity_reconcile_llm_v1"
|
||||||
SchemaVersion = "v1"
|
SchemaVersion = "v1"
|
||||||
SchemaAssetPath = "assets/schemas/dnd_entity_reconcile_llm.v1.json"
|
SchemaAssetPath = "schemas/dnd_entity_reconcile_llm.v1.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func schemaAssetFS() (fs.FS, error) {
|
||||||
|
assets, err := fs.Sub(rootassets.FS(), "dnd/entity-reconciliation")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("scope entity reconciliation assets: %w", err)
|
||||||
|
}
|
||||||
|
return assets, nil
|
||||||
|
}
|
||||||
|
|
||||||
// LoadResponseSchema returns the shared private duplicate-group response
|
// LoadResponseSchema returns the shared private duplicate-group response
|
||||||
// contract. It is intentionally separate from durable artifact schemas.
|
// contract. It is intentionally separate from durable artifact schemas.
|
||||||
func LoadResponseSchema() (llm.ResponseSchema, error) {
|
func LoadResponseSchema() (llm.ResponseSchema, error) {
|
||||||
return llm.LoadResponseSchema(embeddedAssets, llm.ResponseSchemaDefinition{
|
assets, err := schemaAssetFS()
|
||||||
|
if err != nil {
|
||||||
|
return llm.ResponseSchema{}, err
|
||||||
|
}
|
||||||
|
return llm.LoadResponseSchema(assets, llm.ResponseSchemaDefinition{
|
||||||
Key: ResponseSchemaKey,
|
Key: ResponseSchemaKey,
|
||||||
ID: ResponseSchemaID,
|
ID: ResponseSchemaID,
|
||||||
Version: SchemaVersion,
|
Version: SchemaVersion,
|
||||||
@@ -27,5 +43,13 @@ func LoadResponseSchema() (llm.ResponseSchema, error) {
|
|||||||
// RegisterSchemaAssets makes the shared private response schema available to
|
// RegisterSchemaAssets makes the shared private response schema available to
|
||||||
// prompt preparation. A family registrar can register it once for all consumers.
|
// prompt preparation. A family registrar can register it once for all consumers.
|
||||||
func RegisterSchemaAssets(registry *llm.AssetRegistry) error {
|
func RegisterSchemaAssets(registry *llm.AssetRegistry) error {
|
||||||
return registry.RegisterSchemaFS(embeddedAssets, "assets/schemas")
|
assets, err := schemaAssetFS()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
schemas, err := fs.Sub(assets, "schemas")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("scope entity reconciliation schemas: %w", err)
|
||||||
|
}
|
||||||
|
return registry.RegisterSchemaFS(schemas, ".")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user