Files
hao-ai-lab-FastVideo/v2/core/request/requests.py
T
Will Lin 1fe50c0092 [refactor] v2: group flat top-level into planes; isolate vendored under _vendor/
The v2 top level had grown to ~27 dirs + 13 loose files — half of them
single-concept abstraction shells (memory/ 86 LOC, transport/ 222, parity/ 178,
extend/ 226) and half vendored fastvideo code sitting as peers to the actual v2
design. Regroup to mirror README section 3 "Planes & dependency order":

  core/     enums+types, card, loop, program, parity, request, parallel
            (the model-native contracts; no kernels)
  runtime/  + folded-in substrate: cache, memory, transport, extend
            (the import graph shows only runtime consumes them; compile/cudagraph
            already lived here)
  serving/  + deploy/ (products / fleet)
  _vendor/  all copied fastvideo: models, layers, attention, configs, distributed,
            platforms, api, hooks, logging_utils, third_party + fastvideo_args/
            utils/logger/envs/forward_context — internal layout unchanged, still
            mirrors upstream for diffing

28 dirs + 13 root files -> 8 dirs + 6 root files. Pure mechanical move: 947
absolute-import paths rewritten (v2.X -> v2.{core,runtime,serving,_vendor}.X),
boundary-anchored so platform/ (native dispatch) and platforms/ (vendored CUDA
detect) no longer collide and neither does hooks/ vs extend/. Deleted the empty
v2/loader/. README section 3 + 16 updated; stale test count corrected
(34 files/216 tests -> 22 files/143 tests).

Validated: `import v2` stays torch-free; `v2/run_tests.py` and `pytest v2/tests/`
-> 143 passed, 2 skipped on the numpy toy backend. (On a GPU box force the toy
backend with CUDA_VISIBLE_DEVICES="" or detect() picks cuda.) No external importer
changed — examples use the re-exported `from v2 import VideoGenerator`.
2026-07-04 17:15:34 +00:00

103 lines
3.6 KiB
Python

"""Request + Session — the only currency crossing the product boundary.
A typed ``Request`` carries ``task`` (declared, never inferred), ``inputs``
(ModalParts), AR ``sampling`` vs ``diffusion`` params, an ``OutputSpec``, and
per-node overrides. Products construct Requests; they never reach into the model.
"""
from __future__ import annotations
import itertools
from dataclasses import dataclass, field
from v2.core.request.cancel import CancelKind, CancelScope
from v2.core.request.modalpart import ImagePart, ModalPart, TextPart
from v2.core.request.params import DiffusionParams, OutputSpec, SamplingParams
from v2.core.request.streams import Stream
from v2.core.request.tasks import TaskType
_counter = itertools.count(1)
def new_request_id() -> str:
return f"req-{next(_counter)}"
@dataclass(frozen=True)
class Request:
request_id: str
task: TaskType
model_id: str # which ModelCard to run
inputs: tuple[ModalPart, ...] = ()
sampling: SamplingParams = field(default_factory=SamplingParams)
diffusion: DiffusionParams = field(default_factory=DiffusionParams)
outputs: OutputSpec = field(default_factory=OutputSpec)
node_params: dict[str, dict] = field(default_factory=dict) # per graph-node overrides
priority: int = 0
# --- convenience accessors (do not infer task from these) ---
def prompt(self) -> str:
for p in self.inputs:
if isinstance(p, TextPart):
return p.text
return ""
def image(self):
for p in self.inputs:
if isinstance(p, ImagePart):
return p
return None
def node_override(self, node_id: str) -> dict:
return self.node_params.get(node_id, {})
def make_request(task: TaskType,
model_id: str,
prompt: str = "",
*,
inputs: tuple[ModalPart, ...] | None = None,
sampling: SamplingParams | None = None,
diffusion: DiffusionParams | None = None,
outputs: OutputSpec | None = None,
node_params: dict[str, dict] | None = None,
priority: int = 0,
request_id: str | None = None) -> Request:
"""Ergonomic constructor used by the offline API and tests."""
parts: tuple[ModalPart, ...] = inputs if inputs is not None else ((TextPart(prompt), ) if prompt else ())
return Request(
request_id=request_id or new_request_id(),
task=task,
model_id=model_id,
inputs=parts,
sampling=sampling or SamplingParams(),
diffusion=diffusion or DiffusionParams(),
outputs=outputs or OutputSpec(),
node_params=node_params or {},
priority=priority,
)
@dataclass
class Session:
"""A long-lived interactive context.
Holds prompt memory, media streams, a cancel scope, and a handle to
cross-request chunk-KV that persists for a game/scene session (self-forcing /
world-model rollout). Mutable by design.
"""
session_id: str
prompt_memory: list[str] = field(default_factory=list)
streams: dict[str, Stream] = field(default_factory=dict)
cancel_scope: CancelScope | None = None
kv_handle: object | None = None # persistent chunk-KV CacheHandle (see cache/)
def __post_init__(self):
if self.cancel_scope is None:
self.cancel_scope = CancelScope(kind=CancelKind.SESSION, target_id=self.session_id)
def push_text(self, text: str) -> None:
self.prompt_memory.append(text)
def stream(self, name: str) -> Stream:
return self.streams.setdefault(name, Stream(f"{self.session_id}:{name}"))