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

# Turn Events

> Handle user and assistant turn lifecycle events for transcriptions and turn tracking

## Overview

Turn events provide hooks into the conversation turn lifecycle, allowing you to know when users and assistants start or stop speaking. These events are emitted by the context aggregators (`LLMUserAggregator` and `LLMAssistantAggregator`) and are particularly useful for:

* **Collecting transcriptions** - Get complete user and assistant transcripts when turns end
* **Turn tracking** - Monitor conversation flow and timing
* **Analytics** - Measure turn durations, detect timeouts, and track conversation patterns

## Events Summary

| Event                       | Emitter                | Description                                         |
| --------------------------- | ---------------------- | --------------------------------------------------- |
| `on_user_turn_started`      | `user_aggregator`      | User begins speaking                                |
| `on_user_turn_stopped`      | `user_aggregator`      | User finishes speaking (includes transcript)        |
| `on_user_turn_stop_timeout` | `user_aggregator`      | User turn ended due to timeout                      |
| `on_user_turn_idle`         | `user_aggregator`      | User has been idle (not speaking) for timeout       |
| `on_user_mute_started`      | `user_aggregator`      | User input was muted                                |
| `on_user_mute_stopped`      | `user_aggregator`      | User input was unmuted                              |
| `on_assistant_turn_started` | `assistant_aggregator` | Assistant begins responding                         |
| `on_assistant_turn_stopped` | `assistant_aggregator` | Assistant finishes responding (includes transcript) |
| `on_assistant_thought`      | `assistant_aggregator` | Assistant produced a thought (reasoning models)     |
| `on_summary_applied`        | `assistant_aggregator` | Context summarization was applied                   |

## User Turn Events

User turn events are registered on the `user_aggregator` from an `LLMContextAggregatorPair`.

### on\_user\_turn\_started

Fired when a user turn is detected to have started, based on the configured [start strategies](/api-reference/server/utilities/turn-management/user-turn-strategies#start-strategies).

```python theme={null}
@user_aggregator.event_handler("on_user_turn_started")
async def on_user_turn_started(aggregator, strategy):
    print(f"User started speaking (detected by {strategy})")
```

**Parameters:**

| Parameter    | Type                        | Description                                |
| ------------ | --------------------------- | ------------------------------------------ |
| `aggregator` | `LLMUserAggregator`         | The user aggregator instance               |
| `strategy`   | `BaseUserTurnStartStrategy` | The strategy that triggered the turn start |

### on\_user\_turn\_stopped

Fired when a user turn is detected to have ended, based on the configured [stop strategies](/api-reference/server/utilities/turn-management/user-turn-strategies#stop-strategies). This event includes the complete user transcript for the turn.

```python theme={null}
@user_aggregator.event_handler("on_user_turn_stopped")
async def on_user_turn_stopped(aggregator, strategy, message: UserTurnStoppedMessage):
    print(f"User said: {message.content}")
    print(f"Turn started at: {message.timestamp}")
    if message.user_id:
        print(f"User ID: {message.user_id}")
```

**Parameters:**

| Parameter    | Type                       | Description                                 |
| ------------ | -------------------------- | ------------------------------------------- |
| `aggregator` | `LLMUserAggregator`        | The user aggregator instance                |
| `strategy`   | `BaseUserTurnStopStrategy` | The strategy that triggered the turn stop   |
| `message`    | `UserTurnStoppedMessage`   | Contains the user's transcript and metadata |

### on\_user\_turn\_stop\_timeout

Fired when a user turn times out without any stop strategy triggering. This is a fallback mechanism that ends the turn after a configurable timeout period (default: 5.0 seconds) when the user has stopped speaking according to VAD but no transcription-based stop has occurred. Commonly, this event is used to retrigger the LLM response after the user has stopped speaking.

```python theme={null}
@user_aggregator.event_handler("on_user_turn_stop_timeout")
async def on_user_turn_stop_timeout(aggregator):
    message = {
        "role": "developer",
        "content": "Continue.",
    }
    await user_aggregator.queue_frame(LLMMessagesAppendFrame([message], run_llm=True))
```

**Parameters:**

| Parameter    | Type                | Description                  |
| ------------ | ------------------- | ---------------------------- |
| `aggregator` | `LLMUserAggregator` | The user aggregator instance |

<Note>
  After `on_user_turn_stop_timeout` fires, `on_user_turn_stopped` will also be
  called with the accumulated transcript.
</Note>

### on\_user\_turn\_idle

Fired when the user has been idle (not speaking) for a configured timeout period. This event is useful for re-engaging users who may have stepped away or need a prompt to continue the conversation. The idle timer starts when the bot finishes speaking and is cancelled when the user or bot starts speaking again.

```python theme={null}
@user_aggregator.event_handler("on_user_turn_idle")
async def on_user_turn_idle(aggregator):
    # Re-engage the user with a contextual prompt
    message = {
        "role": "developer",
        "content": "The user has been quiet. Politely and briefly ask if they're still there.",
    }
    await aggregator.push_frame(LLMMessagesAppendFrame([message], run_llm=True))
```

**Parameters:**

| Parameter    | Type                | Description                  |
| ------------ | ------------------- | ---------------------------- |
| `aggregator` | `LLMUserAggregator` | The user aggregator instance |

<Note>
  The idle timer starts when the bot stops speaking and is cancelled when the
  user or bot starts speaking. It is suppressed during function calls and active
  user turns. This event can fire multiple times if the user remains idle.
</Note>

### on\_user\_mute\_started

Fired when user input is muted. See [User Input Muting](/pipecat/fundamentals/user-input-muting) for details on muting.

```python theme={null}
@user_aggregator.event_handler("on_user_mute_started")
async def on_user_mute_started(aggregator):
    print("User input muted")
```

**Parameters:**

| Parameter    | Type                | Description                  |
| ------------ | ------------------- | ---------------------------- |
| `aggregator` | `LLMUserAggregator` | The user aggregator instance |

### on\_user\_mute\_stopped

Fired when user input is unmuted.

```python theme={null}
@user_aggregator.event_handler("on_user_mute_stopped")
async def on_user_mute_stopped(aggregator):
    print("User input unmuted")
```

**Parameters:**

| Parameter    | Type                | Description                  |
| ------------ | ------------------- | ---------------------------- |
| `aggregator` | `LLMUserAggregator` | The user aggregator instance |

## Assistant Turn Events

Assistant turn events are registered on the `assistant_aggregator` from an `LLMContextAggregatorPair`.

### on\_assistant\_turn\_started

Fired when the assistant begins generating a response.

```python theme={null}
@assistant_aggregator.event_handler("on_assistant_turn_started")
async def on_assistant_turn_started(aggregator):
    print("Assistant started responding")
```

**Parameters:**

| Parameter    | Type                     | Description                       |
| ------------ | ------------------------ | --------------------------------- |
| `aggregator` | `LLMAssistantAggregator` | The assistant aggregator instance |

### on\_assistant\_turn\_stopped

Fired when the assistant finishes responding or is interrupted. This event includes the complete assistant transcript for the turn.

```python theme={null}
@assistant_aggregator.event_handler("on_assistant_turn_stopped")
async def on_assistant_turn_stopped(aggregator, message: AssistantTurnStoppedMessage):
    if message.content:
        print(f"Assistant said: {message.content}")
    print(f"Turn started at: {message.timestamp}")
    print(f"Was interrupted: {message.interrupted}")
```

**Parameters:**

| Parameter    | Type                          | Description                                      |
| ------------ | ----------------------------- | ------------------------------------------------ |
| `aggregator` | `LLMAssistantAggregator`      | The assistant aggregator instance                |
| `message`    | `AssistantTurnStoppedMessage` | Contains the assistant's transcript and metadata |

<Note>
  This event fires when the LLM response completes, when the user interrupts, or
  when a user image is appended to context. The event always fires when a turn
  ends, even if `content` is empty (e.g., the turn was interrupted before any
  tokens were generated).
</Note>

### on\_assistant\_thought

Fired when the assistant produces a thought from a reasoning model (e.g., extended thinking in Claude). This event provides visibility into the model's reasoning process.

```python theme={null}
@assistant_aggregator.event_handler("on_assistant_thought")
async def on_assistant_thought(aggregator, message: AssistantThoughtMessage):
    print(f"Assistant thought: {message.content}")
```

**Parameters:**

| Parameter    | Type                      | Description                                |
| ------------ | ------------------------- | ------------------------------------------ |
| `aggregator` | `LLMAssistantAggregator`  | The assistant aggregator instance          |
| `message`    | `AssistantThoughtMessage` | Contains the thought content and timestamp |

### on\_summary\_applied

Fired when context summarization is successfully applied. This event is useful for monitoring context management and logging compression metrics.

```python theme={null}
from pipecat.processors.aggregators.llm_context_summarizer import SummaryAppliedEvent

@assistant_aggregator.event_handler("on_summary_applied")
async def on_summary_applied(aggregator, summarizer, event: SummaryAppliedEvent):
    print(
        f"Context summarized: {event.original_message_count} -> "
        f"{event.new_message_count} messages "
        f"({event.summarized_message_count} summarized, "
        f"{event.preserved_message_count} preserved)"
    )
```

**Parameters:**

| Parameter    | Type                     | Description                                 |
| ------------ | ------------------------ | ------------------------------------------- |
| `aggregator` | `LLMAssistantAggregator` | The assistant aggregator instance           |
| `summarizer` | `LLMContextSummarizer`   | The summarizer that applied the summary     |
| `event`      | `SummaryAppliedEvent`    | Contains summarization metrics and metadata |

<Note>
  This event requires `enable_auto_context_summarization=True` in the
  `LLMAssistantAggregatorParams` or manual triggering via
  `LLMSummarizeContextFrame`. See [Context
  Summarization](/api-reference/server/utilities/context-summarization) for
  details.
</Note>

## Message Types

### UserTurnStoppedMessage

Contains the user's complete transcript when their turn ends.

```python theme={null}
from pipecat.processors.aggregators.llm_response_universal import UserTurnStoppedMessage
```

<ParamField path="content" type="str">
  The complete transcribed text from the user's turn.
</ParamField>

<ParamField path="timestamp" type="str">
  ISO 8601 timestamp indicating when the user turn started.
</ParamField>

<ParamField path="user_id" type="str | None">
  Optional identifier for the user, if available from the transport.
</ParamField>

### AssistantTurnStoppedMessage

Contains the assistant's complete transcript when their turn ends.

```python theme={null}
from pipecat.processors.aggregators.llm_response_universal import AssistantTurnStoppedMessage
```

<ParamField path="content" type="str">
  The complete text content from the assistant's turn. May be empty if the LLM
  returned zero tokens (e.g., turn was interrupted before any tokens were
  received or pushed).
</ParamField>

<ParamField path="interrupted" type="bool">
  Whether the assistant turn was interrupted. True if the turn ended due to user
  interruption or cancellation, False if it completed normally.
</ParamField>

<ParamField path="timestamp" type="str">
  ISO 8601 timestamp indicating when the assistant turn started.
</ParamField>

### AssistantThoughtMessage

Contains an assistant's thought content from reasoning models.

```python theme={null}
from pipecat.processors.aggregators.llm_response_universal import AssistantThoughtMessage
```

<ParamField path="content" type="str">
  The thought/reasoning text from the assistant.
</ParamField>

<ParamField path="timestamp" type="str">
  ISO 8601 timestamp indicating when the thought started.
</ParamField>

## Related

* [Transcriptions](/api-reference/server/utilities/turn-management/transcriptions) - Collect conversation transcripts using turn events
* [User Turn Strategies](/api-reference/server/utilities/turn-management/user-turn-strategies) - Configure turn detection behavior
* [Turn Tracking Observer](/api-reference/server/utilities/observers/turn-tracking-observer) - Track complete turn cycles with timing
