233 lines
8.0 KiB
Markdown
233 lines
8.0 KiB
Markdown
# Scriptorium Architecture
|
|
|
|
## 1. Purpose and Non-Goals
|
|
|
|
Scriptorium is a prompt-profile execution engine.
|
|
|
|
It takes named input artifacts, renders prompt templates, calls an LLM, validates output, optionally performs bounded structured-output repair, and returns an artifact with metadata.
|
|
|
|
Scriptorium is not an orchestrator. It should not own transcription, transcript merging, transcript polishing, notification, or cross-step workflow control.
|
|
|
|
For the motivating D&D workflow:
|
|
|
|
- Narratio orchestrates.
|
|
- WhisperX transcribes.
|
|
- Seriatim merges transcripts.
|
|
- Audita polishes transcripts.
|
|
- Scriptorium generates output artifacts from prepared inputs.
|
|
|
|
Core Go code must remain domain-generic.
|
|
|
|
## 2. Current Architecture
|
|
|
|
Current high-level structure:
|
|
|
|
- `cmd/scriptorium`: binary entrypoint.
|
|
- `internal/domain`: core domain types.
|
|
- `internal/usecase`: `Runner` use case and repair loop orchestration.
|
|
- `internal/profile`: filesystem prompt profile repository and profile validation.
|
|
- `internal/artifact`: artifact reference readers (`inline`, `file`) and routing.
|
|
- `internal/prompt`: Go template-based prompt renderer.
|
|
- `internal/llm`: LLM client interface + OpenAI-compatible HTTP adapter.
|
|
- `internal/validate`: output validator implementation (`none/basic/json/json_schema`).
|
|
- `internal/adapter/cli`: CLI adapter.
|
|
- `internal/adapter/http`: HTTP adapter (`POST /v1/runs`).
|
|
|
|
This is a practical ports-and-adapters implementation.
|
|
|
|
## 3. Run Data Flow
|
|
|
|
`Runner.Run(ctx, RunRequest)` currently executes:
|
|
|
|
1. Validate minimum request requirements (`profile_id`).
|
|
2. Load prompt profile by ID/version.
|
|
3. Merge effective model target (profile defaults + request override).
|
|
4. Resolve effective output contract (profile + optional request override).
|
|
5. Resolve named artifact refs to loaded artifacts.
|
|
6. Render prompt messages from templates.
|
|
7. Hash rendered prompt for auditability.
|
|
8. Call LLM client with provider-neutral `GenerateRequest`.
|
|
9. Build output artifact from model content.
|
|
10. Validate output.
|
|
11. If structured validation failed and repair is enabled/bounded, run repair attempts and re-validate.
|
|
12. Return `RunResult` with artifact, validation, raw output, and metadata.
|
|
|
|
Validation content failure remains a successful run result with `validation.status=failed`.
|
|
|
|
## 4. Package Responsibilities
|
|
|
|
- `domain`
|
|
- Owns core nouns and contracts.
|
|
- Must not import adapters/provider SDK types.
|
|
|
|
- `usecase`
|
|
- Owns execution sequence and cross-port orchestration for a single run.
|
|
- May coordinate validation and bounded repair.
|
|
- Must not contain HTTP/CLI/wire concerns.
|
|
|
|
- `profile`
|
|
- Owns prompt profile loading/parsing/validation.
|
|
- Handles YAML strict decoding and profile-level constraints.
|
|
|
|
- `artifact`
|
|
- Owns artifact ref resolution and content loading.
|
|
- Produces normalized `Artifact` values with size/hash/content type.
|
|
|
|
- `prompt`
|
|
- Owns template rendering and required-input enforcement.
|
|
|
|
- `llm`
|
|
- Owns generation port and provider adapters.
|
|
- Current adapter: OpenAI-compatible chat completions over `net/http`.
|
|
|
|
- `validate`
|
|
- Owns output validation semantics and JSON Schema integration.
|
|
|
|
- `adapter/http`, `adapter/cli`
|
|
- Owns transport/wire/flag concerns only.
|
|
- Should stay thin and delegate business flow to `usecase.Runner`.
|
|
|
|
## 5. Domain Model (Current)
|
|
|
|
Key types in `internal/domain`:
|
|
|
|
- `RunRequest`: profile selector, named input refs, vars, optional model override, optional validation override.
|
|
- `RunResult`: output artifact, validation result, raw output, profile/model metadata, hashes, usage, timestamps, duration.
|
|
- `ArtifactRef`: `{type, uri, body}` reference contract.
|
|
- `Artifact`: loaded payload (`name`, `content_type`, `body`, `uri`, `size`, `hash`).
|
|
- `PromptProfile`: YAML-backed profile definition.
|
|
- `RenderedPrompt` / `RenderedMessage`: provider-neutral prompt structure.
|
|
- `GenerateRequest` / `GenerateResponse`: provider-neutral model I/O.
|
|
- `ValidationResult`: passed/failed/skipped + mode/errors/schema/repair attempts.
|
|
|
|
## 6. Interfaces and Adapters
|
|
|
|
Primary ports:
|
|
|
|
- `profile.Repository`
|
|
- `artifact.Reader`
|
|
- `prompt.Renderer`
|
|
- `llm.Client`
|
|
- `validate.Validator`
|
|
- `usecase.OutputRepairer` (usecase-local abstraction)
|
|
|
|
Current adapters:
|
|
|
|
- Profile repository: filesystem YAML loader.
|
|
- Artifact reader: composite reader for `inline` and `file`.
|
|
- Prompt renderer: Go templates with input helper + vars.
|
|
- LLM adapter: OpenAI-compatible `/chat/completions`.
|
|
- Validator: standard validator with `none/basic/json/json_schema`.
|
|
- CLI/HTTP adapters: thin request mapping and response mapping.
|
|
|
|
## 7. Validation and Repair Model
|
|
|
|
Validation modes:
|
|
|
|
- `none`
|
|
- `basic`
|
|
- `json`
|
|
- `json_schema`
|
|
|
|
Repair behavior:
|
|
|
|
- Only applies to structured modes (`json`, `json_schema`).
|
|
- Attempted only when validation fails, repairer exists, and `repair_attempts > 0`.
|
|
- Bounded strictly by `repair_attempts`.
|
|
- Uses a narrow JSON-repair prompt and re-validates each attempt.
|
|
- If still invalid, run succeeds with failed validation and preserved final raw output.
|
|
- Validator runtime/config errors are run errors.
|
|
|
|
## 8. Public Contracts
|
|
|
|
### CLI
|
|
|
|
Commands:
|
|
|
|
- `scriptorium run`
|
|
- `scriptorium serve`
|
|
|
|
`run`:
|
|
|
|
- Required: `--profile-dir`, `--profile-id`, `--input`.
|
|
- Optional: model/endpoint overrides (`--model`, `--llm-base-url`), vars, output path, schema dir, timeout.
|
|
- Artifact bytes go to stdout (or `--out` file); summaries/errors go to stderr.
|
|
|
|
`serve`:
|
|
|
|
- Required: `--profile-dir`, `--llm-base-url`.
|
|
- Exposes HTTP run endpoint.
|
|
|
|
### HTTP
|
|
|
|
- Endpoint: `POST /v1/runs`.
|
|
- Request maps to `RunRequest` (`profile_id`, `inputs`, `vars`, optional `model` override).
|
|
- Response includes `artifact`, `validation`, `metadata`, `raw_model_output`.
|
|
- Validation content failures are represented as `200` with `validation.status=failed`.
|
|
- Error responses are `{error:{code,message}}` with stable code mapping.
|
|
|
|
### Prompt Profile YAML
|
|
|
|
- `id`, `version`, `expected_inputs`, `templates`, `model_defaults`, `output_format`, `validation`.
|
|
- Strict YAML decoding (`KnownFields`) rejects unknown fields.
|
|
- `validation.schema_path` required when `validation_mode=json_schema`.
|
|
- `validation.repair_attempts` must be non-negative.
|
|
|
|
### Metadata
|
|
|
|
Current run metadata includes:
|
|
|
|
- `run_id` (UUID v4)
|
|
- `profile_id`, `profile_version`, `profile_hash`
|
|
- `model_name`, `endpoint`, effective `model_params`
|
|
- `input_hashes`, `prompt_hash`
|
|
- token usage
|
|
- start/end timestamps
|
|
- duration
|
|
- validation mode/status
|
|
- repair attempts used
|
|
|
|
## 9. Extension Points (Future Work)
|
|
|
|
Future features should plug into existing boundaries, not bypass them.
|
|
|
|
Candidate extensions:
|
|
|
|
- S3 artifact refs via `artifact.Reader` extension.
|
|
- Token budgeting in usecase/model-target policy layer.
|
|
- Streaming LLM output via additional `llm.Client` methods/adapters.
|
|
- Batch execution as a separate use case (not hidden in single-run path).
|
|
- Additional LLM providers implementing `llm.Client`.
|
|
- Additional validators/modes in `validate`.
|
|
- Additional profile repositories (embedded, remote, object storage).
|
|
|
|
These are future work, not part of current default behavior.
|
|
|
|
## 10. Architectural Guardrails
|
|
|
|
Contributors should preserve these constraints:
|
|
|
|
- No D&D-specific behavior in core Go packages.
|
|
- No orchestration creep into Scriptorium.
|
|
- No unbounded repair loops.
|
|
- No silent truncation/omission of rendered inputs or outputs.
|
|
- Do not log full artifacts/prompts by default.
|
|
- Keep provider-specific wire/SDK details out of domain types.
|
|
- Keep adapter boundaries explicit and thin.
|
|
|
|
## 11. Testing Strategy
|
|
|
|
Protect behavior at boundaries and in usecase flow:
|
|
|
|
- Profile loading/parsing/validation errors.
|
|
- Artifact reading for inline/file + hash/content type behavior.
|
|
- Prompt rendering required inputs/template error behavior.
|
|
- LLM adapter request/response/error/timeout behavior.
|
|
- Runner success path and metadata population.
|
|
- Validation failure raw-output preservation.
|
|
- Successful/failed/bounded repair flows.
|
|
- HTTP request mapping, response shape, and error mapping.
|
|
- CLI parsing helpers, required flags, and output stream separation.
|
|
|
|
Prefer focused unit tests and small integration-style tests with fake LLMs.
|