mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-16 04:29:31 +00:00
fix(batches): an unreadable error file no longer drops the output line events
Costing counts an error file it could not fetch as no failed lines; line logging refetched it and the raise aborted the whole fan-out, so the successful lines were never emitted. The error file now reads as empty with a warning.
This commit is contained in:
parent
317764c3a1
commit
85fc4498cc
2 changed files with 46 additions and 1 deletions
|
|
@ -419,6 +419,22 @@ async def _fetch_managed_file_or_empty(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _fetch_error_file_or_empty(
|
||||||
|
batch: LiteLLMBatch,
|
||||||
|
custom_llm_provider: _BatchLineProvider,
|
||||||
|
fetch_params: dict[str, object] | None, # mutable-ok: batch_utils file fetch takes the shared litellm_params dict
|
||||||
|
) -> bytes:
|
||||||
|
try:
|
||||||
|
return await _fetch_managed_file_or_empty(batch.error_file_id, custom_llm_provider, fetch_params)
|
||||||
|
except Exception as e: # noqa: BLE001 # costing counts an unreadable error file as no failed lines, so the output lines still go out
|
||||||
|
verbose_logger.warning(
|
||||||
|
"batch line-item callbacks could not fetch the error file, emitting the output lines only. batch_id=%s error=%s",
|
||||||
|
batch.id,
|
||||||
|
e,
|
||||||
|
)
|
||||||
|
return b""
|
||||||
|
|
||||||
|
|
||||||
async def log_batch_line_items(
|
async def log_batch_line_items(
|
||||||
batch: LiteLLMBatch,
|
batch: LiteLLMBatch,
|
||||||
custom_llm_provider: str,
|
custom_llm_provider: str,
|
||||||
|
|
@ -488,7 +504,7 @@ async def log_batch_line_items(
|
||||||
error_content: Final = (
|
error_content: Final = (
|
||||||
result_files.error
|
result_files.error
|
||||||
if result_files is not None and result_files.error is not None
|
if result_files is not None and result_files.error is not None
|
||||||
else await _fetch_managed_file_or_empty(batch.error_file_id, line_provider, fetch_params)
|
else await _fetch_error_file_or_empty(batch, line_provider, fetch_params)
|
||||||
)
|
)
|
||||||
for content in (output_content, error_content):
|
for content in (output_content, error_content):
|
||||||
for entry in _output_entries(content):
|
for entry in _output_entries(content):
|
||||||
|
|
|
||||||
|
|
@ -1241,6 +1241,35 @@ async def test_supplied_result_files_skip_the_output_and_error_fetch(recorder):
|
||||||
assert file_mock.await_args.kwargs["file_id"] == "input-file-1"
|
assert file_mock.await_args.kwargs["file_id"] == "input-file-1"
|
||||||
|
|
||||||
|
|
||||||
|
def _error_file_unreadable(file_id: str, **_kwargs):
|
||||||
|
if file_id == "error-file-1":
|
||||||
|
raise ValueError("error file gone")
|
||||||
|
return SimpleNamespace(content=_FILE_BYTES[file_id])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_an_unreadable_error_file_still_emits_the_output_lines(recorder):
|
||||||
|
file_mock: Final = AsyncMock(side_effect=_error_file_unreadable)
|
||||||
|
with patch(
|
||||||
|
"litellm.files.main.afile_content", file_mock
|
||||||
|
): # test-quality-ok: afile_content is the provider boundary; no injection seam for managed file fetch
|
||||||
|
emitted: Final = await log_batch_line_items(
|
||||||
|
batch=_batch(),
|
||||||
|
custom_llm_provider="openai",
|
||||||
|
parent=_parent_logging(),
|
||||||
|
model_name="gpt-4o",
|
||||||
|
litellm_params=None,
|
||||||
|
model_info=None,
|
||||||
|
result_files=BatchResultFiles(output=OUTPUT_JSONL, error=None),
|
||||||
|
claim_cache=DualCache(),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert emitted == 1
|
||||||
|
assert len(recorder.success_events) == 1
|
||||||
|
assert len(recorder.failure_events) == 0
|
||||||
|
assert sorted(call.kwargs["file_id"] for call in file_mock.await_args_list) == ["error-file-1", "input-file-1"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_empty_result_files_fall_back_to_fetching_everything(recorder):
|
async def test_empty_result_files_fall_back_to_fetching_everything(recorder):
|
||||||
file_mock: Final = AsyncMock(side_effect=_file_content)
|
file_mock: Final = AsyncMock(side_effect=_file_content)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue