diff --git a/litellm/proxy/decisions_endpoints/endpoints.py b/litellm/proxy/decisions_endpoints/endpoints.py index 2b908a8a089..4170e3a13c3 100644 --- a/litellm/proxy/decisions_endpoints/endpoints.py +++ b/litellm/proxy/decisions_endpoints/endpoints.py @@ -40,6 +40,14 @@ async def _invalid_request( ) +async def _request_data(request: Request, user_api_key_dict: UserAPIKeyAuth) -> dict[str, object]: + body: Final = await request.body() + try: + return _REQUEST_DATA_ADAPTER.validate_json(body) + except ValidationError as error: + raise await _invalid_request(raw_data={}, error=error, user_api_key_dict=user_api_key_dict) + + async def _process_systemone( request: Request, fastapi_response: Response, @@ -133,7 +141,7 @@ async def systemone( request=request, fastapi_response=fastapi_response, user_api_key_dict=user_api_key_dict, - raw_data=_REQUEST_DATA_ADAPTER.validate_json(await request.body()), + raw_data=await _request_data(request, user_api_key_dict), openai_body=None, ) @@ -155,7 +163,7 @@ async def decisions( fastapi_response: Response, user_api_key_dict: Annotated[UserAPIKeyAuth, Depends(user_api_key_auth)], ): - raw_data: Final = _REQUEST_DATA_ADAPTER.validate_json(await request.body()) + raw_data: Final = await _request_data(request, user_api_key_dict) try: openai_body: Final = _OPENAI_DECISION_REQUEST_BODY_ADAPTER.validate_python(raw_data) except ValidationError as error: diff --git a/tests/unit/proxy/decisions_endpoints/test_endpoints.py b/tests/unit/proxy/decisions_endpoints/test_endpoints.py index 0650387d280..f61507e1d1d 100644 --- a/tests/unit/proxy/decisions_endpoints/test_endpoints.py +++ b/tests/unit/proxy/decisions_endpoints/test_endpoints.py @@ -421,6 +421,22 @@ def test_openai_format_decisions_rejects_bodies_it_cannot_translate( 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 _decisions_feature() -> LazyFeature: return next(feature for feature in LAZY_FEATURES if feature.name == "decisions")