112 lines
3.6 KiB
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
|
|
}
|