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.1
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.1-cp38-abi3-win_amd64.whl | CPython 3.8 | abi3 | Windows x86-64 | Details |
| needle_rs-0.2.1-cp38-abi3-musllinux_1_2_x86_64.whl | CPython 3.8 | abi3 | Linux musl 1.2+ x86-64 | Details |
| needle_rs-0.2.1-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.1-cp38-abi3-macosx_11_0_arm64.whl | CPython 3.8 | abi3 | macOS 11.0+ ARM64 | Details |
| needle_rs-0.2.1-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.1-cp38-abi3-win_amd64.whl
| Download URL | needle_rs-0.2.1-cp38-abi3-win_amd64.whl |
|---|---|
| Size | 370.4 kB |
| Tags | CPython 3.8 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
2d03d3e9fdff56668e3755a84ab8095c54ff3c1328e4a3d027338edfb6b65957
|
|
BLAKE2b-256 checksum How to use checksums |
834da297dd9329bcd43a41a9fd2c064e67811b98fdb9c58b0793a93d5399f7bd
|
| 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.1-cp38-abi3-musllinux_1_2_x86_64.whl
| Download URL | needle_rs-0.2.1-cp38-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 527.3 kB |
| Tags | CPython 3.8 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
e4be0c00d421ae80da43b9b8fc173528e2789b6199577d0ba26f4aea40eae1f7
|
|
BLAKE2b-256 checksum How to use checksums |
2662ecbadd05dc74ba17d42bace0a8f7dc70a20b643049c76c2b416a800da6ef
|
| 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.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | needle_rs-0.2.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 465.9 kB |
| Tags | CPython 3.8 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
046c340e65a97930d3d92591008e71101b71554aaed48cb2a4a3be27cef872aa
|
|
BLAKE2b-256 checksum How to use checksums |
3a059c3258b4102bf5b901f0445e0b6e9e95ab9c56ea728817827ed962ca5928
|
| 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.1-cp38-abi3-macosx_11_0_arm64.whl
| Download URL | needle_rs-0.2.1-cp38-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 403.6 kB |
| Tags | CPython 3.8 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
a88443d5e386c13ea9763737668b71ff2931000867bbad2e67bd37d1a092f73a
|
|
BLAKE2b-256 checksum How to use checksums |
4f06ac8fd8bdf00ca37dc6de1d6c0c6615fab3b3d8f67059a22264c628b36e1e
|
| 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.1-cp38-abi3-macosx_10_12_x86_64.whl
| Download URL | needle_rs-0.2.1-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 |
c20d6528f44e7cbd498d8af3608f9057ee8811f92bbeb197892a82ee82a19580
|
|
BLAKE2b-256 checksum How to use checksums |
9ce28c7a89dbf9e2ff70676f537426a4e581e66696147c6f0ff8619caa12daaf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|