# Needle `complete()` vs `run()` vs `extract()`: Which Method to Use When

> Understand Needle's complete run and extract methods. Learn when to use complete for single LLM steps run for tool loops and extract for structured data.

- Repository: [Cactus Compute, Inc./needle](https://github.com/cactus-compute/needle)
- Tags: deep-dive
- Published: 2026-08-16

---

**The Needle API provides three distinct entry points: `complete()` for single-step LLM responses, `run()` for multi-step tool execution loops, and `extract()` for one-shot structured data extraction from raw text.**

When building applications with the [cactus-compute/needle](https://github.com/cactus-compute/needle) library, choosing the right method determines whether your code performs a simple completion, orchestrates iterative tool calls, or extracts structured objects. This guide breaks down each method's architecture, implementation details, and optimal use cases based on the actual source code.

## `complete()`: Single-Step LLM Generation

The `complete()` method is the foundational API call in Needle. Located in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), it provides direct access to the underlying language model engine without any iteration logic.

### How `complete()` Works

The implementation follows this flow:

1. Invokes the native C library via `needle_complete`, passing the raw prompt and `max_new_tokens` limit
2. Receives a JSON envelope from the engine and parses it into a Python dictionary
3. Returns the decoded object, which may include `generated_text` and optional `function_calls` if tools were registered

```python
from needle import Needle

agent = Needle()
result = agent.complete(
    "Translate to French: Hello, world!",
    max_new_tokens=64
)
print(result["generated_text"])

# → "Bonjour, le monde !"

```

### When to Use `complete()`

- **Simple completions**: Chat responses, summarization, rewriting, or any standalone generation task
- **Manual tool handling**: When you want full control over function-call parsing and execution
- **Low-latency scenarios**: Avoids the overhead of the multi-step loop in `run()`

The native binding resides in [`needle/model/run.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/run.py), where `needle_complete` handles the actual inference call.

## `run()`: Multi-Step Tool Execution Loop

The `run()` method builds on `complete()` to enable **autonomous agentic behavior**. It implements a reasoning loop where the model can invoke registered Python functions, receive their outputs, and continue generation.

### Architecture of the `run()` Loop

As implemented in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), `run()` executes the following cycle (default maximum 8 steps):

1. Calls `complete()` for the initial response
2. Extracts any `"function_calls"` from the returned envelope
3. Looks up each callable in `self._functions`
4. Executes the functions, capturing results or exceptions
5. Serializes outputs and feeds them back via another `complete()` call
6. Repeats until no function calls remain or `max_steps` is reached
7. Attaches all tool results under `"results"` and returns the final response

```python
from needle import Needle

def web_search(query: str):
    # Simulated tool implementation

    return {"title": f"Result for {query}", "url": "https://example.com"}

agent = Needle(tools=[web_search])
response = agent.run(
    "Find the latest news about AI breakthroughs.",
    max_steps=4,
    max_new_tokens=128,
)

print(response["results"])          # List of tool execution results

print(response["generated_text"])   # Final synthesized answer

```

### When to Use `run()`

- **Tool-augmented reasoning**: Any scenario requiring external data retrieval, computation, or API calls
- **Self-correcting workflows**: The model can iterate based on tool feedback until constraints are satisfied
- **Complex multi-hop queries**: Questions requiring chained tool calls (e.g., search → fetch → analyze)

The tool schema utilities in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) support `run()` by converting Python callables into the JSON schema format the model expects.

## `extract()`: One-Shot Structured Data Extraction

The `extract()` method is purpose-built for **type-safe data extraction** from unstructured text. Unlike `run()`, it does not iterate—it performs a single forced function call against a provided schema.

### Implementation Details

According to lines 57–68 of [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), the method:

1. Delegates to the module-level `extract` function
2. Creates a temporary `Needle` agent with **only** the supplied schema as a tool
3. Calls `complete()` on the raw text, constraining the model to emit a matching function call
4. Parses the first function-call arguments into either:
   - A **Pydantic model instance** (if schema is a `BaseModel` subclass)
   - A **plain dictionary** (if schema is a dict)

```python
from needle import Needle
from pydantic import BaseModel

class ContactInfo(BaseModel):
    name: str
    email: str

text = "Contact: Jane Doe, email jane.doe@example.com."

agent = Needle()
extracted = agent.extract(text, ContactInfo)

print(extracted)

# → ContactInfo(name='Jane Doe', email='jane.doe@example.com')

```

### When to Use `extract()`

- **Entity extraction**: Pulling structured records from documents, emails, or user input
- **Validation pipelines**: Pydantic integration provides automatic type checking
- **No-iteration scenarios**: When a single parsing step is sufficient and loop overhead is undesirable

The model architecture in [`needle/model/architecture.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/architecture.py) defines how the engine produces the JSON envelopes that `extract()` and the other methods consume.

## Comparing Needle API Methods

| Dimension | `complete()` | `run()` | `extract()` |
|-----------|------------|---------|-------------|
| **Interaction pattern** | Single call | Multi-step loop | Single forced call |
| **Tool execution** | None (returns calls only) | Automatic | N/A (schema only) |
| **Iteration depth** | 1 step | ≤ `max_steps` (default 8) | 1 step |
| **Return type** | Raw envelope dict | Envelope with `"results"` list | Pydantic model or dict |
| **Primary source** | [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) | [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) | [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 57–68) |

## Performance and Design Considerations

- **Latency**: `complete()` is fastest; `run()` scales linearly with tool calls; `extract()` adds minimal overhead beyond one `complete()` call
- **Token usage**: `run()` consumes more tokens due to conversation history accumulation; `extract()` minimizes context by using a fresh agent
- **Error handling**: `run()` captures tool exceptions and feeds them to the model; `extract()` raises parsing errors directly

## Summary

- **`complete()`** — Use for direct LLM access, single responses, and manual control over tool interactions. Foundational method calling `needle_complete` in the native layer.

- **`run()`** — Use when the model must autonomously invoke and iterate over multiple tools. Implements the agentic loop with automatic result injection.

- **`extract()`** — Use for type-safe, one-shot structured extraction from text. Leverages Pydantic for validation and returns strongly-typed objects.

## Frequently Asked Questions

### Can `complete()` return function calls without executing them?

Yes. When the `Needle` agent is initialized with tools, `complete()` returns a JSON envelope that may contain `function_calls`, but it does **not** execute them. You must parse and invoke these manually if not using `run()`.

### What happens if `run()` exceeds `max_steps`?

The loop terminates automatically after reaching the `max_steps` limit (default 8). The final response includes any tool results collected up to that point, with `generated_text` reflecting the model's last reasoning state.

### Does `extract()` support nested Pydantic models?

Yes. The schema passed to `extract()` can be any Pydantic `BaseModel`, including those with nested models, lists, or optional fields. The model architecture in [`needle/model/architecture.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/architecture.py) handles nested JSON structure generation.

### When should I prefer `extract()` over `run()` for data extraction?

Use `extract()` when you need **exactly one structured result** and no iterative reasoning. Use `run()` if the extraction requires multiple tool calls (e.g., fetch a webpage, then parse it) or if the model needs to validate or refine its answer based on intermediate results.