Improve Python reliability in channel retries and thread typing (#1776)

Agent-Logs-Url: https://github.com/0xxy0/deer-flow/sessions/95336da6-e16d-43b4-834a-e5534c9396c5

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
Subham Singhania 2026-04-03 05:20:11 +05:30 committed by GitHub
parent c1366cf559
commit 6de9c7b43f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 60 additions and 7 deletions

View File

@ -206,7 +206,9 @@ class FeishuChannel(Channel):
await asyncio.sleep(delay) await asyncio.sleep(delay)
logger.error("[Feishu] send failed after %d attempts: %s", _max_retries, last_exc) logger.error("[Feishu] send failed after %d attempts: %s", _max_retries, last_exc)
raise last_exc # type: ignore[misc] if last_exc is None:
raise RuntimeError("Feishu send failed without an exception from any attempt")
raise last_exc
async def send_file(self, msg: OutboundMessage, attachment: ResolvedAttachment) -> bool: async def send_file(self, msg: OutboundMessage, attachment: ResolvedAttachment) -> bool:
if not self._api_client: if not self._api_client:

View File

@ -126,7 +126,9 @@ class SlackChannel(Channel):
) )
except Exception: except Exception:
pass pass
raise last_exc # type: ignore[misc] if last_exc is None:
raise RuntimeError("Slack send failed without an exception from any attempt")
raise last_exc
async def send_file(self, msg: OutboundMessage, attachment: ResolvedAttachment) -> bool: async def send_file(self, msg: OutboundMessage, attachment: ResolvedAttachment) -> bool:
if not self._web_client: if not self._web_client:

View File

@ -125,7 +125,9 @@ class TelegramChannel(Channel):
await asyncio.sleep(delay) await asyncio.sleep(delay)
logger.error("[Telegram] send failed after %d attempts: %s", _max_retries, last_exc) logger.error("[Telegram] send failed after %d attempts: %s", _max_retries, last_exc)
raise last_exc # type: ignore[misc] if last_exc is None:
raise RuntimeError("Telegram send failed without an exception from any attempt")
raise last_exc
async def send_file(self, msg: OutboundMessage, attachment: ResolvedAttachment) -> bool: async def send_file(self, msg: OutboundMessage, attachment: ResolvedAttachment) -> bool:
if not self._application: if not self._application:

View File

@ -488,16 +488,19 @@ async def get_thread(thread_id: str, request: Request) -> ThreadResponse:
"metadata": {k: v for k, v in ckpt_meta.items() if k not in ("created_at", "updated_at", "step", "source", "writes", "parents")}, "metadata": {k: v for k, v in ckpt_meta.items() if k not in ("created_at", "updated_at", "step", "source", "writes", "parents")},
} }
status = _derive_thread_status(checkpoint_tuple) if checkpoint_tuple is not None else record.get("status", "idle") # type: ignore[union-attr] if record is None:
raise HTTPException(status_code=404, detail=f"Thread {thread_id} not found")
status = _derive_thread_status(checkpoint_tuple) if checkpoint_tuple is not None else record.get("status", "idle")
checkpoint = getattr(checkpoint_tuple, "checkpoint", {}) or {} if checkpoint_tuple is not None else {} checkpoint = getattr(checkpoint_tuple, "checkpoint", {}) or {} if checkpoint_tuple is not None else {}
channel_values = checkpoint.get("channel_values", {}) channel_values = checkpoint.get("channel_values", {})
return ThreadResponse( return ThreadResponse(
thread_id=thread_id, thread_id=thread_id,
status=status, status=status,
created_at=str(record.get("created_at", "")), # type: ignore[union-attr] created_at=str(record.get("created_at", "")),
updated_at=str(record.get("updated_at", "")), # type: ignore[union-attr] updated_at=str(record.get("updated_at", "")),
metadata=record.get("metadata", {}), # type: ignore[union-attr] metadata=record.get("metadata", {}),
values=serialize_channel_values(channel_values), values=serialize_channel_values(channel_values),
) )

View File

@ -1854,6 +1854,20 @@ class TestSlackSendRetry:
_run(go()) _run(go())
def test_raises_runtime_error_when_no_attempts_configured(self):
from app.channels.slack import SlackChannel
async def go():
bus = MessageBus()
ch = SlackChannel(bus=bus, config={"bot_token": "xoxb-test", "app_token": "xapp-test"})
ch._web_client = MagicMock()
msg = OutboundMessage(channel_name="slack", chat_id="C123", thread_id="t1", text="hello")
with pytest.raises(RuntimeError, match="without an exception"):
await ch.send(msg, _max_retries=0)
_run(go())
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Telegram send retry tests # Telegram send retry tests
@ -1912,6 +1926,36 @@ class TestTelegramSendRetry:
_run(go()) _run(go())
def test_raises_runtime_error_when_no_attempts_configured(self):
from app.channels.telegram import TelegramChannel
async def go():
bus = MessageBus()
ch = TelegramChannel(bus=bus, config={"bot_token": "test-token"})
ch._application = MagicMock()
msg = OutboundMessage(channel_name="telegram", chat_id="12345", thread_id="t1", text="hello")
with pytest.raises(RuntimeError, match="without an exception"):
await ch.send(msg, _max_retries=0)
_run(go())
class TestFeishuSendRetry:
def test_raises_runtime_error_when_no_attempts_configured(self):
from app.channels.feishu import FeishuChannel
async def go():
bus = MessageBus()
ch = FeishuChannel(bus=bus, config={"app_id": "id", "app_secret": "secret"})
ch._api_client = MagicMock()
msg = OutboundMessage(channel_name="feishu", chat_id="chat", thread_id="t1", text="hello")
with pytest.raises(RuntimeError, match="without an exception"):
await ch.send(msg, _max_retries=0)
_run(go())
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Telegram private-chat thread context tests # Telegram private-chat thread context tests