Kenpath Labs

Pipecat

Use Svara as the TTS service in a Pipecat pipeline.

Overview

Pipecat builds voice agents as a pipeline of frame processors. Svara plugs in as the TTS service through the official Python SDK: from svara.pipecat import SvaraTTSService. Everything else — transport, STT, LLM, VAD, turn-taking — is stock.

transport.input() ─▶ STT ─▶ user aggregator ─▶ LLM
Svara TTS ◀────────────┘
└─▶ transport.output() ─▶ assistant aggregator

The service streams 24 kHz, 16-bit, mono PCM by default. Set the transport’s audio_out_sample_rate to match and nothing resamples on the hot path; for telephony transports construct it with response_format="ulaw", sample_rate=8000 and the G.711 bytes drop straight onto the call leg.

The TTS service

Pipecat’s base class aggregates the LLM output into sentences and calls the service once per sentence; each sentence streams back over HTTP as it synthesizes, with TTFB metrics wired in.

pip install "svara-voice[pipecat] @ git+https://github.com/kenpath-labs/svara-python.git"
from svara.pipecat import SvaraTTSService
tts = SvaraTTSService(voice="sv_enhdbrj5") # Aanya; reads SVARA_API_KEY
# telephony: 8 kHz G.711 µ-law, no resampling anywhere
# tts = SvaraTTSService(voice="sv_enhdbrj5", response_format="ulaw", sample_rate=8000)
The integration is beta — validate against your pinned pipecat-ai version. The service’s source is one small file (svara/pipecat/__init__.py) if you need to adapt it.

Wiring the pipeline

Put the service where the TTS goes. Nothing else here is Svara-specific; swap STT or LLM for any other Pipecat service.

pip install 'pipecat-ai[openai,silero,webrtc]' pipecat-ai-prebuilt
import os
from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.frames.frames import LLMRunFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.worker import PipelineWorker
from pipecat.processors.aggregators.llm_context import LLMContext
from pipecat.processors.aggregators.llm_response_universal import (
LLMContextAggregatorPair, LLMUserAggregatorParams,
)
from pipecat.runner.types import RunnerArguments
from pipecat.runner.utils import create_transport
from pipecat.services.openai.llm import OpenAILLMService
from pipecat.services.openai.stt import OpenAISTTService
from pipecat.transports.base_transport import TransportParams
from pipecat.workers.runner import WorkerRunner
from svara.pipecat import SvaraTTSService
SAMPLE_RATE = 24000
async def bot(runner_args: RunnerArguments) -> None:
transport = await create_transport(runner_args, {
"webrtc": lambda: TransportParams(
audio_in_enabled=True,
audio_out_enabled=True,
audio_out_sample_rate=SAMPLE_RATE, # match Svara; no resampling
),
})
context = LLMContext([{
"role": "system",
"content": "You are a helpful voice assistant. "
"Reply in one or two short sentences.",
}])
aggregators = LLMContextAggregatorPair(
context,
# In Pipecat 1.6 VAD and turn-taking live on the user aggregator,
# NOT on the transport.
user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()),
)
task = PipelineWorker(Pipeline([
transport.input(),
OpenAISTTService(api_key=os.environ["OPENAI_API_KEY"]),
aggregators.user(),
OpenAILLMService(api_key=os.environ["OPENAI_API_KEY"]),
SvaraTTSService(voice="sv_enhdbrj5"), # any id from GET /v1/voices
transport.output(),
aggregators.assistant(),
]))
@transport.event_handler("on_client_connected")
async def _(transport, client):
context.add_message({"role": "system",
"content": "Greet the caller in one sentence."})
await task.queue_frames([LLMRunFrame()])
await WorkerRunner(handle_sigint=False).run(task)
if __name__ == "__main__":
from pipecat.runner.run import main
main()
ParameterTypeDefaultDescription
SVARA_API_KEYenv-Your Svara key (the SDK reads it automatically).
SVARA_BASE_URLenvhttps://api.kenpathlabs.comThe gateway origin; override for a regional pin (api-in, api-us, api-eu).
OPENAI_API_KEYenv-For the stock STT and LLM services.

python bot.py starts the dev runner and serves a prebuilt WebRTC client at /client — no media account needed.

Token streaming

The service above receives whole sentences — Pipecat’s default text aggregation. To speak while the LLM is still writing, the underlying SDK client exposes the input-streaming WebSocket directly: client.speech.stream_input(token_stream, …) takes any async iterator of text deltas and yields PCM as the model speaks. Pair it with TextAggregationMode.TOKEN and append_to_audio_context() in a custom processor; a first-class eager mode for SvaraTTSService is on the SDK roadmap.

Gotchas

  • Use the gateway host. https://api.kenpathlabs.com serves the API; the console host does not.
  • VAD is not a transport settingin Pipecat 1.6. It moved to LLMUserAggregatorParams(vad_analyzer=…), along with user_turn_strategies. Passing it to TransportParams fails validation.
  • PipelineTask and PipelineRunner are deprecated in favour of PipelineWorker and WorkerRunner. They are drop-in.
  • The prebuilt UI package is pipecat-ai-prebuilt. Install the wrong one and the server starts, prints “Bot ready”, and serves 404 at /client.
  • Errors surface as ErrorFrames with Svara’s status inside (rate_limit_exceeded, too_many_concurrent_requests, insufficient_quota) — three different situations that call for three different responses.
  • Leave the sampling knobs alone. The server fills serving-tuned defaults; per-bot overrides are how long utterances start to ramble.