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