Add D&D spells response schema
This commit is contained in:
64
internal/framework/llm/assets/schemas/dnd_spells.v1.json
Normal file
64
internal/framework/llm/assets/schemas/dnd_spells.v1.json
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "notarius.dnd.spells",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["spell_casts"],
|
||||||
|
"properties": {
|
||||||
|
"spell_casts": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"caster",
|
||||||
|
"spell",
|
||||||
|
"effect",
|
||||||
|
"narrative_description",
|
||||||
|
"source_refs"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"caster": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"spell": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"effect": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"narrative_description": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"source_refs": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["source_id", "start_unit_id", "end_unit_id"],
|
||||||
|
"properties": {
|
||||||
|
"source_id": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"start_unit_id": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"end_unit_id": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ var schemaAssets embed.FS
|
|||||||
type ResponseSchemaKey string
|
type ResponseSchemaKey string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
DNDSpellsSchemaKey ResponseSchemaKey = "dnd_spells"
|
||||||
TestArtifactSchemaKey ResponseSchemaKey = "test_artifact"
|
TestArtifactSchemaKey ResponseSchemaKey = "test_artifact"
|
||||||
TestValidatorDecisionSchemaKey ResponseSchemaKey = "test_validator_decision"
|
TestValidatorDecisionSchemaKey ResponseSchemaKey = "test_validator_decision"
|
||||||
|
|
||||||
@@ -34,6 +35,13 @@ type ResponseSchema struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var responseSchemaRegistry = map[ResponseSchemaKey]ResponseSchema{
|
var responseSchemaRegistry = map[ResponseSchemaKey]ResponseSchema{
|
||||||
|
DNDSpellsSchemaKey: mustLoadResponseSchema(
|
||||||
|
DNDSpellsSchemaKey,
|
||||||
|
"notarius.dnd.spells",
|
||||||
|
schemaVersionV1,
|
||||||
|
"notarius_dnd_spells_v1",
|
||||||
|
"assets/schemas/dnd_spells.v1.json",
|
||||||
|
),
|
||||||
TestArtifactSchemaKey: mustLoadResponseSchema(
|
TestArtifactSchemaKey: mustLoadResponseSchema(
|
||||||
TestArtifactSchemaKey,
|
TestArtifactSchemaKey,
|
||||||
"notarius.test_artifact",
|
"notarius.test_artifact",
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLookupResponseSchemaSucceedsForTestSchemas(t *testing.T) {
|
func TestLookupResponseSchemaSucceedsForRegisteredSchemas(t *testing.T) {
|
||||||
tests := []ResponseSchemaKey{
|
tests := []ResponseSchemaKey{
|
||||||
|
DNDSpellsSchemaKey,
|
||||||
TestArtifactSchemaKey,
|
TestArtifactSchemaKey,
|
||||||
TestValidatorDecisionSchemaKey,
|
TestValidatorDecisionSchemaKey,
|
||||||
}
|
}
|
||||||
@@ -50,17 +51,22 @@ func TestMustLookupResponseSchemaPanicsForUnknownKey(t *testing.T) {
|
|||||||
|
|
||||||
func TestRegisteredResponseSchemasSortedByKey(t *testing.T) {
|
func TestRegisteredResponseSchemasSortedByKey(t *testing.T) {
|
||||||
schemas := RegisteredResponseSchemas()
|
schemas := RegisteredResponseSchemas()
|
||||||
if len(schemas) != 2 {
|
if len(schemas) != 3 {
|
||||||
t.Fatalf("expected two schemas, got %d", len(schemas))
|
t.Fatalf("expected three schemas, got %d", len(schemas))
|
||||||
}
|
}
|
||||||
|
|
||||||
keys := make([]string, len(schemas))
|
keys := make([]string, len(schemas))
|
||||||
|
seen := make(map[ResponseSchemaKey]bool, len(schemas))
|
||||||
for i, schema := range schemas {
|
for i, schema := range schemas {
|
||||||
keys[i] = string(schema.Key)
|
keys[i] = string(schema.Key)
|
||||||
|
seen[schema.Key] = true
|
||||||
}
|
}
|
||||||
if !sort.StringsAreSorted(keys) {
|
if !sort.StringsAreSorted(keys) {
|
||||||
t.Fatalf("expected sorted keys, got %v", keys)
|
t.Fatalf("expected sorted keys, got %v", keys)
|
||||||
}
|
}
|
||||||
|
if !seen[DNDSpellsSchemaKey] {
|
||||||
|
t.Fatalf("registered schemas = %v, want %q", keys, DNDSpellsSchemaKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResponseSchemaContentIsValidJSON(t *testing.T) {
|
func TestResponseSchemaContentIsValidJSON(t *testing.T) {
|
||||||
@@ -72,10 +78,12 @@ func TestResponseSchemaContentIsValidJSON(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestResponseSchemaJSONIsMutationSafe(t *testing.T) {
|
func TestResponseSchemaJSONIsMutationSafe(t *testing.T) {
|
||||||
first := MustLookupResponseSchema(TestArtifactSchemaKey)
|
for _, key := range []ResponseSchemaKey{DNDSpellsSchemaKey, TestArtifactSchemaKey} {
|
||||||
|
t.Run(string(key), func(t *testing.T) {
|
||||||
|
first := MustLookupResponseSchema(key)
|
||||||
first.JSONSchema[0] = '['
|
first.JSONSchema[0] = '['
|
||||||
|
|
||||||
second := MustLookupResponseSchema(TestArtifactSchemaKey)
|
second := MustLookupResponseSchema(key)
|
||||||
if !json.Valid(second.JSONSchema) {
|
if !json.Valid(second.JSONSchema) {
|
||||||
t.Fatalf("schema JSON was mutated: %s", second.JSONSchema)
|
t.Fatalf("schema JSON was mutated: %s", second.JSONSchema)
|
||||||
}
|
}
|
||||||
@@ -85,14 +93,16 @@ func TestResponseSchemaJSONIsMutationSafe(t *testing.T) {
|
|||||||
|
|
||||||
registered := RegisteredResponseSchemas()
|
registered := RegisteredResponseSchemas()
|
||||||
for i := range registered {
|
for i := range registered {
|
||||||
if registered[i].Key == TestArtifactSchemaKey {
|
if registered[i].Key == key {
|
||||||
registered[i].JSONSchema[0] = '['
|
registered[i].JSONSchema[0] = '['
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
again := MustLookupResponseSchema(TestArtifactSchemaKey)
|
again := MustLookupResponseSchema(key)
|
||||||
if !json.Valid(again.JSONSchema) || again.JSONSchema[0] == '[' {
|
if !json.Valid(again.JSONSchema) || again.JSONSchema[0] == '[' {
|
||||||
t.Fatalf("registered schema JSON did not use defensive copy")
|
t.Fatalf("registered schema JSON did not use defensive copy")
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResponseSchemaDiagnosticsMapOmitsRawSchemaContent(t *testing.T) {
|
func TestResponseSchemaDiagnosticsMapOmitsRawSchemaContent(t *testing.T) {
|
||||||
|
|||||||
48
internal/modules/extract/dnd/spells/schema_test.go
Normal file
48
internal/modules/extract/dnd/spells/schema_test.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package spells
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLookupResponseSchemaForSpells(t *testing.T) {
|
||||||
|
schema, ok := llm.LookupResponseSchema(llm.DNDSpellsSchemaKey)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("LookupResponseSchema(%q) ok = false, want true", llm.DNDSpellsSchemaKey)
|
||||||
|
}
|
||||||
|
if schema.ID != "notarius.dnd.spells" {
|
||||||
|
t.Fatalf("schema.ID = %q, want notarius.dnd.spells", schema.ID)
|
||||||
|
}
|
||||||
|
if schema.Version != SchemaVersion {
|
||||||
|
t.Fatalf("schema.Version = %q, want %q", schema.Version, SchemaVersion)
|
||||||
|
}
|
||||||
|
if schema.Name != "notarius_dnd_spells_v1" {
|
||||||
|
t.Fatalf("schema.Name = %q, want notarius_dnd_spells_v1", schema.Name)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(schema.SHA256, "sha256:") {
|
||||||
|
t.Fatalf("schema.SHA256 = %q, want sha256 prefix", schema.SHA256)
|
||||||
|
}
|
||||||
|
if !json.Valid(schema.JSONSchema) {
|
||||||
|
t.Fatalf("schema.JSONSchema is invalid JSON: %s", schema.JSONSchema)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponseSchemaDiagnosticsOmitRawSchema(t *testing.T) {
|
||||||
|
schema := llm.MustLookupResponseSchema(llm.DNDSpellsSchemaKey)
|
||||||
|
diagnostics := schema.DiagnosticsMap()
|
||||||
|
|
||||||
|
if diagnostics["key"] != llm.DNDSpellsSchemaKey {
|
||||||
|
t.Fatalf("diagnostics[key] = %#v, want %q", diagnostics["key"], llm.DNDSpellsSchemaKey)
|
||||||
|
}
|
||||||
|
for _, key := range []string{"id", "version", "name", "sha256"} {
|
||||||
|
if diagnostics[key] == "" {
|
||||||
|
t.Fatalf("diagnostics[%q] = %#v, want value", key, diagnostics[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := diagnostics["json_schema"]; ok {
|
||||||
|
t.Fatalf("diagnostics should omit raw schema content: %#v", diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user