Co-authored-by: Junda (David) Su <90978028+Davids048@users.noreply.github.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>
123 lines
3.6 KiB
Python
123 lines
3.6 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
"""Mock streaming server — a frontend dev aid.
|
|
|
|
Boots the same FastAPI app the real streaming server uses, but backs
|
|
it with :class:`InProcessGpuPool` wrapping a synthetic generator that
|
|
emits pre-baked RGB frames. No GPU or model weights required.
|
|
|
|
Use cases:
|
|
|
|
* Frontend development without a real model loaded.
|
|
* Integration tests that exercise the WS protocol end-to-end.
|
|
* Reproducing protocol bugs locally.
|
|
|
|
Launch: ``python -m fastvideo.entrypoints.streaming.mock_server``.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import time
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
import numpy as np
|
|
|
|
from fastvideo.api.schema import (
|
|
ContinuationState,
|
|
GenerationRequest,
|
|
GeneratorConfig,
|
|
SamplingConfig,
|
|
ServeConfig,
|
|
StreamingConfig,
|
|
)
|
|
from fastvideo.entrypoints.streaming.server import build_app
|
|
|
|
|
|
@dataclass
|
|
class MockGenerator:
|
|
"""Generator stand-in that returns synthetic gradient frames.
|
|
|
|
Each call produces one segment worth of frames whose pixels vary by
|
|
a constant derived from the request seed and segment index. Latency
|
|
is configurable via ``sleep_ms`` so the caller can exercise slow-
|
|
generate scenarios without spinning a GPU.
|
|
"""
|
|
|
|
sleep_ms: float = 0.0
|
|
|
|
def generate(self, request: GenerationRequest) -> dict[str, Any]:
|
|
if self.sleep_ms:
|
|
time.sleep(self.sleep_ms / 1000.0)
|
|
width = max(16, request.sampling.width)
|
|
height = max(16, request.sampling.height)
|
|
num_frames = max(1, request.sampling.num_frames)
|
|
frames = [_gradient_frame(height, width, idx, seed=request.sampling.seed) for idx in range(num_frames)]
|
|
state = ContinuationState(
|
|
kind="ltx2.v1",
|
|
payload={
|
|
"schema_version": 1,
|
|
"segment_index": 0,
|
|
"source_prompt": request.prompt,
|
|
},
|
|
)
|
|
return {
|
|
"frames": frames,
|
|
"audio_sample_rate": 24000,
|
|
"state": state,
|
|
}
|
|
|
|
|
|
def _gradient_frame(height: int, width: int, idx: int, *, seed: int) -> np.ndarray:
|
|
base = (idx * 17 + seed * 3) % 256
|
|
row = np.linspace(base, (base + 64) % 256, width, dtype=np.uint8)
|
|
frame = np.tile(row, (height, 1))
|
|
stacked = np.stack([frame, np.roll(frame, 8, axis=1), np.roll(frame, 16, axis=1)], axis=-1)
|
|
return stacked.astype(np.uint8)
|
|
|
|
|
|
def build_mock_app(*, sleep_ms: float = 0.0):
|
|
"""Build a FastAPI app backed by :class:`MockGenerator`."""
|
|
serve_config = ServeConfig(
|
|
generator=GeneratorConfig(model_path="/models/mock"),
|
|
streaming=StreamingConfig(
|
|
session_timeout_seconds=120,
|
|
generation_segment_cap=6,
|
|
),
|
|
)
|
|
serve_config.default_request.sampling = SamplingConfig(
|
|
num_frames=24,
|
|
height=256,
|
|
width=256,
|
|
fps=24,
|
|
num_inference_steps=1,
|
|
)
|
|
return build_app(serve_config, MockGenerator(sleep_ms=sleep_ms))
|
|
|
|
|
|
def main() -> None: # pragma: no cover - CLI entry
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--host", default="127.0.0.1")
|
|
parser.add_argument("--port", type=int, default=8000)
|
|
parser.add_argument(
|
|
"--sleep-ms",
|
|
type=float,
|
|
default=0.0,
|
|
help="Per-segment artificial latency for testing slow paths",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
import uvicorn
|
|
|
|
app = build_mock_app(sleep_ms=args.sleep_ms)
|
|
uvicorn.run(app, host=args.host, port=args.port)
|
|
|
|
|
|
__all__ = [
|
|
"MockGenerator",
|
|
"build_mock_app",
|
|
"main",
|
|
]
|
|
|
|
if __name__ == "__main__": # pragma: no cover - CLI entry
|
|
main()
|