fix(responses): import BaseLLMException lazily and collect stream chunks via anext

Move the BaseLLMException import into _map_error_event_exception so the
module no longer imports it at load time, clearing the module-level cyclic
import CodeQL flagged. The class is used only on the cold error path.

Replace the mutable list-append test collector with aiter/anext so the
regression tests read the stream immutably.
This commit is contained in:
mateo-berri 2026-09-13 02:35:53 -07:00
parent 073d4fe2b0
commit c246372859
2 changed files with 11 additions and 14 deletions

View file

@ -31,7 +31,6 @@ from litellm.litellm_core_utils.llm_response_utils.response_metadata import (
update_response_metadata,
)
from litellm.litellm_core_utils.thread_pool_executor import executor
from litellm.llms.base_llm.chat.transformation import BaseLLMException
from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig
from litellm.responses.utils import ResponseAPILoggingUtils, ResponsesAPIRequestUtils
from litellm.types.llms.openai import (
@ -553,6 +552,8 @@ class BaseResponsesAPIStreamingIterator:
)
def _map_error_event_exception(self, error_obj: object) -> Exception:
from litellm.llms.base_llm.chat.transformation import BaseLLMException
error_message, error_type, error_code = _error_event_fields(error_obj)
status_code: Final = _status_code_for_error_fields(error_type, error_code)
error_body: Final = {"message": error_message, "type": error_type, "code": error_code}

View file

@ -287,15 +287,12 @@ async def test_async_iterator_content_policy_violation_after_first_chunk_carries
]
)
chunks = []
async def _drain():
async for chunk in iterator:
chunks.append(chunk)
stream = aiter(iterator)
first_chunk = await anext(stream)
assert first_chunk is not None
with pytest.raises(MidStreamFallbackError) as exc_info:
await _drain()
assert len(chunks) == 1
await anext(stream)
assert isinstance(exc_info.value.original_exception, litellm.ContentPolicyViolationError)
assert exc_info.value.is_pre_first_chunk is False
assert exc_info.value.generated_content == "partial "
@ -316,14 +313,13 @@ async def test_async_iterator_error_after_first_chunk_carries_generated_content(
]
)
chunks = []
async def _drain():
async for chunk in iterator:
chunks.append(chunk)
stream = aiter(iterator)
first_chunk = await anext(stream)
second_chunk = await anext(stream)
assert first_chunk is not None and second_chunk is not None
with pytest.raises(MidStreamFallbackError) as exc_info:
await _drain()
assert len(chunks) == 2
await anext(stream)
assert exc_info.value.status_code == 500
assert exc_info.value.is_pre_first_chunk is False
assert exc_info.value.generated_content == "hello world"