vrex-flow-engine 0.3.0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/PKG-INFO +1 -1
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/bridge/flow_rpc.py +22 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/bridge/flow_sdk.py +47 -4
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/catalog.py +3 -4
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/jobs.py +1 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/openai/videos.py +15 -9
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/pyproject.toml +1 -1
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/vrex_flow_engine.egg-info/PKG-INFO +1 -1
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/PYPI_README.md +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/__init__.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/bridge/__init__.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/bridge/flow_client.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/bridge/ws_server.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/cli/__init__.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/cli/app.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/cli/config.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/cli/dashboard.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/cli/health.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/cli/processes.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/cli/supervisor.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/config.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/ingest.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/job_store.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/main.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/media.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/media_store.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/openai/__init__.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/openai/_util.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/openai/images.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/openai/models.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/openai/uploads.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/pool.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/posthog_client.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/session.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/video_context.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/flow_engine/video_context_store.py +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/setup.cfg +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/vrex_flow_engine.egg-info/SOURCES.txt +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/vrex_flow_engine.egg-info/dependency_links.txt +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/vrex_flow_engine.egg-info/entry_points.txt +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/vrex_flow_engine.egg-info/requires.txt +0 -0
- {vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/vrex_flow_engine.egg-info/top_level.txt +0 -0
|
@@ -29,6 +29,7 @@ RPC_GENERATE_IMAGE = "ogiZ0b"
|
|
|
29
29
|
RPC_GENERATE_VIDEO_T2V = "YhhmEf"
|
|
30
30
|
RPC_GENERATE_VIDEO_I2V = "eb1hJf"
|
|
31
31
|
RPC_GENERATE_VIDEO_R2V = "MZZa6b"
|
|
32
|
+
RPC_GENERATE_VIDEO_INTERPOLATION = "nprQif"
|
|
32
33
|
RPC_GENERATE_VIDEO = RPC_GENERATE_VIDEO_T2V # historical alias
|
|
33
34
|
RPC_CHECK_MEDIA = "jwpduf"
|
|
34
35
|
RPC_GET_MEDIA = "as29s"
|
|
@@ -169,6 +170,27 @@ def video_request_item_i2v(
|
|
|
169
170
|
]
|
|
170
171
|
|
|
171
172
|
|
|
173
|
+
def video_request_item_interpolation(
|
|
174
|
+
prompt: str,
|
|
175
|
+
model_key: str,
|
|
176
|
+
aspect_enum: int,
|
|
177
|
+
start_media_id: str,
|
|
178
|
+
end_media_id: str,
|
|
179
|
+
seed: Optional[int] = None,
|
|
180
|
+
crop: Optional[list[Any]] = None,
|
|
181
|
+
) -> list[Any]:
|
|
182
|
+
"""Frames-to-video item (``nprQif``): the i2v item with the end frame in
|
|
183
|
+
the slot before the metadata — ``[textInput, modelKey, aspect, seed,
|
|
184
|
+
startImage, endImage, metadata]``. Passing the same media id for both
|
|
185
|
+
frames yields a clip that ends where it began (a seamless loop)."""
|
|
186
|
+
return [
|
|
187
|
+
_text_input(prompt), model_key, aspect_enum, seed,
|
|
188
|
+
[None, start_media_id, None, None, None, crop],
|
|
189
|
+
[None, end_media_id, None, None, None, crop],
|
|
190
|
+
_item_metadata(),
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
|
|
172
194
|
def video_request_item_r2v(
|
|
173
195
|
prompt: str,
|
|
174
196
|
model_key: str,
|
|
@@ -5,10 +5,10 @@ and the job debug endpoint can inspect Flow's payload when a shape drifts.
|
|
|
5
5
|
|
|
6
6
|
Wired from flow.google.com captures (2026-09): project list/create, model
|
|
7
7
|
catalog, credits/tier, preset voices, image upload (inline) and resumable
|
|
8
|
-
video upload, image generation (sync), text-/image-/reference-to-video
|
|
9
|
-
media status polling and signed URL
|
|
10
|
-
and edit still need their batchexecute payload captured —
|
|
11
|
-
answers 501 for them instead of guessing array positions.
|
|
8
|
+
video upload, image generation (sync), text-/image-/reference-to-video and
|
|
9
|
+
frames-to-video (interpolation), media status polling and signed URL
|
|
10
|
+
resolution. Extend and edit still need their batchexecute payload captured —
|
|
11
|
+
the videos route answers 501 for them instead of guessing array positions.
|
|
12
12
|
"""
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
@@ -350,6 +350,49 @@ class FlowSDK:
|
|
|
350
350
|
]
|
|
351
351
|
return await self._dispatch_video(flow_rpc.RPC_GENERATE_VIDEO_I2V, items, project_id, scene_id)
|
|
352
352
|
|
|
353
|
+
async def gen_video_interpolation(
|
|
354
|
+
self,
|
|
355
|
+
prompt: str,
|
|
356
|
+
project_id: str,
|
|
357
|
+
start_media_ids: list[str],
|
|
358
|
+
end_media_ids: Optional[list[str]] = None,
|
|
359
|
+
aspect_ratio: str = "VIDEO_ASPECT_RATIO_LANDSCAPE",
|
|
360
|
+
paygate_tier: Optional[int] = None,
|
|
361
|
+
seed: Optional[int] = None,
|
|
362
|
+
model_key: Optional[str] = None,
|
|
363
|
+
scene_id: Optional[str] = None,
|
|
364
|
+
) -> dict[str, Any]:
|
|
365
|
+
"""Frames-to-video: motion between a start and an end frame, one clip
|
|
366
|
+
per pair. ``end_media_ids`` defaults to ``start_media_ids`` — the
|
|
367
|
+
seamless-loop case; when both are given they are zipped pairwise and
|
|
368
|
+
must be the same length."""
|
|
369
|
+
if paygate_tier is None:
|
|
370
|
+
raise ValueError("paygate_tier is required — resolve the account first")
|
|
371
|
+
if not model_key:
|
|
372
|
+
return {"raw": None, "error": "interpolation_model_key_required"}
|
|
373
|
+
aspect_enum = flow_rpc.VIDEO_ASPECT_ENUM.get(aspect_ratio)
|
|
374
|
+
if aspect_enum is None:
|
|
375
|
+
return {"raw": None, "error": f"interpolation_aspect_unsupported_{aspect_ratio}"}
|
|
376
|
+
starts = [m for m in (start_media_ids or []) if isinstance(m, str) and m]
|
|
377
|
+
if not starts:
|
|
378
|
+
return {"raw": None, "error": "missing_start_media_id"}
|
|
379
|
+
ends = [m for m in (end_media_ids or []) if isinstance(m, str) and m] or list(starts)
|
|
380
|
+
if len(ends) != len(starts):
|
|
381
|
+
return {
|
|
382
|
+
"raw": None,
|
|
383
|
+
"error": (
|
|
384
|
+
f"start/end frame count mismatch: {len(starts)} start vs "
|
|
385
|
+
f"{len(ends)} end — pass equal-length lists or omit end frames"
|
|
386
|
+
),
|
|
387
|
+
}
|
|
388
|
+
items = [
|
|
389
|
+
flow_rpc.video_request_item_interpolation(prompt, model_key, aspect_enum, s, e, seed=seed)
|
|
390
|
+
for s, e in list(zip(starts, ends))[:MAX_VARIANT_COUNT]
|
|
391
|
+
]
|
|
392
|
+
return await self._dispatch_video(
|
|
393
|
+
flow_rpc.RPC_GENERATE_VIDEO_INTERPOLATION, items, project_id, scene_id
|
|
394
|
+
)
|
|
395
|
+
|
|
353
396
|
async def gen_video_r2v(
|
|
354
397
|
self,
|
|
355
398
|
prompt: str,
|
|
@@ -33,10 +33,9 @@ _CACHE_FILE = STORAGE_DIR / "catalog.json"
|
|
|
33
33
|
_CACHE_VERSION = 2
|
|
34
34
|
|
|
35
35
|
# Modes whose batchexecute payload has been captured and verified. Other modes
|
|
36
|
-
# (
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
_DISPATCHABLE_MODES: frozenset[str] = frozenset({"t2v", "i2v", "r2v"})
|
|
36
|
+
# (extension, edit, upsample) still resolve a key so the error can name it,
|
|
37
|
+
# but resolve() reports them as not dispatchable and the route answers 501.
|
|
38
|
+
_DISPATCHABLE_MODES: frozenset[str] = frozenset({"t2v", "i2v", "r2v", "interpolation"})
|
|
40
39
|
|
|
41
40
|
|
|
42
41
|
class _Catalog:
|
|
@@ -15,10 +15,9 @@ The generation MODE is inferred from the inputs you provide:
|
|
|
15
15
|
- ``start_media_ids`` / ``start_images`` → image-to-video (i2v, "Frames")
|
|
16
16
|
- neither → text-to-video (t2v, prompt only)
|
|
17
17
|
|
|
18
|
-
Text-, image- and
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
ported (see flow-engine/README.md).
|
|
18
|
+
Text-, image-, reference- and frames-to-video have captured flow.google.com
|
|
19
|
+
payloads. ``mode: extend|edit`` and r2v ``reference_audio`` answer HTTP 501
|
|
20
|
+
until their request is recorded and ported (see flow-engine/README.md).
|
|
22
21
|
"""
|
|
23
22
|
from __future__ import annotations
|
|
24
23
|
|
|
@@ -39,7 +38,7 @@ router = APIRouter(prefix="/v1/videos", tags=["videos"])
|
|
|
39
38
|
ALLOWED_VIDEO_MODEL = "veo_3_1_lite_low_priority"
|
|
40
39
|
|
|
41
40
|
# Modes with a verified flow.google.com payload.
|
|
42
|
-
SUPPORTED_MODES = frozenset({"t2v", "i2v", "r2v"})
|
|
41
|
+
SUPPORTED_MODES = frozenset({"t2v", "i2v", "r2v", "interpolation"})
|
|
43
42
|
|
|
44
43
|
|
|
45
44
|
class VideoRequest(BaseModel):
|
|
@@ -70,8 +69,9 @@ class VideoRequest(BaseModel):
|
|
|
70
69
|
source_seconds: Optional[int] = None # edit: source clip length (→ frames)
|
|
71
70
|
|
|
72
71
|
|
|
73
|
-
def
|
|
74
|
-
"""Pick the generation mode from the request shape (explicit ``mode`` wins).
|
|
72
|
+
def _select_mode(req: VideoRequest) -> str:
|
|
73
|
+
"""Pick the generation mode from the request shape (explicit ``mode`` wins).
|
|
74
|
+
``loop`` is sugar for "end frame is the start frame", i.e. interpolation."""
|
|
75
75
|
explicit = (req.mode or "").lower().strip()
|
|
76
76
|
if explicit in ("extend", "extension"):
|
|
77
77
|
return "extension"
|
|
@@ -129,7 +129,7 @@ async def create_video(
|
|
|
129
129
|
detail=f"only model={ALLOWED_VIDEO_MODEL!r} is supported",
|
|
130
130
|
)
|
|
131
131
|
|
|
132
|
-
mode =
|
|
132
|
+
mode = _select_mode(req)
|
|
133
133
|
if mode not in SUPPORTED_MODES or (mode == "r2v" and req.reference_audio):
|
|
134
134
|
# Decided before any upload so a caller never pays for reference
|
|
135
135
|
# ingestion on a request that cannot be dispatched.
|
|
@@ -166,10 +166,16 @@ async def create_video(
|
|
|
166
166
|
ref_ids = list(req.ref_media_ids or [])
|
|
167
167
|
ref_ids += await ingest.resolve_image_inputs(sdk, project_id, req.ref_images)
|
|
168
168
|
kwargs["ref_media_ids"] = ref_ids
|
|
169
|
-
elif mode
|
|
169
|
+
elif mode in ("i2v", "interpolation"):
|
|
170
170
|
start_ids = list(req.start_media_ids or [])
|
|
171
171
|
start_ids += await ingest.resolve_image_inputs(sdk, project_id, req.start_images)
|
|
172
172
|
kwargs["start_media_ids"] = start_ids
|
|
173
|
+
if mode == "interpolation":
|
|
174
|
+
end_ids = list(req.end_media_ids or [])
|
|
175
|
+
end_ids += await ingest.resolve_image_inputs(sdk, project_id, req.end_images)
|
|
176
|
+
# `loop` = end frame is the start frame; gen_video_interpolation
|
|
177
|
+
# defaults to that when no end frames are given.
|
|
178
|
+
kwargs["end_media_ids"] = end_ids or None
|
|
173
179
|
|
|
174
180
|
try:
|
|
175
181
|
job = jobs.start_video_job(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "vrex-flow-engine"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.2"
|
|
4
4
|
description = "Launcher + supervisor and OpenAI-compatible image/video generation service (Vrex Flow Engine)."
|
|
5
5
|
# Neutral public description (the in-repo README.md carries the full internal docs).
|
|
6
6
|
readme = "PYPI_README.md"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/vrex_flow_engine.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{vrex_flow_engine-0.3.0 → vrex_flow_engine-0.3.2}/vrex_flow_engine.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|