pipecat-slng 0.3.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.
@@ -0,0 +1,3 @@
1
+ OPENAI_API_KEY=XXXXX
2
+ OPENAI_MODEL=gpt-4.1
3
+ SLNG_API_KEY=XXX
@@ -0,0 +1,47 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.11", "3.12", "3.13"]
16
+ env:
17
+ # Pin every uv invocation in this job to the matrix interpreter —
18
+ # an unpinned `uv run` rebuilds .venv with the default Python and
19
+ # drops the extras installed by `uv sync` (B1).
20
+ UV_PYTHON: ${{ matrix.python-version }}
21
+
22
+ steps:
23
+ - name: Checkout
24
+ uses: actions/checkout@v4
25
+
26
+ - name: Install uv
27
+ uses: astral-sh/setup-uv@v5
28
+ with:
29
+ enable-cache: true
30
+
31
+ - name: Set up Python ${{ matrix.python-version }}
32
+ run: uv python install ${{ matrix.python-version }}
33
+
34
+ - name: Install dependencies
35
+ run: uv sync --all-extras --locked
36
+
37
+ - name: Lint (ruff)
38
+ run: uv run --no-sync ruff check .
39
+
40
+ - name: Format check (ruff)
41
+ run: uv run --no-sync ruff format --check .
42
+
43
+ - name: Type check (ty)
44
+ run: uv run --no-sync ty check .
45
+
46
+ - name: Tests (offline only — live smoke skipped without SLNG_API_KEY)
47
+ run: uv run --no-sync pytest -k 'not live' -v
@@ -0,0 +1,26 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Local design docs / plans
13
+ docs/
14
+ AGENTS.md
15
+ SPEC.md
16
+
17
+ # Secrets
18
+ .env
19
+
20
+ # Editor / IDE
21
+ .idea/
22
+ .claude/
23
+
24
+ # Tool caches
25
+ .pytest_cache/
26
+ .ruff_cache/
@@ -0,0 +1 @@
1
+ 3.11
@@ -0,0 +1,49 @@
1
+ # Changelog
2
+
3
+ All notable changes to `pipecat-slng` are documented here. This project adheres
4
+ to [Semantic Versioning](https://semver.org/).
5
+
6
+ ## [0.3.0] - 2026-06-10
7
+
8
+ ### Fixed
9
+ - `SlngTTSService` now treats a server-initiated WebSocket close after `audio_end`/`flushed` as the expected per-utterance lifecycle (observed with `slng/rime/arcana` models) and reconnects quietly. Previously every bot turn triggered Pipecat reconnect warnings, and three short turns in a row could trip Pipecat's consecutive quick-failure cap and shut the TTS service down mid-call. Unexpected closes keep the full Pipecat retry/failure machinery.
10
+
11
+ ### Added
12
+ - Top-level constructor kwargs for runtime-tunable settings:
13
+ - `SlngSTTService`: `language`, `enable_vad`, `enable_partials`
14
+ - `SlngTTSService`: `language`, `speed`
15
+ - `SlngHttpTTSService`: `language`, `speed` (kept for parity; not sent over wire — HTTP body is `{text, voice}` only per the SLNG OpenAPI)
16
+ - STT confidence filter: drop transcripts with top-level `confidence < 0.5`, matching the Pipecat community-integration guide. No-op when the bridge does not surface confidence.
17
+ - `py.typed` marker (PEP 561) — downstream type checkers now see inline types.
18
+ - GitHub Actions CI workflow: ruff + ruff-format + ty + pytest matrix on Python 3.11/3.12/3.13.
19
+ - New unit tests covering region/world routing headers, WS-TTS interruption (`clear`/`flush`), STT finalize (`finalize` + `from_finalize`→`confirm_finalize`), and graceful disconnect (`{type: close}`). Suite now 23 unit + 3 live (gated).
20
+
21
+ ### Changed
22
+ - Error handling tightened to the community-integration guide ("raise AND push"):
23
+ - `_connect_websocket` (STT + WS-TTS) now raises after `push_error`, so connect failures surface through `PipelineRunner` instead of dribbling silent send-after-disconnect errors.
24
+ - In-stream send / non-200 / compressed-format paths in `run_stt` and `run_tts` (WS + HTTP) now call `push_error` alongside the existing `yield ErrorFrame`.
25
+ - `examples/bot.py` defaults to the streaming `SlngTTSService`; removed the three commented-out TTS variants (incl. the "Problematic provider" Cartesia stub).
26
+ - `README.md` reorganised "WebSocket first, HTTP fallback"; added explicit company attribution under the title; documented the HTTP body contract (`{text, voice}` only).
27
+
28
+ Tested with Pipecat v1.3.0.
29
+
30
+ ## [0.2.0] - 2026-05-29
31
+
32
+ ### Added
33
+ - `SlngHttpTTSService` — non-streaming HTTP/REST text-to-speech via the SLNG Unified TTS bridge (`POST /v1/bridges/unmute/tts/{model}`), built on `aiohttp`.
34
+
35
+ ### Changed
36
+ - `SlngTTSService` now applies runtime settings updates: a `voice`/`speed`/`language` change reconnects to re-run the init handshake.
37
+
38
+ Tested with Pipecat v1.3.0.
39
+
40
+ ## [0.1.0] - 2026-05-29
41
+
42
+ ### Added
43
+ - `SlngSTTService` — real-time WebSocket speech-to-text via the SLNG Unmute STT bridge.
44
+ - `SlngTTSService` — real-time WebSocket text-to-speech via the SLNG Unmute TTS bridge.
45
+ - Region routing via `region_override` / `world_part_override`.
46
+ - Foundational cascade example (`examples/bot.py`).
47
+ - Unit tests (fake WebSocket) and gated live smoke tests.
48
+
49
+ Tested with Pipecat v1.3.0.
@@ -0,0 +1,25 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2026, SLNG and contributors.
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,165 @@
1
+ Metadata-Version: 2.4
2
+ Name: pipecat-slng
3
+ Version: 0.3.0
4
+ Summary: SLNG STT/TTS services for Pipecat (WebSocket + HTTP)
5
+ Project-URL: Homepage, https://slng.ai
6
+ Project-URL: Repository, https://github.com/slng-ai/pipecat-slng
7
+ Author: slng.ai
8
+ License-Expression: BSD-2-Clause
9
+ License-File: LICENSE
10
+ Keywords: http,pipecat,slng,stt,tts,voice-ai,websocket
11
+ Requires-Python: >=3.11
12
+ Requires-Dist: aiohttp>=3.9
13
+ Requires-Dist: pipecat-ai>=1.3.0
14
+ Requires-Dist: websockets>=13.0
15
+ Provides-Extra: example
16
+ Requires-Dist: pipecat-ai[openai,runner,silero,webrtc]>=1.3.0; extra == 'example'
17
+ Description-Content-Type: text/markdown
18
+
19
+ # pipecat-slng
20
+
21
+ [![CI](https://github.com/slng-ai/pipecat-slng/actions/workflows/ci.yml/badge.svg)](https://github.com/slng-ai/pipecat-slng/actions/workflows/ci.yml)
22
+
23
+ _Built and maintained by the SLNG team (slng.ai)._
24
+
25
+ WebSocket STT and TTS services for [Pipecat](https://github.com/pipecat-ai/pipecat),
26
+ backed by [SLNG](https://slng.ai) — a unified voice AI gateway that routes to
27
+ multiple STT/TTS providers (Deepgram, ElevenLabs, Rime, Sarvam, and more)
28
+ through a single API key. Swap the `model` string to switch providers; no other
29
+ code changes needed.
30
+
31
+ > Tested with Pipecat v1.3.0.
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ uv add pipecat-slng
37
+ # or
38
+ pip install pipecat-slng
39
+ ```
40
+
41
+ ## Environment variables
42
+
43
+ ```env
44
+ SLNG_API_KEY=your_slng_api_key # get one at https://slng.ai
45
+ OPENAI_API_KEY=your_openai_api_key # only needed for the example bot (LLM)
46
+ ```
47
+
48
+ Copy [`.env.example`](.env.example) to `.env` to get started.
49
+
50
+ ## Usage (streaming WebSocket — recommended)
51
+
52
+ `SlngSTTService` and `SlngTTSService` run over WebSocket: low-latency, supports
53
+ mid-utterance interruption, and exposes the full SLNG config surface
54
+ (encoding, sample_rate, language, speed).
55
+
56
+ ```python
57
+ import os
58
+
59
+ from pipecat_slng import SlngSTTService, SlngTTSService
60
+
61
+ stt = SlngSTTService(
62
+ api_key=os.getenv("SLNG_API_KEY"),
63
+ model="slng/deepgram/nova:3-en",
64
+ )
65
+
66
+ tts = SlngTTSService(
67
+ api_key=os.getenv("SLNG_API_KEY"),
68
+ model="slng/deepgram/aura:2-en",
69
+ voice="aura-2-thalia-en",
70
+ )
71
+ ```
72
+
73
+ Common runtime knobs are top-level kwargs (e.g. `language=`, `speed=`,
74
+ `enable_vad=`, `enable_partials=`). For richer overrides pass a
75
+ `SlngSTTSettings(...)` / `SlngTTSSettings(...)` to `settings=`.
76
+
77
+ Defaults when not specified: STT uses `language=Language.EN`,
78
+ `enable_vad=True`, `enable_partials=True`; TTS uses `language=Language.EN`
79
+ and the server's default `speed`.
80
+
81
+ Two behaviors worth knowing:
82
+
83
+ - **Confidence filter (STT).** When the provider surfaces a confidence
84
+ score, transcripts below 0.5 are dropped.
85
+ - **Runtime settings updates.** Changing `voice`, `speed`, or `language`
86
+ mid-session (via Pipecat settings updates) reconnects the WebSocket to
87
+ re-run the init handshake — expect a brief reconnect, not a silent no-op.
88
+
89
+ ## HTTP TTS (non-streaming fallback)
90
+
91
+ For simple request/response synthesis where streaming is not required, use
92
+ `SlngHttpTTSService`. It issues one HTTP POST per utterance and returns the
93
+ full audio body in one frame.
94
+
95
+ ```python
96
+ import os
97
+
98
+ from pipecat_slng import SlngHttpTTSService
99
+
100
+ tts = SlngHttpTTSService(
101
+ api_key=os.getenv("SLNG_API_KEY"),
102
+ model="slng/deepgram/aura:2-en",
103
+ voice="aura-2-thalia-en",
104
+ )
105
+ ```
106
+
107
+ **HTTP contract limits.** Per the SLNG Unified TTS HTTP OpenAPI, the request
108
+ body accepts **only `{text, voice}`** — there is no `config` object. Encoding,
109
+ sample_rate, language, and speed are therefore **not configurable over HTTP**;
110
+ the server returns its default audio format. The service auto-detects WAV
111
+ (decoded to raw PCM at the file's sample rate) and plain PCM (passed through
112
+ at the pipeline's sample rate). Compressed responses (MP3/Ogg) yield an
113
+ `ErrorFrame` — use the streaming `SlngTTSService` if you need codec control.
114
+
115
+ An `aiohttp.ClientSession` is created internally if you don't pass one; supply
116
+ `aiohttp_session=...` to reuse a shared session.
117
+
118
+ ## Region routing
119
+
120
+ Both services support gateway region routing via `region_override` (pin to a
121
+ datacenter: `ap-southeast-2` | `eu-north-1` | `us-east-1`) and
122
+ `world_part_override` (broad zone: `ap` | `eu` | `na`). When both are set,
123
+ `region_override` wins. WebSocket services send these as the
124
+ `X-Region-Override` / `X-World-Part-Override` headers; the HTTP service uses
125
+ the `region` / `world-part` query parameters (per the bridge contract).
126
+
127
+ ```python
128
+ stt = SlngSTTService(
129
+ api_key=os.getenv("SLNG_API_KEY"),
130
+ model="slng/deepgram/nova:3-en",
131
+ region_override="eu-north-1",
132
+ )
133
+ ```
134
+
135
+ ## Example
136
+
137
+ A complete cascade bot (STT → LLM → TTS, WebSocket TTS by default) lives in
138
+ [`examples/bot.py`](examples/bot.py):
139
+
140
+ ```bash
141
+ cp .env.example .env # fill in SLNG_API_KEY and OPENAI_API_KEY
142
+ uv run --extra example examples/bot.py
143
+ ```
144
+
145
+ Then open http://localhost:7860/client in your browser and start talking.
146
+ The bot uses the SmallWebRTC transport by default; pass `-t daily` to use
147
+ Daily instead (requires installing `pipecat-ai[daily]`).
148
+
149
+ ## Development
150
+
151
+ ```bash
152
+ uv sync --all-extras
153
+ uv run pytest # unit tests (live smoke tests skip without SLNG_API_KEY)
154
+ uv run ruff check .
155
+ uv run ty check .
156
+ ```
157
+
158
+ ## About SLNG
159
+
160
+ SLNG (https://slng.ai) is a unified voice AI gateway. Learn more in the
161
+ [SLNG docs](https://docs.slng.ai/).
162
+
163
+ ## License
164
+
165
+ BSD-2-Clause — see [LICENSE](LICENSE).
@@ -0,0 +1,147 @@
1
+ # pipecat-slng
2
+
3
+ [![CI](https://github.com/slng-ai/pipecat-slng/actions/workflows/ci.yml/badge.svg)](https://github.com/slng-ai/pipecat-slng/actions/workflows/ci.yml)
4
+
5
+ _Built and maintained by the SLNG team (slng.ai)._
6
+
7
+ WebSocket STT and TTS services for [Pipecat](https://github.com/pipecat-ai/pipecat),
8
+ backed by [SLNG](https://slng.ai) — a unified voice AI gateway that routes to
9
+ multiple STT/TTS providers (Deepgram, ElevenLabs, Rime, Sarvam, and more)
10
+ through a single API key. Swap the `model` string to switch providers; no other
11
+ code changes needed.
12
+
13
+ > Tested with Pipecat v1.3.0.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ uv add pipecat-slng
19
+ # or
20
+ pip install pipecat-slng
21
+ ```
22
+
23
+ ## Environment variables
24
+
25
+ ```env
26
+ SLNG_API_KEY=your_slng_api_key # get one at https://slng.ai
27
+ OPENAI_API_KEY=your_openai_api_key # only needed for the example bot (LLM)
28
+ ```
29
+
30
+ Copy [`.env.example`](.env.example) to `.env` to get started.
31
+
32
+ ## Usage (streaming WebSocket — recommended)
33
+
34
+ `SlngSTTService` and `SlngTTSService` run over WebSocket: low-latency, supports
35
+ mid-utterance interruption, and exposes the full SLNG config surface
36
+ (encoding, sample_rate, language, speed).
37
+
38
+ ```python
39
+ import os
40
+
41
+ from pipecat_slng import SlngSTTService, SlngTTSService
42
+
43
+ stt = SlngSTTService(
44
+ api_key=os.getenv("SLNG_API_KEY"),
45
+ model="slng/deepgram/nova:3-en",
46
+ )
47
+
48
+ tts = SlngTTSService(
49
+ api_key=os.getenv("SLNG_API_KEY"),
50
+ model="slng/deepgram/aura:2-en",
51
+ voice="aura-2-thalia-en",
52
+ )
53
+ ```
54
+
55
+ Common runtime knobs are top-level kwargs (e.g. `language=`, `speed=`,
56
+ `enable_vad=`, `enable_partials=`). For richer overrides pass a
57
+ `SlngSTTSettings(...)` / `SlngTTSSettings(...)` to `settings=`.
58
+
59
+ Defaults when not specified: STT uses `language=Language.EN`,
60
+ `enable_vad=True`, `enable_partials=True`; TTS uses `language=Language.EN`
61
+ and the server's default `speed`.
62
+
63
+ Two behaviors worth knowing:
64
+
65
+ - **Confidence filter (STT).** When the provider surfaces a confidence
66
+ score, transcripts below 0.5 are dropped.
67
+ - **Runtime settings updates.** Changing `voice`, `speed`, or `language`
68
+ mid-session (via Pipecat settings updates) reconnects the WebSocket to
69
+ re-run the init handshake — expect a brief reconnect, not a silent no-op.
70
+
71
+ ## HTTP TTS (non-streaming fallback)
72
+
73
+ For simple request/response synthesis where streaming is not required, use
74
+ `SlngHttpTTSService`. It issues one HTTP POST per utterance and returns the
75
+ full audio body in one frame.
76
+
77
+ ```python
78
+ import os
79
+
80
+ from pipecat_slng import SlngHttpTTSService
81
+
82
+ tts = SlngHttpTTSService(
83
+ api_key=os.getenv("SLNG_API_KEY"),
84
+ model="slng/deepgram/aura:2-en",
85
+ voice="aura-2-thalia-en",
86
+ )
87
+ ```
88
+
89
+ **HTTP contract limits.** Per the SLNG Unified TTS HTTP OpenAPI, the request
90
+ body accepts **only `{text, voice}`** — there is no `config` object. Encoding,
91
+ sample_rate, language, and speed are therefore **not configurable over HTTP**;
92
+ the server returns its default audio format. The service auto-detects WAV
93
+ (decoded to raw PCM at the file's sample rate) and plain PCM (passed through
94
+ at the pipeline's sample rate). Compressed responses (MP3/Ogg) yield an
95
+ `ErrorFrame` — use the streaming `SlngTTSService` if you need codec control.
96
+
97
+ An `aiohttp.ClientSession` is created internally if you don't pass one; supply
98
+ `aiohttp_session=...` to reuse a shared session.
99
+
100
+ ## Region routing
101
+
102
+ Both services support gateway region routing via `region_override` (pin to a
103
+ datacenter: `ap-southeast-2` | `eu-north-1` | `us-east-1`) and
104
+ `world_part_override` (broad zone: `ap` | `eu` | `na`). When both are set,
105
+ `region_override` wins. WebSocket services send these as the
106
+ `X-Region-Override` / `X-World-Part-Override` headers; the HTTP service uses
107
+ the `region` / `world-part` query parameters (per the bridge contract).
108
+
109
+ ```python
110
+ stt = SlngSTTService(
111
+ api_key=os.getenv("SLNG_API_KEY"),
112
+ model="slng/deepgram/nova:3-en",
113
+ region_override="eu-north-1",
114
+ )
115
+ ```
116
+
117
+ ## Example
118
+
119
+ A complete cascade bot (STT → LLM → TTS, WebSocket TTS by default) lives in
120
+ [`examples/bot.py`](examples/bot.py):
121
+
122
+ ```bash
123
+ cp .env.example .env # fill in SLNG_API_KEY and OPENAI_API_KEY
124
+ uv run --extra example examples/bot.py
125
+ ```
126
+
127
+ Then open http://localhost:7860/client in your browser and start talking.
128
+ The bot uses the SmallWebRTC transport by default; pass `-t daily` to use
129
+ Daily instead (requires installing `pipecat-ai[daily]`).
130
+
131
+ ## Development
132
+
133
+ ```bash
134
+ uv sync --all-extras
135
+ uv run pytest # unit tests (live smoke tests skip without SLNG_API_KEY)
136
+ uv run ruff check .
137
+ uv run ty check .
138
+ ```
139
+
140
+ ## About SLNG
141
+
142
+ SLNG (https://slng.ai) is a unified voice AI gateway. Learn more in the
143
+ [SLNG docs](https://docs.slng.ai/).
144
+
145
+ ## License
146
+
147
+ BSD-2-Clause — see [LICENSE](LICENSE).
@@ -0,0 +1,173 @@
1
+ #
2
+ # Copyright (c) 2026, slng.ai
3
+ #
4
+ # SPDX-License-Identifier: BSD-2-Clause
5
+ #
6
+
7
+ """SLNG Voice Agent example.
8
+
9
+ Cascade pipeline: Speech-to-Text → LLM → Text-to-Speech, with SLNG as the
10
+ unified STT and TTS gateway. Defaults to the streaming WebSocket TTS service
11
+ (``SlngTTSService``) for low-latency, interruptible conversation.
12
+
13
+ Required services:
14
+ - SLNG (STT + TTS) — set SLNG_API_KEY
15
+ - OpenAI (LLM) — set OPENAI_API_KEY
16
+
17
+ Run with::
18
+
19
+ cp .env.example .env # set SLNG_API_KEY and OPENAI_API_KEY
20
+ uv run --extra example examples/bot.py
21
+
22
+ Then open http://localhost:7860/client in your browser and start talking.
23
+ Uses the SmallWebRTC transport by default; pass ``-t daily`` to use Daily
24
+ instead (requires ``pipecat-ai[daily]``).
25
+ """
26
+
27
+ import os
28
+
29
+ from dotenv import load_dotenv
30
+ from loguru import logger
31
+
32
+ from pipecat.audio.vad.silero import SileroVADAnalyzer
33
+ from pipecat.frames.frames import LLMRunFrame
34
+ from pipecat.pipeline.pipeline import Pipeline
35
+ from pipecat.pipeline.runner import PipelineRunner
36
+ from pipecat.pipeline.task import PipelineParams, PipelineTask
37
+ from pipecat.processors.aggregators.llm_context import LLMContext
38
+ from pipecat.processors.aggregators.llm_response_universal import (
39
+ LLMContextAggregatorPair,
40
+ LLMUserAggregatorParams,
41
+ )
42
+ from pipecat.runner.types import (
43
+ DailyRunnerArguments,
44
+ RunnerArguments,
45
+ SmallWebRTCRunnerArguments,
46
+ )
47
+ from pipecat.services.openai.responses.llm import OpenAIResponsesLLMService
48
+ from pipecat.transports.base_transport import BaseTransport, TransportParams
49
+
50
+ from pipecat_slng import SlngSTTService, SlngTTSService
51
+
52
+ load_dotenv(override=True)
53
+
54
+
55
+ async def run_bot(transport: BaseTransport):
56
+ """Main bot logic."""
57
+ logger.info("Starting bot")
58
+
59
+ slng_api_key = os.environ["SLNG_API_KEY"]
60
+
61
+ stt = SlngSTTService(
62
+ api_key=slng_api_key,
63
+ model="slng/deepgram/nova:3-en",
64
+ )
65
+
66
+ # Streaming WebSocket TTS — low latency, supports mid-utterance interruption.
67
+ # Swap model= to switch provider. For non-streaming HTTP, see SlngHttpTTSService.
68
+ tts = SlngTTSService(
69
+ api_key=slng_api_key,
70
+ model="slng/deepgram/aura:2-en",
71
+ voice="aura-2-thalia-en",
72
+ )
73
+
74
+ llm = OpenAIResponsesLLMService(
75
+ api_key=os.getenv("OPENAI_API_KEY"),
76
+ settings=OpenAIResponsesLLMService.Settings(
77
+ model=os.getenv("OPENAI_MODEL", "gpt-4.1"),
78
+ system_instruction=(
79
+ "You are a helpful assistant in a voice conversation. "
80
+ "Your responses will be spoken aloud, so avoid emojis, bullet points, "
81
+ "or other formatting that can't be spoken. "
82
+ "Respond to what the user said in a creative, helpful, and brief way."
83
+ ),
84
+ ),
85
+ )
86
+
87
+ context = LLMContext()
88
+ user_aggregator, assistant_aggregator = LLMContextAggregatorPair(
89
+ context,
90
+ user_params=LLMUserAggregatorParams(
91
+ vad_analyzer=SileroVADAnalyzer(),
92
+ ),
93
+ )
94
+
95
+ pipeline = Pipeline(
96
+ [
97
+ transport.input(),
98
+ stt,
99
+ user_aggregator,
100
+ llm,
101
+ tts,
102
+ transport.output(),
103
+ assistant_aggregator,
104
+ ]
105
+ )
106
+
107
+ task = PipelineTask(
108
+ pipeline,
109
+ params=PipelineParams(
110
+ enable_metrics=True,
111
+ enable_usage_metrics=True,
112
+ ),
113
+ observers=[],
114
+ )
115
+
116
+ @task.rtvi.event_handler("on_client_ready")
117
+ async def on_client_ready(rtvi):
118
+ context.add_message({"role": "user", "content": "Please introduce yourself."})
119
+ await task.queue_frames([LLMRunFrame()])
120
+
121
+ @transport.event_handler("on_client_connected")
122
+ async def on_client_connected(transport, client):
123
+ logger.info("Client connected")
124
+
125
+ @transport.event_handler("on_client_disconnected")
126
+ async def on_client_disconnected(transport, client):
127
+ logger.info("Client disconnected")
128
+ await task.cancel()
129
+
130
+ runner = PipelineRunner(handle_sigint=False)
131
+ await runner.run(task)
132
+
133
+
134
+ async def bot(runner_args: RunnerArguments):
135
+ """Main bot entry point."""
136
+ transport = None
137
+
138
+ match runner_args:
139
+ case DailyRunnerArguments():
140
+ # Imported lazily so the bot runs with only the transport extra
141
+ # you have installed (Daily needs `pipecat-ai[daily]`).
142
+ from pipecat.transports.daily.transport import DailyParams, DailyTransport
143
+
144
+ transport = DailyTransport(
145
+ runner_args.room_url,
146
+ runner_args.token,
147
+ "PipecatSLNG Bot",
148
+ params=DailyParams(
149
+ audio_in_enabled=True,
150
+ audio_out_enabled=True,
151
+ ),
152
+ )
153
+ case SmallWebRTCRunnerArguments():
154
+ from pipecat.transports.smallwebrtc.transport import SmallWebRTCTransport
155
+
156
+ transport = SmallWebRTCTransport(
157
+ webrtc_connection=runner_args.webrtc_connection,
158
+ params=TransportParams(
159
+ audio_in_enabled=True,
160
+ audio_out_enabled=True,
161
+ ),
162
+ )
163
+ case _:
164
+ logger.error(f"Unsupported runner arguments type: {type(runner_args)}")
165
+ return
166
+
167
+ await run_bot(transport)
168
+
169
+
170
+ if __name__ == "__main__":
171
+ from pipecat.runner.run import main
172
+
173
+ main()