Add multi-session lifecycle support to the v2 runtime - #548
Conversation
f5348d0 to
07b9786
Compare
|
/ok to test cc238c2 |
Greptile SummaryThe PR adds multi-session lifecycle support to runtime-v2 while retaining application resources, client windows, and model weights between sessions.
Confidence Score: 3/5The PR is not safe to merge while an unauthenticated valid WebRTC offer can replace the active browser and gain its video and control capabilities. The previously reported peer-takeover path remains: the offer handler negotiates any valid request, releases the current peer, and installs the requester as the active video and control peer without an authorization boundary. Files Needing Attention: flashdreams/flashdreams/runtime_v2/serving/webrtc_server.py Important Files Changed
Sequence DiagramsequenceDiagram
participant B as Browser
participant W as WebRTC Window
participant R as Application Runner
participant S as Session Runner
participant A as Application
B->>W: Connect and submit input
W->>R: Persistent client events
R->>A: Create session
R->>S: Run session
S-->>W: Present generated frames
S-->>R: Model loop finishes
R->>A: Create replacement session
R->>S: Run replacement session
Note over W,A: Client window and application resources remain alive
Reviews (8): Last reviewed commit: "Keep T2V CPU tests headless" | Re-trigger Greptile |
| existing_peer = self._peer_connection | ||
| if existing_peer is not None: | ||
| # This server has one client slot. A valid new offer is a page | ||
| # refresh or replacement browser and takes ownership of it. | ||
| try: | ||
| self._buffer_browser_message( | ||
| message, | ||
| await self._release_peer_connection(existing_peer) | ||
| except BaseException: | ||
| await self._release_peer_connection( | ||
| peer_connection, | ||
| detached_video_track=video_track, | ||
| ) | ||
| except ValueError as error: | ||
| channel.send(json.dumps({"type": "error", "message": str(error)})) | ||
|
|
||
| @channel.on("close") | ||
| def on_close() -> None: | ||
| if is_reliable_control: | ||
| self._record_client_disconnect() | ||
| raise | ||
|
|
||
| @peer_connection.on("connectionstatechange") | ||
| async def on_connectionstatechange() -> None: | ||
| self._media_connected.clear() | ||
| self._final_video_track_metrics = None | ||
| self._peer_connection = peer_connection |
There was a problem hiding this comment.
Valid offers hijack active peers
If the server is bound to an interface reachable by another principal, any valid unauthenticated offer releases the legitimate peer and becomes the active peer, allowing the replacement browser to send keyboard, mouse, touch, reset, close, and new-session input to the running application.
How this was verified: The unauthenticated offer route unconditionally installs a successfully negotiated replacement peer, whose control-channel messages then pass the active-peer guard.
Knowledge Base Used:
| loopResult = ui_loop._begin_run(events, generation) | ||
| if loopResult.stop_requested: | ||
| stop.set() | ||
| return | ||
| if loopResult.new_session_request is not None: | ||
| next_session_desc = replace( | ||
| session_desc, | ||
| metadata={ | ||
| **session_desc.metadata, | ||
| **loopResult.new_session_request, | ||
| }, | ||
| ) | ||
| stop.set() | ||
| return | ||
| if loopResult.step_index is None or not step_requested: | ||
| return |
There was a problem hiding this comment.
We can begin a run, and then never finish the run or stop the ui_loop? We should be very careful about introducing cases where we can begin something but never end them.
We should potentially think about even making stop flag setting a type of _finish_run for clarity reasons. A run should likely be forced to have a begin/end for debugging/code-readability/code-extensibility reasons.
There was a problem hiding this comment.
We can begin a run, and then never finish the run or stop the ui_loop
For serving, this is actually a valid use case. If we want to stop, we just kill the server/app.
For debugging purpose, yea, it's better to have a flag to stop the UI run even for the unbounded interactive case.
But how should we design the flag? something like --timeout 60 for 1-min run and stop the app entirely?
There was a problem hiding this comment.
_finish_run should really just be in a finally: block I suppose... and if this is not possible then something is wrong (begin at entry, finish at end)
There was a problem hiding this comment.
oh I thought you were referring "launching the app, and let the app run forever".
yea, if what we want is matching _begin_run and _finish_run, this makes sense. I will update that
There was a problem hiding this comment.
Moved _finish_run to finally: block
|
/ok to test 5a0933b |
ArielG-NV
left a comment
There was a problem hiding this comment.
Minor suggestions; PR looks very good now.
|
/ok to test 7750f1c |
Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
aioice can run a retry callback after its transaction future has already completed, which produces a non-fatal InvalidStateError. Filter only that exact callback failure on the dedicated WebRTC event loop and delegate every other exception to asyncio. Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
Move prompt-driven session replacement into the application UI and keep the runtime UI loop responsive before and after model inference. Model completion no longer implicitly terminates a session; lifecycle events, UI completion, explicit limits, and failures remain terminal. Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
Let each model loop track its own inference lifecycle and have UI loops query that single source of truth. Initialize the state through the common loop registration path and remove the redundant session-runner wrapper. Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
Parse and fully negotiate replacement offers before releasing the active peer. Serialize offer swaps and clean up failed candidates without disconnecting the connected browser. Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
Let UI loops request a fully resolved SessionDesc and have run_session forward it unchanged. This allows replacement sessions to modify any description field instead of only merging metadata. Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
Co-authored-by: ArielG-NV <159081215+ArielG-NV@users.noreply.github.com> Signed-off-by: Gangzheng Tong <tonggangzheng@gmail.com>
Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
Signed-off-by: Gangzheng Tong <gtong@nvidia.com>
a0c9e56 to
5d48dcc
Compare
|
/ok to test 5d48dcc |
Summary
Add multi-session lifecycle support while keeping the application, model
weights, and client window alive between sessions.
Add an ImGui prompt UI for T2V applications that can request new sessions
before or after inference.
Keep UI processing active after model completion and across WebRTC
reconnects.
Make
IModelLoopthe single source of inference lifecycle state.Suppress the known non-fatal
aioiceSTUN retry race without hiding otherasyncio errors.
fixes [Session Runner] Make IO-Thread/UI-Thread reusable and able to restart (end_old+start_new) a
ISession#502