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
str
required
The name of the model to connect to. Required — the constructor raises
TypeError without it.str
Your Reactor API key. The SDK automatically exchanges this for a token during
connect(). Use
this or jwt, not both — jwt wins if both are given.str
A token to authenticate with directly, skipping the API key exchange.
str
default:"https://api.reactor.inc"
The API URL. Ignored if
local=True.bool
default:"False"
If
True, connects to a local runtime at http://localhost:8080 and relaxes TLS verification. No
API key required.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
str
Attach to an existing session instead of creating a new one — the session this reactor adopts does
not get terminated on
disconnect().int
Adopt a WebRTC connection slot a backend already registered for this session, instead of
registering a new one. The connection-level analogue of
session_id — most callers building a
single connection per session leave this unset. See Multiple connections per
session.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. 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
send_command()
Sends a command to the model and waits for its correlated reply.
Signature
str
required
The command name. Must match a command defined on the model.
dict
required
The command payload. Shape depends on the command.
{"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. See
File Uploads for details.
upload_file()
Uploads a file and returns a FileRef that can be passed
into send_command().
Signature
BinaryIO | bytes | str | PathLike
required
The file to upload. Accepts a file path, raw bytes, or a file-like object opened in binary mode.
str
Custom filename. Inferred from the file path or file object when not given. Defaults to
"upload"
for raw bytes.str
MIME type. Guessed from the filename when not given. Defaults to
"application/octet-stream".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
str
required
Track name. Must match a
sendonly track name the model declares.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
str
required
The name of the track to stop publishing.
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.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
str
required
The track name.
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().
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
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
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
"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
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").