# Using Pydantic BaseModel as Tools in Needle 2: First-Class Integration

> Discover how Needle 2 seamlessly integrates Pydantic BaseModel as tools. Learn how it auto-converts models to OpenAI JSON schemas, eliminating wrapper functions for a streamlined workflow.

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

---

**Needle 2 treats Pydantic `BaseModel` classes as first-class tools, automatically converting model definitions into OpenAI-compatible JSON schemas without requiring wrapper functions or the `@tool` decorator.**

The `cactus-compute/needle` agent framework eliminates boilerplate when integrating structured data models with LLM tool-calling capabilities. By passing a Pydantic model class directly to the `Needle` constructor, the engine introspects the model's fields, types, and docstrings to generate compliant tool schemas on the fly.

## How Needle 2 Registers Pydantic BaseModel as Tools

When you include a Pydantic model in the `tools` list, Needle's internal resolution pipeline handles the conversion through three distinct phases defined in the source code.

### Model Detection via `_is_pydantic_model`

In [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) (lines 49-53), the `_is_pydantic_model` helper inspects candidate classes to determine if they inherit from Pydantic's `BaseModel`. This check enables Needle to distinguish between standard Python callables and data models that require schema extraction rather than function registration.

### Schema Generation with `pydantic_schema`

Once detected, the `pydantic_schema` function (lines 55-66 of [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py)) generates the JSON Schema representation. This process maps Pydantic field types to OpenAI-compatible parameter types, ensuring the LLM receives properly structured function definitions with accurate type constraints.

### Tool Registration and Instance Construction

The `_resolve` method in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 100-110) registers the generated schema under the model's class name. When the LLM invokes the tool, the `extract` function (lines 81-93 of [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py)) constructs an instance of the model by passing the LLM-supplied arguments directly to the Pydantic constructor.

## Practical Implementation Examples

You can integrate Pydantic models into your agent workflows using three distinct patterns supported by the Needle 2 architecture.

### Basic Model Definition

Define your data structure with field types and docstrings. According to the source implementation, the docstring becomes the tool's description, while field annotations define the parameter schema:

```python

# models.py

import pydantic

class Weather(pydantic.BaseModel):
    """Query for weather information."""
    city: str
    units: str = "metric"

```

### Agent-Based Tool Usage

Pass the model class directly to the `Needle` constructor. The framework automatically detects the Pydantic base class and registers the generated schema:

```python

# usage.py

from needle import Needle
from models import Weather

agent = Needle(tools=[Weather])
response = agent.run("What's the weather in Paris?")
print(response["results"])  # → [{'city': 'Paris', 'units': 'metric'}]

```

### One-Shot Extraction

For single-turn extraction without maintaining a persistent agent, use the `extract` function, which internally triggers the same schema generation and instantiation pipeline:

```python
from needle import extract
from models import Weather

result = extract("Give me the forecast for Tokyo", Weather)
print(result)  # → Weather(city='Tokyo', units='metric')

```

### Combining with Function Tools

Pydantic models coexist seamlessly with decorated functions in the same agent, as the `_resolve` method handles heterogeneous tool types:

```python
from needle import Needle, tool

@tool
def greet(name: str) -> str:
    """Say hello."""
    return f"Hello, {name}!"

agent = Needle(tools=[Weather, greet])

# The LLM can invoke either Weather (schema-based) or greet (function-based)

```

## Understanding Tool Schema Generation

Needle's integration leverages Pydantic's introspection capabilities to create precise tool specifications when using BaseModel as tools.

**Field Mapping:** Each model field becomes a parameter in the generated JSON schema. Type annotations translate directly to OpenAI parameter types (e.g., `str` → `string`, `int` → `integer`).

**Optional vs Required:** Fields with default values are marked as optional parameters in the schema. Fields without defaults are flagged as required arguments that the LLM must provide, as implemented in the `pydantic_schema` logic.

**Documentation Inheritance:** The model's docstring populates the tool's `description` field, while individual field descriptions (via `Field(..., description=...)`) annotate specific parameters in the resulting schema.

## Summary

- **Native Support:** Needle 2 accepts Pydantic `BaseModel` classes directly in the `tools` parameter without decorators or wrapper functions.
- **Automatic Schema Generation:** The engine detects models using `_is_pydantic_model` and generates OpenAI-compatible JSON schemas via `pydantic_schema` in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) (lines 49-66).
- **Instantiation Pipeline:** The `extract` function in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 81-93) handles model instantiation when the LLM calls the tool.
- **Flexible Integration:** Pydantic tools work alongside standard `@tool` decorated functions in mixed agent configurations.

## Frequently Asked Questions

### Can I use nested Pydantic models as tools in Needle 2?

Yes. Needle's `pydantic_schema` function recursively processes nested model definitions, converting complex object hierarchies into properly structured JSON schemas. The LLM receives the complete parameter structure, and `extract` instantiates the full nested model tree when processing the tool call.

### How does Needle handle validation when using BaseModel as tools?

When the LLM returns parameters, `extract` passes them directly to the Pydantic constructor, triggering Pydantic's native validation. If the arguments fail validation (wrong types, missing required fields), Pydantic raises a standard `ValidationError` that propagates to your application code for handling.

### What's the difference between using the `@tool` decorator and passing a BaseModel directly?

The `@tool` decorator wraps functions for LLM invocation, requiring you to manually parse arguments and return structured data. Passing a `BaseModel` class directly leverages Needle's automatic schema extraction and instantiation pipeline, eliminating the need for wrapper functions while maintaining type safety through Pydantic's validation layer.

### Can I mix Pydantic models and regular functions in the same Needle agent?

Absolutely. The `_resolve` method in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 100-110) processes heterogeneous tool lists, routing Pydantic models through the schema generation pipeline while handling decorated functions through the standard function registry. Both tool types become available to the LLM in the same conversation context.