Files
go-sip/internal/ai/snapshot.go
T

81 lines
2.2 KiB
Go

// Package ai handles immutable Agent-version snapshots. The JSON Schema is
// loaded from the pinned upstream contract bundle; this package does not
// duplicate or loosen that schema.
package ai
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"unicode/utf8"
"github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer"
"git.ipao.vip/rogee/go-sip/contracts"
"git.ipao.vip/rogee/go-sip/internal/contract"
)
type Mode string
const (
ModeFullAI Mode = "full_ai"
ModeASROnly Mode = "asr_only"
)
type Snapshot struct {
TenantKey string
AgentVersionID string
Digest string
Raw []byte
Mode Mode
}
func Validate(raw []byte) (Snapshot, error) {
if !utf8.Valid(raw) {
return Snapshot{}, errors.New("AI configuration must be valid UTF-8")
}
if err := contract.ValidateSourceSchema("ai-config.schema.json", raw); err != nil {
return Snapshot{}, err
}
var value struct {
AgentVersionID string `json:"agent_version_id"`
Mode Mode `json:"mode"`
}
if err := json.Unmarshal(raw, &value); err != nil {
return Snapshot{}, err
}
mode := value.Mode
if mode == "" {
// Legacy immutable snapshots predate the explicit mode field and are
// full-AI snapshots because llm/prompt/tts are required by that branch.
mode = ModeFullAI
}
if mode != ModeFullAI && mode != ModeASROnly {
return Snapshot{}, fmt.Errorf("unsupported AI mode %q", mode)
}
canonical, err := jsoncanonicalizer.Transform(raw)
if err != nil {
return Snapshot{}, fmt.Errorf("canonicalize AI configuration: %w", err)
}
digest := sha256.Sum256(canonical)
return Snapshot{AgentVersionID: value.AgentVersionID, Digest: hex.EncodeToString(digest[:]), Raw: append([]byte(nil), raw...), Mode: mode}, nil
}
func ValidateForMode(raw []byte, mode Mode) (Snapshot, error) {
if mode != ModeFullAI && mode != ModeASROnly {
return Snapshot{}, fmt.Errorf("unsupported AI mode %q", mode)
}
snapshot, err := Validate(raw)
if err != nil {
return Snapshot{}, err
}
if snapshot.Mode != mode {
return Snapshot{}, fmt.Errorf("AI config mode %q does not match requested mode %q", snapshot.Mode, mode)
}
return snapshot, nil
}
func ContractSource() string { return contracts.SourceCommit }