# Needle complete() vs run(): Controlling the Agent Loop

> Understand the difference between complete() and run() in Needle's agent loop. Use complete() for text-only and run() for full agent execution with tool calling and state management.

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

---

**Use `complete()` for single-shot text generation without tool execution, and `run()` to execute the full agent loop with automatic tool calling and state management.**

The cactus-compute/needle library provides two distinct entry points for interacting with language models. Understanding when to use `complete()` versus `run()` is essential for building effective AI agents that can either generate standalone text or autonomously execute multi-step workflows with tool integration.

## How `complete()` Works: Single-Shot Generation

The `complete()` method generates a single text completion (or embedding) from the model and returns immediately without interpreting any `function_calls` that may be present in the response.

According to the source code at [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), the public `complete()` method (lines 221-226) acts as a thin wrapper around the private `_complete()` helper. The core logic resides in lines 228-263, where the method invokes the native C library (or a fine-tuned worker defined in [`_worker.py`](https://github.com/cactus-compute/needle/blob/main/_worker.py)) and parses the raw JSON result. This implementation returns whatever the underlying engine produces in its JSON envelope, making it ideal for straightforward generation tasks where you do not need the model to invoke external tools.

### When to Use `complete()`

Use this method when you need:

- Direct access to raw model outputs without automatic tool execution
- Simple text completions or embedding generation
- Full manual control over parsing and handling `function_calls`
- Minimal latency for single-turn interactions

## How `run()` Works: The Full Agent Loop

The `run()` method executes the complete agent loop: it produces a completion, resolves any returned tool calls, feeds the results back into the model, and repeats until reaching a terminal response or the `max_steps` limit.

In [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 883-914), the `run()` implementation starts by calling `_complete()` once at line 886. It then iterates up to `max_steps` (lines 891-914). For each iteration, the method extracts `function_calls`, dispatches the corresponding Python tools (using utilities from [`agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/agent/tools.py)), gathers their results, and feeds those back into the model via another `_complete()` call at line 912.

Unlike `complete()`, the `run()` method returns after the loop finishes with an added `results` field containing the outcomes of all invoked tools, enabling stateful, autonomous agent behavior.

### Key Implementation Details

- **Line 886**: Initial `_complete()` call to generate the first response
- **Lines 891-914**: Iteration logic that handles tool execution loops
- **Line 912**: Subsequent `_complete()` calls that incorporate tool results into the context
- **max_steps parameter**: Hard limit on tool-call iterations to prevent infinite loops

## Practical Code Examples

### Basic Completion Without Tool Execution

```python
import needle

# Initialize with tools available but not auto-executed

needle_client = needle.Needle(tools=[my_tool])
resp = needle_client.complete("Write a haiku about sunrise.")
print(resp)  # → {"type":"text","completion":"..."}

```

### Full Agent Loop with Tool Execution

```python
import needle

# my_tool is a function that fetches current temperature

needle_client = needle.Needle(tools=[my_tool])
resp = needle_client.run(
    "What should I wear today?",
    max_steps=5,           # Maximum tool-call iterations

    max_new_tokens=128
)

print(resp["results"])  # → List of tool call results

print(resp["type"])     # → Final response type (e.g., "text")

```

## Summary

- **`complete()`** provides single-shot generation through the `_complete()` helper (lines 228-263 of [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py)), returning raw JSON without tool interpretation.
- **`run()`** implements the full agent loop (lines 883-914 of [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py)), automatically executing tools and iterating up to `max_steps`.
- Use **`complete()`** when you need direct model access without autonomous tool calling.
- Use **`run()`** when building agents that must resolve `function_calls` and maintain state across multiple generation rounds.
- The `max_steps` parameter in `run()` acts as a safety guard against infinite loops during tool execution.

## Frequently Asked Questions

### Does `complete()` ignore tools entirely?

No. The `complete()` method accepts a `tools` parameter and the model may still generate `function_calls` in its response. However, `complete()` returns immediately without executing those calls, leaving the interpretation and execution to your application code. In contrast, `run()` automatically detects and executes these calls through the agent loop.

### What happens if a tool call fails during `run()` execution?

The `run()` method captures tool execution results and feeds them back into the model regardless of success or failure. Error information propagates through the `results` field in the final response, allowing the model to potentially recover or provide alternative responses in subsequent iterations within the `max_steps` limit.

### Can I use `complete()` to manually implement what `run()` does?

Yes, but you would need to replicate the loop logic found in lines 891-914 of [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py). This involves checking for `function_calls` in the response, dispatching to your Python tool functions, collecting results, and calling `_complete()` again with the updated context. The `run()` method encapsulates this boilerplate for production use.

### Is there a performance difference between `complete()` and `run()`?

`complete()` makes exactly one call to the underlying engine, while `run()` makes at least one call plus additional calls for each tool execution step (up to `max_steps`). Each iteration in `run()` invokes `_complete()` at line 912, incurring additional latency equivalent to a full model generation round-trip per tool interaction.