Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.63.3] — 2026-10-28

Against engine **0.62.0**, transformers-only and additive. The Android runtime (`runtime-iree-android`) gains an
external-decoder hook for streaming Moonshine, next to 0.63.2's external encoder; it still depends on nothing but the
Kotlin standard library.

### Added

- **Moonshine streaming: external decoder hook** (`moonshine_stream_jni.c`, `libskainet_moonshine_stream.so` both
ABIs). A prefill path of the form `ext:<library.so>|<model dir>` replaces the prefill and step graphs with a native
library loaded by `dlopen` that exports `moonshine_ext_decoder()` (ABI 1: `create(dir, vocab, dim, maxtok)`,
`cross(memory, rows)`, `reset`, `step(embedded token, logits)`, `destroy`). The streaming loop, greedy choice, token
budgets, repeat guard and text handling stay in the runtime, and partial transcripts keep their locked prefix
(`cross` refreshes the memory without dropping decoded tokens). The step path is then ignored; with both sides
external no params archive is needed. Without the prefix nothing changes: the IREE step moved into one helper,
behaviour identical (444/444 transcripts identical on the external-encoder + Vulkan-decoder path). Checked on an
arm32 device over 444 German commands with an external encoder and decoder on an accelerator outside IREE: 13.5 %
WER, transcript 238 ms after the end of the audio (p50; 1080 ms with the Vulkan decoder), no GPU use.
- `native/moonshine_stream_check.c`: `MOONSHINE_DEC` replaces the prefill path (e.g. an `ext:` decoder).

## [0.63.2] — 2026-10-27

Against engine **0.62.0**, transformers-only and additive. The Android runtime (`runtime-iree-android`) gains an
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Honest status — see the project-status note at the top of this README.

## Current release

The current release is **0.63.2**, against **SKaiNET 0.62.0**: the streaming Moonshine runtime can hand the encoder side to an external native library (`ext:<library.so>|<model dir>` as the encoder path), so an accelerator outside IREE runs it while the decoder stays on the IREE device. 0.63.1 gave the streaming Moonshine runtime an incremental frontend (an optional `frontend_step.vmfb` takes most of the frontend out of the end-of-utterance flush, transcripts unchanged) and `IreeModuleRunner` runs one compiled IREE module on Android. 0.63.0 brought expert heads and routing for Laya — a decision head fine-tuned on top of the frozen encoder (`expert.safetensors` + `expert.json`) loads on its own with `LayaExpertLoader`, any number of heads share one encoder pass per question (`LayaDecisionModel.logitsFrom`), `LayaRouterSpec` + `LayaMixtureAgent` route a request by asking the base head what kind of text it is, and `LayaMixtureDecisionAdapter` puts the mixture behind the typed decision contract; verified against the published layaMOE evaluation. The ModernBERT-large `laya-typed-decisions` checkpoint loads (`position_embedding_type: absolute`). The whisper StableHLO export ships as `WhisperExportCli`. Engine 0.62.0 brings the JVM ↔ Kotlin/Wasm parity: every kernel on the wasm tier, resident activations, fused attention / norms / softmax, browser IO parity. The Laya Q8_0 lane on the desktop JVM with the native provider needs engine 0.62.1 (SKaiNET #1436).
The current release is **0.63.3**, against **SKaiNET 0.62.0**: the streaming Moonshine runtime can hand the decoder to an external native library too (`ext:<library.so>|<model dir>` as the prefill path), so with 0.63.2's external encoder the whole model can run on an accelerator outside IREE while the runtime keeps the streaming loop, greedy decoding and its guards. 0.63.2 handed the encoder side to an external library (`ext:` as the encoder path) while the decoder stays on the IREE device. 0.63.1 gave the streaming Moonshine runtime an incremental frontend (an optional `frontend_step.vmfb` takes most of the frontend out of the end-of-utterance flush, transcripts unchanged) and `IreeModuleRunner` runs one compiled IREE module on Android. 0.63.0 brought expert heads and routing for Laya — a decision head fine-tuned on top of the frozen encoder (`expert.safetensors` + `expert.json`) loads on its own with `LayaExpertLoader`, any number of heads share one encoder pass per question (`LayaDecisionModel.logitsFrom`), `LayaRouterSpec` + `LayaMixtureAgent` route a request by asking the base head what kind of text it is, and `LayaMixtureDecisionAdapter` puts the mixture behind the typed decision contract; verified against the published layaMOE evaluation. The ModernBERT-large `laya-typed-decisions` checkpoint loads (`position_embedding_type: absolute`). The whisper StableHLO export ships as `WhisperExportCli`. Engine 0.62.0 brings the JVM ↔ Kotlin/Wasm parity: every kernel on the wasm tier, resident activations, fused attention / norms / softmax, browser IO parity. The Laya Q8_0 lane on the desktop JVM with the native provider needs engine 0.62.1 (SKaiNET #1436).

It builds on **0.62.2**, against **SKaiNET 0.61.0**: a transformers-only fix, the Android runtime's streaming Moonshine libraries are rebuilt with the utterance-start fix (#500; 0.62.0 and 0.62.1 packed the old build, which garbles transcripts on device). It builds on **0.62.1**, a transformers-only fix on 0.62.0, whose JVM `LayaModelLoader.load(directory, …)` had become an extension function that existing callers did not import; it is a member again (`@JvmOverloads`, so Java callers keep the 0.61 signature).

Expand Down Expand Up @@ -303,7 +303,7 @@ The recommended way to consume is via the BOM. It pins every published `skainet-

```kotlin
dependencies {
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.2"))
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.3"))

// Versions resolved from the BOM:
implementation("sk.ainet.transformers:skainet-transformers-core")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ From your own build, resolve the published module and run its main class:
----
val exportTool by configurations.creating
dependencies {
exportTool(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.2"))
exportTool(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.3"))
exportTool("sk.ainet.transformers:skainet-transformers-inference-moonshine")
}
tasks.register<JavaExec>("exportMoonshine") {
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/reference/moonshine-encoder.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ a DSL decoder is future work.
[source,kotlin]
----
dependencies {
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.2"))
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.3"))
implementation("sk.ainet.transformers:skainet-transformers-inference-moonshine")
}
----
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/reference/typed-decisions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ below put that on the JVM path with the same contract for every model.
[source,kotlin]
----
dependencies {
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.2"))
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.3"))
implementation("sk.ainet.transformers:skainet-transformers-decisions-api") // the contract, no engine dependency
implementation("sk.ainet.transformers:skainet-transformers-decisions-runtime") // validation + acceptance policy
implementation("sk.ainet.transformers:skainet-transformers-inference-laya") // Laya as a DecisionAdapter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ dependencies {
// self-registers it on ART at process start — nothing to call.
runtimeOnly("sk.ainet.core:skainet-backend-jni-cpu")

implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.2"))
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.3"))
implementation("sk.ainet.transformers:skainet-transformers-core")
implementation("sk.ainet.transformers:skainet-transformers-runtime-kllama")
implementation("sk.ainet.transformers:skainet-transformers-inference-llama")
Expand Down
4 changes: 2 additions & 2 deletions docs/modules/ROOT/pages/tutorials/getting-started-java.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ In your `build.gradle.kts`:
[source,kotlin]
----
dependencies {
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.2"))
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.3"))

implementation("sk.ainet.transformers:skainet-transformers-runtime-kllama")
implementation("sk.ainet.transformers:skainet-transformers-agent")
Expand All @@ -41,7 +41,7 @@ Or in Maven (Maven needs the `-jvm` classifier suffix on platform artifacts):
<dependency>
<groupId>sk.ainet.transformers</groupId>
<artifactId>skainet-transformers-bom</artifactId>
<version>0.63.2</version>
<version>0.63.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ encoder output to the advertised dimensionality. The runtime applies it automati
[source,kotlin]
----
dependencies {
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.2"))
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.3"))
implementation("sk.ainet.transformers:skainet-transformers-providers")
}
----
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/tutorials/llama3-tool-calling.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ The pieces you need live in three modules:
[source,kotlin]
----
dependencies {
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.2"))
implementation(platform("sk.ainet.transformers:skainet-transformers-bom:0.63.3"))

implementation("sk.ainet.transformers:skainet-transformers-runtime-kllama")
implementation("sk.ainet.transformers:skainet-transformers-agent")
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
GROUP=sk.ainet.transformers
VERSION_NAME=0.63.2
VERSION_NAME=0.63.3

POM_DESCRIPTION=SKaiNET-transformers

Expand Down
2 changes: 2 additions & 0 deletions llm-runtime/iree-android/native/moonshine_stream_check.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* pcm headerless little-endian s16 mono 16 kHz; a directory = every *.pcm in it, name order
* --fast feed without pacing (timings then no longer resemble a live stream)
* MOONSHINE_ENC (env) replaces the encoder path, e.g. "ext:<lib.so>|<dir>" for an external encoder
* MOONSHINE_DEC (env) replaces the prefill path, e.g. "ext:<lib.so>|<dir>" for an external decoder
*
* Build (same image as the library):
* docker run --rm -v "$PWD/native":/src -w /src --entrypoint sh skainet/iree-android:3.11.0 -c \
Expand Down Expand Up @@ -79,6 +80,7 @@ int main(int argc, char** argv) {
static char p[8][1024];
for (int i = 0; i < 8; ++i) snprintf(p[i], sizeof p[i], "%s/%s", argv[3], names[i]);
if (getenv("MOONSHINE_ENC")) snprintf(p[1], sizeof p[1], "%s", getenv("MOONSHINE_ENC"));
if (getenv("MOONSHINE_DEC")) snprintf(p[3], sizeof p[3], "%s", getenv("MOONSHINE_DEC"));
double t0 = now_ms();
jlong h = create(env, NULL, (jstring)argv[2], (jstring)p[0], (jstring)p[1], (jstring)p[2], (jstring)p[3], (jstring)p[4], (jstring)p[5], (jstring)p[6], (jstring)p[7]);
if (!h) { fprintf(stderr, "nativeCreate failed\n"); return 4; }
Expand Down
Loading
Loading