Skip to content

Commit 142e31e

Browse files
committed
fix: remove modify reject exclusivity
1 parent ceb23af commit 142e31e

File tree

2 files changed

+33
-70
lines changed

2 files changed

+33
-70
lines changed

src/f5_ai_gateway_sdk/parameters.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from collections.abc import Mapping, Iterator
1111

1212
from opentelemetry.util.types import AttributeValue
13-
from pydantic import BaseModel, ConfigDict, Field, model_validator
13+
from pydantic import BaseModel, ConfigDict, Field
1414

1515

1616
class Parameters(BaseModel):
@@ -37,12 +37,6 @@ class Parameters(BaseModel):
3737
description="Whether the processor can reject requests.",
3838
)
3939

40-
@model_validator(mode="after")
41-
def check_not_reject_and_modify(self):
42-
if self.reject and self.modify:
43-
raise ValueError("Modify and Reject modes are mutually exclusive")
44-
return self
45-
4640
def otel_attributes(
4741
self, key_prefix: str = "parameters."
4842
) -> Iterator[tuple[str, AttributeValue]]:

tests/contract/test_processor_exchanges.py

Lines changed: 32 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
from f5_ai_gateway_sdk.processor import Tags
3131
from f5_ai_gateway_sdk.request_input import Message, RequestInput
3232
from f5_ai_gateway_sdk.response_output import Choice, ResponseOutput
33-
from f5_ai_gateway_sdk.result import Result
3433

3534
from ..libs.exceptions import TestTypeError
3635
from ..libs.fakes import processors as fake_processors
@@ -79,22 +78,22 @@ def test_multipart_fields_breaking_change():
7978
f"breaking change detected: {encoded_field} change from {expected_metadata_name}"
8079
)
8180
assert (encoded_fields := multipart_fields.INPUT_NAME) == expected_prompt_name, (
82-
f"breaking change detected: {encoded_fields} change from {expected_optional_multipart_fields}"
81+
f"breaking change detected: {encoded_fields} change from {expected_prompt_name}"
8382
)
8483
assert (
8584
encoded_field := multipart_fields.INPUT_PARAMETERS_NAME
8685
) == expected_prompt_parameters_name, (
87-
f"breaking change detected: {encoded_field} change from {expected_prompt_name}"
86+
f"breaking change detected: {encoded_field} change from {expected_prompt_parameters_name}"
8887
)
8988
assert (
9089
encoded_field := multipart_fields.RESPONSE_NAME
9190
) == expected_response_name, (
92-
f"breaking change detected: {encoded_field} change from {expected_prompt_name}"
91+
f"breaking change detected: {encoded_field} change from {expected_response_name}"
9392
)
9493
assert (
9594
encoded_field := multipart_fields.RESPONSE_PARAMETERS_NAME
9695
) == expected_response_parameters_name, (
97-
f"breaking change detected: {encoded_field} change from {expected_prompt_name}"
96+
f"breaking change detected: {encoded_field} change from {expected_response_parameters_name}"
9897
)
9998
# - validate required versus optional field definitions; order not super important
10099
assert (expected := set(expected_required_multipart_fields)) == (
@@ -109,7 +108,7 @@ def test_multipart_fields_breaking_change():
109108
def test_processor_response_parameters_a_prompt_mismatch(
110109
data_loader, processor_client_loader, test_logger, judgy_class
111110
):
112-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
111+
"""Verify that response parameters cannot be present with only an input field."""
113112
expected_message = (
114113
f"response parameters cannot be present with only a {INPUT_NAME} field"
115114
)
@@ -159,7 +158,7 @@ def test_processor_response_parameters_a_prompt_mismatch(
159158
def test_processor_overload_both_parameters(
160159
data_loader, processor_client_loader, test_logger, judgy_class
161160
):
162-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
161+
"""Verify that providing both input and response parameters generates an error."""
163162
expected_message = (
164163
f"response parameters cannot be present with only a {INPUT_NAME} field"
165164
)
@@ -171,7 +170,7 @@ def test_processor_overload_both_parameters(
171170
test_logger.info(f"given: processor with path: {PROCESSOR_PATH}")
172171
judgy = fake_judgy(judgy_class)
173172

174-
test_logger.info("when: client requests a post with no prompt")
173+
test_logger.info("when: client requests a post with both input and response parameters")
175174
client = processor_client_loader(judgy)
176175
data = build_processor_prompt_content(
177176
data_loader,
@@ -211,7 +210,7 @@ def test_processor_overload_both_parameters(
211210
def test_processor_500_raising(
212211
data_loader, processor_client_loader, test_logger, judgy_class
213212
):
214-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
213+
"""Verify that processor errors are properly handled and return a 500 status code."""
215214
expected_response = """{"detail": "problem executing processor implementation"}"""
216215
expected_status_code = http_status_codes.HTTP_500_INTERNAL_SERVER_ERROR
217216

@@ -223,7 +222,7 @@ def test_processor_500_raising(
223222
http_status_codes.HTTP_500_INTERNAL_SERVER_ERROR, "fool of the fools"
224223
)
225224

226-
test_logger.info("when: client requests a post with no prompt")
225+
test_logger.info("when: client requests a post with a prompt that causes processor to raise an error")
227226
client = processor_client_loader(judgy)
228227
data = build_processor_prompt_content(
229228
data_loader,
@@ -261,7 +260,7 @@ def test_processor_500_raising(
261260
def test_processor_returns_none(
262261
data_loader, processor_client_loader, test_logger, judgy_class
263262
):
264-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
263+
"""Verify that processors returning None are handled properly with appropriate error messages."""
265264

266265
if judgy_class.uses_process_method():
267266

@@ -296,7 +295,7 @@ def process_response(*_, **__):
296295
)
297296
judgy.raise_error = TypeError("fool of the fools")
298297

299-
test_logger.info("when: client requests a post with no prompt")
298+
test_logger.info("when: client requests a post with a prompt and processor returns None")
300299
client = processor_client_loader(judgy)
301300
data = build_processor_prompt_content(
302301
data_loader,
@@ -333,7 +332,7 @@ def process_response(*_, **__):
333332
def test_processor_returns_bogus_class(
334333
data_loader, processor_client_loader, test_logger, judgy_class
335334
):
336-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
335+
"""Verify that processors returning invalid objects are handled properly with appropriate error messages."""
337336

338337
class BogusClass:
339338
rejected = False
@@ -374,7 +373,7 @@ def process_input(*_, **__):
374373
)
375374
judgy.raise_error = TypeError("fool of the fools")
376375

377-
test_logger.info("when: client requests a post with no prompt")
376+
test_logger.info("when: client requests a post with a prompt and processor returns invalid object")
378377
client = processor_client_loader(judgy)
379378
data = build_processor_prompt_content(
380379
data_loader,
@@ -426,7 +425,7 @@ def test_raising_processor(
426425
)
427426
judgy.raise_error = TypeError("fool of the fools")
428427

429-
test_logger.info("when: client requests a post with no prompt")
428+
test_logger.info("when: client requests a post with a prompt that causes processor to raise an error")
430429
client = processor_client_loader(judgy)
431430
data = build_processor_prompt_content(
432431
data_loader,
@@ -512,7 +511,7 @@ def test_request_no_prompt(
512511
def test_request_null_parameters(
513512
data_loader, processor_client_loader, test_logger, judgy_class
514513
):
515-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
514+
"""Verify that null parameters are properly validated and rejected."""
516515
expected_response = """{"detail": "invalid parameters submitted", "messages": ["Input should be an object"]}"""
517516
expected_status_code = http_status_codes.HTTP_400_BAD_REQUEST
518517

@@ -526,7 +525,7 @@ def test_request_null_parameters(
526525
parameters_class=fake_processors.JudgyParameters,
527526
)
528527

529-
test_logger.info("when: client requests a post with no prompt")
528+
test_logger.info("when: client requests a post with null parameters")
530529
client = processor_client_loader(judgy)
531530
data = build_processor_prompt_content(
532531
data_loader,
@@ -561,7 +560,7 @@ def test_request_null_parameters(
561560
def test_request_empty_metadata(
562561
data_loader, processor_client_loader, test_logger, judgy_class
563562
):
564-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
563+
"""Verify that empty metadata is properly handled."""
565564
expected_response = '{"detail": "Unable to parse JSON field [metadata]: Expecting value: line 1 column 1 (char 0)"}'
566565
expected_status_code = http_status_codes.HTTP_400_BAD_REQUEST
567566

@@ -575,7 +574,7 @@ def test_request_empty_metadata(
575574
parameters_class=fake_processors.JudgyParameters,
576575
)
577576

578-
test_logger.info("when: client requests a post with no prompt")
577+
test_logger.info("when: client requests a post with empty metadata")
579578
client = processor_client_loader(judgy)
580579
data = build_processor_prompt_content(
581580
data_loader,
@@ -611,7 +610,7 @@ def test_request_empty_metadata(
611610
def test_request_invalid_metadata(
612611
data_loader, processor_client_loader, test_logger, judgy_class
613612
):
614-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
613+
"""Verify that invalid metadata format is properly rejected."""
615614
expected_response = """{"detail": "invalid metadata submitted"}"""
616615
expected_status_code = http_status_codes.HTTP_400_BAD_REQUEST
617616

@@ -625,7 +624,7 @@ def test_request_invalid_metadata(
625624
parameters_class=fake_processors.JudgyParameters,
626625
)
627626

628-
test_logger.info("when: client requests a post with no prompt")
627+
test_logger.info("when: client requests a post with invalid metadata")
629628
client = processor_client_loader(judgy)
630629
data = build_processor_prompt_content(
631630
data_loader,
@@ -686,7 +685,7 @@ def test_request_invalid_metadata(
686685
def test_request_string_metadata(
687686
data_loader, processor_client_loader, test_logger, judgy_class
688687
):
689-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
688+
"""Verify that string metadata (instead of JSON object) is properly rejected."""
690689
expected_response = """{"detail": "metadata must be a JSON object"}"""
691690
expected_status_code = http_status_codes.HTTP_400_BAD_REQUEST
692691

@@ -695,7 +694,7 @@ def test_request_string_metadata(
695694
test_logger.info(f"given: processor with path: {PROCESSOR_PATH}")
696695
judgy = fake_judgy(judgy_class)
697696

698-
test_logger.info("when: client requests a post with no prompt")
697+
test_logger.info("when: client requests a post with string metadata")
699698
client = processor_client_loader(judgy)
700699
data = build_processor_prompt_content(
701700
data_loader,
@@ -849,7 +848,7 @@ def test_request_query_post_command_invalid_parameters(
849848
def test_request_invalid_parameters(
850849
data_loader, processor_client_loader, test_logger, judgy_class
851850
):
852-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
851+
"""Verify that invalid parameters are properly validated and rejected."""
853852
expected_error = """{"detail": "invalid parameters submitted", "messages": ["Input should be a valid boolean: modified"]}"""
854853
expected_status_code = http_status_codes.HTTP_400_BAD_REQUEST
855854

@@ -863,7 +862,7 @@ def test_request_invalid_parameters(
863862
parameters_class=fake_processors.JudgyParameters,
864863
)
865864

866-
test_logger.info("when: client requests a post with no prompt")
865+
test_logger.info("when: client requests a post with invalid parameters")
867866
client = processor_client_loader(judgy)
868867
parameters = data_loader("judgy_parameters.yaml")
869868
parameters["modified"] = "Lucy in the sky with diamonds"
@@ -899,7 +898,7 @@ def test_request_invalid_parameters(
899898
def test_request_required_parameters_missing(
900899
data_loader, processor_client_loader, test_logger, judgy_class
901900
):
902-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
901+
"""Verify that missing required parameters are properly validated and rejected."""
903902
expected_error = """{"detail": "invalid parameters submitted", "messages": ["Field required: required_message"]}"""
904903
expected_invalid_status_code = http_status_codes.HTTP_400_BAD_REQUEST
905904
method = "post"
@@ -948,7 +947,7 @@ def test_request_required_parameters_missing(
948947
def test_request_required_parameters_present(
949948
data_loader, processor_client_loader, test_logger, judgy_class
950949
):
951-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
950+
"""Verify that requests with required parameters present are handled correctly."""
952951
expected_valid_status_code = http_status_codes.HTTP_400_BAD_REQUEST
953952
method = "post"
954953

@@ -1028,7 +1027,7 @@ def test_request_required_metadata_response_fields(
10281027
def test_request_required_parameters_missing_multipart(
10291028
data_loader, processor_client_loader, test_logger, judgy_class
10301029
):
1031-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
1030+
"""Verify that missing required parameters in multipart requests are properly validated and rejected."""
10321031
expected_error = """{"detail": "invalid parameters submitted", "messages": ["Field required: required_message"]}"""
10331032
expected_valid_status_code = http_status_codes.HTTP_400_BAD_REQUEST
10341033
method = "post"
@@ -1072,45 +1071,16 @@ def test_request_required_parameters_missing_multipart(
10721071
def test_modification_with_reject(
10731072
data_loader, processor_client_loader, test_logger, judgy_class
10741073
):
1075-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
1076-
expected_valid_status_code = http_status_codes.HTTP_400_BAD_REQUEST
1074+
"""Verify that processors allow modify and reject to be set at once."""
1075+
expected_valid_status_code = http_status_codes.HTTP_200_OK
10771076
method = "post"
10781077

1079-
if judgy_class.uses_process_method():
1080-
1081-
class ModifyAndRejectProcessor(judgy_class):
1082-
def process(*_, **__):
1083-
"""Return None as a matter of existence."""
1084-
return Result(
1085-
modified_prompt=RequestInput(
1086-
messages=[Message(content="foo-input")]
1087-
),
1088-
modified_response=ResponseOutput(
1089-
choices=[Choice(message=Message(content="bar-output"))]
1090-
),
1091-
)
1092-
else:
1093-
1094-
class ModifyAndRejectProcessor(judgy_class):
1095-
def process_input(*_, **__):
1096-
"""Return None as a matter of existence."""
1097-
return Result(
1098-
modified_prompt=RequestInput(
1099-
messages=[Message(content="foo-input")]
1100-
),
1101-
modified_response=ResponseOutput(
1102-
choices=[Choice(message=Message(content="bar-output"))]
1103-
),
1104-
)
1105-
1106-
test_logger.info(f"given: processor with path: {PROCESSOR_PATH}")
1107-
judgy = ModifyAndRejectProcessor(
1078+
judgy = judgy_class(
11081079
PROCESSOR_NAME,
11091080
PROCESSOR_VERSION,
11101081
PROCESSOR_NAMESPACE,
1082+
parameters_class=fake_processors.JudgyParameters,
11111083
)
1112-
1113-
test_logger.info("when: client requests a post without a required parameter")
11141084
client = processor_client_loader(judgy)
11151085
data = build_processor_prompt_content(
11161086
data_loader,
@@ -1134,7 +1104,6 @@ def process_input(*_, **__):
11341104
assert response.status_code == expected_valid_status_code, (
11351105
f"({response.status_code} != {expected_valid_status_code}) from {method}({PROCESSOR_PATH}): {content}"
11361106
)
1137-
assert "mutually exclusive" in response.text
11381107

11391108

11401109
@pytest.mark.parametrize("judgy_class", TEST_PROCESSORS)
@@ -1147,7 +1116,7 @@ def test_get_signature_definition(
11471116

11481117
test_logger.info(f"given: processor with path: {SIGNATURE_PATH}")
11491118
judgy = fake_judgy(judgy_class)
1150-
test_logger.info("when: client requests a post with no prompt")
1119+
test_logger.info("when: client requests signature definition")
11511120
client = processor_client_loader(judgy)
11521121
request = client.build_request(
11531122
url=SIGNATURE_PATH, method=method, headers={"Accept": "application/json"}

0 commit comments

Comments
 (0)