@@ -155,6 +155,72 @@ def _create_word_rule_func(
155155 raise ValueError (f"Unsupported word operator: { operator } " )
156156
157157
158+ def _build_field_selector_description (field_selector : AgentFieldSelector ) -> str :
159+ """Build a human-readable selector description for field selector.
160+
161+ Args:
162+ field_selector: The field selector describing which fields this rule applies to.
163+
164+ Returns:
165+ A string describing the selector, using:
166+ - \" All\" for `AgentAllFieldsSelector`
167+ - Comma-separated field paths for `SpecificFieldsSelector`
168+ - ``str(field_selector)`` as a fallback.
169+ """
170+ if isinstance (field_selector , AgentAllFieldsSelector ):
171+ return "All fields"
172+ if isinstance (field_selector , SpecificFieldsSelector ):
173+ field_paths = [field .path for field in field_selector .fields ]
174+ return ", " .join (field_paths )
175+ return str (field_selector )
176+
177+
178+ def _build_rule_description (
179+ operator : AgentWordOperator | AgentNumberOperator | AgentBooleanOperator ,
180+ value : str | float | bool | None ,
181+ field_selector : AgentFieldSelector ,
182+ ) -> str :
183+ """Build the full human-readable description for a word rule.
184+
185+ Args:
186+ operator: The word operator to describe.
187+ value: The comparison value, if applicable for the operator.
188+ field_selector: The field selector describing which fields this rule applies to.
189+
190+ Returns:
191+ A string describing the rule, combining selector, operator, and value.
192+ """
193+ selector_description = _build_field_selector_description (field_selector )
194+
195+ if operator in {
196+ AgentWordOperator .CONTAINS ,
197+ AgentWordOperator .DOES_NOT_CONTAIN ,
198+ AgentWordOperator .EQUALS ,
199+ AgentWordOperator .DOES_NOT_EQUAL ,
200+ AgentWordOperator .STARTS_WITH ,
201+ AgentWordOperator .DOES_NOT_START_WITH ,
202+ AgentWordOperator .ENDS_WITH ,
203+ AgentWordOperator .DOES_NOT_END_WITH ,
204+ AgentWordOperator .MATCHES_REGEX ,
205+ AgentNumberOperator .EQUALS ,
206+ AgentNumberOperator .DOES_NOT_EQUAL ,
207+ AgentNumberOperator .GREATER_THAN ,
208+ AgentNumberOperator .GREATER_THAN_OR_EQUAL ,
209+ AgentNumberOperator .LESS_THAN ,
210+ AgentNumberOperator .LESS_THAN_OR_EQUAL ,
211+ AgentBooleanOperator .EQUALS ,
212+ }:
213+ return f"{ selector_description } { operator .value } { value !r} "
214+
215+ if operator in {
216+ AgentWordOperator .IS_EMPTY ,
217+ AgentWordOperator .IS_NOT_EMPTY ,
218+ }:
219+ return f"{ selector_description } { operator .value } "
220+
221+ raise ValueError (f"Unsupported word operator: { operator } " )
222+
223+
158224def _create_number_rule_func (
159225 operator : AgentNumberOperator , value : float
160226) -> Callable [[float ], bool ]:
@@ -314,6 +380,9 @@ def _convert_agent_rule_to_deterministic(
314380 detects_violation = _create_word_rule_func (
315381 agent_rule .operator , agent_rule .value
316382 ),
383+ rule_description = _build_rule_description (
384+ agent_rule .operator , agent_rule .value , agent_rule .field_selector
385+ ),
317386 )
318387
319388 if isinstance (agent_rule , AgentNumberRule ):
@@ -325,6 +394,9 @@ def _convert_agent_rule_to_deterministic(
325394 detects_violation = _create_number_rule_func (
326395 agent_rule .operator , agent_rule .value
327396 ),
397+ rule_description = _build_rule_description (
398+ agent_rule .operator , agent_rule .value , agent_rule .field_selector
399+ ),
328400 )
329401
330402 if isinstance (agent_rule , AgentBooleanRule ):
@@ -336,6 +408,9 @@ def _convert_agent_rule_to_deterministic(
336408 detects_violation = _create_boolean_rule_func (
337409 agent_rule .operator , agent_rule .value
338410 ),
411+ rule_description = _build_rule_description (
412+ agent_rule .operator , agent_rule .value , agent_rule .field_selector
413+ ),
339414 )
340415
341416 raise ValueError (f"Unsupported agent rule type: { type (agent_rule )} " )
0 commit comments