# Validation Constraints for Tool Function Arguments in Needle: A Complete Guide

> Discover validation constraints for tool function arguments in Needle. Learn how explicit Field configurations and Python type hints enforce rules from numeric ranges to Pydantic models.

- Repository: [Cactus Compute, Inc./needle](https://github.com/cactus-compute/needle)
- Tags: how-to-guide
- Published: 2026-08-22

---

**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`](https://github.com/cactus-compute/needle/blob/main/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`](https://github.com/cactus-compute/needle/blob/main/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`](https://github.com/cactus-compute/needle/blob/main/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`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py), Literal types generate a JSON Schema `enum` array containing the specified literal options.

```python
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.

```python
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.

```python
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.

```python
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"

```

```python

# 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"

```

```python

# 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`](https://github.com/cactus-compute/needle/blob/main/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`](https://github.com/cactus-compute/needle/blob/main/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`](https://github.com/cactus-compute/needle/blob/main/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).