diff --git a/docs/models/requests.json b/docs/models/requests.json
index d6826e607..cea912e65 100644
--- a/docs/models/requests.json
+++ b/docs/models/requests.json
@@ -3165,4 +3165,4 @@
}
},
"paths": {}
-}
\ No newline at end of file
+}
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
-
+
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