Files
notarius/internal/framework/semanticreconcile/assets.go

112 lines
3.6 KiB
Go

package semanticreconcile
import (
"errors"
"fmt"
"io/fs"
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
"gitea.maximumdirect.net/eric/notarius/internal/framework/promptfs"
)
const (
PromptID = "generic.semantic_reconciliation"
PromptVersion = "v1"
promptRoot = "assets/prompts"
)
var promptFiles = []promptfs.ModulePromptFile{
{Name: "prompt.yaml", Path: "prompts/prompt.yaml"},
{Name: "system.md", Path: "prompts/system.md"},
{Name: "protocol.md", Path: "prompts/protocol.md"},
{Name: "instructions.md", Path: "prompts/instructions.md"},
{Name: "candidates.md", Path: "prompts/candidates.md"},
{Name: "transcript-windows.md", Path: "prompts/transcript-windows.md"},
}
// RegisterAssets registers the generic reconciliation prompt and response
// schema as one production-owned asset set.
func RegisterAssets(registry *llm.AssetRegistry) error {
if registry == nil {
return fmt.Errorf("semantic reconciliation asset registry must not be nil")
}
if err := ensureAssetsAbsent(registry); err != nil {
return err
}
assets, err := assetFS()
if err != nil {
return err
}
prompts, err := promptfs.ModulePromptFS(PromptID, assets, append([]promptfs.ModulePromptFile(nil), promptFiles...))
if err != nil {
return fmt.Errorf("prepare semantic reconciliation prompt assets: %w", err)
}
if err := registry.RegisterPromptFS(prompts, promptRoot); err != nil {
return fmt.Errorf("register semantic reconciliation prompt assets: %w", err)
}
if err := registry.RegisterSchemaFS(assets, "schemas"); err != nil {
return fmt.Errorf("register semantic reconciliation schema assets: %w", err)
}
return nil
}
// PromptHash returns the deterministic identity of the complete generic prompt.
func PromptHash() (string, error) {
assets, err := assetFS()
if err != nil {
return "", err
}
parts := make([]llm.AssetHashPart, 0, len(promptFiles))
for _, file := range promptFiles {
parts = append(parts, llm.AssetHashPart{FS: assets, Path: file.Path})
}
return llm.HashAssets(parts)
}
// SchemaHash returns the deterministic identity of the response schema.
func SchemaHash() (string, error) {
schema, err := LoadResponseSchema()
if err != nil {
return "", err
}
return schema.SHA256, nil
}
// SharedPromptFiles returns fresh descriptors for the mandatory protocol and
// variable-input presentation assets that domain prompts may reuse.
func SharedPromptFiles() ([]promptfs.SharedPromptFile, error) {
assets, err := assetFS()
if err != nil {
return nil, err
}
return []promptfs.SharedPromptFile{
{Name: "protocol.md", FS: assets, Path: "prompts/protocol.md"},
{Name: "candidates.md", FS: assets, Path: "prompts/candidates.md"},
{Name: "transcript-windows.md", FS: assets, Path: "prompts/transcript-windows.md"},
}, nil
}
func ensureAssetsAbsent(registry *llm.AssetRegistry) error {
prompts, err := registry.PromptFS()
if err != nil {
return fmt.Errorf("inspect registered prompt assets: %w", err)
}
if _, err := fs.Stat(prompts, PromptID+"/prompt.yaml"); err == nil {
return fmt.Errorf("semantic reconciliation prompt assets already registered")
} else if !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("inspect semantic reconciliation prompt assets: %w", err)
}
schemas, err := registry.SchemaFS()
if err != nil {
return fmt.Errorf("inspect registered schema assets: %w", err)
}
if _, err := fs.Stat(schemas, "semantic_reconciliation_llm.v1.json"); err == nil {
return fmt.Errorf("semantic reconciliation schema assets already registered")
} else if !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("inspect semantic reconciliation schema assets: %w", err)
}
return nil
}