mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-11 08:38:24 +00:00
fix(responses): propagate aclose through MCPEnhancedStreamingIterator and type test helpers
Greptile review: the MCP wrapper stores its upstream-owning iterator in base_iterator, so the inherited response-based aclose was a no-op there. Delegate to the base iterator's aclose when it has one. Also add the missing parameter and return annotations on the new passthrough test helpers
This commit is contained in:
parent
e8e7db248b
commit
62ca98a3f7
3 changed files with 34 additions and 3 deletions
|
|
@ -326,6 +326,11 @@ class MCPEnhancedStreamingIterator(BaseResponsesAPIStreamingIterator):
|
|||
self._error_event_emitted = False
|
||||
self._last_sequence_number = 0
|
||||
|
||||
async def aclose(self) -> None:
|
||||
base_aclose: Final = getattr(self.base_iterator, "aclose", None)
|
||||
if base_aclose is not None:
|
||||
await base_aclose()
|
||||
|
||||
def _extract_mcp_headers_from_params(self) -> None:
|
||||
"""Extract MCP headers from original request params to pass to tool calls"""
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ when the stream ends, including on client disconnect (generator aclose), so
|
|||
the provider connection is released and backends like vLLM stop generating.
|
||||
"""
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from collections.abc import AsyncIterator, Coroutine
|
||||
from datetime import datetime, timezone
|
||||
from typing import Final
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
|
@ -51,7 +51,7 @@ def _logging_obj_stub() -> MagicMock:
|
|||
return logging_obj
|
||||
|
||||
|
||||
def _chunk_processor(response: httpx.Response):
|
||||
def _chunk_processor(response: httpx.Response) -> AsyncIterator[bytes]:
|
||||
return PassThroughStreamingHandler.chunk_processor(
|
||||
response=response,
|
||||
request_body={"model": "claude-sonnet-4-5"},
|
||||
|
|
@ -63,7 +63,7 @@ def _chunk_processor(response: httpx.Response):
|
|||
)
|
||||
|
||||
|
||||
def _drop_scheduled_coroutine(async_coroutine) -> None:
|
||||
def _drop_scheduled_coroutine(async_coroutine: Coroutine[object, object, object]) -> None:
|
||||
async_coroutine.close()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -257,3 +257,29 @@ async def test_initial_call_failure_is_stashed_for_eager_reraise(monkeypatch):
|
|||
|
||||
assert iterator._initial_creation_error is not None
|
||||
assert "initial boom" in str(iterator._initial_creation_error)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_aclose_closes_base_iterator():
|
||||
class _ClosableStream:
|
||||
def __init__(self):
|
||||
self.aclose_called = False
|
||||
|
||||
async def aclose(self) -> None:
|
||||
self.aclose_called = True
|
||||
|
||||
iterator = _make_iterator([])
|
||||
closable = _ClosableStream()
|
||||
iterator.base_iterator = closable
|
||||
|
||||
await iterator.aclose()
|
||||
|
||||
assert closable.aclose_called
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_aclose_is_a_noop_without_base_iterator():
|
||||
iterator = _make_iterator([])
|
||||
iterator.base_iterator = None
|
||||
|
||||
await iterator.aclose()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue