Guardrails AIguardrails-ai 0.11.0 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
27 small wins to finish your pathNext lesson

Validators on one field

Lesson 17's Ticket accepted a priority of ninety-nine and an issue nobody at the shop has ever heard of. The on= argument from lesson 15 takes a JSON path, and that is where field rules go.

Example
from guardrails_ai.valid_choices import ValidChoices
from guardrails_ai.valid_range import ValidRange

tickets = (Guard.for_pydantic(Ticket)
           .use(ValidChoices(choices=["late", "damaged", "wrong item"], on_fail="noop"), on="$.issue")
           .use(ValidRange(min=1, max=3, on_fail="noop"), on="$.priority"))

outcome = tickets.parse('{"order_id": "8821", "issue": "lost", "priority": 99}', num_reasks=0)

for summary in sorted(outcome.validation_summaries, key=lambda s: s.property_path):
    print(summary.property_path, summary.validator_name, "|", summary.failure_reason)

$.issue and $.priority are JSON paths into the parsed object, and property_path in the summary tells you which field complained. Two use calls, two different targets, and both stick, which is the rule lesson 6 ended on.

Pydantic has its own way of attaching validators, inside Field(validators=[...]). It still works and the Guardrails documentation uses it throughout, but on current Pydantic it prints a deprecation warning about extra keyword arguments on Field. The on= form does the same job and keeps the model class clean.

filter, the half that was deferred

Lesson 11 left filter unfinished, because on a plain string it does the same thing as refrain. On an object it does what its name says.

Example
tickets = Guard.for_pydantic(Ticket).use(
    ValidChoices(choices=["late", "damaged"], on_fail="filter"), on="$.issue")

outcome = tickets.parse('{"order_id": "8821", "issue": "lost", "priority": 2}', num_reasks=0)

print(outcome.validated_output)
print(tickets.history.last.iterations.last.guarded_output)

Two different answers to the same question. The iteration holds the ticket with the bad field removed, exactly as the documentation promises. The outcome holds None, because of the rule from lesson 11: guarded_output on the call only returns a value when the Guard passed or every failure was a noop, and a filtered field is neither.

So the sentence in the on-fail table, that filter returns the rest of the generated output, is true one level below the object you actually read. If you want the filtered ticket, take it from the iteration and decide for yourself whether a ticket with a missing field is worth filing.

Try it yourself
  • Add ValidLength on $.order_id so a four digit number is the only thing that fits.
  • Set the issue rule to fix and give ValidChoices nothing to fix with. Lesson 10 predicted what you get.
  • Use on="$.priority" twice on the same Guard and confirm the overwrite rule from lesson 6 applies per path.

Slow is fine. Stopping is the only problem.