Consolidate external documentation contracts

This commit is contained in:
2026-07-26 14:16:09 +00:00
parent 6d1fb66dd7
commit c927b7819d
7 changed files with 535 additions and 1278 deletions

View File

@@ -2,296 +2,142 @@
This is the canonical public HTTP contract for Scriptorium. This is the canonical public HTTP contract for Scriptorium.
Implemented route: ## Service And Route
- `POST /v1/runs` `POST /v1/runs` runs one prompt request and returns generated output,
validation, and metadata. The service has no built-in authentication or
authorization; deploy it behind appropriate network and authentication controls.
For CLI behavior, see [CLI reference](cli.md). For config and prompt/profile The service address and HTTP limits are configured as described in the
file formats, see [Configuration reference](config.md). [configuration reference](config.md). `serve` invocation is defined in the
[CLI reference](cli.md).
The maintained request-shape example is `examples/http-run.json`. It requires a Requests and responses are JSON objects. Requests are decoded as JSON regardless
running `serve` process with an artifact root that can read the referenced of their `Content-Type`; successful JSON responses use
files, plus a reachable model endpoint for full execution. `Content-Type: application/json`. There are no query parameters.
## Base URL And Deployment
`scriptorium serve` listens on `server.addr` or `serve --addr`. The default is
`:8080`.
The route path is always:
```text
/v1/runs
```
The HTTP adapter has no built-in authentication or authorization. Deploy it
behind trusted network and authentication controls.
## Media Types
- Request body: JSON object.
- Response body: JSON object.
- Response `Content-Type`: `application/json`.
Requests are decoded as JSON regardless of the request `Content-Type` header.
There are no shared query parameters.
## Request Limits ## Request Limits
HTTP limits are configured through `server.*` config fields or `serve` flags: The configured request-body limit includes inline artifact bodies. The artifact
limit applies to HTTP `file` inputs. The response limit applies to the encoded
response, including the artifact body and optional raw output. A limit of zero
disables that limit.
- `server.max_request_bytes`: encoded JSON request body limit, including inline input bodies. A request body over its limit returns `413 request_too_large`; an oversized
- `server.max_artifact_bytes`: file artifact limit for HTTP `file` input references. file input returns `413 artifact_too_large`; an oversized encoded response
- `server.max_response_bytes`: encoded JSON response limit, including artifact body and optional raw output. returns `413 response_too_large`.
Each limit defaults to `16777216` bytes. `0` disables that limit.
## `POST /v1/runs` ## `POST /v1/runs`
Runs one prompt request and returns the generated artifact, validation result,
and metadata.
### Request Body ### Request Body
The maintained [request example](../examples/http-run.json) is a complete
copyable shape. The smallest valid shape is:
```json ```json
{ {
"prompt_id": "generic.markdown_summary", "prompt_id": "generic.markdown_summary",
"profile_id": "local-fast",
"prompt_version": "1.0.0",
"inputs": { "inputs": {
"transcript": { "transcript": {"type": "inline", "body": "Source text"}
"type": "file", }
"uri": "./examples/fixtures/transcript.md"
},
"glossary": {
"type": "inline",
"body": "party:\n - Rin"
}
},
"vars": {
"session_date": "2026-05-04"
},
"model": {
"endpoint": "http://localhost:8000/v1",
"model": "gpt-4o-mini",
"temperature": 0,
"max_tokens": 800,
"top_p": 1,
"timeout_seconds": 120,
"service_tier": "priority",
"reasoning_effort": "medium",
"api_key_env": "SCRIPTORIUM_API_KEY",
"extra_params": {
"provider_option": "enabled"
}
},
"include_raw_output": false
} }
``` ```
Request fields: | Field | Required | Meaning |
| Field | Required | Description |
| --- | --- | --- | | --- | --- | --- |
| `prompt_id` | yes | Prompt ID. Must not be blank. | | `prompt_id` | yes | Non-blank prompt ID. |
| `prompt_version` | no | Prompt version filter. | | `prompt_version` | no | Prompt version filter. |
| `profile_id` | no | Execution profile ID. If omitted, the prompt must define `default_profile`. | | `profile_id` | no | Execution-profile ID; otherwise the prompt must set `default_profile`. |
| `inputs` | yes | Object mapping prompt input names to input references. Must contain at least one entry. | | `inputs` | yes | Non-empty object mapping input names to references. |
| `vars` | no | Object mapping template variable names to string values. | | `vars` | no | Object mapping template-variable names to strings. |
| `model` | no | Runtime model override object. | | `model` | no | Runtime model-override object. |
| `include_raw_output` | no | When `true`, include `raw_model_output` in the response. | | `include_raw_output` | no | Include `raw_model_output` when true. |
Input reference fields: An input reference has a required `type` of `file` or `inline`. A `file`
reference requires `uri`; an `inline` reference requires `body`.
| Field | Required | Description | HTTP file references require a configured artifact root. Relative paths resolve
| --- | --- | --- | within that root. Absolute paths must be lexically within it; traversal outside
| `type` | yes | `file` or `inline`. | it is rejected with `400 artifact_not_allowed`. This lexical check does not
| `uri` | for `file` | File URI/path. | resolve symlinks: the operating system follows symlinks inside the root,
| `body` | for `inline` | Inline artifact body. | including ones that target outside it. Keep the root narrow and inaccessible to
untrusted writers.
HTTP `file` references require `server.artifact_root` or `serve The optional `model` object accepts `endpoint`, `model`, `temperature`,
--artifact-root`. Relative file URIs resolve against that root. Absolute file `max_tokens`, `top_p`, `timeout_seconds`, `service_tier`,
URIs are accepted only when lexically inside the root. Relative traversal and `reasoning_effort`, `api_key_env`, and `extra_params`. Numeric ranges and
absolute paths outside the root return `400 artifact_not_allowed`. credential supply are defined by the [configuration reference](config.md).
Explicit zero values for the numeric fields are overrides; zero
`timeout_seconds` disables the outbound client timeout.
The containment check is lexical and does not resolve symlinks. Symlinks inside Raw API-key values are not accepted. `api_key` and any other unknown model
the artifact root are followed by the operating system, including symlinks that field cause `400 invalid_json`.
point outside the root. Keep the artifact root narrow and not writable by
untrusted users.
Model override fields: ### Strict JSON
| Field | Description | Request decoding rejects malformed JSON, unknown fields at every request level,
| --- | --- | and trailing JSON tokens with `400 invalid_json`. A blank `prompt_id` or
| `endpoint` | Runtime endpoint override. | empty `inputs` object returns `400 invalid_request`.
| `model` | Runtime model override. |
| `temperature` | Number in range `0..2`. Explicit `0` is an override. |
| `max_tokens` | Integer greater than or equal to `0`. Explicit `0` is an override. |
| `top_p` | Number in range `0..1`. Explicit `0` is an override. |
| `timeout_seconds` | Integer greater than or equal to `0`. Explicit `0` disables the outbound client timeout. |
| `service_tier` | Provider-specific request tier. |
| `reasoning_effort` | Provider-specific reasoning setting. |
| `api_key_env` | Name of an environment variable containing the API key. |
| `extra_params` | JSON-compatible provider-specific top-level request fields. |
Raw API-key values are not accepted in HTTP payloads. A field such as
`api_key` is rejected as unknown JSON.
`extra_params` keys must not be empty and must not collide with reserved
outbound fields: `model`, `session_id`, `messages`, `temperature`,
`max_tokens`, `top_p`, `service_tier`, `reasoning_effort`, or
`response_format`.
### Strict JSON Rules
Request decoding is strict:
- malformed JSON returns `400 invalid_json`
- unknown request fields return `400 invalid_json`
- unknown `inputs` item fields return `400 invalid_json`
- unknown `model` fields return `400 invalid_json`
- trailing JSON tokens after the request object return `400 invalid_json`
- request bodies above the configured limit return `413 request_too_large`
### Success Response ### Success Response
Status: `200 OK` A completed run returns `200 OK`, including when generated content fails its
validation contract. The response contains:
```json - `artifact`: `name`, `content_type`, `body`, `size`, `hash`, and
{ optional `uri`;
"artifact": { - `validation`: `status`, `mode`, `repair_attempts`, `is_valid`, plus
"name": "output", optional `errors` and `schema_path`;
"content_type": "text/markdown", - `metadata`: run, prompt, rendered-prompt, profile, model, input-hash, usage,
"body": "Generated content", timing, validation, and repair-attempt metadata; and
"size": 17, - optional `raw_model_output` when requested.
"hash": "..."
},
"validation": {
"status": "passed",
"mode": "basic",
"repair_attempts": 0,
"is_valid": true
},
"metadata": {
"run_id": "...",
"prompt_id": "generic.markdown_summary",
"prompt_version": "1.0.0",
"prompt_hash": "...",
"rendered_prompt_hash": "...",
"selected_profile_id": "local-fast",
"model_name": "gpt-4o-mini",
"endpoint": "http://localhost:8000/v1",
"model_params": {
"endpoint": "http://localhost:8000/v1",
"model": "gpt-4o-mini",
"temperature": 0.2,
"max_tokens": 500,
"top_p": 1,
"timeout_seconds": 90
},
"input_hashes": {
"transcript": "..."
},
"usage": {
"prompt_tokens": 11,
"completion_tokens": 22,
"total_tokens": 33,
"cached_tokens": 0,
"cache_write_tokens": 0
},
"start_time": "2026-05-04T12:00:00Z",
"end_time": "2026-05-04T12:00:01Z",
"duration_ms": 1000,
"validation_mode": "basic",
"validation_status": "passed",
"repair_attempts_used": 0
}
}
```
Response fields: `metadata.model_params` has `endpoint`, `model`, `temperature`,
`max_tokens`, `top_p`, and `timeout_seconds`, plus optional
`service_tier`, `reasoning_effort`, `api_key_env`, and `extra_params`.
`metadata.usage` always includes `prompt_tokens`, `completion_tokens`,
`total_tokens`, `cached_tokens`, and `cache_write_tokens`; unavailable
cache usage is reported as zero.
- `artifact`: generated output artifact. A validation failure has `validation.status: "failed"`, `is_valid: false`,
- `validation`: validation result for the generated artifact. and any available diagnostic errors, while still returning the artifact and
- `metadata`: run and effective runtime metadata. metadata.
- `raw_model_output`: omitted unless `include_raw_output` is `true`.
`artifact.uri` is omitted when empty. `validation.errors` and
`validation.schema_path` are omitted when empty. `model_params.service_tier`,
`model_params.reasoning_effort`, `model_params.api_key_env`, and
`model_params.extra_params` are omitted when empty.
`metadata.usage.cached_tokens` and `metadata.usage.cache_write_tokens` are
always present as numbers. They are `0` when the provider omits compatible cache
usage fields or reports no cache activity.
### Validation Failure Response
Generated-content validation failures still return `200 OK`.
```json
{
"validation": {
"status": "failed",
"mode": "json",
"errors": ["invalid JSON: ..."],
"repair_attempts": 0,
"is_valid": false
}
}
```
The response still includes `artifact` and `metadata`.
## Error Responses ## Error Responses
Error body shape: Errors have this shape:
```json ```json
{ {"error":{"code":"invalid_request","message":"prompt_id is required"}}
"error": {
"code": "invalid_request",
"message": "prompt_id is required"
}
}
``` ```
Current status/code mapping: Messages are concise and do not expose wrapped internal causes.
| Status | Code | Meaning | | Status | Code | Meaning |
| --- | --- | --- | | --- | --- | --- |
| `400` | `invalid_json` | Malformed JSON, unknown JSON field, or trailing JSON token. | | `400` | `invalid_json` | Malformed JSON, unknown field, or trailing JSON. |
| `400` | `invalid_request` | Missing/invalid request fields or invalid runtime overrides. | | `400` | `invalid_request` | Missing or invalid request data or runtime override. |
| `400` | `profile_required` | No `profile_id` and prompt has no `default_profile`. | | `400` | `profile_required` | No profile ID and no prompt default profile. |
| `400` | `prompt_load_failed` | Prompt definition YAML/contract failed to load. | | `400` | `prompt_load_failed` | Prompt definition failed to load. |
| `400` | `profile_load_failed` | Profile YAML/contract failed to load, including raw `api_key`. | | `400` | `profile_load_failed` | Profile failed to load. |
| `400` | `artifact_not_allowed` | HTTP file refs are disabled or requested path is outside artifact root. | | `400` | `artifact_not_allowed` | HTTP file input is disabled or outside the artifact root. |
| `400` | `artifact_read_failed` | Input artifact could not be read or input ref was unsupported/invalid. | | `400` | `artifact_read_failed` | Input artifact is invalid or cannot be read. |
| `400` | `prompt_render_failed` | Prompt template rendering failed. | | `400` | `prompt_render_failed` | Prompt template rendering failed. |
| `400` | `api_key_env_missing` | Selected `api_key_env` variable is unset or empty. | | `400` | `api_key_env_missing` | The selected credential environment variable is unset or empty. |
| `404` | `not_found` | Route path is unknown. | | `404` | `not_found` | Route does not exist. |
| `404` | `prompt_not_found` | Prompt ID/version was not found. | | `404` | `prompt_not_found` | Prompt ID or version does not exist. |
| `404` | `profile_not_found` | Profile ID was not found. | | `404` | `profile_not_found` | Profile ID does not exist. |
| `405` | `method_not_allowed` | Method is not `POST` on `/v1/runs`. | | `405` | `method_not_allowed` | The route does not accept the method. |
| `413` | `request_too_large` | Encoded JSON request body exceeds configured request limit. | | `413` | `request_too_large` | Encoded request exceeds its limit. |
| `413` | `artifact_too_large` | HTTP file input artifact exceeds configured artifact limit. | | `413` | `artifact_too_large` | File input exceeds its limit. |
| `413` | `response_too_large` | Encoded JSON response exceeds configured response limit. | | `413` | `response_too_large` | Encoded response exceeds its limit. |
| `500` | `validation_runtime_failed` | Validator runtime/schema loading failed. | | `500` | `validation_runtime_failed` | Schema or validator runtime failure. |
| `500` | `internal_error` | Unclassified server error. | | `500` | `internal_error` | Unclassified server failure. |
| `502` | `llm_failed` | Outbound model request failed. | | `502` | `llm_failed` | Outbound model request failed. |
HTTP error messages are intentionally concise and do not include sensitive
internal causes.
## Retry And Idempotency ## Retry And Idempotency
Scriptorium does not provide idempotency keys, pagination, caching headers, or Scriptorium provides no idempotency keys, pagination, caching headers, or rate
rate limiting. limits. Clients may retry transport failures or `5xx` responses only when
their workflow tolerates another model call: a retry can produce different
Clients may retry transport failures or `5xx` responses when their surrounding output and incur another provider request.
workflow can tolerate another model call. A retry can generate different output
and incur another provider request.
## Example File
- `examples/http-run.json`

View File

@@ -1,5 +1,10 @@
# CLI Reference # CLI Reference
This is the canonical contract for invoking Scriptorium. Configuration discovery,
precedence, directories, profiles, and schemas are defined in the
[configuration reference](config.md). The [HTTP API reference](api.md) owns
service request and response behavior.
## Shortest Useful Command ## Shortest Useful Command
```bash ```bash
@@ -10,224 +15,132 @@ go run ./cmd/scriptorium render \
--input glossary=./examples/fixtures/glossary.yml --input glossary=./examples/fixtures/glossary.yml
``` ```
`render` prepares the prompt, loads input artifacts, resolves the execution `render` prepares a request without calling an LLM.
profile, and prints the prepared request without calling an LLM.
## Command Overview ## Commands
- `scriptorium run`: prepare a prompt, call the configured LLM, write generated output, and print a run summary. - `scriptorium run`: prepare a prompt, call the configured LLM, and write the
- `scriptorium render`: prepare a prompt only; write prepared-run output as `text` or `json`. generated artifact.
- `scriptorium serve`: start the HTTP server for `POST /v1/runs`. - `scriptorium render`: prepare a prompt and write prepared-run output.
- `scriptorium serve`: start the HTTP server.
Canonical related references: All commands accept `--config <path>` and reject positional arguments. An
effective `prompt_dir` is required for every command. Supply it through the
configuration contract or the command's `--prompt-dir` flag.
- [Configuration reference](config.md) ## `scriptorium run`
- [HTTP API reference](api.md)
- [Subprocess integration](integrations/subprocess.md)
## Common Rules ```text
- `--config` is supported by `run`, `render`, and `serve`.
- Positional arguments are rejected.
- `run` and `render` require `--prompt`, at least one `--input`, and an effective `prompt_dir`.
- `serve` requires an effective `prompt_dir`.
- `profile_dir` is optional. Without it, only built-in profiles are available.
- If `profile_dir` is set, custom profiles override built-in profiles with the same ID.
- Prompt cache control, `session_id`, structured output, and provider-specific profile fields are configured in YAML, not with CLI flags.
Config precedence is:
1. built-in defaults
2. config file values
3. CLI flags
## Flag Reference
### `scriptorium run`
```bash
scriptorium run [flags] scriptorium run [flags]
``` ```
Required through flags or config: Required flags:
- `--prompt-dir <dir>`: prompt definition directory. | Flag | Meaning |
| --- | --- |
Required as flags: | `--prompt <id>` | Prompt ID to execute. |
| `--input name=path` | Input file mapping; repeat or use comma-separated mappings. |
- `--prompt <id>`: prompt ID to execute.
- `--input name=path`: input file mapping. Repeat or use comma-separated mappings.
Optional flags: Optional flags:
- `--config <path>`: application config file. | Flag | Meaning |
- `--profile-dir <dir>`: custom profile definition directory. | --- | --- |
- `--schema-dir <dir>`: schema base directory for `json_schema` validation. | `--config <path>` | Application configuration file. |
- `--profile <id>`: execution profile override. If omitted, the prompt `default_profile` is used. | `--prompt-dir <dir>` | Prompt-definition directory override. |
- `--var name=value`: template variable mapping. Repeat or use comma-separated mappings. | `--profile-dir <dir>` | Custom profile-directory override. |
- `--out <path>`: write generated artifact body to a file instead of stdout. | `--schema-dir <dir>` | Schema base-directory override. |
- `--llm-base-url <url>`: runtime endpoint override. | `--profile <id>` | Execution-profile override. |
- `--model <name>`: runtime model override. | `--var name=value` | Template-variable mapping; repeat or use comma-separated mappings. |
- `--api-key-env <name>`: runtime API-key environment variable name override. | `--out <path>` | Write generated content to this file instead of stdout. |
- `--temperature <float>`: runtime temperature override. | `--llm-base-url <url>` | Runtime endpoint override. |
- `--max-tokens <int>`: runtime max tokens override. | `--model <name>` | Runtime model override. |
- `--top-p <float>`: runtime top-p override. | `--api-key-env <name>` | Runtime API-key environment-variable name override. |
- `--timeout <duration>`: runtime timeout override using Go duration syntax, such as `30s` or `2m`. | `--temperature <float>` | Runtime temperature override. |
| `--max-tokens <int>` | Runtime maximum-token override. |
| `--top-p <float>` | Runtime top-p override. |
| `--timeout <duration>` | Runtime timeout override using Go duration syntax. |
Deprecated aliases: Deprecated aliases: `--prompt-id` for `--prompt`, and `--profile-id` for
`--profile`.
- `--prompt-id <id>`: alias for `--prompt`. Omitted numeric runtime flags preserve the selected effective value; explicit
- `--profile-id <id>`: alias for `--profile`. zero values override it. `--timeout 0s` disables the outbound HTTP-client
timeout. CLI durations are converted to whole seconds by truncation toward
zero, so any duration whose absolute value is below one second becomes an
explicit zero-second override.
Runtime override notes: There is no raw API-key flag. Use `--api-key-env`.
- Omitted numeric override flags preserve the selected profile/default value. ## `scriptorium render`
- Explicit zero values override the selected profile/default value.
- `--timeout 0s` disables the outbound HTTP client timeout for that request.
- There is no raw API-key flag; use `--api-key-env`.
### `scriptorium render` ```text
```bash
scriptorium render [flags] scriptorium render [flags]
``` ```
Required through flags or config: `--prompt <id>` and at least one `--input name=path` are required. The
following optional flags are supported: `--config`, `--prompt-dir`,
`--profile-dir`, `--profile`, `--var`, `--out`, `--llm-base-url`,
`--model`, `--api-key-env`, `--temperature`, `--max-tokens`, `--top-p`,
`--timeout`, and `--format text|json`. Their meanings match the corresponding
`run` flags; `--format` selects prepared-run output and otherwise uses
`defaults.render_format`.
- `--prompt-dir <dir>`: prompt definition directory. The same deprecated aliases and numeric/timeout behavior as `run` apply.
`render` does not accept `--schema-dir`; configure `schema_dir` through the
configuration file. It resolves profiles and schemas as part of preparation but
does not call an LLM.
Required as flags: ## `scriptorium serve`
- `--prompt <id>`: prompt ID to render. ```text
- `--input name=path`: input file mapping. Repeat or use comma-separated mappings.
Optional flags:
- `--config <path>`: application config file.
- `--prompt-dir <dir>`: prompt definition directory.
- `--profile-dir <dir>`: custom profile definition directory.
- `--profile <id>`: execution profile override.
- `--var name=value`: template variable mapping. Repeat or use comma-separated mappings.
- `--out <path>`: write prepared-run output to a file instead of stdout.
- `--llm-base-url <url>`: runtime endpoint override for the prepared request.
- `--model <name>`: runtime model override for the prepared request.
- `--api-key-env <name>`: runtime API-key environment variable name override.
- `--temperature <float>`: runtime temperature override.
- `--max-tokens <int>`: runtime max tokens override.
- `--top-p <float>`: runtime top-p override.
- `--timeout <duration>`: runtime timeout override using Go duration syntax.
- `--format text|json`: prepared-run output format. Defaults to config `defaults.render_format`, then `text`.
Deprecated aliases:
- `--prompt-id <id>`: alias for `--prompt`.
- `--profile-id <id>`: alias for `--profile`.
Notes:
- `render` resolves profiles, loads schemas for `json_schema` prompts, and validates `api_key_env`.
- `render` does not accept `--schema-dir`; use config `schema_dir` for render-time schema lookup.
- `render` does not call the LLM.
### `scriptorium serve`
```bash
scriptorium serve [flags] scriptorium serve [flags]
``` ```
Required through flags or config:
- `--prompt-dir <dir>`: prompt definition directory.
Optional flags: Optional flags:
- `--config <path>`: application config file. | Flag | Meaning |
- `--addr <listen-address>`: HTTP listen address. | --- | --- |
- `--prompt-dir <dir>`: prompt definition directory. | `--config <path>` | Application configuration file. |
- `--profile-dir <dir>`: custom profile definition directory. | `--addr <listen-address>` | HTTP listen-address override. |
- `--schema-dir <dir>`: schema base directory for `json_schema` validation. | `--prompt-dir <dir>` | Prompt-definition directory override. |
- `--artifact-root <dir>`: base directory for HTTP `file` input references. | `--profile-dir <dir>` | Custom profile-directory override. |
- `--max-request-bytes <n>`: maximum HTTP request body bytes; `0` disables the limit. | `--schema-dir <dir>` | Schema base-directory override. |
- `--max-artifact-bytes <n>`: maximum HTTP file artifact bytes; `0` disables the limit. | `--artifact-root <dir>` | Root for HTTP `file` input references. |
- `--max-response-bytes <n>`: maximum encoded HTTP response body bytes; `0` disables the limit. | `--max-request-bytes <n>` | Maximum encoded HTTP request-body bytes; `0` disables the limit. |
| `--max-artifact-bytes <n>` | Maximum HTTP file-input artifact bytes; `0` disables the limit. |
| `--max-response-bytes <n>` | Maximum encoded HTTP response bytes; `0` disables the limit. |
Notes: `serve` accepts no runtime model override flags. HTTP request fields, response
schemas, and error codes are defined in the [HTTP API reference](api.md).
- `serve` does not accept runtime model override flags such as `--model` or `--llm-base-url`.
- HTTP request fields and error codes are documented in the [HTTP API reference](api.md).
- HTTP `file` input references are rejected unless an artifact root is configured.
- HTTP size-limit flags affect only `serve`.
## Input And Variable Syntax ## Input And Variable Syntax
- `--input name=path` maps prompt input names to local file paths. `--input name=path` maps an input name to a local file; `--var name=value`
- `--var name=value` maps prompt template variables to string values. maps a template variable to a string. Both flags can be repeated or contain
- Both flags can be repeated. comma-separated mappings. Values may contain `=` after the first separator.
- Both flags also accept comma-separated mappings, such as `--input transcript=./t.md,glossary=./g.yml`. Empty names and values are rejected.
- Values may contain `=` after the first separator, such as `--var note=a=b=c`.
- Empty names and empty values are rejected.
CLI `run` and `render` convert every `--input` mapping to a `file` artifact CLI inputs are file references. HTTP inline inputs are defined by the
reference. HTTP also supports `inline` input references; see [HTTP API [HTTP API reference](api.md).
reference](api.md).
## Output Behavior ## Output And Exit Behavior
`run`: - `run` writes generated content to stdout, or to `--out` when supplied, and
writes a concise summary to stderr.
- `render` writes prepared-run output to stdout, or to `--out` when supplied,
without a success summary.
- `serve` writes startup and server errors to stderr.
- Writes generated artifact content to stdout by default. Exit statuses:
- Writes generated artifact content to `--out` when provided.
- Prints a success summary to stderr.
- Prints errors to stderr on failure.
`render`: | Status | Meaning |
| --- | --- |
| `0` | Success. |
| `1` | Parse, configuration, loading, rendering, generation, output-write, or other runtime error. |
| `2` | `run` generated and wrote output, but validation failed. |
- Writes prepared-run output to stdout by default. ## Workflows And Examples
- Writes prepared-run output to `--out` when provided.
- Does not print a success summary.
`serve`: The [maintained render script](../examples/render-markdown-summary.sh) is a
copyable render workflow. The [HTTP request example](../examples/http-run.json)
- Logs startup and server errors to stderr. is for a running `serve` process.
## Exit Codes
- `0`: success.
- `1`: parse, config, load, render, generation, output-write, or runtime error.
- `2`: `run` completed and wrote output, but validation status is `failed`.
## Common Workflows
Render prompt inputs and variables as JSON:
```bash
go run ./cmd/scriptorium render \
--config ./examples/config.yml \
--prompt generic.markdown_summary \
--input transcript=./examples/fixtures/transcript.md \
--input glossary=./examples/fixtures/glossary.yml \
--var session_date=2026-05-04 \
--format json
```
Run a prompt with an explicit profile and file output:
```bash
go run ./cmd/scriptorium run \
--config ./examples/config.yml \
--prompt generic.markdown_summary \
--profile local-fast \
--input transcript=./examples/fixtures/transcript.md \
--input glossary=./examples/fixtures/glossary.yml \
--out ./summary.md
```
Start the HTTP server with example config:
```bash
go run ./cmd/scriptorium serve --config ./examples/config.yml
```
Copyable maintained script:
- `examples/render-markdown-summary.sh`

View File

@@ -1,324 +1,169 @@
# Configuration Reference # Configuration Reference
## Config Discovery And Precedence This is the canonical reference for Scriptorium application settings and the
prompt, profile, and schema files those settings select. For command syntax,
see the [CLI reference](cli.md); for HTTP request shapes, limits, and outcomes,
see the [HTTP API reference](api.md).
## Discovery And Precedence
Application settings are resolved in this order: Application settings are resolved in this order:
1. built-in defaults 1. built-in defaults;
2. `config.yml` values 2. a configuration file; then
3. CLI overrides 3. CLI overrides.
When `--config` is omitted, Scriptorium searches: When `--config` is omitted, Scriptorium searches
`/usr/local/etc/scriptorium/config.yml` and then `/etc/scriptorium/config.yml`.
If neither exists, it uses built-in defaults. An explicit `--config` path must
exist and decode successfully.
1. `/usr/local/etc/scriptorium/config.yml` The maintained [minimal configuration](../examples/config.yml) and
2. `/etc/scriptorium/config.yml` [full configuration](../examples/config.full.yml) are copyable examples.
If neither file exists, Scriptorium uses built-in defaults. When ## Application Configuration File
`--config <path>` is provided, that file must exist and decode successfully.
## Minimal Working Config Configuration is strict YAML: unknown fields are rejected. Empty string values
do not override a prior value. Raw API-key fields are not accepted.
```yaml | Field | Default | Meaning |
prompt_dir: ./examples/prompts
```
This is enough for `run` and `render` when selected prompts use built-in
profiles. Set `profile_dir` when prompts or requests use custom profiles.
The maintained repository example is `examples/config.yml`.
## Production-Oriented Config
```yaml
prompt_dir: /opt/scriptorium/prompts
profile_dir: /opt/scriptorium/profiles
schema_dir: /opt/scriptorium/schemas
server:
addr: 127.0.0.1:8080
artifact_root: /var/lib/scriptorium/artifacts
max_request_bytes: 16777216
max_artifact_bytes: 16777216
max_response_bytes: 16777216
defaults:
render_format: text
```
The maintained full example is `examples/config.full.yml`.
## App Config Reference
Top-level fields:
| Field | Default | Description |
| --- | --- | --- | | --- | --- | --- |
| `prompt_dir` | unset | Directory containing prompt definition YAML files. Required effectively by `run`, `render`, and `serve`. | | `prompt_dir` | unset | Directory containing prompt-definition YAML. `run`, `render`, and `serve` require an effective value. |
| `profile_dir` | unset | Directory containing custom profile YAML files. Built-in profiles remain available when unset. | | `profile_dir` | unset | Directory containing custom profile YAML. Built-in profiles remain available. |
| `schema_dir` | `.` | Base directory for relative JSON Schema paths. | | `schema_dir` | `.` | Base directory for relative JSON Schema paths. |
| `server` | `{}` | HTTP service settings used by `serve`. | | `server.addr` | `:8080` | Address used by `serve`. |
| `defaults` | `{}` | Adapter defaults. | | `server.artifact_root` | unset | Root that enables HTTP `file` input references. |
| `server.max_request_bytes` | `16777216` | Maximum encoded HTTP request body bytes; `0` disables the limit. |
`server` fields: | `server.max_artifact_bytes` | `16777216` | Maximum HTTP file-input artifact bytes; `0` disables the limit. |
| `server.max_response_bytes` | `16777216` | Maximum encoded HTTP response bytes; `0` disables the limit. |
| Field | Default | Description |
| --- | --- | --- |
| `server.addr` | `:8080` | Listen address for `serve`. |
| `server.artifact_root` | unset | Base directory for HTTP `file` input references. Without it, HTTP file refs are rejected. |
| `server.max_request_bytes` | `16777216` | Maximum encoded HTTP request body bytes. `0` disables the limit. |
| `server.max_artifact_bytes` | `16777216` | Maximum HTTP file artifact bytes. `0` disables the limit. |
| `server.max_response_bytes` | `16777216` | Maximum encoded HTTP response bytes. `0` disables the limit. |
`defaults` fields:
| Field | Default | Description |
| --- | --- | --- |
| `defaults.render_format` | `text` | Default `render` output format: `text` or `json`. | | `defaults.render_format` | `text` | Default `render` output format: `text` or `json`. |
Config rules: The three size fields must be zero or greater. The HTTP contract defines how
each limit is enforced and reported. `server.artifact_root` configures the
- YAML decoding is strict; unknown fields are rejected. deployment boundary; see the [HTTP API reference](api.md) for request-path and
- HTTP size limits must be greater than or equal to `0`. containment behavior, and [operations](operations.md) for deployment handling.
- Empty string config values are ignored.
- Raw API key fields are not supported in app config.
## Prompt Definition Files ## Prompt Definition Files
Prompt definitions are YAML files anywhere under `prompt_dir`. Nested Prompt definitions are strict YAML files anywhere below `prompt_dir`. A prompt
directories are organizational; callers select prompts by YAML `id`, not file is selected by its YAML `id`, not by file path; nested directories are only for
path. organization. See [maintained prompt examples](../examples/prompts/).
Example: | Field | Required | Meaning |
```yaml
id: generic.structured_events
version: "1.0.0"
default_profile: local-quality
description: Produce structured event JSON from a transcript.
inputs:
- name: transcript
required: true
content_type: text/markdown
description: Source transcript content
- name: glossary
required: false
content_type: text/yaml
description: Optional glossary context
messages:
- role: system
content_file: ./generic.structured_events.system.md
- role: user
content_file: ./generic.structured_events.user.md
output:
format: json
validation_mode: json_schema
schema_path: structured_events.schema.json
repair_attempts: 0
```
Prompt fields:
| Field | Required | Description |
| --- | --- | --- | | --- | --- | --- |
| `id` | yes | Prompt identifier used by `--prompt` and HTTP `prompt_id`. | | `id` | yes | Prompt identifier. |
| `version` | yes | Prompt version. | | `version` | yes | Prompt version. |
| `default_profile` | no | Profile ID used when a request does not provide a profile. | | `default_profile` | no | Profile used when a request omits a profile ID. |
| `description` | no | Human-readable description. | | `description` | no | Human-readable description. |
| `session_id` | no | Go-template string rendered from request vars and forwarded as provider `session_id` when non-empty. | | `session_id` | no | Go-template string rendered from request variables and sent to a compatible provider when non-empty. |
| `inputs` | no | Named input declarations. | | `inputs` | no | Declared input metadata. |
| `messages` | yes | Chat message templates. | | `messages` | yes | Chat-message templates. |
| `output` | yes | Output format and validation contract. | | `output` | yes | Output format and validation contract. |
`inputs[]` fields: ### Inputs And Messages
- `name` (required) Each `inputs` item has a required `name` and optional `required`,
- `required` (optional boolean) `content_type`, and `description` fields. Input names must be unique.
- `content_type` (optional metadata)
- `description` (optional)
`messages[]` fields: Each message has a required `role`, exactly one of `content` or `content_file`,
and optional `cache_control`. A `content_file` path is relative to the prompt
file. `cache_control.type` must be `ephemeral`; its optional `ttl` is `1h`.
- `role` (required) `session_id` uses the same template variables as messages. Empty rendered
- exactly one of `content` or `content_file` values are omitted. A rendered value may contain at most 256 Unicode code
- `cache_control` (optional) points.
Message rules: ### Output Contract
- `content_file` resolves relative to the prompt YAML file location. | Field | Required | Values or behavior |
- Repeated roles are allowed.
- Prompt YAML decoding is strict.
- Duplicate input names are invalid.
- Duplicate prompt IDs are invalid for a requested ID/version.
`messages[].cache_control` fields:
| Field | Required | Supported values |
| --- | --- | --- | | --- | --- | --- |
| `type` | yes | `ephemeral` | | `format` | yes | `text`, `markdown`, or `json`. |
| `ttl` | no | `1h` | | `validation_mode` | yes | `none`, `basic`, `json`, or `json_schema`. |
| `schema_path` | for `json_schema` | Schema path, relative to `schema_dir` unless absolute. |
`session_id` behavior: | `repair_attempts` | no | Integer greater than or equal to `0`; omitted means `0`. |
- Rendered with the same variable context as message templates.
- Trimmed and omitted when empty.
- Rejected when longer than 256 Unicode code points.
- CLI callers pass variables with `--var`; HTTP callers use `vars`.
`output` fields:
| Field | Required | Supported values |
| --- | --- | --- |
| `format` | yes | `text`, `markdown`, `json` |
| `validation_mode` | yes | `none`, `basic`, `json`, `json_schema` |
| `schema_path` | only for `json_schema` | Relative to `schema_dir` unless absolute. |
| `repair_attempts` | yes | Integer greater than or equal to `0`. |
Repair boundary:
- `repair_attempts` is part of the prompt contract.
- The current CLI and HTTP wiring constructs the runner without a repairer, so normal `run` and `serve` execution does not perform repair attempts.
## Profile Definition Files ## Profile Definition Files
Execution profiles are YAML files anywhere under `profile_dir`. Nested Profiles are strict YAML files anywhere below `profile_dir`. A profile is
directories are organizational; callers select profiles by YAML `id`, not file selected by YAML `id`; nested directories are organizational. See the
path. [maintained profile examples](../examples/profiles/).
Scriptorium also ships built-in profiles. Custom profiles override built-ins | Field | Required | Meaning |
with the same ID.
Example:
```yaml
id: local-fast
endpoint: http://localhost:8000/v1
model: gpt-4o-mini
temperature: 0.2
max_tokens: 500
top_p: 1.0
timeout_seconds: 90
api_key_env: SCRIPTORIUM_API_KEY
service_tier: priority
reasoning_effort: medium
extra_params:
provider_route: primary
```
Profile fields:
| Field | Required | Description |
| --- | --- | --- | | --- | --- | --- |
| `id` | yes | Profile identifier. | | `id` | yes | Profile identifier. |
| `endpoint` | yes | OpenAI-compatible base URL including `/v1`. | | `endpoint` | yes | OpenAI-compatible base URL, including its API version path when needed. |
| `model` | yes | Provider model name. | | `model` | yes | Provider model name. |
| `temperature` | no | Range `0..2`. | | `temperature` | no | Number from `0` through `2`. |
| `max_tokens` | no | Integer greater than or equal to `0`. | | `max_tokens` | no | Integer zero or greater. |
| `top_p` | no | Range `0..1`. | | `top_p` | no | Number from `0` through `1`. |
| `timeout_seconds` | no | Integer greater than or equal to `0`. | | `timeout_seconds` | no | Integer zero or greater. |
| `service_tier` | no | Provider-specific request tier. | | `service_tier` | no | Non-empty provider-specific request tier. |
| `reasoning_effort` | no | Provider-specific reasoning setting. | | `reasoning_effort` | no | Non-empty provider-specific reasoning setting. |
| `api_key_env` | no | Environment variable name containing the API key. | | `api_key_env` | no | Environment-variable name containing the API key. |
| `extra_params` | no | JSON-compatible provider-specific top-level request fields. | | `extra_params` | no | JSON-compatible provider-specific outbound request fields. |
Execution defaults before profile/request overrides: Execution defaults before profile and request overrides are `temperature: 0`,
`max_tokens: 0`, `top_p: 1`, and `timeout_seconds: 600`. Profile numeric values
merge by non-zero value. Request overrides preserve presence, so an explicit
zero can override a profile value.
| Field | Default | Custom profiles take precedence over built-ins with the same ID. Invalid custom
| --- | --- | profiles are errors; they do not fall back to a built-in profile. Raw `api_key`
| `temperature` | `0.0` | is rejected. Use `api_key_env`, or the public Go package's request-scoped key
| `max_tokens` | `0` | mechanism described in the [package contract](consumers/pkg-scriptorium.md).
| `top_p` | `1.0` |
| `timeout_seconds` | `600` |
Profile rules: `extra_params` keys must be non-empty and cannot be `model`, `session_id`,
`messages`, `temperature`, `max_tokens`, `top_p`, `service_tier`,
`reasoning_effort`, or `response_format`.
- Profile YAML decoding is strict. ### Built-In Profile Catalog
- Duplicate custom profile IDs are invalid.
- Matching custom and built-in IDs are valid override behavior.
- Raw `api_key` is rejected; use `api_key_env`.
- If `api_key_env` is set, the named environment variable must be set before `run`, `render`, or HTTP execution can prepare the request.
- Profile numeric fields merge by non-zero value. Request overrides are presence-aware, so explicit zero values are supported through CLI flags or HTTP model overrides.
- `extra_params` keys must not be empty and must not collide with reserved outbound fields: `model`, `session_id`, `messages`, `temperature`, `max_tokens`, `top_p`, `service_tier`, `reasoning_effort`, or `response_format`.
Built-in profile catalog: Each embedded profile uses `OPENROUTER_API_KEY`.
| Provider | ID | Model | API key env | | Provider | ID | Model |
| --- | --- | --- | --- | | --- | --- | --- |
| aion-labs | `aion-2` | `aion-labs/aion-2.0` | `OPENROUTER_API_KEY` | | aion-labs | `aion-2` | `aion-labs/aion-2.0` |
| anthropic | `claude-fable-latest` | `~anthropic/claude-fable-latest` | `OPENROUTER_API_KEY` | | anthropic | `claude-fable-latest` | `~anthropic/claude-fable-latest` |
| anthropic | `claude-haiku-latest` | `~anthropic/claude-haiku-latest` | `OPENROUTER_API_KEY` | | anthropic | `claude-haiku-latest` | `~anthropic/claude-haiku-latest` |
| anthropic | `claude-opus-latest` | `~anthropic/claude-opus-latest` | `OPENROUTER_API_KEY` | | anthropic | `claude-opus-latest` | `~anthropic/claude-opus-latest` |
| anthropic | `claude-sonnet-latest` | `~anthropic/claude-sonnet-latest` | `OPENROUTER_API_KEY` | | anthropic | `claude-sonnet-latest` | `~anthropic/claude-sonnet-latest` |
| deepseek | `deepseek-3-2` | `deepseek/deepseek-v3.2` | `OPENROUTER_API_KEY` | | deepseek | `deepseek-3-2` | `deepseek/deepseek-v3.2` |
| deepseek | `deepseek-4-pro` | `deepseek/deepseek-v4-pro` | `OPENROUTER_API_KEY` | | deepseek | `deepseek-4-flash` | `deepseek/deepseek-v4-flash` |
| google | `gemini-2-flash` | `google/gemini-2.5-flash` | `OPENROUTER_API_KEY` | | deepseek | `deepseek-4-pro` | `deepseek/deepseek-v4-pro` |
| google | `gemini-2-flash-lite` | `google/gemini-2.5-flash-lite` | `OPENROUTER_API_KEY` | | google | `gemini-2-flash` | `google/gemini-2.5-flash` |
| google | `gemini-2-pro` | `google/gemini-2.5-pro` | `OPENROUTER_API_KEY` | | google | `gemini-2-flash-lite` | `google/gemini-2.5-flash-lite` |
| google | `gemini-3-flash-lite` | `google/gemini-3.1-flash-lite` | `OPENROUTER_API_KEY` | | google | `gemini-2-pro` | `google/gemini-2.5-pro` |
| google | `gemini-flash-latest` | `~google/gemini-flash-latest` | `OPENROUTER_API_KEY` | | google | `gemini-3-flash-lite` | `google/gemini-3.1-flash-lite` |
| google | `gemini-pro-latest` | `~google/gemini-pro-latest` | `OPENROUTER_API_KEY` | | google | `gemini-flash-latest` | `~google/gemini-flash-latest` |
| google | `gemma-4-31b` | `google/gemma-4-31b-it:exacto` | `OPENROUTER_API_KEY` | | google | `gemini-pro-latest` | `~google/gemini-pro-latest` |
| minimax | `minimax-m2` | `minimax/minimax-m2.5` | `OPENROUTER_API_KEY` | | google | `gemma-4-31b` | `google/gemma-4-31b-it:exacto` |
| minimax | `minimax-m3` | `minimax/minimax-m3` | `OPENROUTER_API_KEY` | | minimax | `minimax-m2` | `minimax/minimax-m2.5` |
| mistral | `mistral-large-2512` | `mistralai/mistral-large-2512` | `OPENROUTER_API_KEY` | | minimax | `minimax-m3` | `minimax/minimax-m3` |
| mistral | `mistral-medium-3-5` | `mistralai/mistral-medium-3-5` | `OPENROUTER_API_KEY` | | mistral | `mistral-large-2512` | `mistralai/mistral-large-2512` |
| mistral | `mistral-small-3` | `mistralai/mistral-small-3.2-24b-instruct` | `OPENROUTER_API_KEY` | | mistral | `mistral-medium-3-5` | `mistralai/mistral-medium-3-5` |
| mistral | `mistral-small-4` | `mistralai/mistral-small-2603` | `OPENROUTER_API_KEY` | | mistral | `mistral-small-3` | `mistralai/mistral-small-3.2-24b-instruct` |
| nvidia | `nemotron-3-ultra` | `nvidia/nemotron-3-ultra-550b-a55b` | `OPENROUTER_API_KEY` | | mistral | `mistral-small-4` | `mistralai/mistral-small-2603` |
| openai | `gpt-5-mini` | `openai/gpt-5.4-mini` | `OPENROUTER_API_KEY` | | nvidia | `nemotron-3-ultra` | `nvidia/nemotron-3-ultra-550b-a55b` |
| openai | `gpt-5-nano` | `openai/gpt-5.4-nano` | `OPENROUTER_API_KEY` | | openai | `gpt-5-mini` | `openai/gpt-5.4-mini` |
| openai | `gpt-5-nano` | `openai/gpt-5.4-nano` |
## Schema Behavior ## Schemas
Schemas are JSON files, typically under `schema_dir`. Schemas are JSON files, normally below `schema_dir`. `json_schema` output
requires a `schema_path`. Relative paths resolve from `schema_dir`; absolute
paths are used directly. Referenced nested schemas use relative paths and are
not discovered by basename. An unreadable or invalid schema is a runtime
validation error; generated content that fails JSON or schema validation is a
validation result.
Rules: ## Credentials
- `output.validation_mode: json_schema` requires `output.schema_path`. Keep secrets in environment variables. Store only an environment-variable name
- Relative `schema_path` values resolve from `schema_dir`. in `api_key_env`; do not place raw keys in configuration, prompt or profile
- Absolute `schema_path` values are used directly. files, CLI arguments, examples, or HTTP payloads.
- Nested schemas must be referenced by relative path; schemas are not searched recursively by basename.
- Missing or invalid schema documents are runtime validation errors.
- Invalid generated JSON produces validation status `failed`, not a runtime error.
## Artifact References ## Related References
Supported request input artifact reference types are:
- `file`
- `inline`
CLI `run` and `render` create `file` references from `--input name=path`.
HTTP `file` references require `server.artifact_root` or `serve
--artifact-root`. Relative file URIs resolve under that root. Absolute paths
and relative traversal outside the root are rejected by lexical checks. Symlinks
inside the root are followed by the operating system, including symlinks that
point outside the root.
HTTP `inline` references do not require an artifact root.
## Secrets Handling
- Keep secret values in environment variables.
- Store only environment-variable names in `api_key_env`.
- Do not put raw API keys in config, prompts, profiles, CLI arguments, examples, or HTTP request bodies.
## Maintained Examples
- Minimal app config: `examples/config.yml`
- Full app config: `examples/config.full.yml`
- Prompt examples: `examples/prompts/`
- Custom profile examples: `examples/profiles/`
- Schema examples: `examples/schemas/`
- Input fixtures: `examples/fixtures/`
- Render script: `examples/render-markdown-summary.sh`
- HTTP request-shape example: `examples/http-run.json`
## Integration References
- [CLI reference](cli.md) - [CLI reference](cli.md)
- [HTTP API reference](api.md) - [HTTP API reference](api.md)
- [Outbound OpenAI-compatible contract](integrations/openai-compatible-chat.md) - [OpenAI-compatible outbound contract](integrations/openai-compatible-chat.md)

View File

@@ -1,65 +1,26 @@
# Consumer Integration Overview # Consumer Integration Overview
This guide is for applications that call Scriptorium from another codebase. This guide helps applications choose a Scriptorium interface and understand
their responsibilities. The linked contracts own interface syntax and wire
semantics.
Scriptorium exposes three integration surfaces: | Interface | Use when |
| Surface | Use when |
| --- | --- | | --- | --- |
| Go package | The consumer is Go, needs typed requests/results, or wants injected LLM clients for tests. | | Go package | The consumer is Go and needs typed requests, results, or an injected LLM client. |
| CLI subprocess | The consumer wants process isolation or is not written in Go. | | CLI subprocess | The consumer needs process isolation or is not written in Go. |
| HTTP API | The consumer needs a service boundary or remote access to `POST /v1/runs`. | | HTTP API | The consumer needs a service boundary or remote access. |
Canonical references: - Go package: [package contract](pkg-scriptorium.md)
- CLI subprocess: [subprocess integration](../integrations/subprocess.md)
- HTTP service: [HTTP API reference](../api.md)
- Prompt, profile, schema, and credential configuration: [configuration reference](../config.md)
- Go package: [Package scriptorium](pkg-scriptorium.md) ## Minimal Go Use
- CLI subprocess: [Subprocess integration](../integrations/subprocess.md)
- HTTP: [HTTP API reference](../api.md)
- File formats: [Configuration reference](../config.md)
## Required Deployment Inputs
Every integration needs operators to provide:
- prompt definitions;
- profile definitions or built-in profile IDs;
- schema files when prompts use `json_schema`;
- input artifacts or inline input bodies;
- API-key environment variables or direct per-request keys where supported.
Raw API keys do not belong in config, prompt files, profile YAML, CLI
arguments, or HTTP request bodies.
## Recommended Workflow
Use the Go package when:
- the consumer is a Go application;
- the application needs `context.Context` cancellation;
- repeated calls should avoid subprocess startup;
- tests need a fake LLM client;
- direct per-request `RunRequest.APIKey` is required.
Use the CLI subprocess when:
- the consumer is not Go;
- process isolation is useful;
- stdout/stderr separation and exit codes are enough;
- the consumer already manages local files and environment variables.
Use HTTP when:
- Scriptorium should run as a service;
- multiple clients need a shared prompt/profile deployment;
- clients can reach a trusted, protected HTTP boundary.
## Minimal Go Example
```go ```go
engine, err := scriptorium.NewEngine(scriptorium.Config{ engine, err := scriptorium.NewEngine(scriptorium.Config{
PromptDir: "./examples/prompts", PromptDir: "./examples/prompts",
ProfileDir: "./examples/profiles", ProfileDir: "./examples/profiles",
SchemaDir: "./examples/schemas",
}) })
if err != nil { if err != nil {
return err return err
@@ -69,54 +30,30 @@ prepared, err := engine.Prepare(ctx, scriptorium.RunRequest{
PromptID: "generic.markdown_summary", PromptID: "generic.markdown_summary",
Inputs: map[string]scriptorium.ArtifactRef{ Inputs: map[string]scriptorium.ArtifactRef{
"transcript": scriptorium.File("./examples/fixtures/transcript.md"), "transcript": scriptorium.File("./examples/fixtures/transcript.md"),
"glossary": scriptorium.File("./examples/fixtures/glossary.yml"),
}, },
}) })
if err != nil { if err != nil {
return err return err
} }
_ = prepared.Messages _ = prepared
``` ```
Run the maintained package example: For a maintained program, see
[`examples/go-library/prepare`](../../examples/go-library/prepare).
```bash
go run ./examples/go-library/prepare
```
## Subprocess Workflow
Invoke `scriptorium render` for preflight and `scriptorium run` for generation.
Capture stdout and stderr separately. Treat exit code `2` from `run` as a
completed generation with failed validation.
See [Subprocess integration](../integrations/subprocess.md) for the stable
invocation contract.
## HTTP Workflow
Run `scriptorium serve` behind trusted controls and send JSON requests to
`POST /v1/runs`.
Do not duplicate endpoint schemas in consumers. Use the [HTTP API
reference](../api.md) as the authoritative contract.
## Consumer Responsibilities ## Consumer Responsibilities
Consumers are responsible for: Consumers are responsible for:
- selecting prompt/profile IDs as deployment configuration; - selecting and deploying prompt, profile, and schema assets;
- supplying all required inputs and vars; - supplying required inputs and template variables;
- protecting generated artifacts and rendered prompts as sensitive data; - supplying credentials through the applicable interface;
- deciding whether to keep output when validation fails; - protecting rendered prompts and generated artifacts as potentially sensitive;
- implementing retries only when another model call is acceptable. - deciding whether validation-failed output is usable; and
- retrying only when another model call is acceptable.
Scriptorium does not persist run state. Retrying a failed or timed-out request Scriptorium does not persist run state. A retry can produce different output and
can produce different output and can incur another provider request. can incur another provider request. CLI exit behavior belongs to the
[CLI reference](../cli.md); HTTP status behavior belongs to the
## Status Behavior [HTTP API reference](../api.md); package errors and results belong to the
[package contract](pkg-scriptorium.md).
- Go package methods return typed results or errors that support `errors.Is`.
- CLI `run` exits `2` when generation succeeds but validation fails.
- HTTP returns `200 OK` for generated-content validation failures and exposes the failed status in the response body.
- Runtime validation failures are errors.

View File

@@ -1,4 +1,4 @@
# Package scriptorium # Package `scriptorium`
Import path: Import path:
@@ -6,250 +6,158 @@ Import path:
import "gitea.maximumdirect.net/eric/scriptorium" import "gitea.maximumdirect.net/eric/scriptorium"
``` ```
The root package is the public Go facade for Scriptorium's prompt prepare/run This is the canonical public Go contract for in-process prompt preparation and
workflow. It exposes typed requests, results, source options, injected LLM execution. Prompt, profile, and schema file formats are defined in the
clients, and stable public errors while keeping `internal/*` packages private. [configuration reference](../config.md).
## Intended Use Cases ## Engine Construction
Use the package when a Go application needs: `NewEngine(Config, ...Option)` constructs an engine. `Config` has these
fields:
- in-process prompt preparation or execution; | Field | Meaning |
- typed request/result structs; | --- | --- |
- direct `context.Context` cancellation; | `PromptDir` | Prompt-definition directory, required unless a prompt source option is supplied. |
- injected/fake LLM clients for tests; | `ProfileDir` | Optional custom profile directory over built-ins. |
- direct per-request `RunRequest.APIKey`. | `SchemaDir` | Schema directory; empty uses `.`. |
| `Timeout` | Default timeout for the built-in OpenAI-compatible client. |
| `HTTPClient` | Optional HTTP client for that built-in client. |
Use [Subprocess integration](../integrations/subprocess.md) or the [HTTP API](../api.md) Nil options are ignored. Invalid construction, including
when a process or service boundary is preferred. `WithLLMClient(nil)`, returns an error matching `ErrInvalidConfig`.
## Construct An Engine Source options replace their matching directory source:
- prompts: `WithPromptFS(fsys, root)`, `WithPromptFile(path)`;
- profiles: `WithProfileFS(fsys, root)`, `WithProfileFile(path)`, and
`WithProfiles(profiles...)`;
- schemas: `WithSchemaFS(fsys, root)`, `WithSchemaFile(path)`; and
- LLM client: `WithLLMClient(client)`.
`fs.FS` prompt-content and schema paths stay inside their configured roots.
A single-file option exposes that file by its base name. In-memory profiles take
precedence over an explicit or directory-backed profile source, which in turn
takes precedence over built-ins. File and filesystem sources use the format and
credential rules in the [configuration reference](../config.md).
## Prepare And Run
`Prepare(ctx, request)` resolves the prompt, profile, input artifacts,
validation contract, and rendered messages without calling an LLM.
`Run(ctx, request)` performs that preparation, calls the configured client,
and validates generated content.
```go ```go
engine, err := scriptorium.NewEngine(scriptorium.Config{ engine, err := scriptorium.NewEngine(scriptorium.Config{
PromptDir: "./examples/prompts", PromptDir: "./examples/prompts",
ProfileDir: "./examples/profiles", ProfileDir: "./examples/profiles",
SchemaDir: "./examples/schemas",
}) })
if err != nil { if err != nil {
return err return err
} }
```
`Config` fields:
| Field | Description |
| --- | --- |
| `PromptDir` | Prompt definition directory. Required unless `WithPromptFS` or `WithPromptFile` is used. |
| `ProfileDir` | Optional custom profile directory overlaid above built-in profiles. |
| `SchemaDir` | Schema directory. Defaults to `.` when empty. |
| `Timeout` | Default timeout for the built-in OpenAI-compatible client. |
| `HTTPClient` | Optional HTTP client for the built-in OpenAI-compatible client. |
`NewEngine` accepts `nil` options and ignores them. Invalid construction wraps
`ErrInvalidConfig`.
## Source Options
Directory fields are the compatibility path. Explicit source options override
the matching directory field.
Prompt sources:
- `WithPromptFS(fsys, root)`
- `WithPromptFile(path)`
Profile sources:
- `WithProfileFS(fsys, root)`
- `WithProfileFile(path)`
- `WithProfiles(profiles...)`
Schema sources:
- `WithSchemaFS(fsys, root)`
- `WithSchemaFile(path)`
LLM source:
- `WithLLMClient(client)`
Source behavior:
- Prompt and profile YAML use the same strict rules as directory loading.
- Prompt `content_file` values resolve relative to the prompt file.
- `fs.FS` roots are containment boundaries for prompt content files and schema paths.
- File options expose the selected file by its base name.
- Profile source precedence is in-memory profiles, then explicit profile file/FS/directory source, then built-ins.
- `WithLLMClient(nil)` returns `ErrInvalidConfig`.
## In-Memory Profiles
Use `WithProfiles` when the application already has typed model settings:
```go
profile := scriptorium.OpenAICompatibleProfile(scriptorium.OpenAICompatibleProfileConfig{
ID: "app.default",
Endpoint: "https://openrouter.ai/api/v1",
Model: "mistralai/mistral-small-3.2-24b-instruct",
APIKeyRequired: true,
})
engine, err := scriptorium.NewEngine(cfg, scriptorium.WithProfiles(profile))
```
`Profile` and `OpenAICompatibleProfileConfig` include:
- `ID`
- `Endpoint`
- `Model`
- `Temperature`
- `MaxTokens`
- `TopP`
- `TimeoutSeconds`
- `ServiceTier`
- `ReasoningEffort`
- `APIKeyRequired`
- `ExtraParams`
`WithProfiles` rejects duplicate IDs in one call. In-memory profiles do not
store raw keys. When `APIKeyRequired` is true, pass the secret on each request
with `RunRequest.APIKey`.
`ExtraParams` must be JSON-compatible: strings, booleans, finite numbers,
objects with string keys, arrays/slices, and nil. Unsupported values, non-string
map keys, non-finite floats, and cycles return `ErrInvalidConfig` for profiles
or `ErrInvalidRequest` for request overrides.
## Prepare Workflow
`Prepare` resolves prompt/profile/input/schema state and renders messages
without calling an LLM.
```go
prepared, err := engine.Prepare(ctx, scriptorium.RunRequest{ prepared, err := engine.Prepare(ctx, scriptorium.RunRequest{
PromptID: "generic.markdown_summary", PromptID: "generic.markdown_summary",
Inputs: map[string]scriptorium.ArtifactRef{ Inputs: map[string]scriptorium.ArtifactRef{
"transcript": scriptorium.File("./examples/fixtures/transcript.md"), "transcript": scriptorium.File("./examples/fixtures/transcript.md"),
"glossary": scriptorium.File("./examples/fixtures/glossary.yml"),
}, },
}) })
if err != nil { if err != nil {
return err return err
} }
_ = prepared.EffectiveModelParams _ = prepared.Messages
``` ```
`PreparedRun` includes prompt ID/version/hash, selected profile, effective The maintained package example is
model params, output contract, structured-output metadata, input hashes, [`examples/go-library/prepare`](../../examples/go-library/prepare).
rendered prompt hash, rendered messages, and timing fields. It does not include
raw API-key values, model output, validation results, or internal target
presence metadata.
## Run Workflow `PreparedRun` exposes prompt, selected-profile, effective-model, output
contract, structured-output, input-hash, rendered-message, and timing
information. It does not include a resolved API key, model output, validation
result, or target-presence metadata.
`Run` calls `Prepare`, invokes the configured LLM client, builds the output `RunResult` adds run ID, artifact, raw output, validation, model metadata,
artifact, and validates the output. usage, and duration. Generated-content validation failures return a result with
`Validation.Status == ValidationFailed`; schema or validator runtime failures
return an error matching `ErrValidation`.
```go ## Public Values
result, err := engine.Run(ctx, scriptorium.RunRequest{
PromptID: "generic.markdown_summary",
APIKey: apiKey,
Inputs: map[string]scriptorium.ArtifactRef{
"transcript": scriptorium.File("./examples/fixtures/transcript.md"),
"glossary": scriptorium.File("./examples/fixtures/glossary.yml"),
},
})
if err != nil {
return err
}
_ = result.Artifact
```
`RunResult` includes run ID, output artifact, raw output, validation result, `ArtifactRef` has `Type`, `URI`, and `Body`; `Artifact` has `Name`,
prompt/profile/model metadata, effective model params, input hashes, usage, and `ContentType`, `Body`, `URI`, `Size`, and `Hash`. `ExecutionTarget` exposes the
timing fields. effective endpoint, model, numeric settings, credential-environment name,
service tier, reasoning effort, and extra parameters. `ValidationResult`
contains status, mode, errors, schema path, repair attempts, and validity.
Generated-content validation failures return a successful `RunResult` with The exported constants define these serialized values:
`Validation.Status == ValidationFailed`. Runtime/schema validation errors
return an error that matches `ErrValidation`.
## Inputs - artifact types: `inline` and `file`;
- output formats: `text`, `markdown`, and `json`;
- validation modes: `none`, `basic`, `json`, and `json_schema`; and
- validation statuses: `passed`, `failed`, and `skipped`.
Input helpers: `TokenUsage` reports prompt, completion, total, cached, and cache-write token
counts. `RenderedPrompt`, `RenderedMessage`, `CacheControl`, and
`StructuredOutputSpec` are the public shapes used by injected LLM clients.
- `File(path)`: file-backed artifact reference. ## Requests, Inputs, And Overrides
- `Inline(body)`: inline artifact body.
- `InlineWithURI(uri, body)`: inline artifact body with URI metadata.
Input map keys must match the prompt's expected input names. `RunRequest` fields are `PromptID`, `PromptVersion`, `ProfileID`,
`APIKey`, `Inputs`, `Vars`, `Execution`, `Validation`, and
`Metadata`.
Input helpers are:
- `File(path)` for a file-backed artifact;
- `Inline(body)` for inline content; and
- `InlineWithURI(uri, body)` for inline content with URI metadata.
Required declared inputs must be supplied. Template rendering must also resolve
every input name the prompt actually references. Extra entries in `Inputs`
are not rejected solely because they are undeclared.
`ExecutionTargetOverride` supplies endpoint, model, credential-environment,
service-tier, reasoning-effort, and extra-parameter overrides. Its numeric
fields (`Temperature`, `MaxTokens`, `TopP`, and `TimeoutSeconds`) are
pointers so explicit zero values are preserved. `OutputContract` supplies
`Format`, `ValidationMode`, `SchemaPath`, and `RepairAttempts`.
`ExtraParams` accepts JSON-compatible values: strings, booleans, finite
numbers, objects with string keys, arrays or slices, and nil. Unsupported
values, non-string map keys, non-finite floats, and cycles return
`ErrInvalidConfig` for profiles or `ErrInvalidRequest` for request
overrides.
## Profiles And Credentials
`OpenAICompatibleProfile(OpenAICompatibleProfileConfig)` creates an
in-memory `Profile`. Its public fields are `ID`, `Endpoint`, `Model`,
`Temperature`, `MaxTokens`, `TopP`, `TimeoutSeconds`, `ServiceTier`,
`ReasoningEffort`, `APIKeyRequired`, and `ExtraParams`.
`WithProfiles` rejects duplicate IDs in one call.
A direct `RunRequest.APIKey` is request-scoped and takes precedence over
`api_key_env` for the built-in client. It is excluded from JSON output and
from `PreparedRun` and `RunResult`. The package's `String` and
`GoString` methods report only whether a direct key is set. Do not use
reflection-based dumps of request structs, which can bypass that redaction.
## Injected LLM Clients ## Injected LLM Clients
Use `WithLLMClient` for tests or custom model integrations: `LLMClient` implements:
```go ```go
type fakeLLM struct{} Generate(context.Context, GenerateRequest) (*GenerateResponse, error)
func (fakeLLM) Generate(ctx context.Context, req scriptorium.GenerateRequest) (*scriptorium.GenerateResponse, error) {
return &scriptorium.GenerateResponse{
Content: "generated text",
Usage: scriptorium.TokenUsage{TotalTokens: 12},
}, nil
}
engine, err := scriptorium.NewEngine(cfg, scriptorium.WithLLMClient(fakeLLM{}))
``` ```
Injected clients receive: Injected clients receive the rendered prompt, effective execution target, numeric
target-presence metadata, optional structured-output specification, and direct
- rendered prompt; request API key. `GenerateResponse` returns content and `TokenUsage`.
- effective execution target; Custom clients should avoid logging raw prompts or credentials.
- numeric target presence metadata;
- structured-output spec when applicable;
- direct request API key when provided.
Custom clients should not log raw prompts or API keys by default.
## Overrides And API Keys
`RunRequest` fields:
| Field | Description |
| --- | --- |
| `PromptID` | Prompt ID. |
| `PromptVersion` | Optional prompt version filter. |
| `ProfileID` | Optional profile override. |
| `APIKey` | Direct per-request API key. |
| `Inputs` | Input artifact references. |
| `Vars` | Template variables. |
| `Execution` | Per-request model overrides. |
| `Validation` | Per-request output contract override. |
| `Metadata` | Request metadata reserved for callers. |
`RunRequest.Execution` uses pointer fields for numeric values so explicit zero
overrides are preserved:
```go
zero := 0
req.Execution = &scriptorium.ExecutionTargetOverride{
MaxTokens: &zero,
}
```
Direct `RunRequest.APIKey` takes precedence over profile `api_key_env` for the
default OpenAI-compatible client. It is request-scoped, uses `json:"-"`, and is
not included in `PreparedRun` or `RunResult` JSON. Normal Go string formatting
of `RunRequest` and `GenerateRequest` reports only whether a direct key is set.
Raw API keys do not belong in profile YAML, in-memory profiles, or app config.
Avoid reflection-based debug dumps of request structs because exported fields
remain visible to tools that bypass `String` and `GoString`.
## Errors ## Errors
Public methods wrap context while preserving stable sentinel checks with Public methods preserve these sentinel checks through `errors.Is`:
`errors.Is`:
- `ErrInvalidConfig` - `ErrInvalidConfig`
- `ErrInvalidRequest` - `ErrInvalidRequest`
@@ -262,23 +170,5 @@ Public methods wrap context while preserving stable sentinel checks with
- `ErrLLMGenerate` - `ErrLLMGenerate`
- `ErrValidation` - `ErrValidation`
Example: For interface selection and operational responsibilities, see the
[consumer integration overview](api.md).
```go
if errors.Is(err, scriptorium.ErrPromptNotFound) {
return err
}
```
## Examples
Run the maintained prepare-only example from the repository root:
```bash
go run ./examples/go-library/prepare
```
See also:
- [Configuration reference](../config.md)
- [Consumer integration overview](api.md)

View File

@@ -1,118 +1,67 @@
# OpenAI-Compatible Chat Integration # OpenAI-Compatible Chat Integration
## Scope This is the outbound wire contract for Scriptorium's OpenAI-compatible
chat-completions client.
This document defines the outbound LLM contract implemented by `internal/llm/openai_compatible_client.go`. ## Endpoint And Method
It documents only fields and behaviors currently serialized by code. Scriptorium uses the request endpoint override when present; otherwise it uses
the configured client base URL. It removes a trailing slash and sends
`POST /chat/completions`.
## Endpoint Construction For example, `http://localhost:8000/v1` becomes
`http://localhost:8000/v1/chat/completions`.
Request endpoint is built as: ## Request Payload
1. choose base URL: The payload always contains `model` and rendered `messages`. It additionally
- `GenerateRequest.Target.Endpoint` if set contains these fields when applicable:
- otherwise client config `BaseURL`
2. trim trailing slash
3. append `/chat/completions`
Example: | Field | Inclusion |
| --- | --- |
| `session_id` | Non-empty rendered prompt session ID. |
| `temperature` | Non-zero effective value or an explicit zero override. |
| `max_tokens` | Non-zero effective value or an explicit zero override. |
| `top_p` | Non-zero effective value or an explicit zero override. |
| `service_tier` | Any non-empty configured value. |
| `reasoning_effort` | Any non-empty configured value. |
| `response_format` | Structured output is requested. |
| provider-specific fields | Flattened from `extra_params`. |
- base URL: `http://localhost:8000/v1` `service_tier` and `reasoning_effort` are forwarded without a provider value
- final URL: `http://localhost:8000/v1/chat/completions` catalog; the selected backend decides which values it supports.
## Request Fields Sent `extra_params` are top-level JSON fields, not a nested object. Keys cannot be
empty or collide with `model`, `session_id`, `messages`, `temperature`,
`max_tokens`, `top_p`, `service_tier`, `reasoning_effort`, or
`response_format`. Values must be JSON-serializable.
Serialized JSON fields: A rendered `session_id` is sent as a top-level JSON field, not as a header.
Empty values are omitted. The maximum length is 256 Unicode code points.
- `model` (required after fallback resolution) Messages without cache control use string `content`. A message with cache
- `session_id` (only when the rendered prompt includes a non-empty session ID) control uses one text block:
- `messages` (rendered prompt messages)
- `temperature` (when non-zero, or when explicitly overridden to zero)
- `max_tokens` (when non-zero, or when explicitly overridden to zero)
- `top_p` (when non-zero, or when explicitly overridden to zero)
- `service_tier` (only when non-empty)
- `reasoning_effort` (only when non-empty)
- `response_format` (only when structured output is provided)
- profile/request `extra_params` as additional provider-specific top-level fields
`service_tier` is provider-specific. OpenRouter currently documents request values such as `flex` and `priority`; Scriptorium forwards any non-empty configured value and lets the backend validate support.
`reasoning_effort` is provider-specific. Scriptorium forwards any non-empty configured value as top-level `reasoning_effort` and lets the backend validate support.
`extra_params` are flattened into the outbound JSON object. They are not wrapped in an `extra_params` object:
```json
{
"model": "gpt-4o-mini",
"messages": [
{
"role": "user",
"content": "rendered text"
}
],
"provider_route": "primary",
"provider_options": {
"retry_budget": 2
}
}
```
`extra_params` values must be JSON-compatible. Supported value shapes include strings, numbers, booleans, objects, and arrays.
Reserved `extra_params` keys are rejected before the HTTP request is made:
- `model`
- `session_id`
- `messages`
- `temperature`
- `max_tokens`
- `top_p`
- `service_tier`
- `reasoning_effort`
- `response_format`
Empty `extra_params` keys and values that cannot be encoded as JSON are also rejected before the HTTP request is made.
`session_id` is rendered from prompt YAML using request variables and serialized as a top-level JSON request field. Scriptorium does not send an `x-session-id` header. Empty rendered session IDs are omitted, and values longer than 256 characters are rejected before the HTTP request.
Messages without prompt cache control serialize with string `content`:
```json ```json
{ {
"role": "system", "role": "system",
"content": "rendered text" "content": [{
"type": "text",
"text": "rendered text",
"cache_control": {"type": "ephemeral", "ttl": "1h"}
}]
} }
``` ```
Messages with prompt cache control serialize as a single text content-block array: When the prompt omits cache-control `ttl`, the payload omits `ttl`.
Structured JSON Schema output is sent as:
```json
{
"role": "system",
"content": [
{
"type": "text",
"text": "rendered text",
"cache_control": {
"type": "ephemeral",
"ttl": "1h"
}
}
]
}
```
When cache-control `ttl` is unset in the prompt definition, `ttl` is omitted from the outbound payload.
Structured output is currently `json_schema` only, serialized as:
```json ```json
{ {
"response_format": { "response_format": {
"type": "json_schema", "type": "json_schema",
"json_schema": { "json_schema": {
"name": "...", "name": "schema name",
"strict": true, "strict": true,
"schema": {"type": "object"} "schema": {"type": "object"}
} }
@@ -120,74 +69,40 @@ Structured output is currently `json_schema` only, serialized as:
} }
``` ```
## Authentication Header ## Authentication And Timeout
If `Target.APIKey` is set: When a direct request API key is present, Scriptorium sends
`Authorization: Bearer <key>` and does not read `api_key_env`. Otherwise, it
resolves the configured non-empty `api_key_env` at request time and sends the
same header. If neither mechanism supplies a key, it sends no
`Authorization` header.
- set `Authorization: Bearer <value>` The configured client timeout applies by default. A positive effective
- do not read `Target.APIKeyEnv` `timeout_seconds` replaces it. An explicit request override of zero disables
the HTTP-client timeout; negative values are rejected before a request is sent.
If `Target.APIKey` is empty and `Target.APIKeyEnv` is set: ## Response Subset And Failures
- resolve environment variable value at request time A successful provider response must supply non-empty
- set `Authorization: Bearer <value>` `choices[0].message.content`. Scriptorium reads these optional or required
usage fields when present:
If the environment variable is unset/empty:
- request fails before HTTP call (`ErrInvalidRequest`)
If both `Target.APIKey` and `Target.APIKeyEnv` are empty:
- no `Authorization` header is sent
## Timeout Behavior
Base timeout comes from client configuration.
Per-request override:
- if `Target.TimeoutSeconds > 0`, use that value for request timeout
- if `Target.TimeoutSeconds == 0` and the value came from an explicit request override, disable the HTTP client timeout
- if `Target.TimeoutSeconds < 0`, request is rejected (`ErrInvalidRequest`)
## Response Expectations
Expected successful response shape (subset used):
- `choices[0].message.content`
- `usage.prompt_tokens` - `usage.prompt_tokens`
- `usage.completion_tokens` - `usage.completion_tokens`
- `usage.total_tokens` - `usage.total_tokens`
- `usage.prompt_tokens_details.cached_tokens` (optional) - `usage.prompt_tokens_details.cached_tokens`
- `usage.cache_write_tokens` (optional) - `usage.cache_write_tokens`
Absent cache usage fields are treated as zero. Parsed cache usage is exposed through run results and adapter response surfaces as: Missing cache usage is reported as zero. Invalid JSON, an empty choices array,
or empty first-choice content is a malformed provider response. Network and
request-construction failures, non-2xx responses, and malformed responses fail
the outbound call. Provider response bodies are not exposed by this client.
- `cached_tokens` The client does not implement built-in retries, tool calls, top-level
- `cache_write_tokens` `cache_control`, or multi-request payload modes.
Malformed response conditions include: ## Related References
- invalid JSON Prompt schema preparation and runner orchestration are described in
- empty `choices` [runner internals](../internal/runner.md). Prompt and profile configuration is
- empty `choices[0].message.content` defined by the [configuration reference](../config.md).
Malformed responses return `ErrMalformedResponse`.
## Error Handling
- network/request-construction failures: `ErrRequestFailed`
- non-2xx HTTP status: `ErrUnexpectedStatus` (includes status code; provider response bodies are not included)
- malformed response shape/content: `ErrMalformedResponse`
## Unsupported Or Non-Serialized Fields
The client does not serialize top-level `cache_control`.
No built-in retries, tool-calls, or multi-request payload modes are implemented in this client.
## Relationship To Runner
When prompt validation mode is `json_schema`, runner prepares a structured-output schema spec and passes it to the client as `StructuredOutput`.
The client only serializes the provider request payload; it does not load schema files itself.

View File

@@ -1,130 +1,41 @@
# Subprocess Integration # Subprocess Integration
This document defines the supported subprocess contract for downstream This document covers process-boundary behavior for callers that invoke
applications invoking Scriptorium through the public CLI. Scriptorium as a child process. Command syntax, flags, output, and exit codes
are defined by the [CLI reference](../cli.md). Interface selection belongs in
the [consumer integration overview](../consumers/api.md).
This is a CLI contract. Go callers that want an in-process typed API should use ## Process Contract
the [package guide](../consumers/pkg-scriptorium.md).
## Supported Commands Use `scriptorium render` when the caller needs prepared output without a model
call, and `scriptorium run` for generation. Pass an explicit `--config` or
make the configuration search paths available to the child process; configuration
discovery, fields, profile selection, and credential mechanisms are defined in
the [configuration reference](../config.md).
Downstream applications should invoke: Pass required API-key environment variables through the child environment. Do
not place raw API keys in arguments. Keep the environment limited to the values
needed for the selected profile.
- `scriptorium render` for preflight/debug output without LLM execution. ## Streams And Output Ownership
- `scriptorium run` for generation.
`scriptorium serve` is an HTTP service command, not the recommended subprocess Capture stdout and stderr separately. Stdout contains the requested artifact or
contract for per-request execution. prepared output unless the caller selects an output file; stderr contains
summaries, diagnostics, and server messages. The exact destinations and status
meanings are part of the [CLI reference](../cli.md), not a stable stderr data
protocol.
## Recommended Invocation Shapes When using `--out`, the caller owns the output path, its permissions, and
cleanup. Treat rendered prompts, generated artifacts, stdout, and stderr as
potentially sensitive.
Render: ## Cancellation And Recovery
```bash A CLI invocation performs one synchronous request and creates no durable run
scriptorium render \ state. A supervising process that needs cancellation must terminate the child
--config <config_path> \ process according to its own process-management policy. A later invocation is a
--prompt <prompt_id> \ new request and can make another model call; there is no resume or checkpoint
--input transcript=<path> \ protocol.
--format json
```
Run: For deployment, filesystem permissions, and sensitive-artifact handling, see
the [operations guide](../operations.md).
```bash
scriptorium run \
--config <config_path> \
--prompt <prompt_id> \
--input transcript=<path> \
--out <artifact_path>
```
Callers may add:
- `--profile <profile_id>`
- repeatable `--input name=path`
- repeatable `--var name=value`
- runtime overrides when explicitly needed, such as `--model`, `--llm-base-url`, `--api-key-env`, and `--timeout`
Do not pass raw API keys as command arguments.
## Config And Directory Behavior
Callers can rely on resolved app config or pass explicit paths.
Default config search order:
1. `/usr/local/etc/scriptorium/config.yml`
2. `/etc/scriptorium/config.yml`
Rules:
- Explicit `--config` requires file existence and valid syntax.
- CLI flags override config values.
- `run` and `render` require an effective `prompt_dir`.
- `profile_dir` is optional because built-in profiles are available.
## Profile Selection
Profile selection follows runner behavior:
1. explicit `--profile`
2. prompt `default_profile`
3. error if neither is available
Treat prompt and profile IDs as deployment configuration, not hardcoded business
logic.
## Input And Variable Contract
- Inputs use repeated `--input name=path`.
- Input names must match prompt definition input names.
- Variables use repeated `--var name=value`.
- Both flags also accept comma-separated mappings.
- Prefer file inputs for large content.
CLI inputs are file references. HTTP-only `inline` references are documented in
the [HTTP API reference](../api.md).
## Environment Contract
- Pass through required API-key environment variables referenced by `api_key_env`.
- Keep subprocess environments scoped to required variables.
- Use `--api-key-env` only to name an environment variable.
- Never pass raw API keys via argv.
## Stdout And Stderr
`run`:
- stdout: generated artifact body unless `--out` is used.
- stderr: success summary and errors.
`render`:
- stdout: prepared-run output unless `--out` is used.
- stderr: errors.
Capture stdout and stderr separately. Do not parse stderr as a stable data
format beyond exit status handling.
## Exit Status Contract
- `0`: success.
- `1`: parse, config, load, render, generation, IO, or runtime error.
- `2`: `run` completed and output was written, but validation failed.
A `run` exit code `2` can still produce output on stdout or at `--out`.
Consumers must decide whether to keep or discard that output.
## Security Notes
- Treat generated artifacts, rendered prompts, stdout, and stderr as potentially sensitive.
- Use controlled output paths and access controls for persisted artifacts.
- Avoid logging full rendered prompts or generated artifacts by default.
## Canonical References
- CLI behavior: [CLI reference](../cli.md)
- Config and file formats: [Configuration reference](../config.md)
- Operations: [Operations guide](../operations.md)
- Troubleshooting: [Troubleshooting](../troubleshooting.md)