Browse Source

fix(gateway): route insert_speech and speech_insert payloads to Mac clients without drop

main
Ali Alavi 1 day ago
parent
commit
46a5362bf8
  1. 23
      server/relay_server.py

23
server/relay_server.py

@ -1,10 +1,8 @@
#!/usr/bin/env python3
"""
Soniox Collaborative Bi-Directional Input Synchronization Gateway (v5.2)
- Real-time Google Docs / Figma style collaborative mirroring between Mac and Android.
- Ultra-low latency streaming speech recognition with pre-warmed Soniox pool.
- Monotonic revision counters, echo-loop suppression, and sub-15ms WebSocket routing.
- Process-targeted cursor-aware voice insertion.
Soniox Collaborative Bi-Directional Input Synchronization Gateway (v5.3)
- Handles all message types: sync_state, insert_speech, speech_insert, update_input, paste.
- Sub-15ms WebSocket routing between Android and Mac.
"""
import asyncio
@ -125,6 +123,10 @@ async def broadcast_state(payload_dict: dict, exclude_ws=None):
current_room_state.update(payload_dict)
payload_str = json.dumps(payload_dict, ensure_ascii=False)
logger.info("📡 Broadcasting %s (len: %d) to %d Macs, %d Phones",
payload_dict.get("type"), len(payload_dict.get("text", "")),
len(connected_mac_websockets), len(connected_phone_websockets))
# 1. Send to Phone clients
dead_phones = set()
for ws in list(connected_phone_websockets):
@ -239,10 +241,10 @@ async def handle_phone_stream_ws(request):
msg_type = data.get("type") or data.get("action")
sid = data.get("session_id", f"sess_{int(time.time()*1000)}")
if msg_type == "sync_state" or msg_type == "phone_input_edit" or msg_type == "update_input":
# Phone edited text: broadcast to Mac immediately!
# ALL text / sync / insert operations must be broadcast to Mac!
if msg_type in ("sync_state", "insert_speech", "speech_insert", "phone_input_edit", "update_input", "paste"):
data["source"] = "android"
data["type"] = "sync_state"
data["type"] = msg_type
await broadcast_state(data, exclude_ws=ws)
elif msg_type == "start":
@ -265,7 +267,6 @@ async def handle_phone_stream_ws(request):
if active_soniox_ws and is_ws_open(active_soniox_ws):
await active_soniox_ws.send(json.dumps({"type": "finalize"}))
try:
# 350ms fast timeout for finalize
await asyncio.wait_for(stop_event.wait(), timeout=0.35)
except asyncio.TimeoutError:
pass
@ -350,7 +351,7 @@ async def handle_health(request):
state_copy = dict(current_room_state)
return web.json_response({
"status": "ok",
"service": "Soniox Collaborative Sync Gateway v5.2",
"service": "Soniox Collaborative Sync Gateway v5.3",
"connected_macs": len(connected_mac_websockets),
"connected_phones": len(connected_phone_websockets),
"current_app": state_copy.get("app", ""),
@ -364,7 +365,7 @@ async def handle_paste(request):
text = data.get("text", "")
cursor = data.get("cursor_pos") or data.get("cursor")
data["source"] = "http_post"
data["type"] = "sync_state"
data["type"] = "update_input"
await broadcast_state(data)
return web.json_response({"status": "synced", "revision": current_room_state["revision"]})
except Exception as e:

Loading…
Cancel
Save