Add framework contract test corpus

This commit is contained in:
2026-07-27 21:59:53 +00:00
parent 5edb24a9c1
commit 2bbf13e739
12 changed files with 187 additions and 23 deletions

View File

@@ -20,6 +20,21 @@ import (
"gitea.maximumdirect.net/eric/scriptorium"
)
const (
frameworkContractRoot = "./testdata/framework"
frameworkPromptDir = frameworkContractRoot + "/prompts"
frameworkProfileDir = frameworkContractRoot + "/profiles"
frameworkSchemaDir = frameworkContractRoot + "/schemas"
frameworkMarkdownSummaryPromptID = "contract.markdown_summary"
frameworkStructuredEventsPromptID = "contract.structured_events"
frameworkFastProfileID = "contract-fast"
frameworkQualityProfileID = "contract-quality"
frameworkTranscriptPath = frameworkContractRoot + "/fixtures/transcript.md"
frameworkGlossaryPath = frameworkContractRoot + "/fixtures/glossary.yml"
)
func TestNewEngineRejectsMissingPromptDir(t *testing.T) {
_, err := scriptorium.NewEngine(scriptorium.Config{ProfileDir: "./examples/profiles"})
if !errors.Is(err, scriptorium.ErrInvalidConfig) {
@@ -34,33 +49,88 @@ func TestNewEngineAcceptsMissingProfileDir(t *testing.T) {
}
}
func TestPrepareWorksWithExampleDirectoriesAndFileInputs(t *testing.T) {
engine := newExampleEngine(t)
prepared, err := engine.Prepare(context.Background(), scriptorium.RunRequest{
PromptID: "generic.markdown_summary",
Inputs: map[string]scriptorium.ArtifactRef{
"transcript": scriptorium.File("./examples/fixtures/transcript.md"),
"glossary": scriptorium.File("./examples/fixtures/glossary.yml"),
},
func TestPrepareWorksWithFrameworkContractCorpus(t *testing.T) {
engine, err := scriptorium.NewEngine(scriptorium.Config{
PromptDir: frameworkPromptDir,
ProfileDir: frameworkProfileDir,
SchemaDir: frameworkSchemaDir,
})
if err != nil {
t.Fatalf("expected prepare to succeed, got %v", err)
t.Fatalf("construct engine from framework contract corpus: %v", err)
}
if prepared.PromptID != "generic.markdown_summary" {
t.Fatalf("unexpected prompt id: %q", prepared.PromptID)
tests := []struct {
name string
promptID string
profileID string
model string
structured bool
}{
{
name: "markdown summary",
promptID: frameworkMarkdownSummaryPromptID,
profileID: frameworkFastProfileID,
model: "contract-fast-model",
},
{
name: "structured events",
promptID: frameworkStructuredEventsPromptID,
profileID: frameworkQualityProfileID,
model: "contract-quality-model",
structured: true,
},
}
if prepared.SelectedProfileID != "local-fast" {
t.Fatalf("unexpected selected profile: %q", prepared.SelectedProfileID)
}
if prepared.EffectiveModelParams.Model != "gpt-4o-mini" {
t.Fatalf("unexpected effective model: %q", prepared.EffectiveModelParams.Model)
}
if len(prepared.Messages) != 2 {
t.Fatalf("expected rendered messages, got %d", len(prepared.Messages))
}
if prepared.InputHashes["transcript"] == "" || prepared.InputHashes["glossary"] == "" {
t.Fatalf("expected input hashes, got %#v", prepared.InputHashes)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
prepared, err := engine.Prepare(context.Background(), scriptorium.RunRequest{
PromptID: tt.promptID,
Inputs: map[string]scriptorium.ArtifactRef{
"transcript": scriptorium.File(frameworkTranscriptPath),
"glossary": scriptorium.File(frameworkGlossaryPath),
},
})
if err != nil {
t.Fatalf("prepare framework contract prompt: %v", err)
}
if prepared.PromptID != tt.promptID {
t.Fatalf("unexpected prompt id: got %q, want %q", prepared.PromptID, tt.promptID)
}
if prepared.SelectedProfileID != tt.profileID {
t.Fatalf("unexpected selected profile: got %q, want %q", prepared.SelectedProfileID, tt.profileID)
}
if prepared.EffectiveModelParams.Model != tt.model {
t.Fatalf("unexpected effective model: got %q, want %q", prepared.EffectiveModelParams.Model, tt.model)
}
if len(prepared.Messages) != 2 {
t.Fatalf("expected rendered messages, got %d", len(prepared.Messages))
}
if !strings.Contains(prepared.Messages[1].Content, "Nia labels the archive.") {
t.Fatalf("expected relative prompt content to render the transcript, got %q", prepared.Messages[1].Content)
}
if prepared.InputHashes["transcript"] == "" || prepared.InputHashes["glossary"] == "" {
t.Fatalf("expected input hashes, got %#v", prepared.InputHashes)
}
if !tt.structured {
if prepared.StructuredOutput != nil {
t.Fatalf("expected no structured output specification, got %#v", prepared.StructuredOutput)
}
return
}
if prepared.StructuredOutput == nil || prepared.StructuredOutput.JSONSchema == nil {
t.Fatalf("expected loaded JSON Schema structured output, got %#v", prepared.StructuredOutput)
}
schema, ok := prepared.StructuredOutput.JSONSchema.Schema.(map[string]any)
if !ok || schema["type"] != "object" {
t.Fatalf("expected loaded object JSON Schema, got %#v", prepared.StructuredOutput.JSONSchema.Schema)
}
properties, ok := schema["properties"].(map[string]any)
if !ok || properties["events"] == nil {
t.Fatalf("expected loaded events schema property, got %#v", schema)
}
})
}
}

View File

@@ -0,0 +1,2 @@
archive: A catalogued collection of written records.
marker: A small label used to classify an entry.

View File

@@ -0,0 +1,2 @@
Nia labels the archive.
The archive receives a blue marker.

View File

@@ -0,0 +1,7 @@
id: contract-fast
endpoint: http://localhost:8000/v1
model: contract-fast-model
temperature: 0.2
max_tokens: 500
top_p: 1
timeout_seconds: 90

View File

@@ -0,0 +1,7 @@
id: contract-quality
endpoint: http://localhost:8000/v1
model: contract-quality-model
temperature: 0.1
max_tokens: 1000
top_p: 0.9
timeout_seconds: 120

View File

@@ -0,0 +1 @@
You summarize synthetic archive notes in clear Markdown.

View File

@@ -0,0 +1,7 @@
Summarize this transcript:
{{input "transcript"}}
Optional glossary:
{{input "glossary"}}

View File

@@ -0,0 +1,20 @@
id: contract.markdown_summary
version: "1.0.0"
default_profile: contract-fast
description: Summarize a synthetic transcript in Markdown.
inputs:
- name: transcript
required: true
content_type: text/markdown
- name: glossary
required: false
content_type: text/yaml
messages:
- role: system
content_file: ./contract.markdown_summary.system.md
- role: user
content_file: ./contract.markdown_summary.user.md
output:
format: markdown
validation_mode: basic
repair_attempts: 0

View File

@@ -0,0 +1 @@
Return only JSON that satisfies the requested event schema.

View File

@@ -0,0 +1,7 @@
Extract events from this transcript:
{{input "transcript"}}
Optional glossary:
{{input "glossary"}}

View File

@@ -0,0 +1,21 @@
id: contract.structured_events
version: "1.0.0"
default_profile: contract-quality
description: Extract synthetic events as structured JSON.
inputs:
- name: transcript
required: true
content_type: text/markdown
- name: glossary
required: false
content_type: text/yaml
messages:
- role: system
content_file: ./contract.structured_events.system.md
- role: user
content_file: ./contract.structured_events.user.md
output:
format: json
validation_mode: json_schema
schema_path: structured_events.schema.json
repair_attempts: 0

View File

@@ -0,0 +1,19 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["events"],
"properties": {
"events": {
"type": "array",
"items": {
"type": "object",
"required": ["title"],
"properties": {
"title": {"type": "string"}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
}