Reactor is the base class for every model on the platform. It speaks raw JSON over the wire:
open a session, send commands by name, and receive generic message events.
Typed SDKs like HeliosModel and other model-specific packages extend Reactor:
Inheritance
set_prompt(),
send_image(), …). Under the hood they call send_command() on this base class. Same wire
protocol, just typed. You can always drop down to Reactor directly to send any command as raw
JSON; see the SDK Reference overview for when each path makes sense.
For the list of commands and events a specific model accepts, see the
Model API Reference.
Constructor
Signature
Example
model_name and api_key may also be given positionally — Reactor("your-model-name", "rk_...") — matching the old py-sdk’s own constructor order exactly, so a call site ported from
there needs no rewrite. api_url is keyword-only, so it can never be confused with either.Reactor class also supports async with for automatic cleanup:
Context manager
No audio device is ever opened. A
sendonly audio track carries only the PCM you push into it
with Track.push_frame() — nothing is captured from
your microphone. A model’s audio arrives on its Track, via
on_frame, for you to play through whatever you like — nothing is played through your speakers
on your behalf.Methods
connect()
Establishes a connection to the Reactor Platform, waits for GPU assignment, and opens a WebRTC connection
to the model. If api_key was provided, fetches a token first.
Signature
disconnect()
Disconnects and ends the session on the server. Not recoverable — there is no parameter to keep the
session alive instead. To disconnect and later resume the same session, call
reconnect() directly rather than disconnect() followed by connect().
Signature
session_id leaves it running for its owner.
reconnect()
Reconnects using the same session — after a transient failure, or called from ready to
deliberately cycle the connection. Tears down the live connection itself first if there is one,
without ending the session server-side, so there is no need to call disconnect() beforehand (doing
so would end the session this is about to reuse).
Signature
InvalidStateError if there is no session to
reconnect to — nothing has connected yet, or a previous disconnect() already ended it.
recvonly tracks resume automatically, unless you created the client with
auto_resume_tracks=False. sendonly tracks do not — a track published before the
reconnect is not published after it, so publish again for anything you were sending:Track.published is False until that happens, and
push_frame() raises rather than sending into a slot
with nothing behind it.close()
Synchronously tears down the connection. Prefer disconnect() in async code; close() exists for
cleanup paths (like __del__) that cannot await.
Signature
close() settles it with
AbortedError. A send_command() whose reply had not
arrived yet raises it in the coroutine that awaits it.
send_command()
Sends a command to the model and waits for its correlated reply.
Signature
The SDK carries a payload over 256 KiB, up to 64 MiB, when the runtime accepts data-channel
chunking. A runtime that does not accept it keeps the 256 KiB limit, and a larger payload raises
MessageTooLargeError.
Returns {"type": ..., "data": ...} — the model’s typed reply to this specific command. Returns
None if the handler ran and acknowledged the command but returned no message, as an auto-generated
set_<field> setter does.
Example
To fire a command without waiting on the reply, schedule the call instead of awaiting it directly:
asyncio.create_task(reactor.send_command(...)) — keep a reference to the task so it isn’t
garbage-collected before it completes.FileRef values from
upload_file() as parameters. A FileRef nested
inside a list or a dict in data works too, for a parameter that takes several files. The SDK
sends each one as an upload reference. See File Uploads for details.
upload_file()
Uploads a file and returns a FileRef that can be passed
into send_command().
Signature
Example
Only callable once the connection status is
"ready" — raises
InvalidStateError otherwise, the same guard
request_clip() and Track.pause() use.publish_track()
Activates a named sendonly track slot, so frames pushed with
Track.push_frame() start reaching the model.
Signature
Returns the slot as a
Track, so frames can go straight into it:
Example
unpublish_track()
Deactivates a sendonly track. Synchronous, unlike the other track methods — it never touches the
network, only a local status check and a fire-and-forget notification.
Signature
A failure here is logged, not raised. Unpublish is commonly the last call in a
finally
block, and raising there would replace whatever exception was already propagating instead of
adding to it. Check the logs (reactor_sdk at WARNING) if a track seems to have stayed
published.set_bitrate()
Bounds what the whole connection may allocate, in bits per second. Needs a connected client.
Signature
None leaves a bound at the WebRTC default. The SDK reapplies the bounds after a reconnect. This
ceiling and Track.set_bitrate()’s apply together, so
the lower of the two wins. The 2.5 Mbps default that caps a video encoder is the track-level one, so
raise that as well.
Example
start_bps names the initial encoder target. It has no effect from here: this call needs a live
connection, and the ramp that start_bps skips is over by then.
track()
The named track, as a Track — the only way to push frames into a
track, receive its frames, or pause/resume it. Its publish()/push_frame()/on_frame()/
pause()/resume() refuse whatever the track’s direction doesn’t allow instead of failing
silently.
Signature
Raises
ValueError for a name the session doesn’t declare, once it has declared any tracks — before
that (early in connect(), before the model’s capabilities arrive) any name is accepted, so
handlers can be registered ahead of time. The same object comes back every time for a given name,
including across a reconnect.
Example
Push a frame, receive one, or pause/resume a track through this
Track object.
publish_track() / unpublish_track() also stay on
Reactor directly — publish_track() hands back this same Track.Track reference for every method and property.
tracks
Every track the session declares, in declaration order, as a
TrackList — a list[Track] with
with_kind()/with_direction()/one() filters chained on top, for finding a track without
hardcoding its name.
Signature
connect().
Example
Media is delivered per track: register
on_frame() for
decoded frames, or on_raw_frame() for the same bytes
with no NumPy conversion — found by name via track() or by filtering here.
on("frame", ...) / on("audio", ...) raise ValueError at registration, since a single handler
fed every recvonly track of a kind at once couldn’t tell them apart.paused_tracks
The names of the recvonly tracks currently paused.
Signature
Track.pause(). A client
created with auto_resume_tracks=False sees its recvonly tracks here until it
resumes them.
request_schema()
Requests the model’s command schema, as an OpenAPI document — the same schema published on the
Model API Reference pages, fetched at runtime.
Signature
request_clip()
Requests a clip covering the last duration_seconds of the session.
Signature
Returns a
Clip. See Recordings for the
full flow (the clip streams as HLS segments; this call returns the manifest, not the video itself).
request_recording()
Requests a clip covering the entire session up to now.
Signature
download_clip()
request_clip() and download_clip() in one call —
for when the file is the only thing you want.
Signature
Given
path, streams straight to it and returns None. Without one, returns the assembled bytes
instead. See download_clip() for the full contract —
same function, request_clip() called for you first.
Example
Reach for
request_clip() directly instead if you also want the Clip’s
session_id, its start_marker / end_marker, or predicted_ready_at_ms — or want to decide
whether to download it at all based on what it says.download_recording()
request_recording() and download_clip() in one
call.
Signature
Example
Pass
path: a full-session recording has no upper bound on length, and only the streamed-to-disk
form (returns None) avoids holding the whole thing in memory. Omitting path returns the
assembled bytes, which does mean holding the whole recording in memory to do it.on()
Registers a handler for an event name.
Signature
See Events below for the full list of event names and their payloads.
"frame" and "audio" are not among them — media is delivered per track, not client-wide.
Registering either raises ValueError at registration time, naming the Track method to use
instead (on_frame() /
on_raw_frame()).off()
Removes a handler previously registered with on().
Signature
status / get_status()
The current connection status. get_status() is an older method form of the same thing — both
exist, use whichever reads better.
Signature
ReactorStatus.DISCONNECTED, CONNECTING, WAITING, or READY. ReactorStatus is a
str subclass, so it compares equal to the plain string too (reactor.status == "ready").
session_id / get_session_id()
The current session ID, or None if not connected. get_session_id() is the older method form.
Signature
get_stats()
A snapshot of the live connection: RTT, jitter, packet loss, bitrates, the transport ICE selected,
and the WebRTC engine’s own per-stream counters.
Signature
ConnectionStats. Raises
InvalidStateError unless the session is ready.
Example
Events
on()/off() take the event name as a plain str — there is no separate ReactorEvent type to
import.Media is delivered per track. See
Track.on_frame() /
Track.on_raw_frame().Python events use
snake_case (e.g., "status_changed") while the JavaScript SDK uses
camelCase (e.g., "statusChanged").