--- license: apache-2.0 language: - en library_name: onnx pipeline_tag: audio-classification tags: - function-calling - tool-calling - spoken-language-understanding - onnx - on-device extra_gated_fields: Affiliation (Company or Academic Institution): text Role: text --- # NeuDecide
**A tiny voice-action model. Audio and a list of tools in, a tool call out. No transcript in between.** [GitHub](https://github.com/neuphonic/neudecide) · [PyPI](https://pypi.org/project/neudecide/) · [HF Space](https://huggingface.co/spaces/neuphonic/neudecide) · arXiv (coming soon) NeuDecide converts speech directly into function calls. It's 43 MB, can run on a single thread via ONNX Runtime, and needs no GPU or internet connection once downloaded. Tools are passed in as JSON schemas at inference time, so you change what the model can do by changing the JSON, not by retraining. ``` "clean the bathroom" + [cleanRoom, goToBase, getBattery] → [{"name": "cleanRoom", "arguments": {"room": "bathroom"}}] ``` ## Installation ```bash pip install neudecide ``` Requires Python 3.10 – 3.13. The model weights are downloaded from the Hugging Face Hub on first use and cached after that. ## Quickstart This example turns a recorded voice command into a call for a robot vacuum. Save a WAV recording of yourself saying something like "clean the bathroom" as `command.wav`, then run: ```python from neudecide import NeuDecide TOOLS = [ { "name": "cleanRoom", "description": "Vacuum-cleans one room.", "parameters": { "type": "object", "properties": { "room": { "type": "string", "enum": [ "living room", "bathroom", "kitchen", "bedroom", ], }, }, "required": ["room"], }, }, { "name": "goToBase", "description": "Sends the vacuum back to its charging base.", "parameters": { "type": "object", "properties": {}, }, }, { "name": "getBattery", "description": "Reports the vacuum's battery level.", "parameters": { "type": "object", "properties": {}, }, }, ] def main(): model = NeuDecide.from_pretrained() calls = model.generate("command.wav", TOOLS) print(calls) if __name__ == "__main__": main() ``` Output: ``` [{'name': 'cleanRoom', 'arguments': {'room': 'bathroom'}}] ``` `generate` returns a list of `{"name", "arguments"}` dicts, or `[]` when no tool applies. If the answer reaches the 128-token output limit, you get the calls completed before it, with a warning. A command-line version is in [`examples/basic_example.py`](https://github.com/neuphonic/neudecide/blob/main/examples/basic_example.py). From a clone of the GitHub repo, `python examples/basic_example.py` runs it on `examples/command.wav` with the tools in `examples/tools.json`; pass `--audio` and `--tools` to use your own. ## Usage ### Audio input `audio` can be a path to a PCM WAV file or a NumPy array. Arrays may be mono `(samples,)` or multi-channel `(samples, channels)`; channels are averaged to mono. Integer arrays, such as the int16 samples `scipy.io.wavfile.read` returns, are scaled to [-1, 1] automatically; float arrays should already be in [-1, 1]. Audio is resampled to 16 kHz automatically, so pass `sample_rate=` if your array is at a different rate: ```python calls = model.generate("command.wav", TOOLS) # WAV file calls = model.generate(waveform, TOOLS, sample_rate=44100) # NumPy array ``` Only WAV is read natively. For other formats (MP3, FLAC, OGG), load the audio yourself and pass the array. Audio longer than 30 seconds is truncated. ### Tool definitions Each tool has a `name`, a `description`, and `parameters` as a JSON Schema object. You can pass the list directly or as a JSON string. The model only knows what you tell it about each tool, so clear names and descriptions do most of the work. When an argument has a fixed set of values, like the rooms above, list them with `enum`. ## How it works NeuDecide is a pre-trained streaming English speech encoder joined to a compact tool-calling encoder–decoder, initialised from [Needle](https://github.com/cactus-compute/needle), and trained end to end as one system. Inference runs three ONNX graphs: | Graph | Runs | Does | Parameters | Size | |---|---|---|---|---| | `audio_encoder` | Once per utterance | Waveform → 12.5 Hz latent frames | 28.7M | 22.5 MB | | `tool_encoder` | Once per utterance and tool list | Audio frames + tool tokens → cross-attention keys and values | 11.8M | 10.1 MB | | `decoder_step` | Once per output token | Next token, reusing a cached self-attention state | 15.0M | 10.0 MB | | | | |---|---| | Model width | 512 | | Decoder layers | 8 | | Attention | Grouped-query, 4 key/value heads of 64 dimensions | | Vocabulary | 8,192 SentencePiece BPE tokens | | Sample rate | 16 kHz | | Limits | 30 s of audio, 1,536 tool tokens, 128 output tokens | Tools are serialised exactly as Python's `json.dumps` writes them by default, matching how the model was trained. ## Results