`run()`, `complete()`, and `extract()` in Needle: A Complete API Comparison
TL;DR: Needle provides three distinct APIs—complete() for single-shot generation, run() for iterative tool-calling loops, and extract() for structured data extraction—each designed for different interaction patterns with language models.
The Needle library (cactus-compute/needle) offers multiple ways to interact with language models depending on whether you need raw completions, multi-step reasoning with tools, or structured data extraction. Understanding the differences between run(), complete(), and extract() helps you choose the right abstraction for your use case.
complete(): Direct Single-Shot Completion
The complete() method is the thinnest wrapper around Needle's native engine. It performs exactly one model call and returns the raw response envelope.
How It Works
In needle/__init__.py (lines 103–118), complete() serializes your prompt, calls the underlying C library function needle_complete, and deserializes the JSON response:
from needle import Needle
agent = Needle()
response = agent.complete("What is the capital of France?")
print(response["text"]) # "Paris is the capital of France."
Return Value
complete() returns a JSON object (dict) containing:
- The model's generated text
- Optional
function_callsif your prompt includes tool schemas - Metadata from the native engine
This method does not automatically execute any tool calls—it merely reports them in the response.
run(): Iterative Tool-Calling with Automatic Execution
The run() method builds on complete() to enable multi-step conversations where the model can call tools, observe results, and continue reasoning.
The Execution Loop
As implemented in needle/__init__.py (lines 19–40), run():
- Calls
complete()once - Checks for
function_callsin the response - Executes each call using the Python functions stored in
self._functions - Feeds results back to the model via another
complete()call - Repeats until no calls remain or
max_stepsis reached
from needle import Needle
def get_weather(city: str) -> str:
return f"Sunny and 75°F in {city}"
def convert_temp(fahrenheit: float) -> float:
return (fahrenheit - 32) * 5 / 9
agent = Needle(tools=[get_weather, convert_temp])
result = agent.run(
"What's the weather in Miami in Celsius?",
max_steps=3
)
print(result["results"]) # [{'city': 'Miami', 'return': 'Sunny and 75°F in Miami'}, {'fahrenheit': 75.0, 'return': 23.888...}]
print(result["text"]) # Final answer from the model
Key Difference from complete()
run() enriches the return value with a "results" field containing the complete history of tool executions. This lets you audit what happened during the multi-step process.
extract(): One-Shot Structured Data Extraction
The extract() method is a convenience API for the common pattern of extracting structured data from unstructured text using a Pydantic schema.
Temporary Agent Pattern
Unlike run() and complete(), extract() does not use a persistent Needle instance. According to needle/__init__.py (lines 57–68 and 141–142), it:
- Creates a temporary agent with your schema as the sole available tool
- Runs a single
complete()call - Instantiates the Pydantic model from the first
function_call - Returns the parsed object directly (or
Noneif extraction fails)
from needle import Needle
from pydantic import BaseModel, EmailStr
from datetime import date
class Invoice(BaseModel):
amount: float
due_date: date
vendor_email: EmailStr
text = """
Please pay $1,250.00 to acme-supplies@example.com
by September 15, 2024 for order #4921.
"""
invoice = Needle().extract(text, Invoice)
print(invoice)
# Invoice(amount=1250.0, due_date=date(2024, 9, 15), vendor_email='acme-supplies@example.com')
Return Type
extract() returns either:
- An instance of your Pydantic model (successful extraction)
None(no matching function call generated)
This eliminates boilerplate compared to manually setting up an agent and parsing function_calls.
API Comparison Summary
| API | Steps | Tool Scope | Return Value | Best For |
|---|---|---|---|---|
complete() |
1 | Instance-bound tools | Raw JSON envelope | Simple completions, manual tool handling |
run() |
1 to max_steps |
Instance-bound tools | JSON envelope + "results" list |
Multi-step reasoning with automatic tool execution |
extract() |
1 | Single temporary schema | Pydantic instance or None |
Structured data extraction from text |
Choosing the Right API
Use complete() when you need maximum control over the interaction flow or when implementing your own tool-handling logic.
Use run() when you want the model to autonomously solve problems requiring multiple tool calls, with Needle managing the conversation loop.
Use extract() when your only goal is converting unstructured text into a typed data structure—it's the most concise option for this specific task.
Summary
complete()inneedle/__init__.py(lines 103–118) provides direct access to the native engine with single-shot executionrun()inneedle/__init__.py(lines 19–40) implements an iterative loop that automatically executes tools and feeds results back to the modelextract()inneedle/__init__.py(lines 57–68, 141–142) creates a temporary single-tool agent for streamlined structured extraction- The
run()vscomplete()vsextract()distinction centers on iteration depth, return type enrichment, and tool set flexibility
Frequently Asked Questions
Can I use extract() with multiple schemas at once?
No. The extract() API is designed for exactly one schema. If you need the model to choose between multiple extraction schemas, create a persistent Needle agent with tools=[schema1, schema2, ...] and use run() or complete() to handle the selection logic yourself.
Does run() block until all steps complete?
Yes. The run() method is synchronous and blocks until either no more tool calls are generated or max_steps is reached. There is no built-in async variant in the current Needle implementation—use complete() directly with your own async orchestration if you need non-blocking execution.
What happens if a tool raises an exception during run()?
The exception is caught, recorded in the "results" list with an error indication, and fed back to the model as context. The loop continues if steps remain, allowing the model to potentially recover or report the failure. Check result["results"] after run() completes to inspect any errors.
Is extract() available as a standalone function or only as a method?
Both. You can call Needle().extract(text, schema) as an instance method, or import extract directly from the needle module as a standalone function that internally creates the temporary agent.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →