vrex-flow-engine 0.2.0__py3-none-any.whl
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.
- flow_engine/__init__.py +4 -0
- flow_engine/bridge/__init__.py +6 -0
- flow_engine/bridge/flow_client.py +444 -0
- flow_engine/bridge/flow_sdk.py +1743 -0
- flow_engine/bridge/ws_server.py +84 -0
- flow_engine/catalog.py +332 -0
- flow_engine/cli/__init__.py +1 -0
- flow_engine/cli/app.py +149 -0
- flow_engine/cli/config.py +81 -0
- flow_engine/cli/dashboard.py +125 -0
- flow_engine/cli/health.py +40 -0
- flow_engine/cli/processes.py +109 -0
- flow_engine/cli/supervisor.py +184 -0
- flow_engine/config.py +43 -0
- flow_engine/ingest.py +172 -0
- flow_engine/job_store.py +28 -0
- flow_engine/jobs.py +245 -0
- flow_engine/main.py +187 -0
- flow_engine/media.py +227 -0
- flow_engine/media_store.py +202 -0
- flow_engine/openai/__init__.py +1 -0
- flow_engine/openai/_util.py +59 -0
- flow_engine/openai/images.py +134 -0
- flow_engine/openai/models.py +38 -0
- flow_engine/openai/uploads.py +70 -0
- flow_engine/openai/videos.py +289 -0
- flow_engine/pool.py +138 -0
- flow_engine/posthog_client.py +117 -0
- flow_engine/session.py +89 -0
- flow_engine/video_context.py +77 -0
- flow_engine/video_context_store.py +49 -0
- vrex_flow_engine-0.2.0.dist-info/METADATA +62 -0
- vrex_flow_engine-0.2.0.dist-info/RECORD +36 -0
- vrex_flow_engine-0.2.0.dist-info/WHEEL +5 -0
- vrex_flow_engine-0.2.0.dist-info/entry_points.txt +3 -0
- vrex_flow_engine-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Per-video generation context, captured for extend/edit.
|
|
2
|
+
|
|
3
|
+
Extend and edit need more than a media id. Extend wants the source clip's
|
|
4
|
+
*scene id* and its *operation/generation id* (``video.operation.name`` — which
|
|
5
|
+
differs from the media name); edit wants the source's *workflow id* and *frame
|
|
6
|
+
count*. None of that is recoverable from a bare media id, and there's no
|
|
7
|
+
media→scene reverse lookup, so we capture it as each video job completes and key
|
|
8
|
+
it by the media id flow_engine hands back to the client.
|
|
9
|
+
|
|
10
|
+
In-memory, single process — same model as the media registry. A restart forgets
|
|
11
|
+
it; callers can still drive extend/edit by passing the context explicitly
|
|
12
|
+
(``scene_id`` / ``workflow_id`` on the request). Context is only available for
|
|
13
|
+
videos polled via the workflow schema (low-priority / extend / edit) — that poll
|
|
14
|
+
hits ``/v1/media/<id>`` which carries the workflow/operation/scene fields;
|
|
15
|
+
operation-mode (Lite/Fast/Quality) polls go through ``batchCheckAsync`` and
|
|
16
|
+
don't expose them.
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import threading
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from typing import Optional
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class VideoContext:
|
|
27
|
+
media_id: str
|
|
28
|
+
workflow_id: Optional[str] = None
|
|
29
|
+
operation_id: Optional[str] = None # video.operation.name — extend's source ref
|
|
30
|
+
scene_id: Optional[str] = None
|
|
31
|
+
duration_sec: Optional[float] = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
_registry: dict[str, VideoContext] = {}
|
|
35
|
+
_lock = threading.Lock()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def remember(
|
|
39
|
+
media_id: Optional[str],
|
|
40
|
+
*,
|
|
41
|
+
workflow_id: Optional[str] = None,
|
|
42
|
+
operation_id: Optional[str] = None,
|
|
43
|
+
scene_id: Optional[str] = None,
|
|
44
|
+
duration_sec: Optional[float] = None,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""Record (or merge) what we learned about a generated video. Only non-None
|
|
47
|
+
fields overwrite, so a later, richer poll can fill gaps left by an earlier
|
|
48
|
+
one."""
|
|
49
|
+
if not isinstance(media_id, str) or not media_id:
|
|
50
|
+
return
|
|
51
|
+
with _lock:
|
|
52
|
+
ctx = _registry.get(media_id) or VideoContext(media_id=media_id)
|
|
53
|
+
if workflow_id:
|
|
54
|
+
ctx.workflow_id = workflow_id
|
|
55
|
+
if operation_id:
|
|
56
|
+
ctx.operation_id = operation_id
|
|
57
|
+
if scene_id:
|
|
58
|
+
ctx.scene_id = scene_id
|
|
59
|
+
if duration_sec:
|
|
60
|
+
ctx.duration_sec = duration_sec
|
|
61
|
+
_registry[media_id] = ctx
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get(media_id: str) -> Optional[VideoContext]:
|
|
65
|
+
with _lock:
|
|
66
|
+
return _registry.get(media_id)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def parse_seconds(length: Optional[str]) -> Optional[float]:
|
|
70
|
+
"""Parse a Flow duration string like ``"8s"`` into seconds."""
|
|
71
|
+
if not isinstance(length, str):
|
|
72
|
+
return None
|
|
73
|
+
s = length.strip().rstrip("s")
|
|
74
|
+
try:
|
|
75
|
+
return float(s)
|
|
76
|
+
except ValueError:
|
|
77
|
+
return None
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Per-instance video context store.
|
|
2
|
+
|
|
3
|
+
Extracted from the module-level ``_registry`` dict in video_context.py so
|
|
4
|
+
each pool instance owns an isolated namespace — video context from account A
|
|
5
|
+
is never accessible to account B.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import threading
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from flow_engine.video_context import VideoContext
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class VideoContextStore:
|
|
16
|
+
"""Thread-safe dict-backed video context registry, one per PoolInstance."""
|
|
17
|
+
|
|
18
|
+
def __init__(self) -> None:
|
|
19
|
+
self._registry: dict[str, VideoContext] = {}
|
|
20
|
+
self._lock = threading.Lock()
|
|
21
|
+
|
|
22
|
+
def remember(
|
|
23
|
+
self,
|
|
24
|
+
media_id: Optional[str],
|
|
25
|
+
*,
|
|
26
|
+
workflow_id: Optional[str] = None,
|
|
27
|
+
operation_id: Optional[str] = None,
|
|
28
|
+
scene_id: Optional[str] = None,
|
|
29
|
+
duration_sec: Optional[float] = None,
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Record (or merge) what we learned about a generated video. Only
|
|
32
|
+
non-None fields overwrite, so a later, richer poll can fill gaps."""
|
|
33
|
+
if not isinstance(media_id, str) or not media_id:
|
|
34
|
+
return
|
|
35
|
+
with self._lock:
|
|
36
|
+
ctx = self._registry.get(media_id) or VideoContext(media_id=media_id)
|
|
37
|
+
if workflow_id:
|
|
38
|
+
ctx.workflow_id = workflow_id
|
|
39
|
+
if operation_id:
|
|
40
|
+
ctx.operation_id = operation_id
|
|
41
|
+
if scene_id:
|
|
42
|
+
ctx.scene_id = scene_id
|
|
43
|
+
if duration_sec:
|
|
44
|
+
ctx.duration_sec = duration_sec
|
|
45
|
+
self._registry[media_id] = ctx
|
|
46
|
+
|
|
47
|
+
def get(self, media_id: str) -> Optional[VideoContext]:
|
|
48
|
+
with self._lock:
|
|
49
|
+
return self._registry.get(media_id)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vrex-flow-engine
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Launcher + supervisor and OpenAI-compatible image/video generation service (Vrex Flow Engine).
|
|
5
|
+
Author: Vrex
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Project-URL: Homepage, https://getvrex.com
|
|
8
|
+
Keywords: vrex,flow-engine,cli,supervisor,image-generation,video-generation
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: POSIX
|
|
13
|
+
Classifier: Topic :: Multimedia :: Video
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: fastapi>=0.110
|
|
17
|
+
Requires-Dist: uvicorn[standard]>=0.27
|
|
18
|
+
Requires-Dist: httpx>=0.27
|
|
19
|
+
Requires-Dist: websockets>=12.0
|
|
20
|
+
Requires-Dist: pydantic>=2.5
|
|
21
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
22
|
+
Requires-Dist: posthog>=3.8
|
|
23
|
+
Requires-Dist: rich>=13.7
|
|
24
|
+
|
|
25
|
+
# vrex-flow-engine
|
|
26
|
+
|
|
27
|
+
One-command launcher + supervisor for the **Vrex Flow Engine** — a self-hosted
|
|
28
|
+
service that exposes OpenAI-compatible image/video generation endpoints.
|
|
29
|
+
|
|
30
|
+
`vrex-flow-engine start` stores your engine key + Cloudflare tunnel token,
|
|
31
|
+
launches the engine and the tunnel as supervised child processes (with
|
|
32
|
+
crash-restart), and renders a live status dashboard.
|
|
33
|
+
|
|
34
|
+
## Quickstart
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uvx vrex-flow-engine start
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
First run prompts for the engine API key and the Cloudflare tunnel token (saved
|
|
41
|
+
to `~/.vrex-flow/config.json`, chmod 600), then brings up the engine + tunnel.
|
|
42
|
+
Ctrl-C stops both cleanly.
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
| Command | What it does |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `vrex-flow-engine start` | Run engine + tunnel with a live dashboard. `--no-tunnel` runs the engine only. |
|
|
49
|
+
| `vrex-flow-engine setup` | Store the engine key + tunnel token. |
|
|
50
|
+
| `vrex-flow-engine doctor` | Check prerequisites and probe a running engine. |
|
|
51
|
+
| `vrex-flow-engine serve` | Run just the engine in the foreground. |
|
|
52
|
+
|
|
53
|
+
The short alias `flow-engine` is installed for the same commands.
|
|
54
|
+
|
|
55
|
+
## Requirements
|
|
56
|
+
|
|
57
|
+
- Python 3.10+
|
|
58
|
+
- [`cloudflared`](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/)
|
|
59
|
+
(for the tunnel; `--no-tunnel` skips it)
|
|
60
|
+
- Access to a Vrex Flow Engine deployment.
|
|
61
|
+
|
|
62
|
+
Internal Vrex tooling.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
flow_engine/__init__.py,sha256=bJWwmRl8Qu9j0osEFNf3NBfLC0LRP8EdT-nYE6MwTQA,169
|
|
2
|
+
flow_engine/catalog.py,sha256=_Q4FCgeLtxIcf5Udcsi8XWD_LfkKoWaONNotNmXn_ds,12978
|
|
3
|
+
flow_engine/config.py,sha256=QbvUksexrzFt-JNdzfc2poXK_JPk6BzasKu831l3hYE,2092
|
|
4
|
+
flow_engine/ingest.py,sha256=Xf3drEF9gyTHWBUSa1pJOlu8Wx_gyYe8sguGtRkU3DY,6421
|
|
5
|
+
flow_engine/job_store.py,sha256=0Au6I_q7einhhFCO5Oc4Jjc2dtAAdSfWKWSX4A0WWhA,722
|
|
6
|
+
flow_engine/jobs.py,sha256=uy4U5On7tIO8ml1MZlHjdx1Kd3-MvpNGfp_eOevfkX0,8571
|
|
7
|
+
flow_engine/main.py,sha256=MjOcWFflDePiChUVLCBodrDLARZRFxIq0jxSCHeG7VA,6457
|
|
8
|
+
flow_engine/media.py,sha256=Rm_JH2GEEVJCPgZRbq3vtcjTwiDXq9YADsKfW8p-YQY,7650
|
|
9
|
+
flow_engine/media_store.py,sha256=tgDYyCVpWvxJBkJ1kX-1atT_SahMAsCyDWnI2Irwcro,8028
|
|
10
|
+
flow_engine/pool.py,sha256=wwmv9h7S3mOkJdLtCVaGFklRTyoKgLDh99v7FDwmLQs,5680
|
|
11
|
+
flow_engine/posthog_client.py,sha256=8FPH74rxOgm6vYKyO3RwLBeNW6UxfYZnxkk1DcxQfb4,3672
|
|
12
|
+
flow_engine/session.py,sha256=bv59PsVPPFZl9ygRflVAQCCMyQPr_38pBj6LK2K2qhk,3456
|
|
13
|
+
flow_engine/video_context.py,sha256=u7HNEdsn95EtCitt23stNtie4FFGs8bCUzSW-EM9_Pw,2682
|
|
14
|
+
flow_engine/video_context_store.py,sha256=-5_Sr-lcTSFwjLcNxObwk2PECQE7PbeXuoVqFJH8Pjw,1664
|
|
15
|
+
flow_engine/bridge/__init__.py,sha256=VjD9zKm_UAX0rmHHtNi4lDlZCIb4Lg6o6iEo5rlYtXQ,300
|
|
16
|
+
flow_engine/bridge/flow_client.py,sha256=2mpJY6DXCPz7NGvc70ddl19eq-PRjEWTT5FUQdPEAqc,18889
|
|
17
|
+
flow_engine/bridge/flow_sdk.py,sha256=ynHjY48ms75n2QTnzBn5j2H7ewtOrOYPS9v36GhF7l8,76278
|
|
18
|
+
flow_engine/bridge/ws_server.py,sha256=JFq4Iv5ReVau78z2bxEZWN4XC3GGWkePg3Gq63nDh28,2879
|
|
19
|
+
flow_engine/cli/__init__.py,sha256=xETN-IChMvfTFjcW9vYyZ56UOsS6eIAMh3rNYxywU6w,76
|
|
20
|
+
flow_engine/cli/app.py,sha256=elV35tWQE7kkbHqA4BdLgeeVXLdpkqhgihMwag3yxmI,5694
|
|
21
|
+
flow_engine/cli/config.py,sha256=engG_4HC0E2gDd-pzIiifZAOsYoBbJOfVwL1jfNmdmA,2833
|
|
22
|
+
flow_engine/cli/dashboard.py,sha256=EwIRNaTlIdjy2hwwfoCM4wGybD0hiNnWjgKr7olMRoc,4851
|
|
23
|
+
flow_engine/cli/health.py,sha256=75ObJ-Y7uwYKt1FwZcWrTLSp4Y_Q6EUO5T7oqdm6cQI,1596
|
|
24
|
+
flow_engine/cli/processes.py,sha256=Nem-Z2yhwuYct1nRVvo5yXD2wFTYwil33iXenkFgk8k,3924
|
|
25
|
+
flow_engine/cli/supervisor.py,sha256=WrSbEwac2I1ZxNSkyIOtccNvSk9UcZ5uhluJp7QgjV8,7163
|
|
26
|
+
flow_engine/openai/__init__.py,sha256=KWlJNwXmYcxUPlOoSzwN-FcaWci3_7rKsuA7wMP1ePc,73
|
|
27
|
+
flow_engine/openai/_util.py,sha256=IbB4v8JgaOzvTRU45EJgRqsdD24vfQnVAvEuXLouJ1k,2175
|
|
28
|
+
flow_engine/openai/images.py,sha256=hP1vP_7ysXU-YfavsWzO_PWOJKFeazzbq4XrBOUWvvI,4874
|
|
29
|
+
flow_engine/openai/models.py,sha256=7S0pZ-IAZ4gwrX7ui3DUUilHseEjC2umEOzAdpUGzEY,1240
|
|
30
|
+
flow_engine/openai/uploads.py,sha256=ZFmX0J5BItys4JVbpVk1mc6kgdJPnzZ0x-9BOVzlNHg,2720
|
|
31
|
+
flow_engine/openai/videos.py,sha256=45DkN5gUmCtYqASpbF_MG-gQ5C_dNB7Xd_pjxkQMitg,10505
|
|
32
|
+
vrex_flow_engine-0.2.0.dist-info/METADATA,sha256=_r23P_tWo85zGcvuD_q4UCDXj9tdLwsA5WO9u7uzurw,2151
|
|
33
|
+
vrex_flow_engine-0.2.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
34
|
+
vrex_flow_engine-0.2.0.dist-info/entry_points.txt,sha256=H022OlR20lq-INQQo1Dp15XJgCJ6H2KfplwEJXayjV0,101
|
|
35
|
+
vrex_flow_engine-0.2.0.dist-info/top_level.txt,sha256=h2-ndJR-ki0pQZt_P2X9s58wpkRdAA-IKPqn4x3R6eA,12
|
|
36
|
+
vrex_flow_engine-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
flow_engine
|