1010 evaluate_word_rule ,
1111)
1212from .guardrails import (
13+ AllFieldsSelector ,
14+ ApplyTo ,
1315 BooleanRule ,
1416 DeterministicGuardrail ,
17+ FieldSource ,
1518 GuardrailValidationResult ,
1619 NumberRule ,
20+ SpecificFieldsSelector ,
1721 UniversalRule ,
1822 WordRule ,
1923)
@@ -27,6 +31,16 @@ def evaluate_pre_deterministic_guardrail(
2731 guardrail : DeterministicGuardrail ,
2832 ) -> GuardrailValidationResult :
2933 """Evaluate deterministic guardrail rules against input data (pre-execution)."""
34+ # Check if guardrail contains any output-dependent rules
35+ has_output_rule = self ._has_output_dependent_rule (guardrail , [ApplyTo .OUTPUT ])
36+
37+ # If guardrail has output-dependent rules, skip evaluation in pre-execution
38+ # Output rules will be evaluated during post-execution
39+ if has_output_rule :
40+ return GuardrailValidationResult (
41+ validation_passed = True ,
42+ reason = "Guardrail contains output-dependent rules that will be evaluated during post-execution" ,
43+ )
3044 return self ._evaluate_deterministic_guardrail (
3145 input_data = input_data ,
3246 output_data = {},
@@ -41,12 +55,61 @@ def evaluate_post_deterministic_guardrail(
4155 guardrail : DeterministicGuardrail ,
4256 ) -> GuardrailValidationResult :
4357 """Evaluate deterministic guardrail rules against input and output data."""
58+ # Check if guardrail contains any output-dependent rules
59+ has_output_rule = self ._has_output_dependent_rule (
60+ guardrail , [ApplyTo .OUTPUT , ApplyTo .INPUT_AND_OUTPUT ]
61+ )
62+
63+ # If guardrail has no output-dependent rules, skip post-execution evaluation
64+ # Only input rules exist and they should have been evaluated during pre-execution
65+ if not has_output_rule :
66+ return GuardrailValidationResult (
67+ validation_passed = True ,
68+ reason = "Guardrail contains only input-dependent rules that were evaluated during pre-execution" ,
69+ )
70+
4471 return self ._evaluate_deterministic_guardrail (
4572 input_data = input_data ,
4673 output_data = output_data ,
4774 guardrail = guardrail ,
4875 )
4976
77+ @staticmethod
78+ def _has_output_dependent_rule (
79+ guardrail : DeterministicGuardrail ,
80+ universal_rules_apply_to_values : list [ApplyTo ],
81+ ) -> bool :
82+ """Check if at least one rule EXCLUSIVELY requires output data.
83+
84+ Args:
85+ guardrail: The guardrail to check
86+ universal_rules_apply_to_values: List of ApplyTo values to consider as output-dependent for UniversalRules.
87+
88+ Returns:
89+ True if at least one rule exclusively depends on output data, False otherwise.
90+ """
91+ for rule in guardrail .rules :
92+ # UniversalRule: only return True if it applies to values in universal_rules_apply_to_values
93+ if isinstance (rule , UniversalRule ):
94+ if rule .apply_to in universal_rules_apply_to_values :
95+ return True
96+ # Rules with field_selector
97+ elif isinstance (rule , (WordRule , NumberRule , BooleanRule )):
98+ field_selector = rule .field_selector
99+ # AllFieldsSelector applies to both input and output, not exclusively output
100+ # SpecificFieldsSelector: only return True if at least one field has OUTPUT source
101+ if isinstance (field_selector , SpecificFieldsSelector ):
102+ if field_selector .fields and any (
103+ field .source == FieldSource .OUTPUT
104+ for field in field_selector .fields
105+ ):
106+ return True
107+ elif isinstance (field_selector , AllFieldsSelector ):
108+ if FieldSource .OUTPUT in field_selector .sources :
109+ return True
110+
111+ return False
112+
50113 @staticmethod
51114 def _evaluate_deterministic_guardrail (
52115 input_data : dict [str , Any ],
0 commit comments