Models & Validation
20 min
Prompt Engineering's structured-output module validated a dict by
hand: check each field exists, check its type with isinstance().
Pydantic automates this with declarative models — define the shape once
as a class, and get validation (plus something isinstance() doesn't
do: automatic type COERCION for compatible values) for free every time
you construct one.
from pydantic import BaseModel, ValidationError
class User(BaseModel):
name: str
age: int
active: bool = True
def validate_user(data):
try:
user = User(**data)
return ("ok", user.name, user.age, user.active)
except ValidationError as e:
return ("error", len(e.errors()))
This coercion behavior is a real, easy-to-miss gotcha -- a hand-written isinstance() check (like Prompt Engineering's structured-output) would have flatly rejected a numeric string passed as age, while pydantic accepts it because it can be unambiguously converted. Worth knowing before assuming pydantic validation is exactly as strict as manual type-checking.
Given the provided `User` model (`name: str`, `age: int`, `active: bool = True`), write `validate_user(data)`: try constructing `User(**data)`. On success, return `("ok", user.name, user.age, user.active)`. On a `ValidationError`, return `("error", len(e.errors()))` — the number of fields that failed.
Prompt Engineering's structured-output module hand-wrote a validator checking field presence and exact type with isinstance(). What does pydantic do differently when a field like age: int receives the string "25"?
You can validate data against a pydantic model and handle ValidationError, and understand pydantic's automatic type coercion behavior versus a strict isinstance() check.