@reactor-models/sana-streaming SDK. It wraps every command below
as a typed method (setPrompt({ prompt })), every message as a typed subscription
(useSanaStreamingState), and each track as a typed view component, all checked at compile time.
Under the hood it speaks the wire protocol this page documents, which is also what you send by name
from the base Reactor client, for example in Python, which has no
typed package: send_command("set_prompt", { … }).
The examples below follow one convention: React snippets call hooks inside a component rendered
under <SanaStreamingProvider>; JavaScript snippets call methods on a connected
SanaStreamingModel named sana; Python snippets use the base Reactor client named reactor.
Tracks
Output resolution is 1280 × 704. In live mode you publish your camera to the
camera track; in
file mode the source arrives by upload (set_video), not as a track. Commands that change
generation take effect on chunk boundaries. Chunks hold 24 frames through the middle of a run; the
first and last chunks of a run can be shorter.
In the typed React SDK, render the output with <SanaStreamingMainVideoView> and read incoming
tracks with useSanaStreamingTrack("main_video"); both are pre-bound to these names. There is also a
<SanaStreamingCameraView> that acquires and publishes the webcam for you, but it gives no hook to
set the camera’s content hint, so publish the camera track yourself when you need that (see the
warning below).
Session lifecycle
IDLE. Stage a source there (a
published camera in live mode, set_video in file mode) and, to steer the edit, a prompt; start
transitions to RUNNING; pause moves to PAUSED; resume returns to RUNNING; reset clears
the source, prompt, and progress and returns to IDLE from any state. While RUNNING, a new
set_prompt lands at the next chunk boundary without stopping the stream, and set_anchor_interval
turns on (or adjusts) re-grounding on the fly — anchoring is off by default. See
Sessions for the separate connection-level lifecycle
(disconnected → connecting → waiting → ready) the session passes through before reaching the
states above.
A prompt is optional: start with no prompt and the model streams a near-reconstruction of the
source; set or change one at any time to steer the edit. The source is the only hard requirement to
start (a published camera in live mode, an accepted clip in file mode).
In file mode a run also ends on its own: every source frame gets transformed, then
generation_complete reports the total chunk count and the session returns to IDLE with the clip,
prompt, and seed still staged. Send start again to replay the clip from the top; reset is only
needed to swap in a different clip or to clear the staging. On completion the main_video track
freezes on the last transformed frame rather than going dark, so blank or overlay the stage
yourself.
Commands
In the typed SDK each command is a method onuseSanaStreaming() (React) or a SanaStreamingModel
instance (JavaScript); from the base client you send it by name with send_command(). Below are all
available commands:
- set_mode
- set_video
- set_prompt
- set_seed
- set_anchor_interval
- start / pause / resume / reset
set_mode
Select where the source video comes from:"live" (a published camera track) or "file" (a clip
set with set_video). Repeating the command is safe, even with the same mode, so always send
set_mode then start as a pair; the flow works no matter what was set before. Mode is fixed once a
run starts; reset to switch.Parameters:Example:
Messages
The model emits the following messages. In React, subscribe to each with its typed hook (shown in the table); in JavaScript, withon<Message> on a SanaStreamingModel instance; in Python, with
@reactor.on_message("<name>"). React also offers useSanaStreamingMessage for one handler over the
whole union. The typed hooks hand you a flat, fully-typed object (msg.running, msg.reason); the
base client delivers the same data as JSON { "type": "<name>", "data": { … } }.
video_accepted reports the decoded source’s pre-crop width and height; num_frames and
num_latent_frames arrive as 0 because the clip is decoded and VAE-encoded lazily during
generation, not at upload. conditions_ready lets you check that start will succeed (only a video
is required; a prompt is optional).
state payload
state is the single source of truth for the session’s observable state. Subscribe once and gate
your UI on it (start buttons, mode toggles, transport controls) rather than tracking individual
acknowledgements yourself.
current_prompt is typed unknown on the wire (the field is free-form), but the model only ever
sends a string or null; narrow it on your side.
One timing quirk: right after a restart, state still reports the previous run’s final
current_chunk until the new run’s first chunk_complete lands (indices restart at 0). Don’t drive
progress UI from current_chunk in that window.
Complete example
Live mode in React, file mode from Python:new SanaStreamingModel(), await sana.connect(jwt), then call the same typed methods (sana.setPrompt({ … }), sana.start()) and
subscribe with sana.onState(…) / sana.onChunkComplete(…). The model name and tracks are baked
into the class.