Completes the STREAM_BROWSER=true behaviour:
- handleJoin auto-starts the broadcast on voice join and wires the session to
the guild streamer; each turn reports the live state to the brain so search
routes Chrome (live) vs Gemini (off).
- New setBroadcast tool lets the user toggle the broadcast by voice ("방송
켜줘/꺼줘") via the LLM (no hardcoded phrases); it refuses when
STREAM_BROWSER=false. The directive flows brain -> bridge (broadcast_action)
-> bot streamer.start/stop, guarded by isActive() so it's idempotent.
- Per-turn IPC uses a thread-local (reply/turn_state.py) instead of threading
params through the whole engine chain: bridge sets broadcasting in, tool
records the directive out; Tool.execute exposes broadcasting on ToolContext.
Bot typecheck clean; brain covered by tests/test_set_broadcast.py (+ existing
routing tests). Specs + docs updated.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
49 lines
1.6 KiB
Python
49 lines
1.6 KiB
Python
"""Per-turn, request-scoped state shared between the bridge, the reply engine
|
|
and tools — without threading extra parameters through the whole engine call
|
|
chain (run_reply_engine -> run_tool_with_retries -> execute_tool -> execute).
|
|
|
|
Flask serves each HTTP request on its own thread and the reply engine + tools
|
|
run synchronously on that thread, so a ``threading.local`` cleanly scopes a
|
|
single turn. Anything that can't see the thread-local (a worker thread, an eval,
|
|
a unit test) just reads ``None`` and the caller falls back to its default.
|
|
|
|
Fields
|
|
------
|
|
broadcasting : Optional[bool]
|
|
The live screen-share (Go-Live) state the bot sends with the request. The
|
|
webSearch routing reads it to pick on-screen Chrome (live) vs Gemini (off).
|
|
broadcast_action : Optional[str]
|
|
A directive ("start"/"stop") recorded by the setBroadcast tool for the
|
|
bridge to return to the bot, which owns the actual broadcast.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import threading
|
|
from typing import Optional
|
|
|
|
_state = threading.local()
|
|
|
|
|
|
def reset() -> None:
|
|
"""Clear per-turn state. Call at the start of each request."""
|
|
_state.broadcasting = None
|
|
_state.broadcast_action = None
|
|
|
|
|
|
def set_broadcasting(value: Optional[bool]) -> None:
|
|
_state.broadcasting = value
|
|
|
|
|
|
def get_broadcasting() -> Optional[bool]:
|
|
return getattr(_state, "broadcasting", None)
|
|
|
|
|
|
def request_broadcast(action: str) -> None:
|
|
"""Record a broadcast directive. ``action`` is "start" or "stop"."""
|
|
_state.broadcast_action = action
|
|
|
|
|
|
def get_broadcast_action() -> Optional[str]:
|
|
return getattr(_state, "broadcast_action", None)
|