Add client/server quickstart example
This commit is contained in:
24
examples/client-server-web/client/.gitignore
vendored
Normal file
24
examples/client-server-web/client/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
17
examples/client-server-web/client/index.html
Normal file
17
examples/client-server-web/client/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Pipecat UI</title>
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="stylesheet" href="/src/style.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
2635
examples/client-server-web/client/package-lock.json
generated
Normal file
2635
examples/client-server-web/client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
33
examples/client-server-web/client/package.json
Normal file
33
examples/client-server-web/client/package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "client",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "node_modules/.bin/vite",
|
||||
"build": "node_modules/.bin/tsc && vite build",
|
||||
"preview": "node_modules/.bin/vite preview"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.14.0",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@vitejs/plugin-react-swc": "^3.8.1",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^6.2.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource-variable/geist": "^5.2.6",
|
||||
"@fontsource-variable/geist-mono": "^5.2.6",
|
||||
"@pipecat-ai/client-js": "^1.0.1",
|
||||
"@pipecat-ai/client-react": "^1.0.1",
|
||||
"@pipecat-ai/small-webrtc-transport": "^1.0.0",
|
||||
"@pipecat-ai/daily-transport": "^1.0.0",
|
||||
"@pipecat-ai/voice-ui-kit": "^0.1.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
}
|
||||
}
|
||||
7
examples/client-server-web/client/public/favicon.svg
Normal file
7
examples/client-server-web/client/public/favicon.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.3088 5.05615C3.64682 4.92779 4.02833 5.02411 4.26653 5.29797L7.36884 8.86461H16.6312L19.7335 5.29797C19.9717 5.02411 20.3532 4.92779 20.6912 5.05615C21.0292 5.18452 21.253 5.51072 21.253 5.87504V13.75H24V15.5H19.5181V8.19909L17.6762 10.3167C17.5115 10.506 17.2738 10.6146 17.0241 10.6146H6.9759C6.72616 10.6146 6.48854 10.506 6.32383 10.3167L4.48193 8.19909V15.5H0V13.75H2.74699V5.87504C2.74699 5.51072 2.97078 5.18452 3.3088 5.05615Z" fill="black"/>
|
||||
<path d="M19.5181 17.25H24V19H19.5181V17.25Z" fill="black"/>
|
||||
<path d="M0 17.25H4.48193V19H0V17.25Z" fill="black"/>
|
||||
<path d="M9.25301 14.3333C9.25301 14.9777 8.73517 15.5 8.09639 15.5C7.4576 15.5 6.93976 14.9777 6.93976 14.3333C6.93976 13.689 7.4576 13.1667 8.09639 13.1667C8.73517 13.1667 9.25301 13.689 9.25301 14.3333Z" fill="black"/>
|
||||
<path d="M17.0602 14.3333C17.0602 14.9777 16.5424 15.5 15.9036 15.5C15.2648 15.5 14.747 14.9777 14.747 14.3333C14.747 13.689 15.2648 13.1667 15.9036 13.1667C16.5424 13.1667 17.0602 13.689 17.0602 14.3333Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,7 @@
|
||||
<svg width="332" height="192" viewBox="0 0 332 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M45.7718 0.769635C50.4477 -0.990844 55.7252 0.330188 59.0204 4.08595L101.936 53H230.064L272.98 4.08595C276.275 0.330188 281.552 -0.990844 286.228 0.769635C290.904 2.53011 294 7.00367 294 12V120H332V144H270V43.8728L244.52 72.9141C242.242 75.5111 238.955 77 235.5 77H96.5C93.0452 77 89.7581 75.5111 87.4796 72.9141L62 43.8728V144H0V120H38V12C38 7.00367 41.0958 2.53011 45.7718 0.769635Z" fill="black"/>
|
||||
<path d="M270 168H332V192H270V168Z" fill="black"/>
|
||||
<path d="M0 168H62V192H0V168Z" fill="black"/>
|
||||
<path d="M128 128C128 136.837 120.837 144 112 144C103.163 144 96 136.837 96 128C96 119.164 103.163 112 112 112C120.837 112 128 119.164 128 128Z" fill="black"/>
|
||||
<path d="M236 128C236 136.837 228.837 144 220 144C211.163 144 204 136.837 204 128C204 119.164 211.163 112 220 112C228.837 112 236 119.164 236 128Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 937 B |
28
examples/client-server-web/client/src/index.tsx
Normal file
28
examples/client-server-web/client/src/index.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import {
|
||||
ConsoleTemplate,
|
||||
FullScreenContainer,
|
||||
ThemeProvider,
|
||||
} from '@pipecat-ai/voice-ui-kit';
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
//@ts-ignore - fontsource-variable/geist is not typed
|
||||
import '@fontsource-variable/geist';
|
||||
//@ts-ignore - fontsource-variable/geist is not typed
|
||||
import '@fontsource-variable/geist-mono';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
// @ts-ignore
|
||||
<StrictMode>
|
||||
<ThemeProvider>
|
||||
<FullScreenContainer>
|
||||
<ConsoleTemplate
|
||||
transportType="smallwebrtc"
|
||||
connectParams={{
|
||||
connectionUrl: '/api/offer',
|
||||
}}
|
||||
/>
|
||||
</FullScreenContainer>
|
||||
</ThemeProvider>
|
||||
</StrictMode>
|
||||
);
|
||||
7
examples/client-server-web/client/src/style.css
Normal file
7
examples/client-server-web/client/src/style.css
Normal file
@@ -0,0 +1,7 @@
|
||||
@import "@pipecat-ai/voice-ui-kit/styles.css";
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
27
examples/client-server-web/client/tsconfig.json
Normal file
27
examples/client-server-web/client/tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2016",
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2016", "DOM", "DOM.Iterable"],
|
||||
"types": ["node"],
|
||||
"skipLibCheck": true,
|
||||
"jsx": "preserve",
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"allowJs": true,
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"esModuleInterop": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": false,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
18
examples/client-server-web/client/vite.config.js
Normal file
18
examples/client-server-web/client/vite.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
|
||||
export default defineConfig({
|
||||
base: "./", //Use relative paths so it works at any mount path
|
||||
plugins: [react()],
|
||||
publicDir: "public",
|
||||
server: {
|
||||
allowedHosts: true, // Allows external connections like ngrok
|
||||
proxy: {
|
||||
// Proxy /api requests to the backend server
|
||||
"/api": {
|
||||
target: "http://0.0.0.0:7860", // Replace with your backend URL
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
90
examples/client-server-web/server/README.md
Normal file
90
examples/client-server-web/server/README.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Pipecat Quickstart
|
||||
|
||||
Run your first Pipecat bot in under 5 minutes. This example creates a voice AI bot that you can talk to in your browser.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Python 3.10+
|
||||
|
||||
Pipecat requires Python 3.10 or newer. Check your version:
|
||||
|
||||
```bash
|
||||
python --version
|
||||
```
|
||||
|
||||
If you need to upgrade Python, we recommend using a version manager like `uv` or `pyenv`.
|
||||
|
||||
### AI Service API keys
|
||||
|
||||
Pipecat orchestrates different AI services in a pipeline, ensuring low latency communication. In this quickstart example, we'll use:
|
||||
|
||||
- [Deepgram](https://console.deepgram.com/signup) for Speech-to-Text transcriptions
|
||||
- [OpenAI](https://auth.openai.com/create-account) for LLM inference
|
||||
- [Cartesia](https://play.cartesia.ai/sign-up) for Text-to-Speech audio generation
|
||||
|
||||
Have your API keys ready. We'll add them to your `.env` shortly.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Set up a virtual environment
|
||||
|
||||
From the root directory of the `pipecat` repo, run:
|
||||
|
||||
```bash
|
||||
cd examples/quickstart
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
||||
```
|
||||
|
||||
> Using `uv`? Create your venv using: `uv venv && source .venv/bin/activate`.
|
||||
|
||||
2. Install packages
|
||||
|
||||
Then, install the requirements:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
> Using `uv`? Install requirements using: `uv pip install -r requirements.txt`.
|
||||
|
||||
3. Configure environment variables
|
||||
|
||||
Create a `.env` file:
|
||||
|
||||
```bash
|
||||
cp env.example .env
|
||||
```
|
||||
|
||||
Then, add your API keys:
|
||||
|
||||
```
|
||||
DEEPGRAM_API_KEY=your_deepgram_api_key
|
||||
OPENAI_API_KEY=your_openai_api_key
|
||||
CARTESIA_API_KEY=your_cartesia_api_key
|
||||
```
|
||||
|
||||
4. Run the example
|
||||
|
||||
Run your bot using:
|
||||
|
||||
```bash
|
||||
python bot.py
|
||||
```
|
||||
|
||||
> Using `uv`? Run your bot using: `uv run bot.py`.
|
||||
|
||||
Connect to your bot in a browser at http://localhost:7860.
|
||||
|
||||
> 💡 First run note: The initial startup may take ~10 seconds as Pipecat downloads required models, like the Silero VAD model.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Browser permissions**: Make sure to allow microphone access when prompted by your browser.
|
||||
- **Connection issues**: If the WebRTC connection fails, first try a different browser. If that fails, make sure you don't have a VPN or firewall rules blocking traffic. WebRTC uses UDP to communicate.
|
||||
- **Audio issues**: Check that your microphone and speakers are working and not muted.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **Read the docs**: Check out [Pipecat's docs](https://docs.pipecat.ai/) for guides and reference information.
|
||||
- **Join Discord**: Join [Pipecat's Discord server](https://discord.gg/pipecat) to get help and learn about what others are building.
|
||||
102
examples/client-server-web/server/bot.py
Normal file
102
examples/client-server-web/server/bot.py
Normal file
@@ -0,0 +1,102 @@
|
||||
#
|
||||
# Copyright (c) 2024–2025, Daily
|
||||
#
|
||||
# SPDX-License-Identifier: BSD 2-Clause License
|
||||
#
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from loguru import logger
|
||||
|
||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||
from pipecat.pipeline.pipeline import Pipeline
|
||||
from pipecat.pipeline.runner import PipelineRunner
|
||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||
from pipecat.processors.frameworks.rtvi import RTVIConfig, RTVIObserver, RTVIProcessor
|
||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||
from pipecat.services.openai.llm import OpenAILLMService
|
||||
from pipecat.transports.base_transport import BaseTransport, TransportParams
|
||||
|
||||
load_dotenv(override=True)
|
||||
|
||||
|
||||
async def run_bot(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool):
|
||||
logger.info(f"Starting bot")
|
||||
|
||||
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||
|
||||
tts = CartesiaTTSService(
|
||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||
)
|
||||
|
||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"))
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a friendly AI assistant. Respond naturally and keep your answers conversational.",
|
||||
},
|
||||
]
|
||||
|
||||
context = OpenAILLMContext(messages)
|
||||
context_aggregator = llm.create_context_aggregator(context)
|
||||
|
||||
rtvi = RTVIProcessor(config=RTVIConfig(config=[]))
|
||||
|
||||
pipeline = Pipeline(
|
||||
[
|
||||
transport.input(), # Transport user input
|
||||
rtvi, # RTVI processor
|
||||
stt,
|
||||
context_aggregator.user(), # User responses
|
||||
llm, # LLM
|
||||
tts, # TTS
|
||||
transport.output(), # Transport bot output
|
||||
context_aggregator.assistant(), # Assistant spoken responses
|
||||
]
|
||||
)
|
||||
|
||||
task = PipelineTask(
|
||||
pipeline,
|
||||
params=PipelineParams(
|
||||
enable_metrics=True,
|
||||
enable_usage_metrics=True,
|
||||
),
|
||||
observers=[RTVIObserver(rtvi)],
|
||||
)
|
||||
|
||||
@transport.event_handler("on_client_connected")
|
||||
async def on_client_connected(transport, client):
|
||||
logger.info(f"Client connected")
|
||||
# Kick off the conversation.
|
||||
messages.append({"role": "system", "content": "Say hello and briefly introduce yourself."})
|
||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||
|
||||
@transport.event_handler("on_client_disconnected")
|
||||
async def on_client_disconnected(transport, client):
|
||||
logger.info(f"Client disconnected")
|
||||
await task.cancel()
|
||||
|
||||
runner = PipelineRunner(handle_sigint=handle_sigint)
|
||||
|
||||
await runner.run(task)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from pipecat.runner.local import main
|
||||
|
||||
# SmallWebRTCTransport for a P2P WebRTC connection
|
||||
transport_params = {
|
||||
"webrtc": lambda: TransportParams(
|
||||
audio_in_enabled=True,
|
||||
audio_out_enabled=True,
|
||||
vad_analyzer=SileroVADAnalyzer(),
|
||||
),
|
||||
}
|
||||
|
||||
main(run_bot, transport_params=transport_params)
|
||||
3
examples/client-server-web/server/env.example
Normal file
3
examples/client-server-web/server/env.example
Normal file
@@ -0,0 +1,3 @@
|
||||
DEEPGRAM_API_KEY=your_deepgram_api_key
|
||||
OPENAI_API_KEY=your_openai_api_key
|
||||
CARTESIA_API_KEY=your_cartesia_api_key
|
||||
1
examples/client-server-web/server/requirements.txt
Normal file
1
examples/client-server-web/server/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
pipecat-ai[webrtc,silero,deepgram,openai,cartesia,runner]
|
||||
@@ -4,6 +4,23 @@
|
||||
# SPDX-License-Identifier: BSD 2-Clause License
|
||||
#
|
||||
|
||||
"""Pipecat Quickstart Example.
|
||||
|
||||
The example runs a simple voice AI bot that you can connect to using your
|
||||
browser and speak with it.
|
||||
|
||||
Required AI services:
|
||||
- Deepgram (Speech-to-Text)
|
||||
- OpenAI (LLM)
|
||||
- Cartesia (Text-to-Speech)
|
||||
|
||||
The example connects between client and server using a P2P WebRTC connection.
|
||||
|
||||
Run the bot using::
|
||||
|
||||
python bot.py
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
|
||||
Reference in New Issue
Block a user