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:
- Invokes the native C library via
needle_complete, passing the raw prompt andmax_new_tokenslimit - Receives a JSON envelope from the engine and parses it into a Python dictionary
- Returns the decoded object, which may include
generated_textand optionalfunction_callsif 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):
- Calls
complete()for the initial response - Extracts any
"function_calls"from the returned envelope - Looks up each callable in
self._functions - Executes the functions, capturing results or exceptions
- Serializes outputs and feeds them back via another
complete()call - Repeats until no function calls remain or
max_stepsis reached - 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:
- Delegates to the module-level
extractfunction - Creates a temporary
Needleagent with only the supplied schema as a tool - Calls
complete()on the raw text, constraining the model to emit a matching function call - Parses the first function-call arguments into either:
- A Pydantic model instance (if schema is a
BaseModelsubclass) - A plain dictionary (if schema is a dict)
- A Pydantic model instance (if schema is a
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 onecomplete()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 callingneedle_completein 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →