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.
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
file formats, see [Configuration reference](config.md).
The service address and HTTP limits are configured as described in the
[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
running `serve` process with an artifact root that can read the referenced
files, plus a reachable model endpoint for full execution.
## 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.
Requests and responses are JSON objects. Requests are decoded as JSON regardless
of their `Content-Type`; successful JSON responses use
`Content-Type: application/json`. There are no query parameters.
## 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.
- `server.max_artifact_bytes`: file artifact limit for HTTP `file` input references.
- `server.max_response_bytes`: encoded JSON response limit, including artifact body and optional raw output.
Each limit defaults to `16777216` bytes. `0` disables that limit.
A request body over its limit returns `413 request_too_large`; an oversized
file input returns `413 artifact_too_large`; an oversized encoded response
returns `413 response_too_large`.
## `POST /v1/runs`
Runs one prompt request and returns the generated artifact, validation result,
and metadata.
### Request Body
The maintained [request example](../examples/http-run.json) is a complete
copyable shape. The smallest valid shape is:
```json
{
"prompt_id": "generic.markdown_summary",
"profile_id": "local-fast",
"prompt_version": "1.0.0",
"inputs": {
"transcript": {
"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
"transcript": {"type": "inline", "body": "Source text"}
}
}
```
Request fields:
| Field | Required | Description |
| Field | Required | Meaning |
| --- | --- | --- |
| `prompt_id` | yes | Prompt ID. Must not be blank. |
| `prompt_id` | yes | Non-blank prompt ID. |
| `prompt_version` | no | Prompt version filter. |
| `profile_id` | no | Execution profile ID. If omitted, the prompt must define `default_profile`. |
| `inputs` | yes | Object mapping prompt input names to input references. Must contain at least one entry. |
| `vars` | no | Object mapping template variable names to string values. |
| `model` | no | Runtime model override object. |
| `include_raw_output` | no | When `true`, include `raw_model_output` in the response. |
| `profile_id` | no | Execution-profile ID; otherwise the prompt must set `default_profile`. |
| `inputs` | yes | Non-empty object mapping input names to references. |
| `vars` | no | Object mapping template-variable names to strings. |
| `model` | no | Runtime model-override object. |
| `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 |
| --- | --- | --- |
| `type` | yes | `file` or `inline`. |
| `uri` | for `file` | File URI/path. |
| `body` | for `inline` | Inline artifact body. |
HTTP file references require a configured artifact root. Relative paths resolve
within that root. Absolute paths must be lexically within it; traversal outside
it is rejected with `400 artifact_not_allowed`. This lexical check does not
resolve symlinks: the operating system follows symlinks inside the root,
including ones that target outside it. Keep the root narrow and inaccessible to
untrusted writers.
HTTP `file` references require `server.artifact_root` or `serve
--artifact-root`. Relative file URIs resolve against that root. Absolute file
URIs are accepted only when lexically inside the root. Relative traversal and
absolute paths outside the root return `400 artifact_not_allowed`.
The optional `model` object accepts `endpoint`, `model`, `temperature`,
`max_tokens`, `top_p`, `timeout_seconds`, `service_tier`,
`reasoning_effort`, `api_key_env`, and `extra_params`. Numeric ranges and
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
the artifact root are followed by the operating system, including symlinks that
point outside the root. Keep the artifact root narrow and not writable by
untrusted users.
Raw API-key values are not accepted. `api_key` and any other unknown model
field cause `400 invalid_json`.
Model override fields:
### Strict JSON
| Field | Description |
| --- | --- |
| `endpoint` | Runtime endpoint override. |
| `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`
Request decoding rejects malformed JSON, unknown fields at every request level,
and trailing JSON tokens with `400 invalid_json`. A blank `prompt_id` or
empty `inputs` object returns `400 invalid_request`.
### 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": "output",
"content_type": "text/markdown",
"body": "Generated content",
"size": 17,
"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
}
}
```
- `artifact`: `name`, `content_type`, `body`, `size`, `hash`, and
optional `uri`;
- `validation`: `status`, `mode`, `repair_attempts`, `is_valid`, plus
optional `errors` and `schema_path`;
- `metadata`: run, prompt, rendered-prompt, profile, model, input-hash, usage,
timing, validation, and repair-attempt metadata; and
- optional `raw_model_output` when requested.
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.
- `validation`: validation result for the generated artifact.
- `metadata`: run and effective runtime 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`.
A validation failure has `validation.status: "failed"`, `is_valid: false`,
and any available diagnostic errors, while still returning the artifact and
metadata.
## Error Responses
Error body shape:
Errors have this shape:
```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 |
| --- | --- | --- |
| `400` | `invalid_json` | Malformed JSON, unknown JSON field, or trailing JSON token. |
| `400` | `invalid_request` | Missing/invalid request fields or invalid runtime overrides. |
| `400` | `profile_required` | No `profile_id` and prompt has no `default_profile`. |
| `400` | `prompt_load_failed` | Prompt definition YAML/contract failed to load. |
| `400` | `profile_load_failed` | Profile YAML/contract failed to load, including raw `api_key`. |
| `400` | `artifact_not_allowed` | HTTP file refs are disabled or requested path is outside artifact root. |
| `400` | `artifact_read_failed` | Input artifact could not be read or input ref was unsupported/invalid. |
| `400` | `invalid_json` | Malformed JSON, unknown field, or trailing JSON. |
| `400` | `invalid_request` | Missing or invalid request data or runtime override. |
| `400` | `profile_required` | No profile ID and no prompt default profile. |
| `400` | `prompt_load_failed` | Prompt definition failed to load. |
| `400` | `profile_load_failed` | Profile failed to load. |
| `400` | `artifact_not_allowed` | HTTP file input is disabled or outside the artifact root. |
| `400` | `artifact_read_failed` | Input artifact is invalid or cannot be read. |
| `400` | `prompt_render_failed` | Prompt template rendering failed. |
| `400` | `api_key_env_missing` | Selected `api_key_env` variable is unset or empty. |
| `404` | `not_found` | Route path is unknown. |
| `404` | `prompt_not_found` | Prompt ID/version was not found. |
| `404` | `profile_not_found` | Profile ID was not found. |
| `405` | `method_not_allowed` | Method is not `POST` on `/v1/runs`. |
| `413` | `request_too_large` | Encoded JSON request body exceeds configured request limit. |
| `413` | `artifact_too_large` | HTTP file input artifact exceeds configured artifact limit. |
| `413` | `response_too_large` | Encoded JSON response exceeds configured response limit. |
| `500` | `validation_runtime_failed` | Validator runtime/schema loading failed. |
| `500` | `internal_error` | Unclassified server error. |
| `400` | `api_key_env_missing` | The selected credential environment variable is unset or empty. |
| `404` | `not_found` | Route does not exist. |
| `404` | `prompt_not_found` | Prompt ID or version does not exist. |
| `404` | `profile_not_found` | Profile ID does not exist. |
| `405` | `method_not_allowed` | The route does not accept the method. |
| `413` | `request_too_large` | Encoded request exceeds its limit. |
| `413` | `artifact_too_large` | File input exceeds its limit. |
| `413` | `response_too_large` | Encoded response exceeds its limit. |
| `500` | `validation_runtime_failed` | Schema or validator runtime failure. |
| `500` | `internal_error` | Unclassified server failure. |
| `502` | `llm_failed` | Outbound model request failed. |
HTTP error messages are intentionally concise and do not include sensitive
internal causes.
## Retry And Idempotency
Scriptorium does not provide idempotency keys, pagination, caching headers, or
rate limiting.
Clients may retry transport failures or `5xx` responses when their surrounding
workflow can tolerate another model call. A retry can generate different output
and incur another provider request.
## Example File
- `examples/http-run.json`
Scriptorium provides no idempotency keys, pagination, caching headers, or rate
limits. Clients may retry transport failures or `5xx` responses only when
their workflow tolerates another model call: a retry can produce different
output and incur another provider request.