Files
scriptorium/docs/internal/adapters.md

9.1 KiB

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.
  • root package scriptorium: public Go library facade for preparing and running prompt requests.
  • internal/promptdef: filesystem and fs.FS prompt-definition repositories.
  • internal/profile: filesystem, fs.FS, and overlay execution-profile repositories.
  • internal/filecatalog: shared YAML discovery and display-path helpers for prompt/profile repositories.
  • internal/profile/builtin: embedded built-in execution-profile repository.
  • internal/artifact: input artifact reader.
  • internal/prompt: Go-template renderer.
  • internal/llm: OpenAI-compatible LLM client implementation.
  • internal/validate: filesystem and fs.FS output validators.
  • 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.
  • run summaries include cache usage counters only when either parsed cache counter is non-zero.

HTTP adapter:

  • Input: JSON request body (runRequestDTO).
  • Output: JSON success/error body with mapped status codes.
  • Success metadata includes token usage plus cache usage counters.

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.
  • 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.
  • Prompt, profile, and schema source options can use directories, single files, or fs.FS roots. Explicit source options override the matching Config directory field.
  • Public types are facade types converted at the package boundary; internal domain types remain internal.

Prompt/profile repositories:

  • Input: prompt/profile YAML files under configured directories or fs.FS roots.
  • Output: normalized domain definitions/profiles or typed errors.
  • Shared YAML catalog helpers provide recursive discovery, extension filtering, deterministic ordering, file stems, and fs.FS display paths.
  • Single-file public sources are represented as fs.FS roots containing one YAML file; lookup still uses YAML id values.

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.
  • 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.
  • Overlay lookup falls back only after custom profile-not-found errors; custom load/validation/raw-key errors are returned directly.

Artifact reader:

  • Input: domain.ArtifactRef.
  • Output: loaded domain.Artifact.

LLM adapter:

  • Input: domain.GenerateRequest.
  • Output: domain.GenerateResponse.
  • Direct API-key values are preferred when present; otherwise api_key_env is resolved from the process environment.

Validator:

  • Input: artifact body + output contract.
  • Output: validation result or runtime validation error.
  • 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.

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
  • profile_dir (optional custom profile source)
  • schema_dir
  • server.addr
  • server.artifact_root (HTTP serve file input root)
  • defaults.render_format

Execution profile/request settings used through runner:

  • endpoint, model, temperature, max_tokens, top_p, timeout_seconds, service_tier, api_key_env, reasoning_effort, extra_params
  • 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.

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.
  • prompt/profile repositories scan nested subdirectories recursively.
  • prompt/profile lookup uses YAML id values; subdirectory paths are organizational only.
  • prompt content_file paths resolve relative to the prompt YAML file within the same source.
  • duplicate prompt/profile IDs are invalid and fail instead of using first-match behavior.
  • duplicate profile IDs across custom and built-in sources are allowed; the custom source overrides the built-in profile.
  • 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.
  • CLI run and render use direct filesystem file reads for file references.
  • 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 stay inside that root.
  • HTTP file paths are resolved with clean absolute paths and containment checks, not string-prefix checks.
  • Symlinks inside the root are followed by the operating system; the configured root must not be writable by untrusted users.

LLM adapter:

  • endpoint appends /chat/completions.
  • rendered messages without cache control serialize with string content.
  • rendered messages with cache control serialize as one text content block with cache_control.
  • 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.
  • compatible cache usage response fields are parsed into domain token usage.
  • non-2xx responses map to request failure errors.
  • malformed responses (including missing/empty first choice content) are errors.
  • direct API-key values are never serialized in provider request bodies.

Validator:

  • basic, json, json_schema content failures return ValidationFailed results.
  • schema load/compile/path failures are runtime errors.
  • schema lookup uses explicit schema_path values relative to schema_dir; it does not recursively search by basename.

HTTP error mapping:

  • maps domain/use-case errors to stable HTTP code + error code/message.
  • distinguishes missing profile selection and missing api_key_env variable using stable use-case sentinel errors.
  • 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.
  • 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.
  • Outbound cache control is message-level only; no top-level cache-control field is serialized.