Skip to content

Commit e4f4d88

Browse files
authored
Merge pull request #1155 from guardrails-ai/force-validator-arg-serialization
Force validator arg serialization
2 parents 01c56ec + 4b7bcae commit e4f4d88

File tree

11 files changed

+69
-13
lines changed

11 files changed

+69
-13
lines changed

guardrails/classes/schema/processed_schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from dataclasses import dataclass, field
22
from typing import Any, Dict, List
3-
from guardrails_api_client import ValidatorReference
43
from guardrails.classes.execution.guard_execution_options import GuardExecutionOptions
54
from guardrails.classes.output_type import OutputTypes
5+
from guardrails.classes.validation.validator_reference import ValidatorReference
66
from guardrails.types.validator import ValidatorMap
77

88

guardrails/classes/validation/validator_reference.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
from typing import Any, Dict
12
from guardrails_api_client import ValidatorReference as IValidatorReference
23

4+
from guardrails.utils.serialization_utils import to_dict
5+
36

47
# Docs only
58
class ValidatorReference(IValidatorReference):
@@ -17,3 +20,26 @@ class ValidatorReference(IValidatorReference):
1720
args (Optional[List[Any]]): Positional arguments. Default None.
1821
kwargs (Optional[Dict[str, Any]]): Keyword arguments. Default None.
1922
"""
23+
24+
@classmethod
25+
def from_interface(cls, interface: IValidatorReference) -> "ValidatorReference":
26+
"""Create a ValidatorReference from an interface."""
27+
return cls(
28+
id=interface.id,
29+
on=interface.on,
30+
on_fail=interface.on_fail, # type: ignore
31+
args=interface.args,
32+
kwargs=interface.kwargs,
33+
)
34+
35+
def to_dict(self) -> Dict[str, Any]:
36+
ref_dict = super().to_dict()
37+
38+
# serialize args and kwargs
39+
if self.args:
40+
ref_dict["args"] = [to_dict(a) for a in self.args]
41+
42+
if self.kwargs:
43+
ref_dict["kwargs"] = {k: to_dict(v) for k, v in self.kwargs.items()}
44+
45+
return ref_dict

guardrails/guard.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
from guardrails_api_client import (
2424
Guard as IGuard,
25-
ValidatorReference,
2625
ValidatePayload,
2726
SimpleTypes,
2827
ValidationOutcome as IValidationOutcome,
@@ -36,6 +35,7 @@
3635
from guardrails.classes.rc import RC
3736
from guardrails.classes.validation.validation_result import ErrorSpan
3837
from guardrails.classes.validation.validation_summary import ValidationSummary
38+
from guardrails.classes.validation.validator_reference import ValidatorReference
3939
from guardrails.classes.validation_outcome import ValidationOutcome
4040
from guardrails.classes.execution import GuardExecutionOptions
4141
from guardrails.classes.generic import Stack
@@ -166,7 +166,7 @@ def __init__(
166166
id=id,
167167
name=name,
168168
description=description,
169-
validators=validators,
169+
validators=[],
170170
output_schema=model_schema,
171171
history=history, # type: ignore - pyright doesn't understand pydantic overrides
172172
)
@@ -180,6 +180,9 @@ def __init__(
180180
# self.output_schema: Optional[ModelSchema] = None
181181
# self.history = history
182182

183+
### Overrides ###
184+
self.validators = validators
185+
183186
### Legacy ##
184187
self._num_reasks = None
185188
self._rail: Optional[str] = None
@@ -208,7 +211,10 @@ def __init__(
208211
if loaded_guard:
209212
self.id = loaded_guard.id
210213
self.description = loaded_guard.description
211-
self.validators = loaded_guard.validators or []
214+
self.validators = [ # type: ignore
215+
ValidatorReference.from_interface(v)
216+
for v in loaded_guard.validators or []
217+
]
212218

213219
loaded_output_schema = (
214220
ModelSchema.from_dict( # trims out extra keys
@@ -1264,7 +1270,7 @@ def to_dict(self) -> Dict[str, Any]:
12641270
id=self.id,
12651271
name=self.name,
12661272
description=self.description,
1267-
validators=self.validators,
1273+
validators=self.validators, # type: ignore
12681274
output_schema=self.output_schema,
12691275
history=[c.to_interface() for c in self.history], # type: ignore
12701276
)
@@ -1304,7 +1310,10 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional["Guard"]:
13041310
id=i_guard.id,
13051311
name=i_guard.name,
13061312
description=i_guard.description,
1307-
validators=i_guard.validators,
1313+
validators=[
1314+
ValidatorReference.from_interface(i_val)
1315+
for i_val in i_guard.validators or []
1316+
],
13081317
output_schema=output_schema,
13091318
)
13101319

guardrails/schema/primitive_schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
from guardrails_api_client.models.model_schema import ModelSchema
44
from guardrails_api_client.models.simple_types import SimpleTypes
55
from guardrails_api_client.models.validation_type import ValidationType
6-
from guardrails_api_client.models.validator_reference import ValidatorReference
76

87
from guardrails.classes.output_type import OutputTypes
98
from guardrails.classes.schema.processed_schema import ProcessedSchema
9+
from guardrails.classes.validation.validator_reference import ValidatorReference
1010
from guardrails.validator_base import Validator
1111

1212

guardrails/schema/pydantic_schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313

1414
from pydantic import AliasChoices, AliasGenerator, AliasPath, BaseModel
1515
from pydantic.fields import FieldInfo
16-
from guardrails_api_client import ValidatorReference
1716
from guardrails.classes.output_type import OutputTypes
1817
from guardrails.classes.schema.processed_schema import ProcessedSchema
18+
from guardrails.classes.validation.validator_reference import ValidatorReference
1919
from guardrails.logger import logger
2020
from guardrails.types import (
2121
ModelOrListOfModels,

guardrails/schema/rail_schema.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
from lxml import etree as ET
77
from lxml.etree import _Element, Element, SubElement, XMLParser
88
from xml.etree.ElementTree import canonicalize
9-
from guardrails_api_client import ModelSchema, SimpleTypes, ValidatorReference
9+
from guardrails_api_client import ModelSchema, SimpleTypes
1010
from guardrails.classes.execution.guard_execution_options import GuardExecutionOptions
1111
from guardrails.classes.output_type import OutputTypes
1212
from guardrails.classes.schema.processed_schema import ProcessedSchema
13+
from guardrails.classes.validation.validator_reference import ValidatorReference
1314
from guardrails.logger import logger
1415
from guardrails.types import RailTypes
1516
from guardrails.types.validator import ValidatorMap

guardrails/utils/serialization_utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,29 @@
22
import json
33
from typing import Any, Optional
44
import warnings
5+
from dataclasses import asdict, is_dataclass
6+
from pydantic import BaseModel
57

68
from guardrails.classes.generic.default_json_encoder import DefaultJSONEncoder
79

810

11+
# This is the same logic as the DefaultJSONEncoder but without stringifying everything
12+
def to_dict(o):
13+
if hasattr(o, "to_dict"):
14+
return o.to_dict()
15+
elif isinstance(o, BaseModel):
16+
return o.model_dump()
17+
elif is_dataclass(o):
18+
return asdict(o)
19+
elif isinstance(o, set):
20+
return list(o)
21+
elif isinstance(o, datetime):
22+
return o.isoformat()
23+
elif hasattr(o, "__dict__"):
24+
return o.__dict__
25+
return o
26+
27+
928
# TODO: What other common cases we should consider?
1029
def serialize(val: Any) -> Optional[str]:
1130
try:

tests/integration_tests/schema/test_primitive_schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
22

3-
from guardrails_api_client.models.validator_reference import ValidatorReference
3+
from guardrails.classes.validation.validator_reference import ValidatorReference
44
from guardrails.classes.schema.processed_schema import ProcessedSchema
55
from guardrails.schema.primitive_schema import primitive_to_schema
66
from guardrails.classes.output_type import OutputTypes

tests/integration_tests/schema/test_pydantic_schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
22

3-
from guardrails_api_client.models.validator_reference import ValidatorReference
3+
from guardrails.classes.validation.validator_reference import ValidatorReference
44
from guardrails.classes.schema.processed_schema import ProcessedSchema
55
from guardrails.schema.pydantic_schema import pydantic_model_to_schema
66
from guardrails.classes.output_type import OutputTypes

tests/integration_tests/schema/test_rail_schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from xml.etree.ElementTree import canonicalize
55

6-
from guardrails_api_client.models.validator_reference import ValidatorReference
6+
from guardrails.classes.validation.validator_reference import ValidatorReference
77
from guardrails.classes.schema.processed_schema import ProcessedSchema
88
from guardrails.schema.rail_schema import (
99
rail_file_to_schema,

0 commit comments

Comments
 (0)