> ## Documentation Index
> Fetch the complete documentation index at: https://daily-main.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Detecting Idle Users

> Learn how to detect and respond when users are inactive in conversations

## Overview

In conversational applications, it's important to handle situations where users go silent or inactive. Pipecat provides built-in idle detection through `LLMUserAggregator` and `UserTurnProcessor`, allowing your bot to respond appropriately when users haven't spoken for a defined period.

## How It Works

Idle detection monitors user activity and:

1. Starts a timer when the bot finishes speaking (`BotStoppedSpeakingFrame`)
2. Cancels the timer when the user or bot starts speaking
3. Suppresses the timer during function calls and active user turns (to avoid false triggers during interruptions)
4. Emits an `on_user_turn_idle` event when the timer expires
5. Allows you to implement escalating responses or gracefully end the conversation in your application code

## Basic Implementation

### Step 1: Enable Idle Detection

Enable idle detection by setting the `user_idle_timeout` parameter when creating your aggregator:

```python theme={null}
from pipecat.processors.aggregators.llm_response_universal import (
    LLMContextAggregatorPair,
    LLMUserAggregatorParams,
)

user_aggregator, assistant_aggregator = LLMContextAggregatorPair(
    context,
    user_params=LLMUserAggregatorParams(
        user_idle_timeout=5.0,  # Detect idle after 5 seconds
    ),
)
```

### Step 2: Handle Idle Events

Create an event handler to respond when the user becomes idle:

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

### Step 3: Implement Retry Logic (Optional)

For escalating responses, track retry count in your application:

```python theme={null}
class IdleHandler:
    def __init__(self):
        self._retry_count = 0

    def reset(self):
        self._retry_count = 0

    async def handle_idle(self, aggregator):
        self._retry_count += 1

        if self._retry_count == 1:
            # First attempt - gentle reminder
            message = {
                "role": "developer",
                "content": "The user has been quiet. Politely ask if they're still there.",
            }
            await aggregator.push_frame(LLMMessagesAppendFrame([message], run_llm=True))
        elif self._retry_count == 2:
            # Second attempt - more direct
            message = {
                "role": "developer",
                "content": "The user is still inactive. Ask if they'd like to continue.",
            }
            await aggregator.push_frame(LLMMessagesAppendFrame([message], run_llm=True))
        else:
            # Third attempt - end conversation
            await aggregator.push_frame(
                TTSSpeakFrame("It seems like you're busy. Have a nice day!")
            )
            await aggregator.push_frame(EndWorkerFrame(), FrameDirection.UPSTREAM)

# Use the handler
idle_handler = IdleHandler()

@user_aggregator.event_handler("on_user_turn_idle")
async def on_user_turn_idle(aggregator):
    await idle_handler.handle_idle(aggregator)

@user_aggregator.event_handler("on_user_turn_started")
async def on_user_turn_started(aggregator, strategy):
    idle_handler.reset()  # Reset retry count when user speaks
```

## Updating Timeout at Runtime

You can enable, disable, or change the idle timeout at runtime by pushing a `UserIdleTimeoutUpdateFrame`:

```python theme={null}
from pipecat.frames.frames import UserIdleTimeoutUpdateFrame

# Enable idle detection (or change timeout)
await worker.queue_frame(UserIdleTimeoutUpdateFrame(timeout=10.0))

# Disable idle detection
await worker.queue_frame(UserIdleTimeoutUpdateFrame(timeout=0))
```

**Immediate application**: Timeout updates take effect immediately. If an idle timer is currently running, it restarts with the new duration. If the bot is waiting for the user to speak and you enable a positive timeout (e.g., upgrading from `0` to `10.0`), the timer arms right away without waiting for the next bot turn.

This is useful when you want to enable idle detection only at certain points in the conversation, or adjust the timeout based on context.

## Best Practices

* **Set appropriate timeouts**: Shorter timeouts (5-10 seconds) work well for voice conversations
* **Use escalating responses**: Start with gentle reminders and gradually become more direct
* **Limit retry attempts**: After 2-3 unsuccessful attempts, consider [ending the conversation](/pipecat/learn/pipeline-termination) gracefully by pushing an `EndWorkerFrame`
* **Reset on user activity**: Use the `on_user_turn_started` event to reset your retry counter when the user speaks
* **Let the LLM respond naturally**: Use developer messages to prompt the LLM rather than hardcoded TTS responses for more natural interactions

## Next Steps

<CardGroup cols={2}>
  <Card title="Try the User Idle Example" icon="code" iconType="duotone" href="https://github.com/pipecat-ai/pipecat/blob/main/examples/turn-management/turn-management-detect-user-idle.py">
    Explore a complete working example that demonstrates how to detect and
    respond to user inactivity in Pipecat.
  </Card>

  <Card title="Turn Events" icon="book" iconType="duotone" href="/api-reference/server/utilities/turn-management/turn-events">
    Learn about all available turn events and their parameters.
  </Card>
</CardGroup>

Implementing idle user detection improves the conversational experience by ensuring your bot can handle periods of user inactivity gracefully, either by prompting for re-engagement or politely ending the conversation when appropriate.
