feat(gateway): add Feishu websocket ping timing overrides

Allow Feishu websocket keepalive timing to be configured via platform
extra config so disconnects can be detected faster in unstable networks.

New optional extra settings:
- ws_ping_interval
- ws_ping_timeout

These values are applied only when explicitly configured. Invalid values
fall back to the websocket library defaults by leaving the options unset.

This complements the reconnect timing settings added previously and helps
reduce total recovery time after network interruptions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
jtuki 2026-04-05 23:23:07 +08:00 committed by Teknium
parent 7d0bf15121
commit ea31d9077c
2 changed files with 52 additions and 0 deletions

View file

@ -272,6 +272,8 @@ class FeishuAdapterSettings:
webhook_path: str
ws_reconnect_nonce: int = 30
ws_reconnect_interval: int = 120
ws_ping_interval: Optional[int] = None
ws_ping_timeout: Optional[int] = None
@dataclass
@ -376,6 +378,14 @@ def _coerce_positive_int(value: Any, default: int) -> int:
return parsed if parsed >= 1 else default
def _coerce_optional_positive_int(value: Any) -> Optional[int]:
try:
parsed = int(value)
except (TypeError, ValueError):
return None
return parsed if parsed >= 1 else None
# ---------------------------------------------------------------------------
# Post payload builders and parsers
# ---------------------------------------------------------------------------
@ -939,11 +949,23 @@ def _run_official_feishu_ws_client(ws_client: Any, adapter: Any) -> None:
asyncio.set_event_loop(loop)
ws_client_module.loop = loop
adapter._ws_thread_loop = loop
original_connect = ws_client_module.websockets.connect
async def _connect_with_overrides(*args: Any, **kwargs: Any) -> Any:
if adapter._ws_ping_interval is not None and "ping_interval" not in kwargs:
kwargs["ping_interval"] = adapter._ws_ping_interval
if adapter._ws_ping_timeout is not None and "ping_timeout" not in kwargs:
kwargs["ping_timeout"] = adapter._ws_ping_timeout
return await original_connect(*args, **kwargs)
ws_client_module.websockets.connect = _connect_with_overrides
try:
ws_client.start()
except Exception:
pass
finally:
ws_client_module.websockets.connect = original_connect
pending = [t for t in asyncio.all_tasks(loop) if not t.done()]
for task in pending:
task.cancel()
@ -1060,6 +1082,8 @@ class FeishuAdapter(BasePlatformAdapter):
),
ws_reconnect_nonce=_coerce_non_negative_int(extra.get("ws_reconnect_nonce"), 30),
ws_reconnect_interval=_coerce_positive_int(extra.get("ws_reconnect_interval"), 120),
ws_ping_interval=_coerce_optional_positive_int(extra.get("ws_ping_interval")),
ws_ping_timeout=_coerce_optional_positive_int(extra.get("ws_ping_timeout")),
)
def _apply_settings(self, settings: FeishuAdapterSettings) -> None:
@ -1084,6 +1108,8 @@ class FeishuAdapter(BasePlatformAdapter):
self._webhook_path = settings.webhook_path
self._ws_reconnect_nonce = settings.ws_reconnect_nonce
self._ws_reconnect_interval = settings.ws_reconnect_interval
self._ws_ping_interval = settings.ws_ping_interval
self._ws_ping_timeout = settings.ws_ping_timeout
def _build_event_handler(self) -> Any:
if EventDispatcherHandler is None:

View file

@ -587,6 +587,32 @@ class TestAdapterModule(unittest.TestCase):
self.assertEqual(settings.ws_reconnect_nonce, 0)
self.assertEqual(settings.ws_reconnect_interval, 3)
def test_load_settings_accepts_custom_ws_ping_values(self):
from gateway.platforms.feishu import FeishuAdapter
settings = FeishuAdapter._load_settings(
{
"ws_ping_interval": 10,
"ws_ping_timeout": 8,
}
)
self.assertEqual(settings.ws_ping_interval, 10)
self.assertEqual(settings.ws_ping_timeout, 8)
def test_load_settings_ignores_invalid_ws_ping_values(self):
from gateway.platforms.feishu import FeishuAdapter
settings = FeishuAdapter._load_settings(
{
"ws_ping_interval": 0,
"ws_ping_timeout": -1,
}
)
self.assertIsNone(settings.ws_ping_interval)
self.assertIsNone(settings.ws_ping_timeout)
class TestAdapterBehavior(unittest.TestCase):
@patch.dict(os.environ, {}, clear=True)