mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
fix(mcp): preserve timeout fallback on Python 3.10
This commit is contained in:
parent
e8c411fb43
commit
5c190e69bf
3 changed files with 19 additions and 4 deletions
6
.github/workflows/test-code-quality.yml
vendored
6
.github/workflows/test-code-quality.yml
vendored
|
|
@ -187,3 +187,9 @@ jobs:
|
|||
|
||||
- name: Check litellm CLI
|
||||
run: uv run --no-sync litellm --version
|
||||
|
||||
- name: Verify MCP timeout fallback and auth retries on Python 3.10
|
||||
run: >-
|
||||
uv run --no-sync pytest --noconftest
|
||||
tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_debug.py
|
||||
-k error_capture -q
|
||||
|
|
|
|||
|
|
@ -581,7 +581,7 @@ async def capture_upstream_error_response(response: httpx.Response) -> None:
|
|||
if secrets is not None
|
||||
else "(omitted: request credentials unavailable)"
|
||||
)
|
||||
except (TimeoutError, httpx.HTTPError, httpx.StreamError):
|
||||
except (asyncio.TimeoutError, httpx.HTTPError, httpx.StreamError):
|
||||
response._content = b"" # pyright: ignore[reportPrivateUsage] # rebind-ok: httpx auth retries must survive diagnostic read failures
|
||||
response.extensions[_CAPTURE_EXTENSION] = (
|
||||
"(unavailable: error body read failed)" # rebind-ok: httpx response hooks communicate through extensions
|
||||
|
|
|
|||
|
|
@ -349,7 +349,8 @@ def test_failure_preview_handles_empty_scalar_control_and_long_bodies(body):
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_error_capture_preserves_httpx_auth_retry():
|
||||
@pytest.mark.parametrize("slow_error", [False, True])
|
||||
async def test_error_capture_preserves_httpx_auth_retry(slow_error):
|
||||
from litellm.proxy._experimental.mcp_server.mcp_debug import capture_upstream_error_response
|
||||
|
||||
class RetryAuth(httpx.Auth):
|
||||
|
|
@ -359,16 +360,24 @@ async def test_error_capture_preserves_httpx_auth_retry():
|
|||
request.headers["Authorization"] = "Bearer refreshed"
|
||||
yield request
|
||||
|
||||
class SlowStream(httpx.AsyncByteStream):
|
||||
async def __aiter__(self):
|
||||
await asyncio.sleep(10)
|
||||
yield b'{"error":"expired_token"}'
|
||||
|
||||
def upstream(request):
|
||||
if request.headers.get("Authorization"):
|
||||
return httpx.Response(200, json={"ok": True})
|
||||
return httpx.Response(401, json={"error":"expired_token"})
|
||||
return httpx.Response(401, stream=SlowStream()) if slow_error else httpx.Response(401, json={"error":"expired_token"})
|
||||
|
||||
async with httpx.AsyncClient(transport=httpx.MockTransport(upstream), auth=RetryAuth(),
|
||||
event_hooks={"response":[capture_upstream_error_response]}) as client:
|
||||
response = await client.get("https://upstream/mcp")
|
||||
assert response.status_code == 200 and response.json() == {"ok":True}
|
||||
assert response.history[0].json() == {"error":"expired_token"}
|
||||
if slow_error:
|
||||
assert response.history[0].content == b""
|
||||
else:
|
||||
assert response.history[0].json() == {"error":"expired_token"}
|
||||
|
||||
|
||||
def test_failure_diagnostics_without_request_and_with_streamed_request():
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue