pipecat-roark 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pipecat_roark-0.1.0/.env.example +16 -0
- pipecat_roark-0.1.0/.github/workflows/ci.yml +31 -0
- pipecat_roark-0.1.0/.github/workflows/release.yml +93 -0
- pipecat_roark-0.1.0/.gitignore +27 -0
- pipecat_roark-0.1.0/CHANGELOG.md +64 -0
- pipecat_roark-0.1.0/LICENSE +21 -0
- pipecat_roark-0.1.0/PKG-INFO +400 -0
- pipecat_roark-0.1.0/README.md +346 -0
- pipecat_roark-0.1.0/examples/basic_observer.py +51 -0
- pipecat_roark-0.1.0/examples/bot.py +182 -0
- pipecat_roark-0.1.0/pyproject.toml +66 -0
- pipecat_roark-0.1.0/src/pipecat_roark/__init__.py +34 -0
- pipecat_roark-0.1.0/src/pipecat_roark/_types.py +86 -0
- pipecat_roark-0.1.0/src/pipecat_roark/client.py +190 -0
- pipecat_roark-0.1.0/src/pipecat_roark/observer.py +555 -0
- pipecat_roark-0.1.0/src/pipecat_roark/py.typed +0 -0
- pipecat_roark-0.1.0/tests/__init__.py +0 -0
- pipecat_roark-0.1.0/tests/test_client.py +124 -0
- pipecat_roark-0.1.0/tests/test_observer.py +465 -0
- pipecat_roark-0.1.0/uv.lock +2705 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# ---------------------------------------------------------------------------
|
|
2
|
+
# Roark — required by RoarkObserver itself
|
|
3
|
+
# ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
# Roark API key — create one on the API keys page in your Roark project.
|
|
6
|
+
# This is the only Roark setting you need; the observer knows its own endpoints.
|
|
7
|
+
ROARK_API_KEY=rk_live_replace_me
|
|
8
|
+
|
|
9
|
+
# ---------------------------------------------------------------------------
|
|
10
|
+
# Services — only needed to run examples/bot.py (not by the library itself).
|
|
11
|
+
# Swap providers freely in your own pipeline; RoarkObserver is provider-agnostic.
|
|
12
|
+
# ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
DEEPGRAM_API_KEY=
|
|
15
|
+
OPENAI_API_KEY=
|
|
16
|
+
CARTESIA_API_KEY=
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
- name: Install
|
|
21
|
+
run: |
|
|
22
|
+
python -m pip install --upgrade pip
|
|
23
|
+
# Tests that import pipecat-ai are guarded with pytest.importorskip,
|
|
24
|
+
# so installing the dev extras alone is enough for CI.
|
|
25
|
+
pip install -e ".[dev]"
|
|
26
|
+
- name: Lint
|
|
27
|
+
run: ruff check .
|
|
28
|
+
- name: Type check
|
|
29
|
+
run: mypy src/pipecat_roark
|
|
30
|
+
- name: Test
|
|
31
|
+
run: pytest -q
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Auto-publish to PyPI on merge to main, but only when the version in
|
|
4
|
+
# pyproject.toml has been bumped. Merging changes that don't touch the
|
|
5
|
+
# version is a no-op (no duplicate-upload failures on PyPI).
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
# 1. Decide whether this push is a release: did the version change?
|
|
15
|
+
check-version:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
outputs:
|
|
18
|
+
changed: ${{ steps.check.outputs.changed }}
|
|
19
|
+
version: ${{ steps.check.outputs.version }}
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
fetch-depth: 0 # need full history so we can see existing tags
|
|
24
|
+
- name: Check whether the version was bumped
|
|
25
|
+
id: check
|
|
26
|
+
run: |
|
|
27
|
+
version=$(grep -m1 -E '^version = ' pyproject.toml | sed -E 's/version = "(.*)"/\1/')
|
|
28
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
29
|
+
if git rev-parse "v$version" >/dev/null 2>&1; then
|
|
30
|
+
echo "Tag v$version already exists — nothing to publish."
|
|
31
|
+
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
32
|
+
else
|
|
33
|
+
echo "New version v$version detected — will publish."
|
|
34
|
+
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# 2. Gate the release on the full test/lint/type-check matrix passing.
|
|
38
|
+
test:
|
|
39
|
+
needs: check-version
|
|
40
|
+
if: needs.check-version.outputs.changed == 'true'
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
strategy:
|
|
43
|
+
fail-fast: false
|
|
44
|
+
matrix:
|
|
45
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
- uses: actions/setup-python@v5
|
|
49
|
+
with:
|
|
50
|
+
python-version: ${{ matrix.python-version }}
|
|
51
|
+
- name: Install
|
|
52
|
+
run: |
|
|
53
|
+
python -m pip install --upgrade pip
|
|
54
|
+
pip install -e ".[dev]"
|
|
55
|
+
- name: Lint
|
|
56
|
+
run: ruff check .
|
|
57
|
+
- name: Type check
|
|
58
|
+
run: mypy src/pipecat_roark
|
|
59
|
+
- name: Test
|
|
60
|
+
run: pytest -q
|
|
61
|
+
|
|
62
|
+
# 3. Build, publish to PyPI via Trusted Publishing, then tag + release on GitHub.
|
|
63
|
+
publish:
|
|
64
|
+
needs: [check-version, test]
|
|
65
|
+
if: needs.check-version.outputs.changed == 'true'
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
permissions:
|
|
68
|
+
# PyPI Trusted Publishing — no API token in repo secrets.
|
|
69
|
+
# See https://docs.pypi.org/trusted-publishers/
|
|
70
|
+
id-token: write
|
|
71
|
+
# Needed to push the version tag and create the GitHub release.
|
|
72
|
+
contents: write
|
|
73
|
+
steps:
|
|
74
|
+
- uses: actions/checkout@v4
|
|
75
|
+
- uses: actions/setup-python@v5
|
|
76
|
+
with:
|
|
77
|
+
python-version: "3.12"
|
|
78
|
+
- name: Build
|
|
79
|
+
run: |
|
|
80
|
+
python -m pip install --upgrade pip build
|
|
81
|
+
python -m build
|
|
82
|
+
- name: Publish to PyPI
|
|
83
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
84
|
+
- name: Tag and create GitHub release
|
|
85
|
+
env:
|
|
86
|
+
GH_TOKEN: ${{ github.token }}
|
|
87
|
+
run: |
|
|
88
|
+
version="${{ needs.check-version.outputs.version }}"
|
|
89
|
+
git tag "v$version"
|
|
90
|
+
git push origin "v$version"
|
|
91
|
+
gh release create "v$version" dist/* \
|
|
92
|
+
--title "v$version" \
|
|
93
|
+
--generate-notes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Build / dist
|
|
2
|
+
build/
|
|
3
|
+
dist/
|
|
4
|
+
*.egg-info/
|
|
5
|
+
*.egg
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.pyc
|
|
8
|
+
*.pyo
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
|
|
13
|
+
# Virtualenvs
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
env/
|
|
17
|
+
|
|
18
|
+
# Environment
|
|
19
|
+
.env
|
|
20
|
+
.env.local
|
|
21
|
+
|
|
22
|
+
# IDE
|
|
23
|
+
.vscode/
|
|
24
|
+
.idea/
|
|
25
|
+
|
|
26
|
+
# OS
|
|
27
|
+
.DS_Store
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `pipecat-roark` are documented here.
|
|
4
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
5
|
+
this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **BREAKING:** Roark service endpoints are now built into the client. The
|
|
12
|
+
`ROARK_WEBHOOK_URL` and `ROARK_CHUNK_UPLOAD_URL_ENDPOINT` env vars are no
|
|
13
|
+
longer read or required — `ROARK_API_KEY` (or `api_key=`) is the only Roark
|
|
14
|
+
configuration. Remove those two vars from your environment / deployment
|
|
15
|
+
secrets; they are now ignored.
|
|
16
|
+
|
|
17
|
+
## [0.1.0] - 2026-05-22
|
|
18
|
+
|
|
19
|
+
Initial public release. Drop-in `RoarkObserver` for Pipecat that ships call
|
|
20
|
+
lifecycle, transcripts, tool calls, and audio recordings to Roark. Compatible
|
|
21
|
+
with `pipecat-ai >= 0.0.40, < 1`; tested with `pipecat-ai` 0.0.108. Prepared
|
|
22
|
+
for submission to the
|
|
23
|
+
[Pipecat community-integrations](https://github.com/pipecat-ai/pipecat/blob/main/COMMUNITY_INTEGRATIONS.md)
|
|
24
|
+
listing.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- `RoarkObserver` (`BaseObserver` subclass) capturing call lifecycle from
|
|
29
|
+
native Pipecat frames — `TranscriptionFrame`, `TTSTextFrame`,
|
|
30
|
+
`BotStoppedSpeakingFrame`, `InterruptionFrame`, `FunctionCallInProgressFrame`,
|
|
31
|
+
`FunctionCallResultFrame`, `EndFrame`, `CancelFrame`, `StopFrame`.
|
|
32
|
+
- Default `AudioBufferProcessor` auto-created with stereo channels and
|
|
33
|
+
~256 KB chunks. Sample rate is adopted from the pipeline's `StartFrame` so
|
|
34
|
+
it tracks whatever the transport negotiated (8 kHz Twilio/Telnyx,
|
|
35
|
+
16/24/48 kHz Daily/LiveKit). Exposed as `observer.audio_processor` for
|
|
36
|
+
power users; pass `audio_buffer_processor=` to override.
|
|
37
|
+
- Chunked audio upload via presigned S3 URLs requested per chunk from the
|
|
38
|
+
Roark chunk-upload endpoint; in-flight uploads are drained before
|
|
39
|
+
`call-ended` is posted.
|
|
40
|
+
- Frame deduplication by frame id (Pipecat invokes `on_push_frame` once per
|
|
41
|
+
processor-to-processor hop; without dedupe the same transcription would
|
|
42
|
+
repeat N times).
|
|
43
|
+
- `aflush(reason=...)` idempotent escape hatch for WebRTC transports that
|
|
44
|
+
tear down without pushing `EndFrame` (notably `SmallWebRTC`).
|
|
45
|
+
- OpenTelemetry correlation via shared `pipecat_call_id` ↔
|
|
46
|
+
`PipelineTask.conversation_id`.
|
|
47
|
+
- `examples/basic_observer.py` — minimal transport-agnostic wiring sketch.
|
|
48
|
+
- `examples/bot.py` — runnable foundational voice assistant
|
|
49
|
+
(Deepgram STT → OpenAI LLM → Cartesia TTS) using the canonical Pipecat
|
|
50
|
+
0.0.108 runner pattern (`LLMContext` + `LLMContextAggregatorPair`,
|
|
51
|
+
`create_transport`, `on_client_connected` / `on_client_disconnected`).
|
|
52
|
+
Same file runs self-hosted (`--transport webrtc` / `--transport daily`)
|
|
53
|
+
and deploys to Pipecat Cloud unchanged.
|
|
54
|
+
|
|
55
|
+
### Configuration
|
|
56
|
+
|
|
57
|
+
- `ROARK_WEBHOOK_URL` and `ROARK_CHUNK_UPLOAD_URL_ENDPOINT` env vars are
|
|
58
|
+
required at construction time — read directly from the environment, no
|
|
59
|
+
kwarg overrides.
|
|
60
|
+
- `api_key`, `agent_id` are required; `agent_name`, `agent_prompt`,
|
|
61
|
+
`pipecat_call_id`, `audio_buffer_processor` are optional.
|
|
62
|
+
|
|
63
|
+
[Unreleased]: https://github.com/roarkhq/pipecat-roark/compare/v0.1.0...HEAD
|
|
64
|
+
[0.1.0]: https://github.com/roarkhq/pipecat-roark/releases/tag/v0.1.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Roark, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pipecat-roark
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Roark analytics observer for Pipecat — capture call lifecycle, transcripts, tool calls, and recordings from any Pipecat pipeline.
|
|
5
|
+
Project-URL: Homepage, https://roark.ai
|
|
6
|
+
Project-URL: Documentation, https://docs.roark.ai/integrations/pipecat
|
|
7
|
+
Project-URL: Repository, https://github.com/roarkhq/pipecat-roark
|
|
8
|
+
Project-URL: Issues, https://github.com/roarkhq/pipecat-roark/issues
|
|
9
|
+
Author-email: Roark <support@roark.ai>
|
|
10
|
+
License: MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2026 Roark, Inc.
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Keywords: analytics,observability,pipecat,roark,voice-ai
|
|
33
|
+
Classifier: Development Status :: 4 - Beta
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
41
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
42
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
43
|
+
Requires-Python: >=3.10
|
|
44
|
+
Requires-Dist: httpx<1,>=0.27
|
|
45
|
+
Requires-Dist: pipecat-ai<1,>=0.0.40
|
|
46
|
+
Provides-Extra: dev
|
|
47
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
48
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
49
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
50
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
51
|
+
Provides-Extra: examples
|
|
52
|
+
Requires-Dist: python-dotenv>=1.0; extra == 'examples'
|
|
53
|
+
Description-Content-Type: text/markdown
|
|
54
|
+
|
|
55
|
+
# pipecat-roark
|
|
56
|
+
|
|
57
|
+
A [Roark](https://roark.ai) analytics observer for
|
|
58
|
+
[Pipecat](https://github.com/pipecat-ai/pipecat). Drop one observer into your
|
|
59
|
+
pipeline — Roark captures call lifecycle, transcripts, tool calls, and a
|
|
60
|
+
stereo audio recording. No other code changes required.
|
|
61
|
+
|
|
62
|
+
- **Tested with** `pipecat-ai` 0.0.108 (compatible with `>= 0.0.40, < 1`)
|
|
63
|
+
- **Python** 3.10+
|
|
64
|
+
- **Runtime-agnostic** — same code runs self-hosted *and* on Pipecat Cloud
|
|
65
|
+
|
|
66
|
+
> Maintained by [Roark](https://roark.ai). File issues at
|
|
67
|
+
> <https://github.com/roarkhq/pipecat-roark/issues>.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Contents
|
|
72
|
+
|
|
73
|
+
- [Quick start](#quick-start)
|
|
74
|
+
- [How it works](#how-it-works)
|
|
75
|
+
- [Running modes](#running-modes)
|
|
76
|
+
- [Examples](#examples)
|
|
77
|
+
- [Advanced](#advanced)
|
|
78
|
+
- [Bring your own `AudioBufferProcessor`](#bring-your-own-audiobufferprocessor)
|
|
79
|
+
- [Handling WebRTC disconnects](#handling-webrtc-disconnects)
|
|
80
|
+
- [Correlating with OpenTelemetry tracing](#correlating-with-opentelemetry-tracing)
|
|
81
|
+
- [Troubleshooting](#troubleshooting)
|
|
82
|
+
- [Configuration reference](#configuration-reference)
|
|
83
|
+
- [Development](#development)
|
|
84
|
+
- [License](#license)
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Quick start
|
|
89
|
+
|
|
90
|
+
### 1. Install
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pip install pipecat-roark
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 2. Configure
|
|
97
|
+
|
|
98
|
+
Set one env var:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
ROARK_API_KEY=rk_live_...
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
> The Roark API key is all you configure — the observer knows its own service
|
|
105
|
+
> endpoints. `ROARK_API_KEY` can also be passed as `api_key=` to `RoarkObserver`.
|
|
106
|
+
|
|
107
|
+
### 3. Wire the observer
|
|
108
|
+
|
|
109
|
+
Drop `RoarkObserver` into your pipeline's `observers=[...]` list. Splice the
|
|
110
|
+
auto-created `roark.audio_processor` **after `transport.output()`** so it sees
|
|
111
|
+
the bot's audio post-TTS:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from pipecat.pipeline.pipeline import Pipeline
|
|
115
|
+
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|
116
|
+
from pipecat_roark import RoarkObserver
|
|
117
|
+
|
|
118
|
+
roark = RoarkObserver(
|
|
119
|
+
api_key="rk_live_...",
|
|
120
|
+
agent_id="support-bot-v3",
|
|
121
|
+
agent_name="Support Bot v3",
|
|
122
|
+
agent_prompt=SYSTEM_PROMPT,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
pipeline = Pipeline([
|
|
126
|
+
transport.input(), stt, context_aggregator.user(), llm, tts,
|
|
127
|
+
transport.output(),
|
|
128
|
+
roark.audio_processor, # after transport.output() — L=user, R=bot
|
|
129
|
+
context_aggregator.assistant(),
|
|
130
|
+
])
|
|
131
|
+
|
|
132
|
+
task = PipelineTask(pipeline, params=PipelineParams(observers=[roark]))
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
That's it — transcripts, tool calls, and the stereo recording flow to Roark
|
|
136
|
+
automatically.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## How it works
|
|
141
|
+
|
|
142
|
+
The observer subscribes to Pipecat frames and ships a compact event timeline
|
|
143
|
+
to Roark:
|
|
144
|
+
|
|
145
|
+
| Phase | What's captured |
|
|
146
|
+
|---|---|
|
|
147
|
+
| **Pipeline start** | `call-started` POST + recording begins. Agent is lazy-registered on Roark the first time it sees this `agent_id`. |
|
|
148
|
+
| **User turns** | Final `TranscriptionFrame`s (interim transcriptions ignored). |
|
|
149
|
+
| **Assistant turns** | `TTSTextFrame` chunks aggregated between `BotStoppedSpeakingFrame` / `InterruptionFrame` boundaries. |
|
|
150
|
+
| **Tool calls** | `FunctionCallInProgressFrame` + `FunctionCallResultFrame`, paired by `toolCallId`. |
|
|
151
|
+
| **Audio** | Stereo PCM chunks emitted by `AudioBufferProcessor`, streamed via presigned upload URLs (`POST /v1/integrations/pipecat/chunk-upload-url`). |
|
|
152
|
+
| **Pipeline end** | `EndFrame` / `CancelFrame` / `StopFrame` (or `aflush()`) flushes in-flight turns, drains uploads, and POSTs `call-ended`. Roark finalizes the recording on its side. |
|
|
153
|
+
|
|
154
|
+
Transcripts and tool calls are forwarded in Pipecat's native shape — Roark
|
|
155
|
+
maps them to its internal schema on its side.
|
|
156
|
+
|
|
157
|
+
### Audio capture defaults
|
|
158
|
+
|
|
159
|
+
The observer always creates a sane-default
|
|
160
|
+
[`AudioBufferProcessor`](https://docs.pipecat.ai/server/utilities/audio/audio-recording)
|
|
161
|
+
(stereo, ~256 KB chunks) exposed as `roark.audio_processor`. The sample rate
|
|
162
|
+
is **adopted from the pipeline's `StartFrame`**, so it tracks whatever the
|
|
163
|
+
transport/provider negotiated — 8 kHz on Twilio/Telnyx, 16/24/48 kHz on
|
|
164
|
+
Daily/LiveKit, etc. The rate is forwarded to Roark as the recording sample
|
|
165
|
+
rate.
|
|
166
|
+
|
|
167
|
+
### Failure mode
|
|
168
|
+
|
|
169
|
+
Failures are logged and swallowed — **the observer never raises into the
|
|
170
|
+
pipeline**. Your call keeps running even if Roark is unreachable.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Running modes
|
|
175
|
+
|
|
176
|
+
`RoarkObserver` is **runtime-agnostic** — the same observer wiring works
|
|
177
|
+
whether your Pipecat agent runs as a self-hosted process or is deployed to
|
|
178
|
+
[Pipecat Cloud](https://docs.pipecat.daily.co/). Write one `bot(runner_args)`
|
|
179
|
+
entry point with Pipecat's
|
|
180
|
+
[`create_transport`](https://docs.pipecat.ai/server/utilities/runner) helper,
|
|
181
|
+
and the same file runs in both modes — see `examples/bot.py`.
|
|
182
|
+
|
|
183
|
+
| | Self-hosted | Pipecat Cloud |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| Entry point | `python bot.py` → `pipecat.runner.run.main()` dispatches to `bot()` | Platform invokes `bot(runner_args)` per session |
|
|
186
|
+
| Room/token | You provision (Daily REST, `pipecat.runner.daily.configure`, …) | Injected via `DailyRunnerArguments` |
|
|
187
|
+
| Env vars | `.env` / your secrets manager | `pcc secrets set <name> KEY=value …` |
|
|
188
|
+
| Teardown | `EndFrame` is reliable | Sessions can vanish — wire [`aflush()` on disconnect](#handling-webrtc-disconnects) |
|
|
189
|
+
| Observer wiring | ← identical → | ← identical → |
|
|
190
|
+
|
|
191
|
+
### Self-hosted
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
cp .env.example .env
|
|
195
|
+
# fill in ROARK_API_KEY
|
|
196
|
+
uv sync --all-extras
|
|
197
|
+
uv run python examples/bot.py --transport daily # or: --transport webrtc
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Pipecat Cloud
|
|
201
|
+
|
|
202
|
+
Set the same vars as deployment secrets, then deploy:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
pcc secrets set roark-secrets \
|
|
206
|
+
ROARK_API_KEY=rk_live_...
|
|
207
|
+
|
|
208
|
+
pcc deploy
|
|
209
|
+
pcc agent start <agent-name>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Reference the secrets from your `pcc-deploy.toml` so the container sees them
|
|
213
|
+
as `os.environ["ROARK_API_KEY"]` (etc.) at runtime.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Examples
|
|
218
|
+
|
|
219
|
+
Two example files ship with the package:
|
|
220
|
+
|
|
221
|
+
- **`examples/basic_observer.py`** — minimal transport-agnostic wiring sketch.
|
|
222
|
+
Shows where `RoarkObserver` and `roark.audio_processor` slot into a
|
|
223
|
+
`Pipeline` / `PipelineTask`. STT / LLM / TTS stages are omitted — copy them
|
|
224
|
+
into your own pipeline.
|
|
225
|
+
- **`examples/bot.py`** — runnable foundational voice assistant
|
|
226
|
+
(Deepgram STT → OpenAI LLM → Cartesia TTS) with `RoarkObserver` wired in.
|
|
227
|
+
Same file runs self-hosted (`--transport webrtc` / `--transport daily`)
|
|
228
|
+
**and** deploys to Pipecat Cloud unchanged.
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
cp .env.example .env
|
|
232
|
+
# fill in:
|
|
233
|
+
# ROARK_API_KEY
|
|
234
|
+
# DEEPGRAM_API_KEY, OPENAI_API_KEY, CARTESIA_API_KEY
|
|
235
|
+
|
|
236
|
+
uv sync --all-extras
|
|
237
|
+
uv pip install "pipecat-ai[silero,deepgram,openai,cartesia,webrtc,daily]"
|
|
238
|
+
|
|
239
|
+
# Local browser via Pipecat's built-in WebRTC (no third-party transport account):
|
|
240
|
+
uv run python examples/bot.py --transport webrtc
|
|
241
|
+
# Or Daily (see Pipecat runner docs for transport-specific setup):
|
|
242
|
+
uv run python examples/bot.py --transport daily
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Advanced
|
|
248
|
+
|
|
249
|
+
### Bring your own `AudioBufferProcessor`
|
|
250
|
+
|
|
251
|
+
If you need to tune sample rate, channel count, or buffer size, instantiate
|
|
252
|
+
`AudioBufferProcessor` yourself and pass it via `audio_buffer_processor=`:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from pipecat.processors.audio.audio_buffer_processor import AudioBufferProcessor
|
|
256
|
+
|
|
257
|
+
audio_buffer = AudioBufferProcessor(sample_rate=16000, num_channels=1, buffer_size=128 * 1024)
|
|
258
|
+
|
|
259
|
+
pipeline = Pipeline([..., transport.output(), audio_buffer, ...])
|
|
260
|
+
|
|
261
|
+
RoarkObserver(
|
|
262
|
+
api_key="rk_live_...",
|
|
263
|
+
agent_id="support-bot-v3",
|
|
264
|
+
audio_buffer_processor=audio_buffer,
|
|
265
|
+
)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Handling WebRTC disconnects
|
|
269
|
+
|
|
270
|
+
Pipecat's WebRTC transports (notably `SmallWebRTC`) sometimes tear down
|
|
271
|
+
without pushing `EndFrame` through observers. Call `aflush()` from the
|
|
272
|
+
disconnect handler to guarantee the call is finalized on Roark:
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
@transport.event_handler("on_client_disconnected")
|
|
276
|
+
async def _on_disconnect(_, __):
|
|
277
|
+
await roark_observer.aflush(reason="client-disconnected")
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
`aflush()` is idempotent — the regular `EndFrame` path will no-op on the next call.
|
|
281
|
+
|
|
282
|
+
### Correlating with OpenTelemetry tracing
|
|
283
|
+
|
|
284
|
+
If you also enable Pipecat's OpenTelemetry tracing
|
|
285
|
+
(`PipelineTask(enable_tracing=True)`), generate **one** call ID up front and
|
|
286
|
+
pass it to both sides — the observer's `pipecat_call_id` and `PipelineTask`'s
|
|
287
|
+
`conversation_id` — so each Roark call can be looked up by the same value in
|
|
288
|
+
your tracing backend:
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
import uuid
|
|
292
|
+
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|
293
|
+
from pipecat_roark import RoarkObserver
|
|
294
|
+
|
|
295
|
+
call_id = str(uuid.uuid4()) # or your own external ID (Twilio CallSid, DB row id, …)
|
|
296
|
+
|
|
297
|
+
roark = RoarkObserver(
|
|
298
|
+
api_key="rk_live_...",
|
|
299
|
+
agent_id="support-bot-v3",
|
|
300
|
+
pipecat_call_id=call_id, # appears on the Roark record as `pipecatCallId`
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
task = PipelineTask(
|
|
304
|
+
pipeline,
|
|
305
|
+
params=PipelineParams(observers=[roark]),
|
|
306
|
+
enable_tracing=True,
|
|
307
|
+
conversation_id=call_id, # set as the `conversation.id` span attribute by Pipecat
|
|
308
|
+
)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Pipecat sets `conversation.id` as a **span attribute** on a root
|
|
312
|
+
`"conversation"` span (and propagates it to every child span). The OTel
|
|
313
|
+
`traceId` itself is auto-generated and unrelated to your call ID; correlation
|
|
314
|
+
happens by attribute value. To find the trace for a Roark call, query your
|
|
315
|
+
backend by `conversation.id = <pipecatCallId>` (e.g., Honeycomb:
|
|
316
|
+
`where conversation.id = "..."`, Jaeger: tag filter, Datadog:
|
|
317
|
+
`@conversation.id:...`).
|
|
318
|
+
|
|
319
|
+
> If you omit `pipecat_call_id`, the observer generates one internally — fine
|
|
320
|
+
> for standalone use, but you won't be able to link a Roark call to its trace.
|
|
321
|
+
> With OTel enabled, **always pass the same value to both**.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Troubleshooting
|
|
326
|
+
|
|
327
|
+
<details>
|
|
328
|
+
<summary><strong>Do I need <code>enable_tracing=True</code> on <code>PipelineTask</code>?</strong></summary>
|
|
329
|
+
|
|
330
|
+
<br>
|
|
331
|
+
|
|
332
|
+
No. `RoarkObserver` captures raw frames — it does not consume OpenTelemetry
|
|
333
|
+
spans. The tracing flag is unrelated. If you *do* enable it and want Roark
|
|
334
|
+
calls linked to their traces, see
|
|
335
|
+
[Correlating with OpenTelemetry tracing](#correlating-with-opentelemetry-tracing).
|
|
336
|
+
|
|
337
|
+
</details>
|
|
338
|
+
|
|
339
|
+
<details>
|
|
340
|
+
<summary><strong>Calls aren't finalizing on Roark</strong></summary>
|
|
341
|
+
|
|
342
|
+
<br>
|
|
343
|
+
|
|
344
|
+
Some transports (notably `SmallWebRTC`) tear down without pushing `EndFrame`
|
|
345
|
+
through observers. Wire `aflush()` into your disconnect handler — see
|
|
346
|
+
[Handling WebRTC disconnects](#handling-webrtc-disconnects).
|
|
347
|
+
|
|
348
|
+
</details>
|
|
349
|
+
|
|
350
|
+
<details>
|
|
351
|
+
<summary><strong>Recording captures user audio only / bot audio only</strong></summary>
|
|
352
|
+
|
|
353
|
+
<br>
|
|
354
|
+
|
|
355
|
+
The `AudioBufferProcessor` must sit **after `transport.output()`** so it sees
|
|
356
|
+
the bot's audio post-TTS. If it's placed earlier in the pipeline, the bot
|
|
357
|
+
channel will be silent.
|
|
358
|
+
|
|
359
|
+
</details>
|
|
360
|
+
|
|
361
|
+
<details>
|
|
362
|
+
<summary><strong>Transcripts arrive empty</strong></summary>
|
|
363
|
+
|
|
364
|
+
<br>
|
|
365
|
+
|
|
366
|
+
The observer warns `call-ended with empty transcript ... no TranscriptionFrame
|
|
367
|
+
or TTSTextFrame was observed during the call` when nothing was captured.
|
|
368
|
+
Usually this means the STT service isn't emitting finalized
|
|
369
|
+
`TranscriptionFrame`s, or the pipeline ended before any speech was processed.
|
|
370
|
+
|
|
371
|
+
</details>
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## Configuration reference
|
|
376
|
+
|
|
377
|
+
| Parameter | Type | Default | Notes |
|
|
378
|
+
|-----------|------|---------|-------|
|
|
379
|
+
| `api_key` | `str` | — | **Required.** Roark API key. |
|
|
380
|
+
| `agent_id` | `str` | — | **Required.** Customer-stable agent identifier. |
|
|
381
|
+
| `agent_name` | `str \| None` | `None` | Display name. |
|
|
382
|
+
| `agent_prompt` | `str \| None` | `None` | System prompt. Persisted as the agent's prompt revision. |
|
|
383
|
+
| `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`. |
|
|
384
|
+
| `pipecat_call_id` | `str \| None` | random UUID | Stable call identifier. Pass the same value to `PipelineTask(conversation_id=...)` when OTel tracing is enabled — see [Correlating with OpenTelemetry tracing](#correlating-with-opentelemetry-tracing). |
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Development
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
uv sync --all-extras
|
|
392
|
+
uv run pytest
|
|
393
|
+
uv run ruff check .
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## License
|
|
399
|
+
|
|
400
|
+
MIT — see [LICENSE](./LICENSE).
|