From 8ffffb4d15d3a14d6cfa39453d7e293a8d384266 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Tue, 16 Dec 2025 15:43:55 +0530 Subject: [PATCH 1/4] Fix model getting twice --- litellm/proxy/batches_endpoints/endpoints.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/proxy/batches_endpoints/endpoints.py b/litellm/proxy/batches_endpoints/endpoints.py index 03b9ac3deaa..086105042e8 100644 --- a/litellm/proxy/batches_endpoints/endpoints.py +++ b/litellm/proxy/batches_endpoints/endpoints.py @@ -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, From 3222b4e3a8d012aa79382e81d4fce2d58ad64996 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Tue, 16 Dec 2025 15:44:30 +0530 Subject: [PATCH 2/4] Add batch output file in managed table --- .../proxy/hooks/managed_files.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py index 6620db5ffa2..2dc01fbe0ad 100644 --- a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py +++ b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py @@ -191,6 +191,8 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints): ) if db_object: + print(f"🔥 [get_unified_file_id] db_object: {db_object}") + print(f"🔥 [get_unified_file_id] db_object.model_dump(): {db_object.model_dump()}") return LiteLLM_ManagedFileTable(**db_object.model_dump()) return None @@ -750,9 +752,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, From 6a3f0a8baf47914c4402b91905cc9d577bfdc66f Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Tue, 16 Dec 2025 15:52:07 +0530 Subject: [PATCH 3/4] Add output file id in managed objects for batches --- .../anthropic_claude3_transformation.py | 2 + .../test_openai_batches_and_files.py | 70 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py index 32be1a780a3..dfb6c6514b8 100644 --- a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py @@ -175,6 +175,8 @@ class AmazonAnthropicClaudeMessagesConfig( if beta_set: anthropic_messages_request["anthropic_beta"] = list(beta_set) + + print(f"🔥 [transform_anthropic_messages_request] anthropic_messages_request: {anthropic_messages_request}") return anthropic_messages_request diff --git a/tests/batches_tests/test_openai_batches_and_files.py b/tests/batches_tests/test_openai_batches_and_files.py index 2f4f9bbcda1..055af024949 100644 --- a/tests/batches_tests/test_openai_batches_and_files.py +++ b/tests/batches_tests/test_openai_batches_and_files.py @@ -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") From 755f0240870e5a2a2e275b24de8fd2c6922abdf9 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Tue, 16 Dec 2025 21:37:09 +0530 Subject: [PATCH 4/4] remove print statments --- enterprise/litellm_enterprise/proxy/hooks/managed_files.py | 2 -- .../invoke_transformations/anthropic_claude3_transformation.py | 1 - 2 files changed, 3 deletions(-) diff --git a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py index 2dc01fbe0ad..e12be6baf5d 100644 --- a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py +++ b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py @@ -191,8 +191,6 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints): ) if db_object: - print(f"🔥 [get_unified_file_id] db_object: {db_object}") - print(f"🔥 [get_unified_file_id] db_object.model_dump(): {db_object.model_dump()}") return LiteLLM_ManagedFileTable(**db_object.model_dump()) return None diff --git a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py index dfb6c6514b8..e01dc941849 100644 --- a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py @@ -176,7 +176,6 @@ class AmazonAnthropicClaudeMessagesConfig( if beta_set: anthropic_messages_request["anthropic_beta"] = list(beta_set) - print(f"🔥 [transform_anthropic_messages_request] anthropic_messages_request: {anthropic_messages_request}") return anthropic_messages_request