How to Constrain Tool Argument Values Using `needle.Field` in the Needle Framework
Use needle.Field as a descriptor to attach validation constraints—such as numeric ranges, string patterns, and array limits—to function arguments that become AI tool schemas.
The needle.Field class in the cactus-compute/needle repository provides a declarative way to enforce input validation rules on tool arguments. When you decorate a function with @tool, the framework inspects the signature, extracts any Field objects, and merges their constraints into the JSON-Schema that the LLM uses to validate arguments before invocation.
How needle.Field Works
The implementation lives in [needle/agent/tools.py](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L18-L49). The Field class serves as a lightweight container for constraint metadata:
Field.__init__stores constraint parameters likege,le,pattern, andmin_length【L18-L32】Field.apply(schema)injects those constraints into the generated schema dictionary, handling special cases for enumerations and constant values【L36-L48】
During schema construction, the helper _field_of detects Field instances attached to arguments—either as default values or wrapped in typing.Annotated【L89-L96】—then calls field.apply(schema) to enrich the JSON-Schema property【L30-L33】.
Supported Constraint Keywords
| Keyword | JSON-Schema Output | Use Case |
|---|---|---|
ge |
"minimum": <value> |
Inclusive numeric lower bound |
le |
"maximum": <value> |
Inclusive numeric upper bound |
gt |
"exclusiveMinimum": <value> |
Strict numeric lower bound |
lt |
"exclusiveMaximum": <value> |
Strict numeric upper bound |
enum |
"enum": [...] |
Restrict to specific values |
const |
"const": <value> |
Fixed constant value |
min_length |
"minLength": <value> |
Minimum string length |
max_length |
"maxLength": <value> |
Maximum string length |
pattern |
"pattern": "<regex>" |
Regex string validation |
format |
"format": "<format>" |
Semantic format (email, date-time, etc.) |
min_items |
"minItems": <value> |
Minimum array elements |
max_items |
"maxItems": <value> |
Maximum array elements |
unique_items |
"uniqueItems": true |
Require array elements to be unique |
Usage Examples
Basic Numeric and String Constraints
Attach Field as a default value to constrain a thermostat setting:
from needle import tool, Field
@tool
def set_thermostat(
temperature: int = Field(description="Target temperature in °C", ge=10, le=30)
):
"""Set the thermostat to a specific temperature."""
return f"Thermostat set to {temperature}°C"
The generated schema includes:
{
"temperature": {
"type": "integer",
"description": "Target temperature in °C",
"minimum": 10,
"maximum": 30
}
}
Using Annotated for Non-Default Arguments
When you can't use Field as a default, wrap it in typing.Annotated:
from typing import Annotated
from needle import tool, Field
@tool
def create_reminder(
message: Annotated[str, Field(min_length=1, max_length=120)],
when: str
):
"""Create a reminder with a length-limited message."""
return f"Reminder '{message}' scheduled for {when}"
The _field_of helper detects the Field within the Annotated metadata and applies it identically to default-value usage【L89-L96】.
Enumeration Constraints
Use enum with IntEnum classes to restrict to valid options:
import enum
from needle import tool, Field
class LogLevel(enum.IntEnum):
DEBUG = 10
INFO = 20
WARN = 30
ERROR = 40
@tool
def log(message: str, level: LogLevel = Field(default=LogLevel.INFO)):
"""Log a message with a specific severity level."""
return f"[{level.name}] {message}"
This produces "enum": [10, 20, 30, 40] with a default of 20.
Pattern Validation for Safe Identifiers
Enforce filename safety with regex patterns:
from needle import tool, Field
@tool
def rename_file(
filename: str = Field(pattern=r"^[a-zA-Z0-9_.-]+$", min_length=1, max_length=255)
):
"""Rename a file, allowing only alphanumeric and safe special characters."""
return f"File renamed to {filename}"
The schema includes "pattern": "^[a-zA-Z0-9_.-]+$" and length constraints.
Array Constraints
Control list inputs with item count and uniqueness requirements:
from needle import tool, Field
@tool
def batch_process(
ids: list[str] = Field(min_items=1, max_items=100, unique_items=True)
):
"""Process a batch of unique IDs, requiring at least one."""
return f"Processing {len(ids)} distinct items"
Generated schema properties: "minItems": 1, "maxItems": 100, "uniqueItems": true.
Combining Multiple Constraints
Field accepts multiple keywords simultaneously for compound validation:
from needle import tool, Field
from datetime import date
@tool
def schedule_event(
event_date: Annotated[str, Field(
description="ISO 8601 date",
format="date",
pattern=r"^\d{4}-\d{2}-\d{2}$"
)],
priority: int = Field(ge=1, le=5, default=3),
tags: list[str] = Field(max_items=5, unique_items=True)
):
"""Schedule an event with validated date, priority, and tag constraints."""
pass
All constraints are merged into a single schema property via Field.apply().
Summary
needle.Fieldprovides declarative argument constraints that automatically translate to JSON-Schema- Attach
Fieldas default values or wrap withtyping.Annotatedwhen defaults aren't suitable - The
_field_ofhelper andField.applymethods in [needle/agent/tools.py](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) handle the schema enrichment pipeline - Supported constraints span numeric ranges, string patterns, array bounds, enumerations, and semantic formats
- The LLM receives fully validated schemas, reducing invalid tool invocations
Frequently Asked Questions
What happens if I provide an invalid constraint combination?
needle.Field stores all constraints you provide in __init__ and applies them in apply(), with no built-in conflict detection. The JSON-Schema itself may be rejected by strict schema validators if constraints are logically incompatible (e.g., ge=10 with lt=5). Test your schemas with actual LLM providers to catch validation errors early.
Can I use needle.Field without the @tool decorator?
Field objects work standalone as descriptors, but they only affect JSON-Schema generation when processed through the @tool decorator's schema builder. Using Field on regular Python functions has no runtime effect unless you manually invoke the schema construction utilities from [needle/agent/tools.py](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py).
How does needle.Field compare to Pydantic's Field?
Both classes serve similar purposes, but needle.Field is purpose-built for LLM tool schemas rather than general data validation. It produces JSON-Schema directly without requiring full Pydantic models, keeping the needle framework lightweight for agent tool definitions.
Where can I find more examples of Field usage?
The test suite at [tests/test_tools.py](https://github.com/cactus-compute/needle/blob/main/tests/test_tools.py) contains comprehensive examples of constraint combinations and their expected schema outputs. The re-exports in [needle/__init__.py](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) confirm the public API surface for tool and Field.
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 →