Spaces:
Running
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 and 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, orfailed).retrieve_output()andstream_output()β a validated output descriptor and scoped byte stream.normalize_error()andclose()β 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 /healthGET /readyGET /v1/infoPOST /v1/generateGET /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:
- a server-owned
GenerationModelRegistrationexists; - its provider adapter is configured and available;
/healthreportshealthy;/v1/infodiscovers the exact model ID and output modality;/readyreportsready,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_requestauthentication_errorauthorization_errorworker_unavailableworker_not_readytimeoutrate_limitedprovider_errorinference_erroroutput_errorcancellation_errorunknown_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 404s, generic 500s, or programming/unknown exceptions.
The generic settings are optional and do not enable a worker:
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:
cancelledtransitions the MediaRouter job tocancelled;requestedtransitions it tocancel_requestedonly;unsupportedreturns a capability error without changing the active job;failedreturns 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:
- streams bytes into a controlled temporary request workspace;
- enforces the configured maximum output size and non-empty output;
- verifies the optional SHA-256 and byte count;
- publishes an exclusively created, service-generated filename;
- registers the file with
CanonicalAssetServicefor the same workspace; - 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
- Add a strictly typed adapter under
app/generation/providers/; do not add provider logic to REST, MCP, SDK, n8n, or frontend code. - Add only server-side configuration for its trusted worker endpoint and
SecretStrtoken. Do not make them client-selectable. - Register provider capabilities and a
GenerationModelRegistrationonly after its worker API, request schema, cancellation, output streaming, and reconciliation behavior have been tested. - Build
RemoteWorkerClientfrom the generic timeout/retry settings and the provider's backend-only configuration. - 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. - During reconciliation, use
get_job; for a completed result callGenerationService.ingest_completed_provider_output. Never persist a raw worker URL, output path, bearer token, or arbitrary provider response. - 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.