Initial commit: AI Video Assistant fullstack platform.
Add pipecat-based backend with WebRTC/WS voice routes, Next.js frontend, and Docker Compose orchestration. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
50
backend/routes/voice_ws.py
Normal file
50
backend/routes/voice_ws.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""WS 输出:裸 WebSocket 音频流(第二种输出方式)。
|
||||
|
||||
比 WebRTC 简单——没有 SDP/ICE/STUN/TURN,一条 WS 直接收发音频帧。
|
||||
适合:服务端对接、话务网关、自定义客户端、调试。
|
||||
|
||||
约定:连接建立后,**第一条文本消息**发 JSON 启动参数:
|
||||
{"assistant_id": "asst_xxx"} # 推荐:key 服务端解析
|
||||
{"inline_config": {...AssistantConfig}} # 调试:内联
|
||||
之后的二进制消息即音频帧(protobuf,与 transports.py serializer 对应)。
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from db.session import SessionLocal
|
||||
from fastapi import APIRouter, WebSocket
|
||||
from loguru import logger
|
||||
from models import AssistantConfig
|
||||
from services.config_resolver import resolve_runtime_config
|
||||
from starlette.websockets import WebSocketDisconnect
|
||||
|
||||
# pipecat 重依赖,惰性导入(见 voice_webrtc.py 说明)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
async def _resolve_start_config(raw: str) -> AssistantConfig:
|
||||
data = json.loads(raw)
|
||||
if data.get("assistant_id"):
|
||||
async with SessionLocal() as session:
|
||||
return await resolve_runtime_config(session, data["assistant_id"])
|
||||
if data.get("inline_config"):
|
||||
return AssistantConfig(**data["inline_config"])
|
||||
raise ValueError("启动参数缺少 assistant_id 或 inline_config")
|
||||
|
||||
|
||||
@router.websocket("/ws/stream")
|
||||
async def voice_stream(websocket: WebSocket):
|
||||
from services.pipecat.pipeline import run_pipeline
|
||||
from services.pipecat.transports import build_ws_transport
|
||||
|
||||
await websocket.accept()
|
||||
try:
|
||||
cfg = await _resolve_start_config(await websocket.receive_text())
|
||||
transport = build_ws_transport(websocket)
|
||||
# 直接 await:管线持续读这条 WS 的音频帧,直到对端断开
|
||||
await run_pipeline(transport, cfg)
|
||||
except WebSocketDisconnect:
|
||||
logger.info("WS 音频流断开")
|
||||
except Exception as e:
|
||||
logger.error(f"WS 音频流出错: {e}")
|
||||
Reference in New Issue
Block a user