fix: dedup TTL expiry, compression provider fallback, TCP keepalive CLOSE-WAIT - #10405
Closed
d3momonkey wants to merge 3 commits into
Closed
fix: dedup TTL expiry, compression provider fallback, TCP keepalive CLOSE-WAIT#10405d3momonkey wants to merge 3 commits into
d3momonkey wants to merge 3 commits into
Conversation
…LOSE-WAIT Fixes NousResearch#10306 - MessageDeduplicator TTL never expires on low-traffic instances - wecom.py + dingtalk.py: check TTL on every lookup, not just when over DEDUP_MAX_SIZE. Entries now expire after DEDUP_WINDOW_SECONDS regardless of cache size. Fixes NousResearch#10314 - Context compression falls back to OpenRouter when summary_provider=auto - auxiliary_client.py: add _read_main_provider() and use it in _resolve_task_provider_model() so compression uses the main configured provider instead of falling through to OpenRouter/gemini-3-flash-preview. Eliminates 404 errors and 600s cooldown loops for non-OpenRouter users. Fixes NousResearch#10324 - Agent hangs indefinitely on CLOSE-WAIT when provider drops connection - run_agent.py: add TCP keepalive socket options (SO_KEEPALIVE, TCP_KEEPIDLE=30s, TCP_KEEPINTVL=10s, TCP_KEEPCNT=3) to httpx transport in _create_openai_client(). Dead connections from dropped LiteLLM/custom providers now detected within ~60s instead of hanging forever. try/except fallback for macOS/Windows.
Fixes NousResearch#10216 - Gateway --config flag crashes with JSONDecodeError - gateway/run.py: replace json.load() with yaml.safe_load() in main() when loading config file passed via --config flag. json.load() was being used on a YAML file, immediately crashing on the first colon. Fixes NousResearch#10234 - MCP tool wastes 3x tokens on Chinese/non-ASCII text - tools/mcp_tool.py: add ensure_ascii=False to all 19 json.dumps() calls. Default ensure_ascii=True was escaping Chinese characters to \uXXXX sequences, inflating token counts by ~3-4x for CJK content.
Author
|
Update: Added two more fixes to this branch:
Also investigated #10174 (on_memory_write bridge missing in sequential path) — the bridge described in the ticket does not exist anywhere in the codebase (neither in |
…nt cache fd leak Fixes NousResearch#10318 - hermes update --check not recognized - hermes_cli/main.py: add --check flag to update_parser and implement check mode in cmd_update() — runs git fetch + rev-list to count commits behind, prints status without pulling/installing. Fixes NousResearch#10313 - External skills get wrong skill_dir in _load_skill_payload - tools/skills_tool.py: include absolute 'skill_dir' in skill_view() JSON response so callers don't have to reconstruct it. - agent/skill_commands.py: use skill_dir from JSON response directly; fall back to SKILLS_DIR-relative path only for backward compat. Fixes NousResearch#10225 - load_cli_config() clobbers gateway TERMINAL_CWD - cli.py: check existing TERMINAL_CWD before resolving '.' to os.getcwd(). Gateway's MESSAGING_CWD-derived value is now preserved. Fixes NousResearch#10200 - AsyncOpenAI client cache leaks fds indefinitely - agent/auxiliary_client.py: add MAX_CLIENT_CACHE_SIZE=32 cap with LRU-style eviction. Oldest clients are force-closed when cache grows beyond limit, preventing fd exhaustion in long-running gateways.
Author
|
Update 2: Four more fixes added to the branch:
Running total: 9 bugs fixed across 9 files in this PR. |
Collaborator
Contributor
|
Thanks for the thorough multi-fix PR, @d3momonkey! After an automated hermes-sweeper review against current Evidence by fix:
This is an automated hermes-sweeper review. Closing as implemented on main. |
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
Fixes three bugs identified in issues #10306, #10314, and #10324.
Fix 1 — #10306: MessageDeduplicator TTL never expires on low-traffic instances
Files:
gateway/platforms/wecom.py,gateway/platforms/dingtalk.pyThe
_is_duplicate()method only pruned expired entries when the cache exceededDEDUP_MAX_SIZE(1000). On low-traffic instances the cache stays small, so message IDs accumulated forever and were permanently treated as duplicates.Fix: Check TTL on every lookup (not just size-overflow). The prune condition now fires when
msg_id in self._seen_messagesOR when over max size, so expired entries are evicted before the membership test.Fix 2 — #10314: Context compression falls back to OpenRouter when
summary_provider=autoFile:
agent/auxiliary_client.pyWhen
compression.summary_provider=auto(default) andsummary_modelis empty (default),_resolve_task_provider_model()returned("auto", None, ...)which fed into_resolve_auto(). That chain picks OpenRouter first and usesgoogle/gemini-3-flash-preview— a model that 404s on non-OpenRouter providers (DashScope, custom endpoints, etc.). This caused a 600s cooldown loop, making the agent appear frozen.Fix: Added
_read_main_provider()and wired it into_resolve_task_provider_model(): when task iscompressionand the result is stillautowith no model, read the main configured provider/model directly and return it, skipping the OpenRouter fallback chain.Fix 3 — #10324: Agent hangs indefinitely on CLOSE-WAIT when provider drops connection
File:
run_agent.pyWhen a custom provider (e.g. LiteLLM proxy) drops a connection mid-stream, the socket enters TCP
CLOSE-WAIT. Because httpx usesepoll_waitfor async I/O and aCLOSE-WAITsocket with no buffered data does not become readable, the 900s timeout never starts and the process hangs forever requiringkill -9.Fix: Add TCP keepalive socket options to the httpx transport in
_create_openai_client():SO_KEEPALIVE=1— enable keepalivesTCP_KEEPIDLE=30— start probes after 30s idleTCP_KEEPINTVL=10— probe every 10sTCP_KEEPCNT=3— give up after 3 failuresWorst-case detection window: ~60s instead of infinite.
try/exceptfallback handles macOS/Windows whereTCP_KEEPIDLEmay be unavailable.Testing
tests/gateway/— all passing (pre-existing failures unchanged)tests/tools/— all passing (pre-existing failures unchanged)