# Difference Between complete(), run(), and extract() Methods in Needle

> Understand Needle's complete run and extract methods. Learn how complete() makes single LLM calls, run() iterates tool calls, and extract() performs one-shot structured data extraction.

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

---

**`complete()` performs a single-shot LLM call returning a raw JSON envelope, `run()` executes an iterative tool-calling loop with automatic result feeding, and `extract()` provides one-shot structured data extraction by treating a Pydantic schema as the sole available tool.**

The cactus-compute/needle library exposes three distinct high-level Python APIs for language model interaction. Understanding the difference between `complete()`, `run()`, and `extract()` methods in Needle is essential for selecting the right abstraction: they differ in execution steps, return types, and how they handle tool registration.

## Internal Implementation of the Three APIs

Each method serves a specific interaction pattern with the underlying native engine.

### complete(): Single-Shot Completion

The `complete()` method acts as a thin wrapper around the compiled C library function `needle_complete`. Located in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) at lines 103–118, this method serializes your prompt, calls the native engine, and deserializes the JSON response.

It performs exactly one model invocation. Tool calls only occur if your prompt already contains a tool schema in its content. The return value is a **JSON object** (Python dict) containing the model's text output and, if applicable, a `function_calls` description.

### run(): Iterative Tool Orchestration

The `run()` method builds upon `complete()` to enable multi-step agentic behavior. As implemented in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) at lines 19–40, it first calls `complete()` once, then inspects the response for `"function_calls"` entries.

If function calls exist, it looks up the corresponding Python implementation in `self._functions`, executes it, captures any exceptions, and feeds the accumulated results back to the model via another `complete()` call. This loop repeats until no more calls are present or the `max_steps` limit is reached. The return value is a JSON envelope containing the final model response plus an additional `"results"` field with the list of tool-call results.

### extract(): Structured Data Extraction

The `extract()` method provides a convenience wrapper for one-shot structured extraction. According to [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) at lines 57–68 (standalone function) and lines 141–142 (method wrapper), it creates a temporary `Needle` agent with your supplied Pydantic schema as the **only** available tool.

It executes a single `complete()` call, inspects the returned `function_calls`, and instantiates your schema class with the arguments. If the model generated a matching function call, it returns an instance of the provided Pydantic model (or a plain dict if specified); otherwise, it returns `None`.

## Key Differences Summary

- **Execution Steps**: `complete()` performs exactly one step; `run()` loops up to `max_steps`; `extract()` performs exactly one step but adds schema instantiation.
- **Return Enrichment**: `run()` appends a `"results"` list containing tool execution outputs; `complete()` returns only the raw envelope; `extract()` returns the parsed Python object directly.
- **Tool Registration**: `run()` utilizes the full tool registry of the persistent `Needle` instance; `extract()` temporarily restricts the tool set to a single schema; `complete()` uses tools already bound to the instance or none.
- **Use Case**: Use `complete()` for simple chat completions, `run()` for autonomous agents requiring external data, and `extract()` for parsing unstructured text into typed data models.

## Practical Code Examples

```python
from needle import Needle

```

### Simple Completion with complete()

```python
agent = Needle()
resp = agent.complete("Explain quantum entanglement in one sentence.")
print(resp["text"])

```

### Iterative Tool Execution with run()

```python
def fetch(url: str) -> str:
    import requests
    return requests.get(url).text

agent = Needle(tools=[fetch])
result = agent.run(
    "Fetch the first paragraph of https://example.com and summarise it.",
    max_steps=5
)
print(result["results"])  # List of tool-call results

print(result["text"])     # Final summarised text

```

### Structured Extraction with extract()

```python
from pydantic import BaseModel

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

text = "John Doe's email is john.doe@example.com."
extracted = Needle().extract(text, PersonInfo)
print(extracted)  # PersonInfo(name='John Doe', email='john.doe@example.com')

```

## Summary

- **`complete()`** in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) lines 103–118 provides single-shot LLM access via the native `needle_complete` C function, returning raw JSON.
- **`run()`** in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) lines 19–40 orchestrates multi-step tool calling by looping over `complete()`, executing Python functions from `self._functions`, and aggregating results.
- **`extract()`** in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) lines 57–68 creates a temporary agent for one-shot Pydantic schema instantiation from model outputs.

## Frequently Asked Questions

### When should I use complete() instead of run()?

Use **`complete()`** when you need a single raw model response without automatic tool execution. Choose **`run()`** when your application requires the model to iteratively call external functions (like APIs or databases) and feed results back into the conversation until completion.

### Does run() support limiting the number of tool calls?

Yes. The `run()` method accepts a **`max_steps`** parameter that caps the number of back-and-forth iterations between the model and your Python functions. The loop terminates when no more `function_calls` remain in the response or when the step limit is reached.

### Can extract() return a plain dictionary instead of a Pydantic model?

Yes. While **`extract()`** is designed to instantiate Pydantic models for type safety, it can return a plain Python dictionary if the schema parsing fails or if no function call is generated by the model, returning **`None`** instead.

### Is extract() slower than complete() because it creates a temporary agent?

The overhead is minimal. **`extract()`** creates a short-lived `Needle` instance internally (as seen in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) lines 141–142), executes a single `complete()` call, and immediately tears down. For most use cases, this temporary instantiation introduces negligible latency compared to the model inference time.