Signed-off-by: amy-why-3459 <wuhaiyan17@huawei.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Experimental Full-Duplex Runtime
This package contains three experimental integrations:
- the existing JoyVL framework and example integration;
- the MiniCPM-o 4.5 native audio path used by
/v1/duplexand/v1/realtime?duplex=1; - the PersonaPlex lockstep speech-to-speech path (browser demo + batched
serving, see
recipes/NVIDIA/PersonaPlex.md).
For the MiniCPM active runtime path, lifecycle invariants, capability boundary,
Realtime response contract, and validation scope, see DESIGN.md.
To run JoyVL, see
recipes/JD/JoyAI-VL-Interaction.md.
Package boundaries
core/ model-agnostic duplex contracts (adapter, session, turn runtime)
engine/ AsyncOmni/orchestrator scheduler data-plane adapter
openai/ WebSocket transport, Realtime projection, and audio codecs
minicpmo45/ MiniCPM input framing, policy, compatibility, and Stage0 state
joyvl/ JoyVL model-specific integration
personaplex/ PersonaPlex lockstep engine, model-owned runtime, and serving
MiniCPM does not run through the experimental core.DuplexRuntime
facade. Its active path uses the openai session controller, the experimental
engine contracts, the standard scheduler/model runners, and an injected
MiniCPM-specific runtime extension from minicpmo45/runtime.py.
personaplex/ is a Moshi-class, pure-lockstep speech-to-speech model on the
core/ contracts. It keeps core/ untouched: the lockstep lifecycle (ONE
eternal, frame-clocked response that drains on close, instead of the turn-style
start/cancel-per-trigger one) is model policy and lives in the model package as
PersonaPlexDuplexRuntime, mirroring the model-owned runtime shape of the
MiniCPM-o duplex work. Its runnable serving path is personaplex/serving/
(single-session lease or --batch-size elastic slots) over
personaplex/session.py (lockstep driver).
Adding a full-duplex model on the core contracts
The seam is core.DuplexAdapter. core/ owns the session lifecycle,
epoch-based barge-in, playback cursor, and the event protocol; you implement
only model policy.
- Create a sibling package
vllm_omni/experimental/fullduplex/<model>/; keep model-specific code there and do not touchcore/. - Implement one
DuplexAdapter(capabilities/on_input/respond; the rest have defaults). Turn-based models run throughcore.DuplexRuntimeunchanged; a model needing a different lifecycle carries its own runtime in its package (seepersonaplex/adapter.py::PersonaPlexDuplexRuntime). - Promote a helper from a model package up into
core/only once a second model actually needs it.