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. Both 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
Both versions carry a contrastive head for narrowing a large catalogue before routing. Embeddings are L2-normalised, so similarity is a plain dot product.
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
MIT. The models — architecture, training and weights — are the work of Cactus Compute and are also MIT. If you publish work using them, please cite Needle 2 (arXiv:2607.18363); the entry is in the repository README.
This package is the runtime only.
Metadata
Release files for needle-rs 0.2.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.2.0-cp38-abi3-win_amd64.whl | CPython 3.8 | abi3 | Windows x86-64 | Details |
| needle_rs-0.2.0-cp38-abi3-musllinux_1_2_x86_64.whl | CPython 3.8 | abi3 | Linux musl 1.2+ x86-64 | Details |
| needle_rs-0.2.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.2.0-cp38-abi3-macosx_11_0_arm64.whl | CPython 3.8 | abi3 | macOS 11.0+ ARM64 | Details |
| needle_rs-0.2.0-cp38-abi3-macosx_10_12_x86_64.whl | CPython 3.8 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 2.2 MB
Release files / needle_rs-0.2.0-cp38-abi3-win_amd64.whl
| Download URL | needle_rs-0.2.0-cp38-abi3-win_amd64.whl |
|---|---|
| Size | 370.0 kB |
| Tags | CPython 3.8 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
217d95927f628257a3bf509101d537b7625bb60b1acd10d91026a495d275489e
|
|
BLAKE2b-256 checksum How to use checksums |
b9414dbb9691b71699fe495b351304021cdb3e96ecf73cbf36e7cb3575fae669
|
| 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.2.0-cp38-abi3-musllinux_1_2_x86_64.whl
| Download URL | needle_rs-0.2.0-cp38-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 527.7 kB |
| Tags | CPython 3.8 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
3c53d9b5fb080a26cdd653c3dabef005c8c6098488e715338c95e4f1e5fcf89c
|
|
BLAKE2b-256 checksum How to use checksums |
81eb5e70d03396ced4ef52458346b93ddfeb7ff81cd815f2b68d4f3dfeb7e58b
|
| 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.2.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | needle_rs-0.2.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 466.2 kB |
| Tags | CPython 3.8 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
6d23398ea1e2898dad450e7192cd3609eec6ad9c3c82f00dab7d58ad12b392a0
|
|
BLAKE2b-256 checksum How to use checksums |
18d4d3c15b06b08517509f30f64008a1052c7835e8009dbbd121e9a8546f4904
|
| 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.2.0-cp38-abi3-macosx_11_0_arm64.whl
| Download URL | needle_rs-0.2.0-cp38-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 403.9 kB |
| Tags | CPython 3.8 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
d5c0eb402ccce3ba1fd7f0d27769f7a22250911147f05db82a10b5fef238bd22
|
|
BLAKE2b-256 checksum How to use checksums |
0a1a5a7c6700e5e8ae4238f925401bde1be8e0759f9f9dd3a7d4897258913d96
|
| 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.2.0-cp38-abi3-macosx_10_12_x86_64.whl
| Download URL | needle_rs-0.2.0-cp38-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 434.6 kB |
| Tags | CPython 3.8 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
1936025fa0c94f9076bfa82d26c2f6e66328d26b58b14e832ee636ed01fdbe9f
|
|
BLAKE2b-256 checksum How to use checksums |
036bcaa86b2dc3c7e98fc527a79cf4d09e00e66199ccd1dcff9f21ed0a264c06
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|