Refocus internal component documentation
This commit is contained in:
@@ -36,7 +36,7 @@ Start with:
|
|||||||
| Application configuration | [Configuration contract](config.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) |
|
| 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) |
|
| 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), [runner internals](internal/runner.md), and [adapter internals](internal/adapters.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) |
|
| Subprocess behavior | [Subprocess integration](integrations/subprocess.md) and [CLI contract](cli.md) |
|
||||||
| Runtime operation, recovery, or troubleshooting | [Operations](operations.md) and [troubleshooting](troubleshooting.md) |
|
| Runtime operation, recovery, or troubleshooting | [Operations](operations.md) and [troubleshooting](troubleshooting.md) |
|
||||||
| Examples or copyable assets | The owning contract for the demonstrated behavior and the related files under `examples/` |
|
| Examples or copyable assets | The owning contract for the demonstrated behavior and the related files under `examples/` |
|
||||||
|
|||||||
@@ -2,148 +2,86 @@
|
|||||||
|
|
||||||
## 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`
|
|
||||||
|
Run the affected adapter package tests and recheck the relevant canonical
|
||||||
|
contract. The [testing policy](../policy/testing.md) owns global test
|
||||||
|
sufficiency guidance.
|
||||||
|
|
||||||
## Change Recipes
|
## Change Recipes
|
||||||
|
|
||||||
@@ -154,33 +92,22 @@ Adapters do not add durable run state.
|
|||||||
precedence while wiring it through its consuming adapter.
|
precedence while wiring it through its consuming adapter.
|
||||||
3. Add focused configuration and adapter tests for parsing, mapping, and
|
3. Add focused configuration and adapter tests for parsing, mapping, and
|
||||||
effective behavior.
|
effective behavior.
|
||||||
4. Update the [configuration contract](../config.md) and any external contract
|
4. Update the [configuration contract](../config.md) and any affected external
|
||||||
affected by the new behavior.
|
contract.
|
||||||
|
|
||||||
### CLI Flags
|
### CLI Flags
|
||||||
|
|
||||||
1. Add the flag to the relevant command in `internal/adapter/cli/run.go`.
|
1. Add the flag to the relevant parser in `internal/adapter/cli/run.go`.
|
||||||
2. Keep command scope and application-configuration precedence intentional.
|
2. Keep command scope and application-configuration precedence intentional.
|
||||||
3. Add or update parser and command tests in
|
3. Add or update parser and command tests in
|
||||||
`internal/adapter/cli/run_test.go`.
|
`internal/adapter/cli/run_test.go`.
|
||||||
4. Update the [CLI contract](../cli.md) and any maintained examples affected by
|
4. Update the [CLI contract](../cli.md) and affected maintained examples.
|
||||||
the invocation.
|
|
||||||
|
|
||||||
### Adapter Capabilities
|
### Adapter Capabilities
|
||||||
|
|
||||||
1. Define or reuse the appropriate domain or use-case interface boundary.
|
1. Define or reuse the appropriate domain or use-case interface boundary.
|
||||||
2. Implement translation and IO behavior in the adapter without moving
|
2. Implement translation and IO behavior without moving use-case decisions out
|
||||||
use-case decisions out of `internal/usecase`.
|
of `internal/usecase`.
|
||||||
3. Add focused mapping, parsing, and error-behavior tests.
|
3. Add focused mapping, parsing, and error-behavior tests.
|
||||||
4. Update this document and the affected canonical public or integration
|
4. Update this document and the affected public or integration contract. Update
|
||||||
contract. Update [source internals](sources.md) when source-loading behavior
|
[source internals](sources.md) when source-loading behavior changes.
|
||||||
changes.
|
|
||||||
|
|
||||||
## Architectural Invariants
|
|
||||||
|
|
||||||
- Adapter packages stay thin and translation-focused.
|
|
||||||
- App config is resolved before dependency construction.
|
|
||||||
- External input strictness is part of contract stability.
|
|
||||||
- CLI and HTTP construct runners without a repairer.
|
|
||||||
- HTTP endpoint details remain canonical in `docs/api.md`.
|
|
||||||
- Public Go package details remain canonical in `docs/consumers/pkg-scriptorium.md`.
|
|
||||||
|
|||||||
85
docs/internal/llm.md
Normal file
85
docs/internal/llm.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# 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 establishes the default timeout. A non-positive
|
||||||
|
configured timeout uses 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 default in the clone; a supplied non-zero timeout is retained. The
|
||||||
|
client stores the trimmed base URL, default model, timeout, 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 when an explicit timeout changes the configured
|
||||||
|
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.
|
||||||
@@ -41,7 +41,7 @@ and invariants; public behavior belongs in the linked contracts.
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `internal/format` | Formats prepared-run information for CLI output. | [CLI contract](../cli.md), [adapter internals](adapters.md) |
|
| `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/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), [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
|
Focused internal documents describe the components that have detailed
|
||||||
orchestration, adapter, or source behavior. Package tests live alongside the
|
orchestration, adapter, or source behavior. Package tests live alongside the
|
||||||
|
|||||||
@@ -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,23 +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`
|
||||||
|
|
||||||
## Change Recipe
|
When updating prompt, profile, schema, or built-in assets:
|
||||||
|
|
||||||
When updating prompt, profile, schema, or built-in-profile assets:
|
1. keep assets valid for the strict loader and the relevant source boundary;
|
||||||
|
2. update the [configuration reference](../config.md) when a file-format,
|
||||||
|
catalog, or default changes;
|
||||||
|
3. run focused source and integration tests, including the built-in repository
|
||||||
|
test when embedded assets change; and
|
||||||
|
4. update this document when discovery, precedence, containment, or failure
|
||||||
|
mechanics change.
|
||||||
|
|
||||||
1. Keep files valid for their strict loader and source boundary.
|
The [testing policy](../policy/testing.md) owns global test sufficiency.
|
||||||
2. Keep maintained examples and fixtures secret-free.
|
|
||||||
3. Run focused prompt, profile, schema, or validation tests for the changed
|
|
||||||
source.
|
|
||||||
4. Update the [configuration contract](../config.md) and every affected
|
|
||||||
external contract; update this document when loading or precedence mechanics
|
|
||||||
change.
|
|
||||||
|
|
||||||
## Architectural Invariants
|
|
||||||
|
|
||||||
- Prompt/profile identity comes from YAML `id`.
|
|
||||||
- External YAML decoding remains strict.
|
|
||||||
- File-backed profile YAML never accepts raw API key values.
|
|
||||||
- Built-in profiles are fallback, not a replacement for custom source validation.
|
|
||||||
- HTTP file artifacts remain rooted by lexical containment.
|
|
||||||
- Schema runtime failures remain errors, while JSON/schema content mismatches remain validation results.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user