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

# JavaScript SDK Talk Commands

> Send text for an Anam persona to speak, stream ordered utterances, and control how commands interact with current output.

## Basic Usage

During a persona session, you can force a response from the persona using the `talk` method. This is useful when the user interacts with a UI element or when you use your own LLM instead of Anam's built-in models.

<Note>
  Both `talk()` and `createTalkMessageStream()` require an active streaming session. Call `stream()` or `streamToVideoElement()` first.
</Note>

```typescript theme={"system"}
await anamClient.talk("Hello, how are you?");
```

This sends the text to the persona, which then speaks it aloud.

<Note>
  To learn more about using the `talk` method with your own LLM, see the [Custom LLMs](https://anam.ai/docs/personas/llms/custom-llms) guide.
</Note>

## Streaming Talk Input

For lower latency, stream messages to the persona in chunks. This works well when streaming output from a custom LLM.

```typescript theme={"system"}
const talkMessageStream = anamClient.createTalkMessageStream();

const chunks = ["He", "l", "lo", ", how are you?"];

for (const chunk of chunks) {
  if (talkMessageStream.isActive()) {
    await talkMessageStream.streamMessageChunk(
      chunk,
      chunk === chunks[chunks.length - 1] // endOfSpeech: true on last chunk
    );
  }
}
```

<Note>
  Each `TalkMessageStream` represents one conversation turn. Once the turn ends, create a new stream for the next turn.
</Note>

### Available Methods

The `TalkMessageStream` object provides these methods:

| Method                                                   | Description                                                                                                           |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `streamMessageChunk(content, endOfSpeech, utteranceId?)` | Send a text chunk. Set `endOfSpeech: true` on the final chunk. Optionally start a new utterance with `utteranceId`.   |
| `endMessage()`                                           | End the stream without sending more content and preserve its latest utterance ID. Alternative to `endOfSpeech: true`. |
| `isActive()`                                             | Returns `true` if the stream can still accept chunks.                                                                 |
| `getState()`                                             | Returns the current state: `UNSTARTED`, `STREAMING`, `INTERRUPTED`, or `ENDED`.                                       |
| `getCorrelationId()`                                     | Returns the correlation ID for this stream.                                                                           |

### Streaming Multiple Utterances

With `@anam-ai/js-sdk` version 4.26.0 or later, you can divide one `TalkMessageStream` into ordered utterances. A new utterance waits for the previous one to finish playing while both remain part of the same conversation turn. This lets the persona speak before a tool call and continue when the result is ready.

<Note>
  Multi-utterance talk streams require a [Cara 4 avatar](/docs/introduction/models#cara-4). With Cara 3, the persona still speaks the text, but Anam treats the stream as one utterance and does not return your utterance IDs. The SDK does not throw an error or warning.
</Note>

Pass an `utteranceId` as the third argument on the first chunk of each utterance. The ID must be a lowercase UUID v4; `crypto.randomUUID()` returns the required format. The method throws before sending the chunk if the ID is invalid. Omit the ID from later chunks in the same utterance.

```typescript theme={"system"}
const talkMessageStream = anamClient.createTalkMessageStream();
const beforeToolUtteranceId = crypto.randomUUID();

// The first utterance can play while the tool runs.
await talkMessageStream.streamMessageChunk(
  "Let me check ",
  false,
  beforeToolUtteranceId
);
await talkMessageStream.streamMessageChunk("that for you.", false);

const toolResultText = await runToolCall();
const toolResultUtteranceId = crypto.randomUUID();

// A new ID starts the next utterance after the first one.
await talkMessageStream.streamMessageChunk(
  toolResultText,
  true,
  toolResultUtteranceId
);
```

The IDs are returned in persona `MESSAGE_STREAM_EVENT_RECEIVED` events as `utteranceId`. Use them to match streamed captions to the utterances you sent. See [Listen for JavaScript SDK Events](/docs/javascript-sdk/reference/events) for the event payload.

<Warning>
  Send the next text chunk within 15 seconds. After 15 seconds without a chunk that contains text, the server closes the stream and rejects later chunks with the same correlation ID. Empty chunks do not reset the timeout. For a longer tool call, end the first stream and create a new one for the result.
</Warning>

### Ending a Stream

End a conversation turn in one of two ways:

**Option 1: Set `endOfSpeech` on the last chunk**

```typescript theme={"system"}
await talkMessageStream.streamMessageChunk("final text", true);
```

**Option 2: Call `endMessage()` separately**

```typescript theme={"system"}
await talkMessageStream.streamMessageChunk("final text", false);
await talkMessageStream.endMessage();
```

### Handling Interruptions

When a user speaks during a stream, the SDK emits `AnamEvent.TALK_STREAM_INTERRUPTED` and closes the stream:

```typescript theme={"system"}
import { AnamEvent } from "@anam-ai/js-sdk";

anamClient.addListener(AnamEvent.TALK_STREAM_INTERRUPTED, (correlationId) => {
  console.log("Stream interrupted:", correlationId);
  // Handle the interruption - e.g., stop your LLM generation
});
```

### Checking Stream State

Check whether a stream can still accept chunks:

```typescript theme={"system"}
if (talkMessageStream.isActive()) {
  await talkMessageStream.streamMessageChunk(chunk, false);
}

// Or check the specific state
const state = talkMessageStream.getState();
// Returns: UNSTARTED | STREAMING | INTERRUPTED | ENDED
```

### Error Handling

The `streamMessageChunk` method throws an error if the stream is not active:

```typescript theme={"system"}
try {
  await talkMessageStream.streamMessageChunk("text", false);
} catch (error) {
  // Stream is in INTERRUPTED or ENDED state
  console.error("Cannot send chunk:", error.message);
}
```

## Correlation IDs

Attach a correlation ID to track streams, especially useful for matching interruption events:

```typescript theme={"system"}
const correlationId = "request-123";
const talkMessageStream = anamClient.createTalkMessageStream(correlationId);

// Later, when handling interruptions:
anamClient.addListener(AnamEvent.TALK_STREAM_INTERRUPTED, (interruptedCorrelationId) => {
  if (interruptedCorrelationId === correlationId) {
    // This specific stream was interrupted
  }
});
```

<Note>
  Use unique correlation IDs for each `TalkMessageStream`. The ID appears in `TALK_STREAM_INTERRUPTED` events, helping you identify which stream was interrupted.
</Note>

## Next Steps

<Card title="Audio Control" icon="volume" href="https://anam.ai/docs/javascript-sdk/reference/audio-control">
  Learn how to control audio input in your Anam sessions
</Card>
