Sergei dde0ecb9cd Add Julia AI voice agent with LiveKit integration
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>
2026-01-17 17:58:31 -08:00

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)