Files
notarius/internal/framework/llm/promptkit_profile_fingerprint.go

96 lines
2.8 KiB
Go

package llm
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io/fs"
"os"
"path/filepath"
"sort"
"strings"
)
const (
promptKitProfileFingerprintName = "promptkit_profile_source"
promptKitLocalBackendFingerprintName = "promptkit_local_backend_target"
promptKitLocalBackendMarker = "notarius:promptkit-local-backend:v1"
// The built-in profile catalog is compiled into this pinned PromptKit
// release. Update this identity when the dependency is upgraded.
promptKitBuiltinProfileCatalogID = "promptkit:v0.5.0:builtin-profiles"
)
func promptKitProfileFingerprint(profileDir, profileFile, fallbackProfileDigest string) (CheckpointFingerprint, error) {
hasher := sha256.New()
writeFingerprintPart(hasher, []byte(promptKitBuiltinProfileCatalogID))
writeFingerprintPart(hasher, []byte(strings.TrimSpace(fallbackProfileDigest)))
switch {
case strings.TrimSpace(profileFile) != "":
data, err := os.ReadFile(strings.TrimSpace(profileFile))
if err != nil {
return CheckpointFingerprint{}, fmt.Errorf("read PromptKit profile file for checkpoint identity: %w", err)
}
writeFingerprintPart(hasher, data)
case strings.TrimSpace(profileDir) != "":
digests, err := promptKitProfileFileDigests(strings.TrimSpace(profileDir))
if err != nil {
return CheckpointFingerprint{}, err
}
for _, digest := range digests {
writeFingerprintPart(hasher, digest)
}
}
return CheckpointFingerprint{
Name: promptKitProfileFingerprintName,
Value: "sha256:" + hex.EncodeToString(hasher.Sum(nil)),
}, nil
}
func promptKitLocalBackendFingerprint(endpoint string) CheckpointFingerprint {
hasher := sha256.New()
writeFingerprintPart(hasher, []byte(promptKitLocalBackendMarker))
writeFingerprintPart(hasher, []byte(strings.TrimSpace(endpoint)))
return CheckpointFingerprint{
Name: promptKitLocalBackendFingerprintName,
Value: "sha256:" + hex.EncodeToString(hasher.Sum(nil)),
}
}
func promptKitProfileFileDigests(root string) ([][]byte, error) {
var digests [][]byte
err := filepath.WalkDir(root, func(name string, entry fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if entry.IsDir() {
return nil
}
extension := filepath.Ext(entry.Name())
if extension != ".yaml" && extension != ".yml" {
return nil
}
data, err := os.ReadFile(name)
if err != nil {
return err
}
sum := sha256.Sum256(data)
digests = append(digests, append([]byte(nil), sum[:]...))
return nil
})
if err != nil {
return nil, fmt.Errorf("read PromptKit profile directory for checkpoint identity: %w", err)
}
sort.Slice(digests, func(i, j int) bool {
return string(digests[i]) < string(digests[j])
})
return digests, nil
}
func writeFingerprintPart(hasher interface{ Write([]byte) (int, error) }, value []byte) {
length := []byte(fmt.Sprintf("%d:", len(value)))
_, _ = hasher.Write(length)
_, _ = hasher.Write(value)
}