2025-05-24 13:55:35 +08:00
|
|
|
|
import asyncio
|
|
|
|
|
|
import copy
|
|
|
|
|
|
import sys
|
|
|
|
|
|
import time
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
from unittest import mock
|
|
|
|
|
|
|
|
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
|
|
|
|
|
|
|
|
from litellm.types.utils import StandardCallbackDynamicParams
|
|
|
|
|
|
|
|
|
|
|
|
load_dotenv()
|
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
|
|
sys.path.insert(
|
|
|
|
|
|
0, os.path.abspath("../..")
|
|
|
|
|
|
) # Adds the parent directory to the system-path
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
|
|
import litellm
|
|
|
|
|
|
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler
|
|
|
|
|
|
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
|
async def test_client_session_helper():
|
|
|
|
|
|
"""Test that the client session helper handles event loop changes correctly"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# Create a transport with the new helper
|
|
|
|
|
|
transport = AsyncHTTPHandler._create_aiohttp_transport()
|
|
|
|
|
|
if transport is not None:
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print("✅ Successfully created aiohttp transport with helper")
|
|
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
# Test the helper function directly if it's a LiteLLMAiohttpTransport
|
2026-04-18 04:02:59 +08:00
|
|
|
|
if hasattr(transport, "_get_valid_client_session"):
|
2025-05-24 13:55:35 +08:00
|
|
|
|
session1 = transport._get_valid_client_session() # type: ignore
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print(f"✅ First session created: {type(session1).__name__}")
|
|
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
# Call it again to test reuse
|
|
|
|
|
|
session2 = transport._get_valid_client_session() # type: ignore
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print(f"✅ Second session call: {type(session2).__name__}")
|
|
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
# In the same event loop, should be the same session
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print(f"✅ Same session reused: {session1 is session2}")
|
|
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
return True
|
|
|
|
|
|
else:
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print("ℹ️ No aiohttp transport available (probably missing httpx-aiohttp)")
|
2025-05-24 13:55:35 +08:00
|
|
|
|
return True
|
|
|
|
|
|
except Exception as e:
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print(f"❌ Error: {e}")
|
2025-05-24 13:55:35 +08:00
|
|
|
|
import traceback
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
async def test_event_loop_robustness():
|
|
|
|
|
|
"""Test behavior when event loops change (simulating CI/CD scenario)"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# Test session creation in multiple scenarios
|
|
|
|
|
|
transport = AsyncHTTPHandler._create_aiohttp_transport()
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
|
|
|
|
|
if transport and hasattr(transport, "_get_valid_client_session"):
|
2025-05-24 13:55:35 +08:00
|
|
|
|
# Test 1: Normal usage
|
|
|
|
|
|
session = transport._get_valid_client_session() # type: ignore
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print(f"✅ Normal session creation works: {session is not None}")
|
|
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
# Test 2: Force recreation by setting client to a callable
|
|
|
|
|
|
from aiohttp import ClientSession
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
transport.client = lambda: ClientSession() # type: ignore
|
|
|
|
|
|
session2 = transport._get_valid_client_session() # type: ignore
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print(f"✅ Session recreation after callable works: {session2 is not None}")
|
|
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
return True
|
|
|
|
|
|
else:
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print("ℹ️ Transport not available or no helper method")
|
2025-05-24 13:55:35 +08:00
|
|
|
|
return True
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
except Exception as e:
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print(f"❌ Error in event loop robustness test: {e}")
|
2025-05-24 13:55:35 +08:00
|
|
|
|
import traceback
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
async def test_httpx_request_simulation():
|
|
|
|
|
|
"""Test that the transport can handle a simulated HTTP request"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
transport = AsyncHTTPHandler._create_aiohttp_transport()
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
if transport is not None:
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print("✅ Transport created for request simulation")
|
|
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
# Create a simple httpx request to test with
|
|
|
|
|
|
import httpx
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
|
|
|
|
|
request = httpx.Request("GET", "https://httpbin.org/headers")
|
|
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
# Just test that we can get a valid session for this request context
|
2026-04-18 04:02:59 +08:00
|
|
|
|
if hasattr(transport, "_get_valid_client_session"):
|
2025-05-24 13:55:35 +08:00
|
|
|
|
session = transport._get_valid_client_session() # type: ignore
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print(f"✅ Got valid session for request: {session is not None}")
|
|
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
# Test that session has required aiohttp methods
|
2026-04-18 04:02:59 +08:00
|
|
|
|
has_request_method = hasattr(session, "request")
|
|
|
|
|
|
print(f"✅ Session has request method: {has_request_method}")
|
|
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
return has_request_method
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
return True
|
|
|
|
|
|
else:
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print("ℹ️ No transport available for request simulation")
|
2025-05-24 13:55:35 +08:00
|
|
|
|
return True
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
except Exception as e:
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print(f"❌ Error in request simulation: {e}")
|
2025-05-24 13:55:35 +08:00
|
|
|
|
return False
|
|
|
|
|
|
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
print("Testing client session helper and event loop handling fix...")
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
result1 = asyncio.run(test_client_session_helper())
|
2026-04-18 04:02:59 +08:00
|
|
|
|
result2 = asyncio.run(test_event_loop_robustness())
|
2025-05-24 13:55:35 +08:00
|
|
|
|
result3 = asyncio.run(test_httpx_request_simulation())
|
2026-04-18 04:02:59 +08:00
|
|
|
|
|
2025-05-24 13:55:35 +08:00
|
|
|
|
if result1 and result2 and result3:
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print(
|
|
|
|
|
|
"🎉 All tests passed! The helper function approach should fix the CI/CD event loop issues."
|
|
|
|
|
|
)
|
2025-05-24 13:55:35 +08:00
|
|
|
|
else:
|
2026-04-18 04:02:59 +08:00
|
|
|
|
print("💥 Some tests failed")
|