mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-27 01:22:18 +00:00
* ci: run the unit_selection.sh shard files on every event instead of only fork pull requests Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * ci: rename fork-flag to unit-flag now that it applies on every event * test: move tests/test_litellm root and small trees into tests/unit Pure renames, no content changes. Follow-up commits in this PR fix references, merge the three files that already existed in tests/unit, keep live-provider tests in tests/test_litellm and wire CI. * test: carry tests/test_litellm conftest isolation into tests/unit Callback lists, routing fallbacks, cached HTTP clients, logger state, AWS, proxy-URL and keychain env, and session-end client cleanup now reset for unit tests too. The environment isolation owns its MonkeyPatch so a test's own monkeypatch is undone before the model-cost teardown runs. * test: merge, split and prune the moved root and small-tree tests Merge batches/test_batch_utils.py and the chat_completions and messages dispatch tests into the files that already existed in tests/unit. Keep the live Gemini interactions tests, the async image-fetch format test and the OpenAI embedding scorer test in tests/test_litellm since they need real network or keys. Put test_router.py under tests/unit/test_router so the existing package no longer shadows it. Delete eight tests the audit found superseded by stronger ones kept in this move. * ci: run the moved root and small-tree tests under their legacy flags Add the misc and responses-caching-types flags to unit_selection.sh and CircleCI, extend enterprise-routing and mcp-integration, and point the legacy GHA shards, Makefile, redis-compat workflow, merge smoke manifest and change classifier at the new paths. * test: make the new tests/unit directories packages tests/unit/test_package_layout.py requires every directory to carry an __init__.py, and without one the moved and retained test_litellm_responses_bridge.py modules collide on import. * test: scope the unit socket block to tests/unit in shared sessions The GHA shards collect the legacy test-path and the unit selection in one pytest session. The unit conftest's loopback-only block leaked into legacy modules that reach the network at import. The legacy conftest now lifts the restriction at collect and setup time, and the unit conftest re-applies it when collecting its own modules. * test: give the shard-script tests their own GITHUB_OUTPUT They only passed where the runner set it. The CircleCI unit job's env allowlist drops it, so the script's redirect failed there. * test: point the router and module-deletion checks at tests/unit router_code_coverage and code_qa_check_tests only searched tests/test_litellm, so the moved router tests no longer counted. The two silent-experiment tests the audit deleted were the only direct callers of those methods; they are replaced with tests that assert the forwarded shadow request and the recursion guard. --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
162 lines
5.3 KiB
Python
162 lines
5.3 KiB
Python
"""
|
|
E2E tests for shared session reuse feature
|
|
|
|
WHAT THIS TESTS:
|
|
When you pass shared_session to acompletion(), it should flow through
|
|
the entire call chain so the same aiohttp.ClientSession is reused for
|
|
connection pooling.
|
|
|
|
WHY THIS MATTERS:
|
|
Without session reuse, every request creates new TCP/TLS connections,
|
|
wasting ~100-500ms per request. With reuse, connections are pooled and
|
|
subsequent requests are 40-60% faster.
|
|
"""
|
|
|
|
import inspect
|
|
|
|
import pytest
|
|
|
|
|
|
import litellm
|
|
|
|
|
|
# ============================================================================
|
|
# HELPER FUNCTION
|
|
# ============================================================================
|
|
|
|
|
|
def is_parameter_active_in_source(source_code: str, search_pattern: str) -> bool:
|
|
"""
|
|
Check if a parameter/line exists in source code and is NOT commented out.
|
|
|
|
Args:
|
|
source_code: The source code to search
|
|
search_pattern: The text pattern to look for (e.g., "shared_session=shared_session")
|
|
|
|
Returns:
|
|
True if pattern found and not commented out, False otherwise
|
|
"""
|
|
lines = source_code.split("\n")
|
|
|
|
for line in lines:
|
|
if search_pattern in line:
|
|
# Make sure it's not commented out
|
|
stripped = line.strip()
|
|
if not stripped.startswith("#"):
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
# ============================================================================
|
|
# TEST 1: Check that the parameter exists in the API
|
|
# ============================================================================
|
|
|
|
|
|
def test_acompletion_accepts_shared_session():
|
|
"""Verify acompletion() has a shared_session parameter"""
|
|
sig = inspect.signature(litellm.acompletion)
|
|
|
|
assert (
|
|
"shared_session" in sig.parameters
|
|
), "acompletion() missing shared_session parameter"
|
|
|
|
# Should be optional (defaults to None)
|
|
assert sig.parameters["shared_session"].default is None
|
|
|
|
|
|
def test_completion_accepts_shared_session():
|
|
"""Verify completion() has a shared_session parameter"""
|
|
sig = inspect.signature(litellm.completion)
|
|
|
|
assert (
|
|
"shared_session" in sig.parameters
|
|
), "completion() missing shared_session parameter"
|
|
|
|
assert sig.parameters["shared_session"].default is None
|
|
|
|
|
|
# ============================================================================
|
|
# TEST 2: Check that acompletion passes it to completion
|
|
# ============================================================================
|
|
|
|
|
|
def test_acompletion_passes_session_to_completion():
|
|
"""
|
|
Verify that acompletion() includes shared_session in the kwargs
|
|
it passes to completion()
|
|
"""
|
|
source = inspect.getsource(litellm.acompletion)
|
|
|
|
# Check for both possible quote styles
|
|
found = is_parameter_active_in_source(
|
|
source, '"shared_session": shared_session'
|
|
) or is_parameter_active_in_source(source, "'shared_session': shared_session")
|
|
|
|
assert (
|
|
found
|
|
), "acompletion() doesn't include shared_session in completion_kwargs (or it's commented out)"
|
|
|
|
|
|
# ============================================================================
|
|
# TEST 3: Check the handler methods accept it
|
|
# ============================================================================
|
|
|
|
|
|
def test_handler_completion_accepts_shared_session():
|
|
"""Verify BaseLLMHTTPHandler.completion() accepts shared_session"""
|
|
from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler
|
|
|
|
sig = inspect.signature(BaseLLMHTTPHandler.completion)
|
|
|
|
assert (
|
|
"shared_session" in sig.parameters
|
|
), "Handler.completion() missing shared_session parameter"
|
|
|
|
|
|
def test_handler_async_completion_accepts_shared_session():
|
|
"""Verify BaseLLMHTTPHandler.async_completion() accepts shared_session"""
|
|
from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler
|
|
|
|
sig = inspect.signature(BaseLLMHTTPHandler.async_completion)
|
|
|
|
assert (
|
|
"shared_session" in sig.parameters
|
|
), "Handler.async_completion() missing shared_session parameter"
|
|
|
|
|
|
# ============================================================================
|
|
# TEST 4: THE KEY TEST - Does handler.completion pass it to async_completion?
|
|
# ============================================================================
|
|
|
|
|
|
def test_handler_passes_session_to_async_completion():
|
|
"""
|
|
🔑 KEY TEST - Verifies the fix from commit f0d6d3dd
|
|
|
|
The bug was: handler.completion() accepted shared_session but didn't
|
|
pass it to async_completion(). This test ensures it's being passed.
|
|
|
|
If this test fails, session reuse is BROKEN.
|
|
"""
|
|
from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler
|
|
|
|
source = inspect.getsource(BaseLLMHTTPHandler.completion)
|
|
|
|
# Check if shared_session is being passed (and not commented out)
|
|
found = is_parameter_active_in_source(source, "shared_session=shared_session")
|
|
|
|
assert found, """
|
|
CRITICAL BUG DETECTED!
|
|
|
|
shared_session is NOT being passed from completion() to async_completion()
|
|
|
|
This means session reuse is BROKEN. Every request will create new
|
|
connections instead of reusing them, causing 40-60% slower performance.
|
|
|
|
FIX: In BaseLLMHTTPHandler.completion(), when calling self.async_completion(),
|
|
add this parameter:
|
|
shared_session=shared_session
|
|
|
|
This was the bug fixed in commit f0d6d3dd - it may have regressed!
|
|
"""
|