diff --git a/litellm/utils.py b/litellm/utils.py index ed0d6ee930d..85aa9acc2d8 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -2075,6 +2075,7 @@ def _is_async_request( or kwargs.get("_arealtime", False) is True or kwargs.get("acreate_batch", False) is True or kwargs.get("acreate_fine_tuning_job", False) is True + or kwargs.get("acreate_file", False) is True or is_pass_through is True ): return True diff --git a/test_acreate_file_fix.py b/test_acreate_file_fix.py new file mode 100644 index 00000000000..10417651a9e --- /dev/null +++ b/test_acreate_file_fix.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +""" +Test script to verify the acreate_file RuntimeWarning fix. +This reproduces the issue described in GitHub issue #20798. +""" +import asyncio +import warnings +import os +import tempfile + +# Capture warnings +warnings.simplefilter("always") + +# Add the project to path +import sys +sys.path.insert(0, '/tmp/oss-litellm') + +import litellm +from litellm.utils import _is_async_request + + +async def test_acreate_file_async_detection(): + """Test that _is_async_request properly detects acreate_file calls.""" + print("Testing _is_async_request function...") + + # Test kwargs similar to what acreate_file sets + test_kwargs = {"acreate_file": True} + result = _is_async_request(test_kwargs) + + print(f"_is_async_request with acreate_file=True returned: {result}") + assert result is True, "Expected _is_async_request to return True for acreate_file=True" + print("✅ _is_async_request correctly detects acreate_file!") + + +async def test_acreate_file_mock(): + """Test acreate_file to ensure no runtime warnings are generated.""" + print("\nTesting acreate_file function...") + + # Create a temporary file for testing + with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f: + f.write("Hello, World! This is a test file.") + temp_file_path = f.name + + try: + # Note: This will fail because we don't have valid API keys + # but it should NOT generate a RuntimeWarning about coroutines + try: + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + + result = await litellm.acreate_file( + file=open(temp_file_path, "rb"), + purpose="assistants", + api_key="test-key", # Mock key + ) + except Exception as e: + print(f"Expected exception (no valid API key): {type(e).__name__}") + + # Check if any RuntimeWarnings about coroutines were captured + runtime_warnings = [warn for warn in w if issubclass(warn.category, RuntimeWarning)] + coroutine_warnings = [warn for warn in runtime_warnings if "coroutine" in str(warn.message)] + + if coroutine_warnings: + print("❌ Found RuntimeWarnings about coroutines:") + for warn in coroutine_warnings: + print(f" {warn.message}") + assert False, "RuntimeWarning about coroutines found!" + else: + print("✅ No RuntimeWarning about coroutines detected!") + + finally: + # Clean up temp file + os.unlink(temp_file_path) + + +async def main(): + print("Testing GitHub Issue #20798 fix: acreate_file RuntimeWarning") + print("=" * 60) + + await test_acreate_file_async_detection() + await test_acreate_file_mock() + + print("\n" + "=" * 60) + print("✅ All tests passed! The fix appears to be working correctly.") + + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/test_simple_fix.py b/test_simple_fix.py new file mode 100644 index 00000000000..f374624570d --- /dev/null +++ b/test_simple_fix.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +""" +Simple test to verify the _is_async_request fix without full dependencies. +""" +import sys +import os + +# Read the utils.py file directly to test the function +with open('/tmp/oss-litellm/litellm/utils.py', 'r') as f: + content = f.read() + +def test_function_contains_fix(): + """Test that the _is_async_request function contains our fix.""" + print("Testing that _is_async_request function contains acreate_file check...") + + # Find the _is_async_request function + start_marker = "def _is_async_request(" + start_idx = content.find(start_marker) + + if start_idx == -1: + print("❌ Could not find _is_async_request function!") + return False + + # Find the end of the function (next def or end of file) + end_idx = content.find("\ndef ", start_idx + 1) + if end_idx == -1: + end_idx = len(content) + + function_content = content[start_idx:end_idx] + + # Check if our fix is present + if 'kwargs.get("acreate_file", False) is True' in function_content: + print("✅ Found acreate_file check in _is_async_request function!") + + # Also verify it's in the right place (in the if statement) + if_section_start = function_content.find("if (") + if_section_end = function_content.find("):", if_section_start) + if_section = function_content[if_section_start:if_section_end] + + if 'kwargs.get("acreate_file", False) is True' in if_section: + print("✅ acreate_file check is properly placed in the if condition!") + return True + else: + print("❌ acreate_file check found but not in the if condition!") + return False + else: + print("❌ acreate_file check NOT found in _is_async_request function!") + return False + +def test_function_logic(): + """Test the logic of the fixed function manually.""" + print("\nTesting _is_async_request function logic...") + + # Extract and test the function logic manually + test_cases = [ + ({"acreate_file": True}, True, "acreate_file=True should return True"), + ({"acreate_file": False}, False, "acreate_file=False should return False"), + ({"acompletion": True}, True, "acompletion=True should return True"), + ({"some_other_key": True}, False, "unrelated key should return False"), + ({}, False, "empty dict should return False"), + (None, False, "None should return False"), + ] + + for kwargs, expected, description in test_cases: + # Manually test the logic based on what we see in the function + if kwargs is None: + result = False + elif ( + kwargs.get("acompletion", False) is True + or kwargs.get("aembedding", False) is True + or kwargs.get("aimg_generation", False) is True + or kwargs.get("amoderation", False) is True + or kwargs.get("atext_completion", False) is True + or kwargs.get("atranscription", False) is True + or kwargs.get("arerank", False) is True + or kwargs.get("_arealtime", False) is True + or kwargs.get("acreate_batch", False) is True + or kwargs.get("acreate_fine_tuning_job", False) is True + or kwargs.get("acreate_file", False) is True # Our fix! + ): + result = True + else: + result = False + + if result == expected: + print(f"✅ {description}") + else: + print(f"❌ {description} - got {result}, expected {expected}") + return False + + return True + +def main(): + print("Testing GitHub Issue #20798 fix") + print("=" * 50) + + success1 = test_function_contains_fix() + success2 = test_function_logic() + + print("\n" + "=" * 50) + if success1 and success2: + print("✅ All tests passed! The fix is correctly implemented.") + return True + else: + print("❌ Some tests failed!") + return False + +if __name__ == "__main__": + success = main() + sys.exit(0 if success else 1) \ No newline at end of file