# Pipecat

> Pipecat is an open-source Python framework for real-time voice and multimodal conversational agents. The pipecat-plugins-munsit package bridges Munsit TTS into Pipecat pipelines, so you can build Arabic voice agents with low-latency streaming audio.

## Install the plugin

You need Python 3.9+ and a [Munsit account](https://app.munsit.com/). Generate a key at [Munsit — API Keys](https://app.munsit.com/en/api-keys) before you start.

```
pip install pipecat-plugins-munsit
```

> Your API key is only shown once. Save it securely — set it as the MUNSIT_API_KEY environment variable rather than hardcoding it.

## Quick start

The service takes your API key and a shared `aiohttp` session. That's the whole handshake — drop the resulting `tts` processor into any Pipecat pipeline.

```
import aiohttp
from pipecat_plugins_faseeh import FaseehTTSService

async with aiohttp.ClientSession() as session:
    tts = FaseehTTSService(
        api_key="your-api-key",
        aiohttp_session=session,
    )
```

## Where it sits in the pipeline

In a typical Pipecat pipeline, data flows through processors in sequence:

```
Microphone → Transport → Munsit STT → LLM → Munsit TTS → Transport → Speaker
```

The user speaks; the transport (e.g. Daily WebRTC) captures audio; **Munsit STT** converts speech to text; the LLM (e.g. OpenAI GPT-4o) generates a response; **Munsit TTS** converts it to Arabic audio — streaming PCM16 at 24 kHz — and the transport plays it back. Pipecat's `TTSService` base class aggregates LLM tokens into complete sentences before calling Munsit: each sentence triggers one HTTP streaming request, and PCM16 chunks are yielded back to the pipeline as they arrive.

## Full example

A complete Daily-transported agent: auto-creates a Daily room, wires Munsit STT → GPT-4o → Munsit TTS, and speaks first when a participant joins.

```
import asyncio
import os

import aiohttp
from dotenv import load_dotenv

from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.frames.frames import EndFrame, LLMMessagesUpdateFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat.processors.aggregators.llm_context import LLMContext
from pipecat.processors.aggregators.llm_response_universal import (
    LLMContextAggregatorPair,
    LLMUserAggregatorParams,
)
from pipecat_plugins_munsit import MunsitSTTService
from pipecat.services.openai.llm import OpenAILLMService
from pipecat.transports.daily.transport import DailyParams, DailyTransport

from pipecat_plugins_faseeh import FaseehTTSService

load_dotenv(override=True)

async def main():
    async with aiohttp.ClientSession() as session:
        # Auto-create a Daily room
        from pipecat.transports.daily.utils import DailyRESTHelper, DailyRoomParams

        daily_helper = DailyRESTHelper(
            daily_api_key=os.getenv("DAILY_API_KEY", ""),
            aiohttp_session=session,
        )
        room = await daily_helper.create_room(DailyRoomParams())
        token = await daily_helper.get_token(room.url)

        transport = DailyTransport(
            room.url,
            token,
            "Munsit Bot",
            DailyParams(
                audio_in_enabled=True,
                audio_out_enabled=True,
                audio_out_sample_rate=48000,
            ),
        )

        stt = MunsitSTTService(api_key=os.getenv("MUNSIT_API_KEY", ""))
        llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY", ""), model="gpt-4o")
        tts = FaseehTTSService(
            api_key=os.getenv("MUNSIT_API_KEY"),
            aiohttp_session=session,
        )

        messages = [
            {
                "role": "system",
                "content": "You are a helpful Arabic-speaking assistant. Respond in Arabic.",
            }
        ]
        context = LLMContext(messages=messages)
        context_aggregator = LLMContextAggregatorPair(
            context,
            user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()),
        )

        pipeline = Pipeline([
            transport.input(),
            stt,
            context_aggregator.user(),
            llm,
            tts,
            transport.output(),
            context_aggregator.assistant(),
        ])

        task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))

        @transport.event_handler("on_first_participant_joined")
        async def on_joined(transport, participant):
            await task.queue_frames([LLMMessagesUpdateFrame(messages, run_llm=True)])

        @transport.event_handler("on_participant_left")
        async def on_left(transport, participant, reason):
            await task.queue_frame(EndFrame())

        runner = PipelineRunner()
        await runner.run(task)

if __name__ == "__main__":
    asyncio.run(main())
```

## Configuration

Constructor parameters on `FaseehTTSService`. To pick a voice, browse the [Voice Library](/text-to-speech/voices), listen, and click **"Copy Voice ID"** next to the one you want.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `api_key` | str | env `MUNSIT_API_KEY` | Munsit API key |
| `voice_id` | str | `ar-hijazi-female-2` | Voice identifier |
| `model` | str | `faseeh-v1-preview` | TTS model |
| `stability` | float | `0.5` | Voice consistency (0.0–1.0) |
| `speed` | float | `1.0` | Speech rate (0.7–1.2) |
| `sample_rate` | int | `48000` | Output PCM rate requested from the API. `48000` is the engine's native rate — no downsampling, no client-side resampling. Match your transport's `audio_out_sample_rate` to it. |
| `base_url` | str | `https://api.munsit.com/api/v1` | API base URL |

You can also change voice, model, speed, or stability mid-conversation by queueing a `TTSUpdateSettingsFrame`:

```
tts = FaseehTTSService(
    api_key="your-api-key",
    voice_id="ar-uae-male-1",  # Paste the copied voice ID here
    aiohttp_session=session,
)
```

```
from pipecat.frames.frames import TTSUpdateSettingsFrame

# Switch voice
await task.queue_frame(TTSUpdateSettingsFrame(settings={"voice_id": "ar-emirati-male-1"}))

# Adjust speed and stability
await task.queue_frame(TTSUpdateSettingsFrame(settings={"speed": 1.1, "stability": 0.8}))

# Switch model
await task.queue_frame(TTSUpdateSettingsFrame(settings={"model": "faseeh-v2"}))
```

## Error handling & troubleshooting

The plugin yields non-fatal `ErrorFrame` objects instead of raising exceptions — your pipeline keeps running even if a single TTS request fails.

| HTTP status | Meaning | Action |
| --- | --- | --- |
| `401` | Invalid API key | Check `MUNSIT_API_KEY` |
| `402` | Insufficient balance | Add credits at [app.munsit.com](https://app.munsit.com/) |
| `404` | Voice or model not found | Check `voice_id` and `model` |
| `429` | Rate limit exceeded | Reduce request frequency |

```
from pipecat.frames.frames import ErrorFrame

@task.event_handler("on_error")
async def on_error(task, error: ErrorFrame):
    logger.error(f"TTS error: {error.error}")
```

**No audio output?** Verify `MUNSIT_API_KEY` is set and valid, that `voice_id` exists in the voice library, and that the pipeline sample rate matches the plugin's (48000 Hz default). **High latency?** The plugin already uses HTTP streaming for lowest latency — check connectivity to `api.munsit.com`, or try a voice with faster generation characteristics. **Import errors?** Make sure both `pipecat-ai` and `pipecat-plugins-munsit` are installed; minimum Pipecat version is **0.0.100**.

## Where next

Explore the voices, or compare frameworks.

- [Browse voices](/text-to-speech/voices) — Every Arabic voice, with a preview and a copyable ID. — `GET /voices`

- [TTS API](/text-to-speech/synthesize) — The synthesis endpoint the plugin streams from. — `POST /text-to-speech`

- [LiveKit](/integrations/livekit) — The same Munsit STT and TTS in LiveKit Agents. — `livekit-plugins-munsit`

- [Errors](/errors) — The full error catalogue behind those status codes. — `guide`
