needle-rs
Local tool calling with no server, no API key and no GPU. A compiled Rust extension — importing it costs milliseconds, not the seconds a JAX or PyTorch import takes, and it pulls in no Python ML dependencies at all.
This is the Python binding for needle-rs, a pure-Rust runtime for Cactus Compute's Needle tool-calling models. All three model generations are supported, and output is verified token-exact against the upstream JAX reference.
pip install needle-rs
Needle v2
One .cact file carries the weights, the geometry and the tokenizer.
from needle_rs import V2Engine
engine = V2Engine.load("weights/needle2.cact")
tools = """[{"name":"get_weather","description":"Get current weather for a city",
"parameters":{"type":"object","properties":{"city":{"type":"string"}},
"required":["city"]}}]"""
query = "What's the weather in Paris?"
out = engine.run(query, tools)
# <tool_call>[{"name":"get_weather","arguments":{"city":"Paris"}}]</tool_call>
# Gate execution on the model's confidence in the answer it just gave.
p = engine.confidence_for(query, tools, out)
if p is not None and p >= 0.5:
... # act on the call
else:
... # escalate
The confidence head scores a judgement already made, so confidence_for needs
the completion. Passing a bare query to the lower-level confidence() reads
near zero however answerable the query is.
Grammar-constrained decoding restricts the payload to the declared schema — valid tool names and argument keys only:
engine.generate(query, tools, max_new_tokens=96, temperature=0.0, constrain=True)
Temperature above zero samples, and seed makes that reproducible.
Needle v1
from needle_rs import NeedleEngine
engine = NeedleEngine.load("weights/needle.safetensors", "weights/vocab.txt")
result = engine.run("Book a flight from London to JFK tomorrow", tools)
engine.run_stream(query, tools, lambda tid, piece: print(piece, end="", flush=True))
engine.run_batch([(query1, tools1), (query2, tools2)])
v1 post-processes its output: the <tool_call> marker is stripped and your
original tool-name casing restored, so with run_stream the streamed pieces are
a progress view and the returned string is the answer.
Tool retrieval
Needle 2 and Needle 1 carry a contrastive head for narrowing a large catalogue
before routing. Embeddings are L2-normalised, so similarity is a plain dot
product. Needle 3 does not — it exports a confidence head and nothing else,
so V3Engine has no retrieve_tools.
engine.retrieve_tools(
"What's the weather in Paris?",
["Get current weather for a city", "Book a flight", "Send an email"],
top_k=2,
)
# [(0, 0.897), (2, 0.547)] — (index, score), descending
Weights
Weights are not bundled — download them once:
| Version | Files | Size | Source |
|---|---|---|---|
| v2 | needle2.cact |
13.7 MB | Cactus-Compute/needle2 |
| v1 | needle.safetensors + vocab.txt |
22 MB + 122 KB | Abdalrahman/needle-rs-safetensors |
from huggingface_hub import hf_hub_download
cact = hf_hub_download("Cactus-Compute/needle2", "needle2.cact")
Notes
- Needle is a tool-calling router, not a chat model: one query plus tool definitions in, one JSON call out. It will not produce useful free-form text.
- Single-shot. No multi-turn dialogue and no reasoning over tool results — your application executes the call and decides what to do with the response.
- English-trained; multilingual behaviour is not evaluated upstream.
- Constrained decoding guarantees syntactic validity, not semantic correctness.
Credit and license
This package is MIT. The models — architecture, training and weights — are the work of Cactus Compute and carry their own terms: Needle 3's weights are Apache-2.0, Needle 2's and Needle 1's are MIT, and the upstream repository is Apache-2.0. Check the licence on the generation you ship. If you publish work using them, please cite Needle (arXiv:2607.18363); the entry is in the repository README.
This package is the runtime only.
Metadata
Release files for needle-rs 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| needle_rs-0.3.0-cp38-abi3-win_amd64.whl | CPython 3.8 | abi3 | Windows x86-64 | Details |
| needle_rs-0.3.0-cp38-abi3-musllinux_1_2_x86_64.whl | CPython 3.8 | abi3 | Linux musl 1.2+ x86-64 | Details |
| needle_rs-0.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.8 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| needle_rs-0.3.0-cp38-abi3-macosx_11_0_arm64.whl | CPython 3.8 | abi3 | macOS 11.0+ ARM64 | Details |
| needle_rs-0.3.0-cp38-abi3-macosx_10_12_x86_64.whl | CPython 3.8 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 2.6 MB
Release files / needle_rs-0.3.0-cp38-abi3-win_amd64.whl
| Download URL | needle_rs-0.3.0-cp38-abi3-win_amd64.whl |
|---|---|
| Size | 451.3 kB |
| Tags | CPython 3.8 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
44b9be4e9f9ddcb751a23204b9eff82bcdbfb15323dfc4bddc0dec6eac6a7ffb
|
|
BLAKE2b-256 checksum How to use checksums |
590719415f8e5e470d853b762a016824a0837f9ccd8238061e3094dee4ec3064
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / needle_rs-0.3.0-cp38-abi3-musllinux_1_2_x86_64.whl
| Download URL | needle_rs-0.3.0-cp38-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 605.3 kB |
| Tags | CPython 3.8 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
d1258ab1876cce1decd1e4bddf0c79074b8a20ff04b87a04b9fa6e71270ed2f4
|
|
BLAKE2b-256 checksum How to use checksums |
a8a35f6d9bb5fe21ade3ae0dd210479b08ba36f741fe7c1dd1a08947ac0440de
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / needle_rs-0.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | needle_rs-0.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 543.4 kB |
| Tags | CPython 3.8 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
aef5305b7c795700bd0d0ab29221b9cc9d49856550e53c2fc0b6fcd0822401f2
|
|
BLAKE2b-256 checksum How to use checksums |
27a68b5321cb4e141f8a3e81835a2c1782908c396c3b02d79877c6d13af37222
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / needle_rs-0.3.0-cp38-abi3-macosx_11_0_arm64.whl
| Download URL | needle_rs-0.3.0-cp38-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 474.2 kB |
| Tags | CPython 3.8 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
e000b5d7a4cdb6bee715ec10e6306197816cb25ef21ffa553b222c0328ddaf8b
|
|
BLAKE2b-256 checksum How to use checksums |
c2a8a0ffc5e84354902b651b12f59de46efa9a73d318217bee30d8c39eeb32b2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / needle_rs-0.3.0-cp38-abi3-macosx_10_12_x86_64.whl
| Download URL | needle_rs-0.3.0-cp38-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 509.0 kB |
| Tags | CPython 3.8 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
da590e094ec1ccd756021bb114fb3d6570cad23edd7b93abca3a63c3d84e6167
|
|
BLAKE2b-256 checksum How to use checksums |
2136456b945fc25f58b6a396c76125cf83562afe6b3aed0835175ea1608c1d7b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|