# Can Pydantic Models Be Used Directly as Extraction Schemas in Needle?

> Yes Needle uses Pydantic models as extraction schemas automatically converting them to OpenAI-compatible JSON schemas for LLM tool extraction.

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

---

**Yes, Needle treats Pydantic models as first-class extraction schemas, automatically converting them to OpenAI-compatible JSON schemas for LLM tool extraction.**

The `cactus-compute/needle` library eliminates manual schema writing by detecting `pydantic.BaseModel` subclasses and generating proper extraction schemas at runtime. This native integration means you can define structured data requirements using Pydantic's familiar syntax, then pass models directly as function type hints for automatic schema generation.

## How Needle Detects Pydantic Models

Needle's schema building pipeline includes explicit Pydantic detection in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py). The `_is_pydantic_model` function (lines 45-48) identifies when a parameter annotation inherits from `pydantic.BaseModel`:

```python

# From needle/agent/tools.py (lines 45-48)

def _is_pydantic_model(annotation) -> bool:
    """Check if an annotation is a Pydantic model."""
    return isinstance(annotation, type) and issubclass(annotation, pydantic.BaseModel)

```

Once detected, the model conversion flows through `pydantic_schema` (lines 51-61), which extracts field definitions, types, defaults, and descriptions into standard JSON Schema format.

## Complete Working Example

Here's how to use a Pydantic model directly as an extraction schema in a Needle tool:

```python

# example.py

import pydantic
from needle import tool

# Step 1: Define your data model

class Weather(pydantic.BaseModel):
    """Current weather conditions."""
    location: str              # Required field

    temperature_c: float      # Required numeric field

    description: str = "clear"  # Optional with default

# Step 2: Use the model as a direct type hint

@tool
def get_weather(info: Weather) -> str:
    """Retrieve a weather report based on the provided info."""
    return f"The weather in {info.location} is {info.temperature_c}°C and {info.description}."

# Step 3: Needle generates the extraction schema automatically

```

The resulting schema from `build_schema(get_weather)` produces:

```json
{
    "name": "get_weather",
    "description": "Retrieve a weather report based on the provided info.",
    "parameters": {
        "type": "object",
        "properties": {
            "info": {
                "type": "object",
                "properties": {
                    "location": {"type": "string"},
                    "temperature_c": {"type": "number"},
                    "description": {"type": "string", "default": "clear"}
                },
                "required": ["location", "temperature_c"]
            }
        },
        "required": ["info"]
    }
}

```

## Schema Generation Pipeline

The conversion happens in three stages inside [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py):

1. **`build_schema` (lines 11-42)** — Entry point that processes function signatures and docstrings
2. **`_json_type` (lines 68-70)** — Dispatches to `pydantic_schema` when `_is_pydantic_model` returns `True`
3. **`pydantic_schema` (lines 51-61)** — Converts Pydantic fields to JSON Schema properties, handling:
   - Type mappings (str→string, float→number, etc.)
   - Required vs. optional fields
   - Default values
   - Field descriptions from docstrings

## Nested Models and Complex Structures

Pydantic models with nested structures work automatically. Needle recursively processes compound types:

```python
from typing import List
from needle import tool
import pydantic

class Address(pydantic.BaseModel):
    street: str
    city: str

class Person(pydantic.BaseModel):
    name: str
    addresses: List[Address]  # Nested model in a list

@tool
def register_person(data: Person) -> str:
    """Register a person with their addresses."""
    return f"Registered {data.name} with {len(data.addresses)} addresses"

```

The generated schema includes the full nested `Address` definition under `data.properties.addresses.items`.

## Validation and Testing

The Needle repository includes comprehensive tests confirming Pydantic schema functionality:

- **[`tests/test_tools.py`](https://github.com/cactus-compute/needle/blob/main/tests/test_tools.py)** — Unit tests for `build_schema` with various Pydantic model configurations
- **[`tests/test_inference.py`](https://github.com/cactus-compute/needle/blob/main/tests/test_inference.py)** — End-to-end tests demonstrating LLM extraction into Pydantic instances

## Summary

- **Direct type hint usage** — Annotate function parameters with `pydantic.BaseModel` subclasses
- **Automatic detection** — Needle's `_is_pydantic_model` identifies Pydantic classes at runtime
- **Full schema generation** — `pydantic_schema` converts models to OpenAI-compatible JSON Schema
- **Nested support** — Complex models with nested structures and collections work without additional configuration

## Frequently Asked Questions

### Does Needle support Pydantic v2 models?

Yes, Needle's `pydantic_schema` function (lines 51-61 in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py)) uses Pydantic's core schema generation APIs that work with both Pydantic v1 and v2. The validation logic in [`tests/test_tools.py`](https://github.com/cactus-compute/needle/blob/main/tests/test_tools.py) confirms compatibility across versions.

### What happens if a Pydantic model has validation rules?

Needle extracts the type structure and field metadata, but **does not** embed Pydantic validation rules (like `Field(gt=0)`) into the JSON Schema sent to the LLM. The returned data is validated when Pydantic instantiates the model, so invalid values raise `pydantic.ValidationError` at runtime.

### Can I use standard Python types instead of Pydantic?

Yes. Needle's `_json_type` handles both standard types (str, int, float, bool, list, dict) and Pydantic models. Standard types map directly to JSON Schema primitives, while Pydantic models trigger the full conversion pipeline for structured data extraction.