Validation System
Format-agnostic validation for tool arguments and data schemas
Overview
Aegeantic provides a flexible validation system that supports multiple validation formats. The system is extensible, allowing you to use JSON Schema, custom validators, or the built-in simple validator.
Design Philosophy: The validation system is format-agnostic. You choose which validator to use based on your needs, and all validators implement the same interface.
Validator Registry
The ValidatorRegistry manages all available validators:
from agentic import ValidatorRegistry
# Create registry (simple and passthrough validators are pre-registered)
registry = ValidatorRegistry()
# Validate data
is_valid, errors = registry.validate(
data={"name": "John", "age": 30},
schema={
"validator": "simple",
"required": ["name", "age"],
"fields": {
"name": {"type": "str", "min_length": 1},
"age": {"type": "int", "min": 0}
}
}
)
Simple Validator
Built-in validator for common validation scenarios:
Type Validation
schema = {
"validator": "simple",
"required": ["name"],
"fields": {
"name": {"type": "str"},
"age": {"type": "int"},
"score": {"type": "float"},
"active": {"type": "bool"},
"items": {"type": "list"},
"metadata": {"type": "dict"}
}
}
String Validation
| Constraint | Description | Example |
|---|---|---|
min_length |
Minimum string length | {"type": "str", "min_length": 5} |
max_length |
Maximum string length | {"type": "str", "max_length": 100} |
pattern |
Regex pattern match | {"type": "str", "pattern": "^[a-z]+$"} |
schema = {
"validator": "simple",
"fields": {
"username": {
"type": "str",
"min_length": 3,
"max_length": 20,
"pattern": "^[a-zA-Z0-9_]+$"
},
"email": {
"type": "str",
"pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
}
}
}
Numeric Validation
| Constraint | Description | Example |
|---|---|---|
min |
Minimum value (inclusive) | {"type": "int", "min": 0} |
max |
Maximum value (inclusive) | {"type": "int", "max": 100} |
schema = {
"validator": "simple",
"fields": {
"age": {
"type": "int",
"min": 0,
"max": 150
},
"probability": {
"type": "float",
"min": 0.0,
"max": 1.0
},
"temperature": {
"type": "float",
"min": 0.0,
"max": 2.0
}
}
}
List Validation
| Constraint | Description | Example |
|---|---|---|
min_items |
Minimum list length | {"type": "list", "min_items": 1} |
max_items |
Maximum list length | {"type": "list", "max_items": 10} |
schema = {
"validator": "simple",
"fields": {
"tags": {
"type": "list",
"min_items": 1,
"max_items": 5
}
}
}
Custom Validator Functions
def is_valid_url(value):
return isinstance(value, str) and value.startswith(("http://", "https://"))
def is_positive_even(value):
return isinstance(value, int) and value > 0 and value % 2 == 0
schema = {
"validator": "simple",
"fields": {
"url": {
"type": "str",
"validator_func": is_valid_url
},
"count": {
"type": "int",
"validator_func": is_positive_even
}
}
}
Passthrough Validator
Validator that skips all validation checks:
schema = {
"validator": "passthrough"
}
# Always returns (True, []) - useful for disabling validation on specific tools
is_valid, errors = registry.validate(any_data, schema)
assert is_valid == True
assert errors == []
Custom Validators
Create your own validator functions to extend the system:
from agentic import ValidationError
def custom_validator(value: dict, schema: dict) -> tuple[bool, list[ValidationError]]:
"""Custom validator function."""
errors = []
# Custom validation logic
if "required_field" not in value:
errors.append(ValidationError(
field="required_field",
message="This field is required",
value=None
))
if "custom_check" in value:
if not _check_custom_rule(value["custom_check"]):
errors.append(ValidationError(
field="custom_check",
message="Custom rule failed",
value=value["custom_check"]
))
is_valid = len(errors) == 0
return is_valid, errors
def _check_custom_rule(value):
# Your custom validation logic
return True
# Register custom validator
registry.register("custom", custom_validator)
Tool Validation
Tools automatically validate arguments using the configured validator:
from agentic import create_tool, ToolRegistry, ValidatorRegistry
# Setup validator registry (simple and passthrough validators pre-registered)
validator_registry = ValidatorRegistry()
# Create tool registry with validators
tools = ToolRegistry(validator_registry=validator_registry)
# Create tool with validation
def search_web(args):
query = args["query"]
return {"results": [...]}
search_tool = create_tool(
name="search",
func=search_web,
input_schema={
"validator": "simple",
"required": ["query"],
"fields": {
"query": {
"type": "str",
"min_length": 1,
"max_length": 500
},
"max_results": {
"type": "int",
"min": 1,
"max": 100
}
}
}
)
tools.register(search_tool)
# Validation happens automatically during tool execution
# Invalid arguments will return ToolResult with success=False
Validation Errors
Validation errors provide detailed information about failures:
class ValidationError:
field: str # Field that failed validation
message: str # Error message
value: Any # The invalid value
# Example usage
is_valid, errors = registry.validate(data, schema)
if not is_valid:
for error in errors:
print(f"Field '{error.field}': {error.message}")
print(f"Invalid value: {error.value}")
Complex Validation Examples
API Request Validation
api_request_schema = {
"validator": "simple",
"required": ["method", "url"],
"fields": {
"method": {
"type": "str",
"pattern": "^(GET|POST|PUT|DELETE|PATCH)$"
},
"url": {
"type": "str",
"pattern": "^https?://",
"max_length": 2048
},
"headers": {
"type": "dict"
},
"body": {
"type": "dict"
},
"timeout": {
"type": "int",
"min": 1,
"max": 300
}
}
}
Database Query Validation
query_schema = {
"validator": "simple",
"required": ["table", "operation"],
"fields": {
"table": {
"type": "str",
"pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$" # Valid SQL identifier
},
"operation": {
"type": "str",
"pattern": "^(SELECT|INSERT|UPDATE|DELETE)$"
},
"where": {
"type": "dict"
},
"limit": {
"type": "int",
"min": 1,
"max": 1000
}
}
}
File Operation Validation
import os
def is_safe_path(path):
# Prevent path traversal attacks
return ".." not in path and not os.path.isabs(path)
file_op_schema = {
"validator": "simple",
"required": ["operation", "path"],
"fields": {
"operation": {
"type": "str",
"pattern": "^(read|write|delete)$"
},
"path": {
"type": "str",
"validator_func": is_safe_path,
"max_length": 255
},
"content": {
"type": "str",
"max_length": 1_000_000 # 1MB limit
}
}
}
Disabling Validation
You can disable validation for specific agents or tools:
# Disable validation in agent config
config = AgentConfig(
agent_id="my_agent",
validate_tool_arguments=False # Skip validation
)
# Or create tools without schemas
tool = create_tool(
name="unvalidated_tool",
func=my_function,
input_schema={} # No validation
)
Security: Always validate inputs from untrusted sources. Validation prevents injection attacks, invalid data, and unexpected behavior.
Best Practices
- Validate Early: Validate at the earliest point possible to fail fast
- Specific Constraints: Use specific constraints (min/max, patterns) rather than just type checking
- Clear Error Messages: Provide actionable error messages for validation failures
- Whitelist Patterns: Use pattern matching to whitelist valid inputs (e.g., allowed operations)
- Custom Validators: Create custom validators for domain-specific rules
- Security First: Always validate user inputs and external data
- Test Validation: Write tests for your validation schemas
- Document Schemas: Document what each field should contain
Performance Considerations
Validation adds minimal overhead:
- Simple validator: ~0.1ms per validation
- JSON Schema validator: ~0.5-2ms per validation
- Custom validators: Depends on complexity
For high-throughput scenarios:
- Cache compiled regex patterns
- Use simple validator for common cases
- Only validate untrusted inputs
- Consider disabling validation in development (with caution)
Next Steps
- Tools - Add validation to tools
- Agent System - Configure validation in agents
- API Reference - Complete validation API