feat(video): 1080p and 4K delivery via Flow's upsampler - #6
Merged
Merged
Conversation
Flow generates video at 720p only; the Flow UI's high-resolution download
is a second pass over the finished media against an upsampler model. This
exposes that pass across the CLI, HTTP API, and MCP server.
- flow_engine/generators/upsample.py: submits
POST /v1/video:batchAsyncGenerateVideoUpsampleVideo with
videoModelKey veo_3_1_upsampler_{1080p,4k}, polled through the ordinary
batchCheckAsyncVideoGenerationStatus endpoint.
- The endpoint is undocumented, so model keys and the resolution enum are
env-overridable (VIDEO_UPSAMPLER_*_MODEL, VIDEO_UPSAMPLE_ENUM_*), and a
request rejected for its shape is retried with the other known enum
spelling and then with the field omitted. Only shape rejections retry;
a real failure raises immediately, so the ladder cannot double-charge.
- POST /v1/videos/upsample and a `resolution` field on
POST /v1/videos/generations both route through the generator. When
generation upsamples, the response leads with the upsampled entry so
clients that treat data[0] as "the" output get the high-resolution file;
the 720p original stays in data and in history. Entries carry
`resolution` and, for upsampled files, `source_media_id`.
- 1080p is free. 4K costs credits and is gated by _ensure_upsample_credits,
which refuses with HTTP 402 when the balance cannot cover it.
- A failed upsample still delivers the 720p video with a note rather than
failing the whole generation.
- CLI: `flow upsample <media_id|path>` and `flow video --resolution`.
- MCP: new `upsample_flow_video` tool; `generate_flow_video` gains
`resolution`.
Tests in tests/test_video_upsample.py cover the enum retry ladder, the
credit gate, response ordering, and the degrade-to-720p path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Flow generates video at 720p only. The high-resolution download in the Flow UI is a second upsampler pass over the finished media. This PR exposes that pass across the CLI, HTTP API, and MCP server, so 1080p and 4K delivery is available without leaving the tool.
What changed
flow_engine/generators/upsample.py(new) — submitsPOST /v1/video:batchAsyncGenerateVideoUpsampleVideowithvideoModelKey: veo_3_1_upsampler_{1080p,4k},videoInput.mediaId, and a resolution enum. Captcha action is the ordinaryVIDEO_GENERATION; polling reusesbatchCheckAsyncVideoGenerationStatus.POST /v1/videos/upsample— upsample an already-generated video by media ID or by a local path already in history. Returns the same pollable job shape as generation, polled throughGET /v1/videos/generations/{job_id}.resolutiononPOST /v1/videos/generations—"720p" | "1080p" | "4k". When the request upsamples, the response leads with the upsampled entry so clients that treatdata[0]as "the" output (the CLI's--output) get the high-resolution file. The 720p original stays indataand inhistory.json. Entries now carryresolution, and upsampled ones carrysource_media_id.flow upsample <media_id|path> --resolution {1080p,4k}andflow video --resolution.upsample_flow_videotool;generate_flow_videogainsresolution.Handling an undocumented endpoint
The upsample endpoint is not published, so the resolution enum spelling is a guess that Google can change. Rather than hardcode it:
VIDEO_UPSAMPLER_1080P_MODEL,VIDEO_UPSAMPLER_4K_MODEL,VIDEO_UPSAMPLE_ENUM_1080P,VIDEO_UPSAMPLE_ENUM_4K.python -m flow_server.sniffremains the way to re-derive the wire format if Google changes it.Money
Generation spends real credits, so this follows the existing invariants:
_ensure_upsample_credits, which refuses with HTTP 402 when the balance cannot cover it — before anything is submitted.note, rather than failing the whole generation and wasting the spend.Tests
tests/test_video_upsample.py(new) covers the enum retry ladder, the credit gate, response ordering, and the degrade-to-720p path.Three failures in
tests/test_ext_http_api.pyare environmental —OSError: [Errno 48] Address already in use, a local backend holding:8001during the run. They pass on a free port and are untouched by this branch.ruff check . --select F,E9reports 4 pre-existingF401s int2v.py,v2v.py, andbatch.py. This branch does not touch those files, so they were left alone to keep the diff scoped — happy to fold the cleanup in if you'd prefer.Verification
Exercised end-to-end against a live signed-in Flow session through the MCP server: backend serves
/v1/videos/upsample, the CLI exposesflow upsample, and both MCP tools advertiseresolution.🤖 Generated with Claude Code