How to Implement Custom Field Validation in Needle Tool Schemas
Use the Field class from needle.agent.tools with validation parameters like ge, le, enum, pattern, or min_length, then attach it to function parameters as a default value or inside typing.Annotated.
Needle's tool-schema generation automatically converts Python function signatures into JSON-Schema definitions for LLM tool calls. Implementing custom field validation in Needle tool schemas lets you enforce constraints directly at the schema level, eliminating the need for manual runtime checks.
Understanding Needle's Validation Architecture
The validation system centers on three components in needle/agent/tools.py:
Fieldclass (lines 17-31): Stores validation metadata as instance attributesField.applymethod (lines 35-48): Injects JSON-Schema keys like"minimum","maximum","pattern", and"enum"_field_ofhelper (lines 84-91): ExtractsFieldinstances from parameters during schema building
When you decorate a function with @tool, the build_schema routine inspects each parameter, locates any attached Field objects, and merges their constraints into the final schema. The LLM then receives these constraints and must generate compliant inputs.
Attaching Fields to Parameters
Needle supports two patterns for associating Field instances with function parameters.
Pattern 1: Direct Field as Default Value
Pass a Field directly where a default value would appear:
from needle.agent.tools import Field, tool
@tool
def set_mode(mode: Field(default="auto", enum=["auto", "manual"])):
"""
Switch the system mode.
"""
return f"Mode set to {mode}"
The generated schema contains:
"enum": ["auto", "manual"]"default": "auto"
Pattern 2: Annotated with Field
Use typing.Annotated to combine a type annotation with validation rules:
from typing import Annotated
from needle.agent.tools import Field, tool
@tool
def send_email(
address: Annotated[str, Field(pattern=r"^[\w\.-]+@[\w\.-]+\.\w+$")],
subject: Annotated[str, Field(min_length=5, max_length=100)],
body: str,
):
"""Send an email with validated address and subject."""
return "Email queued"
The schema enforces:
- Regex pattern for
address(valid email format) - Length limits for
subject(5-100 characters)
Available Validation Parameters
The Field class accepts these constraint parameters, each mapping to a JSON-Schema property:
| Parameter | JSON-Schema Key | Use Case |
|---|---|---|
ge |
"minimum" |
Numeric lower bound (≥) |
le |
"maximum" |
Numeric upper bound (≤) |
gt |
"exclusiveMinimum" |
Strict numeric lower bound (>) |
lt |
"exclusiveMaximum" |
Strict numeric upper bound (<) |
enum |
"enum" |
Whitelist of allowed values |
pattern |
"pattern" |
Regex string validation |
min_length |
"minLength" |
String minimum length |
max_length |
"maxLength" |
String maximum length |
default |
"default" |
Default value in schema |
Numeric Range Validation Example
from needle.agent.tools import Field, tool
@tool
def scale_image(
size: Field(default=256, ge=64, le=1024),
keep_aspect: bool = True,
):
"""Resize an image to a given square size."""
return f"Image resized to {size}px"
The resulting schema includes:
"minimum": 64"maximum": 1024"default": 256
How Field Defaults Are Handled
The Field.has_default property (lines 32-33) detects whether a default value was explicitly provided. This ensures:
- Direct
Fieldusage: Thedefaultparameter is always captured Annotatedusage: The type annotation andFieldare separate from any Python default
Needle correctly merges these cases so the JSON-Schema always reflects the intended default behavior.
Complete Validation Workflow
- Import
Fieldandtoolfromneedle.agent.tools - Define constraints using
Fieldparameters relevant to your data type - Attach to parameter via direct default or
Annotated[type, Field(...)] - Decorate with
@toolto trigger schema generation - Verify the output schema contains your constraints
The LLM receives the constrained schema and generates compliant tool calls, with validation occurring before your Python function ever executes.
Summary
- Custom field validation in Needle uses the
Fieldclass inneedle/agent/tools.pyto inject JSON-Schema constraints - Two attachment patterns: direct
Fieldas default, orAnnotated[type, Field(...)]for complex types - Validation parameters map directly to JSON-Schema:
ge/lefor ranges,patternfor regex,enumfor whitelists,min_length/max_lengthfor strings - Default values are captured via
Field.has_defaultand reflected in schema output - The
@tooldecorator'sbuild_schemaroutine processes these fields through_field_ofandField.apply
Frequently Asked Questions
What is the difference between using Field directly versus Annotated?
Using Field directly replaces the default value and works when you don't need an explicit type annotation. Annotated preserves the type hint and separates concerns—use it when you need both strict typing and validation rules, or when the parameter has a separate runtime default.
Can I combine multiple validation constraints on one field?
Yes. The Field constructor accepts multiple parameters simultaneously. For example: Field(ge=0, le=100, default=50) creates a bounded integer with a default. The apply method merges all provided constraints into the schema.
Does Needle validate inputs at runtime or only in the schema?
Needle generates the JSON-Schema for LLM consumption; the LLM is expected to produce compliant inputs. For additional runtime safety, you can use Pydantic models or manual checks inside your tool function, though the schema-level validation handles most cases.
Where are the core validation classes defined?
All validation logic resides in needle/agent/tools.py. Key locations: Field class (lines 17-31), Field.apply method (lines 35-48), and _field_of extraction helper (lines 84-91). The test suite at tests/test_tools.py contains practical examples.
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 →