From 73b49343858c348e12646fe9ab1ab16e6f23447d Mon Sep 17 00:00:00 2001 From: Pavel Tisnovsky Date: Mon, 19 Oct 2026 10:13:06 +0200 Subject: [PATCH 1/2] Regenerated doc --- docs/models/requests.json | 2 +- docs/models/requests.puml | 2 +- docs/models/requests.svg | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/models/requests.json b/docs/models/requests.json index d6826e607..8b655bfba 100644 --- a/docs/models/requests.json +++ b/docs/models/requests.json @@ -2237,7 +2237,7 @@ "generate_topic_summary": true, "input": "Hello World!", "instructions": "You are a helpful assistant", - "model": "openai/gpt-5.6-luna", + "model": "openai/gpt-4o-mini", "store": true, "stream": false } diff --git a/docs/models/requests.puml b/docs/models/requests.puml index 91a1a8ff9..80ca013eb 100644 --- a/docs/models/requests.puml +++ b/docs/models/requests.puml @@ -94,7 +94,7 @@ class "ResponsesRequest" as src.models.api.requests.responses_openai.ResponsesRe tools : Optional[list[InputTool]] check_previous_response_id(value: Optional[str]) -> Optional[str] check_suid(value: Optional[str]) -> Optional[str] - validate_body_size(values: Any) -> Any + validate_body_size(values: Any, info: ValidationInfo) -> Any validate_conversation_and_previous_response_id_mutually_exclusive() -> Self } class "RlsapiV1Attachment" as src.models.api.requests.rlsapi.RlsapiV1Attachment { diff --git a/docs/models/requests.svg b/docs/models/requests.svg index f6bc760c6..f1e0332c4 100644 --- a/docs/models/requests.svg +++ b/docs/models/requests.svg @@ -159,7 +159,7 @@ check_previous_response_id(value: Optional[str]) -> Optional[str] check_suid(value: Optional[str]) -> Optional[str] - validate_body_size(values: Any) -> Any + validate_body_size(values: Any, info: ValidationInfo) -> Any validate_conversation_and_previous_response_id_mutually_exclusive() -> Self @@ -335,6 +335,6 @@ terminal - + From eb70193ffbbdb9b0daa896c16ecd2bc0373f5d33 Mon Sep 17 00:00:00 2001 From: Pavel Tisnovsky Date: Mon, 19 Oct 2026 10:13:27 +0200 Subject: [PATCH 2/2] LCORE-4621: Request model configuration with configurable limit threshold --- docs/models/requests.json | 4 +- src/models/api/requests/responses_openai.py | 13 ++-- .../models/requests/test_responses_request.py | 67 +++++++++++++++++++ 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/docs/models/requests.json b/docs/models/requests.json index 8b655bfba..cea912e65 100644 --- a/docs/models/requests.json +++ b/docs/models/requests.json @@ -2237,7 +2237,7 @@ "generate_topic_summary": true, "input": "Hello World!", "instructions": "You are a helpful assistant", - "model": "openai/gpt-4o-mini", + "model": "openai/gpt-5.6-luna", "store": true, "stream": false } @@ -3165,4 +3165,4 @@ } }, "paths": {} -} \ No newline at end of file +} diff --git a/src/models/api/requests/responses_openai.py b/src/models/api/requests/responses_openai.py index 24c41cf34..142018535 100644 --- a/src/models/api/requests/responses_openai.py +++ b/src/models/api/requests/responses_openai.py @@ -15,7 +15,7 @@ from ogx_api.openai_responses import ( OpenAIResponseText as Text, ) -from pydantic import BaseModel, field_validator, model_validator +from pydantic import BaseModel, ValidationInfo, field_validator, model_validator from constants import RESPONSES_REQUEST_MAX_SIZE from models.common.query import SolrVectorSearchRequest @@ -106,7 +106,7 @@ class ResponsesRequest(BaseModel): @model_validator(mode="before") @classmethod - def validate_body_size(cls, values: Any) -> Any: + def validate_body_size(cls, values: Any, info: ValidationInfo) -> Any: """Validate that the request body does not exceed the maximum allowed size. Serializes the raw request payload to JSON and checks the total character @@ -132,10 +132,15 @@ def validate_body_size(cls, values: Any) -> Any: # model instances). The size guard only applies to wire-format HTTP # requests which FastAPI always parses into JSON-compatible dicts. return values - if len(serialized) > RESPONSES_REQUEST_MAX_SIZE: + + max_size = (info.context or {}).get( + "responses_request_max_size", RESPONSES_REQUEST_MAX_SIZE + ) + + if len(serialized) > max_size: raise ValueError( f"Request body size ({len(serialized)} characters) exceeds maximum " - f"allowed size of {RESPONSES_REQUEST_MAX_SIZE} characters" + f"allowed size of {max_size} characters" ) return values diff --git a/tests/unit/models/requests/test_responses_request.py b/tests/unit/models/requests/test_responses_request.py index 4d8e1e69a..dea886955 100644 --- a/tests/unit/models/requests/test_responses_request.py +++ b/tests/unit/models/requests/test_responses_request.py @@ -55,3 +55,70 @@ def test_large_list_input_rejected(self) -> None: error_text = str(exc_info.value) assert "65536" in error_text + + def test_model_validation_normal_request_accepted(self) -> None: + """A small, normal request must be accepted without raising.""" + req = ResponsesRequest.model_validate( + { + "input": "hello", + } + ) + assert req.input == "hello" + + def test_model_validation_normal_request_accepted_configurable_limit(self) -> None: + """A small, normal request must be accepted without raising.""" + req = ResponsesRequest.model_validate( + { + "input": "hello", + }, + context={"responses_request_max_size": 100_000}, + ) + assert req.input == "hello" + + def test_model_validation_over_limit(self) -> None: + """A request whose JSON serialization is 65,537 chars raises ValidationError.""" + payload = "x" * (_AT_LIMIT_PADDING + 1) + serialized = json.dumps({"input": payload}) + assert len(serialized) == _LIMIT + 1 + + with pytest.raises(ValidationError) as exc_info: + ResponsesRequest.model_validate( + { + "input": payload, + } + ) + + error_text = str(exc_info.value) + assert "65536" in error_text + assert str(_LIMIT + 1) in error_text + + def test_model_validation_over_standard_limit_not_reached_threshold(self) -> None: + """Validation for custom limit.""" + payload = "x" * (_AT_LIMIT_PADDING + 1) + serialized = json.dumps({"input": payload}) + assert len(serialized) == _LIMIT + 1 + + req = ResponsesRequest.model_validate( + { + "input": payload, + }, + context={"responses_request_max_size": 100_000}, + ) + assert req.input is not None + assert "xxxx" in req.input + + def test_model_validation_over_limit_reached_threshold(self) -> None: + """A request whose JSON serialization is longer than max size raises ValidationError.""" + payload = "x" * (110_000 - _OVERHEAD) + + with pytest.raises(ValidationError) as exc_info: + ResponsesRequest.model_validate( + { + "input": payload, + }, + context={"responses_request_max_size": 100_000}, + ) + + error_text = str(exc_info.value) + assert "110000" in error_text + assert "100000" in error_text