Merge pull request #18046 from BerriAI/litellm_fix_managed_files_endpoint

Fix managed files endpoint
This commit is contained in:
Sameer Kankute 2025-12-16 21:39:34 +05:30 committed by GitHub
commit 20bdada900
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 92 additions and 2 deletions

View file

@ -750,9 +750,27 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
model_id=model_id,
model_name=model_name,
)
await self.store_unified_file_id( # need to store otherwise any retrieve call will fail
# Fetch the actual file object for the output file
file_object = None
try:
# Use litellm to retrieve the file object from the provider
from litellm import afile_retrieve
file_object = await afile_retrieve(
custom_llm_provider=model_name.split("/")[0] if model_name and "/" in model_name else "openai",
file_id=original_output_file_id
)
verbose_logger.debug(
f"Successfully retrieved file object for output_file_id={original_output_file_id}"
)
except Exception as e:
verbose_logger.warning(
f"Failed to retrieve file object for output_file_id={original_output_file_id}: {str(e)}. Storing with None and will fetch on-demand."
)
await self.store_unified_file_id(
file_id=response.output_file_id,
file_object=None,
file_object=file_object,
litellm_parent_otel_span=user_api_key_dict.parent_otel_span,
model_mappings={model_id: original_output_file_id},
user_api_key_dict=user_api_key_dict,

View file

@ -199,6 +199,7 @@ class AmazonAnthropicClaudeMessagesConfig(
if beta_set:
anthropic_messages_request["anthropic_beta"] = list(beta_set)
return anthropic_messages_request

View file

@ -573,6 +573,7 @@ async def list_batches(
if target_model_names is None:
raise ValueError("target_model_names is required for this routing scenario")
model = target_model_names.split(",")[0]
data.pop("model", None)
response = await llm_router.alist_batches(
model=model,
after=after,

View file

@ -577,3 +577,73 @@ async def test_vertex_list_batches(monkeypatch):
assert len(list_response["data"]) == 2
assert list_response["data"][0].id == "test-batch-id-456"
assert list_response["data"][1].id == "test-batch-id-789"
@pytest.mark.asyncio
async def test_delete_batch_output_file():
"""
Test that deleting a batch output file works correctly.
This test verifies the fix for:
- When a batch is retrieved and has an output_file_id, the file object is properly stored
- The output file can be deleted without validation errors
- The file_object is fetched and stored with proper metadata instead of None
"""
litellm._turn_on_debug()
print("Testing delete batch output file")
file_name = "openai_batch_completions.jsonl"
_current_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(_current_dir, file_name)
# Create file for batch
file_obj = await litellm.acreate_file(
file=open(file_path, "rb"),
purpose="batch",
custom_llm_provider="openai",
)
print("Response from creating file=", file_obj)
batch_input_file_id = file_obj.id
# Create batch
create_batch_response = await litellm.acreate_batch(
completion_window="24h",
endpoint="/v1/chat/completions",
input_file_id=batch_input_file_id,
custom_llm_provider="openai",
)
print("Batch created with ID=", create_batch_response.id)
# Retrieve batch to get output_file_id
retrieved_batch = await litellm.aretrieve_batch(
batch_id=create_batch_response.id,
custom_llm_provider="openai"
)
print("Retrieved batch=", retrieved_batch)
# If batch has completed and has output file, test deleting it
if retrieved_batch.output_file_id:
print(f"Testing deletion of output file: {retrieved_batch.output_file_id}")
# This is the key test - deleting the output file should work
# without validation errors (file_object should not be None)
delete_output_file_response = await litellm.afile_delete(
file_id=retrieved_batch.output_file_id,
custom_llm_provider="openai"
)
print("Delete output file response=", delete_output_file_response)
assert delete_output_file_response.id == retrieved_batch.output_file_id
assert delete_output_file_response.deleted is True or hasattr(delete_output_file_response, 'id')
print("✓ Successfully deleted batch output file")
else:
print("⚠ Batch has not completed yet or no output file available, skipping output file deletion test")
# Clean up - delete the input file
delete_input_file_response = await litellm.afile_delete(
file_id=batch_input_file_id,
custom_llm_provider="openai"
)
print("Delete input file response=", delete_input_file_response)
assert delete_input_file_response.id == batch_input_file_id
print("✓ Successfully deleted batch input file")