How to Integrate Pydantic Models for Structured Extraction with Needle
Needle automatically converts Pydantic BaseModel type hints into JSON Schema definitions that LLMs use to return structured data, enabling type-safe extraction without manual schema engineering.
When you integrate Pydantic models for structured extraction with Needle, you eliminate the need to manually write JSON schemas for LLM function calling. The framework introspects your Python type annotations at runtime, generates compliant schemas, and handles deserialization automatically. This gives you both the flexibility of natural language interfaces and the safety of Pydantic validation.
Detecting Pydantic Models via _is_pydantic_model
Needle identifies Pydantic models through the _is_pydantic_model utility in needle/agent/tools.py. This function inspects a class's Method Resolution Order (MRO) to verify inheritance from pydantic.BaseModel.
According to the source code, the detection mechanism walks the class hierarchy and checks the module name to confirm the object is a genuine Pydantic model rather than a similar-looking class. This check triggers the schema conversion pipeline whenever Needle encounters a type hint that inherits from BaseModel.
Converting Models to JSON Schema with pydantic_schema
Once Needle detects a Pydantic model, it invokes the pydantic_schema function (located at needle/agent/tools.py) to extract the schema. This utility calls model.model_json_schema() for Pydantic v2 compatibility, falling back to the legacy model.schema() method for older versions.
The function then reshapes the raw Pydantic schema into Needle's tool format, extracting the name, parameters, and optional description fields. This transformation ensures the output conforms to the JSON Schema specification required by LLM providers while preserving all validation constraints defined in your Pydantic model.
Integrating Schemas into Tool Definitions
The build_schema function coordinates the final integration. While walking a function's type hints via _json_type, Needle delegates Pydantic model processing to pydantic_schema. The resulting schema becomes part of the tool's parameters object.
When you decorate a function with @tool, Needle stores the complete generated schema on the function as the _needle_tool attribute. At runtime, this metadata is exposed to the LLM as a standard tool definition:
{
"name": "my_tool",
"description": "...",
"parameters": {
"type": "object",
"properties": {
"payload": { "type": "object", "properties": { ... } }
},
"required": ["payload"]
}
}
Complete Implementation Example
This example demonstrates the full pipeline from model definition to structured extraction:
from pydantic import BaseModel, Field
from needle import tool, Needle
# 1️⃣ Define the extraction schema
class WeatherQuery(BaseModel):
"""Ask for the weather in a specific city."""
city: str = Field(..., description="Name of the city")
units: str = Field("metric", description="Metric or imperial")
# 2️⃣ Register the tool with Needle
@tool
def get_weather(request: WeatherQuery) -> str:
"""Fetch weather info."""
return f"Weather for {request.city} ({request.units}) is 23°C"
# 3️⃣ Execute through Needle's inference loop
needle = Needle()
response = needle.run("What's the weather in Berlin?")
print(response) # → "Weather for Berlin (metric) is 23°C"
Under the hood, build_schema detects the WeatherQuery annotation and calls pydantic_schema to generate the JSON schema. When the LLM returns a JSON object matching that schema, Needle deserializes it into a WeatherQuery instance before passing it to your function.
Key Source Files
The Pydantic integration spans three critical locations in the repository:
needle/agent/tools.py– Contains_is_pydantic_model,pydantic_schema, andbuild_schema, which form the complete conversion pipeline.needle/__init__.py– Re-exportstoolandFieldso users can import them directly viafrom needle import tool, Field.tests/test_tools.py– Houses the test suite includingtest_pydantic_model_schema, which validates the schema generation logic against reference Pydantic models.
Summary
- Automatic Detection: Needle's
_is_pydantic_modelfunction inneedle/agent/tools.pyidentifies Pydantic BaseModel subclasses by inspecting the class MRO. - Schema Generation: The
pydantic_schemautility extracts JSON schemas using Pydantic's nativemodel_json_schema()orschema()methods. - Runtime Binding: Tool schemas are stored in the
_needle_toolfunction attribute, making them available to the LLM at inference time. - Type Safety: Incoming LLM responses are automatically parsed into Pydantic model instances before your function executes, providing validation and IDE support.
Frequently Asked Questions
How does Needle determine if a type hint is a Pydantic model?
Needle uses the _is_pydantic_model function located in needle/agent/tools.py. This utility walks the class's Method Resolution Order (MRO) and checks if any parent class originates from the pydantic module, confirming legitimate BaseModel inheritance.
Which Pydantic schema generation methods does Needle support?
The pydantic_schema function in needle/agent/tools.py attempts to call model.model_json_schema() first for Pydantic v2 compatibility, then falls back to model.schema() for legacy v1 installations. This dual support ensures broad version compatibility.
Where does Needle store the generated tool schema?
When you apply the @tool decorator, Needle attaches the complete JSON schema to your function as the _needle_tool attribute. This metadata dictionary includes the tool name, description, and parameters derived from your Pydantic models.
Can I use Pydantic Field descriptions in my extraction schemas?
Yes. When pydantic_schema processes your model, it preserves Field-level metadata including descriptions and constraints. These become part of the JSON Schema's description fields and validation rules, helping the LLM understand the expected data format.
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 →