[docs] Add Dreamverse app documentation

Apply Dreamverse monorepo changes for stack slice 2/13 from the source branch.

Source-Branch: will/dreamverse-monorepo
Source-SHA: 03d3e61df6
Dreamverse-Stack: 2/13
Co-authored-by: SolitaryThinker <wlsaidhi@gmail.com>
Co-authored-by: Matthew Noto <99706358+RandNMR73@users.noreply.github.com>
Co-authored-by: XOR-op <17672363+XOR-op@users.noreply.github.com>
Co-authored-by: Zhang Peiyuan <42993249+jzhang38@users.noreply.github.com>
This commit is contained in:
Davids048
2026-05-12 12:45:09 -07:00
co-authored by SolitaryThinker Matthew Noto XOR-op Zhang Peiyuan
parent 4ad6880ca6
commit 2a8a7a01a9
7 changed files with 2001 additions and 0 deletions
+203
View File
@@ -0,0 +1,203 @@
# Dreamverse Agent Notes
## Repo-local skills
- `.agents/skills/bootstrap-fastvideo-private-fork/`: temporary setup skill for
cloning `git@github.com:hao-ai-lab/FastVideo-internal.git` at
`will/rebase-nbv` into `../FastVideo-internal`, then running
`uv sync --extra server`.
- Prefer the bundled script in that skill instead of inventing a new private
FastVideo bootstrap flow.
## Repo layout
Current paths:
- `apps/web/`: Next.js frontend, client-side stores, websocket event reduction,
prompt-window editing, devtools UI.
- `server/`: current Python FastAPI runtime, websocket protocol, prompt
enhancement, prompt rewrite orchestration, GPU worker lifecycle.
- `server/tests/`: backend unit and integration-oriented tests.
- `server/benchmarks/`: prompt-provider latency/token benchmarking scripts.
Planned paths during the OSS reorg:
- `controller/`: local control plane for provider credentials, compute
lifecycle, and proxying.
- `runtime/`: eventual rename of `server/` once the controller/runtime split is
stable.
- `providers/`: provider adapters for local, Runpod, and Modal.
Important rule:
- Until the split lands, treat `server/` as the authoritative runtime and keep
provider orchestration out of `apps/web`.
## System split
Dreamverse is moving toward a three-part local-first architecture.
- Frontend owns local UI state, drafts, inspection tools, and user-triggered
actions.
- Controller will own local-only credentials, compute provisioning, runtime
lifecycle, and HTTP/websocket proxying.
- Runtime owns generation sessions, prompt rewrite, prompt safety, and
websocket semantics.
The browser should only talk to the local Dreamverse process, never directly to
Modal or Runpod.
## Current runtime responsibilities
The runtime in `server/` is responsible for:
- websocket session lifecycle on `/ws`
- queueing, GPU assignment, worker startup, and stream chunk emission
- seed prompt memory and the active prompt window used for generation
- prompt enhancement and prompt rewrite execution
- prompt safety checks
- persistence and reload of prompt system prompt files
- curated preset append/read routes in devtools mode
- health and readiness endpoints
Relevant files:
- `server/main.py`: websocket protocol, session state machine, REST routes
- `server/gpu_pool.py`: FastVideo-backed generation workers
- `server/prompt_enhancer.py`: provider clients, prompt enhancement, rewrite
execution
- `server/rewrite_prompt_payload.py`: canonical rewrite request body format
- `server/config.py`: prompt file paths, provider configuration, runtime flags
## Frontend responsibilities
The frontend in `apps/web/` is responsible for:
- collecting user input and deciding whether to send raw prompts or rewrite
requests
- maintaining client-side stores for session, prompt-window, stream, rewrite,
and UI state
- rendering prompt history, playback state, devtools controls, and rewrite
inspection
- building the prompt-window snapshot sent with rewrite requests
- reducing websocket events into UI state
- showing compute status and controller-driven errors once the controller lands
Relevant files:
- `apps/web/src/app/page.tsx`: main orchestration, websocket connect/send paths
- `apps/web/src/lib/ws/reducer.ts`: applies normalized websocket events to
stores
- `apps/web/src/stores/promptWindow.ts`: prompt window and preset/editor state
- `apps/web/src/stores/rewrite.ts`: rewrite activity timeline and flags
- `apps/web/src/lib/prompts/promptWindowSnapshot.ts`: rewrite snapshot
normalization and padding
## Planned controller responsibilities
The future local controller should own:
- local-only provider credential loading and storage
- provider selection
- runtime provisioning, reuse, shutdown, and health checks
- proxying frontend HTTP and websocket traffic to the active runtime
- surfacing provisioning, ready, failed, and idle states to the frontend
- durable local settings that should survive ephemeral remote runtimes
The controller should not own:
- prompt rewrite logic
- seed prompt memory
- generation queue semantics
- websocket event schemas
## Prompt rewrite contract
Prompt rewrite is a shared flow with a strict ownership split.
Frontend responsibilities:
- decide when a user action should trigger `rewrite_seed_prompts` instead of
`append_prompt`
- send `rewrite_instruction` and a snapshot of the current prompt window
- pad the rewrite snapshot to the runtime-expected segment count using
`buildRewritePromptWindowSnapshotFromPrompts(...)`
- show rewrite activity and raw LLM output in local inspection UI
Runtime responsibilities:
- validate and normalize `prompt_window_prompts`
- choose the rewrite system prompt and provider/model/temperature
- build the canonical LLM request body in `server/rewrite_prompt_payload.py`
- run the rewrite through `PromptEnhancer.rewrite_prompt_sequence(...)`
- apply safety filtering to rewritten prompts
- replace the authoritative seed prompt memory when rewrite succeeds
- emit `seed_prompts_updated` and `rewrite_seed_prompts_complete`
Controller responsibilities:
- proxy the request and response
- surface runtime availability and provider lifecycle failures
Important rule:
- The frontend may suggest the prompt window to rewrite, but the runtime owns
the actual rewritten rollout and the authoritative prompt window after
acceptance.
## Rewrite modes
There are two runtime rewrite modes:
- edit existing rollout: when `prompt_window_prompts` is non-empty, rewrite the
current rollout while preserving segment count and ordering
- new rollout: when the prompt window is empty but there is a
`rewrite_instruction`, generate a fresh rollout
The frontend should not emulate runtime rewrite behavior locally. It should
prepare the snapshot, send it, and display the result.
## Prompt window ownership
- Frontend owns editable drafts, selected preset UI, and prompt-window
inspection state.
- Runtime owns the active seed prompt memory used for actual generation.
- After any runtime event with reason `rewrite`, the frontend must replace its
prompt window from the server payload instead of keeping a locally-derived
version.
## Devtools and persistence ownership
Prompt config editing is runtime-owned persistence with frontend-owned forms
today.
- Frontend loads and edits drafts through `/prompt-system-config`.
- Runtime reads and writes prompt files and reloads runtime prompt config.
Curated presets follow the same pattern:
- frontend submits append requests and may update local UI optimistically from
the response
- runtime persists the JSON file and resolves overlay vs fallback file paths
During the controller reorg, avoid moving durable user settings into ephemeral
remote runtimes. Controller-owned local persistence is preferred for anything
that must survive provider restarts.
## Editing guidance
- Do not move rewrite logic into the frontend or controller.
- Do not make the frontend the source of truth for the generated prompt window
after rewrite.
- If you change websocket message types or payload fields in `server/main.py`,
update the reducer in `apps/web/src/lib/ws/reducer.ts` in the same change.
- If you change rewrite request shape, update both
`apps/web/src/lib/prompts/promptWindowSnapshot.ts` and
`server/rewrite_prompt_payload.py`.
- If you add controller-managed status or error payloads, keep them separate
from runtime websocket events unless there is a strong reason to merge them.
- If you change prompt file paths or devtools persistence, update `server/`,
the frontend devtools UI, and any controller-owned local persistence logic
together.
- Keep provider adapters focused on runtime lifecycle and reachability, not on
prompt or session semantics.
+234
View File
@@ -0,0 +1,234 @@
# Dreamverse
Dreamverse is the FastVideo realtime video generation & editing platform. It lives in this monorepo under `apps/dreamverse/`.
## Install Dreamverse
You can install Dreamverse using one of the methods below.
### Method 1: With uv pip
```bash
pip install --upgrade pip
pip install uv
uv venv .venv --python 3.12
source .venv/bin/activate
uv pip install "fastvideo[dreamverse]"
```
### Method 2: From source
```bash
git clone https://github.com/hao-ai-lab/FastVideo.git
cd FastVideo
pip install --upgrade pip
pip install uv
uv venv .venv --python 3.12
source .venv/bin/activate
uv pip install -e ".[dreamverse]"
```
### Method 3: Using Docker
```bash
git clone https://github.com/hao-ai-lab/FastVideo.git
cd FastVideo
apps/dreamverse/docker/docker_build.sh
```
See `apps/dreamverse/docker/README.md` for Docker build and run option details.
## Optional: Building FFmpeg For Better Performance
For full streaming performance in a non-Docker install, build a custom FFmpeg
binary:
```bash
bash apps/dreamverse/scripts/install_native_ffmpeg.sh
```
This builds and installs into `~/opt/ffmpeg-native/` and writes
`apps/dreamverse/scripts/ffmpeg-env.sh`. Source it before starting the backend
so Dreamverse uses the custom FFmpeg binary:
```bash
source apps/dreamverse/scripts/ffmpeg-env.sh
dreamverse-server
```
Docker images already run this FFmpeg build during image creation and source the
generated environment file at container startup. The installer supports Linux
`x86_64` and `aarch64`.
## Launch Dreamverse
Start the backend with the installed Dreamverse commands:
```bash
dreamverse-server --port 8009
dreamverse-mock-server --port 8009
```
## Frontend Setup
Install the web dependencies once from the FastVideo checkout:
```bash
cd apps/dreamverse/web
pnpm install --frozen-lockfile
```
The frontend package also has an npm lockfile, but the bundled launch scripts
use `pnpm`.
## Quick Start: Local GPU
### Start Backend
Export the API keys used for prompt rewrite and prompt enhancement:
```bash
export CEREBRAS_API_KEY=...
export GROQ_API_KEY=...
```
If you built the optional native FFmpeg binary above, source its environment
file in the same shell before starting the backend:
```bash
source apps/dreamverse/scripts/ffmpeg-env.sh
dreamverse-server --host 0.0.0.0 --port 8009
```
The Dreamverse backend defaults to `0.0.0.0:8009` and starts one GPU worker on
the first visible GPU by default.
### Check Readiness
In another shell, verify that the backend process is alive:
```bash
curl http://localhost:8009/healthz
```
Then wait for GPU workers and startup warmup to finish:
```bash
curl http://localhost:8009/readyz
```
You can also run the same readiness path with:
```bash
BACKEND_HOST=localhost BACKEND_PORT=8009 apps/dreamverse/scripts/smoke_local.sh
```
If a backend is already running and you only want the script to probe it:
```bash
DREAMVERSE_SMOKE_START_BACKEND=0 apps/dreamverse/scripts/smoke_local.sh
```
### Start Frontend
Start the frontend:
```bash
cd apps/dreamverse/web
BACKEND_HOST=localhost BACKEND_PORT=8009 pnpm run dev
```
Open `http://localhost:5299`.
## Quick Start: Mock Backend (For UI development)
The mock server emulates the Dreamverse backend protocol and streams a
synthetic FFmpeg-generated fMP4 clip, so the frontend can run without a GPU.
```bash
dreamverse-mock-server --latency 200 --port 8009
```
## Tests
Run the focused backend tests that validate local startup wiring, config, GPU
selection, and mock-server behavior:
```bash
pytest apps/dreamverse/dreamverse/tests/test_config.py \
apps/dreamverse/dreamverse/tests/test_entrypoints.py \
apps/dreamverse/dreamverse/tests/test_gpu_pool.py \
apps/dreamverse/dreamverse/tests/test_mock_server.py -q
```
Run the broader Dreamverse backend suite:
```bash
pytest apps/dreamverse/dreamverse/tests -q
```
Run the frontend tests:
```bash
cd apps/dreamverse/web
pnpm test
```
Run the frontend e2e tests:
```bash
cd apps/dreamverse/web
pnpm run e2e
```
## Troubleshooting
`dreamverse-server` exits with an install hint
- install the Dreamverse extra with `uv pip install -e ".[dreamverse]"` from a
source checkout, or `uv pip install "fastvideo[dreamverse]"` from PyPI.
Prompt-provider environment variable errors
- set `CEREBRAS_API_KEY`
- set `GROQ_API_KEY`
- direct `dreamverse-server` launches do not source `~/.env`; export the keys
in the shell or use the bundled launch scripts, which source `~/.env`.
`/readyz` stays at `503`
- wait for model loading and startup warmup to finish
- confirm a compatible CUDA GPU is visible to the process
- check backend logs for worker startup or warmup failures
- for a startup/debug pass without warmup, set
`FASTVIDEO_ENABLE_STARTUP_WARMUP=0` before starting the backend
Only one GPU is used
- this is the default local behavior
- set `FASTVIDEO_GPU_COUNT=<N>` to start N GPU worker subprocesses inside one
backend instance
- set `FASTVIDEO_GPU_COUNT=all` to start one worker for every visible GPU
- use `CUDA_VISIBLE_DEVICES` first if you need to pin the visible GPU set
Frontend cannot connect to backend
- confirm the backend is running on `8009`; if not, point the frontend at it
with `BACKEND_HOST=<host> BACKEND_PORT=<port> pnpm run dev`
- confirm `http://localhost:8009/healthz` responds before starting the frontend
- confirm `http://localhost:8009/readyz` returns `200` before clicking Generate
- use `apps/dreamverse/scripts/smoke_local.sh` for a repeatable local startup
check
Mock backend fails during startup
- install FFmpeg or set `FASTVIDEO_FFMPEG_BIN` to an FFmpeg binary
- for non-mock local GPU streaming performance, use the native FFmpeg installer
described above
## Notes
Dreamverse owns its backend app under `apps/dreamverse/dreamverse/`. It expects
`dreamverse-server`, not `fastvideo serve`.
+351
View File
@@ -0,0 +1,351 @@
# Dreamverse Architecture
## Overview
Dreamverse currently has two main runtime pieces:
- `apps/web/`: Next.js frontend
- `server/`: Python FastAPI runtime
Today, the browser talks directly to the Dreamverse runtime over HTTP and a
single websocket on `/ws`. The frontend owns UI state and interaction flow. The
server owns generation state, prompt rewrite, prompt safety, websocket session
semantics, and GPU-backed execution.
Near-term OSS note:
- `server/` is the current runtime implementation.
- A future `controller/` layer is planned for local-only compute management and
provider orchestration, but it does not exist yet.
## Repo Map
### Frontend
- `apps/web/src/app/page.tsx`: main client orchestration, websocket connect,
init payloads, send paths, and top-level app behavior
- `apps/web/src/lib/ws/reducer.ts`: reduces normalized websocket events into
client stores
- `apps/web/src/stores/session.ts`: connection, mode, and top-level session UI
- `apps/web/src/stores/promptWindow.ts`: editable prompt window and seed prompt
UI state
- `apps/web/src/stores/rewrite.ts`: rewrite activity timeline and inspection
state
- `apps/web/src/stores/stream.ts`: playback and stream-related client state
- `apps/web/src/lib/prompts/promptWindowSnapshot.ts`: prompt-window snapshot
building for rewrite requests
### Server
- `server/main.py`: websocket endpoint, request handling, session state
machine, rewrite orchestration, REST routes, and stream relay
- `server/gpu_pool.py`: GPU worker processes, warmup, model loading, and
`generate_video()` calls through FastVideo
- `server/prompt_enhancer.py`: prompt enhancement, rollout rewrite execution,
provider selection, and timeout/fallback behavior
- `server/rewrite_prompt_payload.py`: canonical rewrite request payload
building
- `server/config.py`: runtime flags, prompt file paths, provider settings, and
warmup config
- `server/session_init_image.py`: validates and persists uploaded initial
images for segment 1
## Current Split Of Responsibility
### Frontend owns
- local UI state and client-side stores
- prompt drafts and prompt window editing
- websocket connection management
- deciding which user action to send:
- `session_init_v2`
- `project_init_v1`
- `append_prompt`
- `rewrite_seed_prompts`
- `simple_generate`
- showing rewrite progress, stream status, prompt history, and devtools views
### Server owns
- websocket session lifecycle and protocol
- GPU assignment and worker lifecycle
- the authoritative seed prompt memory used for generation
- prompt rewrite execution and prompt safety
- actual generation queue semantics
- stream chunk emission and segment lifecycle events
- prompt config and preset persistence routes
- health and readiness endpoints
Important rule:
- The frontend may propose prompt-window state for rewrite, but the server is
the source of truth for the rewritten rollout and the active prompt memory
used for generation.
## End-To-End Flow
1. The frontend opens `/ws`.
2. The frontend sends `session_init_v2` with the initial prompt-window state,
preset metadata, and current toggles.
3. The server validates init data, persists an optional initial image, acquires
a GPU slot, and emits session status such as `gpu_assigned`.
4. The server starts or resumes project generation and emits events like
`ltx2_stream_start`, `ltx2_segment_start`, media init/chunks, and completion
events.
5. The frontend reduces those websocket events into its stores and updates the
UI.
6. User actions such as appending prompts, rewriting seed prompts, or starting
a single custom clip go back to the server over the same websocket.
## Frontend Architecture
The frontend is store-driven.
- `page.tsx` wires together websocket setup, send helpers, reducer
application, and top-level interaction flows.
- Store modules separate concerns like session state, rewrite state, prompt
window state, and stream state.
- The websocket reducer is responsible for turning normalized runtime events
into store updates. If the server event schema changes, the reducer must
change with it.
The frontend is intentionally not responsible for:
- generating rewritten prompts locally
- deciding final prompt safety outcomes
- reconstructing server session state from scratch
- inventing its own generation semantics independent of the runtime
## Server Architecture
The current runtime is a FastAPI app with a single long-lived websocket per
session.
`server/main.py` manages:
- websocket connect/init
- prompt queues
- project and segment state
- prompt enhancement/rewrite triggers
- stream relay from GPU workers to the browser
- session logging and REST endpoints
`server/gpu_pool.py` manages:
- model loading through FastVideo
- one or more worker processes
- startup warmup
- user join/leave commands
- `USER_STEP` execution for each segment
- continuation state between segments
`server/prompt_enhancer.py` manages:
- prompt enhancement for user-submitted prompts
- rollout rewrite requests for the prompt window
- provider selection and fallback across configured prompt providers
- response normalization and safety-aware failure handling
## FastAPI Surface
The server is a single FastAPI application created in
`server/main.py`.
Current built-in FastAPI docs are enabled:
- `/docs`: Swagger UI
- `/redoc`: ReDoc
- `/openapi.json`: OpenAPI schema
The runtime also mounts static content:
- `/server-assets`: server-local static assets rooted at `server/`
- `/`: frontend static build when one of the configured frontend static
directories exists
## HTTP API
The current HTTP API is small. Most realtime behavior still goes through the
websocket.
### Core health and status routes
- `GET /healthz`
- process liveness probe
- returns a small payload with `status`, `service`, and timestamp
- `GET /readyz`
- readiness probe
- returns `503` until prompt services are initialized and at least one GPU
worker is ready
- returns readiness and GPU pool summary fields such as ready workers, total
GPUs, warmup counts, and queue size
- `GET /status`
- returns the current GPU pool status payload from `gpu_pool`
- `GET /internal/monitor/sessions`
- internal monitoring payload for session dashboards
- includes pending session count, max available sessions, prompt provider
success counts, and timestamp
### Prompt config routes
- `GET /prompt-system-config`
- returns the editable prompt-system configuration currently loaded by
`PromptEnhancer`
- `POST /prompt-system-config`
- saves prompt-system configuration to disk and reloads prompt config in the
runtime
- current editable fields include:
- next-segment system prompt
- auto-extension system prompt
- rewrite-window system prompt
- rewrite-user system prompt
- rewrite model
- rewrite temperature
### Devtools-only preset routes
These exist only when `DEVTOOLS_ENABLED` is true in `server/config.py`.
- `GET /curated-presets`
- returns merged curated presets, applying the local overlay file on top of
the fallback file when both exist
- `POST /curated-presets/append`
- appends a new curated preset to the overlay presets file
- validates non-empty label, normalized id, and at least two non-empty
segment prompts
## Websocket API
`WS /ws` is the main runtime API.
The websocket owns:
- session init
- project init and reset
- prompt append
- prompt rewrite
- generation toggles
- segment lifecycle events
- media stream delivery
- runtime error delivery
The websocket is the authoritative API for realtime Dreamverse behavior. The
HTTP routes mainly support health checks, devtools persistence, and monitoring.
## Prompt Rewrite Architecture
Prompt rewrite is a shared flow with strict ownership boundaries.
### Frontend responsibilities
- collect the rewrite instruction
- build the prompt-window snapshot from current client state
- send `rewrite_seed_prompts`
- show rewrite progress, raw output, fallback state, and resulting prompt list
### Server responsibilities
- validate and normalize the prompt-window payload
- choose rewrite model, system prompt, timeout, and temperature
- build the canonical prompt payload in `server/rewrite_prompt_payload.py`
- execute rewrite through `PromptEnhancer`
- apply safety filtering to rewritten prompts
- replace the authoritative seed prompt memory when rewrite succeeds
- emit `seed_prompts_updated` and `rewrite_seed_prompts_complete`
Important rule:
- The frontend owns editable drafts.
- The server owns the accepted rollout.
After a successful rewrite, the frontend should replace its prompt-window view
from the server payload instead of preserving a locally-derived version.
## Prompt Modes
There are three related prompt paths in the current system:
### Initial rollout
- The frontend sends seed prompts during `session_init_v2`.
- The server uses those prompts as the initial seed prompt memory.
- If the rollout starts from an empty prompt window plus an initial rewrite
instruction, the server can pause generation until rewrite completes.
### Live append
- The frontend sends `append_prompt`.
- The server may enhance that prompt, safety-check it, enqueue it, and use it
as the next generated segment.
### Rewrite
- The frontend sends `rewrite_seed_prompts`.
- The server rewrites the entire seed prompt window or generates a new rollout,
depending on the payload and current state.
## Initial Image And Segment Handling
The frontend currently sends `initial_image` as part of session init or
`simple_generate`.
The server:
- validates and persists the image
- uses it only for segment 1 when present
- keeps continuation state for later segments in the GPU worker
This means the runtime, not the frontend, decides how segment 1 image
conditioning and later continuation conditioning are applied.
## Websocket Contract
The websocket is the main integration surface between UI and runtime.
Typical incoming messages from the frontend:
- `session_init_v2`
- `project_init_v1`
- `append_prompt`
- `rewrite_seed_prompts`
- `simple_generate`
- `set_enhancement`
- `set_auto_extension`
- `set_loop_generation`
Typical outgoing messages from the server:
- `gpu_assigned`
- `ltx2_stream_start`
- `ltx2_segment_start`
- `segment_prompt_source`
- `prompt_received`
- `prompt_ready`
- `prompt_enhancing`
- `seed_prompts_updated`
- `rewrite_seed_prompts_complete`
- `media_init`
- `media_segment_complete`
- `project_idle`
- `error`
Binary websocket frames carry media chunks for playback.
## Current And Planned Architecture
Current architecture:
- browser -> `apps/web`
- `apps/web` -> `server/main.py`
- `server/main.py` -> `gpu_pool.py`
- `gpu_pool.py` -> FastVideo runtime
Planned architecture:
- browser -> `apps/web`
- `apps/web` -> local `controller/`
- `controller/` -> local or remote Dreamverse runtime
- runtime -> FastVideo runtime
That future controller split should not move prompt rewrite, session state, or
generation semantics out of the runtime.
+594
View File
@@ -0,0 +1,594 @@
# Dreamverse OSS Design
## Overview
Dreamverse should ship as a local-first open source application.
- The browser talks only to a local Dreamverse control plane on the user's
machine.
- Provider credentials stay local to that machine.
- Dreamverse may provision compute on the user's behalf, but Dreamverse does
not host that control path as a service.
This keeps the UX simple without turning Dreamverse into a credential-holding
hosted platform.
## Goals
- Support three compute modes behind one product surface:
- local GPU
- managed remote GPU via Runpod
- managed remote GPU via Modal
- Keep prompt rewrite, websocket session state, and generation behavior
consistent across providers.
- Keep provider API keys out of browser state and out of any hosted service.
- Make the existing runtime reusable as the common serving contract.
- Minimize provider-specific code and isolate it behind a narrow interface.
## Non-goals
- Do not make the frontend call provider APIs directly.
- Do not unify providers at the level of SSH, VM, serverless, or pod
semantics.
- Do not move prompt rewrite logic into the frontend or controller.
- Do not require remote compute for the basic product path.
## Current State
Today the repo contains two major pieces:
- `apps/web/`: Next.js frontend
- `server/`: FastAPI runtime that owns websocket state, prompt rewrite, prompt
safety, and GPU-backed generation
The current runtime already exposes useful health and streaming surfaces such
as `/healthz`, `/readyz`, `/status`, and `/ws`.
## Target Architecture
The target open source structure should be:
```text
Dreamverse/
├── apps/
│ └── web/ # browser UI
├── controller/ # local control plane and provider lifecycle
├── runtime/ # Dreamverse websocket/generation runtime
├── providers/ # provider adapters
├── tests/
│ ├── contract/
│ ├── controller/
│ └── smoke/
└── design.md
```
Near-term note:
- `server/` is the current runtime implementation.
- We can keep the code there initially and rename it to `runtime/` only after
the controller lands.
## Trust Model
Dreamverse is local-only for control and secrets.
- The user launches Dreamverse on their own machine.
- Provider API keys are entered into the local app or local CLI.
- The controller uses those credentials to provision or connect to compute.
- The browser never talks to Modal or Runpod directly.
- Dreamverse-hosted infrastructure is not involved.
This is the key reason the provider-based path is acceptable for OSS.
## Responsibility Split
### `apps/web`
The frontend should own:
- UI state, drafts, and local interaction state
- websocket event reduction into client stores
- selection of compute mode and display of cost/health/status
- local forms for provider configuration
- sending prompt requests and rewrite requests to the local controller
The frontend should not own:
- provider credentials after submission
- provider API calls
- runtime lifecycle
- authoritative prompt window after rewrite
- prompt safety or generation policy
### `controller`
The local controller should own:
- provider credential loading and local-only storage
- compute mode selection
- provisioning, reuse, shutdown, and health monitoring of runtimes
- reverse proxying HTTP and websocket traffic from the frontend to the active
runtime
- user-visible status such as provisioning, ready, failed, and idle shutdown
- local persistence for user settings that must survive ephemeral runtimes
The controller should not own:
- prompt rewrite logic
- seed prompt memory semantics
- generation queue behavior
- provider-specific UI state
### `runtime`
The runtime should remain the authoritative owner of:
- `/ws` session state
- prompt rewrite execution
- prompt safety
- seed prompt memory and prompt-window state used for generation
- generation orchestration and GPU worker lifecycle
- websocket event schemas
This preserves the current model and avoids splitting state across layers.
## Runtime Contract
Provider abstraction should happen around a stable Dreamverse runtime contract,
not around infrastructure details.
Minimum runtime surface:
- `GET /healthz`
- `GET /readyz`
- `GET /status`
- `GET/POST /prompt-system-config` if devtools persists config through the
runtime
- curated preset routes if those remain runtime-backed
- `WS /ws`
Important rule:
- The controller only needs to know how to reach a healthy runtime.
- The runtime remains provider-agnostic.
## Provider Abstraction
Use a narrow provider interface:
```python
class ComputeProvider(Protocol):
async def ensure_runtime(self, spec: RuntimeSpec) -> RuntimeHandle: ...
async def wait_until_ready(self, handle: RuntimeHandle) -> None: ...
async def stop_runtime(self, handle: RuntimeHandle) -> None: ...
```
`RuntimeHandle` should include:
- `provider`
- `runtime_id`
- `base_url`
- `ws_url`
- runtime auth headers or tokens if needed
- lifecycle metadata
- cost or hardware metadata for UI display
The controller should work only with `RuntimeHandle`, never with raw SSH hosts
or provider-specific payloads after resolution.
## Provider Notes
### Local
Local mode should be the reference implementation.
- Start the runtime as a local subprocess or connect to an already-running
local runtime URL.
- Reuse the same runtime contract as remote providers.
- Make this the first supported path and the main smoke-test target.
### Runpod
Runpod should be treated as pod lifecycle plus runtime reachability.
- Prefer prepared images or templates that auto-start the Dreamverse runtime.
- Prefer exposed HTTP/TCP ports for steady-state traffic.
- Use SSH only for bootstrap fallback, diagnostics, or repair.
- Avoid a design where the controller shells into the pod for every action.
### Modal
Modal should be treated as deployment-based runtime hosting.
- Wrap the Dreamverse runtime in a thin Modal entrypoint if needed.
- Reuse the same runtime behavior behind that wrapper.
- Do not model Modal as a machine that Dreamverse logs into.
- Do not force the websocket runtime into a per-request serverless handler
shape.
## Config and Persistence
Remote compute may be ephemeral, so mutable user configuration should not live
only inside remote runtimes.
Keep durable state local to the user's machine unless there is a strong reason
otherwise:
- provider selection
- provider credentials or credential references
- default hardware preferences
- editable prompt presets
- prompt system prompt overrides
- idle shutdown policy
Runtime-local state should be treated as disposable unless explicitly synced.
## Prompt Rewrite Ownership
Prompt rewrite remains runtime-owned even after the controller is added.
Frontend responsibilities:
- collect the rewrite instruction
- build the prompt-window snapshot
- display rewrite activity and results
Runtime responsibilities:
- validate and normalize the prompt window
- choose the rewrite system prompt and model settings
- execute rewrite
- apply safety filtering
- replace authoritative seed prompt memory
- emit the canonical completion events
Controller responsibilities:
- proxy the request and response
- surface runtime availability and failure state
This boundary should not move.
## Recommended Rollout
1. Finish the path reorg so docs and code agree on `apps/web`.
2. Introduce `controller/` as a local-only API/proxy process.
3. Keep `server/` as the runtime and adapt it behind the controller.
4. Add `local` provider first.
5. Add "bring your own runtime URL" as an escape hatch.
6. Add automated Runpod provisioning.
7. Add Modal deployment support.
8. Rename `server/` to `runtime/` once the split is stable.
## Implementation Plan
The implementation should start with the smallest milestone that gives users a
working local GPU setup without forcing the controller/provider architecture
into the first patch series.
### Milestone 0: Make local GPU the official baseline
Goal:
- A user with a working `fastvideo` install can run the Dreamverse backend on a
local GPU and connect to it from `apps/web`.
Non-goals for this milestone:
- no controller process yet
- no provider abstraction yet
- no Runpod or Modal support yet
- no secret-management UI yet
Reasoning:
- `server/` already is the real local GPU runtime.
- `apps/web` already knows how to talk to a backend over `/ws` and REST
rewrites.
- The shortest path is to make the existing local path explicit, reliable, and
tested before adding another layer.
### Milestone 0 work items
#### 0.1 Fix repo path assumptions after the frontend move
Current issue:
- Some paths still assume `prod-ui/`, but the frontend now lives at
`apps/web/`.
Required changes:
- update prompt/preset path resolution in `server/config.py`
- update docs that still mention `prod-ui`
- audit any frontend build settings that assume the old repo root
This is prerequisite cleanup. Local GPU mode should not depend on stale
monorepo paths.
#### 0.2 Make local runtime startup the primary supported entrypoint
Required outcome:
- one documented backend command
- one documented frontend command
- one clear env contract for local development
Expected shape:
```bash
uv pip install -e ".[dreamverse]"
dreamverse-server --host 0.0.0.0 --port 8009
cd apps/dreamverse/web
npm ci
BACKEND_HOST=localhost BACKEND_PORT=8009 npm run dev
```
Optional but useful:
- add a small root helper script or Make target for local startup
- add a `dreamverse-doctor` or lightweight startup check later
#### 0.3 Define the minimum local runtime contract
For Milestone 0, the frontend should rely only on the current runtime surface:
- `/ws`
- `/status`
- `/healthz`
- `/readyz`
- existing prompt/devtools routes
Do not add a second local API layer yet unless the current runtime surface is
proven insufficient.
#### 0.4 Make failure states explicit in the UI
Local GPU mode fails in a few predictable ways:
- backend not reachable
- backend reachable but not ready
- `fastvideo` or model runtime missing
- no compatible GPU available
Minimum implementation:
- show a clear connection error when `/ws` or `/status` fails
- surface readiness failures in a human-readable way
- avoid silent retry loops that hide backend startup failures
This is a small UI pass, not a controller project.
#### 0.5 Add a minimal local smoke test path
At this milestone, local GPU support is "done" only if there is a repeatable
test path for the local runtime contract.
Minimum test additions:
- backend tests for `/healthz`, `/readyz`, and `/status`
- a frontend integration test that assumes a reachable backend URL and verifies
connection lifecycle behavior
- one local smoke script that starts the backend and verifies readiness before
the frontend is launched
### Milestone 1: Introduce a thin local controller
Goal:
- Preserve the same local GPU behavior, but place a stable local control-plane
API in front of the runtime.
This should happen only after Milestone 0 is stable.
Scope:
- add `controller/`
- proxy `/ws` and the needed REST routes to `server/`
- expose controller-owned status for "backend starting", "runtime ready", and
"runtime failed"
- optionally spawn the local runtime as a subprocess
Non-goal:
- do not add remote provider logic yet
Reasoning:
- the controller earns its complexity only once it stabilizes the local
contract that future providers will share
### Milestone 2: Provider abstraction on top of the controller
Goal:
- Keep the same frontend contract while allowing the controller to resolve a
runtime via `local`, then later `runpod` and `modal`.
At this point:
- define `ComputeProvider`
- implement `providers/local.py`
- move local-runtime subprocess management behind the provider interface
The first provider should be `local`, because it is cheapest to debug and
matches the runtime most closely.
## Minimal Code Change Order
If we want the shortest path to a working local GPU milestone, the change order
should be:
1. Fix `server/config.py` and any remaining path assumptions from `prod-ui` to
`apps/web`.
2. Update `README.md` to document the real local GPU startup flow.
3. Confirm `apps/dreamverse/web` connects cleanly to the local wrapper-backed backend.
4. Improve frontend error handling for backend-not-ready and backend-missing
cases.
5. Add a local smoke test and keep existing backend/frontend tests green.
6. Only then introduce `controller/`.
## Test Plan for the Local GPU Milestone
### Backend
Keep the current Python test suite as the base:
- `server/tests/test_health_endpoints.py`
- `server/tests/test_mock_server.py`
- `server/tests/test_prompt_enhancer.py`
- `server/tests/test_rewrite_prompt_payload.py`
- related config and logging tests
Add or tighten tests for:
- path resolution in `server/config.py`
- readiness behavior when GPU pool initialization fails
- startup error messaging when `fastvideo` is unavailable
### Frontend
Keep the current Vitest suite as the base:
- websocket reducer tests
- prompt-window snapshot tests
- integration tests under `apps/web/src/app/`
Add or tighten tests for:
- connection failure UX when backend is down
- readiness failure UX when backend returns non-ready status
- backend routing configuration through `BACKEND_HOST` and `BACKEND_PORT`
### Manual smoke path
The first manual smoke checklist should be:
1. start `dreamverse-server`
2. confirm `GET /healthz` returns 200
3. confirm `GET /readyz` returns 200 after warmup
4. start `apps/web`
5. confirm the UI opens and the websocket connects
6. submit a prompt and verify the first generation starts
This checklist should be written down in the README once the milestone is
implemented.
## Test Strategy
The test suite should preserve one rule: provider changes must not be able to
break prompt rewrite, websocket semantics, or runtime behavior silently.
### 1. Runtime unit and integration tests
Keep and expand the current `pytest` coverage in `server/test_*.py`.
Focus areas:
- config loading
- prompt rewrite payload normalization
- prompt enhancement and prompt safety
- health/readiness endpoints
- websocket session behavior
- session logging
- mock runtime behavior
These tests should remain provider-agnostic.
### 2. Controller unit tests
Add a Python test suite for the controller state machine.
Key cases:
- provider selection and validation
- credential loading from local config or env
- runtime lifecycle transitions:
- idle
- provisioning
- ready
- failed
- stopping
- idle timeout and cleanup behavior
- retry and backoff behavior
- HTTP and websocket proxy routing
These tests should use fake providers and fake runtimes by default.
### 3. Provider contract tests
Each provider should pass the same contract tests.
Examples:
- `ensure_runtime()` returns a usable `RuntimeHandle`
- `wait_until_ready()` surfaces timeout vs readiness correctly
- `stop_runtime()` is safe to call twice
- provider errors are mapped into stable controller error types
Use recorded fixtures or fakes wherever possible to avoid spend in CI.
### 4. Web contract tests
The frontend already has useful Vitest coverage under `apps/web/src`.
Preserve that and expand around the controller split.
Priority areas:
- websocket event reduction
- prompt-window snapshot construction
- rewrite request shaping
- compute-status UI
- failure and reconnect UX
The frontend should mock the local controller API, not provider APIs.
### 5. Cross-layer protocol tests
Add contract fixtures that validate shared payloads across layers.
Important fixtures:
- websocket event payloads
- rewrite request payloads
- runtime status payloads
- controller status payloads
These can be simple JSON fixtures validated by both Python and TypeScript
tests. They will catch drift earlier than end-to-end tests.
### 6. Smoke tests
Add a small number of high-signal smoke tests:
- local provider + mock runtime
- local provider + real runtime when GPU is available
- controller startup + frontend health path
These should be cheap enough for routine local use.
### 7. Provider-backed manual or nightly tests
Real Modal and Runpod tests should be opt-in.
- Do not run them in default CI.
- Gate them behind explicit credentials and flags.
- Keep them focused on provisioning and reachability, not full product
regression.
This avoids flaky and expensive CI while still validating real provider flows.
## Testing Recommendations for the Next Step
The next practical additions should be:
1. A controller test suite in Python using fake providers.
2. Shared contract fixtures for websocket and rewrite payloads.
3. A smoke test that starts the local controller against the existing mock
runtime.
I would not add Playwright yet. The current web stack already has Vitest and
integration-style component tests, which are cheaper and better aligned with
the immediate reorg. Add browser automation only after the controller path is
stable.
+577
View File
@@ -0,0 +1,577 @@
<mxfile host="65bd71144e">
<diagram id="gpu-pool" name="gpu_pool.py">
<mxGraphModel dx="1687" dy="3380" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1300" pageHeight="1700" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="title" value="gpu_pool.py — runtime architecture" style="text;html=1;align=center;fontSize=18;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="350" y="-150" width="600" height="30" as="geometry"/>
</mxCell>
<mxCell id="client" value="Client (WebSocket)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=13;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="560" y="-60" width="180" height="60" as="geometry"/>
</mxCell>
<mxCell id="pool" value="GPUPool" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=13;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="560" y="60" width="180" height="60" as="geometry"/>
</mxCell>
<mxCell id="slot_wrap" value="GPUSlot (one per GPU)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e6f2e0;strokeColor=#82b366;strokeWidth=2;verticalAlign=top;align=left;spacingLeft=15;spacingTop=8;fontSize=13;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="40" y="216" width="1220" height="455" as="geometry"/>
</mxCell>
<mxCell id="m_start" value="start()" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;align=left;spacingLeft=8;spacingTop=4;" parent="1" vertex="1">
<mxGeometry x="110" y="270" width="60" height="30" as="geometry"/>
</mxCell>
<mxCell id="m_shutdown" value="shutdown()" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;align=left;spacingLeft=8;spacingTop=4;" parent="1" vertex="1">
<mxGeometry x="180" y="270" width="70" height="30" as="geometry"/>
</mxCell>
<mxCell id="m_join" value="join_user()" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;align=left;spacingLeft=8;spacingTop=4;" parent="1" vertex="1">
<mxGeometry x="499" y="260" width="70" height="40" as="geometry"/>
</mxCell>
<mxCell id="m_step" value="user_step()" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;align=left;spacingLeft=8;spacingTop=4;" parent="1" vertex="1">
<mxGeometry x="580" y="260" width="80" height="40" as="geometry"/>
</mxCell>
<mxCell id="m_leave" value="leave_user()" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;align=left;spacingLeft=8;spacingTop=4;" parent="1" vertex="1">
<mxGeometry x="670" y="255" width="80" height="45" as="geometry"/>
</mxCell>
<mxCell id="m_send" value="_send_command()&lt;br&gt;&lt;br&gt;puts on command_queue,&lt;br&gt;blocks on response_queue.get&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;**for user agonistic commands&lt;/b&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;align=left;spacingLeft=8;spacingTop=4;fontStyle=2;" parent="1" vertex="1">
<mxGeometry x="79" y="370" width="230" height="80" as="geometry"/>
</mxCell>
<mxCell id="m_send_tagged" value="_send_command_tagged()&lt;br&gt;&lt;br&gt;creates Future; registers in&lt;br&gt;_pending_futures[user_id];&lt;br&gt;ensures response reader; &lt;b&gt;awaits future&lt;/b&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;**for user specific commands&lt;/b&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;align=left;spacingLeft=8;spacingTop=4;fontStyle=2;" parent="1" vertex="1">
<mxGeometry x="350" y="374" width="300" height="90" as="geometry"/>
</mxCell>
<mxCell id="m_reader" value="_response_reader()&lt;br&gt;&lt;br&gt;a while loop that reads response_queue;&lt;br&gt;dispatch WorkerEvent:&lt;br&gt;• media → _stream_queues&lt;br&gt;• acks/errors → _pending_futures" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;align=left;spacingLeft=8;spacingTop=4;fontStyle=2;" parent="1" vertex="1">
<mxGeometry x="800" y="360" width="340" height="110" as="geometry"/>
</mxCell>
<mxCell id="c_futures" value="«dict» _pending_futures&#xa;&#xa;{user_id → asyncio.Future}&#xa;&#xa;created by _send_command_tagged;&#xa;resolved + popped by _response_reader;&#xa;cleared on model reload or leave" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=10;align=left;spacingLeft=8;spacingTop=4;" parent="1" vertex="1">
<mxGeometry x="300" y="524" width="340" height="110" as="geometry"/>
</mxCell>
<mxCell id="c_streams" value="«dict» _stream_queues:&amp;nbsp;&lt;div&gt;&lt;span style=&quot;color: rgb(63, 63, 63); background-color: transparent;&quot;&gt;each is a queue of metadata that tell the consumer which part of the shared&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;font color=&quot;#000000&quot;&gt;stream buffer to read next.&lt;br&gt;&lt;/font&gt;&lt;br&gt;{user_id → asyncio.Queue}&lt;br&gt;&lt;br&gt;created by register_stream_queue()&lt;br&gt;written by _response_reader (MediaInit / Chunk / Complete);&lt;br&gt;consumed by&amp;nbsp; AV loop → ws.send_bytes / send_json;&lt;br&gt;popped by leave_user(); cleared on reload&lt;/div&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=10;align=left;spacingLeft=8;spacingTop=4;" parent="1" vertex="1">
<mxGeometry x="720" y="524" width="460" height="126" as="geometry"/>
</mxCell>
<mxCell id="div_line" value="" style="line;strokeColor=#888888;strokeWidth=1;dashed=1;" parent="1" vertex="1">
<mxGeometry x="40" y="690" width="1220" height="10" as="geometry"/>
</mxCell>
<mxCell id="div_label" value="── process boundary (mp.Queue + mp.RawArray) ──" style="text;html=1;align=center;fontSize=11;fontColor=#666666;fontStyle=2;" parent="1" vertex="1">
<mxGeometry x="450" y="688" width="400" height="20" as="geometry"/>
</mxCell>
<mxCell id="cmdq" value="command_queue" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="79" y="720" width="200" height="100" as="geometry"/>
</mxCell>
<mxCell id="respq" value="response_queue" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="520" y="720" width="200" height="100" as="geometry"/>
</mxCell>
<mxCell id="sharedbuf" value="shared_stream_buffer&#xa;(mp.RawArray, 256 MiB)&#xa;&#xa;zero-copy mp4 bytes" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#f8cecc;strokeColor=#b85450;fontSize=11;" parent="1" vertex="1">
<mxGeometry x="900" y="720" width="280" height="100" as="geometry"/>
</mxCell>
<mxCell id="worker_ipc" value="worker_ipc.py — shared types (imported on both sides of the process boundary)&#xa;&#xa;Command(type: CommandType, payload: CommandPayload | None, user_id: str | None)&#xa;CommandPayload = UserStepPayload | WarmupPayload | ReloadModelPayload&#xa;WorkerEvent = StepComplete | WorkerError | JoinAck | LeaveAck | ReloadAck | WarmupComplete | MediaInit | MediaChunk | MediaComplete | InitAck | ShutdownAck" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e0e0e0;strokeColor=#666666;fontSize=10;align=left;spacingLeft=10;spacingTop=6;fontFamily=monospace;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="80" y="860" width="1140" height="80" as="geometry"/>
</mxCell>
<mxCell id="worker_wrap" value="Worker Subprocess - in each GPUSlot" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fef1e0;strokeColor=#d79b00;strokeWidth=2;verticalAlign=top;align=left;spacingLeft=15;spacingTop=8;fontSize=13;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="73" y="1071" width="1140" height="360" as="geometry"/>
</mxCell>
<mxCell id="dispatcher" value="command dispatcher&#xa;&#xa;gpu_worker_process() branches on&#xa;CommandType; asserts payload type&#xa;&#xa;INIT / WARMUP / RELOAD_MODEL&#xa;USER_JOIN / USER_STEP / USER_LEAVE&#xa;SHUTDOWN" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontSize=11;align=left;spacingLeft=10;spacingTop=8;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="120" y="1120" width="240" height="120" as="geometry"/>
</mxCell>
<mxCell id="do_step" value="VideoGenerationWorker.generate_step()&#xa;video_generation.py:380&#xa;&#xa;reads + updates ContinuationState,&#xa;calls generator" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=11;align=left;spacingLeft=10;spacingTop=8;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="460" y="1120" width="240" height="120" as="geometry"/>
</mxCell>
<mxCell id="stream_av" value="stream_fmp4()&#xa;av_streaming.py:121&#xa;&#xa;trims overlap, pipes to ffmpeg,&#xa;publishes StreamInit / StreamChunk /&#xa;StreamComplete via callback" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#b1d8d7;strokeColor=#23445d;fontSize=11;align=left;spacingLeft=10;spacingTop=8;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="800" y="1120" width="260" height="120" as="geometry"/>
</mxCell>
<mxCell id="Ot8BU52QTIb4EhyRSe7I-2" value="" style="edgeStyle=none;html=1;" parent="1" source="generator" target="Ot8BU52QTIb4EhyRSe7I-1" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="generator" value="VideoGenerator (fastvideo)&#xa;&#xa;LTX2 DiT + refine upsampler&#xa;FP4 quant, torch.compile&#xa;&#xa;owned by VideoGenerationWorker&#xa;video_generation.py:211" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=11;" parent="1" vertex="1">
<mxGeometry x="460" y="1300" width="240" height="100" as="geometry"/>
</mxCell>
<mxCell id="ffmpeg" value="ffmpeg subprocess&#xa;&#xa;libx264 / *_nvenc&#xa;fragmented mp4" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#d79b00;fontSize=11;" parent="1" vertex="1">
<mxGeometry x="800" y="1300" width="260" height="100" as="geometry"/>
</mxCell>
<mxCell id="caches" value="ContinuationState&#xa;video_generation.py:89&#xa;&#xa;• video_images: list[PIL.Image]&#xa;• audio_latents: torch.Tensor (CPU)&#xa;&#xa;carried across segments" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=11;align=left;spacingLeft=10;spacingTop=8;" parent="1" vertex="1">
<mxGeometry x="120" y="1300" width="240" height="100" as="geometry"/>
</mxCell>
<mxCell id="e_cp" value="acquire" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#6c8ebf;endArrow=classic;fontSize=11;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="client" target="pool" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="e_ps" value="assigns slot" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#82b366;endArrow=classic;fontSize=11;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="pool" target="slot_wrap" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="e_start_send" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#82b366;endArrow=classic;fontSize=10;" parent="1" target="m_send" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="138" y="300" as="sourcePoint"/>
<Array as="points">
<mxPoint x="138" y="380"/>
<mxPoint x="138" y="380"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e_join_tag" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#82b366;endArrow=classic;fontSize=10;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="m_join" target="m_send_tagged" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="531.6999999999998" y="400.3600000000001" as="targetPoint"/>
<Array as="points">
<mxPoint x="532" y="300"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e_step_tag" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#82b366;endArrow=classic;fontSize=10;exitX=0.1;exitY=1;exitDx=0;exitDy=0;" parent="1" source="m_step" target="m_send_tagged" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="588" y="400" as="targetPoint"/>
<Array as="points">
<mxPoint x="588" y="337"/>
<mxPoint x="590" y="337"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e_leave_tag" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#82b366;endArrow=classic;fontSize=10;exitX=0.1;exitY=1;exitDx=0;exitDy=0;entryX=0.874;entryY=-0.002;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="m_leave" target="m_send_tagged" edge="1">
<mxGeometry x="-0.1388" relative="1" as="geometry">
<Array as="points">
<mxPoint x="680" y="300"/>
<mxPoint x="680" y="330"/>
<mxPoint x="612" y="330"/>
</Array>
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_tag_futures" value="register future" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#d6b656;endArrow=classic;fontSize=10;exitX=0.397;exitY=0.989;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" source="m_send_tagged" target="c_futures" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="e_reader_futures" value="&lt;b&gt;set future result&lt;/b&gt;" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#d6b656;endArrow=classic;fontSize=10;exitX=0.008;exitY=0.908;exitDx=0;exitDy=0;entryX=1;entryY=0.3;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" source="m_reader" target="c_futures" edge="1">
<mxGeometry x="-0.325" relative="1" as="geometry">
<Array as="points">
<mxPoint x="803" y="470"/>
<mxPoint x="670" y="470"/>
<mxPoint x="670" y="557"/>
</Array>
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_reader_streams" value="put(MediaEvent)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#d6b656;endArrow=classic;fontSize=10;exitX=0.146;exitY=1.008;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.282;entryY=0.021;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="m_reader" target="c_streams" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points"/>
<mxPoint x="850" y="522" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="e_send_cmdq" value="put Command" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;fontSize=11;entryX=0.35;entryY=0;entryDx=0;entryDy=0;" parent="1" source="m_send" target="cmdq" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="150" y="585"/>
<mxPoint x="149" y="585"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e_tag_cmdq" value="put Command" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;fontSize=11;exitX=0.022;exitY=0.97;exitDx=0;exitDy=0;entryX=0.85;entryY=0;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" source="m_send_tagged" target="cmdq" edge="1">
<mxGeometry x="0.1755" relative="1" as="geometry">
<Array as="points">
<mxPoint x="357" y="490"/>
<mxPoint x="250" y="490"/>
<mxPoint x="250" y="592"/>
<mxPoint x="249" y="592"/>
</Array>
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_respq_reader" value="WorkerEvent" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;fontSize=11;exitX=0.753;exitY=0.056;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" source="respq" target="m_reader" edge="1">
<mxGeometry x="0.7113" relative="1" as="geometry">
<Array as="points">
<mxPoint x="671" y="720"/>
<mxPoint x="670" y="720"/>
<mxPoint x="670" y="415"/>
</Array>
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_respq_send" value="blocking .get" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;dashed=1;fontSize=10;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="respq" target="m_send" edge="1">
<mxGeometry x="-0.2187" relative="1" as="geometry">
<Array as="points">
<mxPoint x="520" y="680"/>
<mxPoint x="200" y="680"/>
</Array>
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_cd" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;fontSize=11;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" parent="1" source="cmdq" edge="1" target="dispatcher">
<mxGeometry relative="1" as="geometry">
<mxPoint x="310" y="1120" as="targetPoint"/>
<Array as="points">
<mxPoint x="179" y="970"/>
<mxPoint x="180" y="970"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e_dds" value="USER_STEP" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#d79b00;endArrow=classic;fontSize=10;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="dispatcher" target="do_step" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="e_dr" value="StepComplete / ack types" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;fontSize=10;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.15;entryY=1;entryDx=0;entryDy=0;" parent="1" source="dispatcher" target="respq" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="240" y="1000"/>
<mxPoint x="550" y="1000"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e_dsg" value="generator.generate_video()" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#9673a6;endArrow=classic;fontSize=10;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="do_step" target="generator" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="e_dscache" value="read / write" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#d6b656;endArrow=classic;startArrow=classic;fontSize=10;exitX=0;exitY=0.8;exitDx=0;exitDy=0;entryX=1;entryY=0.2;entryDx=0;entryDy=0;" parent="1" source="do_step" target="caches" edge="1">
<mxGeometry relative="1" as="geometry"/>
<Array as="points">
<mxPoint x="420" y="1058"/>
<mxPoint x="420" y="1150"/>
</Array>
</mxCell>
<mxCell id="e_dsstream" value="frames + audio" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#d79b00;endArrow=classic;fontSize=10;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="do_step" target="stream_av" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="e_sf" value="rawvideo + wav" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#d79b00;endArrow=classic;fontSize=10;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="stream_av" target="ffmpeg" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="e_ssb" value="write mp4 bytes&#xa;at shared_write_offset" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;fontSize=10;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="stream_av" target="sharedbuf" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="e_sbs" value="bytes from the buffer are sent through websocket&amp;nbsp;&lt;div&gt;based on info from the stream queue&lt;/div&gt;" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;dashed=1;fontSize=10;exitX=0.591;exitY=0.027;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" source="sharedbuf" target="c_streams" edge="1">
<mxGeometry x="0.0048" relative="1" as="geometry">
<Array as="points">
<mxPoint x="1066" y="720"/>
</Array>
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="e_scl" value="session AV loop (main.py:2124):&#xa;ws.send_bytes(chunk) — binary mp4 frames&#xa;ws.send_json({type: media_init | media_segment_complete | ...})" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#6c8ebf;endArrow=classic;fontSize=10;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="c_streams" target="client" edge="1">
<mxGeometry x="0.0003" relative="1" as="geometry">
<mxPoint as="offset"/>
<Array as="points">
<mxPoint x="1310" y="577"/>
<mxPoint x="1310" y="-30"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="e_sr" value="MediaChunk (offset, len)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;fontSize=10;exitX=0.1;exitY=0;exitDx=0;exitDy=0;entryX=0.85;entryY=1;entryDx=0;entryDy=0;" parent="1" source="stream_av" target="respq" edge="1">
<mxGeometry relative="1" as="geometry"/>
<Array as="points">
<mxPoint x="826" y="880"/>
<mxPoint x="690" y="880"/>
</Array>
</mxCell>
<mxCell id="legend" value="Legend&#xa;&#xa;■ blue client / external&#xa;■ green main-process pool/slot&#xa; (methods — italic label)&#xa;■ yellow containers (routing state)&#xa;■ red IPC primitives (mp.Queue, mp.RawArray)&#xa;&#xa;Worker subprocess modules:&#xa;■ orange gpu_pool.py (dispatcher)&#xa;■ lavender video_generation.py&#xa;■ teal av_streaming.py&#xa;■ gray worker_ipc.py (shared types)&#xa;&#xa;Flow:&#xa; client → pool → slot&#xa; → _send_command(_tagged) → command_queue&#xa; → dispatcher → generate_step()&#xa; → stream_fmp4() → ffmpeg&#xa; → shared_buf + response_queue&#xa; → _response_reader → futures / stream_queues&#xa; → client awaits (via main.py AV loop)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#999999;fontSize=11;align=left;spacingLeft=10;spacingTop=8;" parent="1" vertex="1">
<mxGeometry x="39" y="-200" width="270" height="380" as="geometry"/>
</mxCell>
<mxCell id="Ot8BU52QTIb4EhyRSe7I-1" value="FastVideo video_generator" style="whiteSpace=wrap;html=1;fontSize=11;fillColor=#e1d5e7;strokeColor=#9673a6;rounded=1;" parent="1" vertex="1">
<mxGeometry x="520" y="1470" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#82b366;endArrow=classic;fontSize=10;exitX=0.421;exitY=1.01;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.569;entryY=0.02;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="m_shutdown" target="m_send">
<mxGeometry relative="1" as="geometry">
<mxPoint x="260" y="300" as="sourcePoint"/>
<mxPoint x="260" y="400" as="targetPoint"/>
<Array as="points"/>
</mxGeometry>
</mxCell>
<mxCell id="6" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.983;entryY=0.021;entryDx=0;entryDy=0;entryPerimeter=0;strokeColor=#61A25D;" edge="1" parent="1" source="m_join" target="m_send">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="534" y="320"/>
<mxPoint x="306" y="320"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="11" value="reload model" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="6">
<mxGeometry x="0.1965" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="9" value="" style="group" vertex="1" connectable="0" parent="1">
<mxGeometry x="1340" y="750" width="240" height="40" as="geometry"/>
</mxCell>
<mxCell id="7" value="" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=6.353056066176691;fillColor=#f8cecc;strokeColor=#b85450;fontSize=11;" vertex="1" parent="9">
<mxGeometry y="4" width="21.39" height="22" as="geometry"/>
</mxCell>
<mxCell id="8" value="Queue: owned by GPUSlot, passed to worker process as argument." style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="9">
<mxGeometry x="21.3900000000001" width="218.61" height="40" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram id="gpu-pool-boot" name="boot &amp; ws">
<mxGraphModel grid="1" page="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" pageScale="1" pageWidth="1300" pageHeight="1900" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="b_title" value="gpu_pool.py — boot flow &amp; websocket session" style="text;html=1;align=center;fontSize=18;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="350" y="20" width="600" height="30" as="geometry"/>
</mxCell>
<mxCell id="secA" value="A. Call chain" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1f5fe;strokeColor=#039be5;strokeWidth=2;verticalAlign=top;align=left;spacingLeft=15;spacingTop=8;fontSize=13;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="40" y="70" width="400" height="420" as="geometry"/>
</mxCell>
<mxCell id="a1" value="$ uv run dreamverse-server" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#039be5;fontSize=12;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="70" y="120" width="340" height="40" as="geometry"/>
</mxCell>
<mxCell id="a2" value="server_entry.cli()&#xa;(pyproject.toml [project.scripts])&#xa;&#xa;guards missing fastvideo extra" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#039be5;fontSize=11;align=left;spacingLeft=10;" parent="1" vertex="1">
<mxGeometry x="70" y="180" width="340" height="70" as="geometry"/>
</mxCell>
<mxCell id="a3" value="main.cli() (main.py:2325)&#xa;&#xa;argparse --host/--port&#xa;uvicorn.run(app, host, port)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#039be5;fontSize=11;align=left;spacingLeft=10;" parent="1" vertex="1">
<mxGeometry x="70" y="270" width="340" height="80" as="geometry"/>
</mxCell>
<mxCell id="a4" value="FastAPI(lifespan=lifespan)&#xa;(main.py:147)&#xa;&#xa;on startup → enter lifespan ctx → Section B" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#039be5;fontSize=11;align=left;spacingLeft=10;" parent="1" vertex="1">
<mxGeometry x="70" y="370" width="340" height="90" as="geometry"/>
</mxCell>
<mxCell id="ea12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#039be5;endArrow=classic;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="a1" target="a2" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="ea23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#039be5;endArrow=classic;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="a2" target="a3" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="ea34" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#039be5;endArrow=classic;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="a3" target="a4" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="secB" value="B. lifespan() zoom (main.py:116-144)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f3e5f5;strokeColor=#8e24aa;strokeWidth=2;verticalAlign=top;align=left;spacingLeft=15;spacingTop=8;fontSize=13;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="480" y="70" width="780" height="420" as="geometry"/>
</mxCell>
<mxCell id="b1" value="1. gpu_ids = get_available_gpus()&#xa; respects FASTVIDEO_GPU_COUNT env" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#8e24aa;fontSize=11;align=left;spacingLeft=10;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="510" y="115" width="720" height="45" as="geometry"/>
</mxCell>
<mxCell id="b2" value="2. gpu_pool = GPUPool(gpu_ids)&#xa; creates one GPUSlot per GPU (not yet started)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#8e24aa;fontSize=11;align=left;spacingLeft=10;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="510" y="170" width="720" height="45" as="geometry"/>
</mxCell>
<mxCell id="b3" value="3. await gpu_pool.initialize()&#xa; asyncio.create_task(_init_gpu(id)) for each GPU&#xa; returns immediately — GPUs init in background → Section C" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff9c4;strokeColor=#8e24aa;fontSize=11;align=left;spacingLeft=10;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="510" y="225" width="720" height="60" as="geometry"/>
</mxCell>
<mxCell id="b4" value="4. prompt_enhancer = PromptEnhancer()&#xa;5. session_event_logger = SessionEventLogger(...)&#xa;6. prompt_safety_filter = PromptSafetyFilter() (optional)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#8e24aa;fontSize=11;align=left;spacingLeft=10;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="510" y="295" width="720" height="55" as="geometry"/>
</mxCell>
<mxCell id="byield" value="yield&#xa;server is now accepting HTTP + WS&#xa;/healthz → 200 /readyz → 503 until GPUs warm up" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#c8e6c9;strokeColor=#388e3c;fontSize=11;align=left;spacingLeft=10;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="510" y="360" width="720" height="55" as="geometry"/>
</mxCell>
<mxCell id="bshut" value="[on shutdown] await gpu_pool.shutdown()" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffcdd2;strokeColor=#c62828;fontSize=11;align=left;spacingLeft=10;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="510" y="425" width="720" height="35" as="geometry"/>
</mxCell>
<mxCell id="eab" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#666;endArrow=classic;dashed=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.2;entryDx=0;entryDy=0;" parent="1" source="a4" target="b1" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="secC" value="C. GPUSlot.start() zoom — runs in background per GPU (gpu_pool.py:426)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fff3e0;strokeColor=#ef6c00;strokeWidth=2;verticalAlign=top;align=left;spacingLeft=15;spacingTop=8;fontSize=13;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="40" y="530" width="1220" height="520" as="geometry"/>
</mxCell>
<mxCell id="cparent" value="Parent (main) process" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#ffe0b2;strokeColor=#ef6c00;fontSize=12;fontStyle=1;verticalAlign=top;align=left;spacingLeft=10;spacingTop=5;" parent="1" vertex="1">
<mxGeometry x="70" y="580" width="380" height="440" as="geometry"/>
</mxCell>
<mxCell id="cp1" value="mp.get_context(&#39;spawn&#39;)&#xa;cmd_q, resp_q = ctx.Queue(), ctx.Queue()&#xa;(optional) shared_stream_buffer = mp.RawArray(&#39;B&#39;, 256 MiB)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#ef6c00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="85" y="620" width="350" height="60" as="geometry"/>
</mxCell>
<mxCell id="cp2" value="ctx.Process(target=gpu_worker_process,&#xa; args=(gpu_id, cuda_device, cmd_q, resp_q, ...))&#xa;process.start()" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#ef6c00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="85" y="690" width="350" height="60" as="geometry"/>
</mxCell>
<mxCell id="cp3" value="_send_command(Command(INIT), timeout=600s)&#xa; await response on resp_q" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#ef6c00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="85" y="760" width="350" height="50" as="geometry"/>
</mxCell>
<mxCell id="cp4" value="if STARTUP_WARMUP_ENABLED:&#xa; _send_command(Command(WARMUP, prompt=...),&#xa; timeout=STARTUP_WARMUP_TIMEOUT)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#ef6c00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="85" y="820" width="350" height="60" as="geometry"/>
</mxCell>
<mxCell id="cp5" value="slot.ready = True" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#c8e6c9;strokeColor=#388e3c;fontSize=11;fontStyle=1;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="85" y="890" width="350" height="35" as="geometry"/>
</mxCell>
<mxCell id="cp6" value="parent now enters pool as a ready slot;&#xa;/readyz flips to 200 once ≥1 slot is ready" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#888;fontSize=10;align=left;spacingLeft=8;fontStyle=2;" parent="1" vertex="1">
<mxGeometry x="85" y="935" width="350" height="45" as="geometry"/>
</mxCell>
<mxCell id="cipc" value="IPC" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#ffcdd2;strokeColor=#b71c1c;fontSize=12;fontStyle=1;verticalAlign=top;align=center;spacingTop=5;" parent="1" vertex="1">
<mxGeometry x="480" y="580" width="150" height="440" as="geometry"/>
</mxCell>
<mxCell id="ccmdq" value="command_queue&#xa;(mp.Queue)" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="490" y="650" width="130" height="80" as="geometry"/>
</mxCell>
<mxCell id="crespq" value="response_queue&#xa;(mp.Queue)" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="490" y="760" width="130" height="80" as="geometry"/>
</mxCell>
<mxCell id="cshared" value="shared_stream_buffer&#xa;(mp.RawArray)" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#f8cecc;strokeColor=#b85450;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="490" y="870" width="130" height="80" as="geometry"/>
</mxCell>
<mxCell id="cworker" value="Worker subprocess (gpu_worker_process, gpu_pool.py:151)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fef1e0;strokeColor=#d79b00;fontSize=12;fontStyle=1;verticalAlign=top;align=left;spacingLeft=10;spacingTop=5;" parent="1" vertex="1">
<mxGeometry x="660" y="580" width="580" height="440" as="geometry"/>
</mxCell>
<mxCell id="cw1" value="os.environ[&#39;CUDA_VISIBLE_DEVICES&#39;] = cuda_device&#xa;os.environ[&#39;FASTVIDEO_ATTENTION_BACKEND&#39;] = &#39;FLASH_ATTN&#39;&#xa;&#xa;must be set BEFORE importing torch" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#d79b00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="675" y="620" width="550" height="65" as="geometry"/>
</mxCell>
<mxCell id="cw2" value="from fastvideo.entrypoints.video_generator import VideoGenerator&#xa;from fastvideo.models.dits.ltx2 import DEFAULT_LTX2_AUDIO_*&#xa;&#xa;** Dreamverse reaches into fastvideo internals here **" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe0b2;strokeColor=#d79b00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="675" y="695" width="550" height="60" as="geometry"/>
</mxCell>
<mxCell id="cw3" value="on Command(INIT):&#xa; VideoGenerationWorker.initialize() (video_generation.py:247)&#xa; maybe_download_model(model_id)&#xa; VideoGenerator.from_pretrained(path, FP4Config, PipelineConfig)&#xa; load audio VAE, resolve refine upsampler&#xa; resp_q.put(InitAck(success=True))" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#d79b00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="675" y="765" width="550" height="95" as="geometry"/>
</mxCell>
<mxCell id="cw4" value="on Command(WARMUP) with WarmupPayload:&#xa; VideoGenerationWorker.warmup(payload.prompt) (video_generation.py:518)&#xa; two synthetic segments prime caches + torch.compile&#xa; resp_q.put(WarmupComplete(timings=...))" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#d79b00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="675" y="870" width="550" height="55" as="geometry"/>
</mxCell>
<mxCell id="cw5" value="enter main worker loop → waits for JOIN_USER / USER_STEP / LEAVE" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#c8e6c9;strokeColor=#388e3c;fontSize=11;fontStyle=1;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="675" y="935" width="550" height="40" as="geometry"/>
</mxCell>
<mxCell id="cea1" value="Command" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;fontSize=10;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="cp3" target="ccmdq" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="cea2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="ccmdq" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="675" y="780" as="targetPoint"/>
<Array as="points">
<mxPoint x="648" y="690"/>
<mxPoint x="648" y="780"/>
<mxPoint x="675" y="780"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="cea3" value="WorkerEvent" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;fontSize=10;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.969;entryY=0.798;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" target="crespq" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="684" y="825.5" as="sourcePoint"/>
<mxPoint x="629" y="813" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="cea4" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#b85450;endArrow=classic;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.995;entryY=0.887;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" target="cp3" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="490" y="804" as="sourcePoint"/>
<mxPoint x="435" y="789" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="secD" value="D. WebSocket session — after startup" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e8f5e9;strokeColor=#2e7d32;strokeWidth=2;verticalAlign=top;align=left;spacingLeft=15;spacingTop=8;fontSize=13;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="40" y="1080" width="1220" height="820" as="geometry"/>
</mxCell>
<mxCell id="dc_head" value="Browser / WS" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=11;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="80" y="1130" width="160" height="40" as="geometry"/>
</mxCell>
<mxCell id="ds_head" value="main.py / Session" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=11;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="290" y="1130" width="180" height="40" as="geometry"/>
</mxCell>
<mxCell id="dp_head" value="GPUPool" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=11;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="520" y="1130" width="160" height="40" as="geometry"/>
</mxCell>
<mxCell id="dsl_head" value="GPUSlot" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e6f2e0;strokeColor=#82b366;fontSize=11;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="730" y="1130" width="160" height="40" as="geometry"/>
</mxCell>
<mxCell id="dw_head" value="Worker subprocess" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fef1e0;strokeColor=#d79b00;fontSize=11;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="940" y="1130" width="200" height="40" as="geometry"/>
</mxCell>
<mxCell id="dc_life" style="endArrow=none;html=1;strokeColor=#6c8ebf;dashed=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="160" y="1175" as="sourcePoint"/>
<mxPoint x="160" y="1885" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="ds_life" style="endArrow=none;html=1;strokeColor=#82b366;dashed=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="380" y="1175" as="sourcePoint"/>
<mxPoint x="380" y="1885" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dp_life" style="endArrow=none;html=1;strokeColor=#82b366;dashed=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="600" y="1175" as="sourcePoint"/>
<mxPoint x="600" y="1885" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dsl_life" style="endArrow=none;html=1;strokeColor=#82b366;dashed=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="810" y="1175" as="sourcePoint"/>
<mxPoint x="810" y="1885" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dw_life" style="endArrow=none;html=1;strokeColor=#d79b00;dashed=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1040" y="1175" as="sourcePoint"/>
<mxPoint x="1040" y="1885" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dm1" value="1. WS connect /ws + session_init_v2" style="endArrow=classic;html=1;strokeColor=#6c8ebf;fontSize=10;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="160" y="1200" as="sourcePoint"/>
<mxPoint x="380" y="1200" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dm2" value="2. pool.acquire(client_id, ws)" style="endArrow=classic;html=1;strokeColor=#82b366;fontSize=10;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="380" y="1240" as="sourcePoint"/>
<mxPoint x="600" y="1240" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dm3a" value="3a. if slot free: (gpu_id, slot)" style="endArrow=classic;html=1;strokeColor=#82b366;fontSize=10;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="600" y="1280" as="sourcePoint"/>
<mxPoint x="380" y="1280" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dm3b" value="3b. else: enqueue, emit queue_status until a slot frees" style="endArrow=classic;html=1;strokeColor=#c62828;fontSize=10;dashed=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="600" y="1320" as="sourcePoint"/>
<mxPoint x="160" y="1320" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dm4" value="4. emit gpu_assigned" style="endArrow=classic;html=1;strokeColor=#6c8ebf;fontSize=10;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="380" y="1360" as="sourcePoint"/>
<mxPoint x="160" y="1360" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dm5" value="5. slot._send_command_tagged(Command(USER_JOIN))" style="endArrow=classic;html=1;strokeColor=#82b366;fontSize=10;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="380" y="1400" as="sourcePoint"/>
<mxPoint x="810" y="1400" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dm6" value="6. command_queue.put(JOIN_USER)" style="endArrow=classic;html=1;strokeColor=#b85450;fontSize=10;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="810" y="1440" as="sourcePoint"/>
<mxPoint x="1040" y="1440" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dssteady" value="── steady state: per prompt ──" style="text;html=1;align=center;fontSize=11;fontColor=#666;fontStyle=2;" parent="1" vertex="1">
<mxGeometry x="300" y="1470" width="600" height="25" as="geometry"/>
</mxCell>
<mxCell id="dm7" value="7. append_prompt / simple_generate" style="endArrow=classic;html=1;strokeColor=#6c8ebf;fontSize=10;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="160" y="1510" as="sourcePoint"/>
<mxPoint x="380" y="1510" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dm9" value="8. slot._send_command_tagged(Command(USER_STEP, payload=UserStepPayload(prompt, segment_idx, image_path, reset_conditioning)))" style="endArrow=classic;html=1;strokeColor=#82b366;fontSize=10;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="380" y="1560" as="sourcePoint"/>
<mxPoint x="810" y="1560" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dm10" value="9. command_queue.put(USER_STEP)" style="endArrow=classic;html=1;strokeColor=#b85450;fontSize=10;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="810" y="1610" as="sourcePoint"/>
<mxPoint x="1040" y="1610" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="dm11a" value="10a. worker runs:&#xa;VideoGenerationWorker.generate_step()&#xa; (video_generation.py:380)&#xa; → generator.generate_video()&#xa; → updates ContinuationState&#xa;then stream_fmp4() (av_streaming.py:121)&#xa; → ffmpeg (rawvideo+wav → fmp4)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe0b2;strokeColor=#d79b00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
<mxGeometry x="955" y="1640" width="180" height="70" as="geometry"/>
</mxCell>
<mxCell id="dm11" value="10b. resp_q.put(MediaInit / MediaChunk / MediaComplete / StepComplete)" style="endArrow=classic;html=1;strokeColor=#b85450;fontSize=10;labelBackgroundColor=#ffffff;" parent="1" edge="1">
<mxGeometry x="0.0435" relative="1" as="geometry">
<mxPoint x="1092" y="1733" as="sourcePoint"/>
<mxPoint x="862" y="1733" as="targetPoint"/>
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="dm12" value="11. _response_reader isinstance-dispatches: MediaChunk/etc → _stream_queues[user_id]; StepComplete → _pending_futures" style="endArrow=classic;html=1;strokeColor=#d6b656;fontSize=10;" parent="1" edge="1">
<mxGeometry x="0.0014" relative="1" as="geometry">
<mxPoint x="810" y="1760" as="sourcePoint"/>
<mxPoint x="380" y="1760" as="targetPoint"/>
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="dm13a" value="12a. binary WS frame (mp4 bytes)" style="endArrow=classic;html=1;strokeColor=#6c8ebf;fontSize=10;labelBackgroundColor=#ffffff;" parent="1" edge="1">
<mxGeometry x="-0.0909" relative="1" as="geometry">
<mxPoint x="380" y="1795" as="sourcePoint"/>
<mxPoint x="160" y="1795" as="targetPoint"/>
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="dm13b" value="12b. JSON WS event (media_init, media_segment_complete, prompt_ready, …)" style="endArrow=classic;html=1;strokeColor=#6c8ebf;fontSize=10;labelBackgroundColor=#ffffff;" parent="1" edge="1">
<mxGeometry x="-0.0909" y="5" relative="1" as="geometry">
<mxPoint x="380" y="1825" as="sourcePoint"/>
<mxPoint x="160" y="1825" as="targetPoint"/>
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="dm14" value="13. on WS close: pool.release(client_id) → Command(USER_LEAVE) → LeaveAck" style="endArrow=classic;html=1;strokeColor=#c62828;fontSize=10;dashed=1;labelBackgroundColor=#ffffff;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="160" y="1865" as="sourcePoint"/>
<mxPoint x="1040" y="1865" as="targetPoint"/>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 85 KiB

+41
View File
@@ -0,0 +1,41 @@
[project]
name = "dreamverse"
version = "0.1.0"
description = "Dreamverse application with FastAPI backend and Next.js frontend."
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"fastapi>=0.115,<1",
"fastvideo>=0.1.7",
"pillow>=10.0",
"uvicorn[standard]>=0.30,<1",
]
[project.optional-dependencies]
server = [
"cerebras-cloud-sdk",
"flash-attn-cute @ git+https://github.com/XOR-op/flash-attention.git@fa4-compile#subdirectory=flash_attn/cute",
"flashinfer-python",
"openai>=1.40",
]
safety = [
"fasttext>=0.9.2",
]
test = [
"pytest>=8.0",
]
[project.scripts]
dreamverse-server = "dreamverse.server_entry:cli"
dreamverse-mock-server = "dreamverse.mock_server:cli"
[tool.uv]
package = false
[tool.uv.sources]
fastvideo = { workspace = true }
[tool.pytest.ini_options]
markers = [
"gpu: requires real GPU + model weights; skip in CI",
]