# scriptorium Scriptorium is a generic prompt execution engine. It takes: - a prompt definition - a selected or default execution profile - named input artifacts - template variables - optional runtime overrides It returns: - for `run`: generated artifact, validation result, metadata - for `render`: prepared/rendered prompt data (no model output) ## Prompt vs Profile Scriptorium separates **what** to do (Prompt) from **how** to do it (Profile). ### Prompt Definition Defines the task logic and output contract. - Task description and version. - Message templates (system, user, etc.). - Required and optional input artifacts. - Output format and validation rules. - Repair settings for structured output. - Optional `default_profile` for convenience. ### Execution Profile Defines the runtime environment and model settings. - LLM endpoint (URL). - Model name. - Generation parameters: `temperature`, `max_tokens`, `top_p`. - Runtime settings: `timeout`, `reasoning_effort`. - API key source via `api_key_env`. Callers can explicitly provide a `profile_id` to override the prompt's `default_profile`. ## Precedence Scriptorium uses two precedence layers: ### Application Configuration Precedence For application-level adapter settings (for example prompt/profile/schema directories, server address, and render output default), precedence is: 1. **CLI Flags** 2. **`config.yml`** 3. **Built-in application defaults** Application config loading behavior: - Default config path: `/etc/scriptorium/config.yml` - Override path: `--config ` (supported by `run`, `render`, and `serve`) - If `--config` is provided, the file must exist and be valid. - If `--config` is omitted, missing `/etc/scriptorium/config.yml` is allowed. ### Runtime Model Precedence When resolving runtime model settings, Scriptorium follows this precedence model (highest to lowest): 1. **Runtime Overrides**: Provided via CLI flags or HTTP request `model` object. 2. **Execution Profile**: Settings defined in the selected profile. 3. **Application Defaults**: Built-in fallback values. ### Profile Selection Logic The engine determines which profile to use in this order: 1. Explicit `profile_id` (via `--profile` or HTTP request). 2. The `default_profile` named in the Prompt Definition. 3. Error: If neither is provided and no default exists. ## API Key Policy To ensure security, Scriptorium does not support raw API keys in configuration files, CLI arguments, or HTTP requests. - **`api_key_env`**: Profiles and overrides specify the name of an environment variable (e.g., `SCRIPTORIUM_API_KEY`). - **Runtime Resolution**: The value of the environment variable is read directly from the process environment at runtime. - **Zero Leakage**: API key values are never included in metadata, logs, or response bodies. ## CLI Usage Available CLI commands: - `scriptorium run` - `scriptorium render` - `scriptorium serve` All commands accept `--config `. `prompt_dir` and `profile_dir` may be supplied by CLI flags or `config.yml`: - `--prompt-dir` or `config.yml` `prompt_dir` - `--profile-dir` or `config.yml` `profile_dir` `schema_dir` and `serve` `addr` may also be supplied by `config.yml` where applicable: - `--schema-dir` or `config.yml` `schema_dir` - `--addr` or `config.yml` `server.addr` ### `scriptorium run` Runs a single prompt execution. **Required Flags:** - `--prompt`: The prompt ID to execute. - `--input`: Input mapping `name=path` (repeatable). **Required Effective Settings:** - Prompt directory: `--prompt-dir` or `config.yml` `prompt_dir` - Profile directory: `--profile-dir` or `config.yml` `profile_dir` **Optional Flags:** - `--config`: Application config file path. Default discovery path is `/etc/scriptorium/config.yml`. - `--prompt-dir`: Override prompt directory from config. - `--profile-dir`: Override profile directory from config. - `--profile`: Override the prompt's default profile. - `--var`: Template variable `name=value` (repeatable). - `--out`: Write output to a file instead of stdout. - `--llm-base-url`: Override endpoint. - `--model`: Override model name. - `--api-key-env`: Override API key environment variable name. - `--temperature`: Override temperature. - `--max-tokens`: Override max tokens. - `--top-p`: Override top_p. - `--timeout`: Override request timeout (e.g., `30s`, `1m`). - `--schema-dir`: Base directory for validation schemas. **Examples:** Using `config.yml` for prompt/profile directories: ```bash scriptorium run \ --prompt generic.markdown_summary \ --input transcript=./examples/fixtures/transcript.md ``` Overriding config directories explicitly: ```bash scriptorium run \ --prompt-dir ./prompts \ --profile-dir ./profiles \ --prompt generic.markdown_summary \ --input transcript=./examples/fixtures/transcript.md ``` Overriding the profile: ```bash scriptorium run \ --prompt-dir ./prompts \ --profile-dir ./profiles \ --prompt generic.markdown_summary \ --profile local-quality \ --input transcript=./examples/fixtures/transcript.md ``` Overriding model and runtime values: ```bash scriptorium run \ --prompt-dir ./prompts \ --profile-dir ./profiles \ --prompt generic.markdown_summary \ --model gpt-4o \ --temperature 0.7 \ --input transcript=./examples/fixtures/transcript.md ``` Using a local OpenAI-compatible vLLM endpoint: ```bash scriptorium run \ --prompt-dir ./prompts \ --profile-dir ./profiles \ --prompt generic.markdown_summary \ --llm-base-url http://localhost:8000/v1 \ --model meta-llama-3-8b \ --input transcript=./examples/fixtures/transcript.md ``` ### `scriptorium render` Prepares and renders a prompt without calling the LLM. `render` uses the same prompt/profile/input/variable/runtime override resolution as `run`: - Profile selection precedence: `--profile` -> prompt `default_profile` -> error. - Runtime precedence: CLI runtime overrides -> selected profile -> built-in defaults. `render` is useful for debugging: - prompt template rendering - input mappings - selected profile behavior - runtime override behavior `render` does not: - call the LLM - validate model output - perform repair - expose resolved API key values It may include `api_key_env` names where relevant. **Required Flags:** - `--prompt`: The prompt ID to render. - `--input`: Input mapping `name=path` (repeatable). **Required Effective Settings:** - Prompt directory: `--prompt-dir` or `config.yml` `prompt_dir` - Profile directory: `--profile-dir` or `config.yml` `profile_dir` **Optional Flags:** - `--config`: Application config file path. Default discovery path is `/etc/scriptorium/config.yml`. - `--prompt-dir`: Override prompt directory from config. - `--profile-dir`: Override profile directory from config. - `--profile`: Override the prompt's default profile. - `--var`: Template variable `name=value` (repeatable). - `--out`: Write output to a file instead of stdout. - `--format`: Render output format (`text` or `json`). Default: `text`. - `--llm-base-url`: Runtime override for endpoint. - `--model`: Runtime override for model name. - `--api-key-env`: Runtime override for API key environment variable name. - `--temperature`: Runtime override for temperature. - `--max-tokens`: Runtime override for max tokens. - `--top-p`: Runtime override for top_p. - `--timeout`: Runtime override for timeout (e.g., `30s`, `1m`). **Render Output Formats:** - `text`: Human-readable output (default). - `json`: Machine-readable structured output. Render formatting is modular; additional output formats can be added later without changing prepare/run core logic. **Examples:** Default text output using `config.yml` directories: ```bash scriptorium render \ --prompt generic.markdown_summary \ --input transcript=./examples/fixtures/transcript.md ``` Explicit config path: ```bash scriptorium render \ --config ./examples/config.yml \ --prompt generic.markdown_summary \ --input transcript=./examples/fixtures/transcript.md ``` Explicit directory overrides: ```bash scriptorium render \ --prompt-dir ./prompts \ --profile-dir ./profiles \ --prompt generic.markdown_summary \ --input transcript=./examples/fixtures/transcript.md ``` Explicit JSON output: ```bash scriptorium render \ --prompt-dir ./prompts \ --profile-dir ./profiles \ --prompt generic.markdown_summary \ --input transcript=./examples/fixtures/transcript.md \ --format json ``` Using prompt `default_profile` (omit `--profile`): ```bash scriptorium render \ --prompt-dir ./prompts \ --profile-dir ./profiles \ --prompt generic.markdown_summary \ --input transcript=./examples/fixtures/transcript.md ``` Overriding profile selection: ```bash scriptorium render \ --prompt-dir ./prompts \ --profile-dir ./profiles \ --prompt generic.markdown_summary \ --profile local-quality \ --input transcript=./examples/fixtures/transcript.md ``` Overriding runtime settings: ```bash scriptorium render \ --prompt-dir ./prompts \ --profile-dir ./profiles \ --prompt generic.markdown_summary \ --input transcript=./examples/fixtures/transcript.md \ --llm-base-url http://localhost:8000/v1 \ --model gpt-4o-mini \ --temperature 0.2 \ --max-tokens 800 \ --top-p 1.0 \ --timeout 45s ``` Writing rendered output to a file: ```bash scriptorium render \ --prompt-dir ./prompts \ --profile-dir ./profiles \ --prompt generic.markdown_summary \ --input transcript=./examples/fixtures/transcript.md \ --format text \ --out ./rendered_prompt.txt ``` ### `scriptorium serve` Starts the HTTP API. **Required Effective Settings:** - Prompt directory: `--prompt-dir` or `config.yml` `prompt_dir` - Profile directory: `--profile-dir` or `config.yml` `profile_dir` **Optional Flags:** - `--config`: Application config file path. Default discovery path is `/etc/scriptorium/config.yml`. - `--addr`: Listen address (default `:8080`). - `--schema-dir`: Base directory for validation schemas. **Examples:** Using `config.yml`: ```bash scriptorium serve ``` Overriding config for local use: ```bash scriptorium serve \ --prompt-dir ./prompts \ --profile-dir ./profiles \ --addr :9090 ``` ## HTTP API ### `POST /v1/runs` Executes a prompt. No built-in authentication is provided; deploy behind a trusted gateway. **Request Body:** ```json { "prompt_id": "generic.structured_events", "profile_id": "local-quality", "include_raw_output": false, "inputs": { "transcript": {"type": "file", "uri": "./examples/fixtures/transcript.md"} }, "vars": { "session_date": "2026-05-04" }, "model": { "endpoint": "http://localhost:8000/v1", "model": "gpt-4o-mini", "temperature": 0.0 } } ``` `profile_id` is optional. If omitted, Scriptorium uses the prompt's `default_profile`. If neither is available, the run fails. **Response:** Returns a `200 OK` with the generated artifact, validation results, and metadata including the `prompt_id` and the `selected_profile_id`. **Validation Failures:** If the model output fails validation (e.g., invalid JSON), the API returns `200 OK` with `validation.status = "failed"`. **Raw Output Exposure:** - `raw_model_output` is omitted by default. - Set `include_raw_output: true` in the request to include it in the response. - Raw output is preserved internally in run results regardless of HTTP exposure. ## Prompt Definition Authoring Prompts are defined in YAML. ### Canonical Shape ```yaml id: generic.structured_events version: "1.0.0" description: "Extracts structured events from a transcript" default_profile: local-quality inputs: - name: transcript required: true content_type: text/markdown description: "The raw session transcript" - name: glossary required: false content_type: application/yaml description: "Optional glossary terms" messages: - role: system content: "You are a helpful assistant." - role: user content_file: messages/extract_events.tmpl output: format: json validation_mode: json_schema schema_path: structured_events.schema.json repair_attempts: 2 ``` **Key Features:** - **Inline vs File**: Use `content` for short prompts or `content_file` for larger templates. Exactly one must be set per message. - **Path Resolution**: `content_file` paths are resolved relative to the prompt YAML file. - **Inputs**: Mark inputs as `required` to ensure the runner fails early if they are missing. - **Input Metadata**: `content_type` is currently descriptive metadata and not enforced yet. - **Validation**: Support `none`, `basic`, `json`, and `json_schema`. - **Repair**: `repair_attempts` enables bounded retries to fix structured output. ## Execution Profile Authoring Profiles are defined in YAML. ### Canonical Shape ```yaml id: local-quality endpoint: http://localhost:8000/v1 model: gpt-4o temperature: 0.0 max_tokens: 4096 top_p: 1.0 timeout_seconds: 300 reasoning_effort: high api_key_env: SCRIPTORIUM_API_KEY ``` **Constraints:** - **No Raw Keys**: Do not include actual API keys. Only specify the environment variable name in `api_key_env`. - **Local Profiles**: For local endpoints that don't require auth, `api_key_env` can be omitted. ## Examples - **Prompt Definitions**: `prompts/` - **Execution Profiles**: `profiles/` - **Schemas**: `schemas/` - **Fixtures**: `examples/fixtures/` ## Build and Test ```bash go build -o scriptorium ./cmd/scriptorium go test ./... ```