Consolidate external documentation contracts

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

View File

@@ -1,65 +1,26 @@
# 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:
| Surface | Use when |
| Interface | Use when |
| --- | --- |
| Go package | The consumer is Go, needs typed requests/results, or wants injected LLM clients for tests. |
| CLI subprocess | The consumer wants process isolation or is not written in Go. |
| HTTP API | The consumer needs a service boundary or remote access to `POST /v1/runs`. |
| Go package | The consumer is Go and needs typed requests, results, or an injected LLM client. |
| CLI subprocess | The consumer needs process isolation or is not written in Go. |
| 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)
- 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
## Minimal Go Use
```go
engine, err := scriptorium.NewEngine(scriptorium.Config{
PromptDir: "./examples/prompts",
ProfileDir: "./examples/profiles",
SchemaDir: "./examples/schemas",
})
if err != nil {
return err
@@ -69,54 +30,30 @@ prepared, err := engine.Prepare(ctx, scriptorium.RunRequest{
PromptID: "generic.markdown_summary",
Inputs: map[string]scriptorium.ArtifactRef{
"transcript": scriptorium.File("./examples/fixtures/transcript.md"),
"glossary": scriptorium.File("./examples/fixtures/glossary.yml"),
},
})
if err != nil {
return err
}
_ = prepared.Messages
_ = prepared
```
Run the maintained package example:
```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.
For a maintained program, see
[`examples/go-library/prepare`](../../examples/go-library/prepare).
## Consumer Responsibilities
Consumers are responsible for:
- selecting prompt/profile IDs as deployment configuration;
- supplying all required inputs and vars;
- protecting generated artifacts and rendered prompts as sensitive data;
- deciding whether to keep output when validation fails;
- implementing retries only when another model call is acceptable.
- selecting and deploying prompt, profile, and schema assets;
- supplying required inputs and template variables;
- supplying credentials through the applicable interface;
- protecting rendered prompts and generated artifacts as potentially sensitive;
- 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
can produce different output and can incur another provider request.
## Status Behavior
- 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.
Scriptorium does not persist run state. A retry can produce different output and
can incur another provider request. CLI exit behavior belongs to the
[CLI reference](../cli.md); HTTP status behavior belongs to the
[HTTP API reference](../api.md); package errors and results belong to the
[package contract](pkg-scriptorium.md).

View File

@@ -1,4 +1,4 @@
# Package scriptorium
# Package `scriptorium`
Import path:
@@ -6,250 +6,158 @@ Import path:
import "gitea.maximumdirect.net/eric/scriptorium"
```
The root package is the public Go facade for Scriptorium's prompt prepare/run
workflow. It exposes typed requests, results, source options, injected LLM
clients, and stable public errors while keeping `internal/*` packages private.
This is the canonical public Go contract for in-process prompt preparation and
execution. Prompt, profile, and schema file formats are defined in the
[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;
- typed request/result structs;
- direct `context.Context` cancellation;
- injected/fake LLM clients for tests;
- direct per-request `RunRequest.APIKey`.
| Field | Meaning |
| --- | --- |
| `PromptDir` | Prompt-definition directory, required unless a prompt source option is supplied. |
| `ProfileDir` | Optional custom profile directory over built-ins. |
| `SchemaDir` | Schema directory; empty uses `.`. |
| `Timeout` | Default timeout for the built-in OpenAI-compatible client. |
| `HTTPClient` | Optional HTTP client for that built-in client. |
Use [Subprocess integration](../integrations/subprocess.md) or the [HTTP API](../api.md)
when a process or service boundary is preferred.
Nil options are ignored. Invalid construction, including
`WithLLMClient(nil)`, returns an error matching `ErrInvalidConfig`.
## Construct An Engine
Source options replace their matching directory source:
- prompts: `WithPromptFS(fsys, root)`, `WithPromptFile(path)`;
- profiles: `WithProfileFS(fsys, root)`, `WithProfileFile(path)`, and
`WithProfiles(profiles...)`;
- schemas: `WithSchemaFS(fsys, root)`, `WithSchemaFile(path)`; and
- LLM client: `WithLLMClient(client)`.
`fs.FS` prompt-content and schema paths stay inside their configured roots.
A single-file option exposes that file by its base name. In-memory profiles take
precedence over an explicit or directory-backed profile source, which in turn
takes precedence over built-ins. File and filesystem sources use the format and
credential rules in the [configuration reference](../config.md).
## Prepare And Run
`Prepare(ctx, request)` resolves the prompt, profile, input artifacts,
validation contract, and rendered messages without calling an LLM.
`Run(ctx, request)` performs that preparation, calls the configured client,
and validates generated content.
```go
engine, err := scriptorium.NewEngine(scriptorium.Config{
PromptDir: "./examples/prompts",
ProfileDir: "./examples/profiles",
SchemaDir: "./examples/schemas",
})
if err != nil {
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{
PromptID: "generic.markdown_summary",
Inputs: map[string]scriptorium.ArtifactRef{
"transcript": scriptorium.File("./examples/fixtures/transcript.md"),
"glossary": scriptorium.File("./examples/fixtures/glossary.yml"),
},
})
if err != nil {
return err
}
_ = prepared.EffectiveModelParams
_ = prepared.Messages
```
`PreparedRun` includes prompt ID/version/hash, selected profile, effective
model params, output contract, structured-output metadata, input hashes,
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.
The maintained package example is
[`examples/go-library/prepare`](../../examples/go-library/prepare).
## 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
artifact, and validates the output.
`RunResult` adds run ID, artifact, raw output, validation, model metadata,
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
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
```
## Public Values
`RunResult` includes run ID, output artifact, raw output, validation result,
prompt/profile/model metadata, effective model params, input hashes, usage, and
timing fields.
`ArtifactRef` has `Type`, `URI`, and `Body`; `Artifact` has `Name`,
`ContentType`, `Body`, `URI`, `Size`, and `Hash`. `ExecutionTarget` exposes the
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
`Validation.Status == ValidationFailed`. Runtime/schema validation errors
return an error that matches `ErrValidation`.
The exported constants define these serialized values:
## 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.
- `Inline(body)`: inline artifact body.
- `InlineWithURI(uri, body)`: inline artifact body with URI metadata.
## Requests, Inputs, And Overrides
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
Use `WithLLMClient` for tests or custom model integrations:
`LLMClient` implements:
```go
type fakeLLM struct{}
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{}))
Generate(context.Context, GenerateRequest) (*GenerateResponse, error)
```
Injected clients receive:
- rendered prompt;
- effective execution target;
- 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`.
Injected clients receive the rendered prompt, effective execution target, numeric
target-presence metadata, optional structured-output specification, and direct
request API key. `GenerateResponse` returns content and `TokenUsage`.
Custom clients should avoid logging raw prompts or credentials.
## Errors
Public methods wrap context while preserving stable sentinel checks with
`errors.Is`:
Public methods preserve these sentinel checks through `errors.Is`:
- `ErrInvalidConfig`
- `ErrInvalidRequest`
@@ -262,23 +170,5 @@ Public methods wrap context while preserving stable sentinel checks with
- `ErrLLMGenerate`
- `ErrValidation`
Example:
```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)
For interface selection and operational responsibilities, see the
[consumer integration overview](api.md).