File size: 8,247 Bytes
1b2323a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# WAN 2.2 generation provider

## Status

MediaRouter integrates the audited WAN 2.2 Hugging Face worker as the `wan`
generation provider. The registered public model ID is **`wan2.2`**. The
underlying worker model reported by `/v1/info` is
`Wan-AI/Wan2.2-I2V-A14B-Diffusers`; it is metadata, not the MediaRouter model
identifier.

FLUX is a separately configured optional image provider; it does not share
WAN credentials, request fields, worker IDs, or publishing behavior.

## Worker contract verified

The configured worker is expected to expose:

| Method | Endpoint | Authentication | Use |
| --- | --- | --- | --- |
| `GET` | `/health` | public | Liveness only |
| `GET` | `/ready` | public | Loaded model and whether it accepts jobs |
| `GET` | `/v1/info` | public | Model identity/capability discovery |
| `POST` | `/v1/generate` | Bearer | Authenticated multipart image-to-video submission |
| `GET` | `/v1/jobs/{id}` | Bearer | Job reconciliation |
| `POST` | `/v1/jobs/{id}/cancel` | Bearer | Queued-job cancellation only |
| `GET` | `/v1/jobs/{id}/output` | Bearer | Completed MP4 stream |

The worker reports:

```json
{
  "id": "wan2.2",
  "name": "WAN 2.2 FP8 AOTI Faster",
  "type": "video",
  "task": "image-to-video",
  "model_id": "Wan-AI/Wan2.2-I2V-A14B-Diffusers",
  "fps": 16
}
```

Model availability is deliberately stricter than a successful HTTP health
check. MediaRouter advertises `wan2.2` as available only after all of these
are true: WAN is completely configured, `/health` is healthy, `/ready` says
`ready` with `model_loaded: true` and `accepting_jobs: true`, `/ready` names
`wan2.2`, and `/v1/info` reports the exact `wan2.2` image-to-video model,
the audited `Wan-AI/Wan2.2-I2V-A14B-Diffusers` underlying model, and 16 FPS.

## Server configuration

Set these only in backend deployment secrets:

```dotenv
WAN_SPACE_URL=https://your-wan-worker.hf.space
WAN_SPACE_TOKEN=replace-with-the-worker-secret
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
GENERATION_WORKER_ENABLED=true
GENERATION_WORKER_INTERVAL_SECONDS=5
GENERATION_WORKER_POLL_BACKOFF_SECONDS=2
GENERATION_JOB_STALE_AFTER_SECONDS=900
```

`WAN_SPACE_URL` and `WAN_SPACE_TOKEN` must be configured together. The token
must meet the worker's 32–4096-character requirement. They are never exposed
through REST, browser data, MCP, n8n, SDK results, audit records, or logs.

WAN is optional: omitted or invalid WAN settings leave only WAN unavailable;
they do not stop MediaRouter, social publishing, or unrelated workers.

## Request contract

WAN is image-to-video. The caller references an existing **canonical,
workspace-owned image asset**; no local path, worker URL, worker header, or
output URL may be sent by a client.

```json
{
  "provider": "wan",
  "model_id": "wan2.2",
  "modality": "video",
  "input_asset_id": "canonical-image-asset-uuid",
  "prompt": "Slow cinematic cloud movement over a mountain valley",
  "wan": {
    "negative_prompt": "blur, artifacts",
    "duration_seconds": 0.5,
    "steps": 4,
    "guidance_scale": 1.0,
    "guidance_scale_2": 1.0,
    "seed": 42,
    "randomize_seed": false
  }
}
```

`wan` is a strict typed Pydantic object. Its worker-verified fields are:

| Field | Required | Range/default |
| --- | --- | --- |
| `input_asset_id` | yes | Readable owned image; max 20 MiB at WAN submission |
| `prompt` | yes | Nonblank, 1–4000 characters |
| `negative_prompt` | no | Max 4000 characters; omitted uses WAN's default |
| `duration_seconds` | no | 0.5–5.0; WAN default 5.0 |
| `steps` | no | 1–30; WAN default 4 |
| `guidance_scale` | no | 0–10; WAN default 1 |
| `guidance_scale_2` | no | 0–10; WAN default 1 |
| `seed` | no | 0–2147483647; WAN default 42 |
| `randomize_seed` | no | Boolean; WAN default false |

`width`, `height`, and `num_frames` are rejected. WAN derives supported
dimensions from the source image and frames from duration at 16 FPS. The
worker additionally rejects unreadable images, source sides above 16384,
source images above 40,000,000 pixels, and derived dimensions outside
480–832 or not divisible by 16. Unknown top-level and WAN options are
rejected; MediaRouter does not forward arbitrary provider fields. Output is a
streamed `video/mp4` file.

## Job lifecycle and idempotency

```text
POST /v1/generation/requests
        ↓ 202 (MediaRouter job ID only)
durable generation request + job
        ↓
GenerationWorker claims with database row lock
        ↓
WAN multipart submission
        ↓
persist opaque WAN worker job ID
        ↓
bounded status polling
        ↓
stream /output β†’ staging β†’ validation β†’ canonical asset
        ↓
MediaRouter job succeeds
```

The reconciliation queue uses a durable, bounded lease in the existing job's
`next_attempt_at` field. This prevents separate worker processes from polling
or ingesting the same WAN output at the same time; a crashed worker's lease
expires and another worker can reconcile the already-bound remote job without
submitting it again.

The public API never returns the WAN job ID as the MediaRouter job identity.
The database protects `(provider, external_job_id)` uniqueness and every
lookup/cancellation starts with the caller's workspace-owned MediaRouter job.

The API idempotency key creates one logical MediaRouter request/job for an
identical request and reports conflict for a changed request. WAN itself has
**no submission idempotency protocol**. MediaRouter therefore never
automatically resubmits after a connection failure, timeout, 502, or 504 that
could have created a remote job while losing its response. Such a submission
is recorded as `GENERATION_SUBMISSION_AMBIGUOUS` and is not retried. This is a
safe at-most-once submission strategy, not an exactly-once guarantee.

Received `429` and `503` rejection responses may be retried within the durable
generation attempt limit. Polling uses the shared bounded retry policy for
connection failures, timeouts, `429`, `502`, `503`, and `504`; `400`, `401`,
`403`, invalid worker job IDs, validation failures, generic `500`s, and
programming failures are not retried automatically.

## Cancellation and status

WAN supports cancellation only while the remote job is queued. A confirmed
worker `cancelled` response moves the MediaRouter job to `cancelled`. A WAN
`409 WAN_JOB_NOT_CANCELLABLE` for a running job is a cancellation failure:
MediaRouter preserves the active job and never claims GPU inference stopped.

WAN state is normalized into the existing generation state machine:

| WAN state | MediaRouter state |
| --- | --- |
| `queued` | `queued` after worker identity is bound |
| `running` | `running` |
| `completed` | output finalization, then `succeeded` |
| `failed` | `failed` unless a separately safe pre-acceptance retry applies |
| `cancelled` | `cancelled` |

## Output and security

On completion, WAN returns only a video type and filename. The adapter creates
a validated internal descriptor for the fixed relative endpoint
`/v1/jobs/{worker-job-id}/output`, `video/mp4`, and the opaque worker job ID.
It never trusts a worker filesystem path or arbitrary output URL.

The shared worker client rejects non-server-configured worker origins,
redirects, private literal IPs, unsafe endpoint paths, URL traversal, signed
URLs, and client-supplied output locations. MP4 bytes are streamed without
loading the output into memory, staged in a controlled directory, size and
checksum verified where available, FFprobe/MediaValidator checked, then
registered as a workspace-owned canonical asset. The canonical asset ID, MIME
type, size, checksum, and safe media metadata are attached to the generation
job.

## Verification status

Mocked protocol tests cover model discovery, readiness, multipart translation,
non-idempotent submission behavior, output normalization, cancellation, typed
parameter rejection, optional configuration, and the shared worker runtime
retry/SSRF/redaction behavior. A live WAN smoke test requires both
`WAN_SPACE_URL` and `WAN_SPACE_TOKEN`; it is intentionally not run in normal
CI and must be reported as not verified when those secrets are absent.