Document backend capacity management
This commit is contained in:
8
doc.go
8
doc.go
@@ -9,9 +9,11 @@
|
|||||||
//
|
//
|
||||||
// # Concurrency and ownership
|
// # Concurrency and ownership
|
||||||
//
|
//
|
||||||
// An Engine supports concurrent Prepare and Run calls. An injected [LLMClient]
|
// An Engine supports concurrent Prepare and Run calls. Engine-local backend
|
||||||
// or [ArtifactReader] can therefore receive concurrent calls and must be safe
|
// policies bound admitted Run calls and model generations where configured,
|
||||||
// for that use.
|
// while different backend pools and unlimited backends continue independently.
|
||||||
|
// An injected [LLMClient] or [ArtifactReader] can therefore still receive
|
||||||
|
// concurrent calls and must be safe for that use.
|
||||||
//
|
//
|
||||||
// NewEngine copies in-memory profiles and backend definitions. Prepare and Run
|
// NewEngine copies in-memory profiles and backend definitions. Prepare and Run
|
||||||
// copy request maps, slices, pointer values, and JSON-compatible extra
|
// copy request maps, slices, pointer values, and JSON-compatible extra
|
||||||
|
|||||||
@@ -127,7 +127,9 @@ exact normalization, precedence, error, copying, and exposure contract.
|
|||||||
### Register A Custom Backend
|
### Register A Custom Backend
|
||||||
|
|
||||||
Register a reusable OpenAI-compatible connection once, then select it from a
|
Register a reusable OpenAI-compatible connection once, then select it from a
|
||||||
profile:
|
profile. This local backend limits model generation to two simultaneous calls;
|
||||||
|
because `QueueCapacity` is omitted, the engine admits up to 1024 additional
|
||||||
|
calls waiting behind them:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
engine, err := promptkit.NewEngine(promptkit.Config{
|
engine, err := promptkit.NewEngine(promptkit.Config{
|
||||||
@@ -137,6 +139,7 @@ engine, err := promptkit.NewEngine(promptkit.Config{
|
|||||||
ID: "local",
|
ID: "local",
|
||||||
Endpoint: "http://localhost:8000/v1",
|
Endpoint: "http://localhost:8000/v1",
|
||||||
APIKeyEnv: "LOCAL_LLM_API_KEY",
|
APIKeyEnv: "LOCAL_LLM_API_KEY",
|
||||||
|
ConcurrencyLimit: 2,
|
||||||
}),
|
}),
|
||||||
promptkit.WithProfiles(promptkit.Profile{
|
promptkit.WithProfiles(promptkit.Profile{
|
||||||
ID: "local-summary",
|
ID: "local-summary",
|
||||||
@@ -148,15 +151,42 @@ engine, err := promptkit.NewEngine(promptkit.Config{
|
|||||||
|
|
||||||
Registrations belong to one engine and custom IDs cannot replace built-ins.
|
Registrations belong to one engine and custom IDs cannot replace built-ins.
|
||||||
The [`Backend` and `WithBackend` GoDoc](../../backends.go) defines validation,
|
The [`Backend` and `WithBackend` GoDoc](../../backends.go) defines validation,
|
||||||
copying, uniqueness, and request-default behavior.
|
copying, uniqueness, exact concurrency-field semantics, and request-default
|
||||||
|
behavior.
|
||||||
|
|
||||||
Both file-backed and in-memory profiles select a registration through
|
Both file-backed and in-memory profiles select a registration through
|
||||||
`backend` or `Profile.BackendID`. Profile and request endpoint overrides retain
|
`backend` or `Profile.BackendID`. Profile and request endpoint overrides retain
|
||||||
that routing identity. `PreparedRun.SelectedBackendID`,
|
that routing and capacity identity. `PreparedRun.SelectedBackendID`,
|
||||||
`RunResult.SelectedBackendID`, and the effective `ExecutionTarget.BackendID`
|
`RunResult.SelectedBackendID`, and the effective `ExecutionTarget.BackendID`
|
||||||
expose it to consumers and injected model clients. Endpoint-only profiles
|
expose it to consumers and injected model clients. Endpoint-only profiles
|
||||||
remain supported and expose an empty backend ID.
|
remain supported and expose an empty backend ID.
|
||||||
|
|
||||||
|
### Limit Backend Concurrency
|
||||||
|
|
||||||
|
Set `Backend.ConcurrencyLimit` when a backend needs protection from too many
|
||||||
|
simultaneous model calls. Leaving `QueueCapacity` nil, as in the local-backend
|
||||||
|
example above, selects the default waiting capacity of 1024.
|
||||||
|
|
||||||
|
To accept no waiting backlog beyond the active calls, provide an explicit
|
||||||
|
zero:
|
||||||
|
|
||||||
|
```go
|
||||||
|
noWaiting := 0
|
||||||
|
backend := promptkit.Backend{
|
||||||
|
ID: "local",
|
||||||
|
Endpoint: "http://localhost:8000/v1",
|
||||||
|
ConcurrencyLimit: 2,
|
||||||
|
QueueCapacity: &noWaiting,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The pointer distinguishes an explicit zero from omission. Keep using keyed
|
||||||
|
`Backend` literals so additive configuration fields remain source-compatible.
|
||||||
|
Capacity belongs to one engine and the selected backend ID; endpoint-only
|
||||||
|
profiles and custom backends without a configured limit remain unrestricted.
|
||||||
|
Exact validation, defaulting, ownership, and concurrency semantics belong to
|
||||||
|
the [`Backend` GoDoc](../../backends.go).
|
||||||
|
|
||||||
## Credentials
|
## Credentials
|
||||||
|
|
||||||
File-backed profiles name an environment variable; in-memory profiles can
|
File-backed profiles name an environment variable; in-memory profiles can
|
||||||
@@ -200,6 +230,22 @@ failures. Specific request conditions may also match the broader
|
|||||||
documented. Invalid or duplicate backend registrations match
|
documented. Invalid or duplicate backend registrations match
|
||||||
`ErrInvalidConfig`; selecting an unknown backend matches `ErrProfileLoad`.
|
`ErrInvalidConfig`; selecting an unknown backend matches `ErrProfileLoad`.
|
||||||
|
|
||||||
|
When a limited backend has admitted all active and waiting calls, handle
|
||||||
|
`ErrCapacityExceeded` separately from request errors and provider failures:
|
||||||
|
|
||||||
|
```go
|
||||||
|
result, err := engine.Run(ctx, request)
|
||||||
|
if errors.Is(err, promptkit.ErrCapacityExceeded) {
|
||||||
|
// Apply application policy: shed work, report overload, or retry later.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A rejected call returns no partial result and does not invoke the model
|
||||||
|
client. Promptkit does not prescribe retries or map this error to an HTTP
|
||||||
|
status; those choices remain with the consuming application. The
|
||||||
|
[`Engine.Run` and error GoDoc](../../engine.go) owns exact error and
|
||||||
|
cancellation identities.
|
||||||
|
|
||||||
## Application Boundary
|
## Application Boundary
|
||||||
|
|
||||||
Promptkit is an importable library. It does not own a command, inbound HTTP
|
Promptkit is an importable library. It does not own a command, inbound HTTP
|
||||||
|
|||||||
@@ -207,7 +207,10 @@ Non-empty profile strings replace backend defaults, and non-empty request
|
|||||||
strings replace both. Request reasoning is the exception: a nil
|
strings replace both. Request reasoning is the exception: a nil
|
||||||
`ReasoningEffort` pointer inherits the profile, a pointer to a nonblank string
|
`ReasoningEffort` pointer inherits the profile, a pointer to a nonblank string
|
||||||
trims and replaces it, and a pointer to a blank string clears it. Backend
|
trims and replaces it, and a pointer to a blank string clears it. Backend
|
||||||
identity is retained when either layer overrides the endpoint. A non-empty
|
identity is retained when either layer overrides the endpoint, so the override
|
||||||
|
also retains any engine-local capacity policy configured for that backend.
|
||||||
|
Capacity configuration belongs to the Go
|
||||||
|
[`Backend` API](../backends.go), not prompt or profile YAML. A non-empty
|
||||||
`extra_params` map at each layer replaces the entire lower-precedence map
|
`extra_params` map at each layer replaces the entire lower-precedence map
|
||||||
rather than merging keys.
|
rather than merging keys.
|
||||||
The [outbound integration contract](integrations/openai-compatible-chat.md)
|
The [outbound integration contract](integrations/openai-compatible-chat.md)
|
||||||
|
|||||||
97
docs/internal/capacity.md
Normal file
97
docs/internal/capacity.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Internal Capacity Management
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This document describes the implemented engine-local capacity coordination in
|
||||||
|
`internal/capacity`. The [architecture policy](../policy/architecture.md) owns
|
||||||
|
component boundaries, the [backend GoDoc](../../backends.go) owns exact public
|
||||||
|
configuration semantics, and the
|
||||||
|
[internal runner document](runner.md) owns orchestration around admission.
|
||||||
|
|
||||||
|
Capacity scheduling is outside the provider wire contract. It does not add
|
||||||
|
fields to execution targets, generated requests, prompt or profile YAML, or
|
||||||
|
stable JSON values.
|
||||||
|
|
||||||
|
## Construction And Pool Lifecycle
|
||||||
|
|
||||||
|
Each root `NewEngine` call obtains a normalized capacity-policy snapshot from
|
||||||
|
its immutable backend registry and constructs a new `Manager`. The manager
|
||||||
|
creates one pool for each limited backend ID. It has no package-global mutable
|
||||||
|
state, background workers, shutdown protocol, or persistence, so engines with
|
||||||
|
the same registrations still have independent capacity.
|
||||||
|
|
||||||
|
Unlimited registered backends and endpoint-only profiles have no pool. Their
|
||||||
|
admission and generation calls take the unrestricted fast path. An endpoint
|
||||||
|
override does not change the selected backend ID and therefore does not change
|
||||||
|
the pool.
|
||||||
|
|
||||||
|
One pool owns immutable active and total limits plus mutex-protected admission
|
||||||
|
count, active count, and ordered waiter list. Pool state exists only for the
|
||||||
|
lifetime of its engine.
|
||||||
|
|
||||||
|
## Bounded Run Admission
|
||||||
|
|
||||||
|
The runner asks the manager to admit a run after resolving the prompt, profile,
|
||||||
|
selected backend, effective execution target, credentials, and output contract,
|
||||||
|
but before schema loading, artifact loading, or rendering. Admission is
|
||||||
|
immediate: a limited pool either reserves a slot or returns the internal
|
||||||
|
`ErrCapacityExceeded` identity. The root facade maps that identity to the
|
||||||
|
public error without treating it as an invalid request or generation failure.
|
||||||
|
|
||||||
|
The total admitted bound is the active-generation limit plus its configured
|
||||||
|
waiting capacity. The returned release function is idempotent. The runner
|
||||||
|
defers it as soon as admission succeeds and holds the lease across remaining
|
||||||
|
preparation, initial generation, validation, every repair attempt, and all
|
||||||
|
failure or cancellation exits. A repair is part of its original admission and
|
||||||
|
does not reserve another bounded slot.
|
||||||
|
|
||||||
|
## FIFO Generation Permits
|
||||||
|
|
||||||
|
`NewClient` wraps the engine's selected internal model client after public
|
||||||
|
client adaptation or built-in client construction. Initial generation and the
|
||||||
|
default repairer receive the same wrapper.
|
||||||
|
|
||||||
|
For each `Generate` call, the wrapper selects a pool from the request's
|
||||||
|
effective backend ID. An unlimited call passes directly to the next client. A
|
||||||
|
limited call acquires an active permit, invokes the next client, and defers
|
||||||
|
permit release so ordinary returns and panic unwinding both restore capacity.
|
||||||
|
Preparation and validation never hold an active permit.
|
||||||
|
|
||||||
|
When all active permits are occupied, calls join a mutex-protected FIFO waiter
|
||||||
|
list. Releasing a permit transfers it directly to the oldest remaining waiter
|
||||||
|
before making it generally available. Pools do not order work relative to
|
||||||
|
other backend IDs.
|
||||||
|
|
||||||
|
The wrapper passes generation requests, responses, and collaborator errors
|
||||||
|
through unchanged. It owns scheduling only; the concrete model client remains
|
||||||
|
responsible for provider transport behavior.
|
||||||
|
|
||||||
|
## Cancellation And Release
|
||||||
|
|
||||||
|
Admission checks the caller context before reserving a slot. A call canceled
|
||||||
|
while waiting for an active permit removes its waiter under the same pool lock
|
||||||
|
used to grant permits. If cancellation removes the waiter first, the wrapped
|
||||||
|
client is not invoked. If a concurrent grant wins first, the call owns the
|
||||||
|
permit and invokes the client with the original context, allowing the client
|
||||||
|
to observe cancellation normally.
|
||||||
|
|
||||||
|
This grant-or-cancel decision prevents lost and double-released permits.
|
||||||
|
Admission leases and active permits are released after success, collaborator
|
||||||
|
errors, validation failures, cancellation, and panic unwinding. Canceled
|
||||||
|
waiters are unlinked so their contexts and requests are not retained by the
|
||||||
|
pool.
|
||||||
|
|
||||||
|
## Test Ownership
|
||||||
|
|
||||||
|
The [manager tests](../../internal/capacity/manager_test.go) own policy
|
||||||
|
validation, bounded admission, idempotent release, context handling, and
|
||||||
|
unlimited admission. The
|
||||||
|
[client tests](../../internal/capacity/client_test.go) own peak enforcement,
|
||||||
|
FIFO transfer, canceled-waiter removal, grant/cancel races, independent pools,
|
||||||
|
unlimited calls, passthrough behavior, and panic release.
|
||||||
|
|
||||||
|
The [runner tests](../../internal/usecase/runner_test.go) own early admission,
|
||||||
|
lease lifetime, failure release, and shared initial/repair scheduling. The
|
||||||
|
[external package capacity tests](../../capacity_contract_test.go) own the
|
||||||
|
assembled public-engine behavior for configured limits, capacity errors,
|
||||||
|
endpoint identity, engine independence, cancellation, and injected clients.
|
||||||
@@ -11,10 +11,11 @@ contributor workflow and validation.
|
|||||||
|
|
||||||
| Component | Implemented responsibility | References |
|
| Component | Implemented responsibility | References |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 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) |
|
| 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, public error mapping, and engine-local assembly. | [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/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) |
|
| `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` | Constructs each engine's immutable registry from the built-in OpenRouter definition and consumer additions, validates and defensively copies definitions through the shared JSON-value package, and consumes the LLM-owned OpenAI-compatible 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 through the shared JSON-value package, and consumes the LLM-owned OpenAI-compatible reserved request-field rule. | [Backend registry](../../internal/backend/registry.go) |
|
||||||
|
| `internal/capacity` | Owns engine-local bounded run admission and FIFO model-generation permits for limited backend IDs, including cancellation-safe waiter removal and client wrapping. | [Internal capacity management](capacity.md) |
|
||||||
| `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/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/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/filecatalog` | Provides deterministic YAML discovery and path helpers for operating-system filesystems and `fs.FS` sources. | [File catalog](../../internal/filecatalog/catalog.go) |
|
||||||
|
|||||||
@@ -19,17 +19,20 @@ and override semantics consumed by the runner.
|
|||||||
`Runner` coordinates narrow internal interfaces for prompt definitions,
|
`Runner` coordinates narrow internal interfaces for prompt definitions,
|
||||||
profiles, backend resolution, artifacts, rendering, model generation, and
|
profiles, backend resolution, artifacts, rendering, model generation, and
|
||||||
validation. The root engine supplies one immutable registry containing the
|
validation. The root engine supplies one immutable registry containing the
|
||||||
built-in backend and validated consumer additions. Schema documents are loaded
|
built-in backend and validated consumer additions, one engine-local run
|
||||||
through the validator's optional schema-loader interface. An output repairer
|
admitter, and a model client wrapped by the same capacity manager. Schema
|
||||||
can be injected internally, but the ordinary runner constructor does not
|
documents are loaded through the validator's optional schema-loader interface.
|
||||||
enable one.
|
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.
|
Each invocation carries its state in request, prepared-run, and result values.
|
||||||
The runner has no durable run or session store.
|
The runner has no durable run or session store.
|
||||||
|
|
||||||
## Preparation Flow
|
## Shared Preparation Pipeline
|
||||||
|
|
||||||
`Prepare` performs the reusable pre-generation workflow:
|
`Prepare` and `Run` share one private preparation pipeline split at the point
|
||||||
|
where a run can be assigned to its selected backend pool. The resolution phase
|
||||||
|
performs only the work needed to validate routing and admission:
|
||||||
|
|
||||||
1. validate the required prompt selection and normalize any direct session ID;
|
1. validate the required prompt selection and normalize any direct session ID;
|
||||||
2. load the prompt definition and hash the original definition;
|
2. load the prompt definition and hash the original definition;
|
||||||
@@ -38,13 +41,25 @@ The runner has no durable run or session store.
|
|||||||
5. resolve application-neutral defaults, backend defaults, profile values,
|
5. resolve application-neutral defaults, backend defaults, profile values,
|
||||||
and explicit request overrides in that order;
|
and explicit request overrides in that order;
|
||||||
6. validate endpoint, model, numeric overrides, and credential requirements;
|
6. validate endpoint, model, numeric overrides, and credential requirements;
|
||||||
7. resolve the output contract and load a structured-output schema when
|
7. resolve the effective output contract without loading its schema; and
|
||||||
required;
|
8. retain the definition, source identities, effective settings, output
|
||||||
8. load and hash input artifacts;
|
contract, and preparation start time in invocation-local state.
|
||||||
9. render messages, resolve the effective session ID, and hash the effective
|
|
||||||
rendered prompt; and
|
The completion phase consumes that state without reloading the prompt,
|
||||||
10. return the effective settings, source identities, messages, hashes, and
|
profile, or backend:
|
||||||
preparation timing.
|
|
||||||
|
1. load structured-output schema metadata when required;
|
||||||
|
2. load and hash input artifacts;
|
||||||
|
3. render messages and the prompt-defined session;
|
||||||
|
4. apply any direct session ID;
|
||||||
|
5. hash the effective rendered prompt; and
|
||||||
|
6. construct the prepared value and preparation timing.
|
||||||
|
|
||||||
|
`Prepare` runs both phases consecutively and never performs capacity admission.
|
||||||
|
`Run` performs backend admission between the phases. This structure preserves
|
||||||
|
one execution-precedence and error-ordering implementation while allowing a
|
||||||
|
full backend pool to reject work before expensive schema, artifact, and
|
||||||
|
rendering operations.
|
||||||
|
|
||||||
Pointer-based numeric overrides preserve an explicit zero. Invalid negative or
|
Pointer-based numeric overrides preserve an explicit zero. Invalid negative or
|
||||||
out-of-range values fail as invalid requests. Endpoint overrides do not change
|
out-of-range values fail as invalid requests. Endpoint overrides do not change
|
||||||
@@ -70,15 +85,28 @@ invocation state local.
|
|||||||
|
|
||||||
## Run Flow
|
## Run Flow
|
||||||
|
|
||||||
`Run` calls `Prepare` rather than maintaining a second preparation path. It
|
`Run` records its start time, performs the shared resolution phase, and asks
|
||||||
performs one initial generation call, builds the named output artifact, and
|
its `RunAdmitter` to reserve capacity for the effective backend ID. A nil
|
||||||
validates that artifact. Invalid generated content remains a validation result;
|
admitter is an internal unlimited fallback. After successful admission, `Run`
|
||||||
an inability to perform validation is an operational error.
|
immediately defers the returned release function, performs the completion
|
||||||
|
phase, makes one initial generation call, builds the named output artifact,
|
||||||
|
and validates that artifact. Invalid generated content remains a validation
|
||||||
|
result; an inability to perform validation is an operational error.
|
||||||
|
|
||||||
|
The admission lease covers completion-phase preparation, initial generation,
|
||||||
|
validation, every repair, and every exit. It bounds accepted work without
|
||||||
|
serializing preparation or validation behind the active-generation limit.
|
||||||
|
The wrapped model client separately acquires a FIFO active permit only around
|
||||||
|
each actual generation call.
|
||||||
|
|
||||||
When an internal repairer is present, a JSON or JSON Schema content failure can
|
When an internal repairer is present, a JSON or JSON Schema content failure can
|
||||||
trigger bounded repair attempts. Repair receives the effective execution
|
trigger bounded repair attempts. Repair receives the effective execution
|
||||||
target and session ID, validation errors, prior output, and structured-output
|
target and session ID, validation errors, prior output, and structured-output
|
||||||
specification. This capability remains internal and is not a public option.
|
specification. The default repairer uses the same wrapped client as initial
|
||||||
|
generation, so each repair reacquires the selected backend's active permit
|
||||||
|
while remaining inside its original admission lease. Repair never performs a
|
||||||
|
second bounded admission. This capability remains internal and is not a public
|
||||||
|
option.
|
||||||
|
|
||||||
A successful result includes the output artifact and raw output, validation
|
A successful result includes the output artifact and raw output, validation
|
||||||
state, effective session ID, prompt and rendered-prompt hashes, selected
|
state, effective session ID, prompt and rendered-prompt hashes, selected
|
||||||
@@ -94,8 +122,20 @@ Package errors distinguish invalid requests, required profile selection,
|
|||||||
credential failures, and prompt, profile, artifact, rendering, generation, and
|
credential failures, and prompt, profile, artifact, rendering, generation, and
|
||||||
validation failures. Wrapping preserves the package identities mapped by the
|
validation failures. Wrapping preserves the package identities mapped by the
|
||||||
public facade and retains collaborator identities where they are part of the
|
public facade and retains collaborator identities where they are part of the
|
||||||
internal contract. Context cancellation propagates through the invoked
|
internal contract.
|
||||||
collaborator and is classified by the owning operation.
|
|
||||||
|
Admission capacity exhaustion retains the internal capacity identity and adds
|
||||||
|
the selected backend ID as context. It is not recategorized as an invalid
|
||||||
|
request or generation failure, and no partial result is returned. A context
|
||||||
|
already done at admission retains its context identity directly. Cancellation
|
||||||
|
while waiting for an active generation permit prevents client invocation when
|
||||||
|
it wins the grant race; the model-client boundary then preserves the context
|
||||||
|
error through the generation-failure category. Deferred release restores the
|
||||||
|
admission lease on preparation, generation, validation, repair, and
|
||||||
|
cancellation failures.
|
||||||
|
|
||||||
|
Other context cancellation propagates through the invoked collaborator and is
|
||||||
|
classified by the owning operation.
|
||||||
An overlong direct session is an invalid request before source loading, while
|
An overlong direct session is an invalid request before source loading, while
|
||||||
an invalid or overlong prompt session template remains a prompt-render failure.
|
an invalid or overlong prompt session template remains a prompt-render failure.
|
||||||
An unknown selected backend, or a selected backend with no configured resolver,
|
An unknown selected backend, or a selected backend with no configured resolver,
|
||||||
@@ -104,12 +144,16 @@ is classified as a profile-load failure.
|
|||||||
## Test Ownership And Changes
|
## Test Ownership And Changes
|
||||||
|
|
||||||
The [runner tests](../../internal/usecase/runner_test.go) own preparation order,
|
The [runner tests](../../internal/usecase/runner_test.go) own preparation order,
|
||||||
selection and override precedence, direct-session resolution,
|
selection and override precedence, the two-phase boundary, early admission,
|
||||||
schema-before-generation behavior, hashing, generation and validation
|
lease lifetime and release, direct-session resolution, schema-before-generation
|
||||||
outcomes, backend propagation, bounded repair, credentials and redaction,
|
behavior, hashing, generation and validation outcomes, backend propagation,
|
||||||
error categories, artifact metadata, usage, and timing.
|
bounded repair, shared initial/repair capacity, credentials and redaction,
|
||||||
|
error categories, artifact metadata, usage, and timing. The
|
||||||
|
[capacity subsystem document](capacity.md) identifies the focused pool,
|
||||||
|
waiter, and wrapped-client tests.
|
||||||
|
|
||||||
Changes to orchestration should continue to use the existing package
|
Changes to orchestration should continue to use the existing package
|
||||||
interfaces, keep request state local to an invocation, and preserve `Run`'s use
|
interfaces, keep request state local to an invocation, and preserve the shared
|
||||||
of `Prepare`. Source, renderer, validator, or model-client contract changes
|
resolution and completion pipeline. Source, renderer, validator, or
|
||||||
belong first in their owning package and document.
|
model-client contract changes belong first in their owning package and
|
||||||
|
document.
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ The implemented internal components consist of:
|
|||||||
components;
|
components;
|
||||||
- `internal/backend`, which owns validated immutable OpenAI-compatible backend
|
- `internal/backend`, which owns validated immutable OpenAI-compatible backend
|
||||||
definitions and the built-in OpenRouter definition;
|
definitions and the built-in OpenRouter definition;
|
||||||
|
- `internal/capacity`, which owns engine-local bounded run admission and
|
||||||
|
model-generation scheduling for limited backends;
|
||||||
- `internal/defaults`, which owns application-neutral framework defaults and
|
- `internal/defaults`, which owns application-neutral framework defaults and
|
||||||
constructs the default execution target;
|
constructs the default execution target;
|
||||||
- `internal/filecatalog`, which discovers YAML files and provides source-path
|
- `internal/filecatalog`, which discovers YAML files and provides source-path
|
||||||
@@ -49,19 +51,21 @@ The `examples/go-library/prepare` and `examples/go-library/run` packages are
|
|||||||
maintained downstream consumers of the root facade. They do not expose library
|
maintained downstream consumers of the root facade. They do not expose library
|
||||||
packages or participate in internal assembly.
|
packages or participate in internal assembly.
|
||||||
|
|
||||||
The root facade assembles one immutable backend registry, the internal
|
The root facade assembles one immutable backend registry, one capacity manager,
|
||||||
repositories, renderer, validator, outbound client, and use-case runner while
|
the internal repositories, renderer, validator, outbound client, and use-case
|
||||||
translating public values and errors at the library boundary. The registry
|
runner while translating public values and errors at the library boundary. The
|
||||||
contains built-ins plus validated engine-scoped consumer additions. The
|
registry contains built-ins plus validated engine-scoped consumer additions.
|
||||||
defaults and renderer depend on the domain model. Prompt-definition and
|
The facade constructs the capacity manager from the registry's immutable
|
||||||
profile repositories use the domain model, file catalog, and YAML decoder. The
|
policy snapshot, wraps the selected built-in or injected model client, and
|
||||||
built-in profile repository supplies an embedded `fs.FS` to the profile
|
supplies bounded admission to the runner. The defaults and renderer depend on
|
||||||
package. Artifact reading uses the domain model and application-neutral
|
the domain model. Prompt-definition and profile repositories use the domain
|
||||||
defaults. Validation uses the domain model, file catalog, and JSON Schema
|
model, file catalog, and YAML decoder. The built-in profile repository supplies
|
||||||
implementation. The model client uses the domain model, application-neutral
|
an embedded `fs.FS` to the profile package. Artifact reading uses the domain
|
||||||
defaults, and an injected or standard-library HTTP client. The use-case runner
|
model and application-neutral defaults. Validation uses the domain model, file
|
||||||
depends on the narrow interfaces owned by each internal component, including
|
catalog, and JSON Schema implementation. The model client uses the domain
|
||||||
backend lookup.
|
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 and run admission.
|
||||||
|
|
||||||
The current implementation follows this dependency direction:
|
The current implementation follows this dependency direction:
|
||||||
|
|
||||||
@@ -81,10 +85,12 @@ downstream consumers, including Scriptorium
|
|||||||
The backend registry depends on the domain model and shared JSON-value
|
The backend registry depends on the domain model and shared JSON-value
|
||||||
validation, has no mutation API after construction, and consumes the
|
validation, has no mutation API after construction, and consumes the
|
||||||
OpenAI-compatible reserved request-field rule owned by the model client. The
|
OpenAI-compatible reserved request-field rule owned by the model client. The
|
||||||
model client does not depend on registry configuration. The facade coordinates
|
capacity component depends on the domain model and the narrow internal
|
||||||
internal components and adapts
|
model-client boundary, not on provider transport implementation. The model
|
||||||
the supported public extension interfaces to narrow internal abstractions.
|
client does not depend on registry or capacity configuration. The facade
|
||||||
Internal components must not depend on consumers or on Scriptorium.
|
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
|
## Repository And Consumer Boundary
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Backend-Specific Concurrency Management
|
# Backend-Specific Concurrency Management
|
||||||
|
|
||||||
**Status:** Accepted.
|
**Status:** Complete.
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Backend-Specific Concurrency Management Implementation Plan
|
# Backend-Specific Concurrency Management Implementation Plan
|
||||||
|
|
||||||
**Status:** Ready for implementation.
|
**Status:** Complete.
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
@@ -647,7 +647,7 @@ releases admission, and existing preparation semantics remain unchanged.
|
|||||||
|
|
||||||
## Stage 4 — Engine Assembly And Public Runtime Contract
|
## Stage 4 — Engine Assembly And Public Runtime Contract
|
||||||
|
|
||||||
**Status:** Pending.
|
**Status:** Complete.
|
||||||
|
|
||||||
### Goal
|
### Goal
|
||||||
|
|
||||||
@@ -721,7 +721,7 @@ through the same wrapper.
|
|||||||
|
|
||||||
## Stage 5 — Durable Documentation And Final Validation
|
## Stage 5 — Durable Documentation And Final Validation
|
||||||
|
|
||||||
**Status:** Pending.
|
**Status:** Complete.
|
||||||
|
|
||||||
### Goal
|
### Goal
|
||||||
|
|
||||||
@@ -819,6 +819,19 @@ limits and cancellation safety, durable contracts no longer depend on roadmap
|
|||||||
prose, and the OpenRouter compatibility change is clearly reported for the
|
prose, and the OpenRouter compatibility change is clearly reported for the
|
||||||
next minor release.
|
next minor release.
|
||||||
|
|
||||||
|
## Implementation Handoff
|
||||||
|
|
||||||
|
Backend-specific capacity management is implemented and has passed the complete
|
||||||
|
repository validation sequence. The built-in OpenRouter backend now permits 16
|
||||||
|
active generations and a waiting capacity of 1024. Custom backends remain
|
||||||
|
unlimited when their limit is omitted, and endpoint-only profiles remain
|
||||||
|
unlimited.
|
||||||
|
|
||||||
|
Publishing this behavior requires a pre-`v1` minor release. Its release notes
|
||||||
|
must identify that unusually high concurrent OpenRouter use can now wait or
|
||||||
|
return `ErrCapacityExceeded`. This implementation does not change a module
|
||||||
|
version or create a tag.
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
None. The feature roadmap and this plan fix the public representation,
|
None. The feature roadmap and this plan fix the public representation,
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ var (
|
|||||||
ErrPromptRender = errors.New("failed to render prompt")
|
ErrPromptRender = errors.New("failed to render prompt")
|
||||||
// ErrCapacityExceeded identifies a Run rejected because the selected backend
|
// ErrCapacityExceeded identifies a Run rejected because the selected backend
|
||||||
// already admitted ConcurrencyLimit + QueueCapacity calls. It is not an
|
// already admitted ConcurrencyLimit + QueueCapacity calls. It is not an
|
||||||
// invalid request, provider rate limit, or ErrLLMGenerate.
|
// invalid request, an LLM or provider rate-limit response, or ErrLLMGenerate.
|
||||||
ErrCapacityExceeded = errors.New("backend capacity exceeded")
|
ErrCapacityExceeded = errors.New("backend capacity exceeded")
|
||||||
// ErrLLMGenerate identifies a model-client failure or a nil successful
|
// ErrLLMGenerate identifies a model-client failure or a nil successful
|
||||||
// response. Errors returned by an injected LLMClient remain available
|
// response. Errors returned by an injected LLMClient remain available
|
||||||
|
|||||||
Reference in New Issue
Block a user