How to Perform One-Shot Extraction with Needle 2 Using Pydantic

Needle 2 provides an extract() convenience wrapper that treats a Pydantic model as the sole tool for a single inference pass, automatically converting the schema to JSON and returning an instantiated Python object.

The cactus-compute/needle repository offers a lightweight engine for structured data extraction from unstructured text. When you need to parse free-form content into typed Python objects, performing one-shot extraction with Needle 2 using Pydantic provides a fast, memory-efficient solution that leverages grammars to guide token generation.

Understanding the extract() API

The extract() function in needle/__init__.py serves as the primary entry point for one-shot structured extraction. When you pass raw text alongside a Pydantic BaseModel, the function creates a temporary Needle agent configured with your model as the only available tool.

According to the source code in needle/__init__.py (lines 55-66), the constructor initializes the agent with tools=[schema], ensuring the engine grammar is built specifically for your data structure. The implementation (lines 66-78) handles the complete lifecycle: invoking the engine, parsing the function call response, and instantiating the Pydantic model from the returned arguments dictionary.

How Needle 2 Converts Pydantic Models to Tools

Before the engine can generate structured output, Needle 2 must translate your Pydantic model into a JSON schema that guides token generation. This transformation occurs in needle/agent/tools.py via the pydantic_schema() function (lines 51-62).

The function extracts the model's JSON schema using model_json_schema() or schema(), then packages it as a tool description compatible with the engine's grammar system. This schema defines the available fields, types, and constraints, allowing the underlying engine to generate syntactically valid function calls that conform to your data structure.

Step-by-Step Implementation

Define Your Pydantic Schema

First, create a Pydantic BaseModel that describes the fields you want to extract from unstructured text:

from pydantic import BaseModel
import needle

class Invoice(BaseModel):
    """Invoice details extracted from free-text."""
    vendor: str          # e.g. "Acme Corp"

    total: float         # monetary amount

    due_date: str        # ISO date string like "2026-09-01"

Run the Extraction

Pass your raw text and model class to needle.extract():

text = "Invoice from Acme Corp, $1,200.00, due 2026-09-01"
invoice = needle.extract(text, Invoice)

print(invoice)               # → Invoice(vendor='Acme Corp', total=1200.0, due_date='2026-09-01')

print(invoice.vendor)        # → Acme Corp

print(invoice.total)         # → 1200.0

If the engine cannot produce a valid function call, the function returns None.

The Internal Execution Flow

The one-shot extraction process follows a precise sequence implemented in the source code:

  1. Schema Conversion: The pydantic_schema() function converts your Pydantic model to a JSON schema and wraps it as a tool definition.
  2. Agent Initialization: extract() creates a temporary Needle agent with the schema as the sole tool (Needle(tools=[schema])).
  3. Grammar-Guided Generation: The agent executes a single complete() call. The engine's grammar, built from the model's JSON schema, constrains token generation to produce well-formed function calls.
  4. Response Parsing: The wrapper reads the first entry from function_calls, extracts the arguments dictionary, and passes it to the Pydantic constructor (schema(**arguments)).
  5. Type Validation: Pydantic validates the arguments against your model definition, returning a fully typed Python instance (or a plain dict if the input was not a Pydantic class).

Performance and Memory Considerations

Because the Needle 2 engine cannot unload weights during a session, the extract() function reuses any already-loaded weights or the default base weights for each call. This design ensures that one-shot extraction with Needle 2 using Pydantic remains fast and memory-efficient, avoiding the overhead of model reloading between extractions.

Summary

  • The extract() function in needle/__init__.py provides a convenience wrapper for single-pass structured extraction.
  • Pydantic models are automatically converted to JSON schemas via pydantic_schema() in needle/agent/tools.py.
  • The engine uses grammar-guided generation to ensure valid function call output conforming to your schema.
  • Weight reuse across calls eliminates loading overhead, making repeated extractions efficient.
  • The function returns instantiated Pydantic objects or None if extraction fails.

Frequently Asked Questions

What is one-shot extraction in Needle 2?

One-shot extraction refers to parsing unstructured text into a structured format using a single inference pass. In Needle 2, this is implemented through the extract() function, which treats your Pydantic schema as the only available tool, runs one complete() call, and returns the parsed result without maintaining conversation state.

How does Needle 2 convert Pydantic models to JSON schemas?

Needle 2 uses the pydantic_schema() function located in needle/agent/tools.py (lines 51-62). This function calls the model's model_json_schema() or schema() method to generate the JSON representation, then packages it as a tool description that the engine can use to build a constrained generation grammar.

Can I use extract() without Pydantic?

Yes. While designed primarily for Pydantic integration, the extract() function checks whether the provided model is a Pydantic class. If you pass a non-Pydantic schema definition, the function returns a plain Python dict containing the extracted arguments instead of an instantiated model object.

Where is the extract() function implemented?

The extract() function is implemented in needle/__init__.py (lines 66-78) within the cactus-compute/needle repository. This file also contains the Needle class constructor (lines 55-66) that powers the underlying extraction engine.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →