npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reactor-models/longlive-v2

v3.0.5

Published

Strongly-typed SDK for the LongliveV2 model on Reactor

Downloads

498

Readme

@reactor-models/longlive-v2

Typed JavaScript + React SDK for the LongliveV2 model on Reactor. Version v3.0.5.


Get started

Scaffold a starter app for LongliveV2 with create-reactor-app:

npx create-reactor-app my-app --model=longlive-v2
pnpm dlx create-reactor-app my-app --model=longlive-v2

Install

npm install @reactor-models/longlive-v2
pnpm add @reactor-models/longlive-v2

The package exports a plain-JavaScript client and a set of React bindings. Import whichever you need from @reactor-models/longlive-v2:

import { LongliveV2Model } from "@reactor-models/longlive-v2";
import { LongliveV2Provider, useLongliveV2 } from "@reactor-models/longlive-v2";

React 18 or later is required when using the provider and hooks. The token-loading examples below use React 19's use(); on React 18, fetch the JWT in a useEffect and pass it to the provider once it resolves.


Authenticate

Reactor uses short-lived JWTs for session auth. You hold your API key on your server, mint a token on demand, and the client never sees the raw key. Tokens are valid for 6 hours — if one leaks, it expires on its own.

Mint a JWT with POST https://api.reactor.inc/tokens and the Reactor-API-Key header; the response JSON is { "jwt": "..." }.

JavaScript (Next.js route handler)

// app/api/reactor/token/route.ts
import { NextResponse } from "next/server";

export async function POST() {
  const res = await fetch("https://api.reactor.inc/tokens", {
    method: "POST",
    headers: { "Reactor-API-Key": process.env.REACTOR_API_KEY! },
  });
  const { jwt } = await res.json();
  return NextResponse.json({ jwt });
}

React (provider)

Call the /api/reactor/token route above from a client component and pass the result to the provider:

"use client";

import { use } from "react";
import { LongliveV2Provider } from "@reactor-models/longlive-v2";
import { ReactorView } from "@reactor-team/js-sdk";

async function getToken() {
  const r = await fetch("/api/reactor/token", { method: "POST" });
  const { jwt } = await r.json();
  return jwt;
}

const tokenPromise = getToken();

export default function App() {
  const token = use(tokenPromise);
  return (
    <LongliveV2Provider jwtToken={token} connectOptions={{ autoConnect: true }}>
      <ReactorView className="w-full aspect-video" />
    </LongliveV2Provider>
  );
}

Connect

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
await longliveV2.connect(jwt);

React

The provider takes the JWT as a prop; fetch it from the same /api/reactor/token route the Authenticate example mints:

"use client";

import { use } from "react";
import { LongliveV2Provider, useLongliveV2 } from "@reactor-models/longlive-v2";

async function getToken() {
  const r = await fetch("/api/reactor/token", { method: "POST" });
  const { jwt } = await r.json();
  return jwt;
}

const tokenPromise = getToken();

function Controller() {
  const { status } = useLongliveV2();
  return <span>Status: {status}</span>;
}

export default function App() {
  const token = use(tokenPromise);
  return (
    <LongliveV2Provider jwtToken={token}>
      <Controller />
    </LongliveV2Provider>
  );
}

Events

Client-to-model commands. The typed surface is LongliveV2Model (one method per event) in plain JS, and useLongliveV2() in React — every field name below matches the parameter name the method accepts.

pause

Pause generation after the current chunk finishes. Valid only while generating (after start, not already paused). Emits generation_paused and state on success, or command_error if the session is not generating or is already paused.

Emits: generation_paused, state, command_error

No parameters.

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
await longliveV2.connect(jwt);

await longliveV2.pause();

React

"use client";
import { useLongliveV2 } from "@reactor-models/longlive-v2";

function Example() {
  const { pause } = useLongliveV2();

  return <button onClick={() => pause()}>pause</button>;
}

reset

Abort the current run, clear the active prompt and any scheduled cuts or shots, and return to the waiting state. Always succeeds — there is no command_error path. Emits generation_reset and state. After reset the session is back to its just-connected state: the client must call set_shot again before start will succeed.

Emits: command_error, generation_reset, state

No parameters.

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
await longliveV2.connect(jwt);

await longliveV2.reset();

React

"use client";
import { useLongliveV2 } from "@reactor-models/longlive-v2";

function Example() {
  const { reset } = useLongliveV2();

  return <button onClick={() => reset()}>reset</button>;
}

start

Begin generating video on main_video from the opening shot. Requires an opening shot set via set_shot, and zeros the chunk counters for the new run. Emits state on success, or command_error if no shot has been set, or if a previous run has finished (call reset first).

Emits: state, command_error

No parameters.

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
await longliveV2.connect(jwt);

await longliveV2.start();

React

"use client";
import { useLongliveV2 } from "@reactor-models/longlive-v2";

function Example() {
  const { start } = useLongliveV2();

  return <button onClick={() => start()}>start</button>;
}

resume

Resume generation from a previous pause. Valid only when the session is paused. Emits generation_resumed and state on success, or command_error if the session is not paused.

Emits: generation_resumed, state, command_error

No parameters.

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
await longliveV2.connect(jwt);

await longliveV2.resume();

React

"use client";
import { useLongliveV2 } from "@reactor-models/longlive-v2";

function Example() {
  const { resume } = useLongliveV2();

  return <button onClick={() => resume()}>resume</button>;
}

setSeed

Set seed

| Parameter | Type | Required | Description | |---|---|---|---| | seed | number | | Random seed for noise sampling. Read once when start fires; later changes only take effect after reset followed by a new start. (min 0, default 42) |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
await longliveV2.connect(jwt);

await longliveV2.setSeed({ seed: 42 });

React

"use client";
import { useLongliveV2 } from "@reactor-models/longlive-v2";

function Example() {
  const { setSeed } = useLongliveV2();

  return <button onClick={() => setSeed({ seed: 42 })}>setSeed</button>;
}

setShot

Set the shot prompt. Before start, this seeds the opening shot the video begins from. After start, it triggers a soft shot change: at the next chunk boundary the video transitions to the new prompt while keeping the model's memory of what came before, so motion, identity, and the world carry across the cut. Use it to change framing, action, or subject within the same continuous scene. For a clean break to an unrelated scene — memory wiped, a brief transition pause — use scene_cut instead. Emits shot_set and state on success, or command_error if the prompt is empty or a previous run has finished (call reset first).

Emits: scene_cut, shot_set, state, command_error

| Parameter | Type | Required | Description | |---|---|---|---| | prompt | string | | What the shot should show, in natural language. Before start this is the opening prompt generation begins from; after start it drives a soft shot change at the next chunk boundary. (default "") |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
await longliveV2.connect(jwt);

await longliveV2.setShot({ prompt: "A sunset over the ocean" });

React

"use client";
import { useLongliveV2 } from "@reactor-models/longlive-v2";

function Example() {
  const { setShot } = useLongliveV2();

  return <button onClick={() => setShot({ prompt: "A sunset over the ocean" })}>setShot</button>;
}

sceneCut

Cut to an unrelated scene mid-stream. A hard cut: the model's memory is wiped and generation restarts from the new prompt, giving a clean break with no carry-over of motion, identity, or world. The cut lands at the next chunk boundary and costs a one-time ~150-300ms transition; the per-scene chunk counter (current_chunk) restarts at 0 while the session-wide session_chunk keeps counting. Use it for a genuine change of scene — new subject, new place, new world. For a lighter change that stays in the same continuous scene (new framing, action, or subject with memory preserved) use set_shot instead. Emits scene_cut and state when the next chunk fires, or command_error if the prompt is empty or a previous run has finished (call reset first).

Emits: scene_cut, state, command_error

| Parameter | Type | Required | Description | |---|---|---|---| | prompt | string | | What the new scene should show, in natural language. Queued for the next chunk boundary, where it replaces the current scene wholesale (memory wiped, a fresh start). Calling scene_cut again before the next chunk fires overwrites the queued prompt. (default "") |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
await longliveV2.connect(jwt);

await longliveV2.sceneCut({ prompt: "A sunset over the ocean" });

React

"use client";
import { useLongliveV2 } from "@reactor-models/longlive-v2";

function Example() {
  const { sceneCut } = useLongliveV2();

  return <button onClick={() => sceneCut({ prompt: "A sunset over the ocean" })}>sceneCut</button>;
}

scheduleShot

Schedule a soft set_shot change to fire at a specific chunk index, instead of changing immediately. Same memory-preserving transition as a mid-stream set_shot, but planted ahead of time so it lands frame-accurately without a network round-trip race or activation delay. Use schedule_shot for changes within the same continuous scene and schedule_scene_cut for clean breaks to a new scene. at_session_chunk counts total chunks since the most recent start and keeps counting across cuts. Note: a shot scheduled at at_session_chunk=0 does not render as a transition — there is no earlier chunk to change from — so its prompt simply drives the first chunk, the same as if it were the opening shot (overriding any shot set via set_shot). Emits shot_scheduled and state on success, or command_error if the prompt is empty, at_session_chunk is negative, or a previous run has finished (call reset first).

Emits: shot_scheduled, state, command_error

| Parameter | Type | Required | Description | |---|---|---|---| | prompt | string | | What the shot should change to when the target chunk is reached, in natural language. Held server-side until then so the change fires with no delay. Scheduling at an at_session_chunk already in the schedule replaces that entry; entries whose target chunk has already passed never fire. (default "") | | at_session_chunk | number | | Chunk index at which the shot change fires, counted as total chunks since the most recent start. Must be ≥ 0, with no upper bound — schedule beyond the run length to fire after earlier cuts. A target that has already passed never fires. (default -1) |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
await longliveV2.connect(jwt);

await longliveV2.scheduleShot({ prompt: "A sunset over the ocean", at_session_chunk: 0 });

React

"use client";
import { useLongliveV2 } from "@reactor-models/longlive-v2";

function Example() {
  const { scheduleShot } = useLongliveV2();

  return <button onClick={() => scheduleShot({ prompt: "A sunset over the ocean", at_session_chunk: 0 })}>scheduleShot</button>;
}

scheduleSceneCut

Schedule a hard scene_cut to fire at a specific chunk index, instead of cutting immediately. This removes the network round-trip race for frame-accurate cuts: the cut lands exactly when session_chunk reaches the target, no matter when the command arrives, and with no activation delay. at_session_chunk counts total chunks since the most recent start and keeps counting across cuts, so scheduling at 10 and 20 fires both as the session reaches those counts. Use this to script a multi-scene sequence up front. Emits scene_cut_scheduled and state on success, or command_error if the prompt is empty, at_session_chunk is negative, or a previous run has finished (call reset first).

Emits: scene_cut, scene_cut_scheduled, state, command_error

| Parameter | Type | Required | Description | |---|---|---|---| | prompt | string | | What the scene should show when the target chunk is reached, in natural language. Held server-side until then so the cut fires with no delay. Scheduling at an at_session_chunk already in the schedule replaces that entry; entries whose target chunk has already passed never fire. (default "") | | at_session_chunk | number | | Chunk index at which the cut fires, counted as total chunks since the most recent start. Must be ≥ 0, with no upper bound — schedule beyond the run length to fire after earlier cuts. A target that has already passed never fires. (default -1) |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
await longliveV2.connect(jwt);

await longliveV2.scheduleSceneCut({ prompt: "A sunset over the ocean", at_session_chunk: 0 });

React

"use client";
import { useLongliveV2 } from "@reactor-models/longlive-v2";

function Example() {
  const { scheduleSceneCut } = useLongliveV2();

  return <button onClick={() => scheduleSceneCut({ prompt: "A sunset over the ocean", at_session_chunk: 0 })}>scheduleSceneCut</button>;
}

Messages

Model-to-client messages. Register a typed listener with on… on LongliveV2Model, or a useLongliveV2… hook in React, to receive only the messages you care about.

state

Emitted on connect, after every state-mutating event, and after each completed chunk. Snapshot of the session's observable state — clients drive UI from this single source of truth without reconstructing state from the event stream.

Listener: onState · React hook: useLongliveV2State

| Field | Type | Description | |---|---|---| | seed | number | Current value of the seed input field. | | paused | boolean | True while generation is paused via pause. | | running | boolean | True while the model is actively producing frames (started and not paused). | | started | boolean | True once start has been accepted. Reset to false by reset. | | has_prompt | boolean | True once a shot prompt has been set for the session. | | current_chunk | number | Index of the last completed chunk within the current scene. Restarts at 0 after every hard scene_cut. | | current_frame | number | Running total of frames emitted since the last reset or connect. | | session_chunk | number | Session-wide chunk count — total chunks since the most recent start. Keeps counting across cuts; zeros on start and reset. This is the clock that scheduled cuts and shots fire against. | | current_prompt | unknown | The prompt currently driving generation, or null when none is set. | | scheduled_shots | unknown[] | Sorted chunk indices at which scheduled soft shot changes will fire, as session-wide chunk counts. | | scheduled_scene_cuts | unknown[] | Sorted chunk indices at which scheduled hard cuts will fire, as session-wide chunk counts. |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onState((msg) => {
  console.log(
    "state",
    msg.seed,
    msg.paused,
    msg.running,
    msg.started,
    msg.has_prompt,
    msg.current_chunk,
    msg.current_frame,
    msg.session_chunk,
    msg.current_prompt,
    msg.scheduled_shots,
    msg.scheduled_scene_cuts,
  );
});
await longliveV2.connect(jwt);

React

import { useLongliveV2State } from "@reactor-models/longlive-v2";

// Inside a React component wrapped by <LongliveV2Provider>:
useLongliveV2State((msg) => {
  console.log(
    "state",
    msg.seed,
    msg.paused,
    msg.running,
    msg.started,
    msg.has_prompt,
    msg.current_chunk,
    msg.current_frame,
    msg.session_chunk,
    msg.current_prompt,
    msg.scheduled_shots,
    msg.scheduled_scene_cuts,
  );
});

shot_set

Emitted when set_shot is accepted. Before start it confirms the opening shot has been seeded; after start it confirms a soft shot change has been queued for the next chunk boundary.

Listener: onShotSet · React hook: useLongliveV2ShotSet

| Field | Type | Description | |---|---|---| | prompt | string | The shot prompt that was accepted. |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onShotSet((msg) => {
  console.log("shot_set", msg.prompt);
});
await longliveV2.connect(jwt);

React

import { useLongliveV2ShotSet } from "@reactor-models/longlive-v2";

// Inside a React component wrapped by <LongliveV2Provider>:
useLongliveV2ShotSet((msg) => {
  console.log("shot_set", msg.prompt);
});

scene_cut

Emitted when a hard scene_cut fires (immediate or scheduled). Marks the clean break to a new scene: at_session_chunk is the session-wide chunk count at which the new scene took effect. The per-scene chunk counter (current_chunk) restarts at 0 while the session-wide session_chunk keeps counting.

Listener: onSceneCut · React hook: useLongliveV2SceneCut

| Field | Type | Description | |---|---|---| | prompt | string | The prompt the scene was cut to. | | at_session_chunk | number | Session-wide chunk count at which the cut fired (total chunks since the most recent start). |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onSceneCut((msg) => {
  console.log("scene_cut", msg.prompt, msg.at_session_chunk);
});
await longliveV2.connect(jwt);

React

import { useLongliveV2SceneCut } from "@reactor-models/longlive-v2";

// Inside a React component wrapped by <LongliveV2Provider>:
useLongliveV2SceneCut((msg) => {
  console.log("scene_cut", msg.prompt, msg.at_session_chunk);
});

command_error

Emitted when a command is rejected because preconditions are not met.

Listener: onCommandError · React hook: useLongliveV2CommandError

| Field | Type | Description | |---|---|---| | reason | string | Human-readable explanation of why the command was rejected. | | command | string | Name of the command that was rejected. |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onCommandError((msg) => {
  console.log("command_error", msg.reason, msg.command);
});
await longliveV2.connect(jwt);

React

import { useLongliveV2CommandError } from "@reactor-models/longlive-v2";

// Inside a React component wrapped by <LongliveV2Provider>:
useLongliveV2CommandError((msg) => {
  console.log("command_error", msg.reason, msg.command);
});

chunk_complete

Emitted once per completed chunk of main_video.

Listener: onChunkComplete · React hook: useLongliveV2ChunkComplete

| Field | Type | Description | |---|---|---| | chunk_index | number | Index of this chunk within the current scene. Restarts at 0 after every hard scene_cut; use session_chunk for the cumulative count since start. | | active_prompt | string | The prompt used to generate this chunk. | | session_chunk | number | Session-wide chunk count — total chunks since the most recent start. Keeps counting across cuts. | | frames_emitted | number | Running total of frames emitted since the last reset or connect. |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onChunkComplete((msg) => {
  console.log(
    "chunk_complete",
    msg.chunk_index,
    msg.active_prompt,
    msg.session_chunk,
    msg.frames_emitted,
  );
});
await longliveV2.connect(jwt);

React

import { useLongliveV2ChunkComplete } from "@reactor-models/longlive-v2";

// Inside a React component wrapped by <LongliveV2Provider>:
useLongliveV2ChunkComplete((msg) => {
  console.log(
    "chunk_complete",
    msg.chunk_index,
    msg.active_prompt,
    msg.session_chunk,
    msg.frames_emitted,
  );
});

shot_scheduled

Emitted when schedule_shot is accepted. Confirms a future soft shot change has been queued — the transition itself fires later when the session reaches at_session_chunk and is reported by that chunk's normal chunk_complete and state messages.

Listener: onShotScheduled · React hook: useLongliveV2ShotScheduled

| Field | Type | Description | |---|---|---| | prompt | string | The prompt scheduled for the soft shot change. | | at_session_chunk | number | Session-wide chunk count at which the scheduled shot change fires (i.e. after this many total chunks since start). |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onShotScheduled((msg) => {
  console.log("shot_scheduled", msg.prompt, msg.at_session_chunk);
});
await longliveV2.connect(jwt);

React

import { useLongliveV2ShotScheduled } from "@reactor-models/longlive-v2";

// Inside a React component wrapped by <LongliveV2Provider>:
useLongliveV2ShotScheduled((msg) => {
  console.log("shot_scheduled", msg.prompt, msg.at_session_chunk);
});

generation_reset

Emitted after reset clears session state.

Listener: onGenerationReset · React hook: useLongliveV2GenerationReset

| Field | Type | Description | |---|---|---| | reason | string | Short human-readable reason the reset was issued. |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onGenerationReset((msg) => {
  console.log("generation_reset", msg.reason);
});
await longliveV2.connect(jwt);

React

import { useLongliveV2GenerationReset } from "@reactor-models/longlive-v2";

// Inside a React component wrapped by <LongliveV2Provider>:
useLongliveV2GenerationReset((msg) => {
  console.log("generation_reset", msg.reason);
});

generation_paused

Emitted in response to pause, once the current chunk finishes.

Listener: onGenerationPaused · React hook: useLongliveV2GenerationPaused

| Field | Type | Description | |---|---|---| | chunk_index | number | Index of the last completed chunk before pausing. |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onGenerationPaused((msg) => {
  console.log("generation_paused", msg.chunk_index);
});
await longliveV2.connect(jwt);

React

import { useLongliveV2GenerationPaused } from "@reactor-models/longlive-v2";

// Inside a React component wrapped by <LongliveV2Provider>:
useLongliveV2GenerationPaused((msg) => {
  console.log("generation_paused", msg.chunk_index);
});

generation_resumed

Emitted in response to resume when leaving the paused state.

Listener: onGenerationResumed · React hook: useLongliveV2GenerationResumed

| Field | Type | Description | |---|---|---| | chunk_index | number | Index of the next chunk to be generated. |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onGenerationResumed((msg) => {
  console.log("generation_resumed", msg.chunk_index);
});
await longliveV2.connect(jwt);

React

import { useLongliveV2GenerationResumed } from "@reactor-models/longlive-v2";

// Inside a React component wrapped by <LongliveV2Provider>:
useLongliveV2GenerationResumed((msg) => {
  console.log("generation_resumed", msg.chunk_index);
});

generation_started

Emitted once when start succeeds and frames begin streaming.

Listener: onGenerationStarted · React hook: useLongliveV2GenerationStarted

| Field | Type | Description | |---|---|---| | prompt | string | The prompt active at the start of generation. | | chunk_num | number | Total number of chunks the run will produce. | | frame_num | number | Total number of pixel frames the run will emit. |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onGenerationStarted((msg) => {
  console.log(
    "generation_started",
    msg.prompt,
    msg.chunk_num,
    msg.frame_num,
  );
});
await longliveV2.connect(jwt);

React

import { useLongliveV2GenerationStarted } from "@reactor-models/longlive-v2";

// Inside a React component wrapped by <LongliveV2Provider>:
useLongliveV2GenerationStarted((msg) => {
  console.log(
    "generation_started",
    msg.prompt,
    msg.chunk_num,
    msg.frame_num,
  );
});

generation_complete

Emitted when every chunk of a run has streamed. The session returns to idle (started = false) and is locked: start, set_shot, scene_cut, schedule_scene_cut, and schedule_shot all reject with command_error until the client calls reset. This enforces a clean slate between runs — the prompt and any scheduled cuts or shots from the completed run cannot leak into the next.

Listener: onGenerationComplete · React hook: useLongliveV2GenerationComplete

| Field | Type | Description | |---|---|---| | total_chunks | number | Total number of chunks produced by the run. |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onGenerationComplete((msg) => {
  console.log("generation_complete", msg.total_chunks);
});
await longliveV2.connect(jwt);

React

import { useLongliveV2GenerationComplete } from "@reactor-models/longlive-v2";

// Inside a React component wrapped by <LongliveV2Provider>:
useLongliveV2GenerationComplete((msg) => {
  console.log("generation_complete", msg.total_chunks);
});

scene_cut_scheduled

Emitted when schedule_scene_cut is accepted. Confirms a future hard cut has been queued — the cut itself fires later when the session reaches at_session_chunk and is reported by scene_cut.

Listener: onSceneCutScheduled · React hook: useLongliveV2SceneCutScheduled

| Field | Type | Description | |---|---|---| | prompt | string | The prompt scheduled for the scene cut. | | at_session_chunk | number | Session-wide chunk count at which the scheduled cut fires (total chunks since the most recent start). |

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onSceneCutScheduled((msg) => {
  console.log("scene_cut_scheduled", msg.prompt, msg.at_session_chunk);
});
await longliveV2.connect(jwt);

React

import { useLongliveV2SceneCutScheduled } from "@reactor-models/longlive-v2";

// Inside a React component wrapped by <LongliveV2Provider>:
useLongliveV2SceneCutScheduled((msg) => {
  console.log("scene_cut_scheduled", msg.prompt, msg.at_session_chunk);
});

Tracks

Named media channels between your app and the LongliveV2 model. Use the typed helpers below — LongliveV2Model.publish<Track> / on<Track> in plain JS, and useLongliveV2Track or the per-track <LongliveV2<Track>View> components in React — so track names are checked at compile time.

main_video

A video channel you subscribe to — the model publishes this for your app to render.

JavaScript

import { LongliveV2Model } from "@reactor-models/longlive-v2";

const longliveV2 = new LongliveV2Model();
longliveV2.onMainVideo((track, stream) => {
  // attach to a <video> element, pipe to a canvas, etc.
  videoEl.srcObject = stream;
});
await longliveV2.connect(jwt);

React

"use client";
import { LongliveV2MainVideoView } from "@reactor-models/longlive-v2";

// Inside a component wrapped by <LongliveV2Provider>:
export function Example() {
  return <LongliveV2MainVideoView className="w-full aspect-video" />;
}