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 values
  • gt / lt: Exclusive minimum and maximum boundaries
  • multiple_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 boundaries
  • pattern: Regular expression that the string must match
  • format: JSON Schema format hints such as date-time, email, or uri

Array and Collection Constraints

When validating list or array types, the following constraints apply:

  • min_items / max_items: Boundaries on the number of elements
  • unique_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 enums
  • const: Single constant value the argument must equal
  • default: Supplies a default value; when present, the argument becomes optional in the schema
  • description: 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 Field constraints in needle/agent/tools.py support 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.Literal automatically generates JSON Schema enum constraints at lines 71-73, while typing.Annotated enables mixing types with Field instances.
  • 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 out None values and handling the _MISSING sentinel 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →