> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reactor.inc/llms.txt
> Use this file to discover all available pages before exploring further.

# LTX schema reference

> The complete LTX command and event schema, with end-to-end examples.

This page documents the LTX wire surface: every command you send, every message the model emits
back, and a complete example. For a general understanding of LTX and a quick start, see the
[overview](/model-api-reference/ltx2/overview).

## The session model

A take is generated from the conditions as they stood at `start`. Every `set_*` command stays valid
while a take runs, but a mid-run change applies to the *next* take, never the current one. The model
lists each queued field in `state_update.queued_changes`. `start` is the one exception: it is
rejected while a take is running, so wait for that take to end before beginning the next.

`state_update.valid_commands` is the authoritative list of what the session would accept right now.
Drive your UI from it; any command not listed comes back as `command_error`.

## Commands

Send commands with `reactor.sendCommand()` (base SDK) or the typed methods (typed SDK). All
available commands:

| Command                | Description                                                                         |
| ---------------------- | ----------------------------------------------------------------------------------- |
| `set_avatar_image`     | Set the identity-anchor photo. Required before `start`.                             |
| `set_script`           | Set the spoken words. Required before `start`.                                      |
| `set_prompt`           | Set the scene description. Optional.                                                |
| `set_wpm`              | Set the speaking pace, 80–220 words per minute. Default 140; other values reject.   |
| `set_duration_seconds` | Pin the take length, or 0 to derive it from the script. Clamps to 4–300s.           |
| `set_seed`             | Set the seed for the next take. Same conditions and seed reproduce the take.        |
| `start`                | Begin a take from the conditions as they stand.                                     |
| `pause`                | Freeze the stream mid-take without ending the take; generation buffers ahead.       |
| `resume`               | Continue a paused stream instantly, with no warm-up.                                |
| `stop`                 | End the take early, keeping every condition so `start` can run it again.            |
| `reset`                | End the take and wipe every condition back to defaults, including the avatar image. |

### `set_avatar_image`

Provide the still image that defines the avatar's identity, framing, and background for the whole
take. Required before calling `start`.

The image is fitted to the 16:9 generation canvas (640×352), so a tall portrait gets its top cropped
off by that fit; crop to the frame before uploading. On success the model emits
`avatar_image_accepted` with the decoded dimensions and a `state_update` where `has_avatar_image` is
`true`; an unreadable file returns `command_error`.

**Parameters:**

| Parameter      | Type    | Required | Description                                     |
| -------------- | ------- | -------- | ----------------------------------------------- |
| `avatar_image` | FileRef | Yes      | A PNG/JPEG reference returned by `uploadFile()` |

<Note>
  Use a clear, well-lit photo of one person facing the camera, framed so the whole head is visible.
  A low-quality, occluded, or profile photo degrades the whole take, and a photo with multiple
  people is undefined as to which becomes the avatar.
</Note>

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const ref = await reactor.uploadFile(imageFile);
  await reactor.sendCommand("set_avatar_image", { avatar_image: ref });
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const { uploadFile, setAvatarImage } = useLtx2();

  const ref = await uploadFile(imageFile);
  await setAvatarImage({ avatar_image: ref });
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  ref = await reactor.upload_file(image_file)
  await reactor.send_command("set_avatar_image", {"avatar_image": ref})
  ```
</CodeGroup>

### `set_script`

Set the words the avatar speaks. Required before calling `start`. Unless an explicit duration is
set, the take's length is the script's word count divided by the session's words-per-minute pace.

The script is read once at `start` and cannot change part-way through a take. On success the model
emits `script_accepted` carrying the word count and the take's derived length (`derived_seconds`); a
blank script returns `command_error`.

**Parameters:**

| Parameter | Type   | Required | Description                                       |
| --------- | ------ | -------- | ------------------------------------------------- |
| `script`  | string | Yes      | The speech, up to 10,000 characters of plain text |

<Tip>
  Natural punctuation produces natural pauses. ALL-CAPS or emoji-heavy scripts are spoken literally
  or render garbled.
</Tip>

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.sendCommand("set_script", { script: "Hello from LTX." });
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const { setScript } = useLtx2();

  await setScript({ script: "Hello from LTX." });
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.send_command("set_script", {"script": "Hello from LTX."})
  ```
</CodeGroup>

### `set_prompt`

Describe the voice, the staging, and the delivery: register and accent, lighting, mood, and energy.
The model generates audio and video in one pass and conditions both on this prompt, so it is where
the voice is cast as well as the shot. This is not necessary before sending `start`. If left blank,
the prompt defaults to a straight-to-camera talking head, and sending an empty string restores that
default. The prompt is read once at `start` and applies to the whole take.

`set_prompt` does **not** control identity or wardrobe, which are anchored to the avatar image.
Resending it re-casts the voice, so a character that has been speaking across several takes comes
back sounding like someone else; set it once and pin `set_seed` for the same reason. See the
[prompt guide](/model-api-reference/ltx2/prompt-guide) for how to write one.

**Parameters:**

| Parameter | Type   | Required | Description                                          |
| --------- | ------ | -------- | ---------------------------------------------------- |
| `prompt`  | string | No       | Scene and delivery description, up to 800 characters |

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.sendCommand("set_prompt", {
    prompt: "News anchor at a desk, professional tone, warm studio lighting",
  });
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const { setPrompt } = useLtx2();

  await setPrompt({
    prompt: "News anchor at a desk, professional tone, warm studio lighting",
  });
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.send_command("set_prompt", {
      "prompt": "News anchor at a desk, professional tone, warm studio lighting",
  })
  ```
</CodeGroup>

### `set_wpm`

Override the speaking pace in words per minute. Accepts 80–220; the default is 140. The pace lays
the script out over the take and sets its derived length. This controls how fast the script is
spoken, not video playback speed.

Out-of-range values are rejected with `command_error`, unlike `set_duration_seconds`, which clamps.
That 80–220 range can differ between deployments, so read `wpm_min`/`wpm_max` off `state_update`
rather than hard-coding it. On success the model emits `wpm_accepted` carrying the take's recomputed
length.

**Parameters:**

| Parameter | Type    | Required | Description              |
| --------- | ------- | -------- | ------------------------ |
| `wpm`     | integer | Yes      | Words per minute, 80–220 |

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.sendCommand("set_wpm", { wpm: 170 });
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const { setWpm } = useLtx2();

  await setWpm({ wpm: 170 });
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.send_command("set_wpm", {"wpm": 170})
  ```
</CodeGroup>

### `set_duration_seconds`

Set an explicit take length in seconds. When `duration_seconds` is zero, the length goes back to
being derived from the script at the session's pacing.

Out-of-range values clamp to the deployment's 4–300 second range, unlike `set_wpm`, which rejects.
`state_update.effective_seconds` reports the length the next take will actually have, whichever way
it was determined. If the requested duration exceeds what the script needs at the current pace, the
avatar continues in an idle presence for the remainder; it does not loop or extend the script.

**Parameters:**

| Parameter          | Type   | Required | Description                                                   |
| ------------------ | ------ | -------- | ------------------------------------------------------------- |
| `duration_seconds` | number | Yes      | Take length in seconds, 4–300, or 0 to derive from the script |

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.sendCommand("set_duration_seconds", { duration_seconds: 30 });
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const { setDurationSeconds } = useLtx2();

  await setDurationSeconds({ duration_seconds: 30 });
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.send_command("set_duration_seconds", {"duration_seconds": 30})
  ```
</CodeGroup>

### `set_seed`

Set the seed for the next take. The same conditions with the same seed reproduce the same take.
Reproducibility holds within one deployed build; a new release can change what a given seed
produces.

**Parameters:**

| Parameter | Type    | Required | Description |
| --------- | ------- | -------- | ----------- |
| `seed`    | integer | Yes      | Any integer |

<Info>
  Use `set_seed` to generate variations on the same setup. Keep the seed to get the same take again,
  change it for a different take from the same image, script, and settings.
</Info>

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.sendCommand("set_seed", { seed: 7 });
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const { setSeed } = useLtx2();

  await setSeed({ seed: 7 });
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.send_command("set_seed", {"seed": 7})
  ```
</CodeGroup>

### `start`

Begin generating a take. Requires an avatar image and a script; `state_update.ready` reports when
both are set. Emits `generation_started`, then one `window_progress` for each window it streams.
Returns `command_error` if a condition is missing or a take is already in flight.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.sendCommand("start", {});
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const { start } = useLtx2();

  await start();
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.send_command("start", {})
  ```
</CodeGroup>

### `pause`

Freeze the output stream mid-take on its current frame. Generation keeps running ahead into a
bounded buffer, so `resume` continues instantly. Emits `generation_paused`. Returns `command_error`
if no take is in flight or it is already paused.

<Warning>
  Pausing does not reduce what you pay. Billing is per session-second for as long as the GPU is held
  for you, so a paused take costs the same as a running one. See [Pricing &
  Billing](/resources/billing).
</Warning>

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.sendCommand("pause", {});
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const { pause } = useLtx2();

  await pause();
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.send_command("pause", {})
  ```
</CodeGroup>

### `resume`

Continue a paused stream exactly where it froze, with no warm-up. Emits `generation_resumed`.
Returns `command_error` if the stream is not paused.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.sendCommand("resume", {});
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const { resume } = useLtx2();

  await resume();
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.send_command("resume", {})
  ```
</CodeGroup>

### `stop`

End the take in flight within about a second, keeping every condition (image, script, and settings)
so `start` immediately begins a fresh take with the same setup. Emits `generation_stopped`. Returns
`command_error` if nothing is in flight. To end a take *and* clear the conditions, use `reset`.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.sendCommand("stop", {});
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const { stop } = useLtx2();

  await stop();
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.send_command("stop", {})
  ```
</CodeGroup>

### `reset`

Return every condition to its default and the model to waiting for new ones. This is the only way to
clear the avatar image. Valid at any time; during a take it also stops generation within about a
second. A moment of already-buffered video and audio may still play out, and no
`generation_complete` follows. Emits `generation_reset`.

To stop without losing the conditions, use `stop` instead.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.sendCommand("reset", {});
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const { reset } = useLtx2();

  await reset();
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  await reactor.send_command("reset", {})
  ```
</CodeGroup>

## Messages from model

With the base SDK, listen with `reactor.on("message", ...)`. Every message arrives as JSON with a
`type` and a `data` payload. The typed SDK gives you one hook per message, with the payload fields
on the message itself:

<CodeGroup>
  ```typescript Base SDK theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  reactor.on("message", (msg) => {
    if (msg.type === "state_update") {
      console.log(msg.data.valid_commands);
    }
  });
  ```

  ```tsx Typed SDK theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  useLtx2StateUpdate((msg) => {
    console.log(msg.valid_commands);
  });
  ```
</CodeGroup>

All messages:

| Message                 | When                                                               | Payload                                                          |
| ----------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------- |
| `state_update`          | On connect and after every change to the session's state           | Full session snapshot (see below)                                |
| `avatar_image_accepted` | `set_avatar_image` accepted and the image decodes                  | `name`, `width`, `height`                                        |
| `script_accepted`       | `set_script` accepted                                              | `words`, `derived_seconds`, `effective_seconds`                  |
| `prompt_accepted`       | `set_prompt` accepted                                              | `prompt`                                                         |
| `wpm_accepted`          | `set_wpm` accepted                                                 | `wpm`, `derived_seconds`, `effective_seconds`                    |
| `duration_accepted`     | `set_duration_seconds` accepted                                    | `duration_seconds`, `effective_seconds`                          |
| `seed_accepted`         | `set_seed` accepted                                                | `seed`                                                           |
| `generation_started`    | Once when a take begins, before any frame is sent                  | `seconds`, `total_windows`, `width`, `height`                    |
| `window_progress`       | Once per generation window streamed on the output tracks           | `window_index`, `total_windows`, `seconds_sent`, `total_seconds` |
| `generation_paused`     | `pause` accepted; the stream freezes, generation buffers ahead     | `seconds_sent`                                                   |
| `generation_resumed`    | `resume` accepted; the stream continues where it froze             | `seconds_sent`                                                   |
| `generation_stopped`    | A take ended early because `stop` was accepted                     | `seconds_sent`                                                   |
| `generation_complete`   | A take reached the end of its script                               | `seconds_sent`                                                   |
| `generation_failed`     | A take stopped early because something went wrong; the model idles | `reason`, `seconds_sent`                                         |
| `generation_reset`      | `reset` accepted; every condition is back to defaults              | `was_generating`                                                 |
| `command_error`         | A command was rejected; it had no effect                           | `command`, `reason`                                              |

<Note>
  Each `..._accepted` message fires once and is followed by a `state_update` carrying the same
  information. Render persistent UI from the snapshot and use the accepted messages as
  confirmations.
</Note>

### `state_update` payload

`state_update` is one snapshot of everything observable, so a client can render from it alone
instead of accumulating the individual messages.

| Field               | Type             | Meaning                                                                                 |
| ------------------- | ---------------- | --------------------------------------------------------------------------------------- |
| `script`            | `string \| null` | Script in effect, or `null` when none is set                                            |
| `prompt`            | `string`         | Scene description in effect                                                             |
| `has_avatar_image`  | `boolean`        | An avatar image is set                                                                  |
| `wpm`               | `integer`        | Speaking pace in words per minute                                                       |
| `wpm_min`           | `integer`        | Lowest pace this deployment accepts via `set_wpm`                                       |
| `wpm_max`           | `integer`        | Highest pace this deployment accepts via `set_wpm`                                      |
| `duration_seconds`  | `number`         | Explicit take length, or `0` when the length derives from the script                    |
| `effective_seconds` | `number`         | Length the next take will actually have. Zero until a script is set.                    |
| `seed`              | `integer`        | Seed the next take uses                                                                 |
| `ready`             | `boolean`        | An avatar image and a script are set, so `start` is valid                               |
| `generating`        | `boolean`        | A take is being generated right now                                                     |
| `paused`            | `boolean`        | The output stream is paused mid-take; `resume` continues it instantly                   |
| `finished`          | `boolean`        | The last take ended and the model is idle                                               |
| `valid_commands`    | `string[]`       | Commands the session would accept right now                                             |
| `queued_changes`    | `string[]`       | Condition fields changed during generation. They take effect on the next take.          |
| `window_index`      | `integer`        | Zero-based index of the most recent window streamed this take, or `-1` before the first |
| `total_windows`     | `integer`        | Windows the current take will stream; zero before one starts                            |
| `seconds_sent`      | `number`         | Seconds of video and audio sent so far this take                                        |

<Note>
  There is no single status string. Derive one from the booleans if you need it: `generating` and
  `paused` describe a take in flight, `finished` a completed one, and `ready` whether `start` would
  be accepted.
</Note>

**Example handler:**

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  reactor.on("message", (msg) => {
    switch (msg.type) {
      case "state_update":
        // The snapshot is the source of truth, so render controls from it.
        updateButtons(msg.data.valid_commands);
        progressEl.textContent = `${msg.data.seconds_sent.toFixed(1)}s / ${msg.data.effective_seconds}s`;
        break;
      case "window_progress":
        console.log(`Window ${msg.data.window_index + 1}/${msg.data.total_windows}`);
        break;
      case "generation_complete":
        console.log(`Take finished after ${msg.data.seconds_sent}s`);
        break;
      case "command_error":
        console.error(`${msg.data.command} rejected: ${msg.data.reason}`);
        break;
    }
  });
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  // One typed hook per message; payload fields sit directly on the message.
  useLtx2StateUpdate((msg) => {
    updateButtons(msg.valid_commands);
    setProgress(`${msg.seconds_sent.toFixed(1)}s / ${msg.effective_seconds}s`);
  });

  useLtx2WindowProgress((msg) => {
    console.log(`Window ${msg.window_index + 1}/${msg.total_windows}`);
  });

  useLtx2GenerationComplete((msg) => {
    console.log(`Take finished after ${msg.seconds_sent}s`);
  });

  useLtx2CommandError((msg) => {
    console.error(`${msg.command} rejected: ${msg.reason}`);
  });
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  @reactor.on_message
  def handle(msg):
      data = msg["data"]
      if msg["type"] == "state_update":
          print(f"Valid: {', '.join(data['valid_commands'])}")
          print(f"Sent {data['seconds_sent']:.1f}s of {data['effective_seconds']}s")
      elif msg["type"] == "window_progress":
          print(f"Window {data['window_index'] + 1}/{data['total_windows']}")
      elif msg["type"] == "generation_complete":
          print(f"Take finished after {data['seconds_sent']}s")
      elif msg["type"] == "command_error":
          print(f"{data['command']} rejected: {data['reason']}")
  ```
</CodeGroup>

## Complete example

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  import { Reactor, FileRef } from "@reactor-team/js-sdk";

  const reactor = new Reactor({ modelName: "reactor/ltx2" });

  const videoElement = document.getElementById("video") as HTMLVideoElement;
  const receivedTracks: MediaStreamTrack[] = [];

  // Both tracks go into ONE MediaStream on ONE element, so video and
  // audio stay on the same clock.
  reactor.on("trackReceived", (name, track) => {
    if (name === "main_video" || name === "main_audio") {
      receivedTracks.push(track);
      videoElement.srcObject = new MediaStream(receivedTracks);
    }
  });

  reactor.on("message", (msg) => {
    if (msg.type === "state_update") {
      updateButtons(msg.data.valid_commands);
    }
    if (msg.type === "command_error") {
      console.error(`${msg.data.command} rejected: ${msg.data.reason}`);
    }
  });

  await reactor.connect(token); // see Authentication for how to obtain a token

  const avatarImageRef: FileRef = await reactor.uploadFile(imageFile);
  await reactor.sendCommand("set_avatar_image", { avatar_image: avatarImageRef });
  await reactor.sendCommand("set_script", { script: "Hello from LTX." });
  await reactor.sendCommand("set_prompt", { prompt: "News anchor at a desk, professional tone" });
  await reactor.sendCommand("set_wpm", { wpm: 170 });
  await reactor.sendCommand("set_seed", { seed: 7 });
  await reactor.sendCommand("start", {});
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  import { useMemo, useEffect, useRef } from "react";
  import { Ltx2Provider, useLtx2, useLtx2CommandError } from "@reactor-models/ltx2";

  function App() {
    return (
      <Ltx2Provider getJwt={fetchToken}>
        <Take />
      </Ltx2Provider>
    );
  }

  function Take() {
    const { tracks, uploadFile, setAvatarImage, setScript, setPrompt, setWpm, setSeed, start } =
      useLtx2();

    useLtx2CommandError((msg) => {
      console.error(`${msg.command} rejected: ${msg.reason}`);
    });

    // Both tracks in ONE MediaStream on ONE element, so video and audio
    // stay on the same clock.
    const videoRef = useRef<HTMLVideoElement>(null);
    const stream = useMemo(() => {
      const list = [tracks["main_video"], tracks["main_audio"]].filter(Boolean);
      return list.length ? new MediaStream(list) : null;
    }, [tracks]);
    useEffect(() => {
      if (videoRef.current) videoRef.current.srcObject = stream;
    }, [stream]);

    async function generate(imageFile: File) {
      const ref = await uploadFile(imageFile);
      await setAvatarImage({ avatar_image: ref });
      await setScript({ script: "Hello from LTX." });
      await setPrompt({ prompt: "News anchor at a desk, professional tone" });
      await setWpm({ wpm: 170 });
      await setSeed({ seed: 7 });
      await start();
    }

    return <video ref={videoRef} autoPlay playsInline />;
  }
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  import asyncio
  import os
  from reactor_sdk import Reactor

  async def main():
      reactor = Reactor(model_name="reactor/ltx2", api_key=os.environ["REACTOR_API_KEY"])

      @reactor.on_message
      def handle(msg):
          if msg["type"] == "command_error":
              print(f"{msg['data']['command']} rejected: {msg['data']['reason']}")

      await reactor.connect()

      with open("avatar.jpg", "rb") as f:
          avatar_image_ref = await reactor.upload_file(f)

      await reactor.send_command("set_avatar_image", {"avatar_image": avatar_image_ref})
      await reactor.send_command("set_script", {"script": "Hello from LTX."})
      await reactor.send_command("set_prompt", {"prompt": "News anchor at a desk, professional tone"})
      await reactor.send_command("set_wpm", {"wpm": 170})
      await reactor.send_command("set_seed", {"seed": 7})
      await reactor.send_command("start", {})

      await asyncio.Event().wait()

  asyncio.run(main())
  ```
</CodeGroup>
