Conversation
Each chat_id now tracks its own rate-limit events and cooldown, so bursts to one chat do not trigger the global breaker for others. - _rate_limit_circuit_until: float -> Dict[str, float] (defaultdict) - _rate_limit_events: List[float] -> Dict[str, List[float]] (defaultdict) - All rate-limit methods now accept chat_id parameter
Contributor
teknium1
reviewed
Jul 30, 2026
teknium1
left a comment
Collaborator
There was a problem hiding this comment.
Thanks for isolating a concrete cross-chat delivery behavior: current main's breaker is adapter-wide (gateway/platforms/weixin.py:1210-1211, 1777-1778).
Problems
- The proposed partitioning allows a different chat to send after iLink returned
-2for the same adapter token (gateway/platforms/weixin.py:1780-1787). The adapter also serializes all text sends through one lock (gateway/platforms/weixin.py:1757). The diff provides no evidence that iLink's quota is recipient-scoped rather than token/account-scoped. - No tests are changed. Existing coverage only exercises the same chat ID (
tests/gateway/test_weixin.py:246-271), not the proposed cross-chat behavior. - The profile-scoped
get_secret()substitutions in the diff already landed in6160a8025327112c507e49dd3f7f6c669220a105; salvage should exclude those duplicate hunks.
Suggested changes
- Establish the iLink quota scope and add a two-chat adapter send-path regression test. Preserve the shared circuit if the provider limits by credential; otherwise assert that chat B remains deliverable while chat A is cooling down.
Automated hermes-sweeper review.
| @@ -1816,7 +1822,7 @@ async def _send_text_chunk_locked( | |||
| raise RuntimeError( | |||
| f"iLink sendmessage error: ret={ret} errcode={errcode} errmsg={errmsg}" | |||
| ) | |||
Collaborator
There was a problem hiding this comment.
This partitions a breaker for a shared adapter/token, so a send to chat B can proceed immediately after iLink rate-limits chat A. Please establish that iLink's -2 quota is recipient-scoped and add a two-chat send-path test; otherwise the circuit should remain credential-scoped.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Changes the Weixin adapter's rate-limit circuit breaker from global (one breaker for all chats) to per-chat-id isolation.
Problem
Currently
_rate_limit_circuit_untiland_rate_limit_eventsare single scalars shared across all chat IDs. When chat A triggers the breaker (e.g., a burst of cron push messages to one user), all other chats are blocked — even if they have no rate-limit history.In multi-profile/multi-user environments, this means one user's cron jobs can silently block another user's message delivery.
Fix
_rate_limit_circuit_until: float→Dict[str, float]backed bydefaultdict(float)_rate_limit_events: List[float]→Dict[str, List[float]]backed bydefaultdict(list)_rate_limit_cooldown_remaining,_rate_limit_error,_open_rate_limit_circuit,_record_rate_limit_event,_reset_rate_limit_circuit) now accept achat_idparameter_send_text_chunk_lockedpasschat_idthroughThe threshold logic is unchanged — each chat still independently tracks events in a rolling window and opens its own circuit when the threshold is reached.
Testing
send_weixin_direct) also uses the per-chat-id defaults correctlychat_id=""so any external callers without a chat_id still workRelated
This is a follow-up to the multiplex isolation work — profile-level isolation prevents credential cross-talk, but rate limiting also needs to be scoped to the chat/channel level.