Validation Constraints for Tool Function Arguments in Needle: A Complete Guide
Needle derives JSON Schema validation constraints for tool function arguments from explicit Field configurations and Python type hints, enforcing rules ranging from numeric ranges to complex Pydantic models.
The cactus-compute/needle library enables LLM agents to execute Python functions with strict, schema-based validation. By inspecting function signatures and Field metadata in needle/agent/tools.py, Needle generates comprehensive JSON Schema definitions that constrain how Large Language Models structure their tool calls. This dual approach combines declarative constraints with Python's type system to ensure data integrity before execution.
Explicit Constraints via the Field Class
The Field class in needle/agent/tools.py (lines 18-22) provides explicit constraint arguments that are copied directly into the generated JSON Schema. When Field.apply() is invoked (lines 36-49), each non-None constraint is added to the argument's schema definition, with enum values converted to arrays and const values added only when the field differs from the internal _MISSING sentinel.
Numeric Range Constraints
For integer and float arguments, Needle supports both inclusive and exclusive boundary constraints:
ge/le: Minimum and maximum inclusive valuesgt/lt: Exclusive minimum and maximum boundariesmultiple_of: Value must be an integer multiple of this number
These constraints map directly to JSON Schema's minimum, maximum, exclusiveMinimum, exclusiveMaximum, and multipleOf properties.
String Validation Rules
String arguments accept precise length and pattern controls via the Field constructor:
min_length/max_length: Character count boundariespattern: Regular expression that the string must matchformat: JSON Schema format hints such asdate-time,email, oruri
Array and Collection Constraints
When validating list or array types, the following constraints apply:
min_items/max_items: Boundaries on the number of elementsunique_items: Boolean flag enforcing that all array elements must be distinct
Value Restrictions with Enum and Const
Needle allows explicit value enumeration and constant enforcement:
enum: Explicit list of allowed values that overrides type-derived enumsconst: Single constant value the argument must equaldefault: Supplies a default value; when present, the argument becomes optional in the schemadescription: Human-readable annotation inserted into the generated schema
Type-Driven Validation from Python Hints
Beyond explicit Field constraints, the build_schema() function in needle/agent/tools.py derives validation rules from Python type annotations. This mechanism converts type hints into appropriate JSON Schema types through the internal _json_type mapping.
Literal Types for Enumerations
Using typing.Literal creates an automatic enum constraint. As implemented in lines 71-73 of needle/agent/tools.py, Literal types generate a JSON Schema enum array containing the specified literal options.
from typing import Literal
from needle.agent.tools import tool
@tool
def choose_mode(mode: Literal["fast", "slow", "balanced"]) -> str:
"""Select processing mode."""
return f"Mode set to {mode}"
Annotated Types for Mixed Constraints
The typing.Annotated construct allows you to combine type information with explicit Field constraints. The library handles Annotated types in _json_type (lines 61-63), extracting the underlying type while preserving the Field metadata for schema generation.
from typing import Annotated
from needle.agent.tools import Field, tool
@tool
def set_volume(level: Annotated[int, Field(ge=0, le=100, multiple_of=5)]) -> str:
"""Adjust volume with constraints."""
return f"Volume set to {level}%"
Optional Arguments with Union Types
Union types (using | or typing.Union) trigger optionality detection via the _is_optional helper. If a type hint includes None, the argument is excluded from the schema's required list, allowing the LLM to omit the parameter.
Pydantic Model Integration
When an argument is a subclass of pydantic.BaseModel, Needle invokes pydantic_schema() (lines 45-58) to incorporate the model's own JSON Schema, including any field validators and constraints defined within the Pydantic class.
from pydantic import BaseModel, Field as PydanticField
from needle.agent.tools import tool
class Address(BaseModel):
street: str = PydanticField(min_length=5, max_length=100)
zip_code: str = PydanticField(pattern=r"^\d{5}$")
@tool
def register_user(name: str, address: Address) -> str:
"""Register user with validated address."""
return f"User {name} registered at {address.street}"
Complete Implementation Examples
The following examples demonstrate combining multiple constraint types in real-world tool definitions.
from typing import Annotated, Literal
from needle.agent.tools import Field, tool
# Numeric constraints with exclusive bounds
@tool
def set_temperature(
celsius: Annotated[float, Field(gt=-273.15, lt=1000, multiple_of=0.5)]
) -> str:
"""Set temperature with physical constraints."""
return f"Temperature set to {celsius}°C"
# String pattern and length validation
@tool
def create_username(
username: Annotated[
str,
Field(min_length=3, max_length=20, pattern=r"^[a-z][a-z0-9_]*$",
description="Lowercase alphanumeric with underscores")
]
) -> str:
"""Create validated username."""
return f"Username '{username}' created"
# Array constraints with unique items requirement
@tool
def assign_tags(
tags: Annotated[
list,
Field(min_items=1, max_items=5, unique_items=True,
enum=["urgent", "review", "blocked", "completed"])
]
) -> str:
"""Assign workflow tags."""
return f"Tags assigned: {tags}"
Summary
- Explicit
Fieldconstraints inneedle/agent/tools.pysupport numeric ranges (ge,le,gt,lt,multiple_of), string patterns (pattern,format,min_length,max_length), and array controls (min_items,max_items,unique_items). typing.Literalautomatically generates JSON Schemaenumconstraints at lines 71-73, whiletyping.Annotatedenables mixing types withFieldinstances.- Optional arguments are detected via Union types containing
None, removing them from the required properties list. - Pydantic models integrate through
pydantic_schema()(lines 45-58), inheriting all model-level validators and field constraints. - The
Field.apply()method (lines 36-49) finalizes schema construction by filtering outNonevalues and handling the_MISSINGsentinel for optional fields.
Frequently Asked Questions
How do I make a tool argument optional in Needle?
Annotate the parameter with Optional[T] or T | None. The build_schema() function detects this union pattern via _is_optional and excludes the argument from the required array in the generated JSON Schema, allowing the LLM to omit the parameter entirely.
Can I combine multiple constraints on a single argument?
Yes. Use typing.Annotated to attach a Field instance with multiple constraint parameters to your type hint. For example: Annotated[int, Field(ge=0, le=100, multiple_of=5)] enforces range and divisibility simultaneously.
Does Needle support Pydantic validators or just schema generation?
Needle incorporates the Pydantic model's JSON Schema via pydantic_schema() in needle/agent/tools.py (lines 45-58). While the schema generation captures Pydantic field constraints like min_length and pattern, runtime validation of Pydantic validators depends on whether the consuming application instantiates the Pydantic model with the incoming arguments.
Where is the schema generation logic implemented?
All validation constraint processing resides in needle/agent/tools.py. The Field class (lines 18-22) defines constraint parameters, Field.apply() (lines 36-49) applies them to schemas, type conversion happens in _json_type (including Annotated handling at lines 61-63 and Literal processing at lines 71-73), and Pydantic integration occurs in pydantic_schema() (lines 45-58).
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 →