Centralize output schema and module key validation catalogs
This commit is contained in:
@@ -3,6 +3,8 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/audita/internal/core/modulecatalog"
|
||||
)
|
||||
|
||||
type WorkDirRetention string
|
||||
@@ -14,7 +16,7 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultModulesCSV = "glossary,homophones,glossary,spoken_word,grammar"
|
||||
DefaultModulesCSV = modulecatalog.KeyGlossary + "," + modulecatalog.KeyHomophones + "," + modulecatalog.KeyGlossary + "," + modulecatalog.KeySpokenWord + "," + modulecatalog.KeyGrammar
|
||||
DefaultOutputSchema = "bare-segments"
|
||||
DefaultPrimaryModel = "openrouter/google/gemma-4-31b-it"
|
||||
DefaultPrimaryBaseURL = "https://openrouter.ai/api/v1"
|
||||
|
||||
@@ -4,6 +4,9 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.maximumdirect.net/eric/audita/internal/core/modulecatalog"
|
||||
"gitea.maximumdirect.net/eric/audita/internal/core/outputschema"
|
||||
)
|
||||
|
||||
func TestDefaultConfigValues(t *testing.T) {
|
||||
@@ -318,6 +321,38 @@ func TestValidationFailures(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidationRejectsUnsupportedModuleKey(t *testing.T) {
|
||||
cfg := Default()
|
||||
cfg.Modules = []string{modulecatalog.KeyGlossary, "made_up"}
|
||||
|
||||
err := cfg.Validate()
|
||||
if err == nil {
|
||||
t.Fatalf("expected validation error for unsupported module key")
|
||||
}
|
||||
if !strings.Contains(err.Error(), `unsupported module key "made_up"`) {
|
||||
t.Fatalf("expected unsupported module key error, got %q", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidationAllowsRepeatedSupportedModuleKeys(t *testing.T) {
|
||||
cfg := Default()
|
||||
cfg.Modules = []string{modulecatalog.KeyGlossary, modulecatalog.KeyGlossary, modulecatalog.KeyGrammar}
|
||||
|
||||
if err := cfg.Validate(); err != nil {
|
||||
t.Fatalf("expected repeated supported module keys to validate, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidationAcceptsAllSupportedOutputSchemas(t *testing.T) {
|
||||
for _, schemaKey := range outputschema.SupportedKeys() {
|
||||
cfg := Default()
|
||||
cfg.OutputSchema = schemaKey
|
||||
if err := cfg.Validate(); err != nil {
|
||||
t.Fatalf("expected output schema %q to validate, got %v", schemaKey, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEffectiveValidationLLMInheritance(t *testing.T) {
|
||||
cfg := Default()
|
||||
cfg.PrimaryLLM.APIKey = "primary-key"
|
||||
|
||||
@@ -3,6 +3,9 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/audita/internal/core/modulecatalog"
|
||||
"gitea.maximumdirect.net/eric/audita/internal/core/outputschema"
|
||||
)
|
||||
|
||||
func (c Config) Validate() error {
|
||||
@@ -12,19 +15,19 @@ func (c Config) Validate() error {
|
||||
issues = append(issues, "modules must not be empty")
|
||||
}
|
||||
for _, module := range c.Modules {
|
||||
if strings.TrimSpace(module) == "" {
|
||||
moduleKey := strings.TrimSpace(module)
|
||||
if moduleKey == "" {
|
||||
issues = append(issues, "modules must not contain empty values")
|
||||
break
|
||||
}
|
||||
if !modulecatalog.IsSupported(moduleKey) {
|
||||
issues = append(issues, fmt.Sprintf("unsupported module key %q", moduleKey))
|
||||
}
|
||||
}
|
||||
if strings.TrimSpace(c.OutputSchema) == "" {
|
||||
issues = append(issues, "output schema must not be empty")
|
||||
} else {
|
||||
switch strings.TrimSpace(c.OutputSchema) {
|
||||
case "bare-segments", "audita-v1":
|
||||
default:
|
||||
issues = append(issues, fmt.Sprintf("unsupported output schema %q", c.OutputSchema))
|
||||
}
|
||||
} else if !outputschema.IsSupported(c.OutputSchema) {
|
||||
issues = append(issues, fmt.Sprintf("unsupported output schema %q", c.OutputSchema))
|
||||
}
|
||||
|
||||
if c.PrimaryLLM.TimeoutSeconds <= 0 {
|
||||
|
||||
35
internal/core/modulecatalog/catalog.go
Normal file
35
internal/core/modulecatalog/catalog.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package modulecatalog
|
||||
|
||||
import "strings"
|
||||
|
||||
const (
|
||||
KeyGlossary = "glossary"
|
||||
KeyHomophones = "homophones"
|
||||
KeySpokenWord = "spoken_word"
|
||||
KeyGrammar = "grammar"
|
||||
)
|
||||
|
||||
var supportedKeys = []string{
|
||||
KeyGlossary,
|
||||
KeyHomophones,
|
||||
KeySpokenWord,
|
||||
KeyGrammar,
|
||||
}
|
||||
|
||||
var supportedKeySet = map[string]struct{}{
|
||||
KeyGlossary: {},
|
||||
KeyHomophones: {},
|
||||
KeySpokenWord: {},
|
||||
KeyGrammar: {},
|
||||
}
|
||||
|
||||
func SupportedKeys() []string {
|
||||
out := make([]string, len(supportedKeys))
|
||||
copy(out, supportedKeys)
|
||||
return out
|
||||
}
|
||||
|
||||
func IsSupported(key string) bool {
|
||||
_, ok := supportedKeySet[strings.TrimSpace(key)]
|
||||
return ok
|
||||
}
|
||||
24
internal/core/modulecatalog/catalog_test.go
Normal file
24
internal/core/modulecatalog/catalog_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package modulecatalog
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSupportedKeys(t *testing.T) {
|
||||
want := []string{KeyGlossary, KeyHomophones, KeySpokenWord, KeyGrammar}
|
||||
if got := SupportedKeys(); !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected supported keys: got=%v want=%v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSupported(t *testing.T) {
|
||||
for _, key := range SupportedKeys() {
|
||||
if !IsSupported(key) {
|
||||
t.Fatalf("expected key %q to be supported", key)
|
||||
}
|
||||
}
|
||||
if IsSupported("made_up") {
|
||||
t.Fatalf("did not expect made_up to be supported")
|
||||
}
|
||||
}
|
||||
@@ -31,15 +31,31 @@ var definitions = map[string]Definition{
|
||||
},
|
||||
}
|
||||
|
||||
var supportedKeys = []string{
|
||||
SchemaBareSegments,
|
||||
SchemaAuditaV1,
|
||||
}
|
||||
|
||||
func SupportedKeys() []string {
|
||||
out := make([]string, len(supportedKeys))
|
||||
copy(out, supportedKeys)
|
||||
return out
|
||||
}
|
||||
|
||||
func IsSupported(key string) bool {
|
||||
_, ok := definitions[strings.TrimSpace(key)]
|
||||
return ok
|
||||
}
|
||||
|
||||
func Resolve(key string) (Definition, error) {
|
||||
normalized := strings.TrimSpace(key)
|
||||
if normalized == "" {
|
||||
return Definition{}, fmt.Errorf("output schema must not be empty")
|
||||
}
|
||||
def, ok := definitions[normalized]
|
||||
if !ok {
|
||||
if !IsSupported(normalized) {
|
||||
return Definition{}, fmt.Errorf("unsupported output schema %q", normalized)
|
||||
}
|
||||
def := definitions[normalized]
|
||||
return def, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package outputschema
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -56,3 +57,19 @@ func TestResolveUnknown(t *testing.T) {
|
||||
t.Fatalf("expected unsupported output schema error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSupportedKeysAndIsSupported(t *testing.T) {
|
||||
want := []string{SchemaBareSegments, SchemaAuditaV1}
|
||||
if got := SupportedKeys(); !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected supported schema keys: got=%v want=%v", got, want)
|
||||
}
|
||||
|
||||
for _, key := range want {
|
||||
if !IsSupported(key) {
|
||||
t.Fatalf("expected schema key %q to be supported", key)
|
||||
}
|
||||
}
|
||||
if IsSupported("seriatim-intermediate") {
|
||||
t.Fatalf("did not expect unsupported schema to be reported as supported")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user