@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. The camera track is the only source: publish it and the model
reads it in 24-frame chunks. The model locks playout to the measured camera rate, one output frame
per camera frame. It reads the freshest full chunk and drops the backlog, so latency stays around
one chunk instead of growing. Commands that change generation take effect on chunk boundaries.
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. Publish your camera there and,
to steer the edit, set a prompt; start transitions to RUNNING; pause moves to PAUSED;
resume returns to RUNNING; reset clears the 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. start requires nothing else. It
transforms whatever camera frames arrive, and the first frames land once a full chunk is buffered.
A run has no length limit. It continues until you send reset or the session ends, and
generation_complete then reports the total chunk count. After a reset the main_video track
freezes on the last transformed frame rather than going dark, so blank or overlay the stage
yourself.
A session outlives the clients in it. A client that joins a live session receives the current
state, addressed to it alone, and disturbs nothing: the run carries on for everyone already
watching, and the new client can steer, pause, or reset it in turn. A client that leaves does not
stop the run. The model applies the session’s defaults for seed and anchor interval when the
session starts, not when a client connects.
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_prompt
- set_seed
- set_anchor_interval
- start / pause / resume / reset
set_prompt
Set the edit prompt, beforestart or at any point mid-stream. The model applies the new prompt at
the next chunk boundary, about one chunk (~1-1.5s) after you send it, with no re-render and no
break in the stream. Acknowledged by prompt_accepted.A prompt is optional: with no prompt the model streams a near-reconstruction of the source. Set
or change one to steer the edit. The model processes the prompt when it edits the next chunk, so a
prompt it cannot process is reported about one chunk after prompt_accepted, as a command_error
and a fresh state. The model then keeps the prompt it accepted before that one.See the prompt guide for how to write edit
prompts.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": { … } }.
Messages that describe a chunk (
chunk_complete, anchored, and the chunk’s state) go on the
wire before that chunk’s frames. Every state-changing command broadcasts state before it answers
the caller, so a client that waits on the reply already holds the state the reply describes.
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
The same camera edit in React and in 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.