feat: add FastAPI token server to pipecat-agent

Adds a lightweight HTTP token server (port 8080) alongside the voice
pipeline so web clients can obtain LiveKit access tokens without a
separate service. Adds fastapi and uvicorn to requirements. Adds
LIVEKIT_PUBLIC_URL env var (wss://voice.hobbs.farm) to docker-compose
so tokens carry the correct public WebSocket URL for browser clients.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-01 01:26:07 -05:00
parent 59856bd4fc
commit 68c3731c65
3 changed files with 59 additions and 8 deletions
+1
View File
@@ -84,6 +84,7 @@ services:
- SPEECHBRAIN_SERVICE_URL=http://speechbrain-service:8001 - SPEECHBRAIN_SERVICE_URL=http://speechbrain-service:8001
- PIPER_SERVICE_URL=http://piper-tts:8003 - PIPER_SERVICE_URL=http://piper-tts:8003
- WHISPER_MODEL=medium - WHISPER_MODEL=medium
- LIVEKIT_PUBLIC_URL=wss://voice.hobbs.farm
depends_on: depends_on:
- livekit-server - livekit-server
- speechbrain-service - speechbrain-service
+56 -8
View File
@@ -1,12 +1,16 @@
""" """
Entry point for the Pipecat VoiceCoach agent. Entry point for the Pipecat VoiceCoach agent.
Reads config from environment variables. Runs both the token HTTP server (for web clients) and the voice pipeline.
""" """
import asyncio import asyncio
import logging import logging
import os import os
import threading
import structlog import structlog
import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel
from livekit import api as livekit_api from livekit import api as livekit_api
from pipeline import run_voice_coach_pipeline from pipeline import run_voice_coach_pipeline
@@ -14,15 +18,59 @@ from pipeline import run_voice_coach_pipeline
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
logger = structlog.get_logger() logger = structlog.get_logger()
LIVEKIT_API_KEY = os.environ["LIVEKIT_API_KEY"]
LIVEKIT_API_SECRET = os.environ["LIVEKIT_API_SECRET"]
LIVEKIT_WS_URL = os.getenv("LIVEKIT_URL", "ws://livekit-server:7880")
LIVEKIT_PUBLIC_URL = os.getenv("LIVEKIT_PUBLIC_URL", "wss://voice.hobbs.farm")
# --- Token HTTP server (serves /token for web clients) ---
token_app = FastAPI(title="VoiceCoach Token Service")
class TokenRequest(BaseModel):
identity: str = "jeremy"
class TokenResponse(BaseModel):
token: str
url: str
@token_app.get("/health")
async def health():
return {"status": "ok"}
@token_app.post("/token")
async def get_token(req: TokenRequest):
token = livekit_api.AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET)
token.with_grants(livekit_api.VideoGrants(
room_join=True,
room="voicecoach",
can_publish=True,
can_subscribe=True,
))
token.identity = req.identity
return TokenResponse(token=token.to_jwt(), url=LIVEKIT_PUBLIC_URL)
def run_token_server():
uvicorn.run(token_app, host="0.0.0.0", port=8080, log_level="info")
# --- Main pipeline ---
async def main(): async def main():
anthropic_key = os.environ["ANTHROPIC_API_KEY"] anthropic_key = os.environ["ANTHROPIC_API_KEY"]
livekit_api_key = os.environ["LIVEKIT_API_KEY"]
livekit_api_secret = os.environ["LIVEKIT_API_SECRET"]
livekit_url = os.getenv("LIVEKIT_URL", "ws://livekit-server:7880")
# Generate LiveKit access token for this agent # Start token server in a background thread
token = livekit_api.AccessToken(livekit_api_key, livekit_api_secret) server_thread = threading.Thread(target=run_token_server, daemon=True)
server_thread.start()
logger.info("Token server started on :8080")
# Generate agent's own LiveKit token
token = livekit_api.AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET)
token.with_grants(livekit_api.VideoGrants(room_join=True, room="voicecoach")) token.with_grants(livekit_api.VideoGrants(room_join=True, room="voicecoach"))
token.identity = "voicecoach-agent" token.identity = "voicecoach-agent"
livekit_token = token.to_jwt() livekit_token = token.to_jwt()
@@ -33,9 +81,9 @@ async def main():
"current_mode": "coaching mode", "current_mode": "coaching mode",
} }
logger.info("Starting VoiceCoach pipeline", livekit_url=livekit_url) logger.info("Starting VoiceCoach pipeline", livekit_url=LIVEKIT_WS_URL)
await run_voice_coach_pipeline( await run_voice_coach_pipeline(
livekit_url=livekit_url, livekit_url=LIVEKIT_WS_URL,
livekit_token=livekit_token, livekit_token=livekit_token,
anthropic_key=anthropic_key, anthropic_key=anthropic_key,
session_context=session_context, session_context=session_context,
+2
View File
@@ -1,4 +1,6 @@
pipecat-ai[livekit,silero,openai]==0.0.48 pipecat-ai[livekit,silero,openai]==0.0.48
fastapi==0.115.0
uvicorn[standard]==0.30.0
anthropic==0.34.0 anthropic==0.34.0
faster-whisper==1.0.3 faster-whisper==1.0.3
requests==2.32.3 requests==2.32.3