How to Implement Custom Field Validation Constraints in Needle: A Complete Guide
Use the Field class from needle.agent.tools to declare validation rules directly on function parameters, and Needle automatically translates them into OpenAI-compatible JSON schemas.
Needle is a lightweight Python framework for building agent tools with structured validation. This guide shows you how to implement custom field validation constraints in Needle using the Field class, which is defined in [needle/agent/tools.py](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L18).
Understanding Needle's Validation Architecture
Needle's validation system centers on three core components working together:
Fieldclass – Container for constraint metadatabuild_schemafunction – Schema generation engine (build_schema)@tooldecorator – Registration mechanism that attaches schemas to functions (tool)
When you decorate a function with @tool, Needle inspects the signature through _field_of (_field_of), extracts any Field objects, and merges their constraints into a JSON schema via the apply method (apply).
Declaring Field Validation Constraints
The Field constructor accepts multiple constraint types that map directly to JSON Schema keywords:
Numeric Bounds
ge/le– inclusive minimum/maximumgt/lt– exclusive minimum/maximum
String Validation
min_length/max_length– character limitspattern– regex pattern matchingformat– semantic format (email, uri, etc.)
Collection Constraints
min_items/max_items– length limits for listsunique_items– enforce uniqueness
Value Restrictions
enum– restrict to specific valuesconst– require exact value
Basic Usage: Declaring Fields as Default Values
The simplest pattern attaches a Field as a parameter's default value:
from needle import tool, Field, build_schema
@tool
def set_environment(
temp: int = Field(description="temperature in °C", ge=0, le=100),
mode: str = Field(default="auto", pattern="^(auto|manual)$")
):
"""Configure the environment."""
pass
# Inspect the generated schema
print(build_schema(set_environment))
This produces a schema with validated constraints:
{
"name": "set_environment",
"parameters": {
"type": "object",
"properties": {
"temp": {
"type": "integer",
"description": "temperature in °C",
"minimum": 0,
"maximum": 100
},
"mode": {
"type": "string",
"default": "auto",
"pattern": "^(auto|manual)$"
}
},
"required": ["temp"]
}
}
Note that temp is required (no default provided) while mode is optional (has default="auto").
Using typing.Annotated for Cleaner Defaults
When you need both a default value and validation constraints, use typing.Annotated to separate concerns:
from typing import Annotated
from needle import tool, Field, build_schema
@tool
def upload_file(
path: Annotated[str, Field(pattern=r"^/data/.*\.csv$", description="CSV file path")] = "/data/default.csv"
):
"""Upload a CSV file."""
pass
print(build_schema(upload_file))
This pattern keeps the default value assignment clean while preserving full validation capabilities.
Combining Multiple Validation Constraints
Real-world tools often need layered validation. Here's a comprehensive example:
@tool
def create_user(
username: str = Field(min_length=3, max_length=20, pattern=r"^\w+$"),
age: int = Field(ge=13, le=120),
tags: list = Field(min_items=1, max_items=10, unique_items=True),
role: str = Field(enum=["admin", "editor", "viewer"])
):
"""Create a new user with validated attributes."""
pass
This single function enforces:
- Username: 3-20 word characters only
- Age: 13-120 inclusive
- Tags: 1-10 unique items
- Role: restricted to three specific values
How Schema Generation Works
The internal flow in needle/agent/tools.py processes your declarations as follows:
_field_ofdetectsFieldobjects either as default values or withinAnnotatedwrappers- Type inspection determines JSON Schema types from Python annotations
applymergesFieldconstraints into the schema property- Required/optional status derives from
Optionalannotations or missing defaults @toolstores the final schema onfn._needle_tool
This architecture means validation constraints travel with your function and are available whenever Needle generates tool definitions for language models.
Summary
Fieldis the primary interface for custom validation constraints in Needle- Constraints map directly to JSON Schema validation keywords
- Use default values for simple cases or
Annotatedwhen you need both defaults and validation - The
@tooldecorator automatically generates and attaches the complete schema - All examples are verified in [
tests/test_tools.py](https://github.com/cactus-compute/needle/blob/main/tests/test_tools.py)
Frequently Asked Questions
What validation types does Needle's Field support?
Needle supports numeric bounds (ge, le, gt, lt), string constraints (min_length, max_length, pattern, format), collection limits (min_items, max_items, unique_items), and value restrictions (enum, const). The full implementation is in [needle/agent/tools.py](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L18).
How does Needle handle optional parameters with Field constraints?
Needle determines required status from the presence of a default value. A parameter without a default (like temp: int = Field(...)) becomes required, while one with a default (like mode: str = Field(default="auto")) becomes optional. The build_schema function implements this logic.
Can I use Field without the @tool decorator?
Yes. The build_schema function works independently to generate JSON schemas from any function signature containing Field objects. However, the @tool decorator is required to register the function for use within Needle's agent framework.
Where are Field validation constraints tested?
The test suite in [tests/test_tools.py](https://github.com/cactus-compute/needle/blob/main/tests/test_tools.py) validates that declared constraints correctly appear in generated schemas. These tests verify constraint propagation through build_schema and proper handling of Annotated types.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →