You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: add previousJobId and previousImage for follow-up media edits
Unify video follow-up edits behind previousJobId (adapters resolve job vs media), and add previousImage sugar for image edits, with docs and e2e coverage.
Co-authored-by: Cursor <cursoragent@cursor.com>
feat: first-class follow-up edits for generated media.
11
+
12
+
`generateVideo({ ..., previousJobId })` edits a previously generated video instead of generating from scratch. Callers always pass the prior generation's job id; adapters decide how to consume it via a `VideoAdapter` edit-kind map:
13
+
14
+
-`'job'` — reference the id server-side (OpenAI Sora 2 / Sora 2 Pro remix; Gemini Omni Flash, which maps `previousJobId` onto the Interactions API's `previous_interaction_id` wire field — that field is omitted from Omni `modelOptions`)
15
+
-`'media'` — resolve the finished clip via `getVideoUrl(previousJobId)` (xAI `grok-imagine-video` → `/videos/edits`; fal video-to-video endpoints such as `xai/grok-imagine-video/edit-video` and Seedance 2.0 reference-to-video). Fal generate endpoints with a known edit sibling (e.g. Grok text/image-to-video) resolve on the generate model, then submit to the edit endpoint.
16
+
17
+
Non-editing models (Veo, `grok-imagine-video-1.5`) reject `previousJobId` at compile time. Sora remix and Grok edits accept only a prompt — `size` / `duration` / media inputs are rejected because the output inherits them from the source video.
18
+
19
+
`generateImage({ ..., previousImage })` is the image-side counterpart: pass a prior result's `GeneratedImage` (or an array, or the whole result) and it is prepended to the prompt as an image part, flowing through each adapter's existing edit path; type-gated to models that accept image inputs.
20
+
21
+
Breaking for hand-rolled (non-`BaseVideoAdapter`) `VideoAdapter` implementations: the interface gains `supportedEditKind(): 'job' | 'media' | undefined` (and a 7th, defaulted `TModelEditByName` generic — existing 6-argument instantiations keep compiling). `BaseVideoAdapter` supplies a default returning `undefined`, plus `resolvePreviousJobUrl(previousJobId)`. New exports include `VideoEditKind`, `ModelEditKindByName`, `VideoPreviousJobIdForAdapter`, `ImagePreviousSource`, `ImagePreviousImageForModel`, `generatedImageToImagePart`, `generatedVideoUrlToVideoPart`. Client wire types: `VideoGenerateInput.previousJobId`, `ImageGenerateInput.previousImage`.
Copy file name to clipboardExpand all lines: docs/media/image-generation.md
+64Lines changed: 64 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -79,6 +79,7 @@ All image adapters support these common options:
79
79
|`prompt`|`string \| MediaPromptPart[]`| Description of the image to generate (required). A plain string, or — on models that support image-conditioned generation — an ordered array of content parts interleaving text with image inputs. See [Image-Conditioned Generation](#image-conditioned-generation) below. |
80
80
|`numberOfImages`|`number`| Number of images to generate |
81
81
|`size`|`string`| Size of the generated image in WIDTHxHEIGHT format |
82
+
|`previousImage?`|`GeneratedImage \| GeneratedImage[] \| { images }`| A previously generated image (or images) to edit — prepended to the prompt as image input(s). Only offered (at compile time) for models that accept image inputs — see [Editing generated images](#editing-generated-images-previousimage). |
82
83
|`modelOptions?`|`object`| Model-specific options (renamed from `providerOptions`) |
83
84
84
85
### Size Options
@@ -183,6 +184,69 @@ The accepted part types are narrowed **per model at compile time**: passing
183
184
an image part to a text-only model (e.g. `dall-e-3`, Imagen) is a type
184
185
error, not just a runtime throw.
185
186
187
+
### Editing generated images (previousImage)
188
+
189
+
To run a **follow-up edit** on something you just generated, pass the prior
190
+
result's image as `previousImage` — sugar that prepends it to the prompt as an
191
+
image part, so it flows through the model's regular edit path (OpenAI
Copy file name to clipboardExpand all lines: docs/media/video-generation.md
+98-7Lines changed: 98 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -383,6 +383,7 @@ And returns:
383
383
|`prompt`|`string \| MediaPromptPart[]`| Description of the video to generate (required). A plain string, or — on models that support conditioned generation — an ordered array of content parts interleaving text with image / video / audio inputs. See [Image-to-Video](#image-to-video) below. |
384
384
|`size`|`string`| Video resolution in WIDTHxHEIGHT format |
385
385
|`duration`|`number`| Video duration in seconds (maps to `seconds` parameter in API) |
386
+
|`previousJobId?`|`string`| Prior generation's job id to edit instead of generating from scratch. Only offered (at compile time) for models that support follow-up edits — see [Editing Generated Videos](#editing-generated-videos-previousjobid). |
386
387
|`modelOptions?`|`object`| Model-specific options (renamed from `providerOptions`) |
387
388
388
389
## Image-to-Video
@@ -489,6 +490,91 @@ The API uses the `seconds` parameter. Allowed values:
489
490
-`8` seconds (default)
490
491
-`12` seconds
491
492
493
+
## Editing Generated Videos (previousJobId)
494
+
495
+
Models that support **follow-up runs** can edit a previously generated
496
+
video instead of generating from scratch: pass the prior generation's
497
+
job id as `previousJobId` and describe the change in the prompt. The
Generated clips include an audio track. When the job completes, the adapter reports `usage.unitsBilled` (billed seconds of video) and `usage.cost` (exact USD cost as returned by the API) on the result.
710
799
800
+
`grok-imagine-video` can also edit a previously generated clip via [`previousJobId`](#editing-generated-videos-previousjobid) — pass the prior job id and an edit prompt; the adapter resolves the finished clip and posts to xAI's `/videos/edits` endpoint, then polls like any other job. The output inherits duration and aspect ratio from the source (capped at 720p, input truncated to 8 seconds).
801
+
711
802
## Response Types
712
803
713
804
> **Note:** The interfaces below are the underlying adapter-level types. The `getVideoJobStatus()` helper returns a single merged object, `{ status, progress?, url?, error?, usage? }` — it does not return `jobId` or `expiresAt`.
0 commit comments