2026-05-26 03:28:28 +00:00
# Adapter And Repository Internals
## Purpose
This document describes implemented adapter/repository boundaries and their current behavior.
## Adapter Map
- `internal/adapter/cli` : CLI command parsing, app wiring, stdout/stderr handling, exit codes.
- `internal/adapter/http` : HTTP request/response mapping for `POST /v1/runs` .
2026-07-04 14:24:06 +00:00
- root package `scriptorium` : public Go library facade for preparing and running prompt requests.
2026-07-04 17:02:09 +00:00
- `internal/promptdef` : filesystem and `fs.FS` prompt-definition repositories.
2026-07-04 16:48:18 +00:00
- `internal/profile` : filesystem, `fs.FS` , and overlay execution-profile repositories.
2026-07-05 00:10:47 +00:00
- `internal/filecatalog` : shared YAML discovery, display-path, and `fs.FS` source-root resolution helpers.
2026-07-04 16:48:18 +00:00
- `internal/profile/builtin` : embedded built-in execution-profile repository.
2026-05-26 03:28:28 +00:00
- `internal/artifact` : input artifact reader.
- `internal/prompt` : Go-template renderer.
- `internal/llm` : OpenAI-compatible LLM client implementation.
2026-07-04 17:02:09 +00:00
- `internal/validate` : filesystem and `fs.FS` output validators.
2026-05-26 03:28:28 +00:00
- `internal/format` : prepared-run formatters for `render` output.
## Inputs And Outputs
CLI adapter:
- Input: process args, filesystem config/assets, environment.
- Output: exit code, stdout artifact/prepared output, stderr summaries/errors.
2026-07-02 23:10:24 +00:00
- `run` summaries include cache usage counters only when either parsed cache counter is non-zero.
2026-05-26 03:28:28 +00:00
HTTP adapter:
- Input: JSON request body (`runRequestDTO` ).
- Output: JSON success/error body with mapped status codes.
2026-07-02 23:10:24 +00:00
- Success metadata includes token usage plus cache usage counters.
2026-05-26 03:28:28 +00:00
2026-07-04 14:24:06 +00:00
Public library facade:
- Input: typed `scriptorium.RunRequest` values.
- Output: typed `PreparedRun` and `RunResult` values plus public sentinel errors.
- Custom LLM behavior is injected with `WithLLMClient` ; otherwise the default OpenAI-compatible client is used.
2026-07-04 16:55:01 +00:00
- `RunRequest.APIKey` is a request-scoped Go value only; it is converted into internal execution state for LLM generation and stripped from public result types.
2026-07-04 17:02:09 +00:00
- Prompt, profile, and schema source options can use directories, single files, or `fs.FS` roots. Explicit source options override the matching `Config` directory field.
2026-07-04 14:24:06 +00:00
- Public types are facade types converted at the package boundary; internal domain types remain internal.
2026-07-04 17:02:09 +00:00
Prompt/profile repositories:
2026-05-26 03:28:28 +00:00
2026-07-04 17:02:09 +00:00
- Input: prompt/profile YAML files under configured directories or `fs.FS` roots.
2026-05-26 03:28:28 +00:00
- Output: normalized domain definitions/profiles or typed errors.
2026-07-05 00:10:47 +00:00
- Shared YAML catalog helpers provide recursive discovery, extension filtering, deterministic ordering, file stems, `fs.FS` display paths, and source-root containment checks.
2026-07-04 17:02:09 +00:00
- Single-file public sources are represented as `fs.FS` roots containing one YAML file; lookup still uses YAML `id` values.
2026-05-26 03:28:28 +00:00
2026-07-04 16:48:18 +00:00
Profile repository composition:
- Built-in profiles are embedded and loaded through the same profile validation rules as filesystem profiles.
- When no custom profile directory is configured, the runner receives the built-in profile repository.
2026-07-04 17:02:09 +00:00
- When a custom profile directory/file/`fs.FS` source is configured, the runner receives an overlay repository with custom profiles as primary and built-ins as fallback.
2026-07-04 16:48:18 +00:00
- Overlay lookup falls back only after custom profile-not-found errors; custom load/validation/raw-key errors are returned directly.
2026-05-26 03:28:28 +00:00
Artifact reader:
- Input: `domain.ArtifactRef` .
- Output: loaded `domain.Artifact` .
LLM adapter:
- Input: `domain.GenerateRequest` .
- Output: `domain.GenerateResponse` .
2026-07-04 16:55:01 +00:00
- Direct API-key values are preferred when present; otherwise `api_key_env` is resolved from the process environment.
2026-05-26 03:28:28 +00:00
Validator:
- Input: artifact body + output contract.
- Output: validation result or runtime validation error.
2026-07-04 17:02:09 +00:00
- Schema documents may be loaded from a directory, single file, or `fs.FS` root in the public package. CLI and HTTP continue to use directory-backed schema loading.
2026-05-26 03:28:28 +00:00
## Boundaries
- Adapters convert external representations to domain requests and back.
- Use-case decisions remain in `internal/usecase` .
- External dependency details stay scoped to adapter packages.
## Config Fields Used
Primary app settings consumed by adapters:
- `prompt_dir`
2026-07-04 16:48:18 +00:00
- `profile_dir` (optional custom profile source)
2026-05-26 03:28:28 +00:00
- `schema_dir`
- `server.addr`
2026-07-04 23:34:44 +00:00
- `server.artifact_root` (HTTP `serve` file input root)
2026-07-05 00:17:07 +00:00
- `server.max_request_bytes`
- `server.max_artifact_bytes`
- `server.max_response_bytes`
2026-05-26 03:28:28 +00:00
- `defaults.render_format`
Execution profile/request settings used through runner:
2026-05-26 07:44:49 -05:00
- `endpoint` , `model` , `temperature` , `max_tokens` , `top_p` , `timeout_seconds` , `service_tier` , `api_key_env` , `reasoning_effort` , `extra_params`
2026-07-04 13:29:38 +00:00
- CLI and HTTP request adapters preserve caller intent for numeric runtime overrides. Omitted values remain absent; explicit zero values are mapped as explicit overrides.
- HTTP `extra_params` accepts JSON-compatible values and maps them to domain request overrides without provider-specific adapter logic.
2026-05-26 03:28:28 +00:00
## External Dependencies
- YAML decoding: `gopkg.in/yaml.v3` (strict known-fields mode in config/prompt/profile loaders).
- JSON Schema validation: `github.com/santhosh-tekuri/jsonschema/v6` .
- HTTP client/server: Go standard library.
## Failure Behavior
Strict decoding and input checks:
- config/prompt/profile loaders reject unknown YAML fields.
2026-05-26 07:36:10 -05:00
- prompt/profile repositories scan nested subdirectories recursively.
- prompt/profile lookup uses YAML `id` values; subdirectory paths are organizational only.
2026-07-04 17:02:09 +00:00
- prompt `content_file` paths resolve relative to the prompt YAML file within the same source.
2026-07-05 00:10:47 +00:00
- `fs.FS` prompt `content_file` paths and schema paths must remain inside the configured source root; absolute paths and relative traversal outside the root are rejected.
2026-05-26 07:36:10 -05:00
- duplicate prompt/profile IDs are invalid and fail instead of using first-match behavior.
2026-07-04 16:48:18 +00:00
- duplicate profile IDs across custom and built-in sources are allowed; the custom source overrides the built-in profile.
2026-05-26 03:28:28 +00:00
- HTTP DTO decoder rejects unknown JSON fields.
- raw API key payload fields are rejected by strict decoding in profile/http paths.
Artifact refs:
- Supported reference types: `inline` , `file` .
- Unsupported types return `ErrUnsupportedRefType` .
2026-07-04 23:34:44 +00:00
- CLI `run` and `render` use direct filesystem file reads for `file` references.
2026-07-05 00:22:54 +00:00
- HTTP `serve` uses a restricted artifact reader: `inline` references work without a root, while `file` references require `server.artifact_root` or `--artifact-root` and must pass lexical containment checks against that root.
2026-07-05 00:17:07 +00:00
- HTTP `serve` applies request-body, file-artifact, and encoded-response size limits. CLI `run` and `render` do not use these HTTP limits.
2026-07-05 00:22:54 +00:00
- HTTP file paths are resolved with clean absolute paths and lexical containment checks, not string-prefix checks.
- Symlinks inside the root are followed by the operating system, including symlinks that point outside the root; the configured root must not be writable by untrusted users.
2026-05-26 03:28:28 +00:00
LLM adapter:
- endpoint appends `/chat/completions` .
2026-07-02 23:10:24 +00:00
- rendered messages without cache control serialize with string `content` .
- rendered messages with cache control serialize as one text content block with `cache_control` .
2026-07-04 13:29:38 +00:00
- non-empty `reasoning_effort` serializes as a top-level provider request field.
- `extra_params` flatten into provider-specific top-level JSON request fields.
- reserved `extra_params` keys are rejected before the provider call: `model` , `session_id` , `messages` , `temperature` , `max_tokens` , `top_p` , `service_tier` , `reasoning_effort` , and `response_format` .
- empty `extra_params` keys and values that cannot be JSON-encoded are rejected before the provider call.
2026-07-02 23:10:24 +00:00
- compatible cache usage response fields are parsed into domain token usage.
2026-05-26 03:28:28 +00:00
- non-2xx responses map to request failure errors.
- malformed responses (including missing/empty first choice content) are errors.
2026-07-04 16:55:01 +00:00
- direct API-key values are never serialized in provider request bodies.
2026-05-26 03:28:28 +00:00
Validator:
- `basic` , `json` , `json_schema` content failures return `ValidationFailed` results.
- schema load/compile/path failures are runtime errors.
2026-07-05 00:10:47 +00:00
- directory-backed schema lookup uses explicit `schema_path` values relative to `schema_dir` ; it does not recursively search by basename.
- `fs.FS` schema lookup uses explicit `schema_path` values inside the configured source root. Single-file public schema sources match by base name.
2026-05-26 03:28:28 +00:00
HTTP error mapping:
- maps domain/use-case errors to stable HTTP code + error code/message.
2026-07-05 00:17:07 +00:00
- maps request, artifact, and response size failures to `413` errors.
2026-05-26 15:00:06 +00:00
- distinguishes missing profile selection and missing `api_key_env` variable using stable use-case sentinel errors.
2026-05-26 03:28:28 +00:00
- avoids returning internal wrapped-cause details in response payload.
## CLI Adapter Semantics
Implemented commands:
- `run`
- `render`
- `serve`
Behavior highlights:
- `run` exit `2` indicates validation failed after generation.
- `render` does not call the LLM.
- `serve` exposes HTTP handler only; no built-in auth.
- `render` supports `--format text|json` ; `render` does not expose `--schema-dir` .
- deprecated aliases `--prompt-id` and `--profile-id` are still accepted.
## Tests To Inspect Before Changing
- `internal/adapter/cli/run_test.go`
- `internal/adapter/http/handler_test.go`
- `internal/promptdef/repository_test.go`
- `internal/profile/repository_test.go`
- `internal/artifact/reader_test.go`
- `internal/prompt/renderer_test.go`
- `internal/llm/openai_compatible_client_test.go`
- `internal/validate/standard_validator_test.go`
- `internal/format/prepared_run_test.go`
## Architectural Invariants
- Adapter packages do not own runner decision logic.
- External request/response strictness is part of contract stability.
- Prepared-render output never includes resolved API key values.
2026-07-04 13:29:38 +00:00
- Outbound OpenAI-compatible request includes currently serialized first-class fields (`model` , optional `session_id` , `messages` , optional `temperature` , `max_tokens` , `top_p` , optional `service_tier` , optional `reasoning_effort` , optional `response_format` ) plus validated `extra_params` flattened as provider-specific top-level fields.
2026-07-02 20:08:44 -05:00
- Outbound cache control is message-level only; no top-level cache-control field is serialized.