How Pydantic Models Are Handled for Tool Schemas in Needle 2: A Complete Technical Guide
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. 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:
# 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 (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 (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 (lines 96-101), this process:
- Detects Pydantic models using the utilities from
tools.py - Converts each model to its JSON-Schema representation via
pydantic_schema - Stores the original model class in
_functionsfor later instantiation - 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 (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:
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:
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_modelby inspecting the MRO and base module inneedle/agent/tools.py. - Schema Extraction: The
pydantic_schemafunction converts models to JSON-Schema usingmodel_json_schema()and adds required metadata. - Seamless Registration: The
_resolvemethod inneedle/__init__.pyhandles 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 theextract()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 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 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →