> ## 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.

# RTVI Observer

> Converting pipeline frames to RTVI protocol messages

The `RTVIObserver` translates Pipecat's internal pipeline events into standardized RTVI protocol messages. It monitors frame flow through the pipeline and generates corresponding client messages based on event types.

## Purpose

The `RTVIObserver` primarily serves to convert internal pipeline frames into to client-compatible RTVI messages. It is required for any application using RTVI as the client protocol to ensure proper communication of events such as speech start/stop, user transcript, bot output, metrics, and server messages.

## Automatic Setup

`RTVIObserver` is automatically created and attached when you create a `PipelineTask`. No manual setup is required for standard usage.

To customize the observer's behavior, pass `RTVIObserverParams` to the task:

```python theme={null}
from pipecat.processors.frameworks.rtvi import RTVIObserverParams

task = PipelineTask(
    pipeline,
    rtvi_observer_params=RTVIObserverParams(
        bot_llm_enabled=False,
        metrics_enabled=False,
    ),
)
```

## Configuration

`RTVIObserverParams` accepts the following fields:

<ParamField path="bot_output_enabled" default="True">
  Indicates if bot output messages should be sent.
</ParamField>

<ParamField path="bot_llm_enabled" default="True">
  Indicates if the bot's LLM messages should be sent.
</ParamField>

<ParamField path="bot_tts_enabled" default="True">
  Indicates if the bot's TTS messages should be sent.
</ParamField>

<ParamField path="bot_speaking_enabled" default="True">
  Indicates if the bot's started/stopped speaking messages should be sent.
</ParamField>

<ParamField path="bot_audio_level_enabled" default="False">
  Indicates if bot's audio level messages should be sent.
</ParamField>

<ParamField path="user_llm_enabled" default="True">
  Indicates if the user's LLM input messages should be sent.
</ParamField>

<ParamField path="user_speaking_enabled" default="True">
  Indicates if the user's started/stopped speaking messages should be sent.
</ParamField>

<ParamField path="user_mute_enabled" default="True">
  Indicates if user mute started/stopped messages (`user-mute-started`,
  `user-mute-stopped`) should be sent.
</ParamField>

<ParamField path="user_transcription_enabled" default="True">
  Indicates if user's transcription messages should be sent.
</ParamField>

<ParamField path="user_audio_level_enabled" default="False">
  Indicates if user's audio level messages should be sent.
</ParamField>

<ParamField path="metrics_enabled" default="True">
  Indicates if metrics messages should be sent.
</ParamField>

<ParamField path="system_logs_enabled" default="False">
  Indicates if system logs should be sent.
</ParamField>

<ParamField path="skip_aggregator_types" default="None">
  List of aggregation types to skip sending as tts/output messages.

  <Note>
    {" "}

    If using this to avoid sending secure information, be sure to also disable
    bot\_llm\_enabled to avoid leaking through LLM messages.
  </Note>
</ParamField>

<ParamField path="bot_output_transforms" default="None">
  A list of tuples to transform text just before sending it to TTS. Each tuple should be of the form `(aggregation_type, transform_function)`, where `aggregation_type` is a string (or `'*'` for all types), and `transform_function` is a callable that takes `(text, aggregation_type)` and returns the transformed text.

  **Example:**

  ```python theme={null}
  def redact_sensitive(text, agg_type):
      # Example: redact numbers
      import re
      return re.sub(r"\d+", "[REDACTED]", text)

  bot_output_transforms = [
      ("credit_card", redact_sensitive),  # Only for 'credit_card' type
      ("*", lambda text, agg_type: text.upper()),  # For all types, make uppercase
  ]

  observer = RTVIObserver(
      rtvi,
      params=RTVIObserverParams(bot_output_transforms=bot_output_transforms),
  )
  ```
</ParamField>

<ParamField path="audio_level_period_secs" default="0.15">
  How often audio levels should be sent if enabled.
</ParamField>

<ParamField path="function_call_report_level" type="Dict[str, RTVIFunctionCallReportLevel]" default="{&#x22;*&#x22;: RTVIFunctionCallReportLevel.NONE}">
  Controls what information is exposed in function call lifecycle events
  (`llm-function-call-started`, `llm-function-call-in-progress`,
  `llm-function-call-stopped`). Maps function names to security levels, where
  `"*"` sets the default for unlisted functions.

  **Levels:**

  * `DISABLED`: No events emitted for this function
  * `NONE`: Events with `tool_call_id` only (most secure when events are needed)
  * `NAME`: Adds function name to events
  * `FULL`: Adds function name, arguments, and results

  ```python theme={null}
  from pipecat.processors.frameworks.rtvi import (
      RTVIFunctionCallReportLevel,
      RTVIObserverParams,
  )

  task = PipelineTask(
      pipeline,
      rtvi_observer_params=RTVIObserverParams(
          function_call_report_level={
              "*": RTVIFunctionCallReportLevel.NONE,
              "get_weather": RTVIFunctionCallReportLevel.FULL,
          },
      ),
  )
  ```
</ParamField>

## Frame Translation

The observer maps Pipecat's internal frames to RTVI protocol messages:

| Pipeline Frame                | RTVI Message                                |
| ----------------------------- | ------------------------------------------- |
| **Speech Events**             |                                             |
| `UserStartedSpeakingFrame`    | `RTVIUserStartedSpeakingMessage`            |
| `UserStoppedSpeakingFrame`    | `RTVIUserStoppedSpeakingMessage`            |
| `BotStartedSpeakingFrame`     | `RTVIBotStartedSpeakingMessage`             |
| `BotStoppedSpeakingFrame`     | `RTVIBotStoppedSpeakingMessage`             |
| **User Mute**                 |                                             |
| `UserMuteStartedFrame`        | `RTVIUserMuteStartedMessage`                |
| `UserMuteStoppedFrame`        | `RTVIUserMuteStoppedMessage`                |
| **Transcription**             |                                             |
| `TranscriptionFrame`          | `RTVIUserTranscriptionMessage(final=true)`  |
| `InterimTranscriptionFrame`   | `RTVIUserTranscriptionMessage(final=false)` |
| **Bot Output**                |                                             |
| `AggregatedTextFrame`         | `RTVIBotOutputMessage`                      |
| **LLM Processing**            |                                             |
| `LLMFullResponseStartFrame`   | `RTVIBotLLMStartedMessage`                  |
| `LLMFullResponseEndFrame`     | `RTVIBotLLMStoppedMessage`                  |
| `LLMTextFrame`                | `RTVIBotLLMTextMessage`                     |
| **TTS Events**                |                                             |
| `TTSStartedFrame`             | `RTVIBotTTSStartedMessage`                  |
| `TTSStoppedFrame`             | `RTVIBotTTSStoppedMessage`                  |
| `TTSTextFrame`                | `RTVIBotTTSTextMessage`                     |
| **Function Calls**            |                                             |
| `FunctionCallsStartedFrame`   | `llm-function-call-started`                 |
| `FunctionCallInProgressFrame` | `llm-function-call-in-progress`             |
| `FunctionCallResultFrame`     | `llm-function-call-stopped`                 |
| **Context/Metrics**           |                                             |
| `LLMContextFrame`             | `RTVIUserLLMTextMessage`                    |
| `MetricsFrame`                | `RTVIMetricsMessage`                        |
| `RTVIServerMessageFrame`      | `RTVIServerMessage`                         |
