# Needle 2 `run()` vs `complete()`: Understanding the Difference Between These Methods

> Understand Needle 2's run() vs complete() methods. Learn how complete() generates text in one shot, while run() handles multi-turn agent loops with tool calls.

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

---

**The `complete()` method performs a single-shot text generation without tool calls, while `run()` executes a multi-turn agent loop that can invoke registered Python functions and aggregate their results.**

When working with the `cactus-compute/needle` library, understanding the difference between `run()` and `complete()` methods in Needle 2 is essential for building effective LLM applications. Both methods serve as high-level entry points for text generation, but they operate at fundamentally different levels of abstraction—one providing raw engine access, the other orchestrating complex tool-using agents.

## Core Architectural Differences

The two methods represent distinct interaction patterns with the Needle engine. While both ultimately rely on the same underlying C extension for text generation, they differ significantly in how they handle the response and manage execution flow.

### `complete()` — Single-Shot Text Generation

The `complete()` method provides direct access to the native Needle engine through a thin Python wrapper. Located 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 lines 23–38, this function calls the C-extension `needle_complete`, decodes the returned buffer, and returns a dictionary containing the generated text.

This method does not perform any tool execution or iterative processing. It sends a single prompt and returns the raw response JSON as-is, optionally adding a `confidence` field if model weights are present in the output. Use this when you need straightforward text completion without external function integration.

### `run()` — Multi-Turn Agent Execution

In contrast, `run()` (defined at lines 39–60 in the same file) implements a full agent loop capable of executing Python tools. According to the source code, this method:

1. Invokes `_track()` from [`needle/_telemetry.py`](https://github.com/cactus-compute/needle/blob/main/needle/_telemetry.py) for usage metrics
2. Calls the internal `_complete()` routine to get the initial response
3. Enters a processing loop (up to `max_steps` iterations) that:
   - Extracts `function_calls` from the engine output
   - Looks up each call in `self._functions`
   - Executes the corresponding Python implementation
   - Feeds JSON-encoded results back to `_complete()` for subsequent turns
4. Attaches a `"results"` field containing all tool outputs before returning

This architecture makes `run()` the appropriate choice when building agents that can query APIs, perform calculations, or interact with external systems during generation.

## Implementation Details from Source Code

Examining the [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) implementation reveals the exact behavioral split. The `complete()` method (lines 23–38) immediately returns after decoding the C-extension response, making it stateless regarding function execution. It handles the low-level buffer management and JSON parsing but deliberately avoids any interpretation of `function_calls` fields.

Conversely, `run()` (lines 39–60) maintains state across multiple turns. It leverages the tool registry defined in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) to resolve function names to Python callables. The method implements defensive iteration logic, ensuring that even if the engine requests multiple sequential tool calls, each result is properly serialized and fed back into the context window before the next generation cycle begins.

## Practical Code Examples

### Basic Completion with `complete()`

Use this approach for simple text generation tasks where no external tools are required:

```python
from needle import Needle

needle = Needle()
prompt = "Write a short poem about sunrise."

# Single-shot generation

response = needle.complete(prompt)
print(response["text"])

```

In this example, the method directly returns the model's output without checking for function calls or entering any processing loops.

### Tool-Enabled Workflows with `run()`

When your application requires the model to use external data or calculations, use `run()` with registered tools:

```python
from needle import Needle

# Define a tool that fetches weather data

def get_weather(city: str) -> dict:
    return {"city": city, "temp_c": 22, "condition": "Sunny"}

needle = Needle()
needle.register_tool("get_weather", get_weather)

query = "What is the weather in Paris right now?"

# Multi-turn execution with tool calls

result = needle.run(query, max_steps=5)
print("Final answer:", result["text"])
print("Tool results:", result["results"])

```

In this workflow, the engine may return a `function_calls` entry such as `{"name":"get_weather","arguments":{"city":"Paris"}}`. Needle automatically invokes `get_weather`, feeds the JSON result back to the engine, and continues the conversation until no more calls are required or `max_steps` is reached.

## Summary

- **`complete()`** provides single-shot access to the Needle engine via the C-extension `needle_complete`, returning raw JSON without processing tool calls.
- **`run()`** orchestrates a multi-turn agent loop defined in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) lines 39–60, handling tool registration, execution, and result aggregation.
- The `run()` method includes telemetry tracking via `_track()` and stores all tool outputs in a `"results"` field absent from `complete()` responses.
- Choose **`complete()`** for simple generation tasks and **`run()`** when building function-calling agents that interact with external Python code.

## Frequently Asked Questions

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

Use `complete()` when you need direct, single-turn text generation without any tool execution. This method is ideal for straightforward completions, summarization tasks, or any scenario where the model does not need to invoke external functions. It offers lower overhead and simpler response handling since it bypasses the agent loop and telemetry tracking found in `run()`.

### Can `complete()` return `function_calls` in the response?

Yes, `complete()` may return raw `function_calls` in the JSON response from the engine, but it will **not** process or execute them. The method simply decodes the buffer and returns the dictionary as-is. If you use `complete()`, you must manually inspect the response for function calls and handle the tool execution and follow-up prompts yourself, whereas `run()` automates this entire workflow.

### How does `run()` handle the `max_steps` parameter?

The `max_steps` parameter in `run()` acts as a safety limit to prevent infinite loops during agent execution. According to the source code in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), the method enters a loop that continues extracting and executing `function_calls` until either no more calls remain or the iteration count reaches `max_steps`. Setting this parameter appropriately prevents runaway execution when models generate recursive or cyclical tool requests.

### Is there a performance difference between the two methods?

Yes, `complete()` is significantly lighter since it performs a single C-extension call and returns immediately. `run()` incurs additional overhead from the Python-based agent loop, JSON serialization of tool results, and telemetry collection via `_track()`. However, for tool-enabled workflows, `run()` is more efficient than manually re-implementing the tool-execution loop, as it handles context management and result aggregation optimally within the Needle framework.