These libraries are experimental. APIs may change without notice. Generated from source with koruc 0.1.7 on 11/4/2026.

Needle

@korulang/needle@0.0.1

On-device speech transcription via libneedle (Cactus-Compute), behind phantom obligations

needle/index.kz · 5 tors

@korulang/needle — on-device speech transcription via libneedle. · 28 more lines
@korulang/needle — on-device speech transcription via libneedle. Wraps the Cactus-Compute engine (libneedle.dylib, ~1.1MB) behind phantom obligation types. The C ABI is already an obligation shape: needle_load(weights, len) → engine ready; <0 → needle_last_error() needle_transcribe(...) → one-shot PCM → JSON result needle_stream_transcribe_process/stop → chunked live transcription needle_reset() → releases loaded model state Every verb borrows `*Engine<ready>`; transcribing with no engine loaded is a compile error, not a runtime null. `unload` discharges the minted token. Results land in the caller's out-buffer as a JSON C string ({"text","language","ttft_ms","decode_tps"}, + "words" when word_timestamps=1) — the edition hands back that JSON verbatim as `string<std/io:allocated!>`, discharged by `std/io:free`. Field extraction belongs to a caller-side parser (koru/yyjson), not to this seam. PCM is passed as raw little-endian f32 bytes at 16kHz mono (whistle's contract). A typed sample surface can come later; `string` today carries the same bytes without inventing a sample type the toolchain hasn't ruled on. libs/needle:load(path: "whistle.cact") | ready eng |> libs/needle:transcribe(eng, pcm: pcm, language: "en") | ok json |> std/io:print.ln("{{ json:s }}") |> std/io:free(content: json) |> libs/needle:unload(eng) | err e |> std/io:print.ln("{{ e:s }}") |> libs/needle:unload(eng) | err e |> std/io:print.ln("load: {{ e:s }}")

Phantom lifecycles

Derived from the phantom labels in the declarations below — state! issues an obligation the compiler will chase, !state discharges it, a bare state holds it without moving it. Nothing here is hand-drawn.

Engine 1 state ready!
string 2 states allocated!

load

<ready!> index.kz:77

unload

<!ready> index.kz:96

transcribe

<ready><*><std.io:allocated!> index.kz:111

stream.process

<ready><*><std.io:allocated!> index.kz:134

stream.stop

<ready><std.io:allocated!> index.kz:155