Skip to content

Commit cb925e9

Browse files
authored
Merge pull request #30 from pipecat-ai/mb/update-voice-ui-kit-0.7.0
Update voice-ui-kit to 0.7.1
2 parents 1d4d958 + f63040d commit cb925e9

8 files changed

Lines changed: 4338 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ All notable changes to **SmallWebRTC Prebuilt** will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.1.0] - 2026-02-05
9+
10+
### Changed
11+
12+
- Update to voice-ui-kit 0.7.1, which introduces support for the
13+
`bot-output` event. Now the conversation panel displays text optimally for
14+
configured TTS service.
15+
816
## [2.0.4] - 2025-12-30
917

1018
### Changed

client/package-lock.json

Lines changed: 17 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"@pipecat-ai/client-react": "^1.1.0",
2727
"@pipecat-ai/small-webrtc-transport": "^1.8.1",
2828
"@pipecat-ai/daily-transport": "^1.5.0",
29-
"@pipecat-ai/voice-ui-kit": "^0.6.0",
29+
"@pipecat-ai/voice-ui-kit": "^0.7.1",
3030
"react": "^19.1.0",
3131
"react-dom": "^19.1.0"
3232
}

test/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ This is a simple example app to help you test your Pipecat bot with a prebuilt U
5555
Once setup is complete, start the app with:
5656

5757
```bash
58-
uv run bot.py
58+
uv run bot-cascade.py # or bot-gemini-live.py
5959
```
6060

6161
## 🎉 Test with SmallWebRTC Prebuilt UI

test/bot-cascade.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#
2+
# Copyright (c) 2024-2026, Daily
3+
#
4+
# SPDX-License-Identifier: BSD 2-Clause License
5+
#
6+
7+
import os
8+
9+
from dotenv import load_dotenv
10+
from loguru import logger
11+
from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3
12+
from pipecat.audio.vad.silero import SileroVADAnalyzer
13+
from pipecat.audio.vad.vad_analyzer import VADParams
14+
from pipecat.frames.frames import LLMRunFrame
15+
from pipecat.pipeline.pipeline import Pipeline
16+
from pipecat.pipeline.runner import PipelineRunner
17+
from pipecat.pipeline.task import PipelineParams, PipelineTask
18+
from pipecat.processors.aggregators.llm_context import LLMContext
19+
from pipecat.processors.aggregators.llm_response_universal import (
20+
LLMContextAggregatorPair,
21+
LLMUserAggregatorParams,
22+
)
23+
from pipecat.runner.types import RunnerArguments
24+
from pipecat.runner.utils import create_transport
25+
from pipecat.services.cartesia.tts import CartesiaTTSService
26+
from pipecat.services.deepgram.stt import DeepgramSTTService
27+
from pipecat.services.deepgram.tts import DeepgramTTSService
28+
from pipecat.services.openai.llm import OpenAILLMService
29+
from pipecat.transports.base_transport import BaseTransport, TransportParams
30+
from pipecat.turns.user_stop import TurnAnalyzerUserTurnStopStrategy
31+
from pipecat.turns.user_turn_strategies import UserTurnStrategies
32+
33+
load_dotenv(override=True)
34+
35+
# We use lambdas to defer transport parameter creation until the transport
36+
# type is selected at runtime.
37+
transport_params = {
38+
"webrtc": lambda: TransportParams(
39+
audio_in_enabled=True,
40+
audio_out_enabled=True,
41+
),
42+
}
43+
44+
45+
async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
46+
logger.info(f"Starting bot")
47+
48+
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
49+
50+
tts = CartesiaTTSService(
51+
api_key=os.getenv("CARTESIA_API_KEY"),
52+
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
53+
)
54+
# tts = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY"), voice="aura-2-andromeda-en")
55+
56+
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"))
57+
58+
messages = [
59+
{
60+
"role": "system",
61+
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.",
62+
},
63+
]
64+
65+
context = LLMContext(messages)
66+
user_aggregator, assistant_aggregator = LLMContextAggregatorPair(
67+
context,
68+
user_params=LLMUserAggregatorParams(
69+
user_turn_strategies=UserTurnStrategies(
70+
stop=[TurnAnalyzerUserTurnStopStrategy(turn_analyzer=LocalSmartTurnAnalyzerV3())]
71+
),
72+
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)),
73+
),
74+
)
75+
76+
pipeline = Pipeline(
77+
[
78+
transport.input(), # Transport user input
79+
stt,
80+
user_aggregator, # User responses
81+
llm, # LLM
82+
tts, # TTS
83+
transport.output(), # Transport bot output
84+
assistant_aggregator, # Assistant spoken responses
85+
]
86+
)
87+
88+
task = PipelineTask(
89+
pipeline,
90+
params=PipelineParams(
91+
enable_metrics=True,
92+
enable_usage_metrics=True,
93+
),
94+
idle_timeout_secs=runner_args.pipeline_idle_timeout_secs,
95+
)
96+
97+
@task.rtvi.event_handler("on_client_ready")
98+
async def on_client_ready(rtvi):
99+
logger.info("Pipecat client ready.")
100+
# Kick off the conversation.
101+
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
102+
await task.queue_frames([LLMRunFrame()])
103+
104+
@transport.event_handler("on_client_connected")
105+
async def on_client_connected(transport, client):
106+
logger.info(f"Client connected")
107+
108+
@transport.event_handler("on_client_disconnected")
109+
async def on_client_disconnected(transport, client):
110+
logger.info(f"Client disconnected")
111+
await task.cancel()
112+
113+
runner = PipelineRunner(handle_sigint=runner_args.handle_sigint)
114+
115+
await runner.run(task)
116+
117+
118+
async def bot(runner_args: RunnerArguments):
119+
"""Main bot entry point compatible with Pipecat Cloud."""
120+
transport = await create_transport(runner_args, transport_params)
121+
await run_bot(transport, runner_args)
122+
123+
124+
if __name__ == "__main__":
125+
from pipecat.runner.run import main
126+
127+
main()

test/bot.py renamed to test/bot-gemini-live.py

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@
1818
from pipecat.pipeline.runner import PipelineRunner
1919
from pipecat.pipeline.task import PipelineParams, PipelineTask
2020
from pipecat.processors.aggregators.llm_context import LLMContext
21-
from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair
21+
from pipecat.processors.aggregators.llm_response_universal import (
22+
AssistantTurnStoppedMessage,
23+
LLMContextAggregatorPair,
24+
LLMUserAggregatorParams,
25+
UserTurnStoppedMessage,
26+
)
2227
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
23-
from pipecat.processors.frameworks.rtvi import RTVIObserver, RTVIProcessor
2428
from pipecat.runner.types import RunnerArguments
2529
from pipecat.runner.utils import create_transport
2630
from pipecat.services.google.gemini_live.llm import GeminiLiveLLMService
@@ -39,11 +43,6 @@
3943
video_in_enabled=True,
4044
video_out_enabled=True,
4145
video_out_is_live=True,
42-
# set stop_secs to something roughly similar to the internal setting
43-
# of the Multimodal Live api, just to align events. This doesn't really
44-
# matter because we can only use the Multimodal Live API's phrase
45-
# endpointing, for now.
46-
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)),
4746
),
4847
}
4948

@@ -108,20 +107,21 @@ async def run_bot(
108107
},
109108
],
110109
)
111-
context_aggregator = LLMContextAggregatorPair(context)
112-
113-
# RTVI events for Pipecat client UI
114-
rtvi = RTVIProcessor()
110+
user_aggregator, assistant_aggregator = LLMContextAggregatorPair(
111+
context,
112+
user_params=LLMUserAggregatorParams(
113+
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)),
114+
),
115+
)
115116

116117
pipeline = Pipeline(
117118
[
118119
transport.input(),
119-
context_aggregator.user(),
120-
rtvi,
120+
user_aggregator,
121121
llm,
122122
EdgeDetectionProcessor(params.video_out_width, params.video_out_height),
123123
transport.output(),
124-
context_aggregator.assistant(),
124+
assistant_aggregator,
125125
]
126126
)
127127

@@ -132,13 +132,11 @@ async def run_bot(
132132
enable_usage_metrics=True,
133133
),
134134
idle_timeout_secs=runner_args.pipeline_idle_timeout_secs,
135-
observers=[RTVIObserver(rtvi)],
136135
)
137136

138-
@rtvi.event_handler("on_client_ready")
137+
@task.rtvi.event_handler("on_client_ready")
139138
async def on_client_ready(rtvi):
140139
logger.info("Pipecat client ready.")
141-
await rtvi.set_bot_ready()
142140
await task.queue_frames([LLMRunFrame()])
143141

144142
@transport.event_handler("on_client_connected")
@@ -150,6 +148,18 @@ async def on_client_disconnected(transport, client):
150148
logger.info(f"Client disconnected")
151149
await task.cancel()
152150

151+
@user_aggregator.event_handler("on_user_turn_stopped")
152+
async def on_user_turn_stopped(aggregator, strategy, message: UserTurnStoppedMessage):
153+
timestamp = f"[{message.timestamp}] " if message.timestamp else ""
154+
line = f"{timestamp}user: {message.content}"
155+
logger.info(f"Transcript: {line}")
156+
157+
@assistant_aggregator.event_handler("on_assistant_turn_stopped")
158+
async def on_assistant_turn_stopped(aggregator, message: AssistantTurnStoppedMessage):
159+
timestamp = f"[{message.timestamp}] " if message.timestamp else ""
160+
line = f"{timestamp}assistant: {message.content}"
161+
logger.info(f"Transcript: {line}")
162+
153163
runner = PipelineRunner(handle_sigint=runner_args.handle_sigint)
154164

155165
await runner.run(task)

test/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
description = "Quickstart example for building voice AI bots with Pipecat"
55
requires-python = ">=3.10"
66
dependencies = [
7-
"pipecat-ai[google,silero,webrtc,runner]",
7+
"pipecat-ai[google,openai,cartesia,deepgram,silero,webrtc,runner,local-smart-turn-v3]>=0.0.101",
88
"pipecat-ai-small-webrtc-prebuilt"
99
]
1010

0 commit comments

Comments
 (0)