# How Pydantic Models Are Handled for Tool Schemas in Needle 2: A Complete Technical Guide

> Learn how Needle 2 automatically converts Pydantic models to JSON-Schema tool definitions. Ensure type safety and robust deserialization for LLM invocations with this technical guide.

- Repository: [Cactus Compute, Inc./needle](https://github.com/cactus-compute/needle)
- Tags: deep-dive
- Published: 2026-08-24

---

**Needle 2 automatically converts Pydantic models into JSON-Schema tool definitions by detecting model classes, extracting their schemas, and registering them for LLM invocation with full type safety and deserialization.**

When building LLM-powered agents with **Needle 2**, you can pass Pydantic models directly as tools without manual schema configuration. The framework treats these models as first-class tool definitions, handling the entire lifecycle from schema generation to runtime deserialization. This seamless integration leverages Pydantic's validation capabilities while ensuring the underlying LLM receives properly formatted JSON-Schema specifications.

## Detection and Schema Extraction

The foundation of Pydantic tool support begins with detection utilities located in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py). Needle 2 identifies eligible models through method resolution order (MRO) inspection before extracting their JSON representation.

### Model Detection via `_is_pydantic_model`

Before processing any object as a tool, Needle verifies it is a valid Pydantic model. The `_is_pydantic_model` function inspects the class hierarchy to confirm the object inherits from `pydantic.BaseModel`:

```python

# From needle/agent/tools.py (lines 49-52)

def _is_pydantic_model(obj):
    return (
        isinstance(obj, type) 
        and issubclass(obj, BaseModel)
        and obj.__module__.startswith('pydantic')
    )

```

This check ensures only genuine Pydantic models proceed to schema extraction, preventing false positives from similarly named classes.

### Schema Generation with `pydantic_schema`

Once validated, the model undergoes schema transformation through the `pydantic_schema` function. This utility calls the model's native `model_json_schema()` method (falling back to the legacy `schema()` for older Pydantic versions) and reformats the output to match Needle's internal tool specification:

- **Name**: Derived from the class name
- **Parameters**: The JSON-Schema properties
- **Description**: Extracted from the model's docstring

According to the source code in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) (lines 55-66), this function adds the required metadata wrapper that the native library expects for function calling capabilities.

## Schema Integration and Tool Registration

After extraction, Pydantic schemas integrate into Needle's tool-building pipeline through recursive type resolution and agent initialization.

### Type Resolution in `_json_type`

When processing function signatures, `_json_type` delegates Pydantic annotations to `pydantic_schema`. Located in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) (lines 70-72), this delegation occurs whenever a function parameter is annotated with a Pydantic model class, ensuring consistent schema generation across both standalone models and model-typed function arguments.

### Agent Initialization via `_resolve`

The `Needle.__init__` method orchestrates tool registration by calling `_resolve` for each item in the `tools` list. As implemented in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 96-101), this process:

1. Detects Pydantic models using the utilities from [`tools.py`](https://github.com/cactus-compute/needle/blob/main/tools.py)
2. Converts each model to its JSON-Schema representation via `pydantic_schema`
3. Stores the original model class in `_functions` for later instantiation
4. Appends the generated schema to the tool definitions sent to the native library

This registration makes the model available for LLM invocation while preserving the class for runtime validation.

## Runtime Execution and Deserialization

When the LLM emits a `function_call`, Needle executes the corresponding Pydantic model and handles result reconstruction.

### Function Lookup and Instantiation

During agent execution, Needle retrieves the original model from the `_functions` registry. As shown in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 136-142), the framework reconstructs a Pydantic instance from the LLM's returned arguments, applying Pydantic's validation to ensure type safety. The result is either a fully instantiated model object or a plain dictionary, depending on the execution context.

This deserialization ensures that subsequent pipeline steps receive Python objects with proper typing rather than raw JSON dictionaries.

## Practical Implementation Examples

You can register Pydantic models as tools using two primary patterns: direct tool registration for conversational agents or one-shot extraction for data parsing.

### Registering Models as Agent Tools

Pass Pydantic models directly to the `Needle` constructor without additional decorators:

```python
from pydantic import BaseModel, Field as PydanticField
from needle import Needle

class WeatherQuery(BaseModel):
    """Ask for the current weather in a city."""
    city: str = PydanticField(..., description="Name of the city")
    units: str = PydanticField("metric", description="Metric or imperial")

# Register the model as a tool

agent = Needle(tools=[WeatherQuery])

response = agent.run(
    "What is the temperature in Paris right now?",
    max_steps=2,
)

# Access the validated result

print(response["results"][0])  # WeatherQuery(city='Paris', units='metric')

```

### One-Shot Data Extraction

Use the `extract` helper for structured data extraction without managing conversational state:

```python
from needle import extract

text = "John Doe lives in Berlin and is 29 years old."

class Person(BaseModel):
    name: str
    city: str
    age: int

person = extract(text, Person)

# Returns: Person(name='John Doe', city='Berlin', age=29)

```

## Summary

- **Automatic Detection**: Needle 2 identifies Pydantic models via `_is_pydantic_model` by inspecting the MRO and base module in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py).
- **Schema Extraction**: The `pydantic_schema` function converts models to JSON-Schema using `model_json_schema()` and adds required metadata.
- **Seamless Registration**: The `_resolve` method in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) handles conversion and storage, making models available for LLM function calling.
- **Type-Safe Runtime**: Arguments returned from the LLM are automatically deserialized back into Pydantic instances with full validation.
- **Dual Usage Patterns**: Models work both as conversational tools via `Needle(tools=[...])` and as extractors via the `extract()` utility.

## Frequently Asked Questions

### How does Needle 2 detect if a class is a valid Pydantic model?

Needle 2 uses the `_is_pydantic_model` function in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) to check if an object is a subclass of `pydantic.BaseModel` and verify that its module starts with 'pydantic'. This prevents non-Pydantic classes with similar names from being processed as tools.

### What happens when a Pydantic model is passed to the Needle constructor?

When you pass a Pydantic model to `Needle(tools=[...])`, the `_resolve` method detects it, calls `pydantic_schema` to generate the JSON-Schema, stores the model in `_functions` for lookup, and adds the schema to the tool definitions sent to the LLM.

### Can I use Pydantic models for one-time data extraction?

Yes. The `extract()` function accepts a Pydantic model class and automatically manages the tool registration, LLM invocation, and result deserialization, returning a validated Pydantic instance without requiring manual agent setup.

### Does Needle support both Pydantic v1 and v2?

Yes. The `pydantic_schema` function in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) attempts to call `model_json_schema()` first, then falls back to the legacy `schema()` method, ensuring compatibility across Pydantic versions while generating the required JSON-Schema output.