fix(realtime): don't kill the listen loop on receive() timeout

simple_websocket's receive(timeout=1) returns None when no frame arrives in
time; it does NOT mean the connection closed. The recv_loop treated None as a
disconnect, so any client that pauses sending for >1s (i.e. any silence longer
than one second, or a mic that only transmits while speaking) permanently
stopped the session from hearing the user -- barge-in and the next turn both
went dead while generation kept streaming out.

Also default --device to MPS when CUDA is unavailable, so CPU-only Mac paths
stop falling back to cpu.
pull/24/head
RichardToo 2026-10-15 21:55:21 +09:00
parent f900448c60
commit 346a8076e6
2 changed files with 7 additions and 4 deletions

View File

@ -99,7 +99,7 @@ def main():
parser.add_argument('--temperature', default=0.7, type=float, help="Thinker生成温度")
parser.add_argument('--top_p', default=0.85, type=float, help="nucleus采样阈值")
parser.add_argument('--output_dir', default='./output_audio/', type=str, help="输出音频保存目录")
parser.add_argument('--device', default='cuda' if torch.cuda.is_available() else 'cpu', type=str, help="运行设备")
parser.add_argument('--device', default='cuda' if torch.cuda.is_available() else ('mps' if torch.backends.mps.is_available() else 'cpu'), type=str, help="运行设备")
parser.add_argument('--audio_dir', default='./dataset/eval_omni/', type=str, help="测试音频目录")
parser.add_argument('--image_dir', default='./dataset/eval_omni/', type=str, help="测试图像目录")
parser.add_argument('--open_thinking', default=0, type=int, help="是否开启思考模式(0=否,1=是)(思考模式下禁用audio输出)")

View File

@ -383,9 +383,12 @@ def realtime(ws):
while alive[0]:
try:
data = ws.receive(timeout=1)
if data is None: alive[0] = False; break
except Exception:
alive[0] = False; break # 只有真断开/异常才退出
# flask_sock 超时返回 None,并不表示连接关闭:客户端只要暂停送帧 >1s
# (例如助手说话期间没人出声),旧写法会在这里误判断线并永久停止监听。
if data is not None:
q.put(data)
except: alive[0] = False; break
threading.Thread(target=recv_loop, daemon=True).start()
try:
@ -493,7 +496,7 @@ def init_model(args):
if __name__ == '__main__':
p = argparse.ArgumentParser()
p.add_argument('--load_from', default='./', help='模型权重搜索目录;目录下可放多个 HF 格式模型,WebUI 会自动扫描并允许切换。')
p.add_argument('--device', default='cuda' if torch.cuda.is_available() else 'cpu', help='推理设备;CUDA 可用时默认 cuda。显存不足或排查环境问题时可改为 cpu。')
p.add_argument('--device', default='cuda' if torch.cuda.is_available() else ('mps' if torch.backends.mps.is_available() else 'cpu'), help='推理设备;优先 CUDA,其次 Apple Silicon 的 MPS,否则 CPU。')
p.add_argument('--port', default=7860, type=int, help='WebUI 服务端口;端口被占用或需要同时启动多个实例时调整。')
p.add_argument('--audio_chunk_frames', default=4, type=int, help='流式播放每次解码的 Mimi frame 数;默认 4 约 320ms。WebUI 播放卡顿时可调大到 8/12,低延迟优先时保持 4。')
p.add_argument('--audio_overlap', default=2, type=int, help='分块 Mimi 解码的重叠帧数;默认 2 用于缓解块边界断裂。一般不需要调整,边界杂音明显时可适当增大。')