diff --git a/.playwright-mcp/console-2026-08-08T09-11-01-487Z.log b/.playwright-mcp/console-2026-08-08T09-11-01-487Z.log index a98b051..ade2be7 100644 --- a/.playwright-mcp/console-2026-08-08T09-11-01-487Z.log +++ b/.playwright-mcp/console-2026-08-08T09-11-01-487Z.log @@ -95,3 +95,4 @@ [ 183147ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 [ 188179ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 [ 192874ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 +[ 1348483ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8000/:40 diff --git a/chat_map.png b/chat_map.png index 99ba7b7..6869f7e 100644 Binary files a/chat_map.png and b/chat_map.png differ diff --git a/testbed/bridge.py b/testbed/bridge.py index e409aa1..2a3653f 100644 --- a/testbed/bridge.py +++ b/testbed/bridge.py @@ -11,6 +11,7 @@ from __future__ import annotations import asyncio import base64 +import copy import io import math from typing import Any @@ -206,11 +207,18 @@ class RoomBridge(Bridge): ) async def vision() -> VisionOutput: tick = self._tick() - img, _ = renderer.render(world) - buf = io.BytesIO() - img.save(buf, format="PNG") + # Render off the event loop so frames never stall the bridge under + # load; deepcopy keeps the snapshot consistent with this tool call. + snapshot = copy.deepcopy(world) + img, _ = await asyncio.to_thread(renderer.render, snapshot) + + def _encode() -> str: + buf = io.BytesIO() + img.save(buf, format="PNG") + return base64.b64encode(buf.getvalue()).decode("ascii") + return VisionOutput( - png_b64=base64.b64encode(buf.getvalue()).decode("ascii"), + png_b64=await asyncio.to_thread(_encode), width=VISION_WIDTH, height=VISION_HEIGHT, tick=tick, @@ -222,7 +230,8 @@ class RoomBridge(Bridge): ) async def depth(max_depth: float = 10.0) -> DepthOutput: tick = self._tick() - _, grid = renderer.render(world, max_depth=max_depth) + snapshot = copy.deepcopy(world) + _, grid = await asyncio.to_thread(renderer.render, snapshot, max_depth) return DepthOutput( width=DEPTH_WIDTH, height=DEPTH_HEIGHT, diff --git a/testbed/chat.py b/testbed/chat.py index 032813a..844db2e 100644 --- a/testbed/chat.py +++ b/testbed/chat.py @@ -191,12 +191,34 @@ async def chat_loop(client: AICCClient, manifest, args: argparse.Namespace) -> i async def run(args: argparse.Namespace) -> int: - transport = WebSocketClientTransport(args.url) - async with AICCClient(transport) as client: - manifest = await client.handshake() - print(f"[handshake] session {manifest.session_id} world {manifest.world.name}") - print(f"[handshake] tools: {[t.id for t in manifest.tools]}") - return await chat_loop(client, manifest, args) + """Run the chat session, reconnecting to the bridge if the connection dies.""" + attempts = 0 + while True: + try: + transport = WebSocketClientTransport(args.url) + async with AICCClient(transport) as client: + manifest = await client.handshake() + print( + f"[handshake] session {manifest.session_id} world {manifest.world.name}" + ) + print(f"[handshake] tools: {[t.id for t in manifest.tools]}") + return await chat_loop(client, manifest, args) + except asyncio.CancelledError: + raise + except KeyboardInterrupt: + raise + except Exception as exc: # noqa: BLE001 - connection lost: reconnect + attempts += 1 + print(f"\n[chat] connection lost ({type(exc).__name__}: {exc})") + if attempts >= 3: + print( + "[chat] giving up after 3 attempts — is the bridge running? (scripts/run_bridge.sh)" + ) + return 1 + print( + "[chat] reconnecting in 2 seconds… (the room keeps its state on the bridge)" + ) + await asyncio.sleep(2.0) def main() -> int: diff --git a/testbed/demo.py b/testbed/demo.py index d388f5f..f44bfe8 100644 --- a/testbed/demo.py +++ b/testbed/demo.py @@ -152,7 +152,9 @@ async def run_llm_agent( log=lambda role, msg: print(f"[{role}] {msg}"), ) try: - return await run_llm_agent_loop(controller, max_steps, log=print_log, recorder=recorder) + return await run_llm_agent_loop( + controller, max_steps, log=print_log, recorder=recorder + ) except RuntimeError as exc: return {"steps": 0, "tool_calls": 0, "result": str(exc), "interacted": False} @@ -398,7 +400,14 @@ def main() -> int: "into this directory, plus demo.gif animation and demo_summary.png", ) args = parser.parse_args() - summary = asyncio.run(run_demo(args)) + try: + summary = asyncio.run(run_demo(args)) + except KeyboardInterrupt: + return 2 + except Exception as exc: # noqa: BLE001 - friendly failure instead of a traceback + print(f"\n[demo] failed: {type(exc).__name__}: {exc}") + print("[demo] is the bridge running? scripts/run_bridge.sh") + return 1 return 0 if summary.get("interacted") else 1