MediaRouter / docs /generation-providers.md
basyx's picture
Upload 794 files
1b2323a verified
|
Raw
History Blame Contribute Delete
10.8 kB
# Generation provider runtime
## Status
This document describes the provider-neutral runtime layer used by the
generation foundation. It has two concrete optional adapters: the audited WAN
2.2 worker and FLUX.2 Klein worker. See
[generation-wan.md](generation-wan.md) and
[generation-flux.md](generation-flux.md) for their strict contracts. Other
models remain unregistered and unconfigured.
## Architecture
```
trusted server configuration
|
GenerationProviderAdapter
|
RemoteWorkerClient
|
configured remote worker
|
GenerationOutputIngestor
|
CleanupService -> CanonicalAssetService -> workspace-owned media asset
```
`GenerationService` remains the only application-facing orchestration layer.
It retains authorization, idempotency, request/job ownership, and state
transitions. REST, MCP, SDK, n8n, and browser clients cannot provide a worker
URL, bearer token, worker job ID, output URL, or filesystem path.
## Provider contract
Concrete adapters subclass `GenerationProviderAdapter` and implement the
following asynchronous operations:
- `info()` — non-secret worker and model discovery metadata.
- `health()` — liveness only.
- `ready()` — inference readiness and loaded model IDs.
- `validate_request()` — strict typed provider input validation.
- `submit()` — worker submission. An adapter may use an idempotency protocol
only when its audited worker actually supports one; otherwise it must make
lost-response ambiguity fail safely without blind resubmission.
- `get_job()` — provider-job polling/reconciliation.
- `cancel()` — cancellation result (`requested`, `cancelled`, `unsupported`,
or `failed`).
- `retrieve_output()` and `stream_output()` — a validated output descriptor
and scoped byte stream.
- `normalize_error()` and `close()` — safe error normalization and client
lifecycle cleanup.
An adapter must not expose arbitrary provider payloads. Its capabilities and
typed request schema define the complete public contract. It must use the
existing `GenerationService`, `GenerationRepository`, canonical-asset service,
and generation state machine; it must not write generation tables or files
directly.
## Worker HTTP client
`RemoteWorkerClient` accepts a base URL and optional bearer token only from
trusted server configuration used by a future adapter. It provides fixed
worker endpoints:
- `GET /health`
- `GET /ready`
- `GET /v1/info`
- `POST /v1/generate`
- `GET /v1/jobs/{worker_job_id}`
- `POST /v1/jobs/{worker_job_id}/cancel`
- a worker-relative output download path supplied by a validated output
descriptor
The client disables redirects and proxy environment settings, applies connect,
request, and read timeouts, closes owned HTTP connections during application
shutdown, and does not log request or response bodies. Bearer tokens and
`Authorization` headers are never included in exceptions, persistence, audit
records, or API responses. Metadata is bounded to JSON-safe values and removes
credential-like fields, bearer strings, and HTTP(S) URLs so signed download or
upload links cannot enter job records.
The worker URL is validated as an absolute HTTP(S) URL. Public workers require
HTTPS; literal non-public IP addresses are rejected, while loopback HTTP is
allowed only for an operator-configured local development worker. Clients never
control the hostname. Output fetches accept only a strict relative path under
that configured origin: absolute URLs, redirects, query strings, fragments,
backslashes, and traversal segments are rejected.
DNS ownership remains an operator responsibility: production worker hostnames
must be controlled by the deployment and must not resolve to untrusted internal
services. A future deployment should also enforce its egress allowlist at the
network layer.
## Worker metadata and model discovery
`WorkerInfo` represents worker identity plus a list of discovered models.
`WorkerModelInfo` records a model ID, display name, and media types. Legacy
single-model `/v1/info` responses (`id`, `name`, and `type`/`media_types`) are
normalized into a one-item discovery list; multi-model workers can return a
`models` list. A compact single-model worker can instead return a safe map of
named underlying variants; it remains one discovered top-level model and its
adapter verifies the map where required.
Health is not model availability. A model is advertised only when all of the
following are true:
1. a server-owned `GenerationModelRegistration` exists;
2. its provider adapter is configured and available;
3. `/health` reports `healthy`;
4. `/v1/info` discovers the exact model ID and output modality;
5. `/ready` reports `ready`, `model_loaded: true`, and the exact model ID.
The `GenerationModelRegistry` stores provider ID, model capability,
non-secret configuration reference, safe metadata, and derived availability.
Every registration starts unavailable. Optional WAN and FLUX registrations
remain unavailable until their configured worker passes health, readiness, and
exact-identity checks.
Public provider discovery follows the same rule: a configured adapter is not
reported as available until at least one of its registered models has passed
those checks. This prevents a URL/token configuration from being mistaken for
a ready, authorized model.
## Health and readiness states
Worker liveness is normalized to `healthy`, `starting`, `unavailable`,
`unhealthy`, or `unknown`. Readiness is normalized to `ready`, `starting`,
`unavailable`, or `unknown`. A liveness response alone can never enable a
model. A failed runtime refresh marks that provider's registered models
unavailable instead of retaining stale availability.
## Error model and retries
Remote failures are converted to safe categories:
- `invalid_request`
- `authentication_error`
- `authorization_error`
- `worker_unavailable`
- `worker_not_ready`
- `timeout`
- `rate_limited`
- `provider_error`
- `inference_error`
- `output_error`
- `cancellation_error`
- `unknown_error`
The dedicated `GenerationRetryPolicy` is transport-level only; it does not
create jobs or attempts and therefore cannot conflict with the durable job
state machine. It allows bounded exponential-backoff retries for connection
failures, timeouts, `429`, `502`, `503`, and `504`, when the operation is
idempotent. Submissions carry MediaRouter's idempotency key before the client
will retry them. It does not retry validation failures, `400`, `401`, `403`,
invalid worker-job `404`s, generic `500`s, or programming/unknown exceptions.
The generic settings are optional and do not enable a worker:
```dotenv
AI_WORKER_CONNECT_TIMEOUT_SECONDS=10
AI_WORKER_REQUEST_TIMEOUT_SECONDS=60
AI_WORKER_READ_TIMEOUT_SECONDS=300
AI_WORKER_MAX_RETRIES=3
AI_WORKER_RETRY_BACKOFF_SECONDS=0.5
```
No provider URL, token, model, or credential is configured by these settings.
## Provider-job ownership and cancellation
When the trusted generation dispatcher receives a worker job ID, it must call
`GenerationService.bind_provider_job`. The repository checks the current
workspace and provider and the database enforces a unique `(provider,
external_job_id)` binding. That prevents polling, output ingestion, or
cancellation for one workspace from being attached to another workspace's job.
PostgreSQL startup verifies the required uniqueness index, so deployments that
have not applied `0004_generation_provider_runtime_postgres.sql` fail before
they can process generation work.
Cancellation first resolves the tenant-owned job. Queued/retrying work is
cancelled locally. For an active bound worker job, `GenerationService` calls
the adapter. A worker result of:
- `cancelled` transitions the MediaRouter job to `cancelled`;
- `requested` transitions it to `cancel_requested` only;
- `unsupported` returns a capability error without changing the active job;
- `failed` returns a safe cancellation error without claiming success.
A successful empty `204` cancellation response means only `requested`, not
`cancelled`. This prevents a worker that is still using GPU time from being
reported as stopped.
## Output handling
Workers return a `WorkerOutput` descriptor with modality, MIME type, opaque
provider output ID, worker-relative download path, optional checksum/size, and
safe metadata. Filesystem paths and absolute output URLs are rejected.
`GenerationService.ingest_completed_provider_output` is an internal dispatcher
hook. It verifies the tenant-owned job, its bound provider, its exact worker
job ID, and a completed worker status. `GenerationOutputIngestor` then:
1. streams bytes into a controlled temporary request workspace;
2. enforces the configured maximum output size and non-empty output;
3. verifies the optional SHA-256 and byte count;
4. publishes an exclusively created, service-generated filename;
5. registers the file with `CanonicalAssetService` for the same workspace;
6. atomically attaches the canonical asset ID and a safe metadata subset to
the generation job through `GenerationRepository`.
The output filename never uses a provider/client name. A retry finds and
verifies an existing canonical output instead of overwriting it. Supported
runtime output MIME types are safe raster-image and video types listed in
`GenerationOutputIngestor`; a future provider must extend that reviewed mapping
before advertising a new type.
## Implementing a future provider
1. Add a strictly typed adapter under `app/generation/providers/`; do not add
provider logic to REST, MCP, SDK, n8n, or frontend code.
2. Add only server-side configuration for its trusted worker endpoint and
`SecretStr` token. Do not make them client-selectable.
3. Register provider capabilities and a `GenerationModelRegistration` only
after its worker API, request schema, cancellation, output streaming, and
reconciliation behavior have been tested.
4. Build `RemoteWorkerClient` from the generic timeout/retry settings and the
provider's backend-only configuration.
5. On dispatch, transition through the existing job state machine, start the
existing attempt record, submit with the request idempotency key, then bind
the returned worker job ID through `GenerationService.bind_provider_job`.
6. During reconciliation, use `get_job`; for a completed result call
`GenerationService.ingest_completed_provider_output`. Never persist a raw
worker URL, output path, bearer token, or arbitrary provider response.
7. Add mocked protocol, retry, workspace-isolation, cancellation, output, and
zero-configuration startup tests before enabling the provider.
WAN and FLUX are registered through the provider-neutral contract but remain
unavailable until their respective trusted configuration and runtime
verification succeed.