Replace structured LLM dependency with Audita adapter
This commit is contained in:
97
internal/framework/responseschema/registry.go
Normal file
97
internal/framework/responseschema/registry.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package responseschema
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Key identifies one structured response schema used by Audita.
|
||||
type Key string
|
||||
|
||||
const (
|
||||
CorrectionSetKey Key = "correction_set"
|
||||
ValidatorDecisionSetKey Key = "validator_decision_set"
|
||||
correctionSetSchemaID = "audita.correction_set"
|
||||
validatorDecisionSchemaID = "audita.validator_decision_set"
|
||||
schemaVersionV1 = "v1"
|
||||
)
|
||||
|
||||
// Schema describes one registered structured response schema.
|
||||
type Schema struct {
|
||||
ID string `json:"id"`
|
||||
Version string `json:"version"`
|
||||
Name string `json:"name"`
|
||||
JSONSchema json.RawMessage `json:"json_schema"`
|
||||
SHA256 string `json:"sha256"`
|
||||
}
|
||||
|
||||
var registry = map[Key]Schema{
|
||||
CorrectionSetKey: mustBuildSchema(
|
||||
correctionSetSchemaID,
|
||||
schemaVersionV1,
|
||||
"audita_correction_set_v1",
|
||||
[]byte(`{"type":"object","additionalProperties":false,"required":["corrections"],"properties":{"corrections":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["id","original_text","corrected_text","confidence"],"properties":{"id":{"type":"integer","minimum":1},"original_text":{"type":"string","minLength":1},"corrected_text":{"type":"string","minLength":1},"confidence":{"type":"number","minimum":0,"maximum":1}}}}}}`),
|
||||
),
|
||||
ValidatorDecisionSetKey: mustBuildSchema(
|
||||
validatorDecisionSchemaID,
|
||||
schemaVersionV1,
|
||||
"audita_validator_decision_set_v1",
|
||||
[]byte(`{"type":"object","additionalProperties":false,"required":["validations"],"properties":{"validations":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["correction_index","approved","confidence","reason"],"properties":{"correction_index":{"type":"integer","minimum":0},"approved":{"type":"boolean"},"confidence":{"type":"number","minimum":0,"maximum":1},"reason":{"type":"string"}}}}}}`),
|
||||
),
|
||||
}
|
||||
|
||||
// Lookup returns a copy of the registered schema for the provided key.
|
||||
func Lookup(key Key) (Schema, bool) {
|
||||
schema, ok := registry[key]
|
||||
if !ok {
|
||||
return Schema{}, false
|
||||
}
|
||||
return cloneSchema(schema), true
|
||||
}
|
||||
|
||||
// MustLookup returns a copy of the registered schema and panics when missing.
|
||||
func MustLookup(key Key) Schema {
|
||||
schema, ok := Lookup(key)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown structured response schema key %q", key))
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
func cloneSchema(in Schema) Schema {
|
||||
out := in
|
||||
if in.JSONSchema != nil {
|
||||
out.JSONSchema = append(json.RawMessage(nil), in.JSONSchema...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mustBuildSchema(id string, version string, name string, rawSchema []byte) Schema {
|
||||
id = strings.TrimSpace(id)
|
||||
version = strings.TrimSpace(version)
|
||||
name = strings.TrimSpace(name)
|
||||
if id == "" {
|
||||
panic("schema id must not be empty")
|
||||
}
|
||||
if version == "" {
|
||||
panic("schema version must not be empty")
|
||||
}
|
||||
if name == "" {
|
||||
panic("schema name must not be empty")
|
||||
}
|
||||
if !json.Valid(rawSchema) {
|
||||
panic(fmt.Sprintf("schema %s:%s is not valid JSON", id, version))
|
||||
}
|
||||
|
||||
hash := sha256.Sum256(rawSchema)
|
||||
return Schema{
|
||||
ID: id,
|
||||
Version: version,
|
||||
Name: name,
|
||||
JSONSchema: append(json.RawMessage(nil), rawSchema...),
|
||||
SHA256: hex.EncodeToString(hash[:]),
|
||||
}
|
||||
}
|
||||
91
internal/framework/responseschema/registry_test.go
Normal file
91
internal/framework/responseschema/registry_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package responseschema
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
expectedCorrectionSetSHA256 = "05f8ff3fa04f68115c0cb1859d2656f51aa5c0bae8ff2470b2d4f6f531953195"
|
||||
expectedValidatorDecisionSetSHA256 = "b73f4790b98fbb955f0aec5496dd8ce9a8fe14aa2f35c700b4b4e5634f106fd5"
|
||||
)
|
||||
|
||||
func TestLookupKnownSchemas(t *testing.T) {
|
||||
correction, ok := Lookup(CorrectionSetKey)
|
||||
if !ok {
|
||||
t.Fatalf("expected correction-set schema to be registered")
|
||||
}
|
||||
if correction.ID != correctionSetSchemaID {
|
||||
t.Fatalf("unexpected correction-set schema id %q", correction.ID)
|
||||
}
|
||||
if correction.Version != schemaVersionV1 {
|
||||
t.Fatalf("unexpected correction-set schema version %q", correction.Version)
|
||||
}
|
||||
if correction.Name != "audita_correction_set_v1" {
|
||||
t.Fatalf("unexpected correction-set schema name %q", correction.Name)
|
||||
}
|
||||
|
||||
validation, ok := Lookup(ValidatorDecisionSetKey)
|
||||
if !ok {
|
||||
t.Fatalf("expected validator-decision schema to be registered")
|
||||
}
|
||||
if validation.ID != validatorDecisionSchemaID {
|
||||
t.Fatalf("unexpected validator-decision schema id %q", validation.ID)
|
||||
}
|
||||
if validation.Version != schemaVersionV1 {
|
||||
t.Fatalf("unexpected validator-decision schema version %q", validation.Version)
|
||||
}
|
||||
if validation.Name != "audita_validator_decision_set_v1" {
|
||||
t.Fatalf("unexpected validator-decision schema name %q", validation.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupUnknownSchema(t *testing.T) {
|
||||
if _, ok := Lookup(Key("missing")); ok {
|
||||
t.Fatalf("expected unknown schema lookup to fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSchemaHashesMatchRegisteredJSON(t *testing.T) {
|
||||
expectedByKey := map[Key]string{
|
||||
CorrectionSetKey: expectedCorrectionSetSHA256,
|
||||
ValidatorDecisionSetKey: expectedValidatorDecisionSetSHA256,
|
||||
}
|
||||
for key, expectedHash := range expectedByKey {
|
||||
schema, ok := Lookup(key)
|
||||
if !ok {
|
||||
t.Fatalf("missing schema %q", key)
|
||||
}
|
||||
sum := sha256.Sum256(schema.JSONSchema)
|
||||
expected := hex.EncodeToString(sum[:])
|
||||
if schema.SHA256 != expectedHash {
|
||||
t.Fatalf("unexpected stable hash for %q: got %q want %q", key, schema.SHA256, expectedHash)
|
||||
}
|
||||
if schema.SHA256 != expected {
|
||||
t.Fatalf("unexpected hash for %q: got %q want %q", key, schema.SHA256, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupReturnsSchemaCopy(t *testing.T) {
|
||||
schema, ok := Lookup(CorrectionSetKey)
|
||||
if !ok {
|
||||
t.Fatalf("missing correction-set schema")
|
||||
}
|
||||
if len(schema.JSONSchema) == 0 {
|
||||
t.Fatalf("expected non-empty schema payload")
|
||||
}
|
||||
schema.JSONSchema[0] = 'x'
|
||||
|
||||
again, ok := Lookup(CorrectionSetKey)
|
||||
if !ok {
|
||||
t.Fatalf("missing correction-set schema on second lookup")
|
||||
}
|
||||
if len(again.JSONSchema) == 0 {
|
||||
t.Fatalf("unexpected empty schema payload")
|
||||
}
|
||||
if again.JSONSchema[0] != '{' {
|
||||
t.Fatalf("expected lookup to return independent schema copy")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user