Skip to main content
Rolling red sand dunes at dusk
New releases, breaking changes, and notable improvements across the Reactor platform: JavaScript SDK, Python SDK, models, and API.
reactor-sdk 1.0.0 (Python)

Breaking

  • send_command() now waits for its reply. Previously fire-and-forget — the call resolved as soon as the command was queued, and any reply from the model arrived later as a separate message event. It now returns the model’s correlated reply directly: {"type": ..., "data": ...}, or None if the handler acknowledged the command without returning a message (an auto-generated set_<field> setter, for instance). To fire a command without waiting on the reply, schedule the call instead of awaiting it directly — asyncio.create_task(reactor.send_command(...)) — keeping a reference to the task so it isn’t garbage-collected before it completes.
  • Errors are now typed exceptions, and ReactorError is the one class for both. Every failure used to arrive as one bare exception carrying only a sentence, and separately, on_error handed you a same-shaped-but-different dataclass. ReactorError is now both: the base of 16 concrete subclasses (UnauthorizedError, ConflictError, RateLimitedError, and more — see Error codes) that on_error and a raised call hand you identically, each with a stable code, recoverable, status, operation, retry_after_ms, and (event-only) timestamp_ms. The old component field (api/gpu) is gone — it split codes by platform tier, which wasn’t actionable and could report the same failure under two different codes. except ReactorError still catches everything; code relying on error.component needs updating.
  • Audio devices are synthetic-only. The constructor’s adm_mode argument is gone; no Reactor instance ever opens a real microphone or speaker on its own. A sendonly audio track only ever carries PCM you push yourself with Track.push_frame(), and a model’s audio must be played back explicitly via Track.on_frame() / on_raw_frame() — or with Speaker/Microphone, below.
  • reactor.on_frame (client-wide) is removed. It only ever worked for video, and a single handler fed every recvonly video track couldn’t tell them apart. Register on the Track instead, found by name (reactor.track(name)) or by filtering reactor.tracks (reactor.tracks.with_direction("recvonly").with_kind("video").one()).
  • The client-wide on("frame", ...) / on("audio", ...) events are removed too, for the same reason. on() raises ValueError at registration for either name, naming the Track method to use instead — on_frame() for decoded frames, on_raw_frame() for the same bytes with no NumPy conversion.
  • publish_track() / unpublish_track() are the only name-based track methods still public on Reactor. push_video_frame(), push_audio_frame(), pause_track(), and resume_track() are now internal — reach the same operations through the Track object instead: track.push_frame(), track.pause(), track.resume(). publish_track() already hands back that same Track.
  • Track.push_frame() now raises InvalidStateError on an unpublished sendonly track, rather than silently dropping the frame. Call publish() first — Track.published says whether that’s already happened.
  • reconnect() resumes recvonly tracks automatically, but does not restore a sendonly track’s publish. Call publish() again after reconnecting for anything you were sending before it.
  • @reactor.on_track hands over the Track itself, not a bare (name, mid). The Track is already resolved — reactor.track(name) done for you — so a handler that wants to push frames or register on_frame no longer has to look it up first; track.mid still carries the WebRTC media stream ID.
  • unpublish_track() / Track.unpublish() return None, not an int. A failure is now logged (reactor_sdk at WARNING) rather than left for the caller to notice by checking a return value nobody checked — unpublish is commonly the last call in a finally block, so it still does not raise.
  • Reactor()’s positional arguments are model_name, api_keyapi_url moved to keyword-only. Matches the old py-sdk’s own constructor order exactly, so Reactor(model, key) ported from there needs no rewrite to keyword arguments at all. Previously api_url/model_name were positional (in either order, sniffed by which one looked like a URL); any call relying on that specifically needs updating, but a plain Reactor("your-model-name") is unaffected.
  • upload_file() now requires ready. Previously allowed as soon as the coordinator created a session, before the WebRTC handshake finished — raises InvalidStateError now if called earlier, the same guard request_clip() and Track.pause() already used. See File Uploads.

New

  • reactor.tracks is now a TrackList. Still a plain list[Track] for iterating and indexing, with with_kind() / with_direction() filters that chain, and .one() for the common case of “the track I mean, and an error if that’s ambiguous.” See TrackList.
  • The Track object. reactor.track(name) and reactor.tracks return a Track scoped to one named slot, with publish(), push_frame(), on_frame()/on_raw_frame(), and pause()/resume() that raise on a direction mismatch instead of a silent no-op.
  • Track.published. Whether a sendonly track is currently activated — readable state for something the session itself doesn’t track, cleared automatically whenever the connection leaves ready.
  • download_clip(). Fetches every segment a Clip’s playlist_url names. Given a path, streams straight to it (returns None, never holds more than one segment in memory — the one to use for request_recording()); without one, returns the assembled bytes instead. Takes an optional on_progress callback either way. Closes a real gap left by the rewrite off the old py-sdk, which had this as download_clip_as_file().
  • reactor.download_clip(seconds) / reactor.download_recording(). request_clip() / request_recording() plus the download above, in one call, for when the file is all you want. request_clip() / request_recording() still return just the Clip for anyone who also wants its session_id, markers, or predicted_ready_at_ms.
  • connect(connection_id=...). Adopts a WebRTC connection slot a backend already registered for a session, the connection-level analogue of session_id adoption — see Multiple connections per session. Also closes a gap left by the rewrite: the old py-sdk had this, and the JavaScript SDK still does.
  • reconnect() now works from ready, not only after a drop. It tears the live connection down itself first — without ending the session, unlike disconnect() — so there was never a need to call disconnect() immediately before it; that combination is also no longer required. disconnect()’s own behavior is unchanged — it has always ended the session server-side, with no recoverable option — a few places in these docs previously described it backwards, now corrected. See Sessions.
  • Speaker / Microphone (reactor_sdk.audio_devices). Play a recvonly audio track through real speakers, or capture a real microphone into a sendonly track — context managers wrapping a sounddevice stream around a Track: with Speaker(output_track), Microphone(mic_track): .... Not re-exported from reactor_sdk — the only part of this SDK with a runtime dependency, so pip install "reactor-sdk[audio]" and import from reactor_sdk.audio_devices directly. Speaker promotes what examples/pygame_app had hand-rolled; Microphone is new.
  • fetch_jwt(api_key, api_url=...)’s api_url is optional now, defaulting to the same production coordinator Reactor() itself defaults to. Pass it explicitly only when minting a token against a different one.
helios · Rewind

New

  • Rewind. Helios saves snapshots of the scene and returns to them on demand. save_snapshot captures the world state, rewind restores it, and list_snapshots reports what the buffer holds. The buffer keeps up to 50 snapshots per session and evicts the oldest once full; a reset empties it, and it is released when the session ends. Each snapshot records the one it descends from, so a save after a rewind branches off the restored snapshot while a save without one extends the branch in progress, and current_snapshot_id on the state message says which branch is live. Rewinding restores the scene but not the prompt, so send the prompt you want to continue with after the rewind. Available in @reactor-models/helios as saveSnapshot, rewind, and listSnapshots, with the matching useHeliosSnapshotSaved, useHeliosRewindComplete, useHeliosRewindFailed, and useHeliosSnapshotList hooks.
js-sdk · Multi-client connections

Breaking

  • Publishing an input track is now explicit. Output tracks still stream automatically on connect, but sendonly (input) tracks send nothing until your app calls publishTrack() (or renders <WebcamStream>). Apps that relied on input media flowing at connect must now publish explicitly once the connection is ready. See Tracks.

New

  • Output track pause/resume. New pauseTrack() and resumeTrack() methods, plus an autoResumeTracks connect option (default true), control when each output track streams.
  • Multi-client sessions. A single session can now back several connections at once. connect() accepts sessionId and connectionId options to attach to a session or connection your backend created.
Pricing

Changed

  • Per-second and per-hour pricing in dollars. Model pricing is now displayed as US-dollar rates on the Pricing & Billing page.
Docs · Pricing & billing

New

  • Pricing & billing docs. Published Pricing & Billing, covering session pricing, idle vs. active billing, and recoverable-state behavior.
  • Beta status callouts. Added a banner across docs and the Overview noting that Reactor is in beta. APIs and pricing may evolve.
Authentication · API

New

  • Token TTL with expires_after. Auth tokens now carry an expires_after field so clients can refresh proactively instead of waiting for a 401. See Authentication.
js-sdk · reactor-sdk · Concepts

New

  • File uploads in the Python SDK. reactor-sdk now supports upload_file() returning a FileRef that can be passed into send_command().
  • File uploads in the JavaScript SDK. uploadFile() and FileRef are available in @reactor-team/js-sdk for passing files (images, audio, blobs) as command arguments.
  • New concept page: File Uploads documents the upload-then-reference pattern in both SDKs.
API

Breaking

  • Token endpoint is now POST. The auth-token endpoint moved from GET to POST to allow request-body parameters. Update any direct API callers; the SDKs handle this automatically once upgraded.
js-sdk 2.7 · reactor-sdk 0.4

Breaking

  • New SDK protocol. Wire-level protocol overhaul shipped in @reactor-team/js-sdk 2.7 and reactor-sdk 0.4. Older SDK versions will no longer connect. Upgrade to the latest release.
  • Removed fetchInsecureToken() and JWT helpers. The development-only insecure token helpers were removed; all auth now flows through the standard token endpoint.

Improved

  • Maple theme refresh. Docs site updated to Mintlify’s Maple theme with Lucide icons, new codeblock styling, and refreshed colors.
  • Navigation split into separate “SDK” and “API Reference” tabs.
Helios

New

  • Image conditioning for Helios. set_image() accepts a reference image to steer generation, with cut or blend transitions for swapping mid-stream. See Helios.

Improved

  • Image size guidance clarified: 64KB limit with JPEG quality 0.5 recommended for set-image calls.
js-sdk 2.6.0

Breaking

  • API renames in @reactor-team/js-sdk 2.6.0. Several public methods and types were renamed for consistency with the Python SDK. Update import paths and call sites.
Helios

New

  • Helios launch. Reactor’s first interactive real-time video generation model, built on a 14B-parameter Diffusion Transformer. Produces a continuous infinite stream you can steer with prompts and reference images.
  • Helios prompt guide. A dedicated Prompt Guide covering scene composition, transitions, and steering techniques.
Docs

Improved

  • Renamed “Realtime AI” → “Realtime Video AI” across the docs to better reflect what Reactor does. No API or SDK changes.
reactor-sdk (Python)

New

  • Python SDK launched. reactor-sdk is now available on PyPI, async-first, with parity for sessions, commands, and message handling. See Using the SDK.
js-sdk

Breaking

  • sendCommand() replaces sendMessage(). The command API was renamed to clarify the distinction between commands (client → model) and messages (model → client). Update all sendMessage call sites. See Commands and messages.
  • Removed message queueing. Commands now have explicit waiting state instead of implicit queueing. Applications can decide whether to drop, replace, or buffer in-flight commands.
Authentication · API

Breaking

  • JWT auth replaces insecureApikey. All client connections now require a short-lived JWT minted server-side from your API key. The insecureApikey development flow is removed.
  • New API Reference section. Published a complete API Reference covering the Reactor class, React components, hooks, types, and events.
Docs

New

  • Reactor docs site launched. Initial release with Overview and Quickstart. Ported from the legacy readme.io site.