Needle Agent Complete Calls Response Contract: JSON Schema and Core Guarantees

The agent.complete() method returns a deterministic, single JSON object constrained by byte‑level grammar, enforcing a strict schema that includes decision metadata, tool arguments, reasoning traces, and performance metrics.

The cactus-compute/needle repository implements a structured agent framework where the complete method serves as the primary inference interface. According to the API specification in doc/apis.md, the response contract guarantees machine‑parseable output without free‑text generation, enabling reliable tool orchestration in production environments.

Response Schema and Field Definitions

The JSON payload returned by agent.complete() contains the following fields as defined in doc/apis.md (lines 84‑96):

  • type: A string indicating the decision mode. Value is "call" when selecting tools or "respond" when generating final output.
  • success: Boolean indicating whether the model produced a syntactically valid tool call.
  • error and error_code: Populated only when success is false, describing why the generation failed.
  • function_calls: An array of one or more objects, each containing:
    • name: The tool name as declared in the agent configuration.
    • arguments: A JSON object with concrete parameter values matching the tool signature.
  • reasoning: Human‑readable trace explaining how each argument was derived from the input context.
  • confidence: A calibrated float between 0 and 1 representing the model’s certainty in the call; used for gating decisions.
  • Performance metrics: prefill_tps, decode_tps, and peak_ram_mb provide tokens‑per‑second and memory utilization data.

Core Contract Guarantees

The Needle framework enforces four immutable rules that constitute the response contract, ensuring predictable behavior across all inference calls.

Exactly One JSON Object Per Turn

The byte‑level grammar constrains the model to emit a single, well‑formed JSON object per invocation. As implemented in the framework core referenced in doc/apis.md (lines 82‑96), the output never includes free‑text prefixes or suffixes that would break parsing pipelines.

Empty Call Array for Off‑Topic Inputs

When user input does not match any declared tool signature, the contract mandates an empty function_calls array ([]). This explicit "no action" signal prevents hallucinated tool invocations and allows the application layer to implement fallback logic (refer to doc/apis.md, lines 101‑104).

Final Response Signaling

A turn that terminates the interaction returns "type": "respond" alongside an empty function_calls array. When using the higher‑level run() method, aggregated tool results appear under the results key, but complete() specifically signals completion through this type marker (see doc/apis.md, lines 106‑108).

Deterministic Schema Compilation

The output JSON schema derives directly from declared tool signatures in needle/__init__.py and doc/environments.md. The grammar engine compiles these signatures into constrained generation rules, preventing the emission of malformed JSON or arguments not present in the input schema.

Practical Implementation Examples

The following example demonstrates handling the three primary response variants: successful tool calls, off‑topic inputs, and manual loop control.

import json
import needle

@needle.tool
def set_lights(room: str, on: bool, brightness: int = 0):
    """Turn lights on/off and set brightness."""
    return {"room": room, "on": on, "brightness": brightness}

agent = needle.Needle(tools=[set_lights])

# 1️⃣ Successful tool selection

resp = agent.complete("dim the living room to 30")
print(json.dumps(resp, indent=2))

# Output includes:

# {

#   "type": "call",

#   "success": true,

#   "function_calls": [

#     {"name": "set_lights",

#      "arguments": {"room": "living room", "on": true, "brightness": 30}}

#   ],

#   "reasoning": "'living room' → room; 'dim' → on true, brightness 30",

#   "confidence": 0.94,

#   ...

# }

# 2️⃣ Off-topic input returns empty calls

off_topic = agent.complete("tell me a joke")
print(off_topic["function_calls"])  # → []

# 3️⃣ Manual execution loop

first = agent.complete("set the kitchen lights on")
if first["type"] == "call":
    result = set_lights(**first["function_calls"][0]["arguments"])
    second = agent.complete(json.dumps(result))
    # second["type"] may be "respond" when the conversation concludes

Summary

The response contract for Needle agent complete calls provides a robust foundation for deterministic tool‑using agents:

  • Single JSON object: Grammar‑enforced output eliminates parsing ambiguity.
  • Explicit signaling: type, success, and function_calls clearly indicate decision states.
  • Rich metadata: Reasoning traces and confidence scores enable audit and gating workflows.
  • Performance visibility: Built‑in metrics track inference efficiency.
  • Schema alignment: Output strictly matches declared tool signatures from needle/__init__.py.

Frequently Asked Questions

What happens when the model fails to generate valid syntax?

When the model produces syntactically invalid JSON or violates the schema, the success field returns false and the error and error_code fields populate with diagnostic information. This allows the application to catch generation failures before attempting to execute non‑existent tools.

How does Needle handle queries unrelated to available tools?

For off‑topic inputs that do not match any declared tool signature, the contract guarantees an empty function_calls array ([]) while maintaining success: true. This explicit "no operation" response prevents hallucinated invocations and signals the application to handle the query through alternative means.

What distinguishes type: "call" from type: "respond"?

The type field indicates the conversation phase. "call" signifies that the model has selected one or more tools and populated the function_calls array. "respond" indicates the model has determined that no further tool calls are necessary, typically returning an empty function_calls array and potentially aggregating previous results when using the run() wrapper.

How is the confidence score calibrated and used?

The confidence field contains a calibrated float between 0 and 1 representing the model’s certainty in its tool selection and argument binding. Developers can use this value to implement gating logic, requiring human approval for calls falling below a specified threshold, or to route low‑confidence decisions to fallback handlers.

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 →