mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-19 10:19:07 +00:00
* Fix remaining VCR live-call leaks * test(vcr): dedupe live-test helpers and drop spurious kwargs Extract the duplicated isVertexQuotaError/runVertexRequestOrSkip Vertex quota-skip helpers into tests/pass_through_tests/vertex_test_helpers.js and the duplicated _skip_live_prompt_caching_test guard into tests/_live_test_helpers.py so each lives in one place. In test_aarun_thread_litellm, build a separate message_data carrying role/content for add_message and a thread_data without them for run_thread/run_thread_stream/get_messages, which no longer receive the spurious message fields. * test(overhead): assert mock transport is exercised in non-streaming and stream tests
186 lines
5.1 KiB
Python
186 lines
5.1 KiB
Python
import asyncio
|
|
import json
|
|
import time
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
import litellm
|
|
|
|
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"]
|
|
)
|