# How to Use `needle.extract()` for Typed Pydantic Results: A Complete Guide

> Master needle.extract for typed Pydantic results. This guide shows how to get structured data from unstructured text into Pydantic models or dictionaries. Learn more.

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

---

**`needle.extract()` is a one-shot helper that performs structured extraction from unstructured text and returns a strongly-typed Pydantic model or plain dictionary.**

The `needle` library provides this convenience function to eliminate boilerplate when you need to parse free-form text into typed data structures. Instead of manually configuring agents and tools, you define a schema and receive parsed results in a single call.

## The `extract()` Function Signature

The public API is defined in [[`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py)](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) at line 79:

```python
def extract(
    text: str,
    schema,
    system: str | None = None,
    max_new_tokens: int = 256,
    weights: str | None = None
):

```

- **`text`** – The unstructured input to parse.
- **`schema`** – A Pydantic `BaseModel` subclass or dictionary defining the expected structure.
- **`system`** – Optional system prompt to customize LLM behavior.
- **`max_new_tokens`** – Generation limit (default 256).
- **`weights`** – Optional path to custom model weights.

## How `extract()` Works Under the Hood

The implementation follows a clear five-step workflow as seen in the source code:

1. **Agent initialization** – A temporary `Needle` agent is created with `tools=[schema]` as the sole tool (line 86).
2. **Engine setup** – The shared engine is re-initialized with this tool configuration, loading custom weights if provided or falling back to globally active weights (line 85).
3. **LLM inference** – Input text is sent through the private `_complete` method (line 87), located in [[`needle/model/run.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/run.py)](https://github.com/cactus-compute/needle/blob/main/needle/model/run.py).
4. **Function-call parsing** – The response is inspected for `function_calls` entries (lines 88-90). If absent, the function returns `None`.
5. **Typed return** – Arguments from the first function call are unpacked. If `schema` is a Pydantic model (`_is_pydantic_model(schema)` returns `True`), the function returns `schema(**arguments)`; otherwise it returns the raw dictionary (lines 91-92).

This design makes `extract()` safe for repeated calls without manual agent lifecycle management.

## Extracting into Pydantic Models

The primary use case for `needle.extract()` is obtaining type-safe results. Define your schema with Pydantic and receive a validated model instance:

```python
from pydantic import BaseModel
from needle import extract

class WeatherReport(BaseModel):
    location: str
    temperature_c: float
    condition: str

text = "The weather in Berlin is 22.5°C and sunny."
result = extract(text, WeatherReport)
print(result)          # → WeatherReport(location='Berlin', temperature_c=22.5, condition='sunny')

print(result.dict())   # → {'location': 'Berlin', 'temperature_c': 22.5, 'condition': 'sunny'}

```

The returned object is a full Pydantic instance—you get IDE autocomplete, validation, and serialization methods like `.dict()`, `.json()`, and `.model_dump()`.

## Extracting into Plain Dictionaries

For quick prototyping or dynamic schemas, pass a dictionary instead of a Pydantic class:

```python
from needle import extract

schema = {
    "name": "str",
    "age": "int",
    "email": "str"
}

text = "John Doe is 30 years old, email john@example.com."
result = extract(text, schema)
print(result)          # → {'name': 'John Doe', 'age': 30, 'email': 'john@example.com'}

```

Note that dictionary schemas return raw Python dictionaries without Pydantic validation—they're convenient but lack type safety.

## Customizing System Prompts and Weights

Fine-tune extraction behavior with the optional parameters:

```python
from needle import extract

custom_prompt = "You are a helpful assistant that extracts contact info."
result = extract(
    "Contact: Alice, 28, alice@domain.com",
    schema={"name": "str", "age": "int", "email": "str"},
    system=custom_prompt,
    weights="my-special-weights"
)
print(result)

```

The `system` prompt guides the LLM's extraction strategy, while `weights` loads a specific model checkpoint. If omitted, `weights` defaults to whatever is globally active in the `needle` runtime.

## Engine Re-initialization and Thread Safety

A key implementation detail in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) is that `extract()` re-initializes the shared engine on each call. According to the cactus-compute/needle source code, this ensures:

- **Isolation** – Each extraction uses a fresh tool configuration without polluting global state.
- **Reusability** – No manual cleanup between calls with different schemas.
- **Weight flexibility** – Per-call weight overrides without permanent model swapping.

For production throughput, consider reusing a persistent `Needle` agent if latency is critical—`extract()` trades marginal overhead for convenience.

## Summary

- **`needle.extract()`** provides one-shot structured extraction with minimal setup.
- Returns **Pydantic models** for `BaseModel` schemas, **dictionaries** for plain dict schemas.
- Internally creates a temporary `Needle` agent with the schema as the sole tool.
- Located in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) with low-level calls through [`needle/model/run.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/run.py) and [`needle/model/architecture.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/architecture.py).
- Supports customization via `system` prompts and `weights` overrides.

## Frequently Asked Questions

### What happens if the LLM doesn't return a valid function call?

`extract()` returns `None`. Lines 88-90 in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) explicitly check for `function_calls` in the response and short-circuit to `None` when absent.

### Can I use `extract()` with nested Pydantic models?

Yes. The `_is_pydantic_model()` check recursively validates nested structures, and the unpacking logic at lines 91-92 passes all extracted arguments to your model's constructor, including nested `BaseModel` fields.

### Is there a performance penalty for calling `extract()` repeatedly?

Marginal. Each call re-initializes the engine (line 86), which adds setup overhead. For high-throughput applications, instantiate a persistent `Needle` agent directly instead of using this helper.

### How does `extract()` handle type coercion?

Pydantic handles coercion automatically when `schema(**arguments)` is called. The raw LLM outputs (typically JSON-like structures) are passed as keyword arguments, and Pydantic's validation layer converts strings to numbers, parses dates, or enforces constraints according to your field definitions.