Voice AI Features: - LiveKit Agents integration for real-time voice calls - Julia AI agent (Python) deployed to LiveKit Cloud - Token server for authentication - Debug screen with voice call testing - Voice call screen with full-screen UI Agent Configuration: - STT: Deepgram Nova-2 - LLM: OpenAI GPT-4o - TTS: Deepgram Aura Asteria (female voice) - Turn Detection: LiveKit Multilingual Model - VAD: Silero - Noise Cancellation: LiveKit BVC Files added: - julia-agent/ - Complete agent code and token server - app/voice-call.tsx - Full-screen voice call UI - services/livekitService.ts - LiveKit client service - contexts/VoiceTranscriptContext.tsx - Transcript state - polyfills/livekit-globals.ts - WebRTC polyfills 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
142 lines
4.8 KiB
Python
142 lines
4.8 KiB
Python
"""
|
|
WellNuo Voice Agent - Julia AI
|
|
LiveKit Agents Cloud deployment
|
|
Uses Deepgram STT/TTS + OpenAI GPT-4o LLM
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
from dotenv import load_dotenv
|
|
|
|
from livekit import agents
|
|
from livekit.agents import Agent, AgentSession, RoomEventHandler
|
|
from livekit.plugins import deepgram, openai, silero
|
|
|
|
load_dotenv(".env.local")
|
|
|
|
# Ferdinand data for demo (in production, fetch from API)
|
|
FERDINAND_DATA = {
|
|
"client": {
|
|
"name": "Ferdinand Zmrzli",
|
|
"address": "661 Encore Way"
|
|
},
|
|
"today_alerts": [
|
|
{"type": "fall_detected", "time": "06:32", "severity": "critical", "location": "bathroom"},
|
|
{"type": "short_sleep", "time": "06:30", "severity": "high", "note": "Only 5 hours sleep (normal: 7-8)"},
|
|
{"type": "missed_medication", "time": "08:30", "severity": "high", "note": "Morning medication not taken"}
|
|
],
|
|
"yesterday_alerts": [
|
|
{"type": "high_bathroom_frequency", "time": "15:00", "severity": "medium", "note": "8 visits (normal: 5-6)"}
|
|
],
|
|
"summary": {
|
|
"total_alerts_7days": 12,
|
|
"critical": 2,
|
|
"high": 4,
|
|
"medium": 4,
|
|
"low": 2
|
|
}
|
|
}
|
|
|
|
|
|
def build_system_prompt() -> str:
|
|
"""Build Julia AI system prompt with Ferdinand context"""
|
|
|
|
client = FERDINAND_DATA["client"]
|
|
alerts = FERDINAND_DATA["today_alerts"]
|
|
has_critical = any(a["severity"] in ["critical", "high"] for a in alerts)
|
|
|
|
alerts_text = ""
|
|
for alert in alerts:
|
|
emoji = "RED" if alert["severity"] == "critical" else "ORANGE" if alert["severity"] == "high" else "YELLOW"
|
|
alerts_text += f" [{emoji}] {alert['type'].replace('_', ' ').upper()} at {alert['time']}"
|
|
if alert.get("note"):
|
|
alerts_text += f" - {alert['note']}"
|
|
if alert.get("location"):
|
|
alerts_text += f" ({alert['location']})"
|
|
alerts_text += "\n"
|
|
|
|
return f"""You are Julia, a compassionate AI wellness assistant for WellNuo app.
|
|
You help caregivers monitor their loved ones' wellbeing.
|
|
|
|
CRITICAL: You are ALWAYS talking about {client['name']} (the beneficiary), NOT about yourself!
|
|
|
|
BENEFICIARY INFORMATION:
|
|
- Name: {client['name']}
|
|
- Address: {client['address']}
|
|
- Monitoring Period: Last 7 days
|
|
|
|
TODAY'S ALERTS:
|
|
{alerts_text}
|
|
|
|
7-DAY SUMMARY:
|
|
- Total alerts: {FERDINAND_DATA['summary']['total_alerts_7days']}
|
|
- Critical: {FERDINAND_DATA['summary']['critical']}
|
|
- High: {FERDINAND_DATA['summary']['high']}
|
|
- Medium: {FERDINAND_DATA['summary']['medium']}
|
|
- Low: {FERDINAND_DATA['summary']['low']}
|
|
|
|
CONVERSATION RULES:
|
|
1. When user asks "how are you?" or "how's it going?" - ALWAYS respond about {client['name']}'s status, NOT about yourself as AI
|
|
- NEVER say "I'm doing well as an AI" - the user wants to know about their loved one!
|
|
|
|
2. When user asks "what's happening?" or "any updates?" - report {client['name']}'s current status and alerts
|
|
|
|
3. ALWAYS assume questions are about {client['name']} unless explicitly about app features
|
|
|
|
RESPONSE STYLE - BE CONCISE, NOT PUSHY:
|
|
- DON'T overwhelm with information immediately
|
|
- First give a SHORT summary, then ASK if they want details
|
|
- Example opening: "Hi! {'I have some important updates about ' + client['name'] + '. Would you like to hear them?' if has_critical else client['name'] + ' is doing well today. Anything specific you would like to know?'}"
|
|
- Wait for user to ask before giving long explanations
|
|
- Keep initial responses to 1-2 sentences max
|
|
- Only elaborate when user asks "tell me more", "what happened?", etc.
|
|
|
|
BAD (too pushy): "Hi! Ferdinand had a fall at 6:32 AM in the bathroom, his sleep was only 5 hours, he missed his morning medication..."
|
|
GOOD (concise): "Hi! I have some concerns about {client['name']} today - there was an incident this morning. Want me to tell you more?"
|
|
|
|
You're speaking with a caregiver who cares deeply about {client['name']}."""
|
|
|
|
|
|
class JuliaAssistant(Agent):
|
|
"""Julia AI Voice Assistant for WellNuo"""
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__(
|
|
instructions=build_system_prompt(),
|
|
)
|
|
|
|
|
|
# Create the agent server
|
|
server = agents.AgentServer()
|
|
|
|
|
|
@server.rtc_session()
|
|
async def julia_session(ctx: agents.JobContext):
|
|
"""Main voice session handler"""
|
|
|
|
# Create the agent session with STT, LLM, TTS
|
|
session = AgentSession(
|
|
stt=deepgram.STT(model="nova-2"),
|
|
llm=openai.LLM(
|
|
model="gpt-4o",
|
|
api_key=os.getenv("OPENAI_API_KEY"),
|
|
),
|
|
tts=deepgram.TTS(model="aura-asteria-en"),
|
|
vad=silero.VAD.load(),
|
|
)
|
|
|
|
# Start the session
|
|
await session.start(
|
|
room=ctx.room,
|
|
agent=JuliaAssistant(),
|
|
)
|
|
|
|
# Generate initial greeting
|
|
await session.generate_reply(
|
|
instructions="Greet the user warmly. If there are critical alerts, mention you have important updates. Keep it brief - 1 sentence max."
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
agents.cli.run_app(server)
|