How to Use Needle for Structured Data Extraction: A Complete Guide

Needle provides a high-level extract() helper that transforms unstructured text into typed Python objects or dictionaries by constraining the model's output generation through a user-defined schema.

Needle is an open-source inference engine from cactus-compute/needle designed for deterministic, schema-guided text generation. Its extract() function enables developers to perform structured data extraction from raw text by compiling schemas into byte-level grammars that constrain token generation, ensuring type-safe outputs that respect field definitions and enumerations.

How Needle's Extraction Engine Works

The extract() function in needle/__init__.py implements a four-stage pipeline that converts free-form text into structured data:

  1. Single-tool agent creation – The function instantiates a temporary Needle agent using the supplied schema as the sole available tool (Needle(tools=[schema], …)). This occurs at lines 66-71 of needle/__init__.py, where the schema is wrapped in a list to restrict the model's tool-calling scope.

  2. Engine binding – The agent binds to the native inference engine, loading requested weights (or the base model) and initializing the engine with the JSON representation of the schema. This initialization logic is found at lines 55-63 in needle/__init__.py.

  3. Prompt execution and function calling – Calling agent.complete(text, max_new_tokens) executes the model on the raw input. The model returns a JSON envelope containing function_calls that specify which schema to invoke and the inferred argument values (lines 11-19 of needle/__init__.py).

  4. Argument extraction and instantiation – The function extracts the arguments dictionary from the first function call. If the schema is a Pydantic model, it instantiates the class directly (schema(**arguments)); otherwise, it returns the raw dictionary. This final transformation occurs at lines 74-78 of needle/__init__.py.

Because the schema is compiled into a byte-level grammar that constrains the model's token generation, the extraction is deterministic and respects field types, enumerations, and value ranges defined in the schema.

Extracting Data with Pydantic Models

The most common approach for structured data extraction uses Pydantic models to define strict type constraints. The extract() function automatically instantiates the model when provided with a BaseModel subclass.

import needle
from pydantic import BaseModel

class Invoice(BaseModel):
    vendor: str
    total: float
    due_date: str

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

print(invoice.vendor)   # → Acme Corp

print(invoice.total)   # → 1200.0

This approach loads the schema, executes the model, and returns a fully-typed Invoice instance with validated fields. The schema definition in needle/agent/tools.py handles the underlying Field implementations and schema building required for this conversion.

Extracting Data with Raw JSON Schemas

For scenarios where Pydantic dependencies are unnecessary, extract() accepts plain dictionaries conforming to JSON Schema specifications. When using raw schemas, the function returns a standard Python dict rather than a typed object.

import needle

schema = {
    "name": "Contact",
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "email": {"type": "string", "format": "email"}
    },
    "required": ["name", "email"]
}

text = "John Doe can be reached at john@doe.com"
result = needle.extract(text, schema)

print(result)  # → {'name': 'John Doe', 'email': 'john@doe.com'}

The Needle class processes this schema through the same grammar compilation pipeline defined in doc/apis.md, ensuring that the model outputs valid JSON objects matching the specified properties and required fields.

Using Custom Weights for Domain-Specific Extraction

You can perform structured data extraction using fine-tuned models by instantiating a persistent Needle agent with custom weights. This approach reuses the tuned parameters across multiple extraction operations while maintaining identical schema-binding behavior.


# Assume you have a tuned .cact file from a LoRA fine-tune

agent = needle.Needle(weights="my_needle.cact")
product = agent.extract(
    "Product: UltraWidget, price $99.99, in stock",
    schema={"name": "ProductInfo", "type": "object",
            "properties": {"name": {"type": "string"},
                           "price": {"type": "number"},
                           "stock": {"type": "boolean"}},
            "required": ["name", "price"]})
print(product)

The weights parameter loads the specified .cact file during engine initialization, allowing the extraction pipeline to leverage domain-specific knowledge while maintaining deterministic output constraints.

Summary

  • Single-function API: The extract() helper in needle/__init__.py provides a one-line interface for converting text to structured data using either Pydantic models or JSON schemas.
  • Deterministic generation: Schema compilation into byte-level grammars ensures outputs strictly conform to type definitions, enumerations, and required fields.
  • Flexible input formats: Supports both BaseModel subclasses for typed objects and raw dictionaries for lightweight extraction.
  • Custom model support: The Needle class accepts custom .cact weights for domain-specific extraction tasks while using the same underlying complete() method.

Frequently Asked Questions

What is the difference between using a Pydantic model and a raw dictionary schema?

When you pass a Pydantic BaseModel subclass to extract(), the function instantiates the model class with the extracted arguments (schema(**arguments)), returning a typed object with validation. When using a raw dictionary schema, the function returns a plain Python dict containing the extracted values. Both approaches use the same grammar-constrained generation pipeline defined in needle/__init__.py.

How does Needle ensure deterministic output during extraction?

Needle compiles the provided schema into a byte-level grammar that constrains the model's token generation at the inference level. This means the model can only produce tokens that form valid JSON matching the schema structure, field types, and constraints (such as enums or regex patterns), eliminating hallucinated fields or type mismatches.

Can I use fine-tuned models with the extract() function?

Yes. While the standalone needle.extract() helper uses the base model, you can create a persistent Needle instance with custom weights via needle.Needle(weights="path/to/model.cact"). This instance provides an extract() method that follows the same schema-binding logic while utilizing your fine-tuned parameters for domain-specific structured data extraction.

Where is the extraction logic implemented in the Needle codebase?

The core extraction pipeline resides in needle/__init__.py, specifically lines 11-19 (output parsing), 55-63 (engine binding), 66-71 (agent initialization), and 74-78 (argument instantiation). Schema definitions and tool handling are implemented in needle/agent/tools.py, while detailed API documentation covering grammar compilation is available in doc/apis.md.

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 →