# How to Distinguish Between Needle 2 and Needle 3 Generations in Cactus Compute

> Easily distinguish Needle 2 vs Needle 3 generations in Cactus Compute. Learn about archive tags, environment variables, and the _generation attribute for seamless compatibility.

- Repository: [Cactus Compute, Inc./needle](https://github.com/cactus-compute/needle)
- Tags: deep-dive
- Published: 2026-09-12

---

**Needle 2 and Needle 3 are distinguished by a 4-byte archive tag in `.cact` weight files, runtime environment variables, and the internal `_generation` attribute, with the Python package automatically selecting the correct native engine to prevent cross-generation incompatibility.**

The cactus-compute/needle repository maintains two separate generations of its foundation model, each with distinct capabilities and binary requirements. When you load a model or initialize a `Needle` instance, the package inspects multiple signals to determine which generation is active, ensuring that generation-specific features like audio processing remain incompatible with wrong-version archives.

## Archive Format Tag Detection

Every `.cact` weight archive begins with a 4-byte magic tag that maps directly to its generation. In [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), the `_CACT_GENERATIONS` dictionary (lines 23-26) defines these mappings:

- **Generation 2**: Tag `0x05E12A83`
- **Generation 3**: Tag `0x05E12A84`

The `_weight_generation()` function (lines 29-40) reads the first four bytes of any supplied archive and returns the corresponding integer generation. This detection runs automatically during `Needle` initialization before any native code loads.

```python
import pathlib
from needle import _weight_generation

archive = pathlib.Path("my_model.cact")
gen = _weight_generation(archive)  # Returns 2 or 3

print(f"Detected Needle generation: {gen}")

```

If you attempt to load a generation 3 archive, the package immediately knows to prepare the Needle 3 engine pipeline.

## Runtime Generation Identification

Once initialized, every `Needle` instance exposes its generation through the `_generation` attribute. When you instantiate the class without providing weights, it defaults to generation 2, reflecting the historic compact 14 MB binary format.

```python
from needle import Needle

# Default initialization (no weights)

needle2 = Needle()
print(needle2._generation)  # Output: 2

# Explicit generation 3 archive

needle3 = Needle(weights="model_v3.cact")
print(needle3._generation)  # Output: 3

```

This attribute drives all subsequent capability checks throughout the API implementation.

## Native Library Path Dispatch

The package dynamically selects the appropriate shared library based on the detected generation. The `_library_path()` function (lines 43-73 in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py)) constructs candidate library names (`libneedle2.so` or `libneedle3.so`) and checks environment variables before falling back to the cached engine.

You can override the library selection using generation-specific environment variables:

- `NEEDLE2_LIB_PATH` – Path to the generation 2 native library
- `NEEDLE3_LIB_PATH` – Path to the generation 3 native library
- `NEEDLE_LIB_PATH` – Historic alias that only affects generation 2

```python
import os
import needle

# Force a specific generation 3 library location

os.environ["NEEDLE3_LIB_PATH"] = "/opt/custom/libneedle3.so"
model = needle.Needle(weights="audio_model.cact")

```

## API Capability Constraints

Runtime checks throughout [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) enforce generation-specific limitations. Functions requiring generation 3 capabilities—such as `embed()` (lines 64-68) and internal methods like `_complete()` (lines 30-34)—validate the instance's `_generation` attribute before executing.

If you invoke a generation 3 feature on a generation 2 instance, the API raises a `ValueError` immediately. Conversely, generation 2-specific features (like the compact binary format) are unavailable when a generation 3 archive is loaded.

```python
from needle import Needle

# Generation 2 instance lacks embedding support

legacy = Needle()
try:
    legacy.embed("text sample")
except ValueError as e:
    print("Embeddings require Needle 3")  # Error raised here

```

## Managing Binaries via CLI

The `needle fetch` command downloads generation-specific binaries into isolated cache directories. Use the `--generation` flag to explicitly target one version:

```bash

# Download Needle 2 engine to ~/.cache/cactus-needle/v2/

needle fetch --generation 2

# Download Needle 3 engine to ~/.cache/cactus-needle/v3/

needle fetch --generation 3

```

This ensures the correct native library is available before you instantiate any models, as documented in [`doc/apis.md`](https://github.com/cactus-compute/needle/blob/main/doc/apis.md) around line 127.

## Summary

- **Archive inspection**: The `_weight_generation()` helper reads 4-byte tags (`0x05E12A83` for generation 2, `0x05E12A84` for generation 3) from `.cact` files to determine compatibility.
- **Instance attribute**: Query `needle._generation` to identify which engine is active at runtime.
- **Library control**: Set `NEEDLE2_LIB_PATH` or `NEEDLE3_LIB_PATH` environment variables to override default library discovery in `_library_path()`.
- **Feature gates**: Generation 3 features (audio, embeddings) raise `ValueError` when called on generation 2 instances, preventing silent failures.
- **Binary isolation**: Use `needle fetch --generation` to cache version-specific native engines in separate directories.

## Frequently Asked Questions

### How can I verify which generation my `.cact` file uses without loading it?

You can import the internal `_weight_generation` function from [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) and pass it a `Path` object to your archive. It returns `2` or `3` based on the 4-byte header tag without initializing the full engine.

### What happens if I try to use audio features on a Needle 2 model?

The API raises a `ValueError` during the method call. Generation 2 archives lack the necessary tensor layouts for audio processing, and the runtime checks in methods like `_complete()` explicitly block these operations when `self._generation == 2`.

### Can I force Needle to use generation 3 without providing a weights file?

No. Without a `.cact` archive, the `Needle` constructor defaults to generation 2. To use generation 3 capabilities, you must supply a valid generation 3 archive, which triggers the tag detection logic and sets `self._generation = 3`.

### Where does Needle store the downloaded native libraries for each generation?

The `needle fetch` command stores binaries under `~/.cache/cactus-needle/v2/` for generation 2 and `~/.cache/cactus-needle/v3/` for generation 3. The `_library_path()` function searches these directories automatically when resolving native library paths.