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

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 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, 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
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, 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, 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
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 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, 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)
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 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 needle/__init__.py 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →