forked from Rockachopa/Timmy-time-dashboard
This commit is contained in:
@@ -151,14 +151,8 @@ async def world_ws(websocket: WebSocket) -> None:
|
||||
logger.info("World WS disconnected — %d clients", len(_ws_clients))
|
||||
|
||||
|
||||
async def broadcast_world_state(presence: dict) -> None:
|
||||
"""Broadcast a ``timmy_state`` message to all connected Workshop clients.
|
||||
|
||||
Called by :class:`~timmy.workshop_state.WorkshopHeartbeat` via its
|
||||
``on_change`` callback.
|
||||
"""
|
||||
state = _build_world_state(presence)
|
||||
message = json.dumps({"type": "timmy_state", **state["timmyState"]})
|
||||
async def _broadcast(message: str) -> None:
|
||||
"""Send *message* to every connected Workshop client, pruning dead ones."""
|
||||
dead: list[WebSocket] = []
|
||||
for ws in _ws_clients:
|
||||
try:
|
||||
@@ -170,6 +164,16 @@ async def broadcast_world_state(presence: dict) -> None:
|
||||
_ws_clients.remove(ws)
|
||||
|
||||
|
||||
async def broadcast_world_state(presence: dict) -> None:
|
||||
"""Broadcast a ``timmy_state`` message to all connected Workshop clients.
|
||||
|
||||
Called by :class:`~timmy.workshop_state.WorkshopHeartbeat` via its
|
||||
``on_change`` callback.
|
||||
"""
|
||||
state = _build_world_state(presence)
|
||||
await _broadcast(json.dumps({"type": "timmy_state", **state["timmyState"]}))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Visitor chat — bark engine
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -185,26 +189,35 @@ async def _handle_client_message(raw: str) -> None:
|
||||
if data.get("type") == "visitor_message":
|
||||
text = (data.get("text") or "").strip()
|
||||
if text:
|
||||
asyncio.create_task(_bark_and_broadcast(text))
|
||||
task = asyncio.create_task(_bark_and_broadcast(text))
|
||||
task.add_done_callback(_log_bark_failure)
|
||||
|
||||
|
||||
def _log_bark_failure(task: asyncio.Task) -> None:
|
||||
"""Log unhandled exceptions from fire-and-forget bark tasks."""
|
||||
if task.cancelled():
|
||||
return
|
||||
exc = task.exception()
|
||||
if exc is not None:
|
||||
logger.error("Bark task failed: %s", exc)
|
||||
|
||||
|
||||
async def _bark_and_broadcast(visitor_text: str) -> None:
|
||||
"""Generate a bark response and broadcast it to all Workshop clients."""
|
||||
# Signal "thinking" state
|
||||
await _broadcast_speech({"type": "timmy_thinking"})
|
||||
await _broadcast(json.dumps({"type": "timmy_thinking"}))
|
||||
|
||||
reply = await _generate_bark(visitor_text)
|
||||
|
||||
# Store exchange in conversation buffer
|
||||
_conversation.append({"visitor": visitor_text, "timmy": reply})
|
||||
|
||||
# Broadcast speech bubble + conversation history
|
||||
await _broadcast_speech(
|
||||
{
|
||||
"type": "timmy_speech",
|
||||
"text": reply,
|
||||
"recentExchanges": list(_conversation),
|
||||
}
|
||||
await _broadcast(
|
||||
json.dumps(
|
||||
{
|
||||
"type": "timmy_speech",
|
||||
"text": reply,
|
||||
"recentExchanges": list(_conversation),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -222,17 +235,3 @@ async def _generate_bark(visitor_text: str) -> str:
|
||||
except Exception as exc:
|
||||
logger.warning("Bark generation failed: %s", exc)
|
||||
return "Hmm, my thoughts are a bit tangled right now."
|
||||
|
||||
|
||||
async def _broadcast_speech(payload: dict) -> None:
|
||||
"""Broadcast a speech message to all connected Workshop clients."""
|
||||
message = json.dumps(payload)
|
||||
dead: list[WebSocket] = []
|
||||
for ws in _ws_clients:
|
||||
try:
|
||||
await ws.send_text(message)
|
||||
except Exception:
|
||||
dead.append(ws)
|
||||
for ws in dead:
|
||||
if ws in _ws_clients:
|
||||
_ws_clients.remove(ws)
|
||||
|
||||
Reference in New Issue
Block a user