Needle `agent.complete()` Response Structure Explained: A Complete Guide
agent.complete() returns a Python dictionary with a type key indicating whether the response is a plain text completion ("final") or a tool invocation request ("call"), plus additional fields like text, function_calls, or confidence depending on context.
This guide breaks down the exact structure of the response object from the Needle inference engine, based on the source code in the cactus-compute/needle repository. Understanding this structure is essential for building reliable applications on top of the Needle agent framework.
Core Response Structure
The Needle.complete() method is implemented in needle/__init__.py as a thin wrapper around the native C++ engine. Internally, it calls the private _complete() method, which:
- Sends the prompt to the C++ engine via
needle_complete - Receives a JSON-encoded envelope in a fixed-size buffer
- Parses the buffer with
json.loads - Optionally injects a
"confidence"key when custom weights are loaded
The result is a flat Python dictionary with the following guaranteed and optional fields.
Guaranteed Fields
Every response contains at least these keys:
| Key | Type | Description |
|---|---|---|
type |
str |
Response category: "final" for text completions or "call" for tool invocations |
Conditional Fields Based on Response Type
Depending on type, additional fields appear:
text(str): The generated text, present only whentype == "final"function_calls(list[dict]): Tool invocations, present only whentype == "call". Each element contains:name— the tool name as defined by the@tooldecoratorarguments— a dictionary of parameter values
Fields Added by the Wrapper
confidence(null): Inserted only when a fine-tuned weight file is loaded via theweightsparameter. The current engine does not emit calibrated scores, so this is alwaysNone
Engine-Passthrough Fields
The C++ engine may include additional metadata such as usage, model, or other diagnostic keys. These are passed through unchanged without validation or transformation.
Code Examples: Working with agent.complete() Responses
Basic Text Completion
The simplest case returns "final" with generated text in the text field:
from needle import Needle
agent = Needle()
resp = agent.complete("Write a short poem about rain.")
print(resp["type"]) # → "final"
print(resp["text"]) # → the generated poem
Handling Tool Invocation Responses
When the model decides to use a registered tool, type becomes "call":
from needle import Needle, tool
@tool
def send_email(to: str, subject: str, body: str):
return {"status": "sent"}
agent = Needle(tools=[send_email])
resp = agent.complete("Please email the team the summary of today's meeting.")
print(resp["type"]) # → "call"
print(resp["function_calls"]) # → [{'name': 'send_email', 'arguments': {'to': '...', 'subject': '...', 'body': '...'}}]
Processing Function Calls Manually
To execute tool calls and optionally feed results back to the model:
def handle_calls(calls):
results = []
for call in calls:
fn = agent._functions[call["name"]]
results.append(fn(**call.get("arguments", {})))
return results
if resp["type"] == "call":
tool_results = handle_calls(resp["function_calls"])
# Pass results back for multi-turn tool use
Fine-Tuned Weights Response
Loading custom weights triggers injection of the confidence key:
agent = Needle(weights="my_finetuned.cact")
resp = agent.complete("Summarize the article.")
print(resp.get("confidence")) # → None
print("confidence" in resp) # → True (key is explicitly added even though value is null)
Source Code Reference
The response structure is defined and manipulated in these locations:
needle/__init__.py(lines 24-38): ImplementsNeedle.complete(), parses the JSON envelope fromneedle_complete, and conditionally addsconfidencewhenself._weightsis setneedle/agent/tools.py: Defines the@tooldecorator and schema generation that determines howfunction_callsentries are structuredtests/test_inference.py: Unit tests validatingtype,text, andfunction_callspresence and typestests/test_weights.py: Tests confirmingconfidencekey injection when custom weights are loaded
Summary
agent.complete()returns a plain Python dictionary, not a custom class- Always check
resp["type"]to branch between"final"(useresp["text"]) and"call"(useresp["function_calls"]) function_callsis a list of dictionaries withnameandargumentskeysconfidenceis alwaysNonewhen present; it signals a fine-tuned model is loaded but carries no score data- Additional engine keys (
usage,model, etc.) may appear and should be treated as opaque metadata
Frequently Asked Questions
How do I know if the response contains generated text or a tool call?
Check resp["type"]. If it equals "final", read resp["text"]. If it equals "call", process resp["function_calls"]. The Needle engine never returns both simultaneously.
What is the exact structure of items in function_calls?
Each element is a dictionary with two keys: name (string, the tool's registered name) and arguments (dictionary mapping parameter names to values). This matches the JSON schema generated by the @tool decorator in needle/agent/tools.py.
Why is confidence always None?
The current C++ engine in cactus-compute/needle does not compute calibrated confidence scores for fine-tuned models. The wrapper adds the key anyway for API compatibility and future extension, as implemented in needle/__init__.py lines 34-36.
Can I rely on specific keys beyond type, text, and function_calls?
No. Keys like usage or model are passed through directly from the engine without guarantees. Only the fields documented above are contractually stable; treat engine-specific metadata as diagnostic output subject to change.
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 →