demo mount to fastapi
This commit is contained in:
15
README.md
15
README.md
@@ -25,7 +25,7 @@ uv venv .venv
|
||||
source .venv/bin/activate
|
||||
uv pip install -r requirements.txt
|
||||
export OPENAI_API_KEY=...
|
||||
uv run uvicorn engine.main:app --reload --host 0.0.0.0 --port 8001
|
||||
uv run uvicorn engine.main:app --reload --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
Or pass keys directly in `config.json`.
|
||||
@@ -34,12 +34,21 @@ Or pass keys directly in `config.json`.
|
||||
uv run python -m engine.main --config ./config.json
|
||||
```
|
||||
|
||||
Browser demo (served from the same process when `server.serve_webpage` is
|
||||
true in `config.json`):
|
||||
|
||||
```text
|
||||
http://localhost:8000/demo/
|
||||
```
|
||||
|
||||
See `examples/webpage/README.md` for details.
|
||||
|
||||
## Protocols
|
||||
|
||||
Pipecat-native endpoint:
|
||||
|
||||
```text
|
||||
ws://localhost:8001/ws
|
||||
ws://localhost:8000/ws
|
||||
```
|
||||
|
||||
The websocket payloads are Pipecat protobuf frames. A client should use Pipecat's websocket client/serializer stack or generate frames compatible with `pipecat.frames.protobufs.frames_pb2`.
|
||||
@@ -54,7 +63,7 @@ Important defaults:
|
||||
Product endpoint:
|
||||
|
||||
```text
|
||||
ws://localhost:8001/ws-product
|
||||
ws://localhost:8000/ws-product
|
||||
```
|
||||
|
||||
This endpoint uses a stable JSON/base64 protocol named `va.ws.v1`. It is meant for browser, mobile, or other product applications that should not depend on Pipecat's internal protobuf frame schema.
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
{
|
||||
"server": {
|
||||
"host": "0.0.0.0",
|
||||
"port": 8001,
|
||||
"cors_origins": ["http://localhost:3000", "http://localhost:8080"]
|
||||
"port": 8000,
|
||||
"cors_origins": ["http://localhost:3000", "http://localhost:8080"],
|
||||
"serve_webpage": true,
|
||||
"webpage_mount": "/demo"
|
||||
},
|
||||
"audio": {
|
||||
"sample_rate_hz": 16000,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"server": {
|
||||
"host": "0.0.0.0",
|
||||
"port": 8001,
|
||||
"port": 8000,
|
||||
"cors_origins": ["*"]
|
||||
},
|
||||
"audio": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"server": {
|
||||
"host": "0.0.0.0",
|
||||
"port": 8001,
|
||||
"port": 8000,
|
||||
"cors_origins": ["*"]
|
||||
},
|
||||
"audio": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"server": {
|
||||
"host": "0.0.0.0",
|
||||
"port": 8001,
|
||||
"port": 8000,
|
||||
"cors_origins": ["*"]
|
||||
},
|
||||
"audio": {
|
||||
|
||||
@@ -10,6 +10,8 @@ class ServerConfig:
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 8000
|
||||
cors_origins: list[str] = field(default_factory=list)
|
||||
serve_webpage: bool = True
|
||||
webpage_mount: str = "/demo"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
@@ -2,19 +2,30 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI, WebSocket
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from .config import EngineConfig, load_config
|
||||
from .pipeline import run_product_voice_pipeline, run_voice_pipeline
|
||||
|
||||
WEBPAGE_DIR = Path(__file__).resolve().parent.parent / "examples" / "webpage"
|
||||
|
||||
|
||||
@lru_cache(maxsize=8)
|
||||
def get_config(path: str = "config.json") -> EngineConfig:
|
||||
return load_config(path)
|
||||
|
||||
|
||||
def _normalize_mount_path(path: str) -> str:
|
||||
normalized = path.strip() or "/demo"
|
||||
if not normalized.startswith("/"):
|
||||
normalized = f"/{normalized}"
|
||||
return normalized.rstrip("/") or "/"
|
||||
|
||||
|
||||
def create_app(config_path: str = "config.json") -> FastAPI:
|
||||
config = get_config(config_path)
|
||||
app = FastAPI(title="AI VideoAssistant Engine v5 Pipecat Minimal", version="0.1.0")
|
||||
@@ -28,6 +39,12 @@ def create_app(config_path: str = "config.json") -> FastAPI:
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
webpage_mount = (
|
||||
_normalize_mount_path(config.server.webpage_mount)
|
||||
if config.server.serve_webpage
|
||||
else None
|
||||
)
|
||||
|
||||
@app.get("/health")
|
||||
async def health() -> dict[str, object]:
|
||||
return {
|
||||
@@ -40,6 +57,7 @@ def create_app(config_path: str = "config.json") -> FastAPI:
|
||||
"product_text_input": True,
|
||||
"product_text_interrupt": True,
|
||||
},
|
||||
"demo": webpage_mount,
|
||||
"llm_provider": config.services.llm.provider,
|
||||
"stt_provider": config.services.stt.provider,
|
||||
"tts_provider": config.services.tts.provider,
|
||||
@@ -55,6 +73,13 @@ def create_app(config_path: str = "config.json") -> FastAPI:
|
||||
await websocket.accept()
|
||||
await run_product_voice_pipeline(websocket, config)
|
||||
|
||||
if config.server.serve_webpage and WEBPAGE_DIR.is_dir() and webpage_mount:
|
||||
app.mount(
|
||||
webpage_mount,
|
||||
StaticFiles(directory=str(WEBPAGE_DIR), html=True),
|
||||
name="webpage",
|
||||
)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
|
||||
@@ -39,29 +39,48 @@ examples/webpage/
|
||||
|
||||
## Run
|
||||
|
||||
1. Start the engine (default port `8001`):
|
||||
1. Start the engine (default port `8000`):
|
||||
|
||||
```bash
|
||||
cd AI-VideoAssistant-engine-v5-pipecat-minimal
|
||||
source .venv/bin/activate
|
||||
export OPENAI_API_KEY=...
|
||||
uvicorn engine.main:app --host 127.0.0.1 --port 8001
|
||||
uvicorn engine.main:app --host 127.0.0.1 --port 8000
|
||||
```
|
||||
|
||||
2. In another terminal, serve the page from a port that's on the
|
||||
engine's CORS allow-list (see `config.json`). The default config
|
||||
allows `http://localhost:8080`:
|
||||
2. Open the demo page served by the same process:
|
||||
|
||||
```bash
|
||||
cd AI-VideoAssistant-engine-v5-pipecat-minimal/examples/webpage
|
||||
python -m http.server 8080
|
||||
```text
|
||||
http://127.0.0.1:8000/demo/
|
||||
```
|
||||
|
||||
3. Open <http://localhost:8080> in Chrome, Edge, or Safari.
|
||||
- Click **Connect** (uses `ws://127.0.0.1:8001/ws-product` by default).
|
||||
- Pick a microphone if needed, click **Enable mic**, and start
|
||||
speaking. The browser will prompt for microphone access on first
|
||||
use. Device names may appear only after permission is granted.
|
||||
The default websocket URL is derived from the page host
|
||||
(`ws://127.0.0.1:8000/ws-product`). Click **Connect**, pick a
|
||||
microphone if needed, click **Enable mic**, and start speaking.
|
||||
|
||||
Mount path and on/off are controlled in `config.json`:
|
||||
|
||||
```json
|
||||
"server": {
|
||||
"serve_webpage": true,
|
||||
"webpage_mount": "/demo"
|
||||
}
|
||||
```
|
||||
|
||||
Set `"serve_webpage": false` in production if you serve the UI elsewhere.
|
||||
|
||||
### Standalone static server (optional)
|
||||
|
||||
You can still serve the files from another port for UI-only iteration.
|
||||
Add that origin to `server.cors_origins` in `config.json` if needed:
|
||||
|
||||
```bash
|
||||
cd AI-VideoAssistant-engine-v5-pipecat-minimal/examples/webpage
|
||||
python -m http.server 8080
|
||||
```
|
||||
|
||||
Then open <http://localhost:8080> and point the URL field at
|
||||
`ws://127.0.0.1:8000/ws-product`.
|
||||
|
||||
> The browser's mic API requires a secure context. `http://localhost`
|
||||
> qualifies; if you serve from another host, use HTTPS and a `wss://`
|
||||
|
||||
@@ -18,6 +18,11 @@ const PROTOCOL = "va.ws.v1";
|
||||
const MAX_WS_LOG_LINES = 120;
|
||||
const AUDIO_DELTA_LOG_INTERVAL_MS = 1000;
|
||||
|
||||
function defaultWsUrl() {
|
||||
const scheme = location.protocol === "https:" ? "wss:" : "ws:";
|
||||
return `${scheme}//${location.host}/ws-product`;
|
||||
}
|
||||
|
||||
const els = {
|
||||
url: document.getElementById("ws-url"),
|
||||
connectBtn: document.getElementById("connect-btn"),
|
||||
@@ -885,6 +890,8 @@ window.addEventListener("beforeunload", () => {
|
||||
}
|
||||
});
|
||||
|
||||
els.url.value = defaultWsUrl();
|
||||
|
||||
setStatus("idle", "Disconnected");
|
||||
setConnectButton();
|
||||
setMicButton();
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<input
|
||||
id="ws-url"
|
||||
type="text"
|
||||
value="ws://127.0.0.1:8001/ws-product"
|
||||
placeholder="ws://host/ws-product"
|
||||
spellcheck="false"
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user