Needle 2 `run()` vs `complete()`: Key Differences for Agentic and Single-Shot Generation
The run() method executes a full conversational turn with automatic tool calling and multi-step orchestration, while complete() sends a single raw request to the Needle engine and returns the unprocessed response.
The cactus-compute/needle repository provides a Python client for interacting with the Needle inference engine. Understanding the difference between Needle 2's run() and complete() methods is essential for building applications that either leverage built-in agentic capabilities or require low-level control over model generation.
Execution Model: Agentic Loop vs Single Request
The fundamental distinction lies in how each method handles the interaction lifecycle and tool execution.
Multi-Step Orchestration with run()
The run() method implements a conversational agent loop that can span multiple model calls. According to the source code in needle/__init__.py, this method starts by calling _track("run", …) to initialize telemetry, then enters a deterministic loop that executes for up to max_steps iterations (defaulting to 8).
During each iteration, run() calls the internal self._complete method to fetch a model response. If the response contains function_calls, the method:
- Looks up the corresponding Python implementations in
self._functions - Executes the functions locally
- Serializes the results to JSON
- Feeds the results back into the model via another
self._completecall
This cycle repeats until the conversation ends naturally or the step limit is reached. After the loop terminates, run() aggregates all function results into a "results" field appended to the final response dictionary.
Single-Shot Generation with complete()
The complete() method provides direct access to the underlying inference engine without orchestration logic. As implemented in needle/_worker.py, this method constructs a request dictionary with operation: "complete", forwards it to the worker process via self._worker.complete, and returns the raw "response" field from the worker's reply.
No post-processing, function execution, or looping occurs. The method returns exactly what the Needle engine produced for the single request, making it suitable for straightforward text generation tasks where you manage side effects manually.
Parameter Differences and Method Signatures
Each method exposes different parameters reflecting their distinct execution models.
run() accepts:
query: The initial user prompt for the conversational turnmax_steps: Integer controlling maximum tool-call cycles (default 8)max_new_tokens: Generation limit applied to each stepstrict: Boolean flag to reject ungrounded arguments- Audio payload options for multimodal inputs
complete() accepts:
text: The raw input string for single completionmax_new_tokens: Generation limit for the single request- Audio options for payload preparation
Notably, run() manages conversation state internally across multiple calls, while complete() treats each invocation as stateless.
Source Code Implementation Details
The implementation paths diverge significantly at the architectural level.
In needle/__init__.py, the run() method prepares audio payloads and enters a for loop that repeatedly invokes internal completion logic. The method parses function_calls from each response, dynamically executes Python callables registered in self._functions, and handles JSON serialization of results back to the model context.
Conversely, complete() in needle/_worker.py serves as a thin wrapper over the IPC layer. It builds a minimal request payload and immediately dispatches to self._worker.complete, returning only the "response" field without aggregation or modification.
Return Value Structures
run() returns:
A dictionary containing the final model response plus an additional "results" key. This key holds a list of all function call results executed during the conversational turn, enabling applications to audit which tools were invoked and what they returned.
complete() returns:
A dictionary or raw string matching exactly what the engine produced. No "results" aggregation is added, and no post-processing transforms the output.
Practical Usage Examples
Choose run() when building conversational agents requiring automatic tool use:
from needle import Needle
needle = Needle()
response = needle.run(
query="What's the weather in Paris today?",
max_steps=5,
strict=True
)
print(response["type"]) # "answer"
print(response["results"]) # List of weather API call results
Choose complete() for low-level generation or custom orchestration:
from needle import Needle
needle = Needle()
raw = needle.complete(
text="Explain quantum tunneling in two sentences.",
max_new_tokens=60
)
print(raw["type"]) # "answer"
print(raw["text"]) # Raw model output without processing
When to Use Each Method
Prefer run() when:
- Building conversational agents that need to call tools (e.g., web search, database lookups)
- Requiring automatic aggregation of function results across multiple model turns
- Leveraging the
strictparameter to validate grounded arguments - Managing audio payloads within an agentic context
Prefer complete() when:
- Executing quick tests or benchmarks against the raw engine
- Manually handling tool execution and conversation state
- Implementing custom orchestration logic outside the built-in loop
- Needing minimal latency without the overhead of multi-step parsing
Summary
-
run()implements a high-level agentic loop inneedle/__init__.pythat automatically executes tool calls, manages conversation state across up tomax_stepsiterations, and returns aggregated results in a"results"field. -
complete()provides low-level access to the Needle engine throughneedle/_worker.py, submitting single requests without post-processing, looping, or function execution. -
Key distinction:
run()manages multi-turn tool-enabled conversations automatically, whilecomplete()offers raw, single-shot generation for custom implementations.
Frequently Asked Questions
Can I use complete() to manually implement the tool-calling logic that run() provides?
Yes. The complete() method returns raw model responses including any function_calls fields, allowing you to parse tool requests, execute functions manually, and feed results back into subsequent complete() calls. This approach requires you to manage the conversation loop and result serialization that run() handles automatically according to the logic in needle/__init__.py.
Does run() always execute exactly max_steps iterations?
No. The method enters a for loop configured for up to max_steps iterations (default 8), but terminates early if the model returns a response without function calls or if the conversation reaches a natural conclusion. The default value serves as a safety limit rather than a guaranteed execution count.
Which method should I use for production conversational agents?
Use run() for production agents requiring robust tool orchestration, as it provides built-in telemetry via _track(), automatic function dispatch from self._functions, and proper state management across multiple turns. Use complete() only when you need to bypass the built-in agentic logic for specialized workflows or when implementing custom orchestration that the standard loop does not support.
Are there performance differences between the two methods?
Yes. complete() incurs lower latency for single requests because it bypasses the preparation logic, loop overhead, and JSON serialization of function results found in run(). However, run() optimizes multi-turn tool workflows by managing the conversation state internally, potentially reducing overall latency for complex agentic tasks compared to manual implementation with multiple complete() calls.
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 →