Needle.Field Constraint Types: Complete Guide to JSON-Schema Validation in Python

needle.Field supports 15+ constraint types including numeric bounds (ge, le, gt, lt), string validators (min_length, max_length, pattern, format), array constraints (min_items, max_items, unique_items), and value restrictions (enum, const, multiple_of).

The Field class in cactus-compute/needle provides a lightweight, declarative way to attach JSON-Schema validation rules to function parameters when building AI tool interfaces. By mapping Python keyword arguments directly to schema keywords, it enables precise runtime validation without manual schema construction.

Needle.Field Constraint Types Explained

needle.Field is defined in needle/agent/tools.py and accepts constraint parameters that translate 1:1 with JSON-Schema validation keywords. The apply method (lines 35-48) handles the conversion from Python arguments to schema-compatible dictionaries.

Value and Metadata Constraints

These constraints control default values and basic documentation:

  • default — Fallback value used when the parameter is not supplied.
  • description — Human-readable explanation of the field's purpose.
  • enum — Restricts input to a predefined set of literal values.
  • const — Enforces a single constant value the field must equal.

These map to default, description, enum, and const JSON-Schema keywords respectively.

Numeric Range Constraints

For int and float parameters, needle.Field supports four boundary types (lines 19-21):

Constraint JSON-Schema Key Meaning
ge minimum Inclusive minimum (≥)
le maximum Inclusive maximum (≤)
gt exclusiveMinimum Exclusive minimum (>)
lt exclusiveMaximum Exclusive maximum (<)
multiple_of multipleOf Value must be divisible by this number

String Validation Constraints

String parameters accept length and pattern controls (lines 28-30):

  • min_length / max_length — Inclusive character count bounds.
  • pattern — Regular expression the string must match.
  • format — Semantic format hint (e.g., "email", "date-time", "uri").

Array Constraints

For list parameters, needle.Field provides collection-level validation:

  • min_items / max_items — Bounds on array element count.
  • unique_items — Boolean requiring all array elements to be distinct.

Practical Examples

Basic Numeric Constraints with Defaults

from needle import tool, Field

@tool
def generate(
    temperature: float = Field(default=0.7, ge=0, le=1, description="Sampling temperature"),
    max_tokens: int = Field(default=256, ge=1, le=1024),
    top_p: float = Field(default=1.0, gt=0, le=1)
):
    """Generate text with controlled randomness."""
    return model.generate(temperature=temperature, max_tokens=max_tokens)

This example demonstrates inclusive bounds (ge, le) for closed ranges and exclusive bounds (gt) for open lower limits. The description parameter feeds directly into tool documentation.

String Pattern and Length Validation

from needle import tool, Field
import re

@tool
def extract_email(
    query: str = Field(
        min_length=5,
        max_length=1000,
        pattern=r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
        format="email",
        description="User email address to verify"
    )
):
    """Extract and validate an email from input."""
    return {"email": query.lower().strip()}

The pattern constraint accepts raw regex strings. The format keyword provides semantic hints to LLM callers without enforcing runtime validation.

Enumeration with typing.Annotated

When you cannot use Field as a default value (e.g., for required parameters), combine it with typing.Annotated:

from typing import Annotated
from needle import tool, Field

@tool
def classify_sentiment(
    text: str,
    label: Annotated[
        str,
        Field(enum=["positive", "negative", "neutral"], description="Sentiment category")
    ],
    confidence: Annotated[
        float,
        Field(ge=0, le=1, multiple_of=0.01, description="Confidence score rounded to 2 decimals")
    ]
):
    """Classify text sentiment with constrained output."""
    return {"label": label, "confidence": round(confidence, 2)}

Here enum restricts label to three allowed strings, while multiple_of enforces centesimal precision on the confidence score.

Array Validation

from typing import List
from needle import tool, Field

@tool
def tag_document(
    doc_id: str,
    tags: Field(
        default_factory=list,
        min_items=1,
        max_items=10,
        unique_items=True,
        description="Unique topic tags for the document"
    )
):
    """Apply 1-10 unique tags to a document."""
    return {"doc_id": doc_id, "tags": list(set(tags))}

Note: default_factory allows mutable defaults. The unique_items constraint maps to JSON-Schema's uniqueItems boolean.

How Constraints Map to JSON-Schema

The apply method in needle/agent/tools.py (lines 35-48) performs the translation. Key mappings include:

Python Parameter JSON-Schema Output
ge=0 "minimum": 0
gt=0 "exclusiveMinimum": 0
le=1 "maximum": 1
lt=1 "exclusiveMaximum": 1
multiple_of=5 "multipleOf": 5
min_length=10 "minLength": 10
pattern=r"^\d+$" "pattern": "^\\d+$"
format="date" "format": "date"
enum=["a", "b"] "enum": ["a", "b"]
const="fixed" "const": "fixed"

This translation ensures compatibility with OpenAI function calling and other JSON-Schema-based tool specifications.

Combining Multiple Constraints

needle.Field constraints are composable. You can apply numeric, string, and metadata constraints simultaneously where logically consistent:

from needle import tool, Field

@tool
def configure_api(
    endpoint: Field(
        default="https://api.example.com",
        min_length=10,
        max_length=2048,
        pattern=r"^https?://",
        format="uri",
        description="HTTPS endpoint URL"
    ),
    retries: Field(default=3, ge=0, le=10, multiple_of=1),
    timeout_ms: Field(default=5000, ge=100, le=60000, multiple_of=100)
):
    """Configure API connection parameters."""
    pass

Summary

  • needle.Field supports 15 constraint types spanning numeric bounds, string validation, array restrictions, and value controls.
  • Constraints map directly to JSON-Schema keywords via the apply method in needle/agent/tools.py.
  • Use default for optional parameters or typing.Annotated for required parameters with constraints.
  • Numeric bounds use ge/le (inclusive) and gt/lt (exclusive) following Python's functools naming convention.
  • String constraints include length bounds, regex pattern, and semantic format hints.

Frequently Asked Questions

What is the difference between ge/le and gt/lt in needle.Field?

ge (greater than or equal) and le (less than or equal) set inclusive bounds that include the endpoint values. gt (greater than) and lt (less than) set exclusive bounds that exclude the endpoint. For example, Field(ge=0, le=1) allows 0.0 and 1.0, while Field(gt=0, lt=1) allows only values strictly between them.

Can I use needle.Field without a default value?

Yes. Use typing.Annotated to attach Field constraints to required parameters: param: Annotated[type, Field(ge=0)]. This pattern is necessary when you want validation rules but no default, or when the parameter has no natural default value.

What JSON-Schema format values does needle.Field accept?

The format parameter accepts any string recognized by JSON-Schema including "date-time", "date", "time", "email", "hostname", "ipv4", "ipv6", "uri", and "uuid". Format validation behavior depends on the consuming schema validator—Needle passes the value through to the generated schema without enforcement.

Does needle.Field validate values at runtime?

needle.Field itself only describes constraints for schema generation. Actual runtime validation depends on the tool execution environment consuming the schema. The Needle framework uses these schemas for LLM tool binding; strict runtime validation requires additional enforcement by the caller or a JSON-Schema validator.

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 →