| 196 | |
| 197 | |
| 198 | class ServerState: |
| 199 | def __init__(self, model_manager: GlobalModelManager, sample_rate: int = 24000, output_dir: str = "./output", tts_gpu: int = 1): |
| 200 | self.model_manager = model_manager |
| 201 | self.sample_rate = sample_rate |
| 202 | self.output_dir = output_dir |
| 203 | self.tts_gpu = tts_gpu |
| 204 | self.lock = asyncio.Lock() |
| 205 | |
| 206 | self.template = AUDIO_TEMPLATE |
| 207 | self.APAD_TOKEN = AUDIO_PAD_TOKEN |
| 208 | self.token_fps = TOKEN_FPS |
| 209 | self.system_prompt = SPOKEN_S2M_PROMPT |
| 210 | |
| 211 | os.makedirs(self.output_dir, exist_ok=True) |
| 212 | os.makedirs(os.path.join(self.output_dir, "input"), exist_ok=True) |
| 213 | log("info", f"Output directory: {self.output_dir}") |
| 214 | |
| 215 | # global TTS queue |
| 216 | self.tts_input_queue = MPQueue() |
| 217 | self.tts_output_queue = MPQueue() |
| 218 | self.tts_control_queue = MPQueue() |
| 219 | |
| 220 | self.tts_process = Process( |
| 221 | target=tts_worker_process, |
| 222 | args=(self.tts_input_queue, self.tts_output_queue, self.tts_control_queue, self.tts_gpu), |
| 223 | daemon=True |
| 224 | ) |
| 225 | self.tts_process.start() |
| 226 | log("info", f"Global TTS process started (pid: {self.tts_process.pid})") |
| 227 | |
| 228 | def stop_tts_process(self): |
| 229 | if self.tts_process and self.tts_process.is_alive(): |
| 230 | self.tts_control_queue.put(('stop', None)) |
| 231 | self.tts_process.join(timeout=5.0) |
| 232 | if self.tts_process.is_alive(): |
| 233 | log("warning", "TTS process did not stop in time, terminating...") |
| 234 | self.tts_process.terminate() |
| 235 | log("info", "Global TTS process stopped") |
| 236 | |
| 237 | async def handle_chat(self, request): |
| 238 | # Set heartbeat interval to 30 seconds and timeout to 60 seconds to prevent connections from being disconnected by intermediate proxies/firewalls |
| 239 | ws = web.WebSocketResponse(heartbeat=30.0, receive_timeout=None) |
| 240 | await ws.prepare(request) |
| 241 | |
| 242 | client_id = f"Client-{id(ws)}" |
| 243 | turn_counter = 0 |
| 244 | is_recording = True |
| 245 | |
| 246 | # system_prompt for current session |
| 247 | session_system_prompt = self.system_prompt |
| 248 | |
| 249 | # history |
| 250 | messages = [] |
| 251 | audio_list = [] |
| 252 | |
| 253 | async def recv_loop(): |
| 254 | nonlocal close, is_recording, turn_counter, opus_reader |
| 255 | try: |