mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
add: reproduction scripts for MCP NoneType bug
- simple_repro.py: Minimal script to demonstrate the bug - force_repro_mcp_bug.py: More comprehensive reproduction - stress_test_mcp.py: Stress test for intermittent issues To reproduce the bug: git checkout HEAD~2 -- litellm/responses/mcp/litellm_proxy_mcp_handler.py python simple_repro.py To see the fix working: git checkout HEAD -- litellm/responses/mcp/litellm_proxy_mcp_handler.py python simple_repro.py Co-authored-by: ishaan <ishaan@berri.ai>
This commit is contained in:
parent
9b8c7d75d9
commit
b973b6fe49
3 changed files with 606 additions and 0 deletions
290
force_repro_mcp_bug.py
Normal file
290
force_repro_mcp_bug.py
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
"""
|
||||
Force reproduction of the MCP + Responses API bug.
|
||||
|
||||
This script patches the internal functions to return None,
|
||||
simulating the conditions that cause the bug.
|
||||
|
||||
Usage:
|
||||
python force_repro_mcp_bug.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from unittest.mock import patch, AsyncMock, MagicMock
|
||||
import litellm
|
||||
from litellm._logging import verbose_logger
|
||||
import logging
|
||||
|
||||
# Enable verbose logging to see the error
|
||||
verbose_logger.setLevel(logging.DEBUG)
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
|
||||
|
||||
async def test_with_none_allowed_servers():
|
||||
"""
|
||||
Force the bug by making get_mcp_servers_from_ids return None.
|
||||
This simulates the condition where allowed_mcp_servers becomes None.
|
||||
"""
|
||||
print("\n" + "="*70)
|
||||
print("🐛 TEST 1: Force None from get_mcp_servers_from_ids")
|
||||
print("="*70)
|
||||
|
||||
from litellm.proxy._experimental.mcp_server.mcp_server_manager import global_mcp_server_manager
|
||||
|
||||
# Save original method
|
||||
original_method = global_mcp_server_manager.get_mcp_servers_from_ids
|
||||
|
||||
# Patch to return None
|
||||
def return_none(*args, **kwargs):
|
||||
print(" 🔧 get_mcp_servers_from_ids called - returning None!")
|
||||
return None # This should cause the bug!
|
||||
|
||||
global_mcp_server_manager.get_mcp_servers_from_ids = return_none
|
||||
|
||||
try:
|
||||
mcp_tool_config = {
|
||||
"type": "mcp",
|
||||
"server_url": "litellm_proxy",
|
||||
"require_approval": "never",
|
||||
}
|
||||
|
||||
response = await litellm.aresponses(
|
||||
model="gpt-4o-mini",
|
||||
tools=[mcp_tool_config],
|
||||
input=[{"role": "user", "type": "message", "content": "test"}],
|
||||
stream=True,
|
||||
)
|
||||
|
||||
async for chunk in response:
|
||||
print(f" Chunk: {getattr(chunk, 'type', 'unknown')}")
|
||||
|
||||
print(" ✅ No error occurred (bug might be fixed)")
|
||||
|
||||
except TypeError as e:
|
||||
if "NoneType" in str(e) and "not iterable" in str(e):
|
||||
print(f" 🐛 BUG REPRODUCED! Error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return True
|
||||
else:
|
||||
print(f" ❌ Different error: {e}")
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f" ❌ Other error: {type(e).__name__}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
# Restore original method
|
||||
global_mcp_server_manager.get_mcp_servers_from_ids = original_method
|
||||
|
||||
return False
|
||||
|
||||
|
||||
async def test_with_none_allowed_mcp_servers():
|
||||
"""
|
||||
Force the bug by making get_allowed_mcp_servers return None.
|
||||
"""
|
||||
print("\n" + "="*70)
|
||||
print("🐛 TEST 2: Force None from get_allowed_mcp_servers")
|
||||
print("="*70)
|
||||
|
||||
from litellm.proxy._experimental.mcp_server.mcp_server_manager import global_mcp_server_manager
|
||||
|
||||
# Save original method
|
||||
original_method = global_mcp_server_manager.get_allowed_mcp_servers
|
||||
|
||||
# Patch to return None
|
||||
async def return_none(*args, **kwargs):
|
||||
print(" 🔧 get_allowed_mcp_servers called - returning None!")
|
||||
return None # This should cause the bug!
|
||||
|
||||
global_mcp_server_manager.get_allowed_mcp_servers = return_none
|
||||
|
||||
try:
|
||||
mcp_tool_config = {
|
||||
"type": "mcp",
|
||||
"server_url": "litellm_proxy",
|
||||
"require_approval": "never",
|
||||
}
|
||||
|
||||
response = await litellm.aresponses(
|
||||
model="gpt-4o-mini",
|
||||
tools=[mcp_tool_config],
|
||||
input=[{"role": "user", "type": "message", "content": "test"}],
|
||||
stream=True,
|
||||
)
|
||||
|
||||
async for chunk in response:
|
||||
print(f" Chunk: {getattr(chunk, 'type', 'unknown')}")
|
||||
|
||||
print(" ✅ No error occurred (bug might be fixed)")
|
||||
|
||||
except TypeError as e:
|
||||
if "NoneType" in str(e) and "not iterable" in str(e):
|
||||
print(f" 🐛 BUG REPRODUCED! Error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return True
|
||||
else:
|
||||
print(f" ❌ Different error: {e}")
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f" ❌ Other error: {type(e).__name__}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
# Restore original method
|
||||
global_mcp_server_manager.get_allowed_mcp_servers = original_method
|
||||
|
||||
return False
|
||||
|
||||
|
||||
async def test_with_none_tools_from_server():
|
||||
"""
|
||||
Force the bug by making _get_tools_from_mcp_servers return None.
|
||||
"""
|
||||
print("\n" + "="*70)
|
||||
print("🐛 TEST 3: Force None from _get_tools_from_mcp_servers")
|
||||
print("="*70)
|
||||
|
||||
from litellm.proxy._experimental.mcp_server import server as mcp_server_module
|
||||
|
||||
# Save original function
|
||||
original_func = mcp_server_module._get_tools_from_mcp_servers
|
||||
|
||||
# Patch to return None
|
||||
async def return_none(*args, **kwargs):
|
||||
print(" 🔧 _get_tools_from_mcp_servers called - returning None!")
|
||||
return None # This should cause the bug!
|
||||
|
||||
mcp_server_module._get_tools_from_mcp_servers = return_none
|
||||
|
||||
try:
|
||||
mcp_tool_config = {
|
||||
"type": "mcp",
|
||||
"server_url": "litellm_proxy",
|
||||
"require_approval": "never",
|
||||
}
|
||||
|
||||
response = await litellm.aresponses(
|
||||
model="gpt-4o-mini",
|
||||
tools=[mcp_tool_config],
|
||||
input=[{"role": "user", "type": "message", "content": "test"}],
|
||||
stream=True,
|
||||
)
|
||||
|
||||
async for chunk in response:
|
||||
print(f" Chunk: {getattr(chunk, 'type', 'unknown')}")
|
||||
|
||||
print(" ✅ No error occurred (bug might be fixed)")
|
||||
|
||||
except TypeError as e:
|
||||
if "NoneType" in str(e) and "not iterable" in str(e):
|
||||
print(f" 🐛 BUG REPRODUCED! Error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return True
|
||||
else:
|
||||
print(f" ❌ Different error: {e}")
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f" ❌ Other error: {type(e).__name__}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
# Restore original function
|
||||
mcp_server_module._get_tools_from_mcp_servers = original_func
|
||||
|
||||
return False
|
||||
|
||||
|
||||
async def test_handler_functions_directly():
|
||||
"""
|
||||
Test the handler functions directly with None inputs to reproduce the bug.
|
||||
"""
|
||||
print("\n" + "="*70)
|
||||
print("🐛 TEST 4: Direct function calls with None")
|
||||
print("="*70)
|
||||
|
||||
from litellm.responses.mcp.litellm_proxy_mcp_handler import LiteLLM_Proxy_MCP_Handler
|
||||
|
||||
bugs_found = []
|
||||
|
||||
# Test _deduplicate_mcp_tools
|
||||
print("\n Testing _deduplicate_mcp_tools(None, None)...")
|
||||
try:
|
||||
result = LiteLLM_Proxy_MCP_Handler._deduplicate_mcp_tools(None, None)
|
||||
print(f" ✅ Returned: {result}")
|
||||
except TypeError as e:
|
||||
if "NoneType" in str(e):
|
||||
print(f" 🐛 BUG! {e}")
|
||||
bugs_found.append("_deduplicate_mcp_tools")
|
||||
|
||||
# Test _filter_mcp_tools_by_allowed_tools
|
||||
print("\n Testing _filter_mcp_tools_by_allowed_tools(None, None)...")
|
||||
try:
|
||||
result = LiteLLM_Proxy_MCP_Handler._filter_mcp_tools_by_allowed_tools(None, None)
|
||||
print(f" ✅ Returned: {result}")
|
||||
except TypeError as e:
|
||||
if "NoneType" in str(e):
|
||||
print(f" 🐛 BUG! {e}")
|
||||
bugs_found.append("_filter_mcp_tools_by_allowed_tools")
|
||||
|
||||
# Test _get_allowed_mcp_servers_from_mcp_server_names
|
||||
print("\n Testing _get_allowed_mcp_servers_from_mcp_server_names([], None)...")
|
||||
try:
|
||||
from litellm.proxy._experimental.mcp_server.server import _get_allowed_mcp_servers_from_mcp_server_names
|
||||
result = await _get_allowed_mcp_servers_from_mcp_server_names([], None)
|
||||
print(f" ✅ Returned: {result}")
|
||||
except ImportError as e:
|
||||
print(f" ⏭️ Skipped (missing dependency: {e})")
|
||||
except TypeError as e:
|
||||
if "NoneType" in str(e):
|
||||
print(f" 🐛 BUG! {e}")
|
||||
bugs_found.append("_get_allowed_mcp_servers_from_mcp_server_names")
|
||||
|
||||
return bugs_found
|
||||
|
||||
|
||||
async def main():
|
||||
print("="*70)
|
||||
print("🔬 FORCE REPRODUCTION OF MCP BUG")
|
||||
print("="*70)
|
||||
print("""
|
||||
This script forces the bug to occur by patching internal functions
|
||||
to return None, simulating the conditions that cause:
|
||||
|
||||
"argument of type 'NoneType' is not iterable"
|
||||
""")
|
||||
|
||||
all_bugs = []
|
||||
|
||||
# Test 4 first - direct function calls (doesn't need real API)
|
||||
bugs = await test_handler_functions_directly()
|
||||
all_bugs.extend(bugs)
|
||||
|
||||
# The other tests need more setup, skip for now
|
||||
# await test_with_none_allowed_servers()
|
||||
# await test_with_none_allowed_mcp_servers()
|
||||
# await test_with_none_tools_from_server()
|
||||
|
||||
print("\n" + "="*70)
|
||||
print("📋 SUMMARY")
|
||||
print("="*70)
|
||||
|
||||
if all_bugs:
|
||||
print(f"\n🐛 Found bugs in {len(all_bugs)} function(s):")
|
||||
for bug in all_bugs:
|
||||
print(f" - {bug}")
|
||||
print("\nThese functions don't handle None inputs properly!")
|
||||
else:
|
||||
print("\n✅ No bugs found - all functions handle None properly")
|
||||
print(" (The defensive checks are working)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
64
simple_repro.py
Normal file
64
simple_repro.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple reproduction of the MCP NoneType bug.
|
||||
|
||||
This script demonstrates the exact bug that causes:
|
||||
"argument of type 'NoneType' is not iterable"
|
||||
|
||||
Run with fix reverted to see the bug:
|
||||
git checkout HEAD~2 -- litellm/responses/mcp/litellm_proxy_mcp_handler.py
|
||||
python simple_repro.py
|
||||
|
||||
Run with fix applied to see it working:
|
||||
git checkout HEAD -- litellm/responses/mcp/litellm_proxy_mcp_handler.py
|
||||
python simple_repro.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from litellm.responses.mcp.litellm_proxy_mcp_handler import LiteLLM_Proxy_MCP_Handler
|
||||
|
||||
print("="*60)
|
||||
print("🧪 MCP NoneType Bug Reproduction")
|
||||
print("="*60)
|
||||
|
||||
# This is the exact scenario that causes the bug:
|
||||
# When allowed_mcp_servers or mcp_tools is None (which can happen
|
||||
# in certain edge cases with back-to-back calls), the code crashes.
|
||||
|
||||
print("\nTest 1: _deduplicate_mcp_tools(None, None)")
|
||||
print("-" * 40)
|
||||
try:
|
||||
result = LiteLLM_Proxy_MCP_Handler._deduplicate_mcp_tools(None, None)
|
||||
print(f"✅ SUCCESS: Returned {result}")
|
||||
except TypeError as e:
|
||||
print(f"🐛 BUG REPRODUCED: {e}")
|
||||
|
||||
print("\nTest 2: _filter_mcp_tools_by_allowed_tools(None, None)")
|
||||
print("-" * 40)
|
||||
try:
|
||||
result = LiteLLM_Proxy_MCP_Handler._filter_mcp_tools_by_allowed_tools(None, None)
|
||||
print(f"✅ SUCCESS: Returned {result}")
|
||||
except TypeError as e:
|
||||
print(f"🐛 BUG REPRODUCED: {e}")
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("How this bug manifests in production:")
|
||||
print("="*60)
|
||||
print("""
|
||||
When making back-to-back MCP Responses API calls, if one of these
|
||||
conditions occurs:
|
||||
|
||||
1. get_allowed_mcp_servers() returns None (network/timing issue)
|
||||
2. get_mcp_servers_from_ids() returns None
|
||||
3. The MCP server has no tools configured
|
||||
|
||||
Then the code paths above get called with None, causing:
|
||||
"Error creating initial response iterator: argument of type 'NoneType' is not iterable"
|
||||
|
||||
The fix adds defensive None checks:
|
||||
if mcp_tools is None:
|
||||
mcp_tools = []
|
||||
""")
|
||||
252
stress_test_mcp.py
Normal file
252
stress_test_mcp.py
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
"""
|
||||
Stress test for MCP + Responses API to try to reproduce the intermittent bug.
|
||||
|
||||
This sends many concurrent and sequential requests to try to trigger race conditions.
|
||||
|
||||
Usage:
|
||||
export OPENAI_API_KEY="your-key" # or use litellm proxy
|
||||
python stress_test_mcp.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import List, Any
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
import litellm
|
||||
from litellm._logging import verbose_logger
|
||||
import logging
|
||||
|
||||
# Set to DEBUG to see the actual error
|
||||
verbose_logger.setLevel(logging.DEBUG)
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def make_mcp_streaming_call(call_id: int, model: str = "gpt-4o-mini") -> dict:
|
||||
"""Make a single MCP streaming call and return result info."""
|
||||
|
||||
mcp_tool_config = {
|
||||
"type": "mcp",
|
||||
"server_url": "litellm_proxy",
|
||||
"require_approval": "never",
|
||||
}
|
||||
|
||||
start_time = time.time()
|
||||
result = {
|
||||
"call_id": call_id,
|
||||
"success": False,
|
||||
"error": None,
|
||||
"chunks": 0,
|
||||
"duration": 0,
|
||||
}
|
||||
|
||||
try:
|
||||
response = await litellm.aresponses(
|
||||
model=model,
|
||||
tools=[mcp_tool_config],
|
||||
tool_choice="auto",
|
||||
input=[{
|
||||
"role": "user",
|
||||
"type": "message",
|
||||
"content": f"Call {call_id}: What is {call_id} + {call_id}?",
|
||||
}],
|
||||
stream=True,
|
||||
)
|
||||
|
||||
chunk_count = 0
|
||||
async for chunk in response:
|
||||
chunk_count += 1
|
||||
|
||||
result["success"] = True
|
||||
result["chunks"] = chunk_count
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"{type(e).__name__}: {str(e)}"
|
||||
result["error"] = error_msg
|
||||
logger.error(f"Call {call_id} FAILED: {error_msg}")
|
||||
|
||||
# Check if this is THE bug we're looking for
|
||||
if "NoneType" in str(e) and "not iterable" in str(e):
|
||||
logger.error("🐛 FOUND THE BUG! NoneType not iterable error!")
|
||||
|
||||
result["duration"] = time.time() - start_time
|
||||
return result
|
||||
|
||||
|
||||
async def test_sequential_rapid_fire(num_calls: int = 10, model: str = "gpt-4o-mini"):
|
||||
"""Send requests one after another as fast as possible."""
|
||||
logger.info(f"\n{'='*60}")
|
||||
logger.info(f"🔥 SEQUENTIAL RAPID-FIRE TEST ({num_calls} calls)")
|
||||
logger.info(f"{'='*60}")
|
||||
|
||||
results = []
|
||||
for i in range(num_calls):
|
||||
result = await make_mcp_streaming_call(i, model)
|
||||
results.append(result)
|
||||
status = "✅" if result["success"] else "❌"
|
||||
logger.info(f" {status} Call {i}: {result['chunks']} chunks in {result['duration']:.2f}s")
|
||||
# NO delay between calls - as fast as possible
|
||||
|
||||
return results
|
||||
|
||||
|
||||
async def test_concurrent_burst(num_calls: int = 5, model: str = "gpt-4o-mini"):
|
||||
"""Send multiple requests at the exact same time."""
|
||||
logger.info(f"\n{'='*60}")
|
||||
logger.info(f"⚡ CONCURRENT BURST TEST ({num_calls} simultaneous calls)")
|
||||
logger.info(f"{'='*60}")
|
||||
|
||||
tasks = [make_mcp_streaming_call(i, model) for i in range(num_calls)]
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
for i, result in enumerate(results):
|
||||
if isinstance(result, Exception):
|
||||
logger.error(f" ❌ Call {i}: Exception - {result}")
|
||||
else:
|
||||
status = "✅" if result["success"] else "❌"
|
||||
logger.info(f" {status} Call {i}: {result['chunks']} chunks in {result['duration']:.2f}s")
|
||||
|
||||
return results
|
||||
|
||||
|
||||
async def test_interleaved_calls(num_pairs: int = 5, model: str = "gpt-4o-mini"):
|
||||
"""Start a second call before the first one finishes."""
|
||||
logger.info(f"\n{'='*60}")
|
||||
logger.info(f"🔀 INTERLEAVED CALLS TEST ({num_pairs} pairs)")
|
||||
logger.info(f"{'='*60}")
|
||||
|
||||
results = []
|
||||
|
||||
for pair in range(num_pairs):
|
||||
# Start two calls with a small delay
|
||||
task1 = asyncio.create_task(make_mcp_streaming_call(pair * 2, model))
|
||||
await asyncio.sleep(0.1) # Start second call while first is in progress
|
||||
task2 = asyncio.create_task(make_mcp_streaming_call(pair * 2 + 1, model))
|
||||
|
||||
result1, result2 = await asyncio.gather(task1, task2)
|
||||
results.extend([result1, result2])
|
||||
|
||||
for r in [result1, result2]:
|
||||
status = "✅" if r["success"] else "❌"
|
||||
logger.info(f" {status} Call {r['call_id']}: {r['chunks']} chunks")
|
||||
|
||||
return results
|
||||
|
||||
|
||||
async def test_cancel_and_retry(model: str = "gpt-4o-mini"):
|
||||
"""Start a call, cancel it, then immediately start another."""
|
||||
logger.info(f"\n{'='*60}")
|
||||
logger.info(f"🚫 CANCEL AND RETRY TEST")
|
||||
logger.info(f"{'='*60}")
|
||||
|
||||
results = []
|
||||
|
||||
for i in range(3):
|
||||
# Start a call
|
||||
task = asyncio.create_task(make_mcp_streaming_call(i * 2, model))
|
||||
await asyncio.sleep(0.05) # Let it start
|
||||
task.cancel() # Cancel it
|
||||
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
logger.info(f" 🚫 Call {i*2} cancelled")
|
||||
|
||||
# Immediately start another
|
||||
result = await make_mcp_streaming_call(i * 2 + 1, model)
|
||||
results.append(result)
|
||||
status = "✅" if result["success"] else "❌"
|
||||
logger.info(f" {status} Call {i*2+1} after cancel: {result['chunks']} chunks")
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def summarize_results(all_results: List[dict]):
|
||||
"""Print a summary of all test results."""
|
||||
logger.info(f"\n{'='*60}")
|
||||
logger.info("📊 FINAL SUMMARY")
|
||||
logger.info(f"{'='*60}")
|
||||
|
||||
total = len(all_results)
|
||||
successes = sum(1 for r in all_results if isinstance(r, dict) and r.get("success"))
|
||||
failures = total - successes
|
||||
|
||||
logger.info(f" Total calls: {total}")
|
||||
logger.info(f" Successes: {successes}")
|
||||
logger.info(f" Failures: {failures}")
|
||||
|
||||
# Check for THE bug
|
||||
nonetype_errors = [r for r in all_results if isinstance(r, dict) and r.get("error") and "NoneType" in r["error"]]
|
||||
if nonetype_errors:
|
||||
logger.error(f"\n🐛 FOUND {len(nonetype_errors)} NoneType ERRORS!")
|
||||
for r in nonetype_errors:
|
||||
logger.error(f" Call {r['call_id']}: {r['error']}")
|
||||
else:
|
||||
logger.info("\n✅ No NoneType errors found")
|
||||
|
||||
if failures > 0:
|
||||
logger.info("\nAll errors:")
|
||||
for r in all_results:
|
||||
if isinstance(r, dict) and r.get("error"):
|
||||
logger.info(f" Call {r['call_id']}: {r['error']}")
|
||||
|
||||
|
||||
async def main():
|
||||
logger.info("="*60)
|
||||
logger.info("🧪 MCP + Responses API STRESS TEST")
|
||||
logger.info("="*60)
|
||||
logger.info("""
|
||||
Trying to reproduce the intermittent bug:
|
||||
"Error creating initial response iterator: argument of type 'NoneType' is not iterable"
|
||||
|
||||
This test will:
|
||||
1. Send rapid sequential requests
|
||||
2. Send concurrent burst requests
|
||||
3. Send interleaved requests
|
||||
4. Cancel and retry requests
|
||||
""")
|
||||
|
||||
# Check for API key
|
||||
if not os.getenv("OPENAI_API_KEY") and not os.getenv("LITELLM_API_KEY"):
|
||||
logger.warning("No API key found. Set OPENAI_API_KEY or LITELLM_API_KEY")
|
||||
logger.info("Running with mock to test the code paths...")
|
||||
|
||||
# Use mock response
|
||||
litellm.set_verbose = True
|
||||
|
||||
model = os.getenv("TEST_MODEL", "gpt-4o-mini")
|
||||
all_results = []
|
||||
|
||||
try:
|
||||
# Test 1: Sequential rapid fire
|
||||
results = await test_sequential_rapid_fire(num_calls=10, model=model)
|
||||
all_results.extend(results)
|
||||
|
||||
# Test 2: Concurrent burst
|
||||
results = await test_concurrent_burst(num_calls=5, model=model)
|
||||
all_results.extend([r for r in results if isinstance(r, dict)])
|
||||
|
||||
# Test 3: Interleaved calls
|
||||
results = await test_interleaved_calls(num_pairs=5, model=model)
|
||||
all_results.extend(results)
|
||||
|
||||
# Test 4: Cancel and retry
|
||||
results = await test_cancel_and_retry(model=model)
|
||||
all_results.extend(results)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("\nTest interrupted by user")
|
||||
except Exception as e:
|
||||
logger.error(f"Test failed with exception: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
summarize_results(all_results)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Loading…
Add table
Reference in a new issue