fix: add vLLM/local server error patterns + MCP initial connection retry (#9281)

Port two improvements inspired by Kilo-Org/kilocode analysis:

1. Error classifier: add context overflow patterns for vLLM, Ollama,
   and llama.cpp/llama-server. These local inference servers return
   different error formats than cloud providers (e.g., 'exceeds the
   max_model_len', 'context length exceeded', 'slot context'). Without
   these patterns, context overflow errors from local servers are
   misclassified as format errors, causing infinite retries instead
   of triggering compression.

2. MCP initial connection retry: previously, if the very first
   connection attempt to an MCP server failed (e.g., transient DNS
   blip at startup), the server was permanently marked as failed with
   no retry. Post-connect reconnection had 5 retries with exponential
   backoff, but initial connection had zero. Now initial connections
   retry up to 3 times with backoff before giving up, matching the
   resilience of post-connect reconnection.
   (Inspired by Kilo Code's MCP server disappearing fix in v1.3.3)

Tests: 6 new error classifier tests, 4 new MCP retry tests, 1
updated existing test. All 276 affected tests pass.
This commit is contained in:
Teknium 2026-04-13 18:46:14 -07:00 committed by GitHub
parent 0a4cf5b3e1
commit f324222b79
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 204 additions and 8 deletions

View file

@ -1008,8 +1008,12 @@ class TestReconnection:
asyncio.run(_test())
def test_no_reconnect_on_initial_failure(self):
"""First connection failure reports error immediately, no retry."""
from tools.mcp_tool import MCPServerTask
"""First connection failure retries up to _MAX_INITIAL_CONNECT_RETRIES times.
Before the MCP resilience fix, initial failures gave up immediately.
Now they retry with backoff to handle transient DNS/network blips.
"""
from tools.mcp_tool import MCPServerTask, _MAX_INITIAL_CONNECT_RETRIES
run_count = 0
target_server = None
@ -1032,8 +1036,8 @@ class TestReconnection:
patch("asyncio.sleep", new_callable=AsyncMock):
await server.run({"command": "test"})
# Only one attempt, no retry on initial failure
assert run_count == 1
# Now retries up to _MAX_INITIAL_CONNECT_RETRIES before giving up
assert run_count == _MAX_INITIAL_CONNECT_RETRIES + 1
assert server._error is not None
assert "cannot connect" in str(server._error)