pipecat-roark
A Roark analytics observer for Pipecat. Drop one observer into your pipeline — Roark captures call lifecycle, transcripts, tool calls, and a stereo audio recording. No other code changes required.
- Tested with
pipecat-ai0.0.108 (compatible with>= 0.0.104, < 1) - Python 3.10+
- Runtime-agnostic — same code runs self-hosted and on Pipecat Cloud
Maintained by Roark. File issues at https://github.com/roarkhq/sdk-roark-analytics-python-pipecat/issues.
Contents
- Quick start
- How it works
- Running modes
- Examples
- Advanced
- Troubleshooting
- Configuration reference
- Development
- License
Quick start
1. Install
pip install pipecat-roark
2. Create a Pipecat integration & API key
Every Roark API key is bound to a specific integration — the key only works for the integration it was created under. Before you can send calls from this package, create the integration first:
- In the Roark dashboard, go to Integrations and create a new Pipecat integration.
- Open that integration and generate an API key for it.
- Copy the key (represented as
rk_live_replace_mebelow) — this is the value you'll set asROARK_API_KEYbelow.
Use the key created under the Pipecat integration. A key from a different integration (or an account-level key not bound to one) will be rejected.
3. Configure
Set one env var:
ROARK_API_KEY=rk_live_replace_me
The Roark API key is all you configure — the observer knows its own service endpoints.
ROARK_API_KEYcan also be passed asapi_key=toRoarkObserver.
4. Wire the observer
Inside your Pipecat bot(runner_args) entry point, drop RoarkObserver into
the pipeline's observers=[...] list. Splice the auto-created
roark.audio_processor after transport.output() so it sees the bot's
audio post-TTS:
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat_roark import RoarkObserver
roark = RoarkObserver(
api_key="rk_live_replace_me",
agent_id="support-bot-v3",
runner_args=runner_args,
agent_name="Support Bot v3",
agent_prompt=SYSTEM_PROMPT,
)
pipeline = Pipeline([
transport.input(), stt, context_aggregator.user(), llm, tts,
transport.output(),
roark.audio_processor, # after transport.output() — L=user, R=bot
context_aggregator.assistant(),
])
task = PipelineTask(pipeline, params=PipelineParams(observers=[roark]))
That's it — transcripts, tool calls, and the stereo recording flow to Roark automatically.
How it works
The observer subscribes to Pipecat frames and ships a compact event timeline to Roark:
| Phase | What's captured |
|---|---|
| Pipeline start | call-started POST + recording begins. Agent is lazy-registered on Roark the first time it sees this agent_id. |
| User turns | Final TranscriptionFrames (interim transcriptions ignored). |
| Assistant turns | TTSTextFrame chunks aggregated between BotStoppedSpeakingFrame / InterruptionFrame boundaries. |
| Tool calls | FunctionCallInProgressFrame + FunctionCallResultFrame, paired by toolCallId. |
| Audio | Stereo PCM chunks emitted by AudioBufferProcessor, streamed via presigned upload URLs (POST /v1/integrations/pipecat/chunk-upload-url). |
| Pipeline end | EndFrame / CancelFrame / StopFrame (or aflush()) flushes in-flight turns, drains uploads, and POSTs call-ended. Roark finalizes the recording on its side. |
Transcripts and tool calls are forwarded in Pipecat's native shape — Roark maps them to its internal schema on its side.
Audio capture defaults
The observer always creates a sane-default
AudioBufferProcessor
(stereo, ~256 KB chunks) exposed as roark.audio_processor. The sample rate
is adopted from the pipeline's StartFrame, so it tracks whatever the
transport/provider negotiated — 8 kHz on Twilio/Telnyx, 16/24/48 kHz on
Daily/LiveKit, etc. The rate is forwarded to Roark as the recording sample
rate.
Failure mode
Failures are logged and swallowed — the observer never raises into the pipeline. Your call keeps running even if Roark is unreachable.
Running modes
RoarkObserver is runtime-agnostic — the same observer wiring works
whether your Pipecat agent runs as a self-hosted process or is deployed to
Pipecat Cloud. Write one bot(runner_args)
entry point with Pipecat's
create_transport helper,
and the same file runs in both modes — see examples/bot.py.
Both modes use the same
ROARK_API_KEY— the key created under your Pipecat integration (see Create a Pipecat integration & API key). Only where the key is stored differs: a local.env/ secrets manager self-hosted, deployment secrets on Pipecat Cloud.
| Self-hosted | Pipecat Cloud | |
|---|---|---|
| Entry point | python bot.py → pipecat.runner.run.main() dispatches to bot() |
Platform invokes bot(runner_args) per session |
| Room/token | You provision (Daily REST, pipecat.runner.daily.configure, …) |
Injected via DailyRunnerArguments |
| Env vars | .env / your secrets manager |
pcc secrets set <name> KEY=value … |
| Teardown | EndFrame is reliable |
Sessions can vanish — wire aflush() on disconnect |
| Observer wiring | ← identical → | ← identical → |
Self-hosted
cp .env.example .env
# fill in ROARK_API_KEY
uv sync --all-extras
uv run python examples/bot.py --transport daily # or: --transport webrtc
Pipecat Cloud
Set the same vars as deployment secrets, then deploy:
pcc secrets set roark-secrets \
ROARK_API_KEY=rk_live_replace_me
pcc deploy
pcc agent start <agent-name>
Reference the secrets from your pcc-deploy.toml so the container sees them
as os.environ["ROARK_API_KEY"] (etc.) at runtime.
Examples
Two example files ship with the package:
examples/basic_observer.py— minimal transport-agnostic wiring sketch. Shows whereRoarkObserverandroark.audio_processorslot into aPipeline/PipelineTask. STT / LLM / TTS stages are omitted — copy them into your own pipeline.examples/bot.py— runnable foundational voice assistant (Deepgram STT → OpenAI LLM → Cartesia TTS) withRoarkObserverwired in. Same file runs self-hosted (--transport webrtc/--transport daily) and deploys to Pipecat Cloud unchanged.
cp .env.example .env
# fill in:
# ROARK_API_KEY
# DEEPGRAM_API_KEY, OPENAI_API_KEY, CARTESIA_API_KEY
uv sync --all-extras
uv pip install "pipecat-ai[silero,deepgram,openai,cartesia,webrtc,daily]"
# Local browser via Pipecat's built-in WebRTC (no third-party transport account):
uv run python examples/bot.py --transport webrtc
# Or Daily (see Pipecat runner docs for transport-specific setup):
uv run python examples/bot.py --transport daily
Advanced
Call identity
Pass the runner_args received by your Pipecat bot entry point directly to
the observer. It resolves one call ID internally, in this order:
- SmallWebRTC
runner_args.webrtc_connection.pc_id. - Pipecat Cloud
runner_args.session_id. - A random UUID, generated once for this observer.
The native SmallWebRTC and Pipecat Cloud identifiers let Roark correlate both sides of a supported simulation automatically. Other runner and transport types still ingest normally, but their UUID fallback is local to the observer and does not provide native-ID simulation merging.
The resolved value is available through the read-only
observer.pipecat_call_id property. Applications do not need to generate or
forward an identifier themselves.
Bring your own AudioBufferProcessor
If you need to tune sample rate, channel count, or buffer size, instantiate
AudioBufferProcessor yourself and pass it via audio_buffer_processor=:
from pipecat.processors.audio.audio_buffer_processor import AudioBufferProcessor
audio_buffer = AudioBufferProcessor(sample_rate=16000, num_channels=1, buffer_size=128 * 1024)
pipeline = Pipeline([..., transport.output(), audio_buffer, ...])
RoarkObserver(
api_key="rk_live_replace_me",
agent_id="support-bot-v3",
runner_args=runner_args,
audio_buffer_processor=audio_buffer,
)
Handling WebRTC disconnects
Pipecat's WebRTC transports (notably SmallWebRTC) sometimes tear down
without pushing EndFrame through observers. Call aflush() from the
disconnect handler to guarantee the call is finalized on Roark:
@transport.event_handler("on_client_disconnected")
async def _on_disconnect(_, __):
await roark_observer.aflush(reason="client-disconnected")
aflush() is idempotent — the regular EndFrame path will no-op on the next call.
Correlating with OpenTelemetry tracing
If you also enable Pipecat's OpenTelemetry tracing
(PipelineTask(enable_tracing=True)), use the observer's resolved, read-only
call ID as PipelineTask.conversation_id. Each Roark call can then be looked
up by the same value in your tracing backend:
from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat_roark import RoarkObserver
roark = RoarkObserver(
api_key="rk_live_replace_me",
agent_id="support-bot-v3",
runner_args=runner_args,
)
task = PipelineTask(
pipeline,
params=PipelineParams(observers=[roark]),
enable_tracing=True,
conversation_id=roark.pipecat_call_id,
)
Pipecat sets conversation.id as a span attribute on a root
"conversation" span (and propagates it to every child span). The OTel
traceId itself is auto-generated and unrelated to your call ID; correlation
happens by attribute value. To find the trace for a Roark call, query your
backend by conversation.id = <pipecatCallId> (e.g., Honeycomb:
where conversation.id = "...", Jaeger: tag filter, Datadog:
@conversation.id:...).
Because roark.pipecat_call_id is read-only, the lifecycle payload and tracing
attribute cannot drift after observer construction.
Troubleshooting
Do I need enable_tracing=True on PipelineTask?
No. RoarkObserver captures raw frames — it does not consume OpenTelemetry
spans. The tracing flag is unrelated. If you do enable it and want Roark
calls linked to their traces, see
Correlating with OpenTelemetry tracing.
Calls aren't finalizing on Roark
Some transports (notably SmallWebRTC) tear down without pushing EndFrame
through observers. Wire aflush() into your disconnect handler — see
Handling WebRTC disconnects.
Recording captures user audio only / bot audio only
The AudioBufferProcessor must sit after transport.output() so it sees
the bot's audio post-TTS. If it's placed earlier in the pipeline, the bot
channel will be silent.
Transcripts arrive empty
The observer warns call-ended with empty transcript ... no TranscriptionFrame or TTSTextFrame was observed during the call when nothing was captured.
Usually this means the STT service isn't emitting finalized
TranscriptionFrames, or the pipeline ended before any speech was processed.
Configuration reference
| Parameter | Type | Default | Notes |
|---|---|---|---|
api_key |
str |
— | Required. Roark API key. |
agent_id |
str |
— | Required. Customer-stable agent identifier. |
runner_args |
object |
— | Required. The arguments passed to your Pipecat bot entry point. Used to resolve a native SmallWebRTC or Pipecat Cloud identifier when available. |
agent_name |
str | None |
None |
Display name. |
agent_prompt |
str | None |
None |
System prompt. Persisted as the agent's prompt revision. |
audio_buffer_processor |
AudioBufferProcessor | None |
None |
Power-user override — pass your own AudioBufferProcessor to control sample rate / channels / buffer size. If omitted, the observer creates a default (stereo, ~256 KB chunks; sample rate adopted from the pipeline's StartFrame) accessible via observer.audio_processor. |
pipecat_call_id |
str | None |
None |
Deprecated and ignored. Accepted for compatibility and scheduled for removal in 0.3.0. Pass runner_args instead. |
observer.pipecat_call_id is the read-only resolved call ID. Use it as
PipelineTask(conversation_id=...) when OpenTelemetry tracing is enabled.
Development
uv sync --all-extras
uv run pytest
uv run ruff check .
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pipecat_roark-0.2.0.tar.gz.
File metadata
- Download URL: pipecat_roark-0.2.0.tar.gz
- Upload date:
- Size: 234.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b5593410c7d609e97c017b314f252a3525497a8d32418b96187523ba04490be
|
|
| MD5 |
eb044d90bdfcf21088c9a4b7ca4b7091
|
|
| BLAKE2b-256 |
ec945415529d71b3250da079ee50ee3bfa3bfdb50fa6e37eb95bf5fd60de1a6c
|
Provenance
The following attestation bundles were made for pipecat_roark-0.2.0.tar.gz:
Publisher:
release.yml on roarkhq/sdk-roark-analytics-python-pipecat
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pipecat_roark-0.2.0.tar.gz -
Subject digest:
3b5593410c7d609e97c017b314f252a3525497a8d32418b96187523ba04490be - Sigstore transparency entry: 2339053968
- Sigstore integration time:
-
Permalink:
roarkhq/sdk-roark-analytics-python-pipecat@95a44c24776c47560dc27d3aa621009c53d49b0d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/roarkhq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@95a44c24776c47560dc27d3aa621009c53d49b0d -
Trigger Event:
push
-
Statement type:
File details
Details for the file pipecat_roark-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pipecat_roark-0.2.0-py3-none-any.whl
- Upload date:
- Size: 24.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d01e6966f21c77ad511993a61bd598ed0cf4243a6a062592484d8228611abeb
|
|
| MD5 |
385543ea22a86c482cfbd0d84a378163
|
|
| BLAKE2b-256 |
da9cba40ad81faa5cde428667223b61462675810a3f60ef8b93a0cd449b8272f
|
Provenance
The following attestation bundles were made for pipecat_roark-0.2.0-py3-none-any.whl:
Publisher:
release.yml on roarkhq/sdk-roark-analytics-python-pipecat
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pipecat_roark-0.2.0-py3-none-any.whl -
Subject digest:
0d01e6966f21c77ad511993a61bd598ed0cf4243a6a062592484d8228611abeb - Sigstore transparency entry: 2339053990
- Sigstore integration time:
-
Permalink:
roarkhq/sdk-roark-analytics-python-pipecat@95a44c24776c47560dc27d3aa621009c53d49b0d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/roarkhq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@95a44c24776c47560dc27d3aa621009c53d49b0d -
Trigger Event:
push
-
Statement type: