# scriptorium Scriptorium is a generic prompt-definition execution engine written in Go. Given named input artifacts and a prompt definition, Scriptorium: 1. Loads the prompt definition. 2. Resolves input artifact references. 3. Renders prompt messages from templates. 4. Calls an OpenAI-compatible LLM endpoint. 5. Validates output if configured. 6. Optionally performs bounded structured-output repair. 7. Returns a generated artifact plus run metadata. ## Where Scriptorium Fits Scriptorium is not an orchestrator. In the D&D workflow: - Narratio orchestrates the full pipeline. - WhisperX transcribes audio. - Seriatim merges transcripts. - Audita polishes transcripts. - Scriptorium generates final artifacts from prepared inputs. D&D-specific behavior belongs in profiles, schemas, fixtures, and caller inputs, not in core Go logic. ## Core Concepts - Prompt definition: YAML config for templates, inputs, output format, and validation behavior. - Execution profile: conceptual runtime config (endpoint/model/timeouts/auth source). In this transition, execution settings are supplied as run-time overrides. - Named inputs: logical names (for example `transcript`, `glossary`) mapped to artifact references. - Artifact refs: currently `file` and `inline` are supported. - Template variables: key/value vars passed at run time and referenced as `{{.var_name}}`. - Execution target: endpoint/model plus generation/runtime parameters (`temperature`, `max_tokens`, `top_p`, `timeout_seconds`, `reasoning_effort`, `api_key_env`). - Output format: `text`, `markdown`, or `json`. - Validation mode: `none`, `basic`, `json`, `json_schema`. - Repair attempts: bounded retries for structured modes (`json`, `json_schema`) when output validation fails. ## Build and Test ```bash go build -o scriptorium ./cmd/scriptorium go test ./... ``` ## CLI Usage ### `scriptorium run` Required flags: - `--prompt-dir` - `--profile-dir` - `--prompt` - `--input` (repeatable `name=path`) Common optional flags: - `--profile` (execution profile selector; falls back to prompt `default_profile`) - `--var` (repeatable `name=value`) - `--out` - `--llm-base-url` - `--model` - `--api-key-env` - `--temperature` - `--max-tokens` - `--schema-dir` - `--timeout` Current transitional behavior: execution-profile loading is not implemented yet, so run-time execution settings must be supplied via overrides. In practice, provide at least endpoint and model (`--llm-base-url` and `--model`). Example: ```bash export SCRIPTORIUM_API_KEY="your-key" go run ./cmd/scriptorium run \ --prompt-dir ./prompts \ --profile-dir ./profiles \ --prompt generic.markdown_summary \ --profile local-fast \ --input transcript=./examples/fixtures/transcript.md \ --input glossary=./examples/fixtures/glossary.yml \ --llm-base-url http://localhost:8000/v1 \ --model gpt-4o-mini \ --api-key-env SCRIPTORIUM_API_KEY \ --out ./out.md ``` Output behavior: - Artifact content goes to stdout unless `--out` is set. - Summaries and errors are written to stderr. - Exit code `2` means the run succeeded but validation status is `failed`. ### `scriptorium serve` Starts HTTP API. Required flags: - `--prompt-dir` - `--profile-dir` Common optional flags: - `--addr` (default `:8080`) - `--schema-dir` (default `.`) - `--model` - `--timeout` (default `10m`) ## HTTP API Endpoint: - `POST /v1/runs` No built-in authentication is provided by the server itself. Deploy behind a trusted boundary or gateway. Request example: ```json { "prompt_id": "generic.structured_events", "prompt_version": "1.0.0", "profile_id": "local-default", "inputs": { "transcript": {"type": "file", "uri": "./examples/fixtures/transcript.md"}, "glossary": {"type": "file", "uri": "./examples/fixtures/glossary.yml"} }, "vars": { "session_date": "2026-05-04" }, "model": { "endpoint": "http://localhost:8000/v1", "model": "gpt-4o-mini", "temperature": 0.0, "max_tokens": 600, "top_p": 1.0, "timeout_seconds": 120, "api_key_env": "SCRIPTORIUM_API_KEY" } } ``` Response shape: ```json { "artifact": { "name": "output", "content_type": "application/json", "body": "{...}", "uri": "", "size": 123, "hash": "..." }, "validation": { "status": "passed", "mode": "json_schema", "errors": [], "schema_path": "structured_events.schema.json", "repair_attempts": 0, "is_valid": true }, "metadata": { "run_id": "xxxxxxxx-xxxx-4xxx-8xxx-xxxxxxxxxxxx", "prompt_id": "generic.structured_events", "prompt_version": "1.0.0", "prompt_hash": "...", "rendered_prompt_hash": "...", "selected_profile_id": "local-default", "model_name": "gpt-4o-mini", "endpoint": "http://localhost:8000/v1", "model_params": { "endpoint": "http://localhost:8000/v1", "model": "gpt-4o-mini", "temperature": 0, "max_tokens": 600, "top_p": 1, "timeout_seconds": 120, "api_key_env": "SCRIPTORIUM_API_KEY" }, "input_hashes": {"transcript": "...", "glossary": "..."}, "usage": {"prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30}, "start_time": "...", "end_time": "...", "duration_ms": 1523, "validation_mode": "json_schema", "validation_status": "passed", "repair_attempts_used": 0 }, "raw_model_output": "{...}" } ``` Validation content failures return `200` with `validation.status = "failed"` and preserve `raw_model_output`. Error response shape: ```json { "error": { "code": "artifact_read_failed", "message": "failed to read input artifact" } } ``` ## Prompt Definition Authoring ### Minimal Markdown prompt definition ```yaml id: generic.markdown_summary version: "1.0.0" default_profile: local-default inputs: - name: transcript required: true templates: - role: system content: "You are a concise assistant." - role: user content: | Summarize: {{input "transcript"}} output_format: markdown validation: validation_mode: basic ``` ### Structured JSON prompt definition with schema validation ```yaml id: generic.structured_events version: "1.0.0" default_profile: local-default inputs: - name: transcript required: true templates: - role: system content: "Return only JSON." - role: user content: | Extract events from: {{input "transcript"}} output_format: json validation: format: json validation_mode: json_schema schema_path: structured_events.schema.json repair_attempts: 1 ``` `repair_attempts` is strictly bounded and only applies to structured validation modes. ## Validation Modes - `none`: skipped validation result. - `basic`: fails for empty/whitespace output. - `json`: output must parse as JSON. - `json_schema`: output must parse as JSON and satisfy configured schema. Validation content failures are returned in the structured result; raw model output is preserved. ## Examples - Prompt definitions: `prompts/` - Execution profiles: `profiles/` - Schemas: `schemas/` - Fixtures: `examples/fixtures/` - Local experimentation: `local-test/` ## Development Notes - Core follows ports-and-adapters and remains domain-generic. - Domain/usecase packages do not depend on HTTP/CLI wire DTOs. - To add a new LLM adapter: implement `internal/llm.Client`. - To add a new artifact reader: extend `internal/artifact.Reader` routing. - To add a new validation mode: extend `internal/validate` and preserve run semantics.