# How to Constrain Tool Argument Values Using `needle.Field` in the Needle Framework

> Learn how to constrain tool argument values in the Needle framework using needle.Field. Attach validation constraints like numeric ranges and string patterns to your AI tool schemas.

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

---

**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](https://github.com/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)](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 like `ge`, `le`, `pattern`, and `min_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:

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

```json
{
  "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`:

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

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

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

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

```python
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.Field`** provides declarative argument constraints that automatically translate to JSON-Schema
- Attach `Field` as **default values** or wrap with **`typing.Annotated`** when defaults aren't suitable
- The **`_field_of`** helper and **`Field.apply`** methods in [[`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/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)](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)](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)](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) confirm the public API surface for `tool` and `Field`.