Skip to content

Commit 0ba8df0

Browse files
committed
First successful deploy to cerebrium
1 parent 902d0ed commit 0ba8df0

13 files changed

Lines changed: 1030 additions & 92 deletions

.env.example

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@ VITE_GOOGLE_CLIENT_ID=your-actual-client-id.apps.googleusercontent.com
88
VITE_GMAIL_SCOPES=https://www.googleapis.com/auth/gmail.readonly https://www.googleapis.com/auth/gmail.send https://www.googleapis.com/auth/gmail.modify
99

1010
# Cerebrium API Configuration
11-
VITE_CEREBRIUM_API_KEY=your_cerebrium_api_key_here
11+
VITE_CEREBRIUM_API_KEY=your_cerebrium_inference_token_here
12+
VITE_CEREBRIUM_REST_API_KEY=your_cerebrium_session_token_here
1213

1314
# LiveKit Configuration
1415
VITE_LIVEKIT_URL=wss://your-livekit-server.com
1516
VITE_LIVEKIT_API_KEY=your_livekit_api_key
1617
VITE_LIVEKIT_API_SECRET=your_livekit_api_secret
1718

19+
# Voice Services (following Cerebrium guide)
20+
VITE_DEEPGRAM_API_KEY=your_deepgram_api_key
21+
VITE_OPENAI_API_KEY=your_openai_api_key
22+
1823
# Voice Agent Configuration
1924
VITE_VOICE_AGENT_URL=https://your-cerebrium-voice-agent.com/api

cerebrium/cerebrium.toml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
[cerebrium.deployment]
2+
name = "gmail-voice-assistant-allinone"
3+
python_version = "3.11"
4+
include = ["."]
5+
exclude = ["__pycache__", "*.pyc", ".git"]
6+
7+
[cerebrium.hardware]
8+
cpu = 4.0
9+
memory = 16.0
10+
compute = "AMPERE_A10"
11+
12+
[cerebrium.scaling]
13+
min_replicas = 1
14+
max_replicas = 3
15+
cooldown = 120
16+
17+
[cerebrium.build]
18+
dockerfile = false
19+
python_packages = "./requirements.txt"
20+
include = [".", "*.py"]
21+
22+
[cerebrium.predict]
23+
python_version = "3.11"
24+
gpu_count = 1
25+
setup_commands = [
26+
"apt-get update && apt-get install -y curl wget",
27+
"curl -fsSL https://ollama.ai/install.sh | sh"
28+
]
29+
30+
# Main entry point
31+
[cerebrium.app]
32+
main = "main.py"
33+
34+
[cerebrium.env]
35+
# Required API Keys (All-in-One Deployment)
36+
# DEEPGRAM_API_KEY = "your-deepgram-key" # For Nova-2 STT + Rime TTS
37+
# OPENAI_API_KEY = "your-openai-key" # For GPT-4o-mini LLM
38+
39+
# Internal service configuration (auto-configured)
40+
LIVEKIT_URL = "ws://localhost:7880"
41+
LIVEKIT_API_KEY = "devkey"
42+
LIVEKIT_API_SECRET = "secret"
43+
44+
# External access ports
45+
LIVEKIT_EXTERNAL_PORT = "7880"
46+
OLLAMA_EXTERNAL_PORT = "11434"
47+
48+
# Gmail Integration (optional)
49+
# GMAIL_CREDENTIALS_JSON = "your-service-account-json"

cerebrium/livekit_server.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Embedded LiveKit server for Cerebrium deployment.
4+
Runs LiveKit server alongside the voice agent on the same infrastructure.
5+
"""
6+
7+
import os
8+
import asyncio
9+
import logging
10+
import subprocess
11+
import time
12+
import signal
13+
import sys
14+
15+
logging.basicConfig(level=logging.INFO)
16+
logger = logging.getLogger(__name__)
17+
18+
class LiveKitServer:
19+
def __init__(self):
20+
self.process = None
21+
self.config_file = "/tmp/livekit.yaml"
22+
self.api_key = os.getenv("LIVEKIT_API_KEY", "devkey")
23+
self.api_secret = os.getenv("LIVEKIT_API_SECRET", "secret")
24+
25+
def create_config(self):
26+
"""Create LiveKit server configuration."""
27+
config = f"""
28+
port: 7880
29+
bind_addresses:
30+
- ""
31+
32+
rtc:
33+
tcp_port: 7881
34+
port_range_start: 50000
35+
port_range_end: 60000
36+
use_external_ip: false
37+
38+
redis: null
39+
40+
keys:
41+
{self.api_key}: {self.api_secret}
42+
43+
log_level: info
44+
45+
room:
46+
auto_create: true
47+
enable_recording: false
48+
49+
turn:
50+
enabled: false
51+
52+
webhook:
53+
api_key: {self.api_key}
54+
55+
development: true
56+
"""
57+
58+
with open(self.config_file, 'w') as f:
59+
f.write(config)
60+
61+
logger.info(f"Created LiveKit config at {self.config_file}")
62+
63+
def download_livekit_server(self):
64+
"""Download LiveKit server binary."""
65+
try:
66+
# Download LiveKit server binary
67+
download_cmd = [
68+
"curl", "-L",
69+
"https://github.com/livekit/livekit/releases/latest/download/livekit_linux_amd64",
70+
"-o", "/usr/local/bin/livekit-server"
71+
]
72+
73+
result = subprocess.run(download_cmd, capture_output=True, text=True)
74+
if result.returncode != 0:
75+
raise Exception(f"Failed to download LiveKit server: {result.stderr}")
76+
77+
# Make executable
78+
os.chmod("/usr/local/bin/livekit-server", 0o755)
79+
logger.info("Downloaded and installed LiveKit server")
80+
81+
except Exception as e:
82+
logger.error(f"Failed to setup LiveKit server: {e}")
83+
raise
84+
85+
async def start(self):
86+
"""Start LiveKit server."""
87+
try:
88+
# Create config file
89+
self.create_config()
90+
91+
# Download server if not exists
92+
if not os.path.exists("/usr/local/bin/livekit-server"):
93+
self.download_livekit_server()
94+
95+
# Start LiveKit server
96+
cmd = [
97+
"/usr/local/bin/livekit-server",
98+
"--config", self.config_file,
99+
"--bind", "0.0.0.0"
100+
]
101+
102+
logger.info("Starting LiveKit server...")
103+
self.process = subprocess.Popen(
104+
cmd,
105+
stdout=subprocess.PIPE,
106+
stderr=subprocess.PIPE,
107+
text=True
108+
)
109+
110+
# Wait for server to start
111+
await asyncio.sleep(5)
112+
113+
if self.process.poll() is None:
114+
logger.info("LiveKit server started successfully on port 7880")
115+
return True
116+
else:
117+
stdout, stderr = self.process.communicate()
118+
logger.error(f"LiveKit server failed to start: {stderr}")
119+
return False
120+
121+
except Exception as e:
122+
logger.error(f"Failed to start LiveKit server: {e}")
123+
return False
124+
125+
def stop(self):
126+
"""Stop LiveKit server."""
127+
if self.process:
128+
self.process.terminate()
129+
self.process.wait()
130+
logger.info("LiveKit server stopped")
131+
132+
def get_ws_url(self):
133+
"""Get WebSocket URL for internal connections."""
134+
return "ws://localhost:7880"
135+
136+
# Global server instance
137+
livekit_server = LiveKitServer()
138+
139+
async def start_livekit_server():
140+
"""Start LiveKit server and return the instance."""
141+
success = await livekit_server.start()
142+
if not success:
143+
raise Exception("Failed to start LiveKit server")
144+
return livekit_server
145+
146+
def signal_handler(signum, frame):
147+
"""Handle shutdown signals."""
148+
logger.info("Received shutdown signal")
149+
livekit_server.stop()
150+
sys.exit(0)
151+
152+
if __name__ == "__main__":
153+
# Register signal handlers
154+
signal.signal(signal.SIGINT, signal_handler)
155+
signal.signal(signal.SIGTERM, signal_handler)
156+
157+
# Start server
158+
asyncio.run(start_livekit_server())
159+
160+
# Keep running
161+
try:
162+
while True:
163+
time.sleep(1)
164+
except KeyboardInterrupt:
165+
livekit_server.stop()

0 commit comments

Comments
 (0)