# Needle Field Constraints: A Complete Guide to Per-Argument Validation in `needle.Field`

> Explore per-argument constraints in Needle using needle.Field. Learn about default, required, type, choices, numeric bounds, length bounds, regex, nullable, and description for robust validation.

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

---

**`needle.Field` supports per-argument constraints including `default`, `required`, `type`, `choices`, numeric bounds (`gt`, `ge`, `lt`, `le`), length bounds (`min_length`, `max_length`), `regex`, `nullable`, and `description`.**

The `needle.Field` class in the Cactus Compute **Needle** framework provides declarative validation for tool arguments. When you decorate a function with `@tools.tool`, each parameter annotated with `Field(...)` inherits automatic runtime validation powered by Pydantic. This article catalogs every available constraint based on the implementation in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py).

---

## Core Constraint Categories

### Value Presence and Defaults

| Constraint | Effect | Default |
|------------|--------|---------|
| `required` | Argument must be provided explicitly | `True` |
| `default` | Value used when argument is omitted | `None` |
| `nullable` | Whether `None` is an acceptable value | `False` |

Use `required=False` with `default` to create optional parameters:

```python
from needle.agent import tools

@tools.tool
def fetch_data(
    limit: tools.Field(default=100, required=False, nullable=False)
):
    """Return up to `limit` records."""
    ...

```

### Type Enforcement

The `type` constraint accepts any Python type or `Union` of types. Validation occurs during Pydantic model construction:

```python
@tools.tool
def calculate(
    value: tools.Field(type=float),
    count: tools.Field(type=int)
):
    """Process a float value `count` times."""
    ...

```

### Choice-Based Validation

Restrict inputs to an explicit allowlist with `choices`:

```python
@tools.tool
def set_environment(
    env: tools.Field(choices=["dev", "staging", "production"])
):
    """Select deployment environment."""
    ...

```

Violation raises `ValidationError` with a clear message listing permitted values.

---

## Numeric Bounds

Needle provides four comparison operators for number ranges:

| Constraint | Validates |
|------------|-----------|
| `gt` | Greater than |
| `ge` | Greater than or equal |
| `lt` | Less than |
| `le` | Less than or equal |

Combine multiple constraints for closed or half-open intervals:

```python
@tools.tool
def set_brightness(
    level: tools.Field(type=int, ge=0, le=100)
):
    """Set screen brightness percentage."""
    ...

```

```python
@tools.tool
def configure_timeout(
    seconds: tools.Field(type=float, gt=0.0, lt=3600.0)
):
    """Set a timeout between 0 and 1 hour (exclusive)."""
    ...

```

---

## String and Sequence Constraints

### Length Bounds

Apply `min_length` and `max_length` to any sized collection:

```python
@tools.tool
def create_passcode(
    code: tools.Field(min_length=6, max_length=6)
):
    """Require exactly 6 characters."""
    ...

```

### Regex Pattern Matching

The `regex` constraint accepts any valid Python regular expression. The match must cover the entire string (implicit `^...$` anchoring):

```python
@tools.tool
def register_device(
    serial: tools.Field(regex=r"[A-Z]{2}-\d{6}")
):
    """Validate serial format like 'AB-123456'."""
    ...

```

---

## Documentation and Metadata

The `description` constraint populates generated OpenAPI schemas and tool documentation:

```python
@tools.tool
def search(
    query: tools.Field(description="Search terms for the document index"),
    max_results: tools.Field(
        type=int,
        default=10,
        ge=1,
        le=100,
        description="Maximum number of results to return"
    )
):
    """Perform semantic search across indexed documents."""
    ...

```

---

## Complete Parameter Example

Combine multiple constraints for robust validation in a single field:

```python
from needle.agent import tools
from typing import Optional

@tools.tool
def send_notification(
    recipient: tools.Field(
        regex=r"^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$",
        description="Valid email address"
    ),
    subject: tools.Field(
        min_length=1,
        max_length=200,
        description="Notification subject line"
    ),
    priority: tools.Field(
        default="normal",
        choices=["low", "normal", "high", "urgent"],
        description="Message priority level"
    ),
    retry_count: tools.Field(
        type=int,
        default=3,
        ge=0,
        le=5,
        nullable=False
    ),
    metadata: tools.Field(
        type=Optional[dict],
        default=None,
        nullable=True,
        required=False
    )
):
    """Send a prioritized notification with validation on all fields."""
    ...

```

---

## Implementation Details

According to the **cactus-compute/needle** source code, `needle.Field` is implemented 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). The class constructs a Pydantic `FieldInfo` object internally, which means:

- All constraints map directly to Pydantic field parameters
- Validation errors use Pydantic's standard `ValidationError` format
- Type coercion follows Pydantic's strict/lenient mode settings

The `tool` decorator (exposed via [`needle/agent/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/__init__.py)) inspects function signatures at decoration time and builds a Pydantic model from `Field` annotations, enabling runtime validation without boilerplate.

---

## Summary

- **`needle.Field`** accepts **11 distinct constraint parameters**: `default`, `required`, `type`, `choices`, `gt`, `ge`, `lt`, `le`, `min_length`, `max_length`, `regex`, `nullable`, and `description`
- Constraints compose arbitrarily: combine `type` with bounds, `regex` with `choices`, or any valid combination
- Validation executes **before** tool logic runs, guaranteeing clean inputs
- Implementation leverages **Pydantic**, ensuring ecosystem compatibility and familiar error messages

---

## Frequently Asked Questions

### What happens if multiple constraints conflict in `needle.Field`?

Pydantic evaluates all constraints simultaneously. A value must satisfy every specified constraint. For example, `Field(type=int, ge=0, le=10, choices=[2, 4, 6])` requires an even integer between 0 and 10. Conflicting constraints (like `gt=10` with `lt=5`) create an impossible validation that rejects all inputs.

### Does `needle.Field` support custom validation functions?

The base `Field` class in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) does not expose a `validator` parameter for arbitrary callables. For complex validation, annotate with Pydantic models directly or wrap the tool function and raise `ValueError` manually after the standard constraints pass.

### How does `nullable=True` interact with `default=None`?

Both settings allow `None` values, but serve different purposes. `nullable=True` permits explicit `None` passed by the caller. `default=None` supplies `None` when the argument is omitted. Use `Field(default=None, nullable=False)` to treat "not provided" as `None` while rejecting explicit `None` values.