# How to Integrate Needle 2 into Your Python Project: A Complete Implementation Guide

> Integrate Needle 2 into your Python project with this guide. Learn to install cactus-needle, define tools using @needle.tool, and run offline inference loops with the Needle agent.

- Repository: [Cactus Compute, Inc./needle](https://github.com/cactus-compute/needle)
- Tags: how-to-guide
- Published: 2026-09-02

---

**Integrate Needle 2 into your Python project by installing the `cactus-needle` package, defining tools with the `@needle.tool` decorator, and instantiating the `Needle` agent to run offline inference loops.**

Needle 2 is a local-first agentic inference engine developed by `cactus-compute/needle` that executes Python functions through a model-driven tool-selection loop without network dependencies after initial setup. This guide covers the complete integration workflow from installation to production deployment using the exact APIs implemented in the source code.

## Installation and Dependencies

Install the base inference engine from PyPI. The package name is **`cactus-needle`**, which provides the top-level `needle` module.

```bash
pip install cactus-needle

```

For advanced use cases, install optional extras based on your hardware requirements:

- **`pip install "cactus-needle[train]"`** — Enables LoRA fine-tuning capabilities
- **`pip install "cactus-needle[train,gpu]"`** — Adds GPU acceleration for training workloads  
- **`pip install "cactus-needle[train,metal]"`** — Apple Silicon (Metal) support for training

The engine automatically downloads a **14 MB shared library** from Hugging Face on first use and caches it at `~/.cache/cactus-needle/`. After this initial fetch, all inference runs completely offline.

## Core Architecture and Source Files

Understanding the main components helps navigate the codebase effectively. The public API is centralized in **[`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py)**, while tool handling resides in **[`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py)**.

| Component | Source Location | Purpose |
|-----------|----------------|---------|
| **`needle.Needle` class** | [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 56-73 constructor, 75-99 binding) | Creates the agent, manages the inference engine, and loads optional `.cact` tuned weights |
| **`@needle.tool` decorator** | [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) | Converts Python functions into JSON-schema tools the model can invoke |
| **`needle.Field`** | [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) | Defines per-argument constraints (range, pattern, enum) compiled into the decode grammar |
| **`agent.run()`** | [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 39-61) | High-level agentic loop that auto-executes tools and aggregates results |
| **`agent.complete()`** | [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 19-31) | Single-turn completion for manual loop control |
| **`needle.extract()`** | [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (line 79) | One-shot structured extraction using Pydantic models |

## Defining Tools and Creating the Agent

### Step 1: Decorate Python Functions as Tools

Use the **`@needle.tool`** decorator to expose Python functions to the model. The system automatically generates JSON schemas from type hints and docstrings.

```python
import needle
from typing import Literal, Annotated

@needle.tool
def set_thermostat(
    temperature: int,
    mode: Literal["heat", "cool", "auto"] = "auto"
):
    """Set the thermostat.

    Args:
        temperature: target temperature in Celsius
        mode: heating strategy to use
    """
    return {"temperature": temperature, "mode": mode}

```

Add optional constraints using **`needle.Field`** for arguments requiring validation:

```python
@needle.tool
def set_volume(
    level: Annotated[int, needle.Field(ge=0, le=100)]
):
    """Set volume level."""
    return {"level": level}

```

### Step 2: Instantiate the Needle Agent

Create a `Needle` instance by passing your tool list and optional system facts. According to the source in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), the constructor accepts a `system` string for context (date, device, battery) and an optional `weights` path to a tuned `.cact` file.

```python
agent = needle.Needle(
    tools=[set_thermostat],
    system="date: 2026-09-02; device: laptop; battery: 85%"
)

```

## Executing Inference

### Automatic Agentic Loop with run()

The **`agent.run()`** method implements the full inference loop defined in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) lines 39-61. It selects tools, executes Python functions, feeds results back to the model, and returns aggregated results.

```python
response = agent.run("Make it 22 degrees and cool the room")
print(response["results"])

# → [{'temperature': 22, 'mode': 'cool'}]

```

### Single-Turn Control with complete()

For manual loop management, use **`agent.complete()`** (implemented in lines 19-31 of [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py)). This executes exactly one model inference turn without automatic tool execution.

```python
result = agent.complete("What tools are available?")

```

### Structured Extraction with extract()

For one-shot data extraction without defining explicit tools, use **`needle.extract()`**. This shortcut accepts a Pydantic model and returns validated instances.

```python
from pydantic import BaseModel

class Invoice(BaseModel):
    vendor: str
    total: float
    due_date: str

invoice = needle.extract(
    "Invoice from Acme Corp, $1,200.00, due 2026-10-01",
    Invoice
)
print(invoice)

# → vendor='Acme Corp' total=1200.0 due_date='2026-10-01'

```

## Leveraging Pre-Built Environments

The repository ships with ready-made environment modules in **`needle/environments/`** that bundle curated toolsets for common domains. Import these directly to skip tool definition.

```python
from needle.environments import smart_home

# Run inference using the built-in smart home toolset

smart_home.agent.run("Dim the study lights to 30 percent")

# Validate environment integrity

smart_home.run_tests()

```

Available environments include `smart_home` and `media_player`, documented in [`doc/environments.md`](https://github.com/cactus-compute/needle/blob/main/doc/environments.md).

## Fine-Tuning and Custom Weights

To integrate a custom fine-tuned model, first complete the LoRA training workflow described in [`doc/finetuning.md`](https://github.com/cactus-compute/needle/blob/main/doc/finetuning.md), then build a `.cact` archive:

```bash
needle build checkpoints/base.pkl --lora checkpoints/needle_lora.pkl --out my_needle.cact

```

Load the tuned weights by passing the file path to the `Needle` constructor:

```python
agent = needle.Needle(
    tools=[set_thermostat],
    weights="my_needle.cact"
)

```

## Summary

- **Install** the `cactus-needle` package with optional `[train]`, `[gpu]`, or `[metal]` extras for hardware-specific capabilities
- **Define tools** using `@needle.tool` in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) style, with optional `needle.Field` constraints for validation grammar
- **Create agents** via `needle.Needle` class in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), passing tool lists and system context strings
- **Execute** using `agent.run()` for automatic loops, `agent.complete()` for single-turn control, or `needle.extract()` for Pydantic-based extraction
- **Deploy offline** after the engine binary caches at `~/.cache/cactus-needle/`, enabling inference without network traffic
- **Extend** with pre-built environments from `needle/environments/` or custom `.cact` weights from LoRA fine-tuning

## Frequently Asked Questions

### What is the difference between agent.run() and agent.complete()?

**`agent.run()`** implements the full agentic loop defined in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) lines 39-61, automatically selecting tools, executing Python functions, and feeding results back until the model returns a final response. **`agent.complete()`** (lines 19-31) performs a single inference turn without executing tools or managing conversation state, giving you manual control over the interaction flow.

### How do I constrain tool parameters to specific values?

Use **`needle.Field`** (implemented in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py)) to add per-argument constraints such as `ge` (greater than or equal), `le` (less than or equal), `pattern` (regex), or `enum`. These constraints compile into the model's decode grammar, ensuring valid outputs without post-validation.

### Can Needle 2 run completely offline?

Yes. After the initial installation, the engine downloads a **14 MB binary** from Hugging Face and caches it at `~/.cache/cactus-needle/`. According to the "Offline devices" section in [`doc/apis.md`](https://github.com/cactus-compute/needle/blob/main/doc/apis.md), all subsequent inference runs locally without network traffic, making it suitable for air-gapped or mobile deployments.

### How do I deploy a fine-tuned model in production?

First, complete the LoRA fine-tuning workflow documented in [`doc/finetuning.md`](https://github.com/cactus-compute/needle/blob/main/doc/finetuning.md) to generate a LoRA checkpoint. Then use the CLI command `needle build` to package the base weights and LoRA adapter into a single `.cact` file. In your Python project, pass the path to this file as the `weights` parameter when instantiating `needle.Needle`, as implemented in the constructor at [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) lines 56-73.