litellm/tests/unit/test_litellm_overhead.py
devin-ai-integration[bot] dfe4df8c8a
test: move whole-unit legacy test files into tests/unit and delete dead skips (#44807)
* test: delete unconditionally skipped legacy tests

* test: move whole-unit legacy test files into tests/unit

* test: keep moved legacy tests free of import-time global state

* test: keep the module-level invocation scan pointed at tests/local_testing

* ci: drop the agent_testing CircleCI job emptied by the move

* test: fix moved-test isolation and router coverage

* test: add Tinyfish search package marker

* test: isolate moved tests from logger state leaks

* test: isolate Helicone logging fixture state

* test: isolate Vertex pass-through credentials between moved tests

* test: cancel S3 periodic flush tasks started by moved tests

* ci: restore CircleCI assistant test selection after move

* test: prevent Datadog datetime import shadowing

* ci: drop the litellm_assistants_api_testing CircleCI job emptied by the move

* test: deduplicate imports in rebased unit tests

* test: remove duplicate passthrough router patch import

* test: remove moved legacy source files after rebase

* test: align moved tests with rebased main

* test: carry main's legacy-file edits into moved destinations

* test: make the moved cost map fallback tests assert the fetch and the backup

The four fallback cases only checked the result was non-empty, so they still
passed with integrity validation disabled. They now inject a mock client, assert
one fetch happened, and assert the result is exactly the local backup with the
fallback reason recorded.

---------

Co-authored-by: yuneng <yuneng@berri.ai>
2026-10-07 05:02:28 +00:00

193 lines
5.7 KiB
Python

import asyncio
import importlib
import json
import time
import httpx
import pytest
import litellm
from tests._vcr_conftest_common import install_live_call_probe, record_vcr_outcome
OPENAI_API_BASE = "https://example.openai.test/v1"
def _completion_payload(response_id="chatcmpl-test"):
return {
"id": response_id,
"object": "chat.completion",
"created": 1,
"model": "gpt-4o",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "Hello"},
"finish_reason": "stop",
}
],
"usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15},
}
def _stream_payload(response_id="chatcmpl-stream"):
chunks = [
{
"id": response_id,
"object": "chat.completion.chunk",
"created": 1,
"model": "gpt-4o",
"choices": [
{
"index": 0,
"delta": {"role": "assistant", "content": "Hello"},
"finish_reason": None,
}
],
},
{
"id": response_id,
"object": "chat.completion.chunk",
"created": 1,
"model": "gpt-4o",
"choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}],
"usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15},
},
]
return ("".join(f"data: {json.dumps(chunk)}\n\n" for chunk in chunks) + "data: [DONE]\n\n").encode()
def _mock_openai_completion_transport(monkeypatch, *, stream=False, response_id="chatcmpl-test"):
from litellm.llms.custom_httpx.aiohttp_transport import LiteLLMAiohttpTransport
calls = {"count": 0}
async def delayed_response(_transport, request):
calls["count"] += 1
await asyncio.sleep(0.2)
if stream:
return httpx.Response(
200,
content=_stream_payload(response_id),
headers={"content-type": "text/event-stream"},
request=request,
)
return httpx.Response(200, json=_completion_payload(response_id), request=request)
monkeypatch.setattr(
LiteLLMAiohttpTransport,
"handle_async_request",
delayed_response,
)
return calls
def _assert_overhead_is_smaller_than_total(response, total_time_ms):
litellm_overhead_ms = response._hidden_params["litellm_overhead_time_ms"]
overhead_percent = litellm_overhead_ms * 100 / total_time_ms
assert litellm_overhead_ms > 0
assert litellm_overhead_ms < 1000
assert litellm_overhead_ms < total_time_ms
assert overhead_percent < 40
@pytest.fixture(autouse=True)
def reset_litellm_state():
litellm.cache = None
litellm.success_callback = []
litellm._async_success_callback = []
litellm.failure_callback = []
litellm.callbacks = []
yield
litellm.cache = None
litellm.callbacks = []
@pytest.mark.asyncio
async def test_litellm_overhead_non_streaming(monkeypatch):
calls = _mock_openai_completion_transport(monkeypatch, response_id="chatcmpl-non-stream")
start_time = time.perf_counter()
response = await litellm.acompletion(
model="gpt-4o",
api_key="test-key",
api_base=OPENAI_API_BASE,
messages=[{"role": "user", "content": "Hello, world!"}],
)
total_time_ms = (time.perf_counter() - start_time) * 1000
assert calls["count"] == 1
_assert_overhead_is_smaller_than_total(response, total_time_ms)
@pytest.mark.asyncio
async def test_litellm_overhead_stream(monkeypatch):
calls = _mock_openai_completion_transport(monkeypatch, stream=True, response_id="chatcmpl-stream")
start_time = time.perf_counter()
response = await litellm.acompletion(
model="gpt-4o",
api_key="test-key",
api_base=OPENAI_API_BASE,
messages=[{"role": "user", "content": "Hello, world!"}],
stream=True,
)
async for _chunk in response:
pass
total_time_ms = (time.perf_counter() - start_time) * 1000
assert calls["count"] == 1
_assert_overhead_is_smaller_than_total(response, total_time_ms)
@pytest.mark.asyncio
async def test_litellm_overhead_cache_hit(monkeypatch):
from litellm.caching.caching import Cache
calls = _mock_openai_completion_transport(monkeypatch, response_id="chatcmpl-cache")
litellm.cache = Cache()
messages = [{"role": "user", "content": "Hello, world! Cache test"}]
response1 = await litellm.acompletion(
model="gpt-4o",
api_key="test-key",
api_base=OPENAI_API_BASE,
messages=messages,
caching=True,
)
await asyncio.sleep(0.5)
response2 = await litellm.acompletion(
model="gpt-4o",
api_key="test-key",
api_base=OPENAI_API_BASE,
messages=messages,
caching=True,
)
assert calls["count"] == 1
assert response1.id == response2.id
assert "_response_ms" in response2._hidden_params
assert response2._hidden_params["litellm_overhead_time_ms"] > 0
assert response2._hidden_params["litellm_overhead_time_ms"] < response2._hidden_params["_response_ms"]
@pytest.fixture(autouse=True)
def _vcr_outcome_gate(request, vcr):
install_live_call_probe(request, vcr)
yield
record_vcr_outcome(request, vcr)
@pytest.fixture(scope="function", autouse=True)
def setup_and_teardown():
"""
This fixture reloads litellm before every function. To speed up testing by removing callbacks being chained.
"""
importlib.reload(litellm)
loop = asyncio.get_event_loop_policy().new_event_loop()
asyncio.set_event_loop(loop)
yield
loop.close()
asyncio.set_event_loop(None)