fix: preserve busy steer env override

This commit is contained in:
devatnull 2026-06-29 08:03:05 +03:00 committed by Teknium
parent 12f03b11ff
commit d111faa3a7
2 changed files with 49 additions and 10 deletions

View file

@ -1563,8 +1563,16 @@ if _config_path.exists():
os.environ["HERMES_GATEWAY_BUSY_TEXT_MODE"] = str(_display_cfg["busy_text_mode"])
if "busy_ack_enabled" in _display_cfg:
os.environ["HERMES_GATEWAY_BUSY_ACK_ENABLED"] = str(_display_cfg["busy_ack_enabled"])
if "busy_steer_ack_enabled" in _display_cfg:
os.environ["HERMES_GATEWAY_BUSY_STEER_ACK_ENABLED"] = str(_display_cfg["busy_steer_ack_enabled"])
# This process-level env var is documented as an override for
# service managers, so preserve it when already set. Other display
# bridges stay config-authoritative for backwards compatibility.
if (
"busy_steer_ack_enabled" in _display_cfg
and "HERMES_GATEWAY_BUSY_STEER_ACK_ENABLED" not in os.environ
):
os.environ["HERMES_GATEWAY_BUSY_STEER_ACK_ENABLED"] = str(
_display_cfg["busy_steer_ack_enabled"]
)
# Timezone: bridge config.yaml → HERMES_TIMEZONE env var.
_tz_cfg = _cfg.get("timezone", "")
if _tz_cfg and isinstance(_tz_cfg, str):
@ -5332,6 +5340,15 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
logger.debug("Busy ack suppressed for session %s", session_key)
return True # input still processed, just no ack sent
# Debounce before consulting config-heavy display settings. Rapid
# follow-ups should be processed but should not trigger another config
# read just to discover that no ack will be sent.
_BUSY_ACK_COOLDOWN = 30
now = time.time()
last_ack = self._busy_ack_ts.get(session_key, 0)
if now - last_ack < _BUSY_ACK_COOLDOWN:
return True # interrupt sent (if not queue), ack already delivered recently
from gateway.display_config import resolve_display_setting
platform_key = _platform_config_key(event.source.platform)
@ -5356,14 +5373,6 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
logger.debug("Busy steer ack suppressed for session %s", session_key)
return True
# Debounce: only send an acknowledgment once every 30 seconds per session
# to avoid spamming the user when they send multiple messages quickly
_BUSY_ACK_COOLDOWN = 30
now = time.time()
last_ack = self._busy_ack_ts.get(session_key, 0)
if now - last_ack < _BUSY_ACK_COOLDOWN:
return True # interrupt sent (if not queue), ack already delivered recently
self._busy_ack_ts[session_key] = now
# Build a status-rich acknowledgment. Mobile chat defaults keep this

View file

@ -358,6 +358,36 @@ class TestBusySessionAck:
adapter._send_with_retry.assert_not_called()
assert sk not in adapter._pending_messages
@pytest.mark.asyncio
async def test_busy_ack_debounce_skips_steer_ack_config_load(self, monkeypatch):
"""Rapid follow-ups should not reload display config when ack is debounced."""
import gateway.run as _gr
def _boom():
raise AssertionError("config should not be loaded inside ack cooldown")
monkeypatch.delenv("HERMES_GATEWAY_BUSY_STEER_ACK_ENABLED", raising=False)
monkeypatch.setattr(_gr, "_load_gateway_config", _boom)
runner, sentinel = _make_runner()
runner._busy_input_mode = "steer"
adapter = _make_adapter()
event = _make_event(text="rapid steer")
sk = build_session_key(event.source)
runner.adapters[event.source.platform] = adapter
agent = MagicMock()
agent.steer = MagicMock(return_value=True)
runner._running_agents[sk] = agent
runner._busy_ack_ts[sk] = time.time()
result = await runner._handle_active_session_busy_message(event, sk)
assert result is True
agent.steer.assert_called_once_with("rapid steer")
adapter._send_with_retry.assert_not_called()
@pytest.mark.asyncio
async def test_steer_mode_falls_back_to_queue_when_agent_rejects(self):
"""If agent.steer() returns False, fall back to queue behavior."""