mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-09-04 10:11:46 +03:00
fix(agent): bound session message rate-limit state
This commit is contained in:
@@ -7,7 +7,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import json
|
||||
import time
|
||||
from collections import deque
|
||||
from collections import OrderedDict, deque
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Protocol
|
||||
@@ -127,7 +127,7 @@ class SendSessionMessageTool(Tool):
|
||||
self._max_messages_per_minute = max_messages_per_minute
|
||||
self._schedule_later = schedule_later
|
||||
self._clock = clock or time.monotonic
|
||||
self._sent_at: dict[str, deque[float]] = {}
|
||||
self._sent_at: OrderedDict[str, deque[float]] = OrderedDict()
|
||||
self._pending_replies: dict[tuple[str, str], _PendingReply] = {}
|
||||
self._expiry_tasks: set[asyncio.Task[None]] = set()
|
||||
self._send_lock = asyncio.Lock()
|
||||
@@ -240,8 +240,11 @@ class SendSessionMessageTool(Tool):
|
||||
|
||||
async with self._send_lock:
|
||||
now = self._clock()
|
||||
sent_at = self._sent_at.setdefault(source.session_key, deque())
|
||||
cutoff = now - _RATE_LIMIT_WINDOW_SECONDS
|
||||
self._prune_expired_rate_limits(cutoff)
|
||||
sent_at = self._sent_at.get(source.session_key)
|
||||
if sent_at is None:
|
||||
sent_at = deque[float]()
|
||||
while sent_at and sent_at[0] <= cutoff:
|
||||
sent_at.popleft()
|
||||
if len(sent_at) >= self._max_messages_per_minute:
|
||||
@@ -259,6 +262,8 @@ class SendSessionMessageTool(Tool):
|
||||
input_role="user",
|
||||
))
|
||||
sent_at.append(now)
|
||||
self._sent_at[source.session_key] = sent_at
|
||||
self._sent_at.move_to_end(source.session_key)
|
||||
self._cancel_pending_reply(reverse_wait_key)
|
||||
if timeout_seconds is not None:
|
||||
self._cancel_pending_reply(wait_key)
|
||||
@@ -271,6 +276,14 @@ class SendSessionMessageTool(Tool):
|
||||
|
||||
return f"@{target.name}"
|
||||
|
||||
def _prune_expired_rate_limits(self, cutoff: float) -> None:
|
||||
"""Drop sources ordered by their most recent successful send."""
|
||||
while self._sent_at:
|
||||
_, sent_at = next(iter(self._sent_at.items()))
|
||||
if sent_at[-1] > cutoff:
|
||||
return
|
||||
self._sent_at.popitem(last=False)
|
||||
|
||||
@staticmethod
|
||||
def _validate_reply_timeout(
|
||||
expect_reply: bool,
|
||||
|
||||
Reference in New Issue
Block a user