--- title: "Input Validation" description: "Validate tool inputs using Pydantic field and model validators" icon: "shield-check" --- Tool validation ensures data integrity by enforcing schemas before tools execute. Pydantic validators catch invalid input from LLMs, preventing errors and hallucinations from reaching your tool logic. ## Why Validate Tool Inputs Without validation, LLMs may provide: - Malformed data (e.g., text in numeric fields) - Out-of-range values (e.g., negative ages, future dates in the past) - Invalid formats (e.g., malformed emails, incorrect URLs) - Inconsistent field combinations (e.g., mismatched passwords) Validators enforce constraints at the schema level, giving the LLM immediate feedback to retry with correct inputs. ## Field Validators Field validators check individual fields independently. Use `@field_validator` to validate a single field's value. ### Basic Field Validation ```python from pydantic import field_validator from agency_swarm import BaseTool class User(BaseTool): """Create a user account.""" username: str age: int @field_validator('username') @classmethod def validate_username(cls, value): if ' ' in value: raise ValueError('Username must not contain spaces.') if len(value) < 3: raise ValueError('Username must be at least 3 characters.') return value @field_validator('age') @classmethod def validate_age(cls, value): if value < 0 or value > 120: raise ValueError('Age must be between 0 and 120.') return value def run(self): return f"Created user: {self.username}, age {self.age}" ``` ### Multiple Field Validation Validate multiple fields with a single validator: ```python from pydantic import field_validator from agency_swarm import BaseTool class ScheduleEvent(BaseTool): """Schedule a calendar event.""" title: str description: str @field_validator('title', 'description') @classmethod def validate_not_empty(cls, value): if not value.strip(): raise ValueError('Field cannot be empty or whitespace.') return value.strip() def run(self): return f"Scheduled: {self.title}" ``` ### Format Validation Validate specific formats like URLs or emails: ```python from pydantic import field_validator from agency_swarm import BaseTool import re class CreateWebhook(BaseTool): """Register a webhook URL.""" webhook_url: str notification_email: str @field_validator('webhook_url') @classmethod def validate_url(cls, value): url_pattern = r'^https?://.+\..+' if not re.match(url_pattern, value): raise ValueError('Must be a valid HTTP/HTTPS URL.') return value @field_validator('notification_email') @classmethod def validate_email(cls, value): email_pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$' if not re.match(email_pattern, value): raise ValueError('Must be a valid email address.') return value def run(self): return f"Webhook registered: {self.webhook_url}" ``` ## Model Validators Model validators validate the entire model, enabling checks across multiple fields. Use `@model_validator` when field validation depends on other fields. ### Cross-Field Validation ```python from pydantic import model_validator from agency_swarm import BaseTool class CreateAccount(BaseTool): """Create a new account with password.""" username: str password: str confirm_password: str @model_validator(mode='after') def check_passwords_match(self): if self.password != self.confirm_password: raise ValueError('Passwords do not match.') if len(self.password) < 8: raise ValueError('Password must be at least 8 characters.') return self def run(self): return f"Account created: {self.username}" ``` ### Date Range Validation ```python from datetime import date from pydantic import model_validator from agency_swarm import BaseTool class BookAppointment(BaseTool): """Book an appointment within a date range.""" start_date: date end_date: date reason: str @model_validator(mode='after') def validate_date_range(self): if self.start_date >= self.end_date: raise ValueError('Start date must be before end date.') if self.start_date < date.today(): raise ValueError('Cannot book appointments in the past.') return self def run(self): return f"Appointment booked: {self.start_date} to {self.end_date}" ``` ### Conditional Validation ```python from pydantic import model_validator from agency_swarm import BaseTool class ProcessPayment(BaseTool): """Process payment with optional discount code.""" amount: float discount_code: str | None = None discount_amount: float | None = None @model_validator(mode='after') def validate_discount(self): if self.discount_code and not self.discount_amount: raise ValueError('Discount amount required when discount code is provided.') if self.discount_amount and self.discount_amount >= self.amount: raise ValueError('Discount cannot exceed payment amount.') return self def run(self): total = self.amount - (self.discount_amount or 0) return f"Payment processed: ${total:.2f}" ``` ## Best Practices Provide clear, actionable error messages that guide the LLM to correct inputs. Validate at the schema level before expensive operations or external API calls. Use validators to clean and normalize data (e.g., strip whitespace, lowercase emails). Write unit tests for validators to ensure they catch invalid cases and allow valid ones. ## See Also General best practices for tool design Official Pydantic validators documentation Validate agent inputs and outputs with guardrails Configure tool behavior and parameters