Skip to content

Commit ed13725

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

File tree

2 files changed

+43
-70
lines changed

2 files changed

+43
-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: 42 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,9 @@ 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(
174+
"when: client requests a post with both input and response parameters"
175+
)
175176
client = processor_client_loader(judgy)
176177
data = build_processor_prompt_content(
177178
data_loader,
@@ -211,7 +212,7 @@ def test_processor_overload_both_parameters(
211212
def test_processor_500_raising(
212213
data_loader, processor_client_loader, test_logger, judgy_class
213214
):
214-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
215+
"""Verify that processor errors are properly handled and return a 500 status code."""
215216
expected_response = """{"detail": "problem executing processor implementation"}"""
216217
expected_status_code = http_status_codes.HTTP_500_INTERNAL_SERVER_ERROR
217218

@@ -223,7 +224,9 @@ def test_processor_500_raising(
223224
http_status_codes.HTTP_500_INTERNAL_SERVER_ERROR, "fool of the fools"
224225
)
225226

226-
test_logger.info("when: client requests a post with no prompt")
227+
test_logger.info(
228+
"when: client requests a post with a prompt that causes processor to raise an error"
229+
)
227230
client = processor_client_loader(judgy)
228231
data = build_processor_prompt_content(
229232
data_loader,
@@ -261,7 +264,7 @@ def test_processor_500_raising(
261264
def test_processor_returns_none(
262265
data_loader, processor_client_loader, test_logger, judgy_class
263266
):
264-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
267+
"""Verify that processors returning None are handled properly with appropriate error messages."""
265268

266269
if judgy_class.uses_process_method():
267270

@@ -296,7 +299,9 @@ def process_response(*_, **__):
296299
)
297300
judgy.raise_error = TypeError("fool of the fools")
298301

299-
test_logger.info("when: client requests a post with no prompt")
302+
test_logger.info(
303+
"when: client requests a post with a prompt and processor returns None"
304+
)
300305
client = processor_client_loader(judgy)
301306
data = build_processor_prompt_content(
302307
data_loader,
@@ -333,7 +338,7 @@ def process_response(*_, **__):
333338
def test_processor_returns_bogus_class(
334339
data_loader, processor_client_loader, test_logger, judgy_class
335340
):
336-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
341+
"""Verify that processors returning invalid objects are handled properly with appropriate error messages."""
337342

338343
class BogusClass:
339344
rejected = False
@@ -374,7 +379,9 @@ def process_input(*_, **__):
374379
)
375380
judgy.raise_error = TypeError("fool of the fools")
376381

377-
test_logger.info("when: client requests a post with no prompt")
382+
test_logger.info(
383+
"when: client requests a post with a prompt and processor returns invalid object"
384+
)
378385
client = processor_client_loader(judgy)
379386
data = build_processor_prompt_content(
380387
data_loader,
@@ -426,7 +433,9 @@ def test_raising_processor(
426433
)
427434
judgy.raise_error = TypeError("fool of the fools")
428435

429-
test_logger.info("when: client requests a post with no prompt")
436+
test_logger.info(
437+
"when: client requests a post with a prompt that causes processor to raise an error"
438+
)
430439
client = processor_client_loader(judgy)
431440
data = build_processor_prompt_content(
432441
data_loader,
@@ -512,7 +521,7 @@ def test_request_no_prompt(
512521
def test_request_null_parameters(
513522
data_loader, processor_client_loader, test_logger, judgy_class
514523
):
515-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
524+
"""Verify that null parameters are properly validated and rejected."""
516525
expected_response = """{"detail": "invalid parameters submitted", "messages": ["Input should be an object"]}"""
517526
expected_status_code = http_status_codes.HTTP_400_BAD_REQUEST
518527

@@ -526,7 +535,7 @@ def test_request_null_parameters(
526535
parameters_class=fake_processors.JudgyParameters,
527536
)
528537

529-
test_logger.info("when: client requests a post with no prompt")
538+
test_logger.info("when: client requests a post with null parameters")
530539
client = processor_client_loader(judgy)
531540
data = build_processor_prompt_content(
532541
data_loader,
@@ -561,7 +570,7 @@ def test_request_null_parameters(
561570
def test_request_empty_metadata(
562571
data_loader, processor_client_loader, test_logger, judgy_class
563572
):
564-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
573+
"""Verify that empty metadata is properly handled."""
565574
expected_response = '{"detail": "Unable to parse JSON field [metadata]: Expecting value: line 1 column 1 (char 0)"}'
566575
expected_status_code = http_status_codes.HTTP_400_BAD_REQUEST
567576

@@ -575,7 +584,7 @@ def test_request_empty_metadata(
575584
parameters_class=fake_processors.JudgyParameters,
576585
)
577586

578-
test_logger.info("when: client requests a post with no prompt")
587+
test_logger.info("when: client requests a post with empty metadata")
579588
client = processor_client_loader(judgy)
580589
data = build_processor_prompt_content(
581590
data_loader,
@@ -611,7 +620,7 @@ def test_request_empty_metadata(
611620
def test_request_invalid_metadata(
612621
data_loader, processor_client_loader, test_logger, judgy_class
613622
):
614-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
623+
"""Verify that invalid metadata format is properly rejected."""
615624
expected_response = """{"detail": "invalid metadata submitted"}"""
616625
expected_status_code = http_status_codes.HTTP_400_BAD_REQUEST
617626

@@ -625,7 +634,7 @@ def test_request_invalid_metadata(
625634
parameters_class=fake_processors.JudgyParameters,
626635
)
627636

628-
test_logger.info("when: client requests a post with no prompt")
637+
test_logger.info("when: client requests a post with invalid metadata")
629638
client = processor_client_loader(judgy)
630639
data = build_processor_prompt_content(
631640
data_loader,
@@ -686,7 +695,7 @@ def test_request_invalid_metadata(
686695
def test_request_string_metadata(
687696
data_loader, processor_client_loader, test_logger, judgy_class
688697
):
689-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
698+
"""Verify that string metadata (instead of JSON object) is properly rejected."""
690699
expected_response = """{"detail": "metadata must be a JSON object"}"""
691700
expected_status_code = http_status_codes.HTTP_400_BAD_REQUEST
692701

@@ -695,7 +704,7 @@ def test_request_string_metadata(
695704
test_logger.info(f"given: processor with path: {PROCESSOR_PATH}")
696705
judgy = fake_judgy(judgy_class)
697706

698-
test_logger.info("when: client requests a post with no prompt")
707+
test_logger.info("when: client requests a post with string metadata")
699708
client = processor_client_loader(judgy)
700709
data = build_processor_prompt_content(
701710
data_loader,
@@ -849,7 +858,7 @@ def test_request_query_post_command_invalid_parameters(
849858
def test_request_invalid_parameters(
850859
data_loader, processor_client_loader, test_logger, judgy_class
851860
):
852-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
861+
"""Verify that invalid parameters are properly validated and rejected."""
853862
expected_error = """{"detail": "invalid parameters submitted", "messages": ["Input should be a valid boolean: modified"]}"""
854863
expected_status_code = http_status_codes.HTTP_400_BAD_REQUEST
855864

@@ -863,7 +872,7 @@ def test_request_invalid_parameters(
863872
parameters_class=fake_processors.JudgyParameters,
864873
)
865874

866-
test_logger.info("when: client requests a post with no prompt")
875+
test_logger.info("when: client requests a post with invalid parameters")
867876
client = processor_client_loader(judgy)
868877
parameters = data_loader("judgy_parameters.yaml")
869878
parameters["modified"] = "Lucy in the sky with diamonds"
@@ -899,7 +908,7 @@ def test_request_invalid_parameters(
899908
def test_request_required_parameters_missing(
900909
data_loader, processor_client_loader, test_logger, judgy_class
901910
):
902-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
911+
"""Verify that missing required parameters are properly validated and rejected."""
903912
expected_error = """{"detail": "invalid parameters submitted", "messages": ["Field required: required_message"]}"""
904913
expected_invalid_status_code = http_status_codes.HTTP_400_BAD_REQUEST
905914
method = "post"
@@ -948,7 +957,7 @@ def test_request_required_parameters_missing(
948957
def test_request_required_parameters_present(
949958
data_loader, processor_client_loader, test_logger, judgy_class
950959
):
951-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
960+
"""Verify that requests with required parameters present are handled correctly."""
952961
expected_valid_status_code = http_status_codes.HTTP_400_BAD_REQUEST
953962
method = "post"
954963

@@ -1028,7 +1037,7 @@ def test_request_required_metadata_response_fields(
10281037
def test_request_required_parameters_missing_multipart(
10291038
data_loader, processor_client_loader, test_logger, judgy_class
10301039
):
1031-
"""Verify that with a stood up processor that the request will reject a request with no prompt."""
1040+
"""Verify that missing required parameters in multipart requests are properly validated and rejected."""
10321041
expected_error = """{"detail": "invalid parameters submitted", "messages": ["Field required: required_message"]}"""
10331042
expected_valid_status_code = http_status_codes.HTTP_400_BAD_REQUEST
10341043
method = "post"
@@ -1072,45 +1081,16 @@ def test_request_required_parameters_missing_multipart(
10721081
def test_modification_with_reject(
10731082
data_loader, processor_client_loader, test_logger, judgy_class
10741083
):
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
1084+
"""Verify that processors allow modify and reject to be set at once."""
1085+
expected_valid_status_code = http_status_codes.HTTP_200_OK
10771086
method = "post"
10781087

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(
1088+
judgy = judgy_class(
11081089
PROCESSOR_NAME,
11091090
PROCESSOR_VERSION,
11101091
PROCESSOR_NAMESPACE,
1092+
parameters_class=fake_processors.JudgyParameters,
11111093
)
1112-
1113-
test_logger.info("when: client requests a post without a required parameter")
11141094
client = processor_client_loader(judgy)
11151095
data = build_processor_prompt_content(
11161096
data_loader,
@@ -1134,7 +1114,6 @@ def process_input(*_, **__):
11341114
assert response.status_code == expected_valid_status_code, (
11351115
f"({response.status_code} != {expected_valid_status_code}) from {method}({PROCESSOR_PATH}): {content}"
11361116
)
1137-
assert "mutually exclusive" in response.text
11381117

11391118

11401119
@pytest.mark.parametrize("judgy_class", TEST_PROCESSORS)
@@ -1147,7 +1126,7 @@ def test_get_signature_definition(
11471126

11481127
test_logger.info(f"given: processor with path: {SIGNATURE_PATH}")
11491128
judgy = fake_judgy(judgy_class)
1150-
test_logger.info("when: client requests a post with no prompt")
1129+
test_logger.info("when: client requests signature definition")
11511130
client = processor_client_loader(judgy)
11521131
request = client.build_request(
11531132
url=SIGNATURE_PATH, method=method, headers={"Accept": "application/json"}

0 commit comments

Comments
 (0)