mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-12 23:01:41 +00:00
* test(e2e): harden stage flakes for batches, UI, and MCP Unique batch model names avoid load-balancing onto stale azure-batch deployments that still pointed at the retired gpt-4.1-mini-batch, which only the managed/unified path was hitting. Retry batch retrieve on 500 and /ui/api-keys navigation on ERR_ABORTED. Skip the MCP key-access suite when the compose-only mcp-upstream is unreachable on stage k8s * test(e2e): cover Datadog remote MCP via search_datadog_logs Register the regional Datadog MCP endpoint with DD-API-KEY / DD-APPLICATION-KEY static headers (CI-safe header auth; browser OAuth is not headless-automatable). Seed a chat completion marked e2e-datadog-mcp-*, assert the proxy shipped it, list tools, call search_datadog_logs for the marker, and delete the server on teardown. Math-upstream key-access tests only skip when that compose service is unreachable * test(e2e): drop compose math MCP upstream; use Datadog only Key-access denial and happy-path MCP e2e both register the real regional Datadog remote MCP server with DD-API-KEY / DD-APPLICATION-KEY headers. Remove the mcp-upstream compose service and FastMCP add/multiply fixture * docs(e2e): require real Datadog MCP for all mcp suite tests Document that tests/e2e/mcp must register via datadog_mcp helpers against mcp.<site>/v1/mcp and must not introduce compose or fake MCP upstreams * chore: restore mcp_e2e_upstream_server.py Keep the FastMCP fixture file; e2e no longer wires it in compose, but the module itself is not part of the Datadog-only cleanup * fix(e2e): load tests/e2e/.env and fix datadog_reader importlib load pytest on the host never inherited compose env_file keys, so DD_API_KEY stayed empty. load_dotenv tests/e2e/.env in e2e_config. Register the dynamically loaded datadog_reader module in sys.modules so dataclasses do not crash under Python 3.12 * test(e2e/batches): harden azure/vertex unified lifecycle flakes Put the provider deployment name in every JSONL body so Azure does not depend on a perfect model rewrite. Retry create/retrieve/cancel on transient statuses with backoff. Drop cancel assertions for azure and vertex (registry only has a shared basic cell; create+retrieve prove routing, cancel stays best-effort cleanup) * test(e2e/ui): treat api-keys shell as success after SPA ERR_ABORTED Post-login client redirects abort the first /ui/api-keys/ goto on stage. Wait off /ui/login after cookie set, then accept the page once Create New Key is visible even if goto raised ERR_ABORTED * test(e2e): drop flaky key models dropdown Playwright suite API management e2e already covers key generate/update persistence. The UI Models-dropdown sentinel cases only added SPA ERR_ABORTED noise and no unique product signal. Remove the suite and unused browser fixtures * test(e2e/batches): fail clearly when OPENAI/AZURE provider is missing Replace bare next() over PROVIDERS with _model_for that raises ValueError naming the missing provider and the known list, instead of StopIteration * fix(e2e): migrate load suite from e2e_gateway to ProxyClient Stage collection failed with ModuleNotFoundError: e2e_gateway after the Gateway rename. Wire load/conftest and LoadClient to the shared ProxyClient fixture like every other suite * fix(e2e): drop duplicate datadog_mcp_url and CLAUDE section after merge
66 lines
2 KiB
Python
66 lines
2 KiB
Python
from __future__ import annotations
|
|
|
|
from collections.abc import Iterator
|
|
|
|
import pytest
|
|
from requests import RequestException
|
|
|
|
from e2e_http import NoBody, Success
|
|
from load_client import LoadClient, build_client
|
|
from load_constants import LOAD_MODEL
|
|
from models import KeyGenerateBody, LiteLLMParamsBody, ModelsListResponse
|
|
from lifecycle import ResourceManager
|
|
from proxy_client import ProxyClient
|
|
|
|
LOAD_MODEL_PARAMS = LiteLLMParamsBody(
|
|
model="openai/load-mock",
|
|
mock_response="This is a mock response for the throughput load test.",
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def client(proxy: ProxyClient) -> LoadClient:
|
|
return build_client(proxy)
|
|
|
|
|
|
def _model_is_servable(proxy: ProxyClient, model_name: str) -> bool:
|
|
result = proxy.transport.get(
|
|
"/v1/models",
|
|
headers=proxy.transport.master,
|
|
params=NoBody(),
|
|
response_type=ModelsListResponse,
|
|
)
|
|
return isinstance(result, Success) and any(entry.id == model_name for entry in result.data.data)
|
|
|
|
|
|
@pytest.fixture(scope="session", autouse=True)
|
|
def _ensure_load_model( # pyright: ignore[reportUnusedFunction] # pytest autouse session fixture, wired by name
|
|
client: LoadClient,
|
|
) -> Iterator[None]:
|
|
proxy = client.proxy
|
|
if _model_is_servable(proxy, LOAD_MODEL):
|
|
yield
|
|
return
|
|
|
|
try:
|
|
model_id = proxy.create_model(LOAD_MODEL, LOAD_MODEL_PARAMS)
|
|
except (AssertionError, RequestException) as exc:
|
|
if _model_is_servable(proxy, LOAD_MODEL):
|
|
yield
|
|
return
|
|
raise AssertionError(
|
|
f"failed to register {LOAD_MODEL!r} for the throughput load test "
|
|
f"(not listed on the data plane and /model/new failed): {exc}"
|
|
) from exc
|
|
|
|
try:
|
|
yield
|
|
finally:
|
|
proxy.delete_model(model_id)
|
|
|
|
|
|
@pytest.fixture
|
|
def load_key(resources: ResourceManager, client: LoadClient) -> str:
|
|
key = client.proxy.generate_key(KeyGenerateBody(models=[LOAD_MODEL], user_id="e2e-load"))
|
|
resources.defer(lambda: client.proxy.delete_key(key))
|
|
return key
|