Devotd Github
NewInference & serving

tildaslashalef/nuclis — cached prompt error, ASR repetition, minimal reasoning added

The 2026-11-01 commit of nuclis, a local LLM inference engine written in Zig with a Metal backend, addresses the 500 error on fully cached prompts, the word repetition in nemotron-asr-0.6b, and the new reasoning_effort 'minimal' all together.

Repositorytildaslashalef/nuclis ↗

The 2026-11-01 commit of nuclis, a local LLM inference engine written in Zig with a Metal backend, addresses the 500 error on fully cached prompts, the word repetition in nemotron-asr-0.6b, and the new reasoning_effort 'minimal' all together.

GitHub description: Local LLM inference engine in Zig with a Metal backend

stars
★ 1
Forks
0
Language
Zig
License
MIT
Latest release
v0.7.0
Read original source ↗View on GitHub ↗

tildaslashalef/nuclis is a local LLM inference engine written in Zig with a Metal backend. The 2026-11-01 commit (177daa1c, https://github.com/tildaslashalef/nuclis/commit/177daa1cd71b1fd3c4e3271bf360839a5b101df7) handles three problems reported by a single client, Muon. Muon wrote that they confirmed all three in `0.12.0-dev+990ba6c`. The first is the problem where requests whose prompt was entirely cached failed with `500 model_failed EmptyPrompt`. In Gemma 4, a render ending with a tool result had no generation prompt, so the cache-end marker covered the whole request, and resending the same request left no tokens to fill. Now `Completer` keeps the token immediately before the marker, rewinds the session by one slot, and then puts that token back in to obtain logits. In cases where rewinding is not possible (the drafter is loaded and the preceding token is unknown), it resets the session and prefills the entire prompt again.

The second is the problem where `nemotron-asr-0.6b` repeated 'plus' ten times at the end of the final transcript at lookahead 3. It was the result of the RNN-T greedy loop continuously emitting the same word-start piece within a single encoder row. Now, if a word-start piece appears twice in a row within one row, that row is ended. This rule applies only to word-start tokens whose piece begins with U+2581, and repetition of pieces inside a word is allowed. It went into both the CPU's `Decoder.decodeRow` and Metal's `Stream.decode`. In `scripts/api-check.py`, a `check_again` was added that sends the same request three times and checks the cases ending with a user message and with a tool message, respectively.

Third, `reasoning_effort: "minimal"` has appeared. It renders the same way as `low`, but closes at half of the reasoning budget, so with the default reasoning budget of 1,024 tokens it ends at 512 tokens. The measurements attached to the commit come from Muon's actual `tasks_add` requests, sent on `gemma-4-26b-a4b` with four prompt types and four seeds each, immediately after a `none` request. `low` responded with a tool call or a follow-up question in 31 of 32 cases, with a median time of 14–20 seconds. `minimal` scored 28/32 and 10 seconds at 512 tokens, and 20/32 and 5.5 seconds at 256 tokens. At 256, a third of the tool calls disappeared, so the budget was set to 512.

The remaining limits are also written up in the commit. `minimal` still misses tool calls in 4 of 32 cases, roughly one eighth. Muon falls back to `low` in that case, and the first `low` right after a `none` request reuses not a single cache token because the system block differs. This cost has only been documented and has not been fixed yet. As a result of the checks, `make verify-auto` passed, but `make verify` passed only 56 of 71, the remaining 15 being skipped because of models (Muse, Bonsai, Saluki) not present on this machine. On the ASR side as well, the transcripts of 60 clips were unchanged at lookahead 0·3·6·13, and the Metal WER stayed at 6.52·6.11·6.44·6.36%. The CPU WER gate was 8.13%, within the 11% limit. Since this is a fix narrowly scoped to one client's real-world report, the remaining quality gap has to be awaited separately.