Harden backend contracts and documentation
This commit is contained in:
@@ -33,8 +33,8 @@ engine, err := promptkit.NewEngine(promptkit.Config{
|
||||
})
|
||||
```
|
||||
|
||||
Options support single-file or `fs.FS` sources, in-memory profiles, and
|
||||
injected artifact or model clients. Consult the
|
||||
Options support single-file or `fs.FS` sources, in-memory profiles,
|
||||
engine-scoped backends, and injected artifact or model clients. Consult the
|
||||
[constructor and option GoDoc](../../engine.go) for composition, precedence,
|
||||
validation, and default transport behavior. Source discovery, format
|
||||
validation, and profile precedence are defined by the
|
||||
@@ -123,6 +123,13 @@ Registrations belong to one engine and custom IDs cannot replace built-ins.
|
||||
The [`Backend` and `WithBackend` GoDoc](../../backends.go) defines validation,
|
||||
copying, uniqueness, and request-default behavior.
|
||||
|
||||
Both file-backed and in-memory profiles select a registration through
|
||||
`backend` or `Profile.BackendID`. Profile and request endpoint overrides retain
|
||||
that routing identity. `PreparedRun.SelectedBackendID`,
|
||||
`RunResult.SelectedBackendID`, and the effective `ExecutionTarget.BackendID`
|
||||
expose it to consumers and injected model clients. Endpoint-only profiles
|
||||
remain supported and expose an empty backend ID.
|
||||
|
||||
## Credentials
|
||||
|
||||
File-backed profiles name an environment variable; in-memory profiles can
|
||||
@@ -163,7 +170,8 @@ distinguish invalid construction, invalid requests, absent sources,
|
||||
source-loading failures, collaborator failures, and operational validation
|
||||
failures. Specific request conditions may also match the broader
|
||||
`ErrInvalidRequest`, and injected collaborator identities are preserved where
|
||||
documented.
|
||||
documented. Invalid or duplicate backend registrations match
|
||||
`ErrInvalidConfig`; selecting an unknown backend matches `ErrProfileLoad`.
|
||||
|
||||
## Application Boundary
|
||||
|
||||
|
||||
@@ -168,6 +168,10 @@ variable name in `api_key_env`.
|
||||
|
||||
Promptkit does not infer a backend from a model or endpoint. Endpoint-only
|
||||
profiles remain supported and have no effective backend ID.
|
||||
The engine always provides the built-in `openrouter` ID. Consumers can add
|
||||
engine-scoped IDs with
|
||||
[`WithBackend`](../backends.go); exact registration validation belongs to its
|
||||
GoDoc.
|
||||
|
||||
`extra_params` accepts null, booleans, finite numbers, strings, arrays, and
|
||||
objects with string keys. Keys must be non-empty. With the built-in client,
|
||||
@@ -226,8 +230,9 @@ invalid matching profile is an error and does not fall back. In-memory
|
||||
Every built-in selects the `openrouter` backend. The engine's built-in backend
|
||||
registry supplies `https://openrouter.ai/api/v1` and the environment-variable
|
||||
name `OPENROUTER_API_KEY`, so individual profiles contain only model and
|
||||
generation settings. A custom or in-memory profile with the same profile ID
|
||||
takes precedence.
|
||||
generation settings. Built-in profile files do not repeat those connection
|
||||
values. A custom or in-memory profile with the same profile ID takes
|
||||
precedence.
|
||||
|
||||
| Provider | ID | Model |
|
||||
| --- | --- | --- |
|
||||
|
||||
@@ -13,10 +13,15 @@ that produce these outbound settings.
|
||||
## Endpoint And Method
|
||||
|
||||
Generation sends an HTTP `POST` with `Content-Type: application/json`.
|
||||
A non-empty endpoint from the execution target overrides the client's
|
||||
configured base URL. After trailing slashes are removed,
|
||||
`/chat/completions` is appended. Generation fails before sending when neither
|
||||
source supplies an endpoint.
|
||||
Before the client is called, the engine resolves framework, backend, profile,
|
||||
and request values into one execution target. A non-empty endpoint from that
|
||||
target overrides the client's configured base URL. After trailing slashes are
|
||||
removed, `/chat/completions` is appended. Generation fails before sending when
|
||||
neither source supplies an endpoint.
|
||||
|
||||
The target's backend ID is routing metadata for prepared values, results, and
|
||||
injected clients. The built-in client does not derive the URL from that ID and
|
||||
does not serialize it in the provider request.
|
||||
|
||||
## Authentication
|
||||
|
||||
@@ -26,6 +31,12 @@ the client reads that variable and requires a non-empty value. The selected
|
||||
key is sent as `Authorization: Bearer <key>`. No authorization header is sent
|
||||
when neither mechanism is configured.
|
||||
|
||||
The target contains the already resolved environment-variable name: an
|
||||
explicit request override takes precedence over profile metadata, which takes
|
||||
precedence over the backend default. Only the name reaches prepared metadata;
|
||||
the environment value is read just before the provider call and is never added
|
||||
to the JSON body.
|
||||
|
||||
## Request Body
|
||||
|
||||
The request body always contains `model` and `messages`. The execution
|
||||
@@ -47,9 +58,11 @@ The client conditionally includes:
|
||||
- `response_format` for JSON Schema structured output, including its name,
|
||||
strict flag, and schema document.
|
||||
|
||||
Extra parameters are merged directly into the top-level body after JSON
|
||||
serialization is verified. Empty keys and collisions with these reserved
|
||||
fields are rejected before any provider call:
|
||||
The engine resolves backend, profile, and request extra-parameter maps by
|
||||
whole-map replacement rather than key merging. The resulting effective map is
|
||||
then merged directly into the top-level body after JSON serialization is
|
||||
verified. Empty keys and collisions with these reserved fields are rejected
|
||||
before any provider call:
|
||||
|
||||
- `model`
|
||||
- `session_id`
|
||||
@@ -61,6 +74,9 @@ fields are rejected before any provider call:
|
||||
- `reasoning_effort`
|
||||
- `response_format`
|
||||
|
||||
`backend_id`, `api_key_env`, and resolved credential values are not provider
|
||||
request fields.
|
||||
|
||||
## Response Handling
|
||||
|
||||
Any 2xx response is decoded as an OpenAI-compatible chat response. The client
|
||||
|
||||
@@ -20,6 +20,11 @@ orchestration. `OpenAICompatibleClient` is the built-in implementation. It
|
||||
uses internal domain values for rendered prompts, execution targets,
|
||||
structured output, responses, and token usage.
|
||||
|
||||
The runner supplies a fully resolved target after applying backend, profile,
|
||||
and request precedence. The client uses its endpoint, credential metadata,
|
||||
generation fields, and extra parameters. `BackendID` remains routing metadata
|
||||
for the generation boundary and is not mapped into the provider payload.
|
||||
|
||||
Construction validates the configured base URL and clones any supplied
|
||||
`http.Client` so Promptkit can apply its timeout default without mutating the
|
||||
caller's client. Generation then:
|
||||
@@ -51,5 +56,7 @@ The
|
||||
[OpenAI-compatible client tests](../../internal/llm/openai_compatible_client_test.go)
|
||||
own configuration, client cloning, deterministic deadline precedence,
|
||||
authentication, request and response mapping, malformed data, error identity,
|
||||
cancellation, and response-body suppression. They use local test servers and
|
||||
test transports; the default suite makes no live or paid provider requests.
|
||||
cancellation, and response-body suppression. The root transport contract test
|
||||
also verifies that resolved backend settings reach this client without
|
||||
serializing backend identity. All use local test servers or test transports;
|
||||
the default suite makes no live or paid provider requests.
|
||||
|
||||
@@ -11,21 +11,21 @@ contributor workflow and validation.
|
||||
|
||||
| Component | Implemented responsibility | References |
|
||||
| --- | --- | --- |
|
||||
| Root `promptkit` package | Provides the supported engine facade, source and injection options, public request and result values, built-in profile construction, extension interfaces, value conversion, redacted formatting, and public error mapping. | [Package GoDoc](../../doc.go), [engine assembly](../../engine.go) |
|
||||
| Root `promptkit` package | Provides the supported engine facade, source, backend-registration, and injection options, public request and result values, profile construction, extension interfaces, value conversion, redacted formatting, and public error mapping. | [Package GoDoc](../../doc.go), [backend API](../../backends.go), [engine assembly](../../engine.go) |
|
||||
| `examples/go-library/prepare` | Demonstrates an offline downstream consumer using a prompt file, in-memory profile, inline input, and `Prepare`. It is not a public library package. | [Example program](../../examples/go-library/prepare/main.go) |
|
||||
| `examples/go-library/run` | Demonstrates an offline downstream consumer using a prompt file, in-memory profile, inline input, an injected deterministic model client, and `Run`. It is not a public library package. | [Example program](../../examples/go-library/run/main.go) |
|
||||
| `internal/backend` | Validates and defensively copies immutable OpenAI-compatible backend definitions, supplies the built-in OpenRouter definition, and owns the shared reserved request-field rule. | [Backend registry](../../internal/backend/registry.go) |
|
||||
| `internal/backend` | Constructs each engine's immutable registry from the built-in OpenRouter definition and consumer additions, validates and defensively copies definitions, and owns the shared reserved request-field rule. | [Backend registry](../../internal/backend/registry.go) |
|
||||
| `internal/domain` | Defines internal framework values for requests, artifacts, prompt definitions, profiles, execution targets, rendering, generation, and validation. | [Domain declarations](../../internal/domain/domain.go) |
|
||||
| `internal/defaults` | Defines application-neutral framework constants and constructs the default execution target. It contains no CLI, server, or inbound HTTP limits. | [Framework defaults](../../internal/defaults/defaults.go) |
|
||||
| `internal/filecatalog` | Provides deterministic YAML discovery and path helpers for operating-system filesystems and `fs.FS` sources. | [File catalog](../../internal/filecatalog/catalog.go) |
|
||||
| `internal/promptdef` | Loads strictly decoded, validated prompt definitions from filesystem and `fs.FS` sources, including version selection and contained file-backed message content. | [Framework formats](../formats.md), [prompt-definition repository](../../internal/promptdef/filesystem_repository.go) |
|
||||
| `internal/profile` | Loads strictly decoded, validated execution profiles from filesystem and `fs.FS` sources and composes repositories with error-preserving fallback. | [Framework formats](../formats.md), [profile repositories](../../internal/profile/filesystem_repository.go) |
|
||||
| `internal/profile/builtin` | Embeds the built-in execution profile catalog and combines it with an optional primary repository. | [Built-in catalog](../formats.md#built-in-profile-catalog), [repository](../../internal/profile/builtin/repository.go) |
|
||||
| `internal/profile` | Loads strictly decoded, validated execution profiles, including backend selection, from filesystem and `fs.FS` sources and composes repositories with error-preserving fallback. | [Framework formats](../formats.md), [profile repositories](../../internal/profile/filesystem_repository.go) |
|
||||
| `internal/profile/builtin` | Embeds the built-in profile catalog, whose entries select OpenRouter, and combines it with an optional primary repository. | [Built-in catalog](../formats.md#built-in-profile-catalog), [repository](../../internal/profile/builtin/repository.go) |
|
||||
| `internal/prompt` | Renders prompt messages from Go templates with artifact, variable, session, and cache-control data. | [Go-template renderer](../../internal/prompt/go_renderer.go) |
|
||||
| `internal/artifact` | Resolves ordinary inline and unrestricted caller-selected file references into copied artifacts with metadata and hashes. | [Internal sources and validation](sources.md) |
|
||||
| `internal/validate` | Validates basic, JSON, and JSON Schema output using operating-system filesystem or `fs.FS` schema sources. | [Framework formats](../formats.md#schemas), [internal sources and validation](sources.md) |
|
||||
| `internal/llm` | Defines the internal generation boundary and implements outbound OpenAI-compatible chat requests, response decoding, authentication, and deadline handling. | [Internal model client](llm.md) |
|
||||
| `internal/usecase` | Coordinates preparation and execution across internal sources, rendering, artifact loading, generation, validation, and optional repair. | [Internal runner](runner.md) |
|
||||
| `internal/llm` | Defines the internal generation boundary and implements outbound OpenAI-compatible chat requests from resolved execution targets, including response decoding, authentication, and deadline handling. | [Internal model client](llm.md) |
|
||||
| `internal/usecase` | Resolves backend, profile, and request settings and coordinates preparation and execution across internal sources, rendering, artifact loading, generation, validation, and optional repair. | [Internal runner](runner.md) |
|
||||
|
||||
The root package assembles these internal components without exposing their
|
||||
representations. Consumers depend only on the root facade.
|
||||
|
||||
@@ -18,10 +18,11 @@ and override semantics consumed by the runner.
|
||||
|
||||
`Runner` coordinates narrow internal interfaces for prompt definitions,
|
||||
profiles, backend resolution, artifacts, rendering, model generation, and
|
||||
validation. The root engine supplies an immutable built-in backend registry.
|
||||
Schema documents are loaded through the validator's optional schema-loader
|
||||
interface. An output repairer can be injected internally, but the ordinary
|
||||
runner constructor does not enable one.
|
||||
validation. The root engine supplies one immutable registry containing the
|
||||
built-in backend and validated consumer additions. Schema documents are loaded
|
||||
through the validator's optional schema-loader interface. An output repairer
|
||||
can be injected internally, but the ordinary runner constructor does not
|
||||
enable one.
|
||||
|
||||
Each invocation carries its state in request, prepared-run, and result values.
|
||||
The runner has no durable run or session store.
|
||||
@@ -53,6 +54,10 @@ apply in that order. A profile requiring a direct key clears an inherited
|
||||
backend environment name unless the request supplies its own name. Secret
|
||||
values remain excluded from serialized metadata.
|
||||
|
||||
The registry is read-only after engine construction. Concurrent `Prepare` and
|
||||
`Run` calls resolve independent defensive backend values and keep all
|
||||
invocation state local.
|
||||
|
||||
## Run Flow
|
||||
|
||||
`Run` calls `Prepare` rather than maintaining a second preparation path. It
|
||||
|
||||
@@ -36,7 +36,8 @@ profile selects `openrouter` and inherits its endpoint and credential
|
||||
environment-variable name from the built-in backend registry rather than
|
||||
repeating those values. Profile behavior is owned by the
|
||||
[profile repository tests](../../internal/profile/repository_test.go), while
|
||||
catalog completeness, backend-selection invariant, duplicate IDs, and overlay behavior are owned by the
|
||||
catalog completeness, the backend-selection invariant, duplicate IDs, and
|
||||
overlay behavior are owned by the
|
||||
[built-in repository tests](../../internal/profile/builtin/repository_test.go).
|
||||
|
||||
## Ordinary Artifacts
|
||||
|
||||
@@ -47,17 +47,19 @@ The `examples/go-library/prepare` and `examples/go-library/run` packages are
|
||||
maintained downstream consumers of the root facade. They do not expose library
|
||||
packages or participate in internal assembly.
|
||||
|
||||
The root facade assembles the internal repositories, renderer, validator,
|
||||
outbound client, and use-case runner while translating public values and
|
||||
errors at the library boundary. The defaults and renderer depend on the domain
|
||||
model. Prompt-definition and profile repositories use the domain model, file
|
||||
catalog, and YAML decoder. The built-in profile repository supplies an
|
||||
embedded `fs.FS` to the profile package. Artifact reading uses the domain model
|
||||
and application-neutral defaults. Validation uses the domain model, file
|
||||
catalog, and JSON Schema implementation. The model client uses the domain
|
||||
model, application-neutral defaults, and an injected or standard-library HTTP
|
||||
client. The use-case runner depends on the narrow interfaces owned by each
|
||||
internal component.
|
||||
The root facade assembles one immutable backend registry, the internal
|
||||
repositories, renderer, validator, outbound client, and use-case runner while
|
||||
translating public values and errors at the library boundary. The registry
|
||||
contains built-ins plus validated engine-scoped consumer additions. The
|
||||
defaults and renderer depend on the domain model. Prompt-definition and
|
||||
profile repositories use the domain model, file catalog, and YAML decoder. The
|
||||
built-in profile repository supplies an embedded `fs.FS` to the profile
|
||||
package. Artifact reading uses the domain model and application-neutral
|
||||
defaults. Validation uses the domain model, file catalog, and JSON Schema
|
||||
implementation. The model client uses the domain model, application-neutral
|
||||
defaults, and an injected or standard-library HTTP client. The use-case runner
|
||||
depends on the narrow interfaces owned by each internal component, including
|
||||
backend lookup.
|
||||
|
||||
The current implementation follows this dependency direction:
|
||||
|
||||
@@ -74,11 +76,11 @@ downstream consumers, including Scriptorium
|
||||
narrow injected abstractions
|
||||
```
|
||||
|
||||
The backend registry depends on the domain model, and the model client reuses
|
||||
its OpenAI-compatible reserved request-field rule. The facade coordinates
|
||||
internal components and adapts the supported public extension interfaces to
|
||||
narrow internal abstractions. Internal components must not depend on consumers
|
||||
or on Scriptorium.
|
||||
The backend registry depends on the domain model, has no mutation API after
|
||||
construction, and shares its OpenAI-compatible reserved request-field rule
|
||||
with the model client. The facade coordinates internal components and adapts
|
||||
the supported public extension interfaces to narrow internal abstractions.
|
||||
Internal components must not depend on consumers or on Scriptorium.
|
||||
|
||||
## Repository And Consumer Boundary
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Extensible LLM Backend Registry
|
||||
|
||||
**Status:** Complete.
|
||||
|
||||
## Purpose
|
||||
|
||||
This roadmap defines the scope and target end state for formal
|
||||
|
||||
@@ -428,6 +428,8 @@ external endpoint. Keep lower-level registry validation cases in
|
||||
|
||||
## Stage 4 — Contract Hardening And Documentation Completion
|
||||
|
||||
**Status:** Complete.
|
||||
|
||||
### Goal
|
||||
|
||||
Audit the completed feature across public, profile, use-case, and transport
|
||||
|
||||
Reference in New Issue
Block a user