Compare commits
12 Commits
v0.11.1
...
a0e905ce46
| Author | SHA1 | Date | |
|---|---|---|---|
| a0e905ce46 | |||
| 719243e90c | |||
| a9e1b7435c | |||
| eb6dfb19b0 | |||
| d86b65adad | |||
| 31faaf4259 | |||
| 9932153b97 | |||
| f0ca233c25 | |||
| ff31f8daf8 | |||
| c927b7819d | |||
| 6d1fb66dd7 | |||
| e0b1d6a0dc |
@@ -1,4 +1 @@
|
|||||||
Please carefully review the relevant documents in `docs/policy` before making any changes to this repository.
|
Please review `docs/development.md` for initial orientation in this repository and follow its task-specific reading guide.
|
||||||
- `development.md` defines the contributor workflow for this application.
|
|
||||||
- `architecture.md` provides the canonical high-level architecture policy for this repository, and should be reviewed before writing or changing any code.
|
|
||||||
- `documentation.md` provides the canonical documentation policy for this repository, and should be reviewed before writing or changing any documentation.
|
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -22,6 +22,7 @@ go run ./cmd/scriptorium render \
|
|||||||
```
|
```
|
||||||
|
|
||||||
This command renders the prepared prompt and effective runtime settings without calling an LLM.
|
This command renders the prepared prompt and effective runtime settings without calling an LLM.
|
||||||
|
For complete invocation and output behavior, see the [CLI reference](docs/cli.md).
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
@@ -29,7 +30,6 @@ This command renders the prepared prompt and effective runtime settings without
|
|||||||
- [Configuration reference](docs/config.md)
|
- [Configuration reference](docs/config.md)
|
||||||
- [HTTP API reference](docs/api.md)
|
- [HTTP API reference](docs/api.md)
|
||||||
- [Operations guide](docs/operations.md)
|
- [Operations guide](docs/operations.md)
|
||||||
- [Troubleshooting](docs/troubleshooting.md)
|
|
||||||
- [Consumer integration overview](docs/consumers/api.md)
|
- [Consumer integration overview](docs/consumers/api.md)
|
||||||
- [Go library package](docs/consumers/pkg-scriptorium.md)
|
- [Go library package](docs/consumers/pkg-scriptorium.md)
|
||||||
- [Subprocess integration](docs/integrations/subprocess.md)
|
- [Subprocess integration](docs/integrations/subprocess.md)
|
||||||
@@ -38,8 +38,8 @@ This command renders the prepared prompt and effective runtime settings without
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
- `examples/config.yml`
|
- [Minimal configuration](examples/config.yml) and [complete configuration](examples/config.full.yml)
|
||||||
- `examples/config.full.yml`
|
- [Prompt definitions](examples/prompts/), [execution profiles](examples/profiles/), [schemas](examples/schemas/), and [synthetic input fixtures](examples/fixtures/)
|
||||||
- `examples/render-markdown-summary.sh`
|
- [Render script](examples/render-markdown-summary.sh)
|
||||||
- `examples/http-run.json`
|
- [HTTP request](examples/http-run.json)
|
||||||
- `examples/go-library/prepare`
|
- [Go library example](examples/go-library/prepare/main.go)
|
||||||
|
|||||||
49
docs/adr/0001-adopt-canonical-documentation-ownership.md
Normal file
49
docs/adr/0001-adopt-canonical-documentation-ownership.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# ADR 0001: Adopt Canonical Documentation Ownership
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Date
|
||||||
|
|
||||||
|
2026-07-26
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Scriptorium's documentation grew alongside its CLI, HTTP, public Go, and
|
||||||
|
integration interfaces. As a result, several documents repeated mutable
|
||||||
|
contracts such as flags, configuration fields, and status behavior. Those
|
||||||
|
parallel definitions made it unclear which document to update when behavior
|
||||||
|
changed and increased the risk of documentation drift.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Assign each documentation topic one canonical owner, as defined in
|
||||||
|
[`docs/policy/documentation.md`](../policy/documentation.md). Non-owning
|
||||||
|
documents may provide short orientation and links, but do not redefine volatile
|
||||||
|
contracts. Current behavior is documented outside `docs/roadmap/`; roadmaps own
|
||||||
|
future work, sequencing, and implementation status.
|
||||||
|
|
||||||
|
## Alternatives Considered
|
||||||
|
|
||||||
|
- Keep broad reference material in several audience-specific documents. This
|
||||||
|
would preserve local convenience but leave conflicting contract definitions
|
||||||
|
likely.
|
||||||
|
- Consolidate all documentation into one reference. This would reduce duplicate
|
||||||
|
text but would not serve the distinct needs of users, operators, consumers,
|
||||||
|
and contributors.
|
||||||
|
|
||||||
|
## Rationale
|
||||||
|
|
||||||
|
Canonical ownership retains audience-specific guidance while making the source
|
||||||
|
of truth for each contract discoverable. It also makes documentation changes
|
||||||
|
reviewable alongside the implementation change that requires them.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Changes to behavior must update the canonical owner in the same change.
|
||||||
|
- Cross-cutting documentation links to the owner instead of copying its
|
||||||
|
details.
|
||||||
|
- Documentation restructuring follows the implementation sequence in
|
||||||
|
[`docs/roadmap/documentation.md`](../roadmap/documentation.md); the roadmap,
|
||||||
|
not this ADR, records completion status.
|
||||||
336
docs/api.md
336
docs/api.md
@@ -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`
|
|
||||||
|
|||||||
277
docs/cli.md
277
docs/cli.md
@@ -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`
|
|
||||||
|
|||||||
393
docs/config.md
393
docs/config.md
@@ -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)
|
||||||
|
|||||||
@@ -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.
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Package scriptorium
|
# Package `scriptorium`
|
||||||
|
|
||||||
Import path:
|
Import path:
|
||||||
|
|
||||||
@@ -6,250 +6,166 @@ 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` | Base timeout for the built-in OpenAI-compatible client when `HTTPClient` is absent or has a zero timeout. A non-positive value uses the internal default. |
|
||||||
|
| `HTTPClient` | Optional HTTP client for that built-in client. It is cloned; a non-zero `Timeout` on it takes precedence over `Config.Timeout` as the base timeout. |
|
||||||
|
|
||||||
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
|
An effective positive `timeout_seconds` replaces the base timeout. An explicit
|
||||||
|
request override of zero disables the HTTP-client timeout. The timeout is
|
||||||
|
otherwise inherited from the supplied client, `Config.Timeout`, or the internal
|
||||||
|
default in that order.
|
||||||
|
|
||||||
|
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.
|
||||||
|
Single-file prompt and profile sources are selected by their YAML `id`, not
|
||||||
|
their file names. `WithPromptFile` resolves relative `content_file` paths from
|
||||||
|
the prompt file's directory. `WithSchemaFile` exposes its schema by the schema
|
||||||
|
file's 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 +178,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)
|
|
||||||
|
|||||||
63
docs/development.md
Normal file
63
docs/development.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# Development
|
||||||
|
|
||||||
|
This is the contributor entry point for Scriptorium. Use the task-specific
|
||||||
|
reading guide below before making changes. Canonical architecture, contracts,
|
||||||
|
component behavior, and policies remain in their owning documents.
|
||||||
|
|
||||||
|
## Initial Orientation
|
||||||
|
|
||||||
|
Before starting work:
|
||||||
|
|
||||||
|
1. inspect the working tree and preserve unrelated changes;
|
||||||
|
2. read the architecture policy for code or design work;
|
||||||
|
3. read the policy, contract, and internal documents listed for the task;
|
||||||
|
4. inspect the relevant implementation and tests before deciding how to change
|
||||||
|
them.
|
||||||
|
|
||||||
|
Start with:
|
||||||
|
|
||||||
|
- [Architecture policy](policy/architecture.md) for system boundaries,
|
||||||
|
invariants, and non-goals;
|
||||||
|
- [Internal component overview](internal/overview.md) for the current package
|
||||||
|
and component map;
|
||||||
|
- [Documentation policy](policy/documentation.md) before changing
|
||||||
|
documentation;
|
||||||
|
- [Testing policy](policy/testing.md) before adding, rewriting, or deleting
|
||||||
|
tests.
|
||||||
|
|
||||||
|
## Task-Specific Reading Guide
|
||||||
|
|
||||||
|
| Task | Read before changing |
|
||||||
|
| --- | --- |
|
||||||
|
| Repository orientation or component responsibility | [Internal component overview](internal/overview.md) and [architecture policy](policy/architecture.md) |
|
||||||
|
| Public Go package or engine behavior | [Go package consumer contract](consumers/pkg-scriptorium.md), [internal component overview](internal/overview.md), [runner internals](internal/runner.md), [adapter internals](internal/adapters.md), and [source internals](internal/sources.md) |
|
||||||
|
| CLI commands, flags, output, or exit behavior | [CLI contract](cli.md), [internal component overview](internal/overview.md), and [adapter internals](internal/adapters.md) |
|
||||||
|
| HTTP routes, DTOs, limits, or status mapping | [HTTP API contract](api.md), [internal component overview](internal/overview.md), [adapter internals](internal/adapters.md), and [source internals](internal/sources.md) |
|
||||||
|
| Application configuration | [Configuration contract](config.md), [internal component overview](internal/overview.md), [adapter internals](internal/adapters.md), and [source internals](internal/sources.md) |
|
||||||
|
| Prompt, profile, schema, or artifact loading | [Configuration contract](config.md), [internal component overview](internal/overview.md), and [source internals](internal/sources.md) |
|
||||||
|
| Runner orchestration, rendering, validation, or repair | [Runner internals](internal/runner.md) and [source internals](internal/sources.md) |
|
||||||
|
| OpenAI-compatible request or response behavior | [OpenAI-compatible integration](integrations/openai-compatible-chat.md), [LLM internals](internal/llm.md), [runner internals](internal/runner.md), and [adapter internals](internal/adapters.md) |
|
||||||
|
| Subprocess behavior | [Subprocess integration](integrations/subprocess.md) and [CLI contract](cli.md) |
|
||||||
|
| Runtime operation or recovery | [Operations](operations.md) |
|
||||||
|
| Examples or copyable assets | The owning contract for the demonstrated behavior and the related files under `examples/` |
|
||||||
|
| Architecture decisions or future work | The [documentation policy](policy/documentation.md), relevant accepted ADRs such as [ADR 0001](adr/0001-adopt-canonical-documentation-ownership.md), and relevant roadmap documents under `roadmap/` |
|
||||||
|
|
||||||
|
For cross-cutting changes, follow every applicable row. Internal component
|
||||||
|
documents own detailed subsystem change recipes.
|
||||||
|
|
||||||
|
## Baseline Validation
|
||||||
|
|
||||||
|
Use focused checks while iterating, then run validation proportionate to the
|
||||||
|
change and the risks described by the testing policy.
|
||||||
|
|
||||||
|
The repository-level baseline for code changes is:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./...
|
||||||
|
go vet ./...
|
||||||
|
go build ./cmd/scriptorium
|
||||||
|
```
|
||||||
|
|
||||||
|
Documentation-only work does not require the full Go suite unless it changes
|
||||||
|
commands, examples, generated output, or another behavior that the suite
|
||||||
|
validates. Always check changed links, paths, examples, and canonical ownership.
|
||||||
@@ -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,43 @@ 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 client base timeout is chosen at construction: a non-zero timeout on a
|
||||||
- do not read `Target.APIKeyEnv`
|
supplied `http.Client` takes precedence over `Config.Timeout`; otherwise a
|
||||||
|
positive `Config.Timeout` is used, then the internal default. A positive
|
||||||
|
effective `timeout_seconds` replaces that base. 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.
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
|
|||||||
@@ -2,154 +2,112 @@
|
|||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
Adapters translate external interfaces into domain requests and translate domain results back out. They wire dependencies, apply app config, and own IO concerns, but they do not make runner decisions.
|
Adapters translate external inputs into domain requests, compose dependencies,
|
||||||
|
and translate domain results or errors back to their interface. They own IO and
|
||||||
|
presentation mechanics; use-case decisions remain in `internal/usecase`.
|
||||||
|
|
||||||
Source-loading behavior belongs in `docs/internal/sources.md`. User-facing CLI, HTTP, and package contracts belong in `docs/cli.md`, `docs/api.md`, and `docs/consumers/pkg-scriptorium.md`.
|
External contracts are canonical in the [CLI reference](../cli.md), [HTTP API
|
||||||
|
reference](../api.md), and [Go package contract](../consumers/pkg-scriptorium.md).
|
||||||
|
|
||||||
## Adapter Map
|
## Components And Collaborators
|
||||||
|
|
||||||
- `cmd/scriptorium`: process entrypoint.
|
- `cmd/scriptorium` passes process arguments and streams to
|
||||||
- `internal/adapter/cli`: command parsing, config handoff, runner construction, stdout/stderr, exit codes.
|
`internal/adapter/cli`.
|
||||||
- `internal/adapter/http`: `POST /v1/runs` request/response mapping and HTTP error/status mapping.
|
- `internal/adapter/cli` parses commands, resolves application settings through
|
||||||
- root package `scriptorium`: public Go facade over internal runner types and dependencies.
|
`internal/config`, constructs a runner, and owns process output handling.
|
||||||
|
- `internal/adapter/http` decodes DTOs, maps them to `domain.RunRequest`, calls
|
||||||
|
a runner interface, and maps errors and results to HTTP DTOs.
|
||||||
|
- The root `scriptorium` package maps its public types and options to internal
|
||||||
|
collaborators and maps selected internal errors to public sentinels.
|
||||||
|
- `internal/format` formats prepared runs for the CLI; `internal/llm`,
|
||||||
|
`internal/prompt`, and source packages supply runner dependencies.
|
||||||
|
|
||||||
Supporting implementation packages used during adapter wiring:
|
## Wiring Flows
|
||||||
|
|
||||||
- `internal/config`
|
### CLI
|
||||||
- `internal/defaults`
|
|
||||||
- `internal/format`
|
|
||||||
- `internal/llm`
|
|
||||||
- `internal/prompt`
|
|
||||||
|
|
||||||
## Inputs And Outputs
|
The CLI resolves configuration before constructing dependencies. `run` builds a
|
||||||
|
runner with the ordinary composite artifact reader and invokes `Runner.Run`;
|
||||||
|
`render` uses the same wiring and invokes `Runner.Prepare`; `serve` replaces the
|
||||||
|
file reader with the restricted artifact reader, builds an HTTP handler, and
|
||||||
|
starts the server.
|
||||||
|
|
||||||
CLI adapter:
|
Parser state records whether numeric runtime values were explicitly supplied.
|
||||||
|
That presence is carried into `domain.ExecutionTargetOverride`, allowing the
|
||||||
|
runner to distinguish omitted values from explicit zero overrides.
|
||||||
|
|
||||||
- Input: process args, optional config file, filesystem sources, environment variables.
|
### HTTP
|
||||||
- Output: process exit code, stdout artifact/prepared output, stderr summaries and errors.
|
|
||||||
|
|
||||||
HTTP adapter:
|
The handler first enforces transport limits, strict JSON decoding, and the
|
||||||
|
minimal request shape. It maps DTO values to domain types without deciding
|
||||||
|
prompt selection, source behavior, or validation semantics. On success it maps
|
||||||
|
the domain result to the response DTO; on failure it uses `errors.Is` over
|
||||||
|
runner, source, artifact, and profile errors to choose the public error mapping.
|
||||||
|
|
||||||
- Input: HTTP request method/path/headers/body for `POST /v1/runs`.
|
The [HTTP API reference](../api.md) owns the route, DTO schema, status codes,
|
||||||
- Output: JSON success or error body with mapped status code.
|
and externally observable limit behavior.
|
||||||
|
|
||||||
Public Go facade:
|
### Public Go Facade
|
||||||
|
|
||||||
- Input: typed `scriptorium.Config`, `Option`, and `RunRequest` values.
|
`NewEngine` applies public options, selects filesystem, `fs.FS`, single-file,
|
||||||
- Output: typed `PreparedRun` and `RunResult` values plus public sentinel errors.
|
or in-memory dependencies, and constructs a runner. The conversion functions
|
||||||
|
copy maps and slices across the boundary so callers do not receive internal
|
||||||
|
domain values. The facade maps selected internal errors to the public sentinel
|
||||||
|
set and keeps direct request API keys out of public results.
|
||||||
|
|
||||||
## Boundaries
|
## Package-Local Guarantees
|
||||||
|
|
||||||
- Adapters convert external shapes to `domain.RunRequest` and back.
|
- Adapters do not embed runner orchestration or source-loading decisions.
|
||||||
- Runner orchestration remains in `internal/usecase`.
|
- Configuration is resolved before adapter dependency composition.
|
||||||
- Prompt/profile/schema/artifact source rules remain in repository, validator, and artifact packages.
|
- CLI and HTTP create runners without a repairer; a repairer is available only
|
||||||
- LLM provider request serialization remains in `internal/llm`.
|
through explicit internal runner construction.
|
||||||
- Public package types are facade types; internal domain types do not leak across the package boundary.
|
- DTO conversion preserves explicit numeric-override presence.
|
||||||
|
- Error mapping matches error identities, not error text.
|
||||||
|
- No adapter creates durable run state; caller-selected output files are not
|
||||||
|
application state.
|
||||||
|
|
||||||
## Config Fields Used
|
## Failure And Verification Boundaries
|
||||||
|
|
||||||
Adapter app settings:
|
Keep external error payloads concise, preserve strict external decoding, and do
|
||||||
|
not serialize resolved secret values. Validation content failures remain result
|
||||||
|
state; runtime failures remain errors for the relevant adapter to map.
|
||||||
|
|
||||||
- `prompt_dir`
|
Inspect focused tests when changing this area:
|
||||||
- `profile_dir`
|
|
||||||
- `schema_dir`
|
|
||||||
- `server.addr`
|
|
||||||
- `server.artifact_root`
|
|
||||||
- `server.max_request_bytes`
|
|
||||||
- `server.max_artifact_bytes`
|
|
||||||
- `server.max_response_bytes`
|
|
||||||
- `defaults.render_format`
|
|
||||||
|
|
||||||
Execution request/profile settings passed through the runner:
|
|
||||||
|
|
||||||
- `endpoint`
|
|
||||||
- `model`
|
|
||||||
- `temperature`
|
|
||||||
- `max_tokens`
|
|
||||||
- `top_p`
|
|
||||||
- `timeout_seconds`
|
|
||||||
- `service_tier`
|
|
||||||
- `api_key_env`
|
|
||||||
- `reasoning_effort`
|
|
||||||
- `extra_params`
|
|
||||||
|
|
||||||
CLI and HTTP preserve numeric override presence so omitted values and explicit zero values remain distinct.
|
|
||||||
|
|
||||||
## CLI Adapter
|
|
||||||
|
|
||||||
Implemented commands:
|
|
||||||
|
|
||||||
- `run`
|
|
||||||
- `render`
|
|
||||||
- `serve`
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
|
|
||||||
- `run` constructs a runner with direct filesystem artifact reading and calls `Runner.Run`.
|
|
||||||
- `render` constructs a runner and calls `Runner.Prepare`; it does not call the LLM.
|
|
||||||
- `serve` constructs a restricted artifact reader and HTTP handler, then starts an unauthenticated HTTP server.
|
|
||||||
- `run` exits `2` when generation succeeds but validation fails.
|
|
||||||
- parse, runtime, and output-write errors exit `1`.
|
|
||||||
- deprecated `--prompt-id` and `--profile-id` aliases are accepted.
|
|
||||||
|
|
||||||
## HTTP Adapter
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
|
|
||||||
- Accepts only `POST /v1/runs`.
|
|
||||||
- Decodes JSON strictly and rejects unknown fields and trailing JSON tokens.
|
|
||||||
- Rejects empty `prompt_id` and empty `inputs` before calling the runner.
|
|
||||||
- Does not accept raw API key values in the request body.
|
|
||||||
- Returns validation failures as `200` responses with failed validation details.
|
|
||||||
- Maps request-body, artifact, and encoded-response size failures to `413`.
|
|
||||||
- Maps domain and repository errors to stable error codes without returning wrapped internal cause text.
|
|
||||||
|
|
||||||
The HTTP adapter has no built-in authentication or authorization. Deployment controls must be provided outside the process.
|
|
||||||
|
|
||||||
## Public Go Facade
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
|
|
||||||
- `NewEngine` wires the same default runner components as CLI/HTTP unless options override them.
|
|
||||||
- Prompt, profile, and schema sources may come from directories, single files, or `fs.FS` roots.
|
|
||||||
- `WithProfiles` adds in-memory profiles ahead of file-backed and built-in profiles.
|
|
||||||
- `WithLLMClient` injects custom model behavior.
|
|
||||||
- `RunRequest.APIKey` is request-scoped and direct; it is used only for generation and is stripped from public results.
|
|
||||||
- internal errors are mapped to public sentinels in `errors.go`.
|
|
||||||
|
|
||||||
## Failure Behavior
|
|
||||||
|
|
||||||
Adapters should:
|
|
||||||
|
|
||||||
- keep external error payloads concise and stable.
|
|
||||||
- avoid leaking raw secret values.
|
|
||||||
- use sentinels and typed errors for mapping.
|
|
||||||
- preserve strict external input decoding.
|
|
||||||
- keep validation content failures distinct from runtime errors.
|
|
||||||
|
|
||||||
CLI writes human-readable summaries to stderr. HTTP writes JSON error envelopes. The public Go facade returns typed errors.
|
|
||||||
|
|
||||||
## State And Manifests
|
|
||||||
|
|
||||||
Adapters do not add durable run state.
|
|
||||||
|
|
||||||
- No adapter writes run manifests.
|
|
||||||
- No adapter implements checkpoint, skip, or resume behavior.
|
|
||||||
- CLI output files are caller-selected artifacts, not internal state.
|
|
||||||
|
|
||||||
## Tests To Inspect
|
|
||||||
|
|
||||||
- `internal/adapter/cli/run_test.go`
|
- `internal/adapter/cli/run_test.go`
|
||||||
- `internal/adapter/http/handler_test.go`
|
- `internal/adapter/http/handler_test.go`
|
||||||
- `engine_test.go`
|
- `engine_test.go`
|
||||||
- `internal/format/prepared_run_test.go`
|
- `internal/format/prepared_run_test.go`
|
||||||
- `internal/llm/openai_compatible_client_test.go`
|
|
||||||
|
|
||||||
## Architectural Invariants
|
Run the affected adapter package tests and recheck the relevant canonical
|
||||||
|
contract. The [testing policy](../policy/testing.md) owns global test
|
||||||
|
sufficiency guidance.
|
||||||
|
|
||||||
- Adapter packages stay thin and translation-focused.
|
## Change Recipes
|
||||||
- App config is resolved before dependency construction.
|
|
||||||
- External input strictness is part of contract stability.
|
### Application Configuration Fields
|
||||||
- CLI and HTTP construct runners without a repairer.
|
|
||||||
- HTTP endpoint details remain canonical in `docs/api.md`.
|
1. Add the field to the relevant `internal/config` shape and default handling.
|
||||||
- Public Go package details remain canonical in `docs/consumers/pkg-scriptorium.md`.
|
2. Parse and validate it, then preserve configuration and CLI-override
|
||||||
|
precedence while wiring it through its consuming adapter.
|
||||||
|
3. Add focused configuration and adapter tests for parsing, mapping, and
|
||||||
|
effective behavior.
|
||||||
|
4. Update the [configuration contract](../config.md) and any affected external
|
||||||
|
contract.
|
||||||
|
|
||||||
|
### CLI Flags
|
||||||
|
|
||||||
|
1. Add the flag to the relevant parser in `internal/adapter/cli/run.go`.
|
||||||
|
2. Keep command scope and application-configuration precedence intentional.
|
||||||
|
3. Add or update parser and command tests in
|
||||||
|
`internal/adapter/cli/run_test.go`.
|
||||||
|
4. Update the [CLI contract](../cli.md) and affected maintained examples.
|
||||||
|
|
||||||
|
### Adapter Capabilities
|
||||||
|
|
||||||
|
1. Define or reuse the appropriate domain or use-case interface boundary.
|
||||||
|
2. Implement translation and IO behavior without moving use-case decisions out
|
||||||
|
of `internal/usecase`.
|
||||||
|
3. Add focused mapping, parsing, and error-behavior tests.
|
||||||
|
4. Update this document and the affected public or integration contract. Update
|
||||||
|
[source internals](sources.md) when source-loading behavior changes.
|
||||||
|
|||||||
86
docs/internal/llm.md
Normal file
86
docs/internal/llm.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# LLM Internals
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
`internal/llm` defines the provider-neutral `Client` interface and the
|
||||||
|
OpenAI-compatible client implementation. The [OpenAI-compatible integration
|
||||||
|
contract](../integrations/openai-compatible-chat.md) owns the outbound HTTP wire
|
||||||
|
format and protocol behavior.
|
||||||
|
|
||||||
|
## Construction
|
||||||
|
|
||||||
|
`NewOpenAICompatibleClient` validates a non-empty configured base URL, records
|
||||||
|
an optional default model, and resolves one base timeout. A supplied client with
|
||||||
|
a non-zero timeout supplies that base; otherwise a positive configured timeout
|
||||||
|
is used, then the internal default.
|
||||||
|
|
||||||
|
When callers supply an `http.Client`, construction clones it rather than
|
||||||
|
mutating the caller's instance. A supplied client with no timeout receives the
|
||||||
|
resolved base timeout in the clone. The client stores the trimmed base URL,
|
||||||
|
default model, and cloned client.
|
||||||
|
|
||||||
|
## Generate Flow
|
||||||
|
|
||||||
|
`Generate` receives a `domain.GenerateRequest` from the runner:
|
||||||
|
|
||||||
|
1. validate the effective timeout and choose the request endpoint;
|
||||||
|
2. map the domain request to the internal wire-request representation;
|
||||||
|
3. validate and flatten extra parameters, encode JSON, and create the HTTP
|
||||||
|
request;
|
||||||
|
4. prefer a direct API key, otherwise resolve the configured key environment
|
||||||
|
variable;
|
||||||
|
5. derive a request HTTP client only when an explicit timeout changes the base
|
||||||
|
client;
|
||||||
|
6. execute the request, reject non-success status responses without returning
|
||||||
|
provider response bodies; and
|
||||||
|
7. decode the response subset into `domain.GenerateResponse`.
|
||||||
|
|
||||||
|
`openAIChatRequestFromGenerateRequest` is the conversion boundary for effective
|
||||||
|
model defaults, explicit numeric-presence state, rendered messages, structured
|
||||||
|
output, and session-ID validation. `openAIChatRequestPayload` protects reserved
|
||||||
|
fields and JSON encoding before an HTTP call. The external payload shape is
|
||||||
|
defined only in the [integration contract](../integrations/openai-compatible-chat.md).
|
||||||
|
|
||||||
|
## Error Categories
|
||||||
|
|
||||||
|
The package uses these internal sentinels:
|
||||||
|
|
||||||
|
- `ErrInvalidConfig` for invalid client construction;
|
||||||
|
- `ErrInvalidRequest` for invalid effective generation input;
|
||||||
|
- `ErrRequestFailed` for request construction or transport failures;
|
||||||
|
- `ErrUnexpectedStatus` for non-success HTTP responses; and
|
||||||
|
- `ErrMalformedResponse` for invalid or incomplete successful-response data.
|
||||||
|
|
||||||
|
The runner maps an invalid LLM request to its invalid-request category and
|
||||||
|
other LLM failures to its generation category. Adapters then apply their public
|
||||||
|
error contracts.
|
||||||
|
|
||||||
|
## Package-Local Guarantees
|
||||||
|
|
||||||
|
- The default-model fallback happens before wire encoding.
|
||||||
|
- Per-request timeout handling clones a configured HTTP client when needed; it
|
||||||
|
does not mutate shared client state.
|
||||||
|
- Direct API keys take precedence over environment lookup within this client.
|
||||||
|
- Provider response bodies are discarded for non-success status responses.
|
||||||
|
- The client does not implement retries, tool calls, or a stateful session
|
||||||
|
store.
|
||||||
|
|
||||||
|
## Verification And Change Recipe
|
||||||
|
|
||||||
|
Inspect:
|
||||||
|
|
||||||
|
- `internal/llm/openai_compatible_client_test.go`
|
||||||
|
- `internal/usecase/runner_test.go`
|
||||||
|
- `internal/adapter/http/handler_test.go`
|
||||||
|
|
||||||
|
When changing the client:
|
||||||
|
|
||||||
|
1. keep domain-to-wire mapping inside `internal/llm` and preserve the `Client`
|
||||||
|
interface;
|
||||||
|
2. test construction, timeout selection, mapping, and error categorization;
|
||||||
|
3. update the [OpenAI-compatible integration contract](../integrations/openai-compatible-chat.md)
|
||||||
|
for any observable wire or protocol change; and
|
||||||
|
4. update [runner internals](runner.md) if the client boundary or structured
|
||||||
|
output handoff changes.
|
||||||
|
|
||||||
|
The [testing policy](../policy/testing.md) owns global test sufficiency.
|
||||||
48
docs/internal/overview.md
Normal file
48
docs/internal/overview.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Internal Component Overview
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This is the inventory of Scriptorium's implemented components for contributors.
|
||||||
|
The [architecture policy](../policy/architecture.md) owns normative boundaries
|
||||||
|
and invariants; public behavior belongs in the linked contracts.
|
||||||
|
|
||||||
|
## Public And Command Entrypoints
|
||||||
|
|
||||||
|
| Component | Implemented responsibility | References |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Root package `scriptorium` | Public Go facade that constructs the engine, exposes request/result types and options, and maps internal errors. | [Go package contract](../consumers/pkg-scriptorium.md), [adapter internals](adapters.md) |
|
||||||
|
| `cmd/scriptorium` | Process entrypoint that delegates command execution to the CLI adapter. | [CLI contract](../cli.md), [adapter internals](adapters.md) |
|
||||||
|
|
||||||
|
## Adapters, Domain, And Use Case
|
||||||
|
|
||||||
|
| Component | Implemented responsibility | References |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `internal/adapter/cli` | Parses CLI commands, applies application wiring, and handles process input and output. | [CLI contract](../cli.md), [adapter internals](adapters.md) |
|
||||||
|
| `internal/adapter/http` | Maps HTTP requests and responses to domain operations and maps public errors. | [HTTP API contract](../api.md), [adapter internals](adapters.md) |
|
||||||
|
| `internal/domain` | Defines core request, result, output-contract, and LLM-boundary types. | [runner internals](runner.md) |
|
||||||
|
| `internal/usecase` | Implements `Runner` preparation, execution, validation coordination, and the repairer boundary. | [runner internals](runner.md) |
|
||||||
|
|
||||||
|
## Configuration And Sources
|
||||||
|
|
||||||
|
| Component | Implemented responsibility | References |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `internal/config` | Loads application settings, applies defaults, and applies CLI overrides. | [configuration contract](../config.md), [adapter internals](adapters.md) |
|
||||||
|
| `internal/defaults` | Holds compile-time default values used when application settings are resolved. | [configuration contract](../config.md) |
|
||||||
|
| `internal/promptdef` | Loads prompt definitions from filesystem and `fs.FS` sources. | [configuration contract](../config.md), [source internals](sources.md) |
|
||||||
|
| `internal/profile` | Loads filesystem and `fs.FS` execution profiles and combines profile repositories. | [configuration contract](../config.md), [source internals](sources.md) |
|
||||||
|
| `internal/profile/builtin` | Provides embedded built-in execution profiles as a repository. | [configuration contract](../config.md), [source internals](sources.md) |
|
||||||
|
| `internal/filecatalog` | Provides shared YAML discovery and source-root helpers. | [source internals](sources.md) |
|
||||||
|
| `internal/artifact` | Reads inline and file-backed input artifacts. | [configuration contract](../config.md), [HTTP API contract](../api.md), [source internals](sources.md) |
|
||||||
|
| `internal/prompt` | Renders prompt templates into messages. | [runner internals](runner.md) |
|
||||||
|
|
||||||
|
## Formatting, Validation, And Model Access
|
||||||
|
|
||||||
|
| Component | Implemented responsibility | References |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `internal/format` | Formats prepared-run information for CLI output. | [CLI contract](../cli.md), [adapter internals](adapters.md) |
|
||||||
|
| `internal/validate` | Defines validation interfaces and provides standard filesystem and `fs.FS` schema validation. | [configuration contract](../config.md), [source internals](sources.md), [runner internals](runner.md) |
|
||||||
|
| `internal/llm` | Defines the provider-neutral LLM client boundary and its OpenAI-compatible implementation. | [OpenAI-compatible integration](../integrations/openai-compatible-chat.md), [LLM internals](llm.md), [runner internals](runner.md) |
|
||||||
|
|
||||||
|
Focused internal documents describe the components that have detailed
|
||||||
|
orchestration, adapter, or source behavior. Package tests live alongside the
|
||||||
|
implementation and are identified in those focused documents where relevant.
|
||||||
@@ -2,145 +2,118 @@
|
|||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
`internal/usecase.Runner` is the core prompt-execution orchestrator. It prepares prompt requests, calls the configured LLM client for `Run`, validates generated output, and returns domain results.
|
`internal/usecase.Runner` is the prompt-execution orchestrator. It prepares
|
||||||
|
domain requests, invokes an injected LLM client, validates output, and returns
|
||||||
|
domain results. Transport parsing, response mapping, and public type conversion
|
||||||
|
remain outside this package.
|
||||||
|
|
||||||
Transport parsing, DTOs, CLI output, HTTP status mapping, and public package type conversion belong outside the runner.
|
The [configuration reference](../config.md) owns prompt, profile, schema, and
|
||||||
|
runtime-setting definitions. Public error behavior is defined by the
|
||||||
|
[HTTP API](../api.md) and [Go package](../consumers/pkg-scriptorium.md)
|
||||||
|
contracts.
|
||||||
|
|
||||||
## Inputs And Outputs
|
## Dependencies And Construction
|
||||||
|
|
||||||
Primary inputs:
|
`Runner` receives these collaborators:
|
||||||
|
|
||||||
- `domain.RunRequest`
|
- `promptdef.Repository`;
|
||||||
- repositories/readers/renderers/validators injected at construction
|
- `profile.Repository`;
|
||||||
- `context.Context` for cancellation
|
- `artifact.Reader`;
|
||||||
|
- `prompt.Renderer`;
|
||||||
|
- `llm.Client`;
|
||||||
|
- `validate.Validator`; and
|
||||||
|
- an optional `OutputRepairer`.
|
||||||
|
|
||||||
Primary outputs:
|
`NewRunner` constructs a runner without a repairer. `NewRunnerWithRepairer`
|
||||||
|
accepts one explicitly. Adapters and the public engine choose concrete
|
||||||
- `domain.PreparedRun` from `Prepare`
|
repositories and readers; the runner does not load application configuration.
|
||||||
- `domain.RunResult` from `Run`
|
|
||||||
- wrapped sentinel errors for adapter mapping
|
|
||||||
|
|
||||||
LLM boundary types:
|
|
||||||
|
|
||||||
- `domain.GenerateRequest`
|
|
||||||
- `domain.GenerateResponse`
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
`Runner` depends on package interfaces instead of concrete adapter types:
|
|
||||||
|
|
||||||
- `promptdef.Repository`
|
|
||||||
- `profile.Repository`
|
|
||||||
- `artifact.Reader`
|
|
||||||
- `prompt.Renderer`
|
|
||||||
- `llm.Client`
|
|
||||||
- `validate.Validator`
|
|
||||||
- optional `usecase.OutputRepairer`
|
|
||||||
|
|
||||||
The CLI, HTTP adapter, and public Go package construct these dependencies and pass them in.
|
|
||||||
|
|
||||||
## Config Fields
|
|
||||||
|
|
||||||
`Runner` does not read app config files. Effective behavior is determined by injected dependencies and the `domain.RunRequest`.
|
|
||||||
|
|
||||||
Adapter wiring commonly reflects these app config fields:
|
|
||||||
|
|
||||||
- `prompt_dir`
|
|
||||||
- `profile_dir`
|
|
||||||
- `schema_dir`
|
|
||||||
- `server.artifact_root`
|
|
||||||
- HTTP request/artifact/response size limits
|
|
||||||
|
|
||||||
Runtime model settings are resolved from the selected profile plus request overrides.
|
|
||||||
|
|
||||||
## Prepare Flow
|
## Prepare Flow
|
||||||
|
|
||||||
`Prepare`:
|
`Prepare` performs one deterministic preparation pass for a request:
|
||||||
|
|
||||||
1. requires a non-empty prompt ID.
|
1. validate the prompt ID and load the prompt definition;
|
||||||
2. loads the prompt definition and computes its hash.
|
2. hash the definition and select the explicit or default profile;
|
||||||
3. selects the profile from request `profile_id`, then prompt `default_profile`.
|
3. load the profile and resolve effective execution settings;
|
||||||
4. loads the selected execution profile.
|
4. validate endpoint, model, and credential availability;
|
||||||
5. merges built-in execution defaults, profile values, and request overrides.
|
5. resolve the output contract and, for JSON Schema output, load a structured
|
||||||
6. applies request-scoped direct API key values for public Go callers.
|
schema document before model execution;
|
||||||
7. validates endpoint, model, and credential requirements.
|
6. read and hash input artifacts;
|
||||||
8. resolves the output contract and JSON Schema document when required.
|
7. render messages and the session ID; and
|
||||||
9. reads input artifacts.
|
8. return a `PreparedRun` containing the effective state and rendered-prompt
|
||||||
10. renders prompt messages and hashes the rendered prompt.
|
hash.
|
||||||
11. returns a prepared run without calling the LLM.
|
|
||||||
|
|
||||||
Numeric request overrides are presence-aware: omitted values preserve the current effective value, while explicit zero values are real overrides.
|
Execution settings merge defaults, profile values, and a request override.
|
||||||
|
Numeric override presence is retained so explicit zero values are not confused
|
||||||
|
with omissions.
|
||||||
|
|
||||||
## Run Flow
|
## Run And Validation Flow
|
||||||
|
|
||||||
`Run`:
|
`Run` creates a run ID and timestamps, then calls `Prepare` rather than
|
||||||
|
duplicating preparation. It sends the prepared prompt, effective target,
|
||||||
|
target-presence state, and optional structured-output specification to the LLM
|
||||||
|
client. It converts the returned content to an output artifact, validates it,
|
||||||
|
and returns the artifact, validation, hashes, usage, and timing metadata.
|
||||||
|
|
||||||
1. creates a run ID and start timestamp.
|
A validator can return a content result or an operational error. Content
|
||||||
2. calls `Prepare`.
|
failures stay in the result; schema loading, compilation, and validator
|
||||||
3. calls the injected LLM client with rendered messages, effective target, target presence, and structured-output settings.
|
operational failures are returned as `ErrValidation`. The canonical distinction
|
||||||
4. builds the output artifact.
|
for callers is documented by the public contracts.
|
||||||
5. validates the output.
|
|
||||||
6. optionally attempts bounded repair when a repairer is injected and the contract permits repair.
|
|
||||||
7. returns the run result with artifact, raw output, validation, hashes, selected profile/model metadata, usage, and timing.
|
|
||||||
|
|
||||||
`Run` must reuse `Prepare`; prepare logic should not be duplicated elsewhere.
|
## Repair Boundary
|
||||||
|
|
||||||
## Validation And Repair
|
Repair is an internal optional loop. It starts only when a repairer is present,
|
||||||
|
the output contract permits one or more attempts, validation failed, and the
|
||||||
|
validation mode is JSON or JSON Schema. Each repair receives the previous
|
||||||
|
output, validation errors, effective target, structured-output specification,
|
||||||
|
and attempt metadata; every repaired result is validated again.
|
||||||
|
|
||||||
Validation content failures are returned as successful run results with `Validation.Status == failed`. They are not runtime errors.
|
`NewDefaultOutputRepairer` delegates to the injected LLM client. CLI, HTTP, and
|
||||||
|
the public engine use `NewRunner` and therefore do not inject this repairer.
|
||||||
|
|
||||||
Validation runtime failures, such as schema load or compile errors, return `ErrValidation`.
|
## Error Translation
|
||||||
|
|
||||||
Repair attempts occur only when all conditions are true:
|
Runner sentinels identify failure categories for adapters:
|
||||||
|
|
||||||
- a repairer is injected
|
|
||||||
- `repair_attempts` is greater than zero
|
|
||||||
- validation status is `failed`
|
|
||||||
- validation mode is `json` or `json_schema`
|
|
||||||
|
|
||||||
CLI and HTTP wiring call `usecase.NewRunner(...)`, which does not inject a repairer. Normal CLI and HTTP execution therefore does not repair invalid output.
|
|
||||||
|
|
||||||
## Failure Behavior
|
|
||||||
|
|
||||||
Stable runner sentinels include:
|
|
||||||
|
|
||||||
- `ErrInvalidRequest`
|
- `ErrInvalidRequest`
|
||||||
- `ErrProfileRequired`
|
- `ErrProfileRequired`
|
||||||
- `ErrAPIKeyEnvMissing`
|
- `ErrAPIKeyEnvMissing` and `ErrAPIKeyRequired`
|
||||||
- `ErrAPIKeyRequired`
|
- `ErrPromptLoad`, `ErrProfileLoad`, and `ErrArtifactLoad`
|
||||||
- `ErrPromptLoad`
|
|
||||||
- `ErrProfileLoad`
|
|
||||||
- `ErrArtifactLoad`
|
|
||||||
- `ErrPromptRender`
|
- `ErrPromptRender`
|
||||||
- `ErrLLMGenerate`
|
- `ErrLLMGenerate`
|
||||||
- `ErrValidation`
|
- `ErrValidation`
|
||||||
|
|
||||||
Adapters should use `errors.Is` against sentinels and lower-level repository errors instead of matching message text.
|
Wrap errors with those sentinels and preserve their identities through
|
||||||
|
`errors.Is`; adapters must not classify errors by message text. The runner
|
||||||
|
passes direct keys only to the LLM boundary and never includes resolved key
|
||||||
|
values in prepared or run results.
|
||||||
|
|
||||||
Secret values must not appear in prepared output, run results, logs, HTTP responses, or serialized public package results. The effective API-key environment-variable name may appear.
|
## Package-Local Guarantees
|
||||||
|
|
||||||
## State And Manifests
|
- `Run` always reuses `Prepare`.
|
||||||
|
- Schema documents are loaded before the initial LLM call when structured output
|
||||||
|
is required.
|
||||||
|
- Output validation records attempts used, including repair attempts.
|
||||||
|
- Runner state is per request; the package does not create a durable run store
|
||||||
|
or manifest.
|
||||||
|
- Source, renderer, validator, and LLM implementations remain injected
|
||||||
|
boundaries.
|
||||||
|
|
||||||
The runner is stateless across requests.
|
## Verification And Change Recipe
|
||||||
|
|
||||||
- No durable run store.
|
Inspect:
|
||||||
- No manifest files.
|
|
||||||
- No checkpoint, skip, or resume behavior.
|
|
||||||
- Recovery is a new request after correcting inputs, config, or environment.
|
|
||||||
|
|
||||||
## Tests To Inspect
|
|
||||||
|
|
||||||
- `internal/usecase/runner_test.go`
|
- `internal/usecase/runner_test.go`
|
||||||
- `internal/usecase/integration_test.go`
|
- `internal/usecase/integration_test.go`
|
||||||
- `engine_test.go`
|
- `engine_test.go`
|
||||||
- `internal/adapter/cli/run_test.go`
|
|
||||||
- `internal/adapter/http/handler_test.go`
|
|
||||||
|
|
||||||
## Architectural Invariants
|
When changing orchestration:
|
||||||
|
|
||||||
- Use-case decisions stay in `internal/usecase`.
|
1. identify the collaborator boundary and the affected `Prepare` or `Run` state;
|
||||||
- `Run` reuses `Prepare`.
|
2. preserve the `Run`-through-`Prepare` path and error identity;
|
||||||
- Prompt/profile/artifact/schema loading remains behind injected boundaries.
|
3. add focused runner or integration tests for changed state transitions,
|
||||||
- Validation content failures are result state; validation runtime failures are errors.
|
validation, or repair behavior; and
|
||||||
- Repair loops are bounded by `repair_attempts` and repairer presence.
|
4. update the owning external contract and any affected source or LLM internal
|
||||||
- Resolved secret values are never serialized or emitted.
|
document.
|
||||||
|
|
||||||
|
The [testing policy](../policy/testing.md) owns global test sufficiency.
|
||||||
|
|||||||
@@ -2,142 +2,79 @@
|
|||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
This document covers implemented prompt, profile, schema, artifact, and catalog source behavior. It is for developers changing loaders or source wiring.
|
This document describes how source packages load prompt definitions, profiles,
|
||||||
|
schemas, and artifacts. The [configuration reference](../config.md) owns their
|
||||||
|
user-facing formats and settings. The [HTTP API reference](../api.md) owns
|
||||||
|
HTTP-visible artifact outcomes; [operations](../operations.md) owns deployment
|
||||||
|
handling.
|
||||||
|
|
||||||
Full user-facing YAML and config reference material belongs in `docs/config.md`.
|
## Prompt Definitions
|
||||||
|
|
||||||
## Prompt Definition Sources
|
`internal/promptdef` provides filesystem and `fs.FS` repositories. Both use
|
||||||
|
`internal/filecatalog` for recursive YAML discovery, deterministic ordering,
|
||||||
|
display paths, and root cleaning.
|
||||||
|
|
||||||
`internal/promptdef` provides directory-backed and `fs.FS` repositories.
|
Repositories select a prompt by YAML ID and optional version rather than by
|
||||||
|
path. They decode through strict YAML handling, reject duplicate matching
|
||||||
|
definitions, and resolve `content_file` relative to the definition. The `fs.FS`
|
||||||
|
implementation resolves content paths inside its source root; absolute paths and
|
||||||
|
traversal outside that root are rejected before file access.
|
||||||
|
|
||||||
Behavior:
|
## Profiles And Built-Ins
|
||||||
|
|
||||||
- recursively scans `.yaml` and `.yml` files.
|
`internal/profile` provides filesystem, `fs.FS`, and overlay repositories.
|
||||||
- decodes YAML with known-fields checking.
|
`internal/profile/builtin` exposes embedded assets through the same repository
|
||||||
- looks up prompts by YAML `id`, not by path.
|
interface.
|
||||||
- optionally filters by prompt `version`.
|
|
||||||
- rejects duplicate matching prompt IDs.
|
|
||||||
- requires `id`, `version`, and at least one message.
|
|
||||||
- requires each message to set exactly one of `content` or `content_file`.
|
|
||||||
- resolves filesystem `content_file` values relative to the prompt YAML file.
|
|
||||||
- resolves `fs.FS` `content_file` values inside the configured source root.
|
|
||||||
- permits prompt subdirectories only as organization; they are not part of prompt identity.
|
|
||||||
|
|
||||||
For `fs.FS` roots, absolute paths and relative traversal outside the source root are rejected by catalog path helpers.
|
An overlay asks its primary source first. It falls back only when the primary
|
||||||
|
reports `ErrProfileNotFound`; invalid YAML, duplicate IDs, validation failures,
|
||||||
|
and raw-key failures are returned rather than hidden by fallback. This makes a
|
||||||
|
custom ID override a built-in ID while retaining errors in the custom source.
|
||||||
|
|
||||||
## Profile Sources
|
The public engine can overlay in-memory profiles ahead of both file-backed and
|
||||||
|
built-in repositories. Profile field definitions, validation ranges, and the
|
||||||
|
built-in catalog remain in the [configuration reference](../config.md).
|
||||||
|
|
||||||
`internal/profile` provides directory-backed, `fs.FS`, and overlay repositories. `internal/profile/builtin` embeds built-in profile YAML assets and exposes them through the same repository interface.
|
## Schemas
|
||||||
|
|
||||||
Behavior:
|
`internal/validate` supplies `StandardValidator` for filesystem sources and
|
||||||
|
`FSValidator` for `fs.FS` sources. Directory-backed validation loads the named
|
||||||
|
schema path; it does not search directories by basename. `fs.FS` schema paths
|
||||||
|
are cleaned and checked against their configured root, while a single-file
|
||||||
|
source matches its file base name.
|
||||||
|
|
||||||
- recursively scans `.yaml` and `.yml` files.
|
The runner requests a schema document before generation when it needs
|
||||||
- decodes YAML with known-fields checking.
|
structured output. JSON and schema mismatches in generated content are
|
||||||
- looks up profiles by YAML `id`, not by path.
|
validation results; source access, decoding, registration, and compilation
|
||||||
- rejects duplicate IDs inside the same source.
|
failures are operational errors.
|
||||||
- rejects raw `api_key` fields in YAML; file-backed profiles must use `api_key_env`.
|
|
||||||
- validates required `endpoint` and `model` values.
|
|
||||||
- validates numeric profile ranges.
|
|
||||||
|
|
||||||
Overlay behavior:
|
## Artifacts
|
||||||
|
|
||||||
- custom profiles are primary.
|
`internal/artifact` composes inline and file readers. The ordinary composite
|
||||||
- built-in profiles are fallback.
|
reader used by CLI and the public engine reads file references from the process
|
||||||
- fallback occurs only after a primary `ErrProfileNotFound`.
|
filesystem. The restricted composite reader used by the HTTP adapter combines
|
||||||
- primary validation, YAML, duplicate, and raw-key errors are returned directly.
|
inline reading with a rooted file reader and optional byte limit.
|
||||||
- duplicate IDs across custom and built-in sources are allowed because the custom profile overrides the built-in one.
|
|
||||||
|
|
||||||
The public Go facade can add in-memory profiles ahead of file-backed and built-in profiles.
|
The rooted reader cleans paths and applies lexical containment without resolving
|
||||||
|
symlinks. It checks relative references against the configured root and accepts
|
||||||
|
absolute references only when they remain inside that lexical root. The OS still
|
||||||
|
follows symlinks after that check. The public containment outcome is documented
|
||||||
|
by the [HTTP API reference](../api.md); deployment permissions belong in
|
||||||
|
[operations](../operations.md).
|
||||||
|
|
||||||
## Schema Sources
|
## Failure Boundaries
|
||||||
|
|
||||||
`internal/validate` provides:
|
Source packages report repository, decoding, duplicate, validation, and read
|
||||||
|
failures to their callers. They do not select public status codes or response
|
||||||
|
schemas. The runner wraps source failures with use-case categories; adapters map
|
||||||
|
them to their own external contract.
|
||||||
|
|
||||||
- `StandardValidator` for filesystem paths.
|
Source reads use current filesystem or `fs.FS` content for each request. These
|
||||||
- `FSValidator` for `fs.FS` roots and single-file public schema sources.
|
packages create no manifests, checkpoints, or durable run state.
|
||||||
|
|
||||||
Behavior:
|
## Verification And Change Recipe
|
||||||
|
|
||||||
- `json_schema` validation requires a non-empty `schema_path`.
|
Inspect:
|
||||||
- filesystem schema paths resolve relative to `schema_dir` unless absolute.
|
|
||||||
- directory-backed schema lookup uses the explicit `schema_path`; it does not search recursively by basename.
|
|
||||||
- `fs.FS` schema paths must remain inside the configured source root.
|
|
||||||
- single-file schema sources match by the configured file base name.
|
|
||||||
- schema documents are loaded before the LLM call for structured output.
|
|
||||||
- JSON parse failures are validation content failures.
|
|
||||||
- schema access, decode, registration, and compile failures are runtime validation errors.
|
|
||||||
|
|
||||||
## Artifact Sources
|
|
||||||
|
|
||||||
`internal/artifact` supports two input artifact reference types:
|
|
||||||
|
|
||||||
- `inline`
|
|
||||||
- `file`
|
|
||||||
|
|
||||||
Inline behavior:
|
|
||||||
|
|
||||||
- requires a non-empty body.
|
|
||||||
- produces text/plain artifacts.
|
|
||||||
- hashes the body bytes.
|
|
||||||
|
|
||||||
Direct file behavior:
|
|
||||||
|
|
||||||
- used by CLI `run`, CLI `render`, and the public Go facade.
|
|
||||||
- requires a non-empty URI.
|
|
||||||
- reads from the process filesystem without HTTP artifact-root restrictions.
|
|
||||||
- infers content type from file extension, defaulting to text/plain.
|
|
||||||
|
|
||||||
Restricted file behavior:
|
|
||||||
|
|
||||||
- used by HTTP `serve`.
|
|
||||||
- allows inline artifacts even when no artifact root is configured.
|
|
||||||
- denies file artifacts when no artifact root is configured.
|
|
||||||
- resolves relative file URIs against `server.artifact_root`.
|
|
||||||
- accepts absolute file URIs only when they pass containment checks.
|
|
||||||
- applies `server.max_artifact_bytes` when configured.
|
|
||||||
|
|
||||||
Restricted containment is lexical. It cleans paths and checks the relative path against the configured root; it does not resolve symlinks. Symlinks inside the root are followed by the operating system, including symlinks that target files outside the root.
|
|
||||||
|
|
||||||
## Catalog Helpers
|
|
||||||
|
|
||||||
`internal/filecatalog` centralizes shared source helpers:
|
|
||||||
|
|
||||||
- recursive YAML discovery for filesystem and `fs.FS` roots.
|
|
||||||
- deterministic sorting.
|
|
||||||
- `.yaml` and `.yml` filtering.
|
|
||||||
- display paths for diagnostics.
|
|
||||||
- YAML file stems.
|
|
||||||
- `fs.FS` root cleaning and containment checks.
|
|
||||||
|
|
||||||
Repository code should use these helpers instead of reimplementing path traversal and containment rules.
|
|
||||||
|
|
||||||
## Failure Behavior
|
|
||||||
|
|
||||||
Common source failures:
|
|
||||||
|
|
||||||
- missing prompt/profile/schema/artifact files.
|
|
||||||
- invalid YAML or JSON.
|
|
||||||
- unknown YAML fields.
|
|
||||||
- duplicate prompt or profile IDs.
|
|
||||||
- prompt/profile validation errors.
|
|
||||||
- raw API key fields in profile YAML.
|
|
||||||
- unsupported artifact reference type.
|
|
||||||
- missing inline body or file URI.
|
|
||||||
- artifact outside HTTP root.
|
|
||||||
- artifact exceeding HTTP size limit.
|
|
||||||
- schema load or compile failure.
|
|
||||||
|
|
||||||
Prompt/profile repository lookup errors are mapped by adapters separately from runtime runner errors. Validation content failures remain result state; source and schema runtime failures return errors.
|
|
||||||
|
|
||||||
## State And Manifests
|
|
||||||
|
|
||||||
Source packages do not persist run state.
|
|
||||||
|
|
||||||
- No manifests are read or written.
|
|
||||||
- No source package implements skip or resume behavior.
|
|
||||||
- Source reads reflect the current filesystem or `fs.FS` state for each request.
|
|
||||||
|
|
||||||
## Tests To Inspect
|
|
||||||
|
|
||||||
- `internal/promptdef/repository_test.go`
|
- `internal/promptdef/repository_test.go`
|
||||||
- `internal/profile/repository_test.go`
|
- `internal/profile/repository_test.go`
|
||||||
@@ -147,11 +84,14 @@ Source packages do not persist run state.
|
|||||||
- `internal/usecase/integration_test.go`
|
- `internal/usecase/integration_test.go`
|
||||||
- `engine_test.go`
|
- `engine_test.go`
|
||||||
|
|
||||||
## Architectural Invariants
|
When updating prompt, profile, schema, or built-in assets:
|
||||||
|
|
||||||
- Prompt/profile identity comes from YAML `id`.
|
1. keep assets valid for the strict loader and the relevant source boundary;
|
||||||
- External YAML decoding remains strict.
|
2. update the [configuration reference](../config.md) when a file-format,
|
||||||
- File-backed profile YAML never accepts raw API key values.
|
catalog, or default changes;
|
||||||
- Built-in profiles are fallback, not a replacement for custom source validation.
|
3. run focused source and integration tests, including the built-in repository
|
||||||
- HTTP file artifacts remain rooted by lexical containment.
|
test when embedded assets change; and
|
||||||
- Schema runtime failures remain errors, while JSON/schema content mismatches remain validation results.
|
4. update this document when discovery, precedence, containment, or failure
|
||||||
|
mechanics change.
|
||||||
|
|
||||||
|
The [testing policy](../policy/testing.md) owns global test sufficiency.
|
||||||
|
|||||||
@@ -1,164 +1,157 @@
|
|||||||
# Operations Guide
|
# Operations Guide
|
||||||
|
|
||||||
## Scope
|
## Scope And References
|
||||||
|
|
||||||
This guide covers operating the implemented CLI commands and HTTP service. It
|
This runbook covers deployment, normal operation, capacity planning, and safe
|
||||||
does not replace the [CLI reference](cli.md), [Configuration reference](config.md),
|
recovery for Scriptorium. It does not redefine invocation syntax, configuration
|
||||||
or [HTTP API reference](api.md).
|
fields, or HTTP wire behavior.
|
||||||
|
|
||||||
## Operational Model
|
- [CLI reference](cli.md): commands, output destinations, and exit codes.
|
||||||
|
- [Configuration reference](config.md): configuration, prompt/profile/schema
|
||||||
|
formats, defaults, and credentials.
|
||||||
|
- [HTTP API reference](api.md): route, request/response schema, status codes,
|
||||||
|
limits, and HTTP artifact access.
|
||||||
|
- [Consumer integration overview](consumers/api.md): caller responsibilities.
|
||||||
|
|
||||||
Scriptorium executes one prompt request per CLI invocation or HTTP request.
|
## Operational Model And State
|
||||||
|
|
||||||
Important boundaries:
|
Scriptorium handles one prompt request for each CLI invocation or HTTP request.
|
||||||
|
It has no durable run store, archive, checkpoint, cache, or resume mechanism.
|
||||||
|
A failed or interrupted request is recovered by correcting its inputs,
|
||||||
|
configuration, or environment and submitting a new request.
|
||||||
|
|
||||||
- No durable run state is stored.
|
Generated artifacts, rendered prompts, model output, and run metadata are
|
||||||
- No manifest, archive, checkpoint, or built-in backup workflow is written.
|
caller-owned data. Retention, encryption, backup, and deletion are deployment
|
||||||
- No built-in resume behavior exists.
|
responsibilities.
|
||||||
- Recovery is rerun-based: correct inputs, config, or environment, then run again.
|
|
||||||
|
|
||||||
## Filesystem Layout
|
## Deploy The Filesystem And Process
|
||||||
|
|
||||||
Operational deployments usually provide:
|
Provide the process with readable prompt, profile, and schema sources. Keep
|
||||||
|
prompt templates adjacent to the prompt definitions that reference them. For an
|
||||||
|
HTTP deployment that accepts file artifacts, use a dedicated, narrow artifact
|
||||||
|
directory rather than a general-purpose or sensitive filesystem tree.
|
||||||
|
|
||||||
- `prompt_dir`: prompt definition YAML files and adjacent `content_file` templates.
|
Run Scriptorium under an identity that can:
|
||||||
- `profile_dir`: optional custom profile YAML files.
|
|
||||||
- `schema_dir`: optional JSON Schema files.
|
|
||||||
- `server.artifact_root`: optional HTTP file-input root for `serve`.
|
|
||||||
|
|
||||||
Keep these directories readable by the Scriptorium process. Keep
|
- read only the prompt, profile, schema, and allowed input-artifact paths it
|
||||||
`server.artifact_root` narrow and not writable by untrusted users.
|
needs;
|
||||||
|
- read the required credential environment variables without writing them to
|
||||||
|
files or logs; and
|
||||||
|
- write only caller-selected output locations when CLI output files are used.
|
||||||
|
|
||||||
## Normal CLI Workflow
|
Do not make the HTTP artifact directory writable by untrusted users. The HTTP
|
||||||
|
artifact containment behavior is lexical and the operating system follows
|
||||||
|
symlinks; account for that when choosing ownership and mount boundaries. See
|
||||||
|
the [HTTP API reference](api.md) for the externally observable behavior.
|
||||||
|
|
||||||
Use `render` before `run` when changing prompt/profile/input wiring:
|
## Supply Credentials And Protect Runtime Data
|
||||||
|
|
||||||
```bash
|
Set secret values in the process environment and configure only their
|
||||||
go run ./cmd/scriptorium render \
|
environment-variable names. Do not put raw keys in configuration, prompt or
|
||||||
--config ./examples/config.yml \
|
profile files, process arguments, HTTP payloads, captured command lines, or
|
||||||
--prompt generic.markdown_summary \
|
debug dumps.
|
||||||
--input transcript=./examples/fixtures/transcript.md \
|
|
||||||
--input glossary=./examples/fixtures/glossary.yml \
|
|
||||||
--format json
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `run` for generation after preflight:
|
Treat stdout, stderr, prepared-run output, generated artifacts, and HTTP
|
||||||
|
responses as potentially sensitive. Send service logs to a controlled collector
|
||||||
|
and apply the same retention and access rules as for model input and output.
|
||||||
|
|
||||||
```bash
|
## Run A Normal Workflow
|
||||||
go run ./cmd/scriptorium run \
|
|
||||||
--config ./examples/config.yml \
|
|
||||||
--prompt generic.markdown_summary \
|
|
||||||
--input transcript=./examples/fixtures/transcript.md \
|
|
||||||
--input glossary=./examples/fixtures/glossary.yml \
|
|
||||||
--out ./summary.md
|
|
||||||
```
|
|
||||||
|
|
||||||
Before production runs, confirm:
|
Before changing production inputs, profiles, or schemas:
|
||||||
|
|
||||||
- the effective config path is the intended one;
|
1. confirm the deployed configuration selects the intended sources and model
|
||||||
- prompt/profile/schema directories are readable;
|
credentials;
|
||||||
- input file paths exist and match prompt input names;
|
2. use [`render`](cli.md) with the same request inputs and variables to confirm
|
||||||
- required API-key environment variables are set;
|
preparation without a model call;
|
||||||
- the selected model endpoint is reachable from the process environment.
|
3. use [`run`](cli.md) for generation; and
|
||||||
|
4. retain or discard validation-failed output according to the caller's
|
||||||
|
policy.
|
||||||
|
|
||||||
## HTTP Service Operation
|
The [maintained render script](../examples/render-markdown-summary.sh) is a
|
||||||
|
copyable preflight example. The CLI reference owns its complete invocation and
|
||||||
|
exit semantics.
|
||||||
|
|
||||||
Start the service with:
|
## Expose The HTTP Service
|
||||||
|
|
||||||
```bash
|
The HTTP service has no built-in authentication or authorization. Place it on a
|
||||||
go run ./cmd/scriptorium serve --config ./examples/config.yml
|
trusted network or behind an authenticated reverse proxy, API gateway, or
|
||||||
```
|
equivalent access control. Restrict who can reach it and who can read the
|
||||||
|
artifact root.
|
||||||
|
|
||||||
The implemented HTTP route is `POST /v1/runs`; request and response fields are
|
Use a service manager or supervisor appropriate to the deployment to manage
|
||||||
defined in the [HTTP API reference](api.md).
|
process lifetime, restart policy, log capture, and environment injection. The
|
||||||
|
[HTTP API reference](api.md) owns client request shapes, status behavior, and
|
||||||
|
artifact-access outcomes.
|
||||||
|
|
||||||
The maintained HTTP request-shape example is `examples/http-run.json`.
|
## Plan Capacity And Limits
|
||||||
|
|
||||||
HTTP service notes:
|
Capacity is primarily determined by concurrent model calls, input and output
|
||||||
|
sizes, schema complexity, provider latency, and network behavior. Size limits
|
||||||
|
protect request bodies, HTTP file artifacts, and encoded responses; configure
|
||||||
|
them through the [configuration reference](config.md) and rely on the
|
||||||
|
[HTTP API reference](api.md) for their response effects.
|
||||||
|
|
||||||
- Unknown JSON fields are rejected.
|
Before increasing a limit:
|
||||||
- `inline` input references work without an artifact root.
|
|
||||||
- `file` input references require `server.artifact_root` or `serve --artifact-root`.
|
|
||||||
- Request bodies, HTTP file input artifacts, and encoded JSON responses are size-limited.
|
|
||||||
- Validation content failures return `200 OK` with `validation.status: "failed"`.
|
|
||||||
|
|
||||||
Security boundary:
|
1. measure representative input, generated-output, and optional raw-output
|
||||||
|
sizes;
|
||||||
|
2. confirm memory, network, and upstream-provider capacity;
|
||||||
|
3. retain an upstream request-size and authentication boundary; and
|
||||||
|
4. test the intended workload in a non-production environment.
|
||||||
|
|
||||||
- `serve` has no built-in authentication or authorization.
|
For large local inputs, prefer a controlled file-artifact directory over
|
||||||
- Put it behind trusted controls such as a private network, authenticated reverse proxy, or API gateway.
|
placing arbitrary paths on the service host. Avoid disabling a limit unless an
|
||||||
- Do not expose an artifact root containing unrelated sensitive files.
|
equivalent trusted control exists elsewhere.
|
||||||
- Symlinks inside the artifact root are followed by the operating system.
|
|
||||||
|
|
||||||
## Secrets Handling
|
## Diagnose And Recover
|
||||||
|
|
||||||
Raw API keys are not accepted in app config, profiles, CLI flags, or HTTP
|
### Preparation Or Configuration Failure
|
||||||
request bodies.
|
|
||||||
|
|
||||||
Use this pattern:
|
Capture the CLI diagnostic or HTTP error response, then verify the selected
|
||||||
|
configuration, prompt ID, profile selection, source readability, and input
|
||||||
|
mapping. Use `render` with the same request when it is unclear whether failure
|
||||||
|
occurs before model execution. Consult the [CLI reference](cli.md), the
|
||||||
|
[configuration reference](config.md), and the [HTTP API reference](api.md) for
|
||||||
|
the exact interface contract.
|
||||||
|
|
||||||
1. Set an environment variable containing the secret value.
|
### Credential Or Provider Failure
|
||||||
2. Store only the variable name in profile `api_key_env` or request override `api_key_env`.
|
|
||||||
3. Scope the process environment to the minimum required variables.
|
|
||||||
|
|
||||||
## Output, Logs, And Exit Codes
|
Confirm that the process environment contains the configured credential name
|
||||||
|
without printing the secret. Check endpoint reachability and provider health
|
||||||
|
from the process network. If preparation succeeds but generation fails, inspect
|
||||||
|
the selected model settings in prepared output and the service's controlled
|
||||||
|
logs. Correct the deployment or provider issue, then submit a new request.
|
||||||
|
|
||||||
`run`:
|
### Artifact Or Permission Failure
|
||||||
|
|
||||||
- stdout: generated artifact body unless `--out` is used.
|
Verify that the process can read the intended local input. For HTTP file
|
||||||
- stderr: summary on success, errors on failure.
|
artifacts, verify the deployment's artifact root, ownership, path layout, and
|
||||||
- exit `2`: generation completed and output was written, but validation failed.
|
file size. Do not widen filesystem permissions or the allowed root merely to
|
||||||
|
make an arbitrary path work; move or copy the required artifact into the
|
||||||
|
controlled location instead.
|
||||||
|
|
||||||
`render`:
|
### Validation Failure
|
||||||
|
|
||||||
- stdout: prepared-run output unless `--out` is used.
|
A generated-content validation failure is distinct from a runtime failure.
|
||||||
- stderr: errors.
|
CLI `run` reports the validation result and error count in its success summary;
|
||||||
- exit `0` on success, `1` on failure.
|
it does not print the individual validation messages. For HTTP, inspect the
|
||||||
|
validation object in the response according to the [HTTP API reference](api.md).
|
||||||
|
|
||||||
`serve`:
|
Use rendered input and generated output to determine whether prompt instructions,
|
||||||
|
the selected model, or the schema needs correction. If schema loading or
|
||||||
|
compilation itself fails, correct the source deployment or schema document
|
||||||
|
before rerunning.
|
||||||
|
|
||||||
- stderr: startup and server errors.
|
### HTTP Limit Or Request Failure
|
||||||
- HTTP response body: JSON success or error envelope.
|
|
||||||
|
|
||||||
## Validation Behavior
|
Compare the request, artifact, or expected response size with the deployed
|
||||||
|
configuration, and validate the request against the [HTTP API reference](api.md).
|
||||||
|
Reduce the payload, use an appropriate controlled artifact source, omit
|
||||||
|
unneeded raw output, or adjust the deployment limit after capacity review.
|
||||||
|
|
||||||
Prompt `output.validation_mode` controls validation:
|
## Cleanup And Reruns
|
||||||
|
|
||||||
- `none`: skipped.
|
Because no run state is retained, cleanup concerns caller-owned output files,
|
||||||
- `basic`: output body must not be empty.
|
logs, and artifacts only. Remove or rotate them using the deployment's normal
|
||||||
- `json`: output body must parse as JSON.
|
retention policy. After a correction, rerun the request from the beginning;
|
||||||
- `json_schema`: output body must parse as JSON and satisfy the configured schema.
|
there is no safe resume point.
|
||||||
|
|
||||||
Runtime/schema failures are hard failures (`run` exit `1`, HTTP error).
|
|
||||||
Generated-content validation failures are soft failures (`run` exit `2`, HTTP
|
|
||||||
`200 OK` with failed validation status).
|
|
||||||
|
|
||||||
## Size Limits
|
|
||||||
|
|
||||||
Defaults are documented in [Configuration reference](config.md). Operationally:
|
|
||||||
|
|
||||||
- Keep default HTTP limits unless larger payloads are measured and expected.
|
|
||||||
- Prefer `inline` HTTP inputs for small payloads.
|
|
||||||
- Prefer `file` HTTP inputs for larger local artifacts under a controlled artifact root.
|
|
||||||
- Increase `server.max_response_bytes` when generated artifacts or requested raw output are expected to be large.
|
|
||||||
- Use `0` only when another trusted layer enforces size limits.
|
|
||||||
|
|
||||||
## Maintained Examples
|
|
||||||
|
|
||||||
- `examples/config.yml`
|
|
||||||
- `examples/config.full.yml`
|
|
||||||
- `examples/render-markdown-summary.sh`
|
|
||||||
- `examples/http-run.json`
|
|
||||||
|
|
||||||
## Safe Recovery
|
|
||||||
|
|
||||||
For failed CLI commands or HTTP requests:
|
|
||||||
|
|
||||||
1. Capture stderr or the HTTP error `code` and `message`.
|
|
||||||
2. Confirm config path and effective directory settings.
|
|
||||||
3. Verify prompt ID, profile ID, schema path, and input mappings.
|
|
||||||
4. Verify required API-key environment variables.
|
|
||||||
5. Reproduce with `render --format json` when pre-LLM resolution is uncertain.
|
|
||||||
6. Rerun after correction.
|
|
||||||
|
|
||||||
Because Scriptorium does not persist run state, rerun is the supported recovery
|
|
||||||
path.
|
|
||||||
|
|||||||
@@ -4,14 +4,12 @@ This document is the development architecture policy for Scriptorium.
|
|||||||
|
|
||||||
It is for developers and LLM coding agents. User-facing behavior belongs in `README.md` and the docs under `docs/` that target operators/users.
|
It is for developers and LLM coding agents. User-facing behavior belongs in `README.md` and the docs under `docs/` that target operators/users.
|
||||||
|
|
||||||
## Project Shape
|
## System Shape
|
||||||
|
|
||||||
Scriptorium is a narrow prompt-execution application with three entry paths:
|
Scriptorium is a narrow prompt-execution application with three executable
|
||||||
|
entry paths: CLI `run`, CLI `render`, and the HTTP service started by `serve`.
|
||||||
- CLI `run`
|
It also provides a public Go package for in-process use. Its current component
|
||||||
- CLI `render`
|
inventory is maintained in the [internal overview](../internal/overview.md).
|
||||||
- HTTP `POST /v1/runs` through `serve`
|
|
||||||
- public Go package `gitea.maximumdirect.net/eric/scriptorium`
|
|
||||||
|
|
||||||
Domain behavior is centralized in `internal/usecase` and `internal/domain`.
|
Domain behavior is centralized in `internal/usecase` and `internal/domain`.
|
||||||
|
|
||||||
@@ -20,48 +18,21 @@ Domain behavior is centralized in `internal/usecase` and `internal/domain`.
|
|||||||
- Keep orchestration narrow: Scriptorium executes one prompt request; it is not a multi-step workflow engine.
|
- Keep orchestration narrow: Scriptorium executes one prompt request; it is not a multi-step workflow engine.
|
||||||
- Keep adapter logic thin: adapters map external shapes to domain requests/results and should not hold domain decisions.
|
- Keep adapter logic thin: adapters map external shapes to domain requests/results and should not hold domain decisions.
|
||||||
- Keep boundaries explicit: repositories/loaders/renderers/validators/LLM client stay behind package interfaces.
|
- Keep boundaries explicit: repositories/loaders/renderers/validators/LLM client stay behind package interfaces.
|
||||||
- Keep config strict: YAML/JSON decoding for external inputs should reject unknown fields.
|
- Keep external decoding strict: configuration, prompt, and profile YAML and
|
||||||
|
HTTP JSON should reject unknown fields.
|
||||||
- Keep secrets out of payloads: raw API key values must not be accepted or emitted.
|
- Keep secrets out of payloads: raw API key values must not be accepted or emitted.
|
||||||
|
|
||||||
## Package Boundaries
|
## Dependency Direction
|
||||||
|
|
||||||
Current package map:
|
- Adapters translate external shapes and IO concerns; they do not make
|
||||||
|
use-case decisions.
|
||||||
- root package `scriptorium`: public Go facade over engine construction, source options, request/result types, and error mapping.
|
- Use-case and domain code depend on explicit repository, renderer, validator,
|
||||||
- `cmd/scriptorium`: process entrypoint.
|
and LLM interfaces rather than adapter implementations.
|
||||||
- `internal/adapter/cli`: command parsing, app wiring for CLI commands, output behavior.
|
- Source, rendering, validation, and LLM implementations remain behind their
|
||||||
- `internal/adapter/http`: HTTP DTO mapping and error/status mapping.
|
package boundaries.
|
||||||
- `internal/config`: application settings loading and CLI override precedence.
|
- Dependency-specific types must not leak across unrelated package boundaries.
|
||||||
- `internal/defaults`: compile-time default constants.
|
- Prefer the standard library; add an external dependency only when it
|
||||||
- `internal/domain`: core request/result and contract types.
|
materially reduces risk or complexity.
|
||||||
- `internal/usecase`: `Runner` prepare/run orchestration and repair-hook boundary.
|
|
||||||
- `internal/promptdef`: filesystem prompt-definition repository.
|
|
||||||
- `internal/profile`: filesystem, `fs.FS`, and overlay execution-profile repositories.
|
|
||||||
- `internal/profile/builtin`: embedded built-in execution profiles.
|
|
||||||
- `internal/filecatalog`: shared YAML discovery and `fs.FS` source helpers.
|
|
||||||
- `internal/artifact`: artifact reference readers.
|
|
||||||
- `internal/prompt`: template renderer.
|
|
||||||
- `internal/llm`: provider-neutral LLM client interface and OpenAI-compatible implementation.
|
|
||||||
- `internal/validate`: validator interfaces and standard implementation.
|
|
||||||
- `internal/format`: prepared-run output formatting.
|
|
||||||
|
|
||||||
Detailed component behavior is documented in:
|
|
||||||
|
|
||||||
- `docs/internal/runner.md`
|
|
||||||
- `docs/internal/adapters.md`
|
|
||||||
- `docs/internal/sources.md`
|
|
||||||
|
|
||||||
## Configuration And Precedence
|
|
||||||
|
|
||||||
Application settings are resolved as:
|
|
||||||
|
|
||||||
1. built-in defaults
|
|
||||||
2. config file values
|
|
||||||
3. CLI overrides
|
|
||||||
|
|
||||||
`config.yml` is for application wiring (directories, server address, render default format), not prompt/profile runtime execution settings.
|
|
||||||
|
|
||||||
Profile selection and runtime model resolution remain use-case concerns.
|
|
||||||
|
|
||||||
## State And Persistence Policy
|
## State And Persistence Policy
|
||||||
|
|
||||||
@@ -70,44 +41,31 @@ Scriptorium has no durable run-state store.
|
|||||||
- No built-in resume/checkpoint/archive behavior.
|
- No built-in resume/checkpoint/archive behavior.
|
||||||
- Recovery model is rerun after correcting inputs/config/environment.
|
- Recovery model is rerun after correcting inputs/config/environment.
|
||||||
|
|
||||||
## External Integration Policy
|
## Contract Ownership
|
||||||
|
|
||||||
Current external contracts:
|
The [CLI](../cli.md), [configuration](../config.md), [HTTP API](../api.md),
|
||||||
|
[public Go package](../consumers/pkg-scriptorium.md), and
|
||||||
- inbound HTTP contract: `POST /v1/runs`, documented canonically in `docs/api.md`
|
[integration](../integrations/) documents own their respective external
|
||||||
- outbound model contract: OpenAI-compatible chat completions subset
|
contracts. This policy keeps only the architectural boundaries that govern
|
||||||
- subprocess contract for integrators: CLI `run`/`render`
|
their implementation.
|
||||||
- public Go package contract: `docs/consumers/pkg-scriptorium.md`
|
|
||||||
|
|
||||||
Integration docs belong under `docs/integrations/`.
|
|
||||||
|
|
||||||
## Error Handling And Logging
|
## Error Handling And Logging
|
||||||
|
|
||||||
- Wrap errors with domain/operation context.
|
- Wrap errors with domain/operation context.
|
||||||
- Map domain errors to adapter-appropriate statuses/codes without leaking sensitive internals.
|
- Map domain errors to adapter-appropriate statuses/codes without leaking sensitive internals.
|
||||||
- Keep stderr summaries concise for CLI success/error paths.
|
|
||||||
- Never emit raw secret values.
|
- Never emit raw secret values.
|
||||||
|
|
||||||
## Testing Expectations
|
## Testing And Documentation
|
||||||
|
|
||||||
- Core runner behavior should be covered with isolated unit tests and fixture-based integration tests.
|
Testing philosophy and change-validation expectations are defined by the
|
||||||
- Adapter behavior should be tested for parse/mapping/error semantics.
|
[testing policy](testing.md). Documentation ownership and maintenance rules are
|
||||||
- Config parsing, prompt/profile loading, validator behavior, and LLM client error handling should remain covered by package tests.
|
defined by the [documentation policy](documentation.md).
|
||||||
- Repository-level docs/examples that claim runnable behavior should be validated by tests or smoke commands.
|
|
||||||
|
|
||||||
## Documentation Expectations
|
|
||||||
|
|
||||||
- Document implemented behavior only outside `docs/roadmap/`.
|
|
||||||
- Keep canonical reference locations stable (`docs/cli.md`, `docs/config.md`, `docs/operations.md`, `docs/troubleshooting.md`, `docs/internal/`).
|
|
||||||
- Update docs in the same change when architecture-relevant behavior changes.
|
|
||||||
|
|
||||||
## Architectural Invariants
|
## Architectural Invariants
|
||||||
|
|
||||||
- `Runner.Run` reuses `Runner.Prepare` flow.
|
- `Runner.Run` reuses `Runner.Prepare` flow.
|
||||||
- CLI and HTTP currently instantiate `Runner` without a repairer.
|
- Raw API key values must not be accepted through external configuration or
|
||||||
- Artifact reading supports `inline` and `file` references.
|
request payloads, and resolved secret values must not be emitted.
|
||||||
- Unknown input fields in config/prompt/profile/http JSON should be rejected by strict decoding.
|
|
||||||
- Raw API key values must not be accepted through config/HTTP payloads.
|
|
||||||
|
|
||||||
## Non-Goals
|
## Non-Goals
|
||||||
|
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
# Development Guide
|
|
||||||
|
|
||||||
This document defines contributor workflow for Scriptorium.
|
|
||||||
|
|
||||||
## Repository Layout
|
|
||||||
|
|
||||||
- root package `scriptorium`: public Go facade, options, types, and error mapping.
|
|
||||||
- `cmd/scriptorium`: application entrypoint.
|
|
||||||
- `internal/domain`: core contracts.
|
|
||||||
- `internal/usecase`: runner orchestration.
|
|
||||||
- `internal/adapter/cli`: CLI adapter.
|
|
||||||
- `internal/adapter/http`: HTTP adapter.
|
|
||||||
- `internal/config`: application settings loading and precedence.
|
|
||||||
- `internal/defaults`: default constants.
|
|
||||||
- `internal/promptdef`: prompt-definition repository.
|
|
||||||
- `internal/profile`: execution-profile repository.
|
|
||||||
- `internal/profile/builtin`: embedded built-in execution profiles.
|
|
||||||
- `internal/filecatalog`: shared source discovery and path helpers.
|
|
||||||
- `internal/artifact`: artifact readers.
|
|
||||||
- `internal/prompt`: prompt rendering.
|
|
||||||
- `internal/llm`: LLM client interface and OpenAI-compatible implementation.
|
|
||||||
- `internal/validate`: validation interfaces and implementation.
|
|
||||||
- `internal/format`: prepared-run formatting.
|
|
||||||
- `docs/`: canonical documentation.
|
|
||||||
- `examples/`: copyable maintained examples and fixtures.
|
|
||||||
|
|
||||||
## Common Commands
|
|
||||||
|
|
||||||
Build:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go build ./cmd/scriptorium
|
|
||||||
```
|
|
||||||
|
|
||||||
Test:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./...
|
|
||||||
```
|
|
||||||
|
|
||||||
Targeted test runs commonly used during changes:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test .
|
|
||||||
go test ./internal/adapter/cli ./internal/adapter/http ./internal/usecase
|
|
||||||
go test ./internal/...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Coding Conventions
|
|
||||||
|
|
||||||
- Prefer small interfaces at package boundaries.
|
|
||||||
- Keep adapter packages focused on translation and IO concerns.
|
|
||||||
- Keep domain/use-case logic outside adapters.
|
|
||||||
- Wrap errors with operation context.
|
|
||||||
- Use strict decoding for user-provided YAML/JSON where applicable.
|
|
||||||
- Avoid introducing dependencies unless they materially reduce risk/complexity.
|
|
||||||
|
|
||||||
## Dependency Policy
|
|
||||||
|
|
||||||
- Prefer standard library unless an external library is clearly justified.
|
|
||||||
- Current non-stdlib dependencies are intentionally small:
|
|
||||||
- `gopkg.in/yaml.v3` for YAML decoding.
|
|
||||||
- `github.com/santhosh-tekuri/jsonschema/v6` for JSON Schema validation.
|
|
||||||
- Do not leak dependency-specific types across unrelated package boundaries.
|
|
||||||
|
|
||||||
## How To Add App Config Fields
|
|
||||||
|
|
||||||
1. Add fields in `internal/config/config.go` (`Config`, `AppSettings`, and/or `CLIOverrides` as needed).
|
|
||||||
2. Apply defaults in `BuiltInDefaults()` when required.
|
|
||||||
3. Parse and validate in `applyConfig` / `ApplyCLIOverrides`.
|
|
||||||
4. Wire the field through the consuming adapter(s).
|
|
||||||
5. Add/update config tests in `internal/config/config_test.go`.
|
|
||||||
6. Update canonical docs (`docs/config.md`, and other affected docs).
|
|
||||||
|
|
||||||
## How To Add CLI Flags
|
|
||||||
|
|
||||||
1. Add flags in `internal/adapter/cli/run.go` for the relevant command.
|
|
||||||
2. Ensure precedence behavior remains consistent with app config rules.
|
|
||||||
3. Keep `run`, `render`, and `serve` flag surfaces intentionally scoped.
|
|
||||||
4. Add/update parser and command tests in `internal/adapter/cli/run_test.go`.
|
|
||||||
5. Update `docs/cli.md` and any related docs/examples.
|
|
||||||
|
|
||||||
## How To Add Adapters Or Adapter Capabilities
|
|
||||||
|
|
||||||
1. Define or reuse the appropriate interface boundary in domain/use-case packages.
|
|
||||||
2. Implement adapter code under `internal/adapter/<name>` (or relevant boundary package).
|
|
||||||
3. Keep business decisions in `internal/usecase`.
|
|
||||||
4. Add focused adapter tests for mapping, parse, and error behavior.
|
|
||||||
5. Document the new/changed boundary in `docs/internal/adapters.md`.
|
|
||||||
6. If source-loading behavior changes, update `docs/internal/sources.md`.
|
|
||||||
7. If an external contract changes, update the canonical public or integration doc in the same change.
|
|
||||||
|
|
||||||
## How To Update Prompt/Profile/Schema Assets
|
|
||||||
|
|
||||||
1. Keep prompt/profile/schema files valid under strict loaders.
|
|
||||||
2. Keep examples secret-free.
|
|
||||||
3. Re-run tests that cover prompt/profile/validation behavior.
|
|
||||||
4. Update `docs/config.md` and any docs that reference changed contracts.
|
|
||||||
|
|
||||||
## Documentation Update Expectations
|
|
||||||
|
|
||||||
When behavior changes:
|
|
||||||
|
|
||||||
1. Update canonical doc locations, not duplicate files.
|
|
||||||
2. Keep non-roadmap docs limited to implemented behavior.
|
|
||||||
3. Update links after file moves/renames.
|
|
||||||
4. Re-run relevant tests and smoke commands.
|
|
||||||
5. For internal boundary docs, check references with `rg "docs/internal|internal/sources" docs/policy docs/internal`.
|
|
||||||
|
|
||||||
Docs work is complete only when code/tests/examples/docs agree.
|
|
||||||
@@ -1,446 +1,163 @@
|
|||||||
# Go Project Documentation Policy
|
# Documentation Policy
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
Project documentation must help five audiences:
|
This policy assigns each documentation topic to one canonical owner. Its goal is
|
||||||
|
to keep this repository's documentation accurate, concise, discoverable, and
|
||||||
1. users who need to run the application;
|
resistant to drift for users, operators, developers, integrators, and LLM
|
||||||
2. administrators/operators who need to configure and operate it;
|
coding agents.
|
||||||
3. developers who need to understand and change it safely;
|
|
||||||
4. LLM coding agents that need clear scope, boundaries, and invariants;
|
|
||||||
5. developers and LLM coding agents integrating this project from another codebase.
|
|
||||||
|
|
||||||
Docs should be accurate, concise, task-oriented, and organized by audience. Prefer links to canonical docs over repetition.
|
|
||||||
|
|
||||||
## Core Rules
|
## Core Rules
|
||||||
|
|
||||||
### 1. Keep docs concise
|
### One Canonical Owner
|
||||||
|
|
||||||
Each document should cover a defined scope and only the essentials for that scope.
|
Each authoritative fact belongs in one document. A non-owning document may give
|
||||||
|
a short, stable summary for orientation, but it must link to the canonical owner
|
||||||
|
instead of repeating volatile details.
|
||||||
|
|
||||||
Avoid:
|
Volatile details include commands, flags, configuration fields and defaults,
|
||||||
- long background explanations;
|
module keys, schemas, file names, paths, status codes, retry behavior, and
|
||||||
- repeated reference material;
|
runtime guarantees. If readers could reasonably treat a statement as a
|
||||||
- implementation detail in user-facing docs;
|
contract, maintain it only in the owning document.
|
||||||
- aspirational language outside roadmap docs;
|
|
||||||
- verbose examples where one minimal example is clearer.
|
|
||||||
|
|
||||||
### 2. Document only implemented behavior outside roadmap files
|
Minimal tested usage examples are allowed outside the owning contract when this
|
||||||
|
policy assigns them an orientation or instructional purpose. They must link to
|
||||||
|
the canonical contract and must not redefine complete syntax, defaults, or
|
||||||
|
semantics.
|
||||||
|
|
||||||
|
### Current And Future Behavior
|
||||||
|
|
||||||
|
Outside `docs/roadmap/`, documentation describes implemented behavior only.
|
||||||
|
Partial features may be described only to their implemented boundary.
|
||||||
|
|
||||||
Unimplemented, planned, aspirational, experimental, or future work may be described only under:
|
ADRs are the narrow exception: an ADR may record an accepted architectural
|
||||||
|
decision before implementation, but acceptance must not be presented as proof
|
||||||
|
that the behavior exists. The roadmap owns implementation status and sequencing
|
||||||
|
until the decision is implemented. Current architecture, user, operator,
|
||||||
|
integration, and internal documentation are updated when the behavior lands.
|
||||||
|
|
||||||
- `docs/roadmap/`
|
### Audience And Detail
|
||||||
|
|
||||||
|
Write for the document's stated audience and include only the detail needed for
|
||||||
|
its owned topic. User and operator docs should not expose implementation detail.
|
||||||
|
Developer docs should link to user-facing and external contracts rather than
|
||||||
|
restate them.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
Complete copyable files belong in `examples/`. Documentation may use the
|
||||||
|
smallest illustrative snippet needed to explain its owned topic, but should link
|
||||||
|
to maintained examples instead of embedding a second complete copy.
|
||||||
|
|
||||||
|
Examples must be valid, secret-free, and tested where practical. Commands and
|
||||||
|
configuration used in documentation should match the application.
|
||||||
|
|
||||||
No other documentation file, including `README.md`, should describe code, features, modules, stages, commands, config fields, or behaviors that do not currently exist.
|
### Security And Privacy
|
||||||
|
|
||||||
If a feature is partial, non-roadmap docs may describe only the implemented portion and its current boundary.
|
Documentation and examples must not contain real credentials, private keys,
|
||||||
|
private environment dumps, sensitive source material, or private infrastructure
|
||||||
### 3. Use canonical homes
|
details unless intentionally public. Document secret-handling mechanisms, not
|
||||||
|
secret values.
|
||||||
Each type of information should have one canonical location.
|
|
||||||
|
## Canonical Ownership
|
||||||
Canonical homes:
|
|
||||||
|
| Topic | Canonical owner | Owned content | Content owned elsewhere |
|
||||||
- project purpose and quickstart: `README.md`
|
| --- | --- | --- | --- |
|
||||||
- development principles: `docs/policy/architecture.md`
|
| Product orientation and minimal end-to-end quickstart | `README.md` | What this project is, why it is useful, one shortest successful invocation, and links onward. | Complete command reference, configuration reference, operational procedures, implementation detail. |
|
||||||
- public HTTP API reference: `docs/api.md`
|
| Contributor entry point | `docs/development.md` | Task-oriented reading guide, minimal contributor orientation, baseline validation commands, and links to canonical docs. | Package inventory, architecture rules, subsystem behavior, and detailed change recipes, which belong in the relevant internal component document. |
|
||||||
- configuration reference: `docs/config.md`
|
| Current application architecture | `docs/policy/architecture.md` | System shape, normative ownership, dependency direction, architectural boundaries, invariants, safety properties, and non-goals. | Concrete package inventory, implementation mechanics, contributor procedures, decision history, future work. |
|
||||||
- CLI reference: `docs/cli.md`
|
| Documentation organization | `docs/policy/documentation.md` | Documentation ownership, audience boundaries, maintenance rules, and ADR/document lifecycle. | Application architecture or product behavior. |
|
||||||
- operations and recovery: `docs/operations.md`
|
| Testing policy | `docs/policy/testing.md` | Test philosophy, risk-based sufficiency, test boundaries, doubles, coverage guidance, regression-test policy, and criteria for adding, rewriting, or deleting tests. | Subsystem behavior, application contracts, subsystem-specific test inventories, and implementation plans. |
|
||||||
- troubleshooting: `docs/troubleshooting.md`
|
| CLI contract | `docs/cli.md` | Commands, arguments, flags, invocation semantics, and exit codes. | End-to-end operating procedures, configuration field definitions, runtime filesystem layout, module implementation details. |
|
||||||
- public API/package consumer guidance: `docs/consumers/`
|
| Configuration contract | `docs/config.md` | Discovery and precedence, file schema, fields, defaults, environment overrides, validation rules, and user-selectable module or validator keys. | Complete example files, CLI syntax, runtime state lifecycle, module implementation details. |
|
||||||
- implemented internals: `docs/internal/`
|
| Operations | `docs/operations.md` | Runtime workflows, physical filesystem and state layout, output, cache, and debug handling, resume, cleanup, permissions, recovery, and operational limits. | CLI flag syntax, configuration field definitions, logical output schemas, implementation mechanics. |
|
||||||
- external protocol, service, and file-format contracts: `docs/integrations/`
|
| Public HTTP contract | `docs/api.md` | Routes, authentication, media types, request and response schemas, status codes, pagination, caching, idempotency, rate limits, and HTTP retry semantics. | Client walkthroughs, upstream or downstream integration internals, implementation detail. |
|
||||||
- future work: `docs/roadmap/`
|
| Consumer guidance | `docs/consumers/` | Task-oriented use of the public interface, minimal client examples, and consumer responsibilities. | HTTP wire semantics, external protocol contracts, internal implementation detail. |
|
||||||
- contributor workflow: `docs/policy/development.md`
|
| External and durable integration contracts | `docs/integrations/` | External file formats and protocols, upstream and downstream contracts, logical output bundle paths and schemas, media types, and compatibility behavior. | Physical runtime placement and lifecycle, internal transformations, CLI syntax, configuration defaults. |
|
||||||
- copyable examples: `examples/`
|
| Implemented component inventory | `docs/internal/overview.md` | Current packages and components, their implemented responsibilities, and links to focused internal docs. | Normative architecture, contributor reading policy, external contracts. |
|
||||||
|
| Internal component behavior | Other files under `docs/internal/` | Implementation flow, internal collaborators and state transitions, package-local guarantees and failures, and relevant tests. | Global architecture invariants, configuration definitions and defaults, external schemas, operator procedures. |
|
||||||
Other files should summarize briefly and link to the canonical source.
|
| Architectural decision history | `docs/adr/` | Significant decisions, context, alternatives, rationale, consequences, and supersession history. | Current behavior reference, implementation status, task sequencing. |
|
||||||
|
| Future work and implementation status | `docs/roadmap/` | Proposed, accepted, deferred, or rejected work; implementation status; sequencing; and task breakdowns. | Implemented behavior reference and architectural decision rationale. |
|
||||||
### 4. Keep examples real
|
| Complete copyable artifacts | `examples/` | Maintained configuration, inputs, and other files intended to be copied or run. | Field-by-field reference, command reference, prose explanation. |
|
||||||
|
|
||||||
Examples should be valid, maintained, and free of secrets.
|
Documents that do not exist are required only when the corresponding interface
|
||||||
|
or responsibility exists. Do not create placeholder API, consumer, integration,
|
||||||
Where practical:
|
or operations documents for behavior the application does not have.
|
||||||
- example configs should load successfully;
|
|
||||||
- example commands should match real CLI syntax;
|
## Boundary Rules
|
||||||
- important examples should be covered by tests.
|
|
||||||
|
### Orientation
|
||||||
## Documentation Profiles
|
|
||||||
|
The README owns product orientation. The developer guide routes contributors.
|
||||||
All projects require:
|
Architecture owns normative structure. Internal overview owns the current
|
||||||
|
concrete component map. These documents may link to one another but should not
|
||||||
- `README.md`
|
maintain parallel package or behavior descriptions.
|
||||||
- `docs/policy/architecture.md`
|
|
||||||
|
### Commands, Configuration, And Operations
|
||||||
Additional docs depend on the project.
|
|
||||||
|
CLI documentation answers how to invoke the application. Configuration
|
||||||
### Small library
|
documentation answers what settings mean. Operations answers what happens to
|
||||||
|
runtime state and how to operate or recover the application. When a workflow
|
||||||
Recommended:
|
crosses these topics, choose the document that owns the task and link to the
|
||||||
- `docs/policy/development.md`, if contributor conventions are non-obvious
|
other contracts.
|
||||||
|
|
||||||
### Simple CLI
|
### Contracts And Implementation
|
||||||
|
|
||||||
Required:
|
Integration and API documents define externally observable shapes and
|
||||||
- `docs/cli.md`
|
semantics. Internal documents explain how this project implements or consumes
|
||||||
|
those contracts. Internal docs may name a field, file, or protocol to identify
|
||||||
Recommended:
|
a dependency, but must link to its canonical contract for the definition.
|
||||||
- `docs/policy/development.md`
|
|
||||||
|
### Security Topics
|
||||||
### Config-driven CLI
|
|
||||||
|
This policy owns what documentation and examples may contain. Architecture owns
|
||||||
Required:
|
application security invariants. Configuration owns credential-supply
|
||||||
- `docs/cli.md`
|
mechanisms. Operations owns permissions and handling of sensitive runtime
|
||||||
- `docs/config.md`
|
artifacts. Internal docs own implementation mechanisms only.
|
||||||
|
|
||||||
Recommended:
|
## Architecture Decision Records
|
||||||
- `examples/`
|
|
||||||
- `docs/policy/development.md`
|
Use sequentially numbered ADR filenames such as
|
||||||
|
`0001-record-architecture-decisions.md`. Follow the lightweight Nygard format:
|
||||||
### Stateful or operator-facing application
|
|
||||||
|
1. title;
|
||||||
Required:
|
2. status;
|
||||||
- `docs/cli.md`, if CLI-based
|
3. date;
|
||||||
- `docs/config.md`, if config-driven
|
4. context;
|
||||||
- `docs/operations.md`
|
5. decision;
|
||||||
|
6. alternatives considered;
|
||||||
Recommended:
|
7. consequences.
|
||||||
- `docs/troubleshooting.md`
|
|
||||||
- `examples/`
|
Use one of these statuses:
|
||||||
- `docs/policy/development.md`
|
|
||||||
|
- **Proposed:** the decision is under consideration and may change;
|
||||||
### Modular, service-oriented, or orchestration application
|
- **Accepted:** the decision is approved, whether or not implementation is
|
||||||
|
complete;
|
||||||
Required:
|
- **Rejected:** the proposed decision was considered and not adopted;
|
||||||
- `docs/cli.md`, if CLI-based
|
- **Superseded:** a later ADR replaces the accepted decision.
|
||||||
- `docs/config.md`, if config-driven
|
|
||||||
- `docs/operations.md`
|
A proposed ADR transitions to accepted or rejected. An accepted ADR transitions
|
||||||
- `docs/internal/`
|
to superseded only when a later accepted ADR replaces it. An ADR may be created
|
||||||
- `docs/policy/development.md`
|
as accepted when the decision has already been made.
|
||||||
|
|
||||||
Recommended:
|
Treat the decision content of an accepted ADR as immutable. Its status and
|
||||||
- `docs/troubleshooting.md`
|
supersession metadata may be updated, but a changed decision requires a new ADR.
|
||||||
- validated examples under `examples/`
|
A superseded ADR must link to its replacement, and the replacement must link
|
||||||
|
back to the superseded ADR. Rejected architectural alternatives belong in the
|
||||||
### Public HTTP API service
|
ADR; rejected product ideas belong in the roadmap.
|
||||||
|
|
||||||
Required:
|
## Maintenance
|
||||||
- `docs/api.md`
|
|
||||||
- `docs/cli.md`, if CLI-based
|
When behavior changes, update its canonical owner in the same change. If
|
||||||
- `docs/config.md`, if config-driven
|
ownership moves, remove the old definition and replace it with a link where
|
||||||
- `docs/operations.md`
|
navigation remains useful.
|
||||||
- `docs/internal/`
|
|
||||||
- `docs/policy/development.md`
|
Before completing documentation work:
|
||||||
|
|
||||||
Recommended:
|
- verify affected behavior and examples;
|
||||||
- `docs/troubleshooting.md`
|
- check commands, flags, fields, defaults, schemas, and paths against their
|
||||||
- `docs/consumers/`, for task-oriented client integration guides
|
implementation;
|
||||||
- `docs/integrations/`, for upstream/downstream service contracts
|
- keep unimplemented behavior in the roadmap, subject to the ADR exception;
|
||||||
- validated examples under `examples/`
|
- remove stale references and validate links;
|
||||||
|
- confirm that non-owning documents summarize and link rather than redefine;
|
||||||
### Project with public packages or consumer APIs
|
- confirm that no secrets or sensitive private data were added.
|
||||||
|
|
||||||
Required:
|
|
||||||
- `docs/consumers/api.md`
|
|
||||||
- one `docs/consumers/pkg-<name>.md` file per public package, if public packages exist
|
|
||||||
|
|
||||||
Recommended:
|
|
||||||
- copyable consumer examples under `examples/`, if practical
|
|
||||||
|
|
||||||
## Required Documents
|
|
||||||
|
|
||||||
### README.md
|
|
||||||
|
|
||||||
**Audience:** users, administrators, operators
|
|
||||||
|
|
||||||
The README is the outward-facing project orientation page.
|
|
||||||
|
|
||||||
It should include, in order:
|
|
||||||
|
|
||||||
1. concise description;
|
|
||||||
2. elevator pitch;
|
|
||||||
3. shortest useful command or usage example;
|
|
||||||
4. links to targeted docs.
|
|
||||||
|
|
||||||
The README should be short. It is not a manual.
|
|
||||||
|
|
||||||
The “shortest useful command” means the simplest command that performs the project’s core use case. (It does not mean `app --help`.)
|
|
||||||
|
|
||||||
### docs/policy/architecture.md
|
|
||||||
|
|
||||||
**Audience:** developers, LLM coding agents
|
|
||||||
|
|
||||||
`docs/policy/architecture.md` is required for every project.
|
|
||||||
|
|
||||||
It is an inward-facing development policy document. It should describe how the project is intended to be built and changed.
|
|
||||||
|
|
||||||
It should include:
|
|
||||||
|
|
||||||
- project shape;
|
|
||||||
- core design principles;
|
|
||||||
- package and boundary philosophy;
|
|
||||||
- state/persistence philosophy, if applicable;
|
|
||||||
- external integration philosophy, if applicable;
|
|
||||||
- error-handling and logging principles;
|
|
||||||
- testing expectations;
|
|
||||||
- documentation expectations;
|
|
||||||
- architectural invariants;
|
|
||||||
- explicit non-goals, if useful.
|
|
||||||
|
|
||||||
Notably, this file should prescribe a core development *policy* that should remain unchanged as the application evolves. It is not a place for details (e.g., CLI flags) that could change over time.
|
|
||||||
|
|
||||||
The contents of `architecture.md` should be trim and concise. LLMs may be directed to review it routinely via AGENTS.md, CLAUDE.md, or similar.
|
|
||||||
|
|
||||||
### docs/api.md
|
|
||||||
|
|
||||||
**Audience:** external HTTP API consumers, developers, LLM coding agents integrating by HTTP
|
|
||||||
|
|
||||||
Required for projects whose primary public interface is HTTP.
|
|
||||||
|
|
||||||
`docs/api.md` is the canonical public HTTP API contract. It should be normative for external consumers and should not be duplicated by README, operations docs, consumer guides, or integration docs.
|
|
||||||
|
|
||||||
It should include:
|
|
||||||
|
|
||||||
1. base URL conventions;
|
|
||||||
2. authentication and authorization behavior, if implemented;
|
|
||||||
3. response envelope;
|
|
||||||
4. supported media types and content negotiation behavior;
|
|
||||||
5. shared query parameters;
|
|
||||||
6. endpoint reference grouped by route family;
|
|
||||||
7. request parameters and validation rules;
|
|
||||||
8. response fields, units, nullability, and optionality;
|
|
||||||
9. error response shape and status codes;
|
|
||||||
10. pagination, caching, rate-limit, idempotency, and retry behavior, if implemented;
|
|
||||||
11. compact request and response examples.
|
|
||||||
|
|
||||||
It must document only implemented endpoints and behavior. Planned endpoints, proposed fields, future filters, and experimental response shapes belong only under `docs/roadmap/`.
|
|
||||||
|
|
||||||
For HTTP API projects, `docs/consumers/` may provide task-oriented client integration guides, but those guides should link to `docs/api.md` for the authoritative endpoint contract.
|
|
||||||
|
|
||||||
### docs/policy/development.md
|
|
||||||
|
|
||||||
**Audience:** developers, LLM coding agents
|
|
||||||
|
|
||||||
Required for projects maintained by humans and LLM coding agents.
|
|
||||||
|
|
||||||
It should include:
|
|
||||||
|
|
||||||
- repository layout;
|
|
||||||
- build/test commands;
|
|
||||||
- coding conventions;
|
|
||||||
- dependency policy;
|
|
||||||
- how to add config fields;
|
|
||||||
- how to add CLI flags;
|
|
||||||
- how to add modules or adapters, if applicable;
|
|
||||||
- how to update examples;
|
|
||||||
- documentation update expectations.
|
|
||||||
|
|
||||||
### docs/config.md
|
|
||||||
|
|
||||||
**Audience:** administrators, operators, advanced users
|
|
||||||
|
|
||||||
Required for applications with configuration files.
|
|
||||||
|
|
||||||
It should include, in order:
|
|
||||||
|
|
||||||
1. config file locations and discovery precedence;
|
|
||||||
2. minimal working config;
|
|
||||||
3. production-oriented config;
|
|
||||||
4. full configuration reference;
|
|
||||||
5. secrets handling, if applicable;
|
|
||||||
6. links to maintained examples.
|
|
||||||
|
|
||||||
The full configuration reference should be canonical.
|
|
||||||
|
|
||||||
### docs/cli.md
|
|
||||||
|
|
||||||
**Audience:** users, administrators, operators
|
|
||||||
|
|
||||||
Required for CLI applications.
|
|
||||||
|
|
||||||
It should include, in order:
|
|
||||||
|
|
||||||
1. shortest useful command;
|
|
||||||
2. command overview;
|
|
||||||
3. complete flag reference;
|
|
||||||
4. common workflows;
|
|
||||||
5. diagnostic or recovery commands, if applicable.
|
|
||||||
|
|
||||||
Explain when commands are useful, not just their syntax.
|
|
||||||
|
|
||||||
### docs/operations.md
|
|
||||||
|
|
||||||
**Audience:** administrators, operators
|
|
||||||
|
|
||||||
Required for applications that maintain state, support resume behavior, run multi-step workflows, write durable artifacts, use remote storage, or require recovery procedures.
|
|
||||||
|
|
||||||
It should cover:
|
|
||||||
|
|
||||||
- normal workflow;
|
|
||||||
- filesystem layout;
|
|
||||||
- remote storage layout, if applicable;
|
|
||||||
- logs and manifests;
|
|
||||||
- resume/retry behavior;
|
|
||||||
- cleanup behavior;
|
|
||||||
- archive/backup behavior;
|
|
||||||
- safe recovery procedures;
|
|
||||||
- operational caveats.
|
|
||||||
|
|
||||||
### docs/troubleshooting.md
|
|
||||||
|
|
||||||
**Audience:** administrators, operators
|
|
||||||
|
|
||||||
Recommended once recurring failure modes exist.
|
|
||||||
|
|
||||||
Each entry should include:
|
|
||||||
|
|
||||||
- symptom;
|
|
||||||
- likely cause;
|
|
||||||
- diagnostic command or inspection step;
|
|
||||||
- safe fix;
|
|
||||||
- relevant links.
|
|
||||||
|
|
||||||
### docs/consumers/
|
|
||||||
|
|
||||||
**Audience:** developers and LLM coding agents integrating this project from another codebase
|
|
||||||
|
|
||||||
Required for projects with public packages, SDKs, client APIs, plugin APIs, or other application-facing integration surfaces.
|
|
||||||
|
|
||||||
This directory describes how an external codebase should consume the project's public API. It should be task-oriented and copyable where useful. It is not the place for internal implementation details or operator procedures.
|
|
||||||
|
|
||||||
For projects whose public API is HTTP, `docs/consumers/` is not required, and it should not duplicate the endpoint reference in `docs/api.md`. If present, it may provide practical integration workflows, client-specific examples, or migration notes that link back to `docs/api.md`.
|
|
||||||
|
|
||||||
`docs/consumers/api.md` should provide the consumer-facing overview and primary implementation workflow. It should include:
|
|
||||||
|
|
||||||
1. intended consumer audience and use cases;
|
|
||||||
2. required inputs supplied by operators or deployment configuration;
|
|
||||||
3. recommended public package or API workflow;
|
|
||||||
4. minimal copyable example;
|
|
||||||
5. consumer responsibilities and boundaries;
|
|
||||||
6. retry, idempotency, or status behavior, if applicable;
|
|
||||||
7. links to package-specific docs and canonical integration contracts.
|
|
||||||
|
|
||||||
Package-specific docs should be named `pkg-<name>.md` and should include:
|
|
||||||
|
|
||||||
1. import path;
|
|
||||||
2. intended use cases;
|
|
||||||
3. primary types and functions needed by consumers;
|
|
||||||
4. minimal examples;
|
|
||||||
5. validation, error, retry, and boundary behavior;
|
|
||||||
6. links to canonical file-format or wire-protocol contracts.
|
|
||||||
|
|
||||||
### docs/internal/
|
|
||||||
|
|
||||||
**Audience:** developers, LLM coding agents
|
|
||||||
|
|
||||||
Required for modular, service-oriented, or orchestration projects.
|
|
||||||
|
|
||||||
This directory describes implemented internal components. It is not the roadmap.
|
|
||||||
|
|
||||||
Use one file per major component where useful.
|
|
||||||
|
|
||||||
Each component doc should include:
|
|
||||||
|
|
||||||
1. purpose;
|
|
||||||
2. inputs and outputs;
|
|
||||||
3. boundaries;
|
|
||||||
4. config fields used;
|
|
||||||
5. external adapters used;
|
|
||||||
6. state or manifest behavior, if applicable;
|
|
||||||
7. skip/resume behavior, if applicable;
|
|
||||||
8. failure behavior;
|
|
||||||
9. tests to inspect before changing;
|
|
||||||
10. architectural invariants.
|
|
||||||
|
|
||||||
### docs/roadmap/
|
|
||||||
|
|
||||||
**Audience:** maintainers, developers, LLM coding agents
|
|
||||||
|
|
||||||
This is the only place for planned, future, aspirational, experimental, or unimplemented work.
|
|
||||||
|
|
||||||
Roadmap docs should clearly distinguish:
|
|
||||||
|
|
||||||
- proposed work;
|
|
||||||
- accepted plans;
|
|
||||||
- deferred ideas;
|
|
||||||
- rejected ideas;
|
|
||||||
- implementation prompts or task breakdowns, if useful.
|
|
||||||
|
|
||||||
Roadmap docs should not be confused with current behavior.
|
|
||||||
|
|
||||||
### docs/integrations/
|
|
||||||
|
|
||||||
**Audience:** developers, LLM coding agents
|
|
||||||
|
|
||||||
Required for projects that depend on external CLIs, APIs, services, protocols, or file formats where the integration contract is important to maintain.
|
|
||||||
|
|
||||||
This directory contains concise, versioned reference notes for external integration contracts. It should document only the parts of the external system that this project actually uses or exposes.
|
|
||||||
|
|
||||||
For public HTTP API services, `docs/integrations/` should document upstream, downstream, storage, protocol, or runtime contracts that the service depends on or bridges. It should not become a second copy of the public HTTP endpoint reference; that belongs in `docs/api.md`.
|
|
||||||
|
|
||||||
Use one file per integration where useful.
|
|
||||||
|
|
||||||
## Examples Directory
|
|
||||||
|
|
||||||
Projects with non-trivial configuration or workflows should include `examples/`.
|
|
||||||
|
|
||||||
Useful examples include:
|
|
||||||
|
|
||||||
- minimal working config;
|
|
||||||
- production-oriented config;
|
|
||||||
- full annotated config;
|
|
||||||
- local development config;
|
|
||||||
- remote/object-storage config;
|
|
||||||
- minimal session/input file.
|
|
||||||
|
|
||||||
Examples should be valid, maintained, tested when practical, and linked from relevant docs.
|
|
||||||
|
|
||||||
## Security and Privacy
|
|
||||||
|
|
||||||
Docs and examples must not include:
|
|
||||||
|
|
||||||
- real API keys;
|
|
||||||
- tokens;
|
|
||||||
- passwords;
|
|
||||||
- private keys;
|
|
||||||
- private environment dumps;
|
|
||||||
- sensitive user data;
|
|
||||||
- raw private transcripts;
|
|
||||||
- private infrastructure details unless intentionally public.
|
|
||||||
|
|
||||||
Document secret-handling mechanisms, not actual secret values.
|
|
||||||
|
|
||||||
## Maintenance Rules
|
|
||||||
|
|
||||||
When docs change, verify the affected behavior.
|
|
||||||
|
|
||||||
Where practical:
|
|
||||||
|
|
||||||
- load example config files in tests;
|
|
||||||
- test CLI examples or command parser behavior;
|
|
||||||
- validate documented flags against real flags;
|
|
||||||
- remove stale references;
|
|
||||||
- update links after renames;
|
|
||||||
- keep roadmap content out of non-roadmap docs.
|
|
||||||
|
|
||||||
If documentation and code disagree, fix the documentation and/or open a roadmap item; do not leave aspirational behavior in current-behavior docs.
|
|
||||||
|
|
||||||
Documentation is complete only when it matches the current code.
|
|
||||||
|
|
||||||
## Documentation Change Checklist
|
|
||||||
|
|
||||||
Before merging documentation changes, verify:
|
|
||||||
|
|
||||||
- README is concise and orientation-focused.
|
|
||||||
- `docs/policy/architecture.md` describes development principles.
|
|
||||||
- `docs/api.md` is the canonical HTTP contract for HTTP API services.
|
|
||||||
- Future work appears only under `docs/roadmap/`.
|
|
||||||
- User-facing docs avoid unnecessary internals.
|
|
||||||
- Consumer-facing docs explain public APIs without duplicating HTTP endpoint or integration contracts.
|
|
||||||
- Developer-facing docs preserve boundaries and invariants.
|
|
||||||
- Config examples match the schema.
|
|
||||||
- CLI examples match real commands and flags.
|
|
||||||
- Defaults appear in the canonical config reference.
|
|
||||||
- No secrets or private data are included.
|
|
||||||
- Links are accurate.
|
|
||||||
|
|||||||
296
docs/policy/testing.md
Normal file
296
docs/policy/testing.md
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
# Testing Policy
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Our tests exist to make **incorrect changes expensive and correct changes cheap**.
|
||||||
|
|
||||||
|
We do not optimize for test count, line coverage, exhaustive isolation, or the fewest possible tests. We optimize for sufficient confidence in important behavior while imposing as little unnecessary friction as possible on future development.
|
||||||
|
|
||||||
|
## Every test has a cost
|
||||||
|
|
||||||
|
Testing is not an unqualified good. Every test imposes both an immediate cost and a continuing lifetime cost.
|
||||||
|
|
||||||
|
A test must be:
|
||||||
|
|
||||||
|
- written and reviewed;
|
||||||
|
- understood by future maintainers and coding agents;
|
||||||
|
- executed in local and CI workflows;
|
||||||
|
- diagnosed when it fails;
|
||||||
|
- updated when legitimate behavior changes;
|
||||||
|
- maintained as fixtures, APIs, and dependencies evolve; and
|
||||||
|
- removed or rewritten when it becomes redundant, brittle, misleading, or obsolete.
|
||||||
|
|
||||||
|
Tests also create cognitive and architectural friction. They can constrain refactoring, duplicate policy, slow feedback loops, add noise to failures, and cause harmless implementation changes to require unrelated edits across the suite.
|
||||||
|
|
||||||
|
A test is warranted only when the confidence it provides justifies these costs.
|
||||||
|
|
||||||
|
Apply this cost-benefit analysis at two levels:
|
||||||
|
|
||||||
|
1. **Per test:** What realistic defect does this test detect, how consequential would that defect be, and is that protection worth the test's lifetime cost?
|
||||||
|
2. **Across the suite:** Does this collection provide materially more confidence than a smaller, simpler suite would?
|
||||||
|
|
||||||
|
The preferred test suite is a **lean suite that provides sufficient confidence in the risks that matter, without redundant or low-value tests**. We seek sufficient confidence with the least unnecessary testing friction, not the fewest possible tests.
|
||||||
|
|
||||||
|
Some friction is intentional. Tests should make dangerous changes—such as breaking compatibility, corrupting data, violating security boundaries, or reintroducing subtle bugs—require deliberate review. They should not make ordinary internal changes needlessly expensive.
|
||||||
|
|
||||||
|
The cost of a test is not a reason to omit testing by default. Do not cite maintenance cost abstractly. When omitting a plausible test, be able to state why the protected failure is low-risk, already covered, obvious, reversible, or cheaper to detect elsewhere. For consequential, subtle, or difficult-to-observe behavior, the presumption should favor testing.
|
||||||
|
|
||||||
|
## Default testing style
|
||||||
|
|
||||||
|
Use a **classical/Detroit-style** approach:
|
||||||
|
|
||||||
|
- Test observable behavior, resulting state, contracts, and invariants.
|
||||||
|
- Use real internal collaborators when they are fast and deterministic.
|
||||||
|
- Use fakes, stubs, or mocks primarily at expensive, nondeterministic, destructive, or external boundaries.
|
||||||
|
- Prefer package-level behavioral tests over tests coupled to private helpers or internal call sequences.
|
||||||
|
- Treat exact collaborator interactions as testable behavior only when the interaction itself is a requirement.
|
||||||
|
|
||||||
|
Examples of appropriate seams include clocks, randomness, subprocesses, remote APIs, object storage, email, and paid LLM calls.
|
||||||
|
|
||||||
|
## Test execution requirements
|
||||||
|
|
||||||
|
Tests in the default suite must be deterministic, offline, and independent of real credentials. They must not invoke paid APIs or depend on mutable external services. Tests that require live infrastructure must be explicitly opt-in and clearly separated from the default suite.
|
||||||
|
|
||||||
|
Control clocks, randomness, environment variables, and other process-global or machine-specific state when they affect behavior. Tests should be safe to run repeatedly and alongside other tests without depending on execution order or state left by an earlier test.
|
||||||
|
|
||||||
|
## What deserves tests
|
||||||
|
|
||||||
|
Prioritize tests for:
|
||||||
|
|
||||||
|
1. Public and package-level contracts.
|
||||||
|
2. Domain rules and important invariants.
|
||||||
|
3. Boundary conditions and malformed input.
|
||||||
|
4. Failure handling, cancellation, retries, recovery, and partial success.
|
||||||
|
5. Serialization, schemas, compatibility, and round trips.
|
||||||
|
6. Previously observed or plausible regressions.
|
||||||
|
7. Representative integration and end-to-end workflows.
|
||||||
|
|
||||||
|
A package-level contract is behavior relied upon by another package or major collaborator, not every observable detail of a package implementation.
|
||||||
|
|
||||||
|
For behavior involving **data integrity, destructive operations, compatibility, security, concurrency, idempotency, or recovery**, presume that durable tests are required unless the behavior is already credibly protected at another layer.
|
||||||
|
|
||||||
|
Do not add tests merely because a function, branch, or line exists. Do not add a test when the same meaningful risk is already adequately protected elsewhere.
|
||||||
|
|
||||||
|
## Choose the right test boundary
|
||||||
|
|
||||||
|
Test through the narrowest stable boundary that expresses the behavior clearly.
|
||||||
|
|
||||||
|
This is often the package API, but it may instead be:
|
||||||
|
|
||||||
|
- a smaller pure function when dense domain logic is most clearly isolated there;
|
||||||
|
- a package-level operation when several internal collaborators jointly produce the behavior; or
|
||||||
|
- a larger integration boundary when correctness emerges from interaction with a real dependency.
|
||||||
|
|
||||||
|
Do not force all behavior through oversized end-to-end tests. Do not test every private helper merely because it exists. Choose the boundary that gives durable confidence with the least incidental coupling.
|
||||||
|
|
||||||
|
## Test behavior, not implementation
|
||||||
|
|
||||||
|
A test should protect a decision, contract, or invariant—not memorialize the current implementation.
|
||||||
|
|
||||||
|
Before adding or retaining a test, ask:
|
||||||
|
|
||||||
|
> What realistic defect would this test catch?
|
||||||
|
|
||||||
|
A test is suspect when its main purpose is to detect that someone:
|
||||||
|
|
||||||
|
- changed an internal constant;
|
||||||
|
- renamed or split a private helper;
|
||||||
|
- reordered equivalent internal operations;
|
||||||
|
- changed incidental formatting;
|
||||||
|
- replaced one correct algorithm with another; or
|
||||||
|
- refactored internal object structure without changing behavior.
|
||||||
|
|
||||||
|
Refactoring should normally require no test edits unless the refactored structure is itself part of the contract.
|
||||||
|
|
||||||
|
A test can be factually correct and still have negative value. Accurately describing current behavior is not enough; the protected behavior must be important enough to justify the future friction.
|
||||||
|
|
||||||
|
## Expected effects of different changes
|
||||||
|
|
||||||
|
Use the following expectations when evaluating test failures and test maintenance:
|
||||||
|
|
||||||
|
| Change | Expected effect on tests |
|
||||||
|
|---|---|
|
||||||
|
| Internal refactor that preserves behavior | Existing tests should normally remain unchanged and continue to pass. |
|
||||||
|
| Change to an internal default with no contractual significance | Behavioral tests should normally remain unchanged; tests should derive expectations from configuration or relationships rather than duplicate the old value. |
|
||||||
|
| Intentional change to public behavior, policy, schema, or compatibility guarantees | The relevant tests should be reviewed and changed deliberately. |
|
||||||
|
| Accidental violation of a contract or invariant | Tests should fail; fix the production code rather than rewriting the tests to accept the defect. |
|
||||||
|
|
||||||
|
A test failing is not the same as a test needing to be edited. Many tests may correctly fail because of one production defect. The maintenance smell is a correct internal change that requires unrelated expectation updates throughout the suite.
|
||||||
|
|
||||||
|
## Separate mechanism from policy
|
||||||
|
|
||||||
|
Configurable thresholds and defaults must not be duplicated throughout the test suite.
|
||||||
|
|
||||||
|
For example, do not encode an internal concurrency limit indirectly:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Production policy:
|
||||||
|
const maxConcurrency = 4
|
||||||
|
|
||||||
|
// Brittle test:
|
||||||
|
err := startProcesses(5)
|
||||||
|
require.Error(t, err)
|
||||||
|
```
|
||||||
|
|
||||||
|
Instead, test the mechanism relationally:
|
||||||
|
|
||||||
|
```go
|
||||||
|
const limit = 2
|
||||||
|
runner := NewRunner(limit)
|
||||||
|
|
||||||
|
require.NoError(t, runner.Start(limit))
|
||||||
|
require.ErrorIs(t, runner.Start(limit+1), ErrTooMuchConcurrency)
|
||||||
|
```
|
||||||
|
|
||||||
|
The test should prove:
|
||||||
|
|
||||||
|
- the configured limit is accepted; and
|
||||||
|
- one beyond the configured limit is rejected.
|
||||||
|
|
||||||
|
The production default should be tested exactly only when its literal value is itself a public, operational, safety, protocol, or compatibility requirement.
|
||||||
|
|
||||||
|
Apply the same rule to limits, timeouts, capacities, retry counts, and ranges: test relationships and behavior, not duplicated literals.
|
||||||
|
|
||||||
|
For concurrency limits, test both kinds of behavior when relevant:
|
||||||
|
|
||||||
|
1. **Configuration enforcement:** invalid or excessive requested values are handled correctly.
|
||||||
|
2. **Runtime enforcement:** observed peak concurrency never exceeds the configured limit.
|
||||||
|
|
||||||
|
Use a test-controlled limit and measure the behavior relative to that limit. Do not merely assert today's default value.
|
||||||
|
|
||||||
|
## Avoid semantic duplication across layers
|
||||||
|
|
||||||
|
Each behavior should have a clear test owner.
|
||||||
|
|
||||||
|
- Parser tests own parsing cases.
|
||||||
|
- Validator tests own validation rules.
|
||||||
|
- Domain tests own transformations and invariants.
|
||||||
|
- Adapter tests own external integration behavior.
|
||||||
|
- Orchestrator tests own coordination and failure propagation.
|
||||||
|
- CLI tests own argument and configuration mapping.
|
||||||
|
- End-to-end tests prove that representative assembled workflows work.
|
||||||
|
|
||||||
|
Higher-level tests should not repeat every lower-level case. A single intentional policy change should not require unrelated edits across many test files.
|
||||||
|
|
||||||
|
Tests that are individually reasonable may still be collectively redundant. Evaluate the marginal value of each additional test in light of the protection already provided by the rest of the suite.
|
||||||
|
|
||||||
|
## Use test doubles deliberately
|
||||||
|
|
||||||
|
Choose the least elaborate test double that provides the required control or observation.
|
||||||
|
|
||||||
|
As a default:
|
||||||
|
|
||||||
|
1. Prefer real collaborators when they are fast and deterministic.
|
||||||
|
2. Use small in-memory fakes when realistic stateful behavior is helpful.
|
||||||
|
3. Use stubs when a dependency only needs to provide controlled responses.
|
||||||
|
4. Use mocks when the interaction itself is contractual.
|
||||||
|
|
||||||
|
Mocks are appropriate when the contract includes facts such as:
|
||||||
|
|
||||||
|
- a notification is sent exactly once;
|
||||||
|
- a transaction is committed only after successful writes;
|
||||||
|
- cancellation reaches a subprocess;
|
||||||
|
- an expensive API is called no more than once; or
|
||||||
|
- a security audit event is emitted.
|
||||||
|
|
||||||
|
Do not use mocks merely to isolate every object or reproduce the implementation's call graph.
|
||||||
|
|
||||||
|
## Go-specific guidance
|
||||||
|
|
||||||
|
Use:
|
||||||
|
|
||||||
|
- table-driven tests for meaningful behavioral categories and boundaries;
|
||||||
|
- `t.TempDir()` for real filesystem behavior;
|
||||||
|
- `httptest.Server` for realistic HTTP interactions;
|
||||||
|
- fuzz tests for parsers, normalization, path handling, and broad input spaces;
|
||||||
|
- golden files only when the complete output is intentionally stable;
|
||||||
|
- integration tests where correctness depends on component interaction; and
|
||||||
|
- a small number of representative end-to-end tests.
|
||||||
|
|
||||||
|
Avoid exact error-string assertions unless the wording is itself contractual. Prefer `errors.Is`, `errors.As`, typed errors, or structured error fields.
|
||||||
|
|
||||||
|
At CLI boundaries, prefer exit classifications, structured output, and the smallest stable semantic fragment needed to identify the error. Do not snapshot complete diagnostic wording unless it is contractual.
|
||||||
|
|
||||||
|
Golden-file updates must require an explicit local flag. CI must not update golden files automatically, and reviewers must inspect the semantic diff before accepting an update.
|
||||||
|
|
||||||
|
Keep tests readable and direct. Test helpers and fixture frameworks must earn their own maintenance cost; do not build elaborate test infrastructure for small or isolated needs.
|
||||||
|
|
||||||
|
## Coverage
|
||||||
|
|
||||||
|
Coverage is a diagnostic, not a target.
|
||||||
|
|
||||||
|
Use it to find untested critical branches and unexpectedly weak packages. Do not write low-value tests solely to increase a percentage, and do not infer test quality from coverage alone.
|
||||||
|
|
||||||
|
Pure domain logic will often warrant higher coverage than CLI wiring or external adapters. Uneven coverage is acceptable when it reflects risk.
|
||||||
|
|
||||||
|
Increasing coverage is valuable only when the newly covered behavior protects a meaningful risk at an acceptable cost.
|
||||||
|
|
||||||
|
## Regression tests
|
||||||
|
|
||||||
|
A bug fix should normally include a regression test that fails before the fix and passes afterward.
|
||||||
|
|
||||||
|
Retain the test when the defect could realistically recur and its consequences justify the ongoing cost. Prefer the narrowest durable test of the violated contract or invariant; do not preserve accidental implementation details from the original bug.
|
||||||
|
|
||||||
|
Not every historical bug requires a permanent test. If the underlying design has made recurrence impossible, the test has become redundant, or a stronger invariant test now subsumes it, remove or consolidate it.
|
||||||
|
|
||||||
|
## Deleting or rewriting tests
|
||||||
|
|
||||||
|
Tests are maintained code, not permanent historical artifacts.
|
||||||
|
|
||||||
|
Delete or rewrite a test when its maintenance cost exceeds the confidence it provides.
|
||||||
|
|
||||||
|
Strong candidates include tests that:
|
||||||
|
|
||||||
|
- require updates after harmless internal changes;
|
||||||
|
- directly assert private constants without protecting a real contract;
|
||||||
|
- duplicate the same policy across several layers;
|
||||||
|
- verify mock choreography rather than outcomes;
|
||||||
|
- snapshot large amounts of incidental output;
|
||||||
|
- test trivial private helpers already exercised through stable package behavior;
|
||||||
|
- protect risks already covered more effectively elsewhere;
|
||||||
|
- are flaky, misleading, obsolete, or disproportionately expensive to diagnose; or
|
||||||
|
- no longer correspond to a plausible failure mode.
|
||||||
|
|
||||||
|
Several brittle tests may encode one genuine requirement. Replace them with one durable behavior-level or invariant test rather than preserving all of them.
|
||||||
|
|
||||||
|
Deleting a low-value test can improve the quality of the suite by reducing noise, maintenance burden, and friction around legitimate change.
|
||||||
|
|
||||||
|
## Reviewing a proposed test
|
||||||
|
|
||||||
|
Use the following questions when the value, boundary, or durability of a proposed test is not self-evident. Significant test additions should be reviewable against them, but written answers are not required for every routine test.
|
||||||
|
|
||||||
|
1. What realistic defect would it catch?
|
||||||
|
2. How likely is that defect?
|
||||||
|
3. How consequential would it be?
|
||||||
|
4. Is the behavior already protected elsewhere?
|
||||||
|
5. At which layer should this behavior be owned?
|
||||||
|
6. Does the test assert a durable contract or an incidental implementation detail?
|
||||||
|
7. Could the implementation be refactored without changing the behavior and without editing this test?
|
||||||
|
8. What should cause this test to fail?
|
||||||
|
9. What legitimate changes should not cause this test to fail?
|
||||||
|
10. What ongoing maintenance, execution, and diagnostic cost will the test impose?
|
||||||
|
11. Is there a smaller or more direct test that protects the same risk?
|
||||||
|
|
||||||
|
Do not add the test when its expected lifetime cost exceeds its expected protective value.
|
||||||
|
|
||||||
|
When deciding not to test plausible behavior, record or be able to explain why the risk is low, already protected, obvious, reversible, or cheaper to detect elsewhere.
|
||||||
|
|
||||||
|
## Definition of sufficient
|
||||||
|
|
||||||
|
A test suite is sufficient when:
|
||||||
|
|
||||||
|
- important contracts and invariants are protected;
|
||||||
|
- meaningful boundaries and failure modes are exercised;
|
||||||
|
- realistic and consequential regressions are credibly protected against silent recurrence;
|
||||||
|
- behavior involving data integrity, destructive operations, compatibility, security, concurrency, idempotency, and recovery is credibly protected;
|
||||||
|
- important external boundaries have realistic integration coverage;
|
||||||
|
- representative complete workflows are tested;
|
||||||
|
- failures provide useful signal rather than redundant noise;
|
||||||
|
- legitimate internal changes usually do not require test edits; and
|
||||||
|
- additional tests would mostly repeat existing protection or preserve inconsequential implementation details.
|
||||||
|
|
||||||
|
Sufficiency is a risk judgment, not a coverage percentage or test count. Reassess it as the application, its users, and the consequences of failure evolve.
|
||||||
|
|
||||||
|
The governing rule is:
|
||||||
|
|
||||||
|
> Test heavily where failure is consequential, subtle, or difficult to detect after the fact. Test lightly where failure is obvious, reversible, and inexpensive—and retain no test whose lifetime cost exceeds the confidence it provides.
|
||||||
141
docs/roadmap/documentation.md
Normal file
141
docs/roadmap/documentation.md
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
# Documentation Compliance Roadmap
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
The initial documentation refresh and its follow-up compliance work were
|
||||||
|
completed on 2026-07-26. The documentation prerequisite for Step 2 of the
|
||||||
|
[Promptkit migration roadmap](migration.md) is satisfied.
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Maintain an accurate, concise documentation baseline under the canonical
|
||||||
|
ownership policy, and close the remaining review findings before architectural
|
||||||
|
migration begins.
|
||||||
|
|
||||||
|
## Completed Refresh
|
||||||
|
|
||||||
|
The initial refresh:
|
||||||
|
|
||||||
|
- established the documentation and ADR policies and recorded
|
||||||
|
[ADR 0001](../adr/0001-adopt-canonical-documentation-ownership.md);
|
||||||
|
- made `docs/development.md` the contributor entry point and added a canonical
|
||||||
|
internal component overview;
|
||||||
|
- refocused architecture on normative boundaries and moved implementation
|
||||||
|
detail into focused internal documents;
|
||||||
|
- consolidated CLI, configuration, HTTP, public Go, and integration contracts
|
||||||
|
under distinct canonical owners;
|
||||||
|
- rewrote operations as a runbook, incorporated useful recovery guidance, and
|
||||||
|
removed the standalone troubleshooting document;
|
||||||
|
- normalized maintained examples and added an offline HTTP request-example
|
||||||
|
check;
|
||||||
|
- corrected the factual discrepancies found by the original audit, including
|
||||||
|
the entry-path count, built-in profile catalog, prompt output optionality,
|
||||||
|
input-map behavior, validation diagnostics, session-ID units, and CLI flag
|
||||||
|
descriptions; and
|
||||||
|
- validated local links, configuration examples, smoke examples, tests, vet,
|
||||||
|
and the documented build.
|
||||||
|
|
||||||
|
## Follow-Up Findings
|
||||||
|
|
||||||
|
The post-completion review found:
|
||||||
|
|
||||||
|
1. The effective precedence between `Config.Timeout` and a supplied
|
||||||
|
`HTTPClient.Timeout` is not consistently expressed by the implementation,
|
||||||
|
tests, and documentation.
|
||||||
|
2. The public Go contract overgeneralizes basename selection for single-file
|
||||||
|
sources; prompt and profile files remain selected by YAML ID, while
|
||||||
|
`WithSchemaFile` uses the schema file's base name.
|
||||||
|
3. The maintained HTTP request-example test pins incidental fixture values
|
||||||
|
beyond the structural contract it needs to protect.
|
||||||
|
4. The Promptkit migration roadmap's overall status says no work is implemented
|
||||||
|
even though its documentation-refresh step is complete.
|
||||||
|
5. One architecture invariant incorrectly describes configuration, prompt, and
|
||||||
|
profile inputs as JSON rather than YAML.
|
||||||
|
|
||||||
|
## Follow-Up Implementation Sequence
|
||||||
|
|
||||||
|
### Stage 1: Resolve The Timeout Contract
|
||||||
|
|
||||||
|
1. Decide and document the precedence among:
|
||||||
|
- `Config.Timeout`;
|
||||||
|
- a non-zero timeout on a supplied `http.Client`; and
|
||||||
|
- an explicit per-request `timeout_seconds` override.
|
||||||
|
2. Align `internal/llm` with that decision, removing any constructor state that
|
||||||
|
is immediately discarded during generation.
|
||||||
|
3. Add or revise a behavior-level test that performs `Generate` with a supplied
|
||||||
|
HTTP client and proves the selected timeout without relying only on private
|
||||||
|
constructor state.
|
||||||
|
4. Update the canonical public Go contract, outbound OpenAI-compatible
|
||||||
|
integration contract, and internal LLM document together.
|
||||||
|
|
||||||
|
**Gate:** There is one documented timeout-precedence rule, the implementation
|
||||||
|
follows it, and a request-level test protects it.
|
||||||
|
|
||||||
|
### Stage 2: Correct The Remaining Contract And Architecture Text
|
||||||
|
|
||||||
|
1. Clarify in the public Go contract that:
|
||||||
|
- prompt and profile single-file sources are selected by their YAML IDs;
|
||||||
|
- `WithPromptFile` resolves relative `content_file` paths from the prompt
|
||||||
|
file's directory; and
|
||||||
|
- `WithSchemaFile` exposes the schema through its base name.
|
||||||
|
2. Correct or remove the duplicate architecture invariant so it accurately
|
||||||
|
distinguishes strict YAML configuration, prompt, and profile inputs from
|
||||||
|
strict JSON HTTP input.
|
||||||
|
3. Recheck the affected statements against the public facade and source
|
||||||
|
implementations without duplicating file-format definitions outside the
|
||||||
|
configuration contract.
|
||||||
|
|
||||||
|
**Gate:** Public source-option behavior and strict-decoding invariants agree
|
||||||
|
with the implementation and retain one canonical owner.
|
||||||
|
|
||||||
|
### Stage 3: Refine The Maintained HTTP Example Check
|
||||||
|
|
||||||
|
1. Preserve an offline check that `examples/http-run.json` is accepted by the
|
||||||
|
HTTP request contract.
|
||||||
|
2. Remove assertions that freeze prompt IDs, profile IDs, fixture paths, or
|
||||||
|
example variable values unless a specific value protects an independently
|
||||||
|
identified contract.
|
||||||
|
3. Rely on focused handler tests, rather than the maintained-example check, for
|
||||||
|
exhaustive DTO-to-domain mapping behavior.
|
||||||
|
4. Run the affected HTTP adapter tests and confirm that the revised test still
|
||||||
|
fails for structurally invalid example content.
|
||||||
|
|
||||||
|
**Gate:** The maintained example has durable structural protection without
|
||||||
|
duplicating mapping coverage or making legitimate fixture edits unnecessarily
|
||||||
|
expensive.
|
||||||
|
|
||||||
|
### Stage 4: Reconcile Status And Revalidate The Baseline
|
||||||
|
|
||||||
|
1. Update the Promptkit migration roadmap's overall status to distinguish the
|
||||||
|
completed documentation step from the unimplemented migration steps.
|
||||||
|
2. Recheck local Markdown links and paths.
|
||||||
|
3. Verify both maintained configuration files through the real configuration
|
||||||
|
loader.
|
||||||
|
4. Run the maintained render script and Go package example.
|
||||||
|
5. Run `go test ./...`, `go vet ./...`, and `go build ./cmd/scriptorium`.
|
||||||
|
6. Record completion of these follow-up stages here and confirm the
|
||||||
|
documentation gate in the Promptkit migration roadmap.
|
||||||
|
|
||||||
|
**Gate:** Code, tests, examples, canonical contracts, architecture, and both
|
||||||
|
roadmap status documents agree. Promptkit migration Step 2 may then begin.
|
||||||
|
|
||||||
|
## Completion Criteria
|
||||||
|
|
||||||
|
This follow-up is complete when:
|
||||||
|
|
||||||
|
- timeout precedence is unambiguous, implemented, and protected by an
|
||||||
|
execution-level test;
|
||||||
|
- public single-file source behavior is accurately documented;
|
||||||
|
- strict YAML and JSON boundaries are accurately stated;
|
||||||
|
- the HTTP example check protects structure without pinning incidental content;
|
||||||
|
- roadmap status accurately distinguishes completed and pending migration work;
|
||||||
|
and
|
||||||
|
- links, examples, tests, vet, and build validation pass.
|
||||||
|
|
||||||
|
## Completion Record
|
||||||
|
|
||||||
|
Completed on 2026-07-26. The timeout contract, single-file source contract,
|
||||||
|
strict-decoding boundary, and maintained HTTP example check were reconciled.
|
||||||
|
Final validation confirmed local links and paths, both maintained configuration
|
||||||
|
files through the real loader, the maintained render and Go package examples,
|
||||||
|
`go test ./...`, `go vet ./...`, and `go build ./cmd/scriptorium`.
|
||||||
296
docs/roadmap/migration.md
Normal file
296
docs/roadmap/migration.md
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
# Promptkit Migration Roadmap
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted plan. Step 1, the documentation refresh, is complete. Steps 2 through
|
||||||
|
9 remain proposed and are not yet implemented.
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Split the current repository into two projects:
|
||||||
|
|
||||||
|
- **Promptkit**: the reusable Go framework, public Go facade, execution engine,
|
||||||
|
source and validation support, OpenAI-compatible client, extension
|
||||||
|
interfaces, and built-in execution-profile registry.
|
||||||
|
- **Scriptorium**: a slim runnable application that imports Promptkit and
|
||||||
|
provides the CLI and HTTP interfaces.
|
||||||
|
|
||||||
|
Scriptorium will become another downstream Promptkit consumer rather than the
|
||||||
|
owner of the framework.
|
||||||
|
|
||||||
|
## Compatibility And Migration Policy
|
||||||
|
|
||||||
|
This is an intentionally breaking change.
|
||||||
|
|
||||||
|
- New and migrated Go consumers must import Promptkit instead of Scriptorium.
|
||||||
|
- Scriptorium will not retain type aliases, forwarding packages, deprecated
|
||||||
|
facade APIs, or other source-compatibility shims.
|
||||||
|
- Existing consumers may continue using a previously tagged Scriptorium module
|
||||||
|
version until they are migrated.
|
||||||
|
- The migration does not need to preserve compatibility between intermediate
|
||||||
|
development states. Each completed phase must instead leave the affected
|
||||||
|
repository internally consistent and tested.
|
||||||
|
- Promptkit should initially preserve the useful shape and behavior of the
|
||||||
|
current public Go facade where doing so reduces extraction risk. Broader API
|
||||||
|
redesign should follow the split unless required to establish the new
|
||||||
|
boundary.
|
||||||
|
|
||||||
|
## Target Ownership
|
||||||
|
|
||||||
|
Promptkit should own application-neutral framework behavior:
|
||||||
|
|
||||||
|
- public engine, request, result, option, extension, and error APIs;
|
||||||
|
- prompt-definition loading and rendering;
|
||||||
|
- execution profiles, overlays, and the built-in profile registry;
|
||||||
|
- artifact-loading interfaces and general-purpose `file` and `inline` support;
|
||||||
|
- schema loading and output validation;
|
||||||
|
- LLM client boundaries and the OpenAI-compatible implementation;
|
||||||
|
- preparation and execution orchestration;
|
||||||
|
- framework and execution defaults.
|
||||||
|
|
||||||
|
Scriptorium should own executable and transport concerns:
|
||||||
|
|
||||||
|
- the `scriptorium` command and its `run`, `render`, and `serve` interfaces;
|
||||||
|
- CLI parsing, output formatting, exit codes, and process behavior;
|
||||||
|
- application-config discovery and CLI precedence;
|
||||||
|
- HTTP routing, request and response DTOs, limits, and error/status mapping;
|
||||||
|
- HTTP artifact-root and deployment security policy;
|
||||||
|
- server and adapter defaults;
|
||||||
|
- executable examples, operations guidance, and transport documentation.
|
||||||
|
|
||||||
|
The intended dependency direction is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Scriptorium CLI and HTTP adapters
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Promptkit
|
||||||
|
|
|
||||||
|
v
|
||||||
|
consumer-supplied sources and clients
|
||||||
|
```
|
||||||
|
|
||||||
|
Scriptorium must use Promptkit's public API. It must not depend on Promptkit
|
||||||
|
implementation packages or reproduce framework orchestration.
|
||||||
|
|
||||||
|
## Migration Steps
|
||||||
|
|
||||||
|
### Step 1: Refresh And Synchronize Documentation
|
||||||
|
|
||||||
|
Perform a repository-wide documentation refresh before migration development.
|
||||||
|
|
||||||
|
At minimum:
|
||||||
|
|
||||||
|
- reconcile all current-behavior documentation with the code, tests, examples,
|
||||||
|
defaults, and current public contracts;
|
||||||
|
- introduce the planned documentation-policy updates;
|
||||||
|
- establish an architecture decision record policy and canonical ADR location;
|
||||||
|
- resolve stale, duplicated, or misplaced material;
|
||||||
|
- validate documentation links and maintained examples;
|
||||||
|
- leave future migration behavior in `docs/roadmap/` until implemented.
|
||||||
|
|
||||||
|
**Gate:** Do not begin architectural migration work until the documentation
|
||||||
|
refresh and policy updates are merged and the repository has an agreed,
|
||||||
|
accurate baseline.
|
||||||
|
|
||||||
|
**Gate status:** Complete as of 2026-07-26. The completed documentation
|
||||||
|
refresh and follow-up verification record are in the [documentation compliance
|
||||||
|
roadmap](documentation.md).
|
||||||
|
|
||||||
|
### Step 2: Record The Architectural Decision And Detailed Boundary
|
||||||
|
|
||||||
|
Create an ADR, under the policy established in Step 1, that records:
|
||||||
|
|
||||||
|
- the decision to split Promptkit from Scriptorium;
|
||||||
|
- the target ownership and dependency direction;
|
||||||
|
- the selected Promptkit repository and Go module paths;
|
||||||
|
- the breaking-change and versioning policy;
|
||||||
|
- ownership of configuration fields and defaults;
|
||||||
|
- artifact-reader and HTTP containment responsibilities;
|
||||||
|
- local multi-repository development and release coordination;
|
||||||
|
- documentation ownership after the split.
|
||||||
|
|
||||||
|
Use the ADR to resolve any remaining public-boundary decisions before code is
|
||||||
|
moved.
|
||||||
|
|
||||||
|
**Gate:** The ADR is accepted, and every existing package, public contract,
|
||||||
|
configuration category, and maintained asset has a target owner.
|
||||||
|
|
||||||
|
### Step 3: Characterize Existing Framework Behavior
|
||||||
|
|
||||||
|
Strengthen or add contract-focused tests where needed so extraction can be
|
||||||
|
verified without relying on package placement.
|
||||||
|
|
||||||
|
Preserve coverage of:
|
||||||
|
|
||||||
|
- `Prepare` and `Run` behavior;
|
||||||
|
- prompt, profile, execution-default, and request-override precedence;
|
||||||
|
- presence-aware numeric overrides;
|
||||||
|
- built-in profile fallback and custom-profile overlays;
|
||||||
|
- strict YAML and JSON decoding;
|
||||||
|
- prompt, profile, schema, and artifact source behavior;
|
||||||
|
- structured-output requests and output validation;
|
||||||
|
- validation failures versus validation runtime errors;
|
||||||
|
- secret handling and redaction;
|
||||||
|
- public error classification;
|
||||||
|
- HTTP artifact restrictions and transport mappings.
|
||||||
|
|
||||||
|
**Gate:** Current framework and adapter contracts are represented by passing
|
||||||
|
tests sufficient to detect behavioral regressions during the split.
|
||||||
|
|
||||||
|
### Step 4: Make Scriptorium Adapters Consume The Public Facade
|
||||||
|
|
||||||
|
Within the current repository, refactor the CLI and HTTP adapters to use the
|
||||||
|
public framework facade rather than constructing or importing internal runner
|
||||||
|
components directly.
|
||||||
|
|
||||||
|
Add only the minimum public capabilities needed to support this boundary. These
|
||||||
|
may include:
|
||||||
|
|
||||||
|
- a small `Run`/`Prepare` consumer interface;
|
||||||
|
- injectable artifact-reading behavior for Scriptorium's restricted HTTP
|
||||||
|
policy;
|
||||||
|
- source options currently available only through internal constructors;
|
||||||
|
- prepared-run formatting based on public types;
|
||||||
|
- stable public error classification required by CLI and HTTP mappings.
|
||||||
|
|
||||||
|
Do not broadly export internal repositories, domain types, or use-case
|
||||||
|
implementations.
|
||||||
|
|
||||||
|
**Gate:** The CLI and HTTP adapters use only the public framework API for
|
||||||
|
framework behavior, and all tests and documented smoke commands pass.
|
||||||
|
|
||||||
|
### Step 5: Create The Promptkit Repository
|
||||||
|
|
||||||
|
Create the Promptkit repository and Go module as an explicit out-of-band
|
||||||
|
operation.
|
||||||
|
|
||||||
|
Establish:
|
||||||
|
|
||||||
|
- repository access, ownership, and branch protections;
|
||||||
|
- the module path selected by the ADR;
|
||||||
|
- baseline development, architecture, documentation, and release policies;
|
||||||
|
- CI for build, test, vet, and other agreed checks;
|
||||||
|
- an initial package layout centered on a small public facade with internal
|
||||||
|
implementation packages;
|
||||||
|
- a local development workflow for coordinated Promptkit and Scriptorium
|
||||||
|
changes, using a workspace or temporary uncommitted module replacement where
|
||||||
|
appropriate.
|
||||||
|
|
||||||
|
Do not commit local filesystem `replace` directives to release branches.
|
||||||
|
|
||||||
|
**Gate:** The Promptkit repository exists, is accessible to maintainers, has
|
||||||
|
working CI and policy scaffolding, and can receive the extracted framework.
|
||||||
|
Do not begin cross-repository extraction until this out-of-band work is
|
||||||
|
confirmed complete.
|
||||||
|
|
||||||
|
### Step 6: Extract And Stabilize Promptkit
|
||||||
|
|
||||||
|
Move the application-neutral framework and built-in profile assets into
|
||||||
|
Promptkit. Preserve implementation packages as internal where practical.
|
||||||
|
|
||||||
|
The initial public API should remain focused on the established engine workflow
|
||||||
|
and the source and client extension points required by real consumers. Avoid
|
||||||
|
combining the extraction with unrelated API redesign.
|
||||||
|
|
||||||
|
Move or recreate the relevant:
|
||||||
|
|
||||||
|
- framework implementation;
|
||||||
|
- public package tests and framework contract tests;
|
||||||
|
- built-in profile assets and registry tests;
|
||||||
|
- Go consumer examples;
|
||||||
|
- framework, consumer, configuration-format, and integration documentation.
|
||||||
|
|
||||||
|
Verify that Promptkit can be built, tested, and consumed independently of the
|
||||||
|
Scriptorium repository.
|
||||||
|
|
||||||
|
**Gate:** Promptkit independently provides the agreed framework contract,
|
||||||
|
passes its CI checks, and has a tagged version that Scriptorium and other
|
||||||
|
consumers can import.
|
||||||
|
|
||||||
|
### Step 7: Slim Scriptorium And Adopt Promptkit
|
||||||
|
|
||||||
|
Update Scriptorium to import the tagged Promptkit module and remove the
|
||||||
|
framework implementation and public Go facade that Promptkit replaces.
|
||||||
|
|
||||||
|
Retain only Scriptorium-owned executable and transport behavior. In particular:
|
||||||
|
|
||||||
|
- wire CLI and HTTP requests through Promptkit's public API;
|
||||||
|
- keep application config and transport defaults in Scriptorium;
|
||||||
|
- keep restricted HTTP artifact policy in Scriptorium while injecting it
|
||||||
|
through Promptkit's supported boundary;
|
||||||
|
- remove obsolete framework packages, tests, and documentation;
|
||||||
|
- update Scriptorium examples and docs to describe the CLI and HTTP application;
|
||||||
|
- direct Go framework consumers to Promptkit without providing compatibility
|
||||||
|
aliases or forwarding APIs.
|
||||||
|
|
||||||
|
**Gate:** Scriptorium builds and passes all tests using a tagged Promptkit
|
||||||
|
dependency, contains no duplicate framework implementation, and its current
|
||||||
|
documentation describes only the slimmed application.
|
||||||
|
|
||||||
|
### Step 8: Migrate Downstream Consumers To Promptkit
|
||||||
|
|
||||||
|
Inventory downstream Go consumers and migrate each from the Scriptorium package
|
||||||
|
to Promptkit. This work may occur in external repositories and must be tracked
|
||||||
|
explicitly.
|
||||||
|
|
||||||
|
For each consumer:
|
||||||
|
|
||||||
|
- update module imports and dependencies;
|
||||||
|
- adapt to any intentionally changed public API;
|
||||||
|
- run its tests and relevant integration or smoke checks;
|
||||||
|
- confirm configuration, source, validation, and error behavior;
|
||||||
|
- release or deploy the migrated consumer through its normal process.
|
||||||
|
|
||||||
|
Consumers that cannot migrate immediately may remain pinned to the last
|
||||||
|
framework-bearing Scriptorium tag. No compatibility work is required in the new
|
||||||
|
Scriptorium project for those consumers.
|
||||||
|
|
||||||
|
**Gate:** All in-scope downstream consumers are either migrated and verified or
|
||||||
|
explicitly recorded as remaining on the previous Scriptorium version with an
|
||||||
|
owner and follow-up plan. Do not declare the ecosystem migration complete until
|
||||||
|
the required out-of-band consumer changes are confirmed.
|
||||||
|
|
||||||
|
### Step 9: Complete Release And Documentation Cutover
|
||||||
|
|
||||||
|
Complete the coordinated project transition:
|
||||||
|
|
||||||
|
- publish Promptkit before dependent Scriptorium releases;
|
||||||
|
- release the breaking Scriptorium version against the tagged Promptkit
|
||||||
|
dependency;
|
||||||
|
- publish migration guidance that maps the former Scriptorium Go API to
|
||||||
|
Promptkit;
|
||||||
|
- update cross-project links, examples, package documentation, and release
|
||||||
|
notes;
|
||||||
|
- verify that no release artifact depends on local workspaces or replacements;
|
||||||
|
- archive completed roadmap material according to the documentation policy in
|
||||||
|
effect at that time.
|
||||||
|
|
||||||
|
**Gate:** Promptkit and Scriptorium are independently releasable, their
|
||||||
|
documentation has distinct and accurate ownership, and the migration status of
|
||||||
|
all identified downstream consumers is recorded.
|
||||||
|
|
||||||
|
## Cross-Cutting Constraints
|
||||||
|
|
||||||
|
- Preserve the invariant that execution orchestration remains narrow and
|
||||||
|
application-neutral.
|
||||||
|
- Keep adapter-specific decisions out of Promptkit.
|
||||||
|
- Keep Scriptorium dependent only on Promptkit's supported public API.
|
||||||
|
- Preserve strict external decoding, error classification, validation
|
||||||
|
semantics, and secret redaction throughout the migration.
|
||||||
|
- Keep each repository buildable and testable at merged phase boundaries.
|
||||||
|
- Coordinate cross-repository changes through tagged dependencies and explicit
|
||||||
|
gates rather than assuming atomic commits across repositories.
|
||||||
|
- Document only implemented behavior outside roadmap files.
|
||||||
|
|
||||||
|
## Completion Criteria
|
||||||
|
|
||||||
|
The migration is complete when:
|
||||||
|
|
||||||
|
- Promptkit is the independent owner of the reusable framework and built-in
|
||||||
|
profiles;
|
||||||
|
- Scriptorium is a slim CLI and HTTP consumer of Promptkit;
|
||||||
|
- Scriptorium no longer exposes or maintains the former public Go framework;
|
||||||
|
- all required downstream migrations and external repository work have been
|
||||||
|
completed or explicitly dispositioned;
|
||||||
|
- both repositories build, test, document, version, and release independently.
|
||||||
@@ -1,361 +0,0 @@
|
|||||||
# Troubleshooting
|
|
||||||
|
|
||||||
This guide lists common implemented failure modes and safe fixes.
|
|
||||||
|
|
||||||
Canonical references:
|
|
||||||
|
|
||||||
- [CLI reference](cli.md)
|
|
||||||
- [Configuration reference](config.md)
|
|
||||||
- [HTTP API reference](api.md)
|
|
||||||
- [Operations guide](operations.md)
|
|
||||||
|
|
||||||
## Missing Or Invalid Config
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- CLI error includes `application config error`, `config file not found`, `invalid config YAML`, or `invalid config`.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- `--config` points to a missing file.
|
|
||||||
- YAML syntax is invalid.
|
|
||||||
- Config contains unknown fields or negative HTTP size limits.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go run ./cmd/scriptorium render --config /path/to/config.yml --prompt generic.markdown_summary --input transcript=./examples/fixtures/transcript.md --input glossary=./examples/fixtures/glossary.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Correct the config path.
|
|
||||||
- Fix YAML syntax.
|
|
||||||
- Remove unknown fields.
|
|
||||||
- Keep raw secrets out of config.
|
|
||||||
|
|
||||||
Relevant links: [Configuration reference](config.md), [CLI reference](cli.md)
|
|
||||||
|
|
||||||
## Missing Prompt Directory
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- CLI parse error says the prompt directory is required.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- Neither config nor CLI flags provide an effective `prompt_dir`.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
- Re-run once with explicit `--prompt-dir`.
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Set `prompt_dir` in config or pass `--prompt-dir`.
|
|
||||||
|
|
||||||
Relevant links: [Configuration reference](config.md), [CLI reference](cli.md)
|
|
||||||
|
|
||||||
## Unknown Flags
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- CLI parse error for an unknown flag.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- Typo.
|
|
||||||
- Flag is valid for another command.
|
|
||||||
- `serve` was given runtime model override flags.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
- Compare the command with the command-specific flag list.
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Remove unsupported flags.
|
|
||||||
- Use `run` or `render` for runtime model overrides.
|
|
||||||
|
|
||||||
Relevant links: [CLI reference](cli.md)
|
|
||||||
|
|
||||||
## Prompt Load Failures
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- CLI run/render fails during prompt loading.
|
|
||||||
- HTTP returns `404 prompt_not_found` or `400 prompt_load_failed`.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- Prompt ID/version does not exist.
|
|
||||||
- Prompt YAML is invalid or has unknown fields.
|
|
||||||
- Prompt contract is invalid, such as missing messages, invalid output mode, bad `content_file`, or missing `schema_path` for `json_schema`.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go run ./cmd/scriptorium render --config ./examples/config.yml --prompt <prompt-id> --input transcript=./examples/fixtures/transcript.md --input glossary=./examples/fixtures/glossary.yml --format json
|
|
||||||
```
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Correct prompt ID/version.
|
|
||||||
- Fix prompt YAML and referenced `content_file` paths.
|
|
||||||
- Fix output contract fields.
|
|
||||||
|
|
||||||
Relevant links: [Configuration reference](config.md), [CLI reference](cli.md)
|
|
||||||
|
|
||||||
## Profile Load Failures
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- CLI run/render fails during profile loading.
|
|
||||||
- HTTP returns `404 profile_not_found`, `400 profile_load_failed`, or `400 profile_required`.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- Profile ID does not exist.
|
|
||||||
- Request omitted profile and prompt has no `default_profile`.
|
|
||||||
- Profile YAML is invalid or has unknown fields.
|
|
||||||
- Profile contains raw `api_key`.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go run ./cmd/scriptorium render --config ./examples/config.yml --prompt generic.markdown_summary --profile <profile-id> --input transcript=./examples/fixtures/transcript.md --input glossary=./examples/fixtures/glossary.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Correct profile ID or prompt `default_profile`.
|
|
||||||
- Fix profile YAML and value ranges.
|
|
||||||
- Replace raw `api_key` with `api_key_env`.
|
|
||||||
|
|
||||||
Relevant links: [Configuration reference](config.md), [CLI reference](cli.md)
|
|
||||||
|
|
||||||
## Input Artifact Failures
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- CLI run/render fails while reading inputs.
|
|
||||||
- HTTP returns `400 artifact_read_failed`, `400 artifact_not_allowed`, or `413 artifact_too_large`.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- Input file path is missing or unreadable.
|
|
||||||
- HTTP input type is unsupported or missing required fields.
|
|
||||||
- HTTP file refs are disabled because no artifact root is configured.
|
|
||||||
- HTTP file path is lexically outside the artifact root.
|
|
||||||
- HTTP file input exceeds `server.max_artifact_bytes`.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
- Verify each input path exists and is readable by the process.
|
|
||||||
- For HTTP, verify input refs use `file` or `inline`.
|
|
||||||
- For HTTP file refs, verify the artifact root and compare file size to `server.max_artifact_bytes`.
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Correct paths and permissions.
|
|
||||||
- Configure a narrow artifact root for HTTP file refs.
|
|
||||||
- Use relative paths under the artifact root or switch to `inline`.
|
|
||||||
- Increase `server.max_artifact_bytes` only for expected larger inputs.
|
|
||||||
|
|
||||||
Relevant links: [HTTP API reference](api.md), [Configuration reference](config.md)
|
|
||||||
|
|
||||||
## Missing API-Key Environment Variable
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- CLI render/run fails with an API-key environment error.
|
|
||||||
- HTTP returns `400 api_key_env_missing`.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- Selected profile or runtime override sets `api_key_env`, but the environment variable is unset or empty.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
printenv SCRIPTORIUM_API_KEY
|
|
||||||
```
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Set the required environment variable before starting the CLI command or HTTP service.
|
|
||||||
- Or use a profile that does not require provider API-key auth.
|
|
||||||
|
|
||||||
Relevant links: [Configuration reference](config.md), [Operations guide](operations.md)
|
|
||||||
|
|
||||||
## Prompt Template Render Failures
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- CLI render/run fails during prompt rendering.
|
|
||||||
- HTTP returns `400 prompt_render_failed`.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- Template references an input that was not supplied.
|
|
||||||
- Template syntax or variable reference is invalid.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
- Run `render --format json` with the same prompt, inputs, vars, and profile.
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Align `{{input "name"}}` references with request input names.
|
|
||||||
- Fix template syntax and variable names.
|
|
||||||
|
|
||||||
Relevant links: [Configuration reference](config.md), [CLI reference](cli.md)
|
|
||||||
|
|
||||||
## LLM Request Failures
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- CLI `run` fails during generation.
|
|
||||||
- HTTP returns `502 llm_failed`.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- Endpoint is unreachable.
|
|
||||||
- Provider returns non-2xx.
|
|
||||||
- Request times out.
|
|
||||||
- Provider response is malformed.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
- Run `render` first to confirm pre-LLM preparation works.
|
|
||||||
- Check selected endpoint/model in prepared output.
|
|
||||||
- Check network/provider logs for timeout or non-2xx details.
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Correct endpoint/model/profile settings.
|
|
||||||
- Adjust timeout when appropriate.
|
|
||||||
- Resolve provider or network issue.
|
|
||||||
|
|
||||||
Relevant links: [Operations guide](operations.md), [Configuration reference](config.md)
|
|
||||||
|
|
||||||
## Validation Failed
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- CLI `run` exits `2`.
|
|
||||||
- HTTP returns `200 OK` with `validation.status` set to `failed`.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- Generated output failed `basic`, `json`, or `json_schema` content validation.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
- Inspect validation errors in CLI stderr or the HTTP response.
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Refine prompt instructions.
|
|
||||||
- Adjust schema or model/profile settings.
|
|
||||||
- Rerun after correction.
|
|
||||||
|
|
||||||
Relevant links: [Operations guide](operations.md), [HTTP API reference](api.md)
|
|
||||||
|
|
||||||
## Validation Runtime Failure
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- CLI `run` fails with validation runtime error.
|
|
||||||
- HTTP returns `500 validation_runtime_failed`.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- `json_schema` schema file is missing or unreadable.
|
|
||||||
- Schema JSON is invalid.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
- Verify `schema_dir` and prompt `output.schema_path`.
|
|
||||||
- Check schema file readability and JSON syntax.
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Correct schema path or permissions.
|
|
||||||
- Fix schema JSON.
|
|
||||||
- Rerun.
|
|
||||||
|
|
||||||
Relevant links: [Configuration reference](config.md), [Operations guide](operations.md)
|
|
||||||
|
|
||||||
## HTTP JSON Or Request Contract Errors
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- HTTP returns `400 invalid_json` or `400 invalid_request`.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- JSON body is malformed.
|
|
||||||
- Request has unknown fields or trailing JSON tokens.
|
|
||||||
- Required `prompt_id` or `inputs` is missing.
|
|
||||||
- Runtime override values are out of range.
|
|
||||||
- `extra_params` collides with reserved outbound fields.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
- Revalidate request JSON and compare fields with the API reference.
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Send one JSON object with only supported fields.
|
|
||||||
- Include `prompt_id` and at least one input.
|
|
||||||
- Use valid model override ranges.
|
|
||||||
- Remove reserved `extra_params` keys.
|
|
||||||
|
|
||||||
Relevant links: [HTTP API reference](api.md)
|
|
||||||
|
|
||||||
## HTTP Size Limit Errors
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- HTTP returns `413 request_too_large`, `413 artifact_too_large`, or `413 response_too_large`.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- JSON request body exceeds `server.max_request_bytes`.
|
|
||||||
- HTTP file input exceeds `server.max_artifact_bytes`.
|
|
||||||
- Encoded JSON response exceeds `server.max_response_bytes`.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
- Compare request, file input, and expected response sizes with configured limits.
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Use smaller inline inputs or switch to file inputs under the artifact root.
|
|
||||||
- Reduce generated output size.
|
|
||||||
- Omit `include_raw_output`.
|
|
||||||
- Increase limits only when the deployment expects larger payloads.
|
|
||||||
|
|
||||||
Relevant links: [HTTP API reference](api.md), [Operations guide](operations.md)
|
|
||||||
|
|
||||||
## HTTP Route Or Method Errors
|
|
||||||
|
|
||||||
Symptom:
|
|
||||||
|
|
||||||
- HTTP returns `404 not_found` or `405 method_not_allowed`.
|
|
||||||
|
|
||||||
Likely cause:
|
|
||||||
|
|
||||||
- Path is not `/v1/runs`.
|
|
||||||
- Method on `/v1/runs` is not `POST`.
|
|
||||||
|
|
||||||
Diagnostic step:
|
|
||||||
|
|
||||||
- Check the request URL and method.
|
|
||||||
|
|
||||||
Safe fix:
|
|
||||||
|
|
||||||
- Send `POST /v1/runs`.
|
|
||||||
|
|
||||||
Relevant links: [HTTP API reference](api.md)
|
|
||||||
@@ -36,6 +36,38 @@ func (f *fakeRunner) Run(ctx context.Context, req domain.RunRequest) (*domain.Ru
|
|||||||
return f.result, nil
|
return f.result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMaintainedHTTPRunExampleMatchesRequestContract(t *testing.T) {
|
||||||
|
body, err := os.ReadFile(filepath.Join("..", "..", "..", "examples", "http-run.json"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("read maintained HTTP request example: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
runner := &fakeRunner{result: &domain.RunResult{}}
|
||||||
|
h := NewHandler(runner)
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/v1/runs", bytes.NewReader(body))
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
h.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected maintained HTTP request example to be accepted, got %d: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidExample map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(body, &invalidExample); err != nil {
|
||||||
|
t.Fatalf("decode maintained HTTP request example: %v", err)
|
||||||
|
}
|
||||||
|
invalidExample["unexpected"] = json.RawMessage(`true`)
|
||||||
|
invalidBody, err := json.Marshal(invalidExample)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("encode structurally invalid request example: %v", err)
|
||||||
|
}
|
||||||
|
invalidReq := httptest.NewRequest(http.MethodPost, "/v1/runs", bytes.NewReader(invalidBody))
|
||||||
|
invalidW := httptest.NewRecorder()
|
||||||
|
h.ServeHTTP(invalidW, invalidReq)
|
||||||
|
assertHTTPErrorCode(t, invalidW, http.StatusBadRequest, "invalid_json")
|
||||||
|
}
|
||||||
|
|
||||||
type handlerPromptRepo struct {
|
type handlerPromptRepo struct {
|
||||||
def *domain.PromptDefinition
|
def *domain.PromptDefinition
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ type OpenAICompatibleConfig struct {
|
|||||||
type OpenAICompatibleClient struct {
|
type OpenAICompatibleClient struct {
|
||||||
baseURL string
|
baseURL string
|
||||||
defaultModel string
|
defaultModel string
|
||||||
timeout time.Duration
|
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +66,6 @@ func NewOpenAICompatibleClient(cfg OpenAICompatibleConfig) (*OpenAICompatibleCli
|
|||||||
return &OpenAICompatibleClient{
|
return &OpenAICompatibleClient{
|
||||||
baseURL: strings.TrimRight(baseURL, "/"),
|
baseURL: strings.TrimRight(baseURL, "/"),
|
||||||
defaultModel: cfg.Model,
|
defaultModel: cfg.Model,
|
||||||
timeout: timeout,
|
|
||||||
httpClient: client,
|
httpClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -116,19 +114,20 @@ func (c *OpenAICompatibleClient) Generate(ctx context.Context, req domain.Genera
|
|||||||
httpReq.Header.Set("Authorization", "Bearer "+apiKey)
|
httpReq.Header.Set("Authorization", "Bearer "+apiKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
effectiveTimeout := c.timeout
|
|
||||||
if req.Target.TimeoutSeconds > 0 {
|
|
||||||
effectiveTimeout = time.Duration(req.Target.TimeoutSeconds) * time.Second
|
|
||||||
} else if req.TargetPresence.TimeoutSeconds {
|
|
||||||
effectiveTimeout = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
httpClient := c.httpClient
|
httpClient := c.httpClient
|
||||||
if httpClient == nil {
|
if httpClient == nil {
|
||||||
httpClient = &http.Client{Timeout: effectiveTimeout}
|
httpClient = &http.Client{Timeout: defaults.LLMRequestTimeoutDefault}
|
||||||
} else if httpClient.Timeout != effectiveTimeout {
|
}
|
||||||
|
if req.Target.TimeoutSeconds > 0 {
|
||||||
|
effectiveTimeout := time.Duration(req.Target.TimeoutSeconds) * time.Second
|
||||||
|
if httpClient.Timeout != effectiveTimeout {
|
||||||
|
cloned := *httpClient
|
||||||
|
cloned.Timeout = effectiveTimeout
|
||||||
|
httpClient = &cloned
|
||||||
|
}
|
||||||
|
} else if req.TargetPresence.TimeoutSeconds && httpClient.Timeout != 0 {
|
||||||
cloned := *httpClient
|
cloned := *httpClient
|
||||||
cloned.Timeout = effectiveTimeout
|
cloned.Timeout = 0
|
||||||
httpClient = &cloned
|
httpClient = &cloned
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ func TestNewOpenAICompatibleClientDoesNotMutateSuppliedZeroTimeoutClient(t *test
|
|||||||
if client.httpClient == supplied {
|
if client.httpClient == supplied {
|
||||||
t.Fatal("expected constructed client to use a cloned HTTP client")
|
t.Fatal("expected constructed client to use a cloned HTTP client")
|
||||||
}
|
}
|
||||||
if client.httpClient.Timeout != client.timeout {
|
|
||||||
t.Fatalf("expected cloned client timeout %v, got %v", client.timeout, client.httpClient.Timeout)
|
|
||||||
}
|
|
||||||
if client.httpClient.Timeout <= 0 {
|
if client.httpClient.Timeout <= 0 {
|
||||||
t.Fatalf("expected constructed client to use a positive default timeout, got %v", client.httpClient.Timeout)
|
t.Fatalf("expected constructed client to use a positive default timeout, got %v", client.httpClient.Timeout)
|
||||||
}
|
}
|
||||||
@@ -674,8 +671,9 @@ func TestOpenAICompatibleClientExplicitZeroTimeoutDisablesClientTimeout(t *testi
|
|||||||
defer ts.Close()
|
defer ts.Close()
|
||||||
|
|
||||||
client, err := NewOpenAICompatibleClient(OpenAICompatibleConfig{
|
client, err := NewOpenAICompatibleClient(OpenAICompatibleConfig{
|
||||||
BaseURL: ts.URL + "/v1",
|
BaseURL: ts.URL + "/v1",
|
||||||
Timeout: time.Nanosecond,
|
Timeout: time.Nanosecond,
|
||||||
|
HTTPClient: &http.Client{Timeout: time.Nanosecond},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -718,6 +716,34 @@ func TestOpenAICompatibleClientOmittedTimeoutUsesClientTimeout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOpenAICompatibleClientSuppliedHTTPClientTimeoutOverridesConfigTimeout(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
time.Sleep(25 * time.Millisecond)
|
||||||
|
_, _ = w.Write([]byte(`{"choices":[{"message":{"content":"ok"}}]}`))
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
client, err := NewOpenAICompatibleClient(OpenAICompatibleConfig{
|
||||||
|
BaseURL: ts.URL + "/v1",
|
||||||
|
Model: "model",
|
||||||
|
Timeout: 5 * time.Millisecond,
|
||||||
|
HTTPClient: &http.Client{Timeout: 100 * time.Millisecond},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Generate(context.Background(), domain.GenerateRequest{
|
||||||
|
Prompt: domain.RenderedPrompt{Messages: []domain.RenderedMessage{{Role: "user", Content: "hi"}}},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected supplied client timeout to allow the request, got %v", err)
|
||||||
|
}
|
||||||
|
if resp.Content != "ok" {
|
||||||
|
t.Fatalf("unexpected response content: %q", resp.Content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestOpenAICompatibleClientRejectsInvalidExtraParamsBeforeProviderCall(t *testing.T) {
|
func TestOpenAICompatibleClientRejectsInvalidExtraParamsBeforeProviderCall(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -1011,9 +1037,10 @@ func TestOpenAICompatibleClientRequestTimeoutOverride(t *testing.T) {
|
|||||||
defer ts.Close()
|
defer ts.Close()
|
||||||
|
|
||||||
client, err := NewOpenAICompatibleClient(OpenAICompatibleConfig{
|
client, err := NewOpenAICompatibleClient(OpenAICompatibleConfig{
|
||||||
BaseURL: ts.URL + "/v1",
|
BaseURL: ts.URL + "/v1",
|
||||||
Model: "m",
|
Model: "m",
|
||||||
Timeout: 50 * time.Millisecond,
|
Timeout: 5 * time.Millisecond,
|
||||||
|
HTTPClient: &http.Client{Timeout: 50 * time.Millisecond},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
Reference in New Issue
Block a user