mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-29 06:31:32 +00:00
fix(dingtalk): finalize open streaming cards before disconnect
AI Card "tool progress" cards created with finalize=False were left in streaming state on DingTalk's UI after a gateway restart because disconnect() called _streaming_cards.clear() without first closing them via _close_streaming_siblings. Move the finalization loop before self._http_client.aclose() so the HTTP client is still available when the finalize requests are sent. Adds a regression test that asserts the HTTP client is alive during finalization.
This commit is contained in:
parent
a7b622effc
commit
39b8d1d313
2 changed files with 43 additions and 0 deletions
|
|
@ -358,6 +358,19 @@ class DingTalkAdapter(BasePlatformAdapter):
|
|||
await asyncio.gather(*self._bg_tasks, return_exceptions=True)
|
||||
self._bg_tasks.clear()
|
||||
|
||||
# Finalize any open streaming cards before the HTTP client closes so
|
||||
# they don't stay stuck in streaming state on DingTalk's UI after
|
||||
# a gateway restart. _close_streaming_siblings handles its own
|
||||
# per-card exceptions; the outer try is a safety net for token fetch.
|
||||
for _chat_id in list(self._streaming_cards):
|
||||
try:
|
||||
await self._close_streaming_siblings(_chat_id)
|
||||
except Exception as _exc:
|
||||
logger.debug(
|
||||
"[%s] Failed to finalize streaming card on disconnect for %s: %s",
|
||||
self.name, _chat_id, _exc,
|
||||
)
|
||||
|
||||
if self._http_client:
|
||||
await self._http_client.aclose()
|
||||
self._http_client = None
|
||||
|
|
|
|||
|
|
@ -407,6 +407,36 @@ class TestConnect:
|
|||
assert len(adapter._dedup._seen) == 0
|
||||
assert adapter._http_client is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_disconnect_finalizes_open_streaming_cards(self):
|
||||
"""Streaming cards must be finalized before HTTP client closes."""
|
||||
from unittest.mock import AsyncMock, patch
|
||||
from gateway.platforms.dingtalk import DingTalkAdapter
|
||||
adapter = DingTalkAdapter(PlatformConfig(enabled=True))
|
||||
adapter._http_client = AsyncMock()
|
||||
adapter._stream_task = None
|
||||
adapter._streaming_cards = {
|
||||
"chat-1": {"track-a": "last content"},
|
||||
"chat-2": {"track-b": "other"},
|
||||
}
|
||||
|
||||
close_calls = []
|
||||
|
||||
async def fake_close_siblings(chat_id):
|
||||
# HTTP client must still be alive at call time.
|
||||
assert adapter._http_client is not None, (
|
||||
"HTTP client was already closed before card finalization"
|
||||
)
|
||||
close_calls.append(chat_id)
|
||||
adapter._streaming_cards.pop(chat_id, None)
|
||||
|
||||
with patch.object(adapter, "_close_streaming_siblings", side_effect=fake_close_siblings):
|
||||
await adapter.disconnect()
|
||||
|
||||
assert set(close_calls) == {"chat-1", "chat-2"}
|
||||
assert adapter._streaming_cards == {}
|
||||
assert adapter._http_client is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Platform enum
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue