mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-28 06:51:16 +08:00
fix(gateway): make Feishu websocket overrides effective at runtime
Reapply local reconnect and ping settings after the Feishu SDK refreshes its client config so user-provided websocket tuning actually takes effect. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -951,6 +951,16 @@ def _run_official_feishu_ws_client(ws_client: Any, adapter: Any) -> None:
|
||||
adapter._ws_thread_loop = loop
|
||||
|
||||
original_connect = ws_client_module.websockets.connect
|
||||
original_configure = getattr(ws_client, "_configure", None)
|
||||
|
||||
def _apply_runtime_ws_overrides() -> None:
|
||||
try:
|
||||
setattr(ws_client, "_reconnect_nonce", adapter._ws_reconnect_nonce)
|
||||
setattr(ws_client, "_reconnect_interval", adapter._ws_reconnect_interval)
|
||||
if adapter._ws_ping_interval is not None:
|
||||
setattr(ws_client, "_ping_interval", adapter._ws_ping_interval)
|
||||
except Exception:
|
||||
logger.debug("[Feishu] Failed to apply websocket runtime overrides", exc_info=True)
|
||||
|
||||
async def _connect_with_overrides(*args: Any, **kwargs: Any) -> Any:
|
||||
if adapter._ws_ping_interval is not None and "ping_interval" not in kwargs:
|
||||
@@ -959,13 +969,23 @@ def _run_official_feishu_ws_client(ws_client: Any, adapter: Any) -> None:
|
||||
kwargs["ping_timeout"] = adapter._ws_ping_timeout
|
||||
return await original_connect(*args, **kwargs)
|
||||
|
||||
def _configure_with_overrides(conf: Any) -> Any:
|
||||
result = original_configure(conf)
|
||||
_apply_runtime_ws_overrides()
|
||||
return result
|
||||
|
||||
ws_client_module.websockets.connect = _connect_with_overrides
|
||||
if callable(original_configure):
|
||||
setattr(ws_client, "_configure", _configure_with_overrides)
|
||||
_apply_runtime_ws_overrides()
|
||||
try:
|
||||
ws_client.start()
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
ws_client_module.websockets.connect = original_connect
|
||||
if callable(original_configure):
|
||||
setattr(ws_client, "_configure", original_configure)
|
||||
pending = [t for t in asyncio.all_tasks(loop) if not t.done()]
|
||||
for task in pending:
|
||||
task.cancel()
|
||||
@@ -3080,11 +3100,6 @@ class FeishuAdapter(BasePlatformAdapter):
|
||||
event_handler=self._event_handler,
|
||||
domain=domain,
|
||||
)
|
||||
try:
|
||||
setattr(self._ws_client, "_reconnect_nonce", self._ws_reconnect_nonce)
|
||||
setattr(self._ws_client, "_reconnect_interval", self._ws_reconnect_interval)
|
||||
except Exception:
|
||||
logger.debug("[Feishu] Failed to override websocket reconnect settings", exc_info=True)
|
||||
self._ws_future = loop.run_in_executor(
|
||||
None,
|
||||
_run_official_feishu_ws_client,
|
||||
|
||||
@@ -262,12 +262,21 @@ class TestFeishuAdapterMessaging(unittest.TestCase):
|
||||
with (
|
||||
patch("gateway.platforms.feishu.FEISHU_AVAILABLE", True),
|
||||
patch("gateway.platforms.feishu.FEISHU_WEBHOOK_AVAILABLE", True),
|
||||
patch("gateway.platforms.feishu.EventDispatcherHandler") as mock_handler_class,
|
||||
patch("gateway.platforms.feishu.acquire_scoped_lock", return_value=(True, None)),
|
||||
patch("gateway.platforms.feishu.release_scoped_lock"),
|
||||
patch.object(adapter, "_hydrate_bot_identity", new=AsyncMock()),
|
||||
patch.object(adapter, "_build_lark_client", return_value=SimpleNamespace()),
|
||||
patch("gateway.platforms.feishu.web", web_module),
|
||||
):
|
||||
mock_builder = Mock()
|
||||
mock_builder.register_p2_im_message_message_read_v1 = Mock(return_value=mock_builder)
|
||||
mock_builder.register_p2_im_message_receive_v1 = Mock(return_value=mock_builder)
|
||||
mock_builder.register_p2_im_message_reaction_created_v1 = Mock(return_value=mock_builder)
|
||||
mock_builder.register_p2_im_message_reaction_deleted_v1 = Mock(return_value=mock_builder)
|
||||
mock_builder.register_p2_card_action_trigger = Mock(return_value=mock_builder)
|
||||
mock_builder.build = Mock(return_value=object())
|
||||
mock_handler_class.builder = Mock(return_value=mock_builder)
|
||||
connected = asyncio.run(adapter.connect())
|
||||
|
||||
self.assertTrue(connected)
|
||||
@@ -613,6 +622,61 @@ class TestAdapterModule(unittest.TestCase):
|
||||
self.assertIsNone(settings.ws_ping_interval)
|
||||
self.assertIsNone(settings.ws_ping_timeout)
|
||||
|
||||
def test_runtime_ws_overrides_reapply_after_sdk_configure(self):
|
||||
import sys
|
||||
from types import ModuleType
|
||||
|
||||
class _FakeWSClient:
|
||||
def __init__(self):
|
||||
self._reconnect_nonce = 30
|
||||
self._reconnect_interval = 120
|
||||
self._ping_interval = 120
|
||||
self.configure_calls = []
|
||||
|
||||
def _configure(self, conf):
|
||||
self.configure_calls.append(conf)
|
||||
self._reconnect_nonce = conf.ReconnectNonce
|
||||
self._reconnect_interval = conf.ReconnectInterval
|
||||
self._ping_interval = conf.PingInterval
|
||||
|
||||
def start(self):
|
||||
conf = SimpleNamespace(ReconnectNonce=99, ReconnectInterval=88, PingInterval=77)
|
||||
self._configure(conf)
|
||||
raise RuntimeError("stop test client")
|
||||
|
||||
fake_client = _FakeWSClient()
|
||||
fake_adapter = SimpleNamespace(
|
||||
_ws_thread_loop=None,
|
||||
_ws_reconnect_nonce=2,
|
||||
_ws_reconnect_interval=3,
|
||||
_ws_ping_interval=4,
|
||||
_ws_ping_timeout=5,
|
||||
)
|
||||
fake_client_module = ModuleType("lark_oapi.ws.client")
|
||||
fake_client_module.loop = None
|
||||
fake_client_module.websockets = SimpleNamespace(connect=AsyncMock())
|
||||
fake_ws_module = ModuleType("lark_oapi.ws")
|
||||
fake_ws_module.client = fake_client_module
|
||||
fake_root_module = ModuleType("lark_oapi")
|
||||
fake_root_module.ws = fake_ws_module
|
||||
|
||||
original_modules = sys.modules.copy()
|
||||
sys.modules["lark_oapi"] = fake_root_module
|
||||
sys.modules["lark_oapi.ws"] = fake_ws_module
|
||||
sys.modules["lark_oapi.ws.client"] = fake_client_module
|
||||
try:
|
||||
from gateway.platforms.feishu import _run_official_feishu_ws_client
|
||||
|
||||
_run_official_feishu_ws_client(fake_client, fake_adapter)
|
||||
finally:
|
||||
sys.modules.clear()
|
||||
sys.modules.update(original_modules)
|
||||
|
||||
self.assertEqual(len(fake_client.configure_calls), 1)
|
||||
self.assertEqual(fake_client._reconnect_nonce, 2)
|
||||
self.assertEqual(fake_client._reconnect_interval, 3)
|
||||
self.assertEqual(fake_client._ping_interval, 4)
|
||||
|
||||
|
||||
class TestAdapterBehavior(unittest.TestCase):
|
||||
@patch.dict(os.environ, {}, clear=True)
|
||||
|
||||
Reference in New Issue
Block a user