From 9c19de67bf63a86eeda1e6a068c19e7f5c6c8354 Mon Sep 17 00:00:00 2001 From: "chengjun.zhu" Date: Fri, 17 Apr 2026 09:02:24 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=94=99=E8=AF=AF=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=B5=81=E8=BD=AC=E8=B7=AF=E5=BE=84=EF=BC=9A1.=20=E5=BD=93=20L?= =?UTF-8?q?LM=20=E6=9C=8D=E5=8A=A1=E5=87=BA=E7=8E=B0=E4=B8=B4=E6=97=B6?= =?UTF-8?q?=E6=80=A7=E9=94=99=E8=AF=AF=EF=BC=88=E5=A6=82=E7=BD=91=E7=BB=9C?= =?UTF-8?q?=E6=B3=A2=E5=8A=A8=E3=80=81=E8=B6=85=E6=97=B6=E3=80=81429?= =?UTF-8?q?=E9=99=90=E6=B5=81=E7=AD=89=EF=BC=89=E6=97=B6=EF=BC=8C=20base.p?= =?UTF-8?q?y=20=E4=B8=AD=E7=9A=84=20=5Frun=5Fwith=5Fretry=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E4=BC=9A=E5=90=AF=E5=8A=A8=E9=87=8D=E8=AF=95=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=E3=80=822.=20=E5=9C=A8=E9=87=8D=E8=AF=95=E7=AD=89?= =?UTF-8?q?=E5=BE=85=E6=9C=9F=E9=97=B4=EF=BC=8C=20=5Fsleep=5Fwith=5Fheartb?= =?UTF-8?q?eat=20=E6=96=B9=E6=B3=95=E4=BC=9A=E5=91=A8=E6=9C=9F=E6=80=A7?= =?UTF-8?q?=E8=B0=83=E7=94=A8=20on=5Fretry=5Fwait=20=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E5=87=BD=E6=95=B0=EF=BC=8C=E5=8F=91=E9=80=81=E7=B1=BB=E4=BC=BC?= =?UTF-8?q?=20'Model=20request=20failed,=20retry=20in=201s=20(attempt=201)?= =?UTF-8?q?'=20=E7=9A=84=E5=BF=83=E8=B7=B3=E6=B6=88=E6=81=AF=E3=80=823.=20?= =?UTF-8?q?=E4=B9=8B=E5=89=8D=20on=5Fretry=5Fwait=20=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E8=A2=AB=E9=94=99=E8=AF=AF=E5=9C=B0=E7=BB=91=E5=AE=9A=E5=88=B0?= =?UTF-8?q?=20=5Fbus=5Fprogress=20=EF=BC=8C=E5=AF=BC=E8=87=B4=E8=BF=99?= =?UTF-8?q?=E4=BA=9B=E5=86=85=E9=83=A8=E8=AF=8A=E6=96=AD=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E8=A2=AB=E5=BD=93=E4=BD=9C=E6=99=AE=E9=80=9A=E8=BF=9B=E5=BA=A6?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=8F=91=E9=80=81=E5=88=B0=E9=A3=9E=E4=B9=A6?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E3=80=824.=20manager.py=20=E7=9A=84?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=88=86=E5=8F=91=E5=99=A8=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E8=BF=99=E7=B1=BB=E9=87=8D=E8=AF=95=E5=BF=83?= =?UTF-8?q?=E8=B7=B3=E6=B6=88=E6=81=AF=E3=80=82=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=96=B9=E6=A1=88=EF=BC=9A1.=20loop.py=20-=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E9=87=8D=E8=AF=95=E7=AD=89=E5=BE=85=E5=9B=9E=E8=B0=83?= =?UTF-8?q?-=20=E6=96=B0=E5=A2=9E=E7=8B=AC=E7=AB=8B=E7=9A=84=20=5Fon=5Fret?= =?UTF-8?q?ry=5Fwait=20=E5=9B=9E=E8=B0=83=E5=87=BD=E6=95=B0=EF=BC=8C?= =?UTF-8?q?=E4=B8=BA=E9=87=8D=E8=AF=95=E6=B6=88=E6=81=AF=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=20=5Fretry=5Fwait:=20True=20=E5=85=83=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=A0=87=E8=AF=86-=20=E5=9C=A8=20AgentRunSpec=20=E4=B8=AD?= =?UTF-8?q?=E4=BC=A0=E5=85=A5=20retry=5Fwait=5Fcallback=20=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E3=80=822.=20runner.py=20-=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=87=8D=E8=AF=95=E5=9B=9E=E8=B0=83=E5=8F=82=E6=95=B0-=20?= =?UTF-8?q?=E5=9C=A8=20AgentRunSpec=20=E6=95=B0=E6=8D=AE=E7=B1=BB=E4=B8=AD?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20retry=5Fwait=5Fcallback=20=E5=AD=97?= =?UTF-8?q?=E6=AE=B5-=20=E5=9C=A8=20=5Fbuild=5Frequest=5Fkwargs=20?= =?UTF-8?q?=E4=B8=AD=E5=B0=86=20on=5Fretry=5Fwait=20=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E4=BB=8E=20progress=5Fcallback=20=E6=94=B9=E4=B8=BA=20retry=5F?= =?UTF-8?q?wait=5Fcallback=E3=80=823.=20manager.py=20-=20=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E9=87=8D=E8=AF=95=E5=BF=83=E8=B7=B3=E6=B6=88=E6=81=AF?= =?UTF-8?q?-=20=E5=9C=A8=20=5Fdispatch=5Foutbound=20=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E4=B8=AD=E6=96=B0=E5=A2=9E=E8=BF=87=E6=BB=A4=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E4=B8=A2=E5=BC=83=E6=89=80=E6=9C=89=E5=B8=A6=20=5Fret?= =?UTF-8?q?ry=5Fwait=20=E6=A0=87=E8=AF=86=E7=9A=84=E6=B6=88=E6=81=AF?= =?UTF-8?q?=EF=BC=8C=E7=A1=AE=E4=BF=9D=E9=87=8D=E8=AF=95=E5=BF=83=E8=B7=B3?= =?UTF-8?q?=E4=B8=8D=E4=BC=9A=E5=8F=91=E9=80=81=E5=88=B0=E4=BB=BB=E4=BD=95?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nanobot/agent/loop.py | 15 +++++++++++++++ nanobot/agent/runner.py | 3 ++- nanobot/channels/manager.py | 3 +++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/nanobot/agent/loop.py b/nanobot/agent/loop.py index e7393e24b..3350c447b 100644 --- a/nanobot/agent/loop.py +++ b/nanobot/agent/loop.py @@ -351,6 +351,7 @@ class AgentLoop: on_progress: Callable[..., Awaitable[None]] | None = None, on_stream: Callable[[str], Awaitable[None]] | None = None, on_stream_end: Callable[..., Awaitable[None]] | None = None, + on_retry_wait: Callable[[str], Awaitable[None]] | None = None, *, session: Session | None = None, channel: str = "cli", @@ -428,6 +429,7 @@ class AgentLoop: context_block_limit=self.context_block_limit, provider_retry_mode=self.provider_retry_mode, progress_callback=on_progress, + retry_wait_callback=on_retry_wait, checkpoint_callback=_checkpoint, injection_callback=_drain_pending, )) @@ -738,6 +740,18 @@ class AgentLoop: ) ) + async def _on_retry_wait(content: str) -> None: + meta = dict(msg.metadata or {}) + meta["_retry_wait"] = True + await self.bus.publish_outbound( + OutboundMessage( + channel=msg.channel, + chat_id=msg.chat_id, + content=content, + metadata=meta, + ) + ) + # Persist the triggering user message immediately, before running the # agent loop. If the process is killed mid-turn (OOM, SIGKILL, self- # restart, etc.), the existing runtime_checkpoint preserves the @@ -756,6 +770,7 @@ class AgentLoop: on_progress=on_progress or _bus_progress, on_stream=on_stream, on_stream_end=on_stream_end, + on_retry_wait=_on_retry_wait, session=session, channel=msg.channel, chat_id=msg.chat_id, diff --git a/nanobot/agent/runner.py b/nanobot/agent/runner.py index 9b7d50316..d90c79fe6 100644 --- a/nanobot/agent/runner.py +++ b/nanobot/agent/runner.py @@ -71,6 +71,7 @@ class AgentRunSpec: context_block_limit: int | None = None provider_retry_mode: str = "standard" progress_callback: Any | None = None + retry_wait_callback: Any | None = None checkpoint_callback: Any | None = None injection_callback: Any | None = None @@ -552,7 +553,7 @@ class AgentRunner: "tools": tools, "model": spec.model, "retry_mode": spec.provider_retry_mode, - "on_retry_wait": spec.progress_callback, + "on_retry_wait": spec.retry_wait_callback, } if spec.temperature is not None: kwargs["temperature"] = spec.temperature diff --git a/nanobot/channels/manager.py b/nanobot/channels/manager.py index 0e4821701..c0622a272 100644 --- a/nanobot/channels/manager.py +++ b/nanobot/channels/manager.py @@ -189,6 +189,9 @@ class ChannelManager: if not msg.metadata.get("_tool_hint") and not self.config.channels.send_progress: continue + if msg.metadata.get("_retry_wait"): + continue + # Coalesce consecutive _stream_delta messages for the same (channel, chat_id) # to reduce API calls and improve streaming latency if msg.metadata.get("_stream_delta") and not msg.metadata.get("_stream_end"):