A scoped, CPU-testable realization of design_v3.md — the architecture where the atomic unit is a typed (recipe, runtime) ModelCard, the model owns loop semantics while the runtime owns loop lifecycle, one resident instance runs many loops, and training records behavior on the same loops it serves. Implements: - card/ loop/ runtime/ cache/ memory/ parallel/ parity/ extend/ program/ request/ training/ spanning design_v3 §4-§13: ModelCard + validate(); driven loops (init/next/advance/finalize); step-interleaving Engine with reservation-before-admission + per-class caches keyed by CacheKey; the C0-C4 consistency ladder + the non-negotiable batch-of-N interleave parity gate. - Inference: Wan2.1-1.3B (T2V), LTX2.3 (two-stage distilled, shared transformer), Wan-causal (chunk rollout + slab-KV streaming). - Training (Wan2.1-1.3B), each driving the SAME loops the engine serves: finetune (flow-match), DMD2 (teacher/critic distribution matching), DiffusionNFT (likelihood-free C2, samples from the decay-blended old policy, group-relative advantages, shared-prompt cache reuse), self-forcing (causal chunk loop). The engine never imports training (the §10 dependency rule, grep-verified). numpy-only core (no torch/GPU here); heavy Wan/LTX forwards are deterministic toy stand-ins with lazy torch-adapter seams (ComponentSpec.load_id/factory) for a GPU box. 49 tests pass via pytest and a zero-dependency runner; interleave parity verified (and a buggy module-global interceptor provably breaks it). Omni-ready spine (ar_decode/chunk_step loop kinds, multi-loop instances, LoopState.extension) for the phase-2 Cosmos3 + vllm-omni omni ports. Run: python3 -m pytest mini_fastvideo/tests/ -q ; python3 -m mini_fastvideo.examples
69 lines
2.5 KiB
Python
69 lines
2.5 KiB
Python
"""LoopRunner — the only place iteration lives (design_v3 §5.1).
|
|
|
|
This is the runtime's driver, factored so the engine can either run it to completion
|
|
(``run``) or advance it one step at a time (``peek`` + ``step``) to **interleave** the steps
|
|
of concurrent requests. Per-request state is entirely in ``LoopState``; the runner holds no
|
|
hidden iteration state beyond a cached pending plan, so interleaving is safe by construction
|
|
(the property the §9.3 interleave gate verifies).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from .contracts import Done, LoopContext, LoopResult, LoopState, WorkPlan
|
|
|
|
|
|
class LoopRunner:
|
|
def __init__(self, loop: Any, ctx: LoopContext, request: Any, model: Any):
|
|
self.loop = loop
|
|
self.ctx = ctx
|
|
self.state: LoopState = loop.init(request, model, ctx)
|
|
self._done = False
|
|
self._result: LoopResult | None = None
|
|
self._pending: WorkPlan | None = None
|
|
|
|
@property
|
|
def done(self) -> bool:
|
|
return self._done
|
|
|
|
@property
|
|
def result(self) -> LoopResult | None:
|
|
return self._result
|
|
|
|
def peek(self) -> WorkPlan | None:
|
|
"""Compute (and cache) the next WorkPlan. ``next`` is kernel-free, so this is cheap.
|
|
Returns None when the loop is finished (and runs ``finalize`` exactly once)."""
|
|
if self._done:
|
|
return None
|
|
if self._pending is None:
|
|
self.ctx.check_cancel()
|
|
nxt = self.loop.next(self.state)
|
|
if isinstance(nxt, Done):
|
|
self._result = self.loop.finalize(self.state)
|
|
self._done = True
|
|
return None
|
|
self._pending = nxt
|
|
return self._pending
|
|
|
|
def step(self) -> bool:
|
|
"""Execute the pending plan (the inversion point) and fold the result in.
|
|
Returns True when the loop has just finished."""
|
|
plan = self.peek()
|
|
if self._done or plan is None:
|
|
return True
|
|
# the engine binds the current state onto ctx so interceptors see per-request state
|
|
if hasattr(self.ctx, "bind_state"):
|
|
self.ctx.bind_state(self.state)
|
|
result = self.ctx.execute(plan)
|
|
for chunk in plan.emits:
|
|
self.ctx.emit(chunk)
|
|
self.state = self.loop.advance(self.state, result)
|
|
self._pending = None
|
|
return self._done
|
|
|
|
def run(self) -> LoopResult:
|
|
while not self._done:
|
|
self.step()
|
|
assert self._result is not None
|
|
return self._result
|