# How to Apply Field Constraints Using `needle.Field`: A Complete Guide with Code Examples

> Learn how to apply field constraints with needle.Field in this complete guide. Automatically enrich your tool schema with JSON-Schema validation for function parameters.

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

---

**`needle.Field` is a lightweight helper class that injects JSON‑Schema validation constraints into function parameters decorated with `@needle.tool`, automatically enriching the generated tool schema.**

When building AI tools with the [Needle](https://github.com/cactus-compute/needle) framework, you often need to specify validation rules—ranges, patterns, enums, descriptions—directly on function parameters. The `needle.Field` class provides a declarative way to do this without leaving your Python code.

---

## What Is `needle.Field`?

`needle.Field` 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) and serves as a **constraint container**. When you use it as a parameter default in a `@tool`‑decorated function, Needle's schema generator detects it and merges its values into the final JSON‑Schema definition.

The class handles three core responsibilities according to the source code:

- **Storage**: Captures constraint values (`description`, `enum`, `minimum`, `maximum`, `pattern`, etc.) in `__init__`.
- **Detection**: Provides `has_default()` to determine if a parameter should be optional.
- **Merging**: Offers `apply(schema)` to inject constraints into a schema dictionary.

---

## How Field Constraints Work in Practice

Needle's tool generation pipeline follows this flow, as implemented in [`build_schema`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L11-L43):

1. Function signature inspection extracts parameter names and type hints.
2. Each type hint is converted to a base JSON‑Schema type (`_json_type`).
3. If a parameter default is a `Field` instance (detected via `_field_of`), `field.apply(schema)` is called.
4. The `apply` method iterates over supported constraint keys and adds any non‑`None` values to the schema.

This design keeps your validation logic **co‑located with your function definition** rather than scattered in separate schema files.

---

## Supported Field Constraints

Based on the `Field.apply` implementation 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#L36-L49), you can specify:

| Constraint | JSON‑Schema Keyword | Description |
|------------|-------------------|-------------|
| `description` | `description` | Human‑readable parameter explanation |
| `enum` | `enum` | List of allowed values (converted from iterable) |
| `const` | `const` | Single fixed value (when not `_MISSING`) |
| `ge` / `le` | `minimum` / `maximum` | Numeric inclusive bounds |
| `gt` / `lt` | `exclusiveMinimum` / `exclusiveMaximum` | Numeric exclusive bounds |
| `pattern` | `pattern` | Regular expression for string validation |
| `default` | `default` | Default value used when parameter is omitted |

---

## Code Example: Numeric Constraints with `ge` and `le`

Here's how to apply range validation to a numeric parameter:

```python
from needle.agent.tools import Field, tool

@tool
def add(
    a: int,
    b: int = Field(
        default=0,
        description="Second addend",
        ge=0,      # minimum: 0

        le=100,    # maximum: 100

    ),
) -> int:
    """Returns the sum of two integers."""
    return a + b

```

The generated JSON‑Schema includes the constraints:

```json
{
  "name": "add",
  "parameters": {
    "type": "object",
    "properties": {
      "a": { "type": "integer" },
      "b": {
        "type": "integer",
        "description": "Second addend",
        "minimum": 0,
        "maximum": 100,
        "default": 0
      }
    },
    "required": ["a"]
  }
}

```

Note that `a` appears in `required` because it has no `Field` default, while `b` is optional.

---

## Code Example: String Constraints with `enum` and `pattern`

For enumerated string values, combine `enum` with `pattern` validation:

```python
from needle.agent.tools import Field, tool

@tool
def set_mode(
    mode: str = Field(
        default="auto",
        enum=["auto", "manual", "off"],
        pattern="^(auto|manual|off)$",
        description="Operating mode of the device"
    )
) -> None:
    """Sets the device mode."""
    pass

```

Resulting schema output:

```json
{
  "name": "set_mode",
  "parameters": {
    "type": "object",
    "properties": {
      "mode": {
        "type": "string",
        "description": "Operating mode of the device",
        "enum": ["auto", "manual", "off"],
        "pattern": "^(auto|manual|off)$",
        "default": "auto"
      }
    },
    "required": []
  }
}

```

The `enum` constraint is automatically converted to a list, and `pattern` provides regex‑based validation for additional safety.

---

## Integration with `typing.Annotated`

Needle also supports attaching `Field` constraints via `typing.Annotated`, allowing you to preserve non‑`Field` default values. The `build_schema` function checks for `Field` instances through `_field_of`, which handles both default‑value and `Annotated` attachment.

```python
from typing import Annotated
from needle.agent.tools import Field, tool

@tool
def configure(
    timeout: Annotated[int, Field(ge=1, le=300, description="Timeout in seconds")] = 30
) -> None:
    """Configure connection settings."""
    pass

```

This pattern separates the **runtime default** (`30`) from the **schema constraints** (`Field`).

---

## Key Source Files and Methods

| Component | Location | Purpose |
|-----------|----------|---------|
| `Field` class | [`needle/agent/tools.py#L18`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L18) | Constraint storage and `apply` logic |
| `Field.__init__` | [`needle/agent/tools.py#L18-L22`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L18-L22) | Parameter initialization |
| `Field.apply` | [`needle/agent/tools.py#L36-L49`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L36-L49) | Schema enrichment implementation |
| `build_schema` | [`needle/agent/tools.py#L11-L43`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L11-L43) | Core schema generation function |
| `tool` decorator | [`needle/agent/tools.py#L64-L67`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L64-L67) | Attaches schema via `_needle_tool` |

---

## Summary

- `needle.Field` provides **declarative JSON‑Schema constraints** directly in Python function signatures.
- Constraints are merged during `build_schema` via the `Field.apply` method, supporting `description`, `enum`, `minimum`, `maximum`, `pattern`, and more.
- Parameters with `Field` defaults become **optional**; those without remain **required**.
- Both **default‑value** and **`Annotated`** attachment patterns are supported for flexibility.

---

## Frequently Asked Questions

### How does Needle detect which parameters are required?

Required parameters are determined by `Field.has_default()`. If a parameter's default is a `Field` instance with an explicit `default` value (or any default at all), the parameter is considered optional and excluded from the `required` array in the JSON‑Schema. Parameters without any default value are marked required.

### Can I use `needle.Field` without a default value?

Yes, through `typing.Annotated`. This lets you attach constraints while keeping a separate runtime default, or even requiring the parameter. The `build_schema` function 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) extracts `Field` instances from `Annotated` metadata via the `_field_of` helper.

### What happens if I specify both `ge` and `gt` on the same field?

Both translate to JSON‑Schema keywords (`minimum` for `ge`, `exclusiveMinimum` for `gt`). The source code's `apply` method simply adds whichever values are non‑`None`, so specifying both creates a schema with both constraints. Ensure your values are logically consistent—JSON‑Schema validators will enforce both rules.

### Is `pattern` validated at runtime or only in the schema?

The `pattern` constraint is emitted into the JSON‑Schema only. Needle does not perform runtime validation of parameter values against the pattern; the receiving system (typically an LLM or downstream API) is responsible for enforcing regex constraints based on the schema.