litellm/tests/unit/proxy/decisions_endpoints/test_endpoints.py
Mateo Wang e34c779c67
fix(proxy): decode zstd and gzip request bodies before auth reads them (#47418)
* test(proxy): cover gzip and zstd request bodies on /v1/systemone

* fix(proxy): decode zstd and gzip request bodies before auth reads them

* fix(proxy): bound request body decompression and keep it inside CORS

Limit the decoded size by an expansion ratio and the operator's max_request_size_mb, pass bodies without the coding's magic number through, answer 415 when zstd is unavailable, and register the middleware innermost so errors carry CORS and security headers

* fix(proxy): budget decoded request bodies per worker and forward plugin bodies as sent

Each worker holds at most MAX_DECOMPRESSED_REQUEST_BODIES_IN_FLIGHT_MB of
decoded bodies and answers 503 past it. /plugin-proxy/ bodies reach the
plugin as sent, a zstd skippable frame counts as zstd, the decoded-size 413
names the decoded limit, and an integer max_request_size_mb stays an integer
in the 413 text.

* fix(proxy): read the magic number before size limits and reserve declared decoded sizes

* test(proxy): pin gzip mtime so parametrized test ids match across xdist workers

* fix(proxy): parse max_request_size_mb the same way in the auth size check

* fix(proxy): pass Claude Code OTLP export bodies through undecoded and skip the size parse without premium

* test(proxy): cover client disconnects, chunked bodies past the cap, and body-less request messages in the decompression middleware

* test(proxy): name the decompression test cases so CI logs stay small
2026-10-20 05:11:30 +00:00

1414 lines
53 KiB
Python

from __future__ import annotations
import asyncio
import gzip
import json
import random
import sys
from collections.abc import AsyncGenerator, Callable, Iterator, Mapping
from contextlib import asynccontextmanager
from pathlib import Path
from typing import Final
import pytest
import respx
from fastapi import FastAPI
from fastapi.routing import APIRoute
from fastapi.testclient import TestClient
from pydantic import TypeAdapter
from starlette.routing import Match
import litellm
from litellm.litellm_core_utils.decisions_pricing import apply_decisions_pricing
from litellm.proxy._lazy_features import LAZY_FEATURES, LazyFeature, attach_lazy_features
from litellm.proxy._types import UserAPIKeyAuth
from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache
from litellm.proxy.decisions_endpoints.endpoints import decisions, router, systemone
from litellm.proxy.pass_through_endpoints.pass_through_endpoints import SafeRouteAdder
from litellm.proxy.proxy_server import (
app,
cleanup_router_config_variables,
hash_token,
initialize,
)
from litellm.types.decisions import DecisionsRequestBody, OpenAIDecisionRequestBody
from tests._master_key import MASTER_KEY
if sys.version_info >= (3, 14):
from compression import zstd
else:
from backports import zstd
_INPUT_TOKENS: Final[int] = 367
_OUTPUT_TOKENS: Final[int] = 3
_RESPONSE: Final[Mapping[str, object]] = {
"model": "pplx-decider-v1-27b",
"answers": {
"is_defect": {"type": "noul", "noul": 0.9},
},
"usage": {"input_tokens": _INPUT_TOKENS, "output_tokens": _OUTPUT_TOKENS},
}
_STRANDS_RESPONSE: Final[Mapping[str, object]] = {
"model": "strands-decider-2B-hobson-v19",
"answers": {
"is_defect": {"type": "noul", "noul": 0.9},
},
"usage": {"input_tokens": 216, "output_tokens": 3},
"latency_ms": 3722.17,
}
_REQUEST: Final[Mapping[str, object]] = {
"model": "decider",
"state": {"source": "proxy-test"},
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
}
@pytest.fixture
def client(monkeypatch: pytest.MonkeyPatch) -> Iterator[TestClient]:
monkeypatch.setenv("OPENAI_API_KEY", "fake-openai-key")
monkeypatch.setenv("OPENAI_API_BASE", "https://fake-openai.example")
monkeypatch.setenv("REDIS_HOST", "localhost")
cleanup_router_config_variables()
config_path: Final = Path(__file__).parents[1] / "test_configs" / "test_config_no_auth.yaml"
asyncio.run(initialize(config=str(config_path), debug=True))
monkeypatch.setattr(
litellm.proxy.proxy_server,
"llm_router",
litellm.Router(
model_list=[
{
"model_name": "decider",
"litellm_params": {
"model": "perplexity/pplx-decider-v1-27b",
"api_key": "test-key",
},
}
]
),
)
monkeypatch.setattr(litellm, "disable_aiohttp_transport", True)
litellm.in_memory_llm_clients_cache.flush_cache()
yield TestClient(app)
litellm.in_memory_llm_clients_cache.flush_cache()
@pytest.mark.parametrize("endpoint", ("/v1/systemone", "/systemone"))
def test_proxy_decisions_route_returns_answers_and_cost(
client: TestClient,
respx_mock: respx.MockRouter,
endpoint: str,
) -> None:
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post(endpoint, json=_REQUEST)
assert response.status_code == 200, response.text
assert response.json()["answers"] == _RESPONSE["answers"]
assert "_hidden_params" not in response.json()
perplexity_cost: Final = litellm.model_cost["perplexity/pplx-decider-v1-27b"]
expected_cost: Final = _INPUT_TOKENS * float(perplexity_cost["input_cost_per_token"]) + _OUTPUT_TOKENS * float(
perplexity_cost["output_cost_per_token"]
)
assert expected_cost > 0
assert float(response.headers["x-litellm-response-cost"]) == pytest.approx(expected_cost)
assert upstream.called
assert json.loads(upstream.calls[0].request.content) == {
"model": "pplx-decider-v1-27b",
"state": {"source": "proxy-test"},
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
}
assert upstream.calls[0].request.headers["authorization"] == "Bearer test-key"
def test_proxy_decisions_dispatches_typesafe_deployment(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
) -> None:
router: Final = litellm.Router(
model_list=[
{
"model_name": "jev",
"litellm_params": {
"model": "typesafe/jev-latest",
"api_key": "k",
},
}
]
)
monkeypatch.setattr(litellm.proxy.proxy_server, "llm_router", router)
upstream: Final = respx_mock.post("https://api.typesafe.ai/v1/systemone").respond(json=_RESPONSE)
response: Final = client.post(
"/v1/systemone",
json={
"model": "jev",
"state": {"source": "proxy-test"},
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
},
)
assert response.status_code == 200, response.text
assert response.json()["answers"] == _RESPONSE["answers"]
assert upstream.called
assert json.loads(upstream.calls[0].request.content) == {
"model": "jev-latest",
"state": {"source": "proxy-test"},
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
}
def test_proxy_decisions_sends_the_env_key_to_the_deployment_api_base(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
) -> None:
monkeypatch.setenv("PERPLEXITYAI_API_KEY", "server-key")
monkeypatch.delenv("PERPLEXITY_API_KEY", raising=False)
router: Final = litellm.Router(
model_list=[
{
"model_name": "decider",
"litellm_params": {
"model": "perplexity/pplx-decider-v1-27b",
"api_base": "https://egress.example/perplexity",
},
}
]
)
monkeypatch.setattr(litellm.proxy.proxy_server, "llm_router", router)
upstream: Final = respx_mock.post("https://egress.example/perplexity/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post("/v1/systemone", json=_REQUEST)
assert response.status_code == 200, response.text
assert upstream.call_count == 1
assert upstream.calls[0].request.headers["authorization"] == "Bearer server-key"
def test_proxy_decisions_unknown_model_is_a_client_error(
client: TestClient,
respx_mock: respx.MockRouter,
) -> None:
response: Final = client.post(
"/v1/systemone",
json={
"model": "missing-model",
"state": "review",
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
},
)
assert 400 <= response.status_code < 500, response.text
assert len(respx_mock.calls) == 0
@pytest.mark.parametrize(
"request_body",
(
{
"model": "decider",
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
},
{
"model": "decider",
"state": {"source": "proxy-test"},
},
),
ids=("missing_state", "missing_questions"),
)
def test_proxy_decisions_missing_required_field_is_a_client_error(
client: TestClient,
respx_mock: respx.MockRouter,
request_body: Mapping[str, object],
) -> None:
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post("/v1/systemone", json=request_body)
assert response.status_code == 400, response.text
assert not upstream.called
def test_proxy_decisions_dispatches_strands_decider(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
) -> None:
monkeypatch.delenv("STRANDS_DECIDER_API_BASE", raising=False)
monkeypatch.delenv("STRANDS_DECIDER_API_KEY", raising=False)
router: Final = litellm.Router(
model_list=[
{
"model_name": "strands",
"litellm_params": {
"model": "strands_decider/strands-decider-2B-hobson-v19",
"api_base": "https://strands.example",
},
}
]
)
monkeypatch.setattr(litellm.proxy.proxy_server, "llm_router", router)
upstream: Final = respx_mock.post("https://strands.example/v1/systemone").respond(json=_STRANDS_RESPONSE)
response: Final = client.post(
"/v1/systemone",
json={
"model": "strands",
"state": {"source": "proxy-test"},
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
},
)
assert response.status_code == 200, response.text
assert response.json()["answers"] == _STRANDS_RESPONSE["answers"]
assert upstream.called
assert json.loads(upstream.calls[0].request.content) == {
"model": "strands-decider-2B-hobson-v19",
"state": {"source": "proxy-test"},
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
}
assert "authorization" not in upstream.calls[0].request.headers
@pytest.mark.parametrize(
("model", "env_base", "env_key"),
(
("laya/english", "LAYA_API_BASE", "LAYA_API_KEY"),
("bespoke/nimble-latest", "BESPOKE_API_BASE", "BESPOKE_API_KEY"),
),
ids=("laya", "bespoke"),
)
def test_proxy_decisions_dispatches_oss_classifier_deployment(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
model: str,
env_base: str,
env_key: str,
) -> None:
monkeypatch.delenv(env_base, raising=False)
monkeypatch.delenv(env_key, raising=False)
router: Final = litellm.Router(
model_list=[
{
"model_name": "oss-classifier",
"litellm_params": {
"model": model,
"api_base": "https://oss-decider.example",
},
}
]
)
monkeypatch.setattr(litellm.proxy.proxy_server, "llm_router", router)
upstream: Final = respx_mock.post("https://oss-decider.example/v1/systemone").respond(json=_STRANDS_RESPONSE)
response: Final = client.post(
"/v1/systemone",
json={
"model": "oss-classifier",
"state": {"source": "proxy-test"},
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
},
)
assert response.status_code == 200, response.text
assert response.json()["answers"] == _STRANDS_RESPONSE["answers"]
assert upstream.called
assert json.loads(upstream.calls[0].request.content) == {
"model": model.partition("/")[2],
"state": {"source": "proxy-test"},
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
}
assert "authorization" not in upstream.calls[0].request.headers
def test_proxy_systemone_dispatches_databricks_serving_endpoint(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
) -> None:
monkeypatch.delenv("DATABRICKS_API_BASE", raising=False)
monkeypatch.delenv("DATABRICKS_API_KEY", raising=False)
monkeypatch.delenv("DATABRICKS_TOKEN", raising=False)
router: Final = litellm.Router(
model_list=[
{
"model_name": "databricks-decider",
"litellm_params": {
"model": "databricks/databricks-openjev-qwen35-4b",
"api_base": "https://workspace.example/serving-endpoints",
"api_key": "dapi-deployment",
},
}
]
)
monkeypatch.setattr(litellm.proxy.proxy_server, "llm_router", router)
upstream: Final = respx_mock.post(
"https://workspace.example/serving-endpoints/databricks-openjev-qwen35-4b/invocations"
).respond(json={**_RESPONSE, "model": "/mosaicml/local_model"})
response: Final = client.post(
"/v1/systemone",
json={
"model": "databricks-decider",
"state": {"source": "proxy-test"},
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
},
)
assert response.status_code == 200, response.text
assert response.json()["answers"] == _RESPONSE["answers"]
assert upstream.called
assert json.loads(upstream.calls[0].request.content) == {
"model": "databricks-openjev-qwen35-4b",
"state": {"source": "proxy-test"},
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
}
assert upstream.calls[0].request.headers["authorization"] == "Bearer dapi-deployment"
def test_proxy_systemone_dispatches_databricks_ai_decide(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
) -> None:
monkeypatch.delenv("DATABRICKS_API_BASE", raising=False)
monkeypatch.delenv("DATABRICKS_API_KEY", raising=False)
monkeypatch.delenv("DATABRICKS_TOKEN", raising=False)
router: Final = litellm.Router(
model_list=[
{
"model_name": "databricks-decider",
"litellm_params": {
"model": "databricks/ai_decide",
"api_base": "https://workspace.example/serving-endpoints",
"api_key": "dapi-deployment",
},
}
]
)
monkeypatch.setattr(litellm.proxy.proxy_server, "llm_router", router)
upstream: Final = respx_mock.post("https://workspace.example/api/2.0/ai-functions/ai-decide").respond(
json={
"response": {"answers": {"is_defect": {"type": "noul", "probability": 0.9}}},
"metadata": {"version": "1.0"},
}
)
response: Final = client.post(
"/v1/systemone",
json={
"model": "databricks-decider",
"state": {"source": "proxy-test"},
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
},
)
assert response.status_code == 200, response.text
assert response.json()["answers"] == _RESPONSE["answers"]
assert upstream.called
assert json.loads(upstream.calls[0].request.content) == {
"state": {"source": "proxy-test"},
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
}
assert upstream.calls[0].request.headers["authorization"] == "Bearer dapi-deployment"
def test_proxy_decisions_without_model_uses_the_proxy_default_model(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
) -> None:
monkeypatch.setattr(litellm.proxy.proxy_server, "user_model", "decider")
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post(
"/v1/systemone", json={key: value for key, value in _REQUEST.items() if key != "model"}
)
assert response.status_code == 200, response.text
assert response.json()["answers"] == _RESPONSE["answers"]
assert upstream.called
assert json.loads(upstream.calls[0].request.content)["model"] == "pplx-decider-v1-27b"
def test_a_request_body_cannot_turn_on_box_questions_for_a_proxy_without_a_router(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
) -> None:
monkeypatch.setattr(litellm, "drop_params", False)
monkeypatch.setattr(litellm.proxy.proxy_server, "llm_router", None)
monkeypatch.setattr(litellm.proxy.proxy_server, "user_model", "openai/gpt-6-luna")
upstream: Final = respx_mock.post("https://fake-openai.example/v1/decisions").respond(json={})
response: Final = client.post(
"/v1/decisions",
json={
"model": "openai/gpt-6-luna",
"input": "A phone screen with a diagonal crack.",
"questions": [{"type": "box", "name": "crack", "instructions": "Where is the crack?"}],
"model_info": {"supports_box_questions": True},
},
)
assert response.status_code == 400, response.text
assert "['box questions']" in response.json()["error"]["message"]
assert not upstream.called
_OPENAI_FORMAT_REQUEST: Final[Mapping[str, object]] = {
"model": "decider",
"input": [
{
"role": "user",
"content": [
{"type": "input_text", "text": "The package arrived with a broken screen."},
{"type": "input_text", "text": "I want a refund."},
],
},
{"role": "user", "content": "Order 1234."},
],
"questions": [
{"type": "predicate", "name": "damaged", "instructions": "Does the customer report a damaged item?"},
{
"type": "choice",
"instructions": "Should we refund?",
"choices": [{"value": True, "description": "Refund now"}, {"value": "escalate"}],
},
{
"type": "score",
"name": "severity",
"instructions": "How severe is the issue?",
"levels": [{"label": "minor"}, {"label": "major", "description": "Product unusable"}],
},
{"type": "predicate", "name": "fraud", "instructions": "Is this fraud?"},
],
"safety_identifier": "end-user-1",
}
_SYSTEMONE_ANSWERS_FOR_OPENAI_REQUEST: Final[Mapping[str, object]] = {
"model": "pplx-decider-v1-27b",
"answers": {
"0": {"type": "noul", "noul": 0.95},
"1": {"type": "choice", "choice": "true", "confidence": 0.8, "probabilities": {"true": 0.9, "escalate": 0.1}},
"2": {
"type": "score",
"score": 0.7,
"confidence": 0.6,
"legend": {"0": "minor", "1": "major: Product unusable"},
"probabilities": {"0": 0.3, "1": 0.7},
},
},
"usage": {"input_tokens": _INPUT_TOKENS, "output_tokens": _OUTPUT_TOKENS},
}
_OPENAI_FORMAT_ANSWERS: Final = [
{"type": "predicate", "name": "damaged", "probability": 0.95},
{
"type": "choice",
"name": None,
"choice": True,
"probabilities": [{"value": True, "probability": 0.9}, {"value": "escalate", "probability": 0.1}],
"confidence": 0.8,
},
{
"type": "score",
"name": "severity",
"score": 0.7,
"probabilities": [
{"value": 0, "label": "minor", "probability": 0.3},
{"value": 1, "label": "major", "probability": 0.7},
],
"confidence": 0.6,
},
{"type": "refusal", "name": "fraud"},
]
@pytest.mark.parametrize("endpoint", ("/v1/decisions", "/decisions"))
def test_openai_format_decisions_translate_through_systemone(
client: TestClient,
respx_mock: respx.MockRouter,
endpoint: str,
) -> None:
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(
json=_SYSTEMONE_ANSWERS_FOR_OPENAI_REQUEST
)
response: Final = client.post(endpoint, json=_OPENAI_FORMAT_REQUEST)
assert response.status_code == 200, response.text
assert json.loads(upstream.calls[0].request.content) == {
"model": "pplx-decider-v1-27b",
"state": "The package arrived with a broken screen.\n\nI want a refund.\n\nOrder 1234.",
"questions": {
"0": {"type": "noul", "instructions": "Does the customer report a damaged item?"},
"1": {
"type": "choice",
"instructions": "Should we refund?",
"criteria": {"true": "Refund now", "escalate": None},
},
"2": {
"type": "score",
"instructions": "How severe is the issue?",
"criteria": ["minor", "major: Product unusable"],
},
"3": {"type": "noul", "instructions": "Is this fraud?"},
},
}
body: Final = response.json()
assert body["model"] == _SYSTEMONE_ANSWERS_FOR_OPENAI_REQUEST["model"]
assert body["answers"] == _OPENAI_FORMAT_ANSWERS
assert body["usage"] == {
"input_tokens": _INPUT_TOKENS,
"input_tokens_details": {"cached_tokens": 0, "cache_write_tokens": 0},
"output_tokens": _OUTPUT_TOKENS,
"output_tokens_details": {"reasoning_tokens": 0},
"total_tokens": _INPUT_TOKENS + _OUTPUT_TOKENS,
}
assert float(response.headers["x-litellm-response-cost"]) > 0
@pytest.mark.parametrize(
("endpoint", "request_body", "upstream_response"),
(
("/v1/systemone", _REQUEST, _RESPONSE),
("/v1/decisions", _OPENAI_FORMAT_REQUEST, _SYSTEMONE_ANSWERS_FOR_OPENAI_REQUEST),
),
ids=("systemone", "openai_format"),
)
def test_decisions_return_guardrail_information_when_requested(
client: TestClient,
respx_mock: respx.MockRouter,
endpoint: str,
request_body: Mapping[str, object],
upstream_response: Mapping[str, object],
) -> None:
respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=upstream_response)
response: Final = client.post(endpoint, json={**request_body, "include_guardrail_response": True})
assert response.status_code == 200, response.text
assert response.json()["guardrail_information"] == []
@pytest.mark.parametrize(
"request_body",
(
_REQUEST,
{
"model": "decider",
"input": "review",
"questions": [
{
"type": "choice",
"instructions": "Pick one",
"choices": [{"value": True}, {"value": "true"}],
}
],
},
),
ids=("systemone_body", "colliding_choice_values"),
)
def test_openai_format_decisions_rejects_bodies_it_cannot_translate(
client: TestClient,
respx_mock: respx.MockRouter,
request_body: Mapping[str, object],
) -> None:
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post("/v1/decisions", json=request_body)
assert response.status_code == 400, response.text
assert not upstream.called
@pytest.mark.parametrize("endpoint", ("/v1/systemone", "/v1/decisions"))
@pytest.mark.parametrize("raw_body", (b"", b"{not json"), ids=("empty", "malformed"))
def test_a_body_that_is_not_json_is_a_client_error(
client: TestClient,
respx_mock: respx.MockRouter,
endpoint: str,
raw_body: bytes,
) -> None:
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post(endpoint, content=raw_body, headers={"Content-Type": "application/json"})
assert response.status_code == 400, response.text
assert not upstream.called
def test_openai_format_decisions_reach_an_openai_deployment_unchanged_including_images(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
) -> None:
monkeypatch.setitem(
litellm.model_cost,
"gpt-6-luna",
{
**litellm.model_cost["gpt-6-luna"],
"input_cost_per_token_decisions": 1e-07,
"cache_read_input_token_cost_decisions": 1e-07,
"output_cost_per_token_decisions": 0.0,
},
)
monkeypatch.setattr(litellm, "api_base", None)
monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
monkeypatch.delenv("OPENAI_API_BASE", raising=False)
monkeypatch.setattr(
litellm.proxy.proxy_server,
"llm_router",
litellm.Router(
model_list=[{"model_name": "decider", "litellm_params": {"model": "openai/gpt-6-luna", "api_key": "k"}}]
),
)
image_message: Final = {
"type": "message",
"role": "user",
"content": [{"type": "input_image", "image_url": "data:image/png;base64,AA==", "detail": "low"}],
}
request_body: Final = {**_OPENAI_FORMAT_REQUEST, "input": [*_OPENAI_FORMAT_REQUEST["input"], image_message]}
cached_tokens: Final = 128
upstream_usage: Final = {
"input_tokens": _INPUT_TOKENS,
"input_tokens_details": {"cached_tokens": cached_tokens, "cache_write_tokens": 0},
"output_tokens": _OUTPUT_TOKENS,
"output_tokens_details": {"reasoning_tokens": 0},
"total_tokens": _INPUT_TOKENS + _OUTPUT_TOKENS,
}
upstream: Final = respx_mock.post("https://api.openai.com/v1/decisions").respond(
json={"model": "gpt-6-luna", "answers": _OPENAI_FORMAT_ANSWERS, "usage": upstream_usage}
)
response: Final = client.post("/v1/decisions", json=request_body)
assert response.status_code == 200, response.text
assert json.loads(upstream.calls[0].request.content) == {
"model": "gpt-6-luna",
"input": [
{
"type": "message",
"role": "user",
"content": [
{"type": "input_text", "text": "The package arrived with a broken screen."},
{"type": "input_text", "text": "I want a refund."},
],
},
{"type": "message", "role": "user", "content": "Order 1234."},
image_message,
],
"questions": _OPENAI_FORMAT_REQUEST["questions"],
"safety_identifier": "end-user-1",
}
body: Final = response.json()
assert body["answers"] == _OPENAI_FORMAT_ANSWERS
assert body["usage"] == upstream_usage
luna_decisions_cost: Final = apply_decisions_pricing(litellm.model_cost["gpt-6-luna"])
expected_cost: Final = _INPUT_TOKENS * float(luna_decisions_cost["input_cost_per_token"]) + _OUTPUT_TOKENS * float(
luna_decisions_cost["output_cost_per_token"]
)
assert expected_cost > 0
assert float(response.headers["x-litellm-response-cost"]) == pytest.approx(expected_cost)
_CLEF_OMNI_MEDIA: Final[Mapping[str, object]] = {
"audio": ["data:audio/wav;base64,YQ==", {"content_type": "audio/webm;codecs=opus", "base64": "Yg=="}],
"videos": ["data:video/mp4;base64,Yw=="],
}
_CLEF_OMNI_RESPONSE: Final[Mapping[str, object]] = {
"success": True,
"result": {
"model": "clef-omni",
"answers": {"heard": {"type": "noul", "noul": 0.9}},
"usage": {"input_tokens": 100, "output_tokens": 0},
},
}
_CLEF_OMNI_UPSTREAM: Final = "https://cf.example/ai/run/@cf/cloudflare/clef-omni"
def _route_to_clef_omni(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
litellm.proxy.proxy_server,
"llm_router",
litellm.Router(
model_list=[
{
"model_name": "clef-omni",
"litellm_params": {
"model": "cloudflare/clef-omni",
"api_base": "https://cf.example",
"api_key": "cf-key",
},
},
{
"model_name": "decider",
"litellm_params": {"model": "perplexity/pplx-decider-v1-27b", "api_key": "k"},
},
]
),
)
def _clef_omni_request(endpoint: str) -> Mapping[str, object]:
if endpoint.endswith("systemone"):
return {
"model": "clef-omni",
"state": "Evaluate the clips",
"questions": {"heard": {"type": "noul", "instructions": "Is there speech?"}},
**_CLEF_OMNI_MEDIA,
}
return {
"model": "clef-omni",
"input": "Evaluate the clips",
"questions": [{"type": "predicate", "name": "heard", "instructions": "Is there speech?"}],
**_CLEF_OMNI_MEDIA,
}
@pytest.mark.parametrize("endpoint", ("/v1/systemone", "/systemone", "/v1/decisions", "/decisions"))
def test_proxy_forwards_clef_omni_media_on_every_decisions_route(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
endpoint: str,
) -> None:
_route_to_clef_omni(monkeypatch)
upstream: Final = respx_mock.post(_CLEF_OMNI_UPSTREAM).respond(json=_CLEF_OMNI_RESPONSE)
response: Final = client.post(endpoint, json=_clef_omni_request(endpoint))
assert response.status_code == 200, response.text
assert response.json()["answers"] == (
{"heard": {"type": "noul", "noul": 0.9}}
if endpoint.endswith("systemone")
else [{"type": "predicate", "name": "heard", "probability": 0.9}]
)
assert json.loads(upstream.calls[0].request.content) == {
"model": "clef-omni",
"state": "Evaluate the clips",
"questions": {"heard": {"type": "noul", "instructions": "Is there speech?"}},
**_CLEF_OMNI_MEDIA,
}
@pytest.mark.parametrize(
("endpoint", "request_body"),
(
("/v1/systemone", {**_REQUEST, **_CLEF_OMNI_MEDIA}),
("/v1/decisions", {**_clef_omni_request("/v1/decisions"), "audio": [{"content_type": "audio/wav"}]}),
("/systemone", {**_clef_omni_request("/systemone"), "audio": ["data:image/png;base64,AA=="]}),
("/decisions", {**_clef_omni_request("/decisions"), "videos": ["data:video/mp4;base64,Yw=="] * 3}),
),
ids=("model_without_media_support", "clip_missing_base64", "audio_slot_holding_an_image", "too_many_videos"),
)
def test_proxy_refuses_decisions_media_it_cannot_send(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
endpoint: str,
request_body: Mapping[str, object],
) -> None:
monkeypatch.setattr(litellm, "drop_params", False)
_route_to_clef_omni(monkeypatch)
clef_upstream: Final = respx_mock.post(_CLEF_OMNI_UPSTREAM).respond(json=_CLEF_OMNI_RESPONSE)
perplexity_upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post(endpoint, json=request_body)
assert response.status_code == 400, response.text
assert not clef_upstream.called
assert not perplexity_upstream.called
@pytest.mark.parametrize("safety_identifier", (7, ["end-user-1"]), ids=("numeric", "list"))
@pytest.mark.parametrize("deployment_drops_params", (False, True), ids=("strict", "drop_params"))
def test_a_non_string_safety_identifier_is_refused_unless_the_deployment_drops_params(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
safety_identifier: object,
deployment_drops_params: bool,
) -> None:
monkeypatch.setattr(litellm, "drop_params", False)
monkeypatch.setattr(litellm, "api_base", None)
monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
monkeypatch.delenv("OPENAI_API_BASE", raising=False)
monkeypatch.setattr(
litellm.proxy.proxy_server,
"llm_router",
litellm.Router(
model_list=[
{
"model_name": "decider",
"litellm_params": {
"model": "openai/gpt-6-luna",
"api_key": "k",
"drop_params": deployment_drops_params,
},
}
]
),
)
upstream: Final = respx_mock.post("https://api.openai.com/v1/decisions").respond(
json={
"model": "gpt-6-luna",
"answers": _OPENAI_FORMAT_ANSWERS[:1],
"usage": {
"input_tokens": _INPUT_TOKENS,
"output_tokens": _OUTPUT_TOKENS,
"total_tokens": _INPUT_TOKENS + _OUTPUT_TOKENS,
},
}
)
request_body: Final = {
"model": "decider",
"input": "The package arrived with a broken screen.",
"questions": _OPENAI_FORMAT_REQUEST["questions"][:1],
"safety_identifier": safety_identifier,
}
response: Final = client.post("/v1/decisions", json=request_body)
if not deployment_drops_params:
assert response.status_code == 400, response.text
assert "safety_identifier" in response.json()["error"]["message"]
assert not upstream.called
return
assert response.status_code == 200, response.text
assert json.loads(upstream.calls[0].request.content) == {
"model": "gpt-6-luna",
"input": "The package arrived with a broken screen.",
"questions": _OPENAI_FORMAT_REQUEST["questions"][:1],
}
_IMAGE_TEXT: Final = "Is the screen cracked?"
_PNG_DATA_URL: Final = "data:image/png;base64,AA=="
_IMAGE_ENDPOINTS: Final = ("/v1/decisions", "/v1/systemone")
_IMAGE_REQUEST_BODIES: Final[tuple[Mapping[str, object], ...]] = (
{
"model": "decider",
"input": [
{
"role": "user",
"content": [
{"type": "input_text", "text": _IMAGE_TEXT},
{"type": "input_image", "image_url": _PNG_DATA_URL},
],
}
],
"questions": [{"type": "predicate", "name": "is_defect", "instructions": "Is this a defect?"}],
},
{
"model": "decider",
"state": _IMAGE_TEXT,
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
"images": [_PNG_DATA_URL],
},
)
_IMAGE_REQUESTS: Final = tuple(zip(_IMAGE_ENDPOINTS, _IMAGE_REQUEST_BODIES, strict=True))
_IMAGE_REQUEST_IDS: Final = ("openai_format", "systemone_format")
def _route_decider_to(monkeypatch: pytest.MonkeyPatch, litellm_params: Mapping[str, object]) -> None:
monkeypatch.setattr(
litellm.proxy.proxy_server,
"llm_router",
litellm.Router(model_list=[{"model_name": "decider", "litellm_params": dict(litellm_params)}]),
)
@pytest.mark.parametrize(("endpoint", "request_body"), _IMAGE_REQUESTS, ids=_IMAGE_REQUEST_IDS)
def test_images_reach_a_cloudflare_clef_deployment_as_its_images_field(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
endpoint: str,
request_body: Mapping[str, object],
) -> None:
_route_decider_to(
monkeypatch,
{
"model": "cloudflare/clef",
"api_key": "cloudflare-key",
"api_base": "https://api.cloudflare.com/client/v4/accounts/acct/ai/run",
},
)
upstream: Final = respx_mock.post(
"https://api.cloudflare.com/client/v4/accounts/acct/ai/run/@cf/cloudflare/clef"
).respond(json=_RESPONSE)
response: Final = client.post(endpoint, json=request_body)
assert response.status_code == 200, response.text
assert json.loads(upstream.calls[0].request.content) == {
"model": "clef",
"state": _IMAGE_TEXT,
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
"images": [_PNG_DATA_URL],
}
@pytest.mark.parametrize(
("endpoint", "request_body", "param"),
tuple(zip(_IMAGE_ENDPOINTS, _IMAGE_REQUEST_BODIES, ("input_image", "images"), strict=True)),
ids=_IMAGE_REQUEST_IDS,
)
def test_images_to_a_text_only_deployment_are_refused_without_drop_params(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
endpoint: str,
request_body: Mapping[str, object],
param: str,
) -> None:
monkeypatch.setattr(litellm, "drop_params", False)
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post(endpoint, json=request_body)
assert response.status_code == 400, response.text
assert f"['{param}']" in response.json()["error"]["message"]
assert not upstream.called
@pytest.mark.parametrize(("endpoint", "request_body"), _IMAGE_REQUESTS, ids=_IMAGE_REQUEST_IDS)
def test_images_to_a_text_only_deployment_are_dropped_when_it_drops_params(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
endpoint: str,
request_body: Mapping[str, object],
) -> None:
monkeypatch.setattr(litellm, "drop_params", False)
_route_decider_to(
monkeypatch, {"model": "perplexity/pplx-decider-v1-27b", "api_key": "test-key", "drop_params": True}
)
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post(endpoint, json=request_body)
assert response.status_code == 200, response.text
assert json.loads(upstream.calls[0].request.content) == {
"model": "pplx-decider-v1-27b",
"state": _IMAGE_TEXT,
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
}
@pytest.mark.parametrize("deployment_drops_params", (False, True), ids=("strict", "drop_params"))
def test_malformed_images_are_refused_unless_the_deployment_drops_params(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
deployment_drops_params: bool,
) -> None:
monkeypatch.setattr(litellm, "drop_params", False)
_route_decider_to(
monkeypatch,
{
"model": "cloudflare/clef",
"api_key": "cloudflare-key",
"api_base": "https://api.cloudflare.com/client/v4/accounts/acct/ai/run",
"drop_params": deployment_drops_params,
},
)
upstream: Final = respx_mock.post(
"https://api.cloudflare.com/client/v4/accounts/acct/ai/run/@cf/cloudflare/clef"
).respond(json=_RESPONSE)
response: Final = client.post(
"/v1/systemone", json={**_IMAGE_REQUEST_BODIES[1], "images": [{"url": _PNG_DATA_URL}]}
)
if not deployment_drops_params:
assert response.status_code == 400, response.text
assert "images" in response.json()["error"]["message"]
assert not upstream.called
return
assert response.status_code == 200, response.text
assert json.loads(upstream.calls[0].request.content) == {
"model": "clef",
"state": _IMAGE_TEXT,
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
}
@pytest.mark.parametrize("deployment_drops_params", (False, True), ids=("strict", "drop_params"))
def test_malformed_assets_for_a_deployment_without_asset_support_are_dropped_only_when_it_drops_params(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
deployment_drops_params: bool,
) -> None:
monkeypatch.setattr(litellm, "drop_params", False)
_route_decider_to(
monkeypatch,
{"model": "perplexity/pplx-decider-v1-27b", "api_key": "test-key", "drop_params": deployment_drops_params},
)
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post(
"/v1/systemone",
json={**_IMAGE_REQUEST_BODIES[1], "assets": {"photo-1": {"type": "image", "part": "file1"}}},
)
if not deployment_drops_params:
assert response.status_code == 400, response.text
assert "assets" in response.json()["error"]["message"]
assert not upstream.called
return
assert response.status_code == 200, response.text
assert json.loads(upstream.calls[0].request.content) == {
"model": "pplx-decider-v1-27b",
"state": _IMAGE_TEXT,
"questions": {"is_defect": {"type": "noul", "instructions": "Is this a defect?"}},
}
@pytest.mark.parametrize("endpoint", ("/v1/systemone", "/v1/decisions"))
@pytest.mark.parametrize("deployment_drops_params", (False, True), ids=("strict", "drop_params"))
def test_clips_for_a_model_without_media_support_are_refused_unless_the_deployment_drops_params(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
endpoint: str,
deployment_drops_params: bool,
) -> None:
monkeypatch.setattr(litellm, "drop_params", False)
monkeypatch.setattr(
litellm.proxy.proxy_server,
"llm_router",
litellm.Router(
model_list=[
{
"model_name": "clef-flash",
"litellm_params": {
"model": "cloudflare/clef-flash",
"api_base": "https://cf.example",
"api_key": "cf-key",
"drop_params": deployment_drops_params,
},
}
]
),
)
upstream: Final = respx_mock.post("https://cf.example/ai/run/@cf/cloudflare/clef-flash").respond(
json=_CLEF_OMNI_RESPONSE
)
response: Final = client.post(endpoint, json={**_clef_omni_request(endpoint), "model": "clef-flash"})
if not deployment_drops_params:
assert response.status_code == 400, response.text
assert "['audio', 'videos']" in response.json()["error"]["message"]
assert not upstream.called
return
assert response.status_code == 200, response.text
assert json.loads(upstream.calls[0].request.content) == {
"model": "clef-flash",
"state": "Evaluate the clips",
"questions": {"heard": {"type": "noul", "instructions": "Is there speech?"}},
}
@pytest.mark.parametrize("endpoint", ("/v1/systemone", "/systemone", "/v1/decisions", "/decisions"))
def test_null_media_on_every_decisions_route_means_no_clips(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
endpoint: str,
) -> None:
_route_to_clef_omni(monkeypatch)
upstream: Final = respx_mock.post(_CLEF_OMNI_UPSTREAM).respond(json=_CLEF_OMNI_RESPONSE)
response: Final = client.post(endpoint, json={**_clef_omni_request(endpoint), "audio": None, "videos": None})
assert response.status_code == 200, response.text
assert json.loads(upstream.calls[0].request.content) == {
"model": "clef-omni",
"state": "Evaluate the clips",
"questions": {"heard": {"type": "noul", "instructions": "Is there speech?"}},
}
@pytest.mark.parametrize("endpoint", ("/v1/systemone", "/v1/decisions"))
@pytest.mark.parametrize("deployment_drops_params", (False, True), ids=("strict", "drop_params"))
def test_a_malformed_clip_for_a_model_without_media_support_is_dropped_only_when_the_deployment_drops_params(
client: TestClient,
monkeypatch: pytest.MonkeyPatch,
respx_mock: respx.MockRouter,
endpoint: str,
deployment_drops_params: bool,
) -> None:
monkeypatch.setattr(litellm, "drop_params", False)
monkeypatch.setattr(
litellm.proxy.proxy_server,
"llm_router",
litellm.Router(
model_list=[
{
"model_name": "clef-flash",
"litellm_params": {
"model": "cloudflare/clef-flash",
"api_base": "https://cf.example",
"api_key": "cf-key",
"drop_params": deployment_drops_params,
},
}
]
),
)
upstream: Final = respx_mock.post("https://cf.example/ai/run/@cf/cloudflare/clef-flash").respond(
json=_CLEF_OMNI_RESPONSE
)
malformed: Final = {"audio": ["https://example.com/clip.wav"], "videos": [{"content_type": "video/mp4"}]}
response: Final = client.post(endpoint, json={**_clef_omni_request(endpoint), "model": "clef-flash", **malformed})
if not deployment_drops_params:
assert response.status_code == 400, response.text
assert "Invalid Decisions request" in response.json()["error"]["message"]
assert not upstream.called
return
assert response.status_code == 200, response.text
assert json.loads(upstream.calls[0].request.content) == {
"model": "clef-flash",
"state": "Evaluate the clips",
"questions": {"heard": {"type": "noul", "instructions": "Is there speech?"}},
}
def _decisions_feature() -> LazyFeature:
return next(feature for feature in LAZY_FEATURES if feature.name == "decisions")
def _serving_endpoint(bare: FastAPI, path: str) -> object:
scope: Final = {"type": "http", "method": "POST", "path": path, "root_path": "", "query_string": b"", "headers": ()}
return next(
route.endpoint for route in bare.routes if isinstance(route, APIRoute) and route.matches(scope)[0] is Match.FULL
)
def test_a_config_pass_through_at_v1_decisions_keeps_its_route_and_the_native_api_serves_decisions(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.delenv("LITELLM_DISABLE_LAZY_ROUTES", raising=False)
async def pass_through() -> dict[str, str]:
return {"served_by": "pass-through"}
bare: Final = FastAPI()
attach_lazy_features(bare, (_decisions_feature(),))
SafeRouteAdder.add_api_route_if_not_exists(bare, "/v1/decisions", pass_through, ["POST"])
with TestClient(bare) as client:
assert client.post("/v1/decisions", json={"model": "gpt-6-luna"}).json() == {"served_by": "pass-through"}
assert _serving_endpoint(bare, "/v1/decisions") is pass_through
assert _serving_endpoint(bare, "/decisions") is decisions
assert _serving_endpoint(bare, "/v1/systemone") is systemone
assert _serving_endpoint(bare, "/systemone") is systemone
def test_with_lazy_routes_disabled_a_config_pass_through_at_v1_decisions_still_wins(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("LITELLM_DISABLE_LAZY_ROUTES", "true")
async def pass_through() -> dict[str, str]:
return {"served_by": "pass-through"}
@asynccontextmanager
async def loads_the_config(app_: FastAPI) -> AsyncGenerator[None]:
assert SafeRouteAdder.add_api_route_if_not_exists(app_, "/v1/decisions", pass_through, ["POST"]), (
"the native route registered at startup must not block the config pass-through"
)
yield
bare: Final = FastAPI(lifespan=loads_the_config)
attach_lazy_features(bare, (_decisions_feature(),))
with TestClient(bare) as client:
assert client.post("/v1/decisions", json={"model": "gpt-6-luna"}).json() == {"served_by": "pass-through"}
assert _serving_endpoint(bare, "/v1/decisions") is pass_through
assert _serving_endpoint(bare, "/decisions") is decisions
@pytest.fixture(scope="module")
def decisions_openapi() -> dict[str, object]:
app = FastAPI()
app.include_router(router)
return app.openapi()
@pytest.mark.parametrize(
("path", "body_adapter", "response_schema", "required"),
(
("/v1/systemone", TypeAdapter(DecisionsRequestBody), "DecisionsResponse", {"model", "state", "questions"}),
("/systemone", TypeAdapter(DecisionsRequestBody), "DecisionsResponse", {"model", "state", "questions"}),
(
"/v1/decisions",
TypeAdapter(OpenAIDecisionRequestBody),
"OpenAIDecisionResponse",
{"model", "input", "questions"},
),
(
"/decisions",
TypeAdapter(OpenAIDecisionRequestBody),
"OpenAIDecisionResponse",
{"model", "input", "questions"},
),
),
)
def test_swagger_documents_the_request_format_and_response_of_each_decisions_route(
decisions_openapi: dict[str, object],
path: str,
body_adapter: TypeAdapter[DecisionsRequestBody] | TypeAdapter[OpenAIDecisionRequestBody],
response_schema: str,
required: set[str],
) -> None:
operation = decisions_openapi["paths"][path]["post"]
media = operation["requestBody"]["content"]["application/json"]
assert "Decisions" in operation["summary"] or "System One" in operation["summary"]
assert "docs.litellm.ai/docs/decisions" in operation["description"]
assert required <= set(media["schema"]["required"])
assert required <= set(media["schema"]["properties"])
assert "$ref" not in json.dumps(media["schema"])
assert media["schema"]["properties"]["model"]["description"]
example = {key: value for key, value in media["example"].items() if key != "model"}
assert len(body_adapter.validate_python(example).questions) == 3
response_ref = operation["responses"]["200"]["content"]["application/json"]["schema"]["$ref"]
assert response_ref == f"#/components/schemas/{response_schema}"
assert "answers" in decisions_openapi["components"]["schemas"][response_schema]["properties"]
def test_the_two_decisions_routes_document_different_request_formats(decisions_openapi: dict[str, object]) -> None:
def properties(path: str) -> set[str]:
operation = decisions_openapi["paths"][path]["post"]
return set(operation["requestBody"]["content"]["application/json"]["schema"]["properties"])
assert "state" in properties("/v1/systemone") and "state" not in properties("/v1/decisions")
assert "input" in properties("/v1/decisions") and "input" not in properties("/v1/systemone")
def test_proxy_systemone_reads_a_zstd_compressed_request_body(
client: TestClient,
respx_mock: respx.MockRouter,
) -> None:
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post(
"/v1/systemone",
content=zstd.compress(json.dumps(_REQUEST).encode()),
headers={"content-type": "application/json", "content-encoding": "zstd"},
)
assert response.status_code == 200, response.text
assert response.json()["answers"] == _RESPONSE["answers"]
assert json.loads(upstream.calls[0].request.content)["questions"] == _REQUEST["questions"]
@pytest.mark.parametrize(
("content_encoding", "compress"),
(("gzip", gzip.compress), ("zstd", zstd.compress)),
)
def test_a_compressed_body_naming_a_model_outside_the_key_is_refused_by_auth(
client: TestClient,
respx_mock: respx.MockRouter,
monkeypatch: pytest.MonkeyPatch,
content_encoding: str,
compress: Callable[[bytes], bytes],
) -> None:
user_key: Final = "sk-decider-only"
user_api_key_cache: Final = UserApiKeyCache()
user_api_key_cache.set_cache(
key=hash_token(user_key), value=UserAPIKeyAuth(token=hash_token(user_key), models=["decider"])
)
monkeypatch.setattr(litellm.proxy.proxy_server, "user_api_key_cache", user_api_key_cache)
monkeypatch.setattr(litellm.proxy.proxy_server, "master_key", MASTER_KEY)
monkeypatch.setattr(litellm.proxy.proxy_server, "prisma_client", "no-db-needed-for-a-cached-key")
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post(
"/v1/systemone",
content=compress(json.dumps({**_REQUEST, "model": "not-on-this-key"}).encode()),
headers={
"content-type": "application/json",
"content-encoding": content_encoding,
"authorization": f"Bearer {user_key}",
},
)
assert response.status_code == 403, response.text
assert "not-on-this-key" in response.text
assert not upstream.called
@pytest.mark.parametrize(
("content_encoding", "undecodable_body"),
(("gzip", gzip.compress(b"{}", mtime=0)[:-8]), ("zstd", zstd.compress(b"{}") + b"trailing")),
)
def test_a_browser_can_read_the_error_for_a_compressed_body_the_proxy_cannot_decode(
client: TestClient,
respx_mock: respx.MockRouter,
content_encoding: str,
undecodable_body: bytes,
) -> None:
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
origin: Final = "https://app.example.test"
response: Final = client.post(
"/v1/systemone",
content=undecodable_body,
headers={"content-type": "application/json", "content-encoding": content_encoding, "origin": origin},
)
assert response.status_code == 400, response.text
assert response.headers["access-control-allow-origin"] in ("*", origin)
assert response.headers["x-content-type-options"] == "nosniff"
assert not upstream.called
def test_the_operator_request_size_limit_applies_to_the_decoded_body(
client: TestClient,
respx_mock: respx.MockRouter,
monkeypatch: pytest.MonkeyPatch,
) -> None:
max_request_size_mb: Final = 0.01
monkeypatch.setattr(litellm.proxy.proxy_server, "premium_user", True)
monkeypatch.setitem(litellm.proxy.proxy_server.general_settings, "max_request_size_mb", max_request_size_mb)
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
state_text: Final = "".join(random.Random(0).choices("abcd", k=20_000))
compressed: Final = zstd.compress(json.dumps({**_REQUEST, "state": {"source": state_text}}).encode())
response: Final = client.post(
"/v1/systemone",
content=compressed,
headers={"content-type": "application/json", "content-encoding": "zstd"},
)
assert len(compressed) < max_request_size_mb * 1024 * 1024
assert response.status_code == 413, response.text
assert response.json()["error"]["message"] == f"Request body is larger than {max_request_size_mb} MB when decoded"
assert not upstream.called
def test_a_whole_number_request_size_limit_is_named_as_configured_in_the_413(
client: TestClient,
respx_mock: respx.MockRouter,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(litellm.proxy.proxy_server, "premium_user", True)
monkeypatch.setitem(litellm.proxy.proxy_server.general_settings, "max_request_size_mb", 1)
upstream: Final = respx_mock.post("https://api.perplexity.ai/v1/decisions").respond(json=_RESPONSE)
response: Final = client.post("/v1/systemone", json={**_REQUEST, "state": {"source": "a" * 1024 * 1024}})
assert response.status_code == 413, response.text
assert response.json() == {"error": "Request size is too large. Max size is 1 MB"}
assert not upstream.called