How to Use the Field Class for Complex Tool Schemas in Needle 2
The Field class in Needle 2 allows you to add JSON Schema validation constraints like ranges, enums, and patterns to tool parameters using either default values or typing.Annotated wrappers.
Needle 2 transforms Python functions into LLM-callable tools through the @tool decorator, automatically generating JSON Schema descriptions from type hints. When you need to enforce complex validation rules—such as numeric ranges, regex patterns, or enumerated values—the Field class provides a declarative way to inject these constraints directly into the schema without manual JSON editing.
How the Field Class Works in Needle 2
The Field implementation resides in needle/agent/tools.py (source lines 18-49) alongside the schema-building logic. Understanding its three-stage pipeline helps you leverage it effectively for complex tool definitions.
Field Construction and Storage
When you instantiate Field, it stores optional JSON Schema keywords as attributes. The constructor accepts parameters including description, enum, ge/le/gt/lt for numeric bounds, minLength/maxLength for strings, pattern for regex validation, and array constraints like min_items or unique_items.
Schema Application Logic
The Field.apply(schema) method (lines 36-48) injects stored keywords into the property's schema dictionary through a direct loop. This mutation happens during the schema generation phase, enriching the base type definition with your validation metadata before the final JSON Schema is returned to the LLM runtime.
Detection Mechanisms
During build_schema execution, Needle detects Field instances through two distinct pathways defined in the _field_of helper (lines 85-92):
- As a default value: When a parameter uses
def func(param: int = Field(...)), the decorator extracts theFieldfrom the default argument position. - Inside
typing.Annotated: When a parameter usesAnnotated[int, Field(...)], the helper inspects the type hint metadata to locate theFieldinstance.
Once detected, Field.has_default() determines whether the parameter should be marked as optional in the generated schema.
Practical Usage Examples
Using Field as a Default Value
Attach validation constraints by assigning Field as the parameter default. This approach works best when the parameter is inherently optional.
from needle.agent.tools import tool, Field
@tool
def generate_image(
prompt: str,
steps: int = Field(default=30, description="Number of diffusion steps", ge=10, le=100),
sampler: str = Field(default="ddim", enum=["ddim", "plms"], description="Sampling algorithm"),
) -> str:
"""Generates an image from a text prompt."""
...
Here, steps constrains integers between 10 and 100, while sampler restricts input to the enumeration ["ddim", "plms"]. The build_schema function generates:
{
"steps": {"type": "integer", "description": "Number of diffusion steps", "minimum": 10, "maximum": 100, "default": 30},
"sampler": {"type": "string", "enum": ["ddim","plms"], "description": "Sampling algorithm", "default": "ddim"}
}
Using Field Inside typing.Annotated
For required parameters or when you need to preserve a separate default value, wrap the type in typing.Annotated to attach Field metadata without consuming the default argument slot.
from typing import Annotated
from needle.agent.tools import tool, Field
@tool
def summarize(
text: str,
length: Annotated[int, Field(description="Desired length in tokens", ge=5, le=500)] = 100,
) -> str:
"""Summarizes the given text."""
...
In this pattern, Annotated carries the Field instance while the = 100 assignment provides the runtime default. According to the source code in needle/agent/tools.py, the _field_of helper extracts the Field from the annotation metadata regardless of where the actual default value resides.
Combining Multiple Constraints
You can stack multiple validation keywords within a single Field instance to create sophisticated constraints for complex tool schemas.
@tool
def upload_file(
path: Annotated[str, Field(description="Local file path", pattern=r"^/[^\\]*$")],
tags: Annotated[list[str], Field(min_items=1, unique_items=True)] = [],
) -> bool:
"""Uploads a file and optional tags."""
...
This configuration enforces that path matches the specified regular expression pattern and that tags contains at least one unique string when provided. The Field.apply method merges these keywords into the array and string schema definitions respectively.
Key Implementation Files
According to the Needle 2 repository structure, the Field class and schema generation logic are distributed across these critical files:
needle/agent/tools.py: Defines theFieldclass, helper functions (_json_type,_field_of), and the@tooldecorator that orchestratesbuild_schemato generate JSON Schema.needle/agent/__init__.py: Exports thetooldecorator for public API consumption.tests/test_tools.py: Contains unit tests verifyingFieldintegration with the schema builder, including edge cases for both default-value andAnnotateddetection patterns.
Summary
- The
Fieldclass inneedle/agent/tools.pystores JSON Schema validation keywords likege,le,pattern, andenum. - Needle 2 detects
Fieldinstances either as parameter defaults or insidetyping.Annotatedmetadata via the_field_ofhelper. - Use
Field(default=...)for optional parameters with built-in defaults, orAnnotated[T, Field(...)]when you need to separate validation metadata from runtime defaults. - The
Field.applymethod injects constraints directly into the generated JSON Schema, making tools self-documenting and enforceable by the LLM runtime without manual schema maintenance.
Frequently Asked Questions
What is the difference between using Field as a default value versus inside Annotated?
Using Field as a default value (param: int = Field(default=...)) combines the validation schema with the runtime default in a single assignment, making the parameter optional. Using Field inside Annotated (Annotated[int, Field(...)] = 100) separates the schema metadata from the default value assignment, which is necessary when you want to apply constraints to required parameters or when the default value is not a Field instance. Both methods are detected by the _field_of function in needle/agent/tools.py.
Which JSON Schema keywords does the Needle 2 Field class support?
The Field constructor in Needle 2 accepts standard JSON Schema validation keywords including description, enum, ge (greater than or equal), le (less than or equal), gt (greater than), lt (less than), minLength, maxLength, pattern, min_items, and unique_items. These are applied to the schema via the apply method at lines 36-48 of needle/agent/tools.py.
How does Needle 2 determine if a parameter with Field is optional or required?
Needle 2 uses the Field.has_default() method to check whether the Field instance contains a default value. If has_default() returns True, or if the parameter has a non-Field default value, build_schema marks the parameter as optional in the generated JSON Schema. Required parameters must have no default value and must be wrapped in Annotated if they use Field for validation.
Where is the Field class defined in the Needle repository?
The Field class is defined in needle/agent/tools.py at lines 18-49. This file also contains the _field_of helper function (lines 85-92) that extracts Field instances from parameters, and the build_schema logic that assembles the final JSON Schema definition for tools decorated with @tool.
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 →