From 72c7e4be14f8df3fe8c8368a081ae4fc1bb1657d Mon Sep 17 00:00:00 2001 From: michalharakal Date: Fri, 16 Oct 2026 22:19:40 +0200 Subject: [PATCH 1/2] refactor(io)!: remove the SKEEP-006 compatibility aliases, as scheduled for 0.61 WARNING in 0.58 and 0.59, ERROR in 0.60, gone in 0.61: the sk.ainet.io.tokenizer typealiases, sk.ainet.io.model.ModelFormat, sk.ainet.io.weights.NameMap / RoleTableNameMap / TransformerNameMaps, and the sk.ainet.io.gguf.registry package (ModelArchitecture.fromGguf, ModelLoaderRegistry and its descriptors). Nothing in the tree could reference them since 0.60; TokenizerFactory and UnsupportedTokenizerException stay in sk.ainet.io.tokenizer for Java callers, asWeightNameResolver stays in sk.ainet.io.weights. --- .../ROOT/pages/reference/input-frontends.adoc | 4 +- ...06-input-frontends-and-model-identity.adoc | 2 +- .../kotlin/sk/ainet/io/model/ModelFormat.kt | 9 -- .../sk/ainet/io/tokenizer/Deprecated.kt | 31 ------- .../kotlin/sk/ainet/io/weights/NameMap.kt | 7 -- .../ainet/io/weights/TransformerNameMaps.kt | 9 -- .../gguf/registry/Gemma3nLoaderDescriptor.kt | 15 --- .../io/gguf/registry/LlamaLoaderDescriptor.kt | 15 --- .../io/gguf/registry/ModelArchitecture.kt | 31 ------- .../io/gguf/registry/ModelLoaderRegistry.kt | 93 ------------------- 10 files changed, 3 insertions(+), 213 deletions(-) delete mode 100644 skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/model/ModelFormat.kt delete mode 100644 skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/tokenizer/Deprecated.kt delete mode 100644 skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/weights/TransformerNameMaps.kt delete mode 100644 skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/Gemma3nLoaderDescriptor.kt delete mode 100644 skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/LlamaLoaderDescriptor.kt delete mode 100644 skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/ModelArchitecture.kt delete mode 100644 skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/ModelLoaderRegistry.kt diff --git a/docs/modules/ROOT/pages/reference/input-frontends.adoc b/docs/modules/ROOT/pages/reference/input-frontends.adoc index 1f554734..51c8482b 100644 --- a/docs/modules/ROOT/pages/reference/input-frontends.adoc +++ b/docs/modules/ROOT/pages/reference/input-frontends.adoc @@ -30,8 +30,8 @@ own side files and decorated by its model family. |=== `skainet-io-core` keeps `sk.ainet.io.tokenizer.*`, `sk.ainet.io.model.ModelFormat` and -`sk.ainet.io.weights.NameMap` as deprecated aliases (WARNING in 0.58 and 0.59, ERROR since 0.60, -removed in 0.61), and `TokenizerFactory` as a forwarding object for Java callers. +`sk.ainet.io.weights.NameMap` as deprecated aliases for two releases (WARNING in 0.58 and 0.59, ERROR +in 0.60, removed in 0.61), and keeps `TokenizerFactory` as a forwarding object for Java callers. == Resolving a checkpoint's tokenizer diff --git a/docs/modules/skeep/pages/006-input-frontends-and-model-identity.adoc b/docs/modules/skeep/pages/006-input-frontends-and-model-identity.adoc index ce4bf34c..8e4385b4 100644 --- a/docs/modules/skeep/pages/006-input-frontends-and-model-identity.adoc +++ b/docs/modules/skeep/pages/006-input-frontends-and-model-identity.adoc @@ -2,7 +2,7 @@ :description: SKaiNET proposal for one resolvable input front-end contract (tokenizers first, audio and image features next) keyed on a string-based model identity with an immutable family registry, so that every model type SKaiNET runs gets its preprocessing from the engine instead of re-deriving it downstream. Status: Implemented + -Implementation: https://github.com/SKaiNET-developers/SKaiNET/pull/1332[#1332] (text lane, identity, resolver; engine 0.58.0), https://github.com/SKaiNET-developers/SKaiNET-transformers/pull/485[SKaiNET-transformers #485] (consumer, 0.58.0); the io-core compatibility aliases moved to `DeprecationLevel.ERROR` in 0.60.0 (https://github.com/SKaiNET-developers/SKaiNET/pull/1350[#1350]) and leave in 0.61. The audio (F4, F6) and image (F5) front ends are follow-up proposals. + +Implementation: https://github.com/SKaiNET-developers/SKaiNET/pull/1332[#1332] (text lane, identity, resolver; engine 0.58.0), https://github.com/SKaiNET-developers/SKaiNET-transformers/pull/485[SKaiNET-transformers #485] (consumer, 0.58.0); the io-core compatibility aliases moved to `DeprecationLevel.ERROR` in 0.60.0 (https://github.com/SKaiNET-developers/SKaiNET/pull/1350[#1350]) and were removed in 0.61.0. The audio (F4, F6) and image (F5) front ends are follow-up proposals. + Audience: SKaiNET maintainers and contributors; SKaiNET-transformers maintainers (the first consumer, and the repository whose parallel tokenizer stack this proposal retires); SKaiNET-cartridge maintainers (the facade binds front ends to cartridge ports) + Created: 2026-10-11 + Tracking issue: https://github.com/SKaiNET-developers/SKaiNET/issues/1331[#1331] (implementation lanes: https://github.com/SKaiNET-developers/SKaiNET/issues/1321[#1321], https://github.com/SKaiNET-developers/SKaiNET-transformers/issues/471[SKaiNET-transformers #471], https://github.com/SKaiNET-developers/SKaiNET-transformers/issues/330[SKaiNET-transformers #330]) + diff --git a/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/model/ModelFormat.kt b/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/model/ModelFormat.kt deleted file mode 100644 index bddac583..00000000 --- a/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/model/ModelFormat.kt +++ /dev/null @@ -1,9 +0,0 @@ -package sk.ainet.io.model - -/** Moved to `skainet-model-identity` (SKEEP-006); alias an error since 0.60, removed in 0.61. */ -@Deprecated( - "Moved to sk.ainet.model.ModelFormat (SKEEP-006); the alias is an error since 0.60 and is removed in 0.61.", - ReplaceWith("ModelFormat", "sk.ainet.model.ModelFormat"), - level = DeprecationLevel.ERROR, -) -public typealias ModelFormat = sk.ainet.model.ModelFormat diff --git a/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/tokenizer/Deprecated.kt b/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/tokenizer/Deprecated.kt deleted file mode 100644 index b815f4f1..00000000 --- a/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/tokenizer/Deprecated.kt +++ /dev/null @@ -1,31 +0,0 @@ -@file:Suppress("DEPRECATION", "DEPRECATION_ERROR") - -package sk.ainet.io.tokenizer - -/* - * SKEEP-006: the tokenizer implementations moved to `skainet-frontend-text` - * (`sk.ainet.frontend.tokenizer`) and the contract to `skainet-frontend-core` - * (`sk.ainet.frontend.text`). These aliases kept 0.57 sources compiling for one release cycle - * (WARNING in 0.58 and 0.59, ERROR in 0.60, removed in 0.61). `TokenizerFactory` stays a real object - * because Java callers pin its static methods. - */ - -private const val MOVED = "Moved to skainet-frontend-core / skainet-frontend-text (SKEEP-006); the alias is an error since 0.60 and is removed in 0.61." - -@Deprecated(MOVED, ReplaceWith("Tokenizer", "sk.ainet.frontend.text.Tokenizer"), level = DeprecationLevel.ERROR) -public typealias Tokenizer = sk.ainet.frontend.text.Tokenizer - -@Deprecated(MOVED, ReplaceWith("QwenByteLevelBpeTokenizer", "sk.ainet.frontend.tokenizer.QwenByteLevelBpeTokenizer"), level = DeprecationLevel.ERROR) -public typealias QwenByteLevelBpeTokenizer = sk.ainet.frontend.tokenizer.QwenByteLevelBpeTokenizer - -@Deprecated(MOVED, ReplaceWith("SentencePieceTokenizer", "sk.ainet.frontend.tokenizer.SentencePieceTokenizer"), level = DeprecationLevel.ERROR) -public typealias SentencePieceTokenizer = sk.ainet.frontend.tokenizer.SentencePieceTokenizer - -@Deprecated(MOVED, ReplaceWith("SpecialTokenSplitter", "sk.ainet.frontend.tokenizer.SpecialTokenSplitter"), level = DeprecationLevel.ERROR) -public typealias SpecialTokenSplitter = sk.ainet.frontend.tokenizer.SpecialTokenSplitter - -@Deprecated(MOVED, ReplaceWith("TekkenTokenizer", "sk.ainet.frontend.tokenizer.TekkenTokenizer"), level = DeprecationLevel.ERROR) -public typealias TekkenTokenizer = sk.ainet.frontend.tokenizer.TekkenTokenizer - -@Deprecated(MOVED, ReplaceWith("ByteToUnicode", "sk.ainet.frontend.tokenizer.ByteToUnicode"), level = DeprecationLevel.ERROR) -public typealias ByteToUnicode = sk.ainet.frontend.tokenizer.ByteToUnicode diff --git a/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/weights/NameMap.kt b/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/weights/NameMap.kt index cfc4e50b..96451dc3 100644 --- a/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/weights/NameMap.kt +++ b/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/weights/NameMap.kt @@ -1,17 +1,10 @@ -@file:Suppress("DEPRECATION", "DEPRECATION_ERROR") package sk.ainet.io.weights import sk.ainet.lang.tensor.TensorId /** Moved to `skainet-model-identity` (SKEEP-006); alias an error since 0.60, removed in 0.61. */ -@Deprecated("Moved to sk.ainet.model.NameMap (SKEEP-006); the alias is an error since 0.60 and is removed in 0.61.", ReplaceWith("NameMap", "sk.ainet.model.NameMap"), level = DeprecationLevel.ERROR) -public typealias NameMap = sk.ainet.model.NameMap - /** Moved to `skainet-model-identity` (SKEEP-006); alias an error since 0.60, removed in 0.61. */ -@Deprecated("Moved to sk.ainet.model.RoleTableNameMap (SKEEP-006); the alias is an error since 0.60 and is removed in 0.61.", ReplaceWith("RoleTableNameMap", "sk.ainet.model.RoleTableNameMap"), level = DeprecationLevel.ERROR) -public typealias RoleTableNameMap = sk.ainet.model.RoleTableNameMap - /** * This map as a legacy [WeightNameResolver]: `(modulePath, paramName)` in the slash form * (`model/layers[3]/attn`, `q_proj.weight`) → checkpoint name. An extension since SKEEP-006 diff --git a/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/weights/TransformerNameMaps.kt b/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/weights/TransformerNameMaps.kt deleted file mode 100644 index 9df19224..00000000 --- a/skainet-io/skainet-io-core/src/commonMain/kotlin/sk/ainet/io/weights/TransformerNameMaps.kt +++ /dev/null @@ -1,9 +0,0 @@ -package sk.ainet.io.weights - -/** Moved to `skainet-model-identity` (SKEEP-006); alias an error since 0.60, removed in 0.61. */ -@Deprecated( - "Moved to sk.ainet.model.TransformerNameMaps (SKEEP-006); the alias is an error since 0.60 and is removed in 0.61.", - ReplaceWith("TransformerNameMaps", "sk.ainet.model.TransformerNameMaps"), - level = DeprecationLevel.ERROR, -) -public typealias TransformerNameMaps = sk.ainet.model.TransformerNameMaps diff --git a/skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/Gemma3nLoaderDescriptor.kt b/skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/Gemma3nLoaderDescriptor.kt deleted file mode 100644 index 23e2d9b4..00000000 --- a/skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/Gemma3nLoaderDescriptor.kt +++ /dev/null @@ -1,15 +0,0 @@ -package sk.ainet.io.gguf.registry - -import sk.ainet.io.model.ModelArchitecture - -/** - * Loader descriptor for Gemma-family models (Gemma, Gemma 3, Gemma 3n). - * - * Register with [ModelLoaderRegistry] to enable auto-detection of Gemma GGUF files. - */ -@Suppress("DEPRECATION", "DEPRECATION_ERROR") -@Deprecated("Register families with sk.ainet.model.ModelFamilyRegistry (SKEEP-006). An error since 0.60, removed in 0.61.", level = DeprecationLevel.ERROR) -public object Gemma3nLoaderDescriptor : ModelLoaderDescriptor { - override val architecture: ModelArchitecture = ModelArchitecture.GEMMA - override val displayName: String = "Gemma 3n" -} diff --git a/skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/LlamaLoaderDescriptor.kt b/skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/LlamaLoaderDescriptor.kt deleted file mode 100644 index 2822f9fb..00000000 --- a/skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/LlamaLoaderDescriptor.kt +++ /dev/null @@ -1,15 +0,0 @@ -package sk.ainet.io.gguf.registry - -import sk.ainet.io.model.ModelArchitecture - -/** - * Loader descriptor for LLaMA-family models (LLaMA, Mistral, Qwen, etc.). - * - * Register with [ModelLoaderRegistry] to enable auto-detection of LLaMA GGUF files. - */ -@Suppress("DEPRECATION", "DEPRECATION_ERROR") -@Deprecated("Register families with sk.ainet.model.ModelFamilyRegistry (SKEEP-006). An error since 0.60, removed in 0.61.", level = DeprecationLevel.ERROR) -public object LlamaLoaderDescriptor : ModelLoaderDescriptor { - override val architecture: ModelArchitecture = ModelArchitecture.LLAMA - override val displayName: String = "LLaMA" -} diff --git a/skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/ModelArchitecture.kt b/skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/ModelArchitecture.kt deleted file mode 100644 index 8a4eb955..00000000 --- a/skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/ModelArchitecture.kt +++ /dev/null @@ -1,31 +0,0 @@ -package sk.ainet.io.gguf.registry - -import sk.ainet.io.model.ModelArchitecture - -/** - * GGUF-specific extension: resolve a `general.architecture` string to a [ModelArchitecture]. - */ -private val ggufIdMap: Map = mapOf( - "llama" to ModelArchitecture.LLAMA, - "gemma4" to ModelArchitecture.GEMMA, - "gemma3n" to ModelArchitecture.GEMMA, - "gemma3" to ModelArchitecture.GEMMA, - "gemma" to ModelArchitecture.GEMMA, - "bert" to ModelArchitecture.BERT, - "qwen2" to ModelArchitecture.QWEN, -) - -/** - * Resolve a GGUF `general.architecture` value to a [ModelArchitecture]. - * - * @param arch The architecture string from GGUF metadata (may be null) - * @return The matching architecture, or [UNKNOWN][ModelArchitecture.UNKNOWN] if not recognized - */ -@Deprecated( - "The enum cannot grow with new families; identify checkpoints through sk.ainet.model.ModelFamilyRegistry (SKEEP-006). An error since 0.60, removed in 0.61.", - ReplaceWith("ModelFamilyRegistry.builtIn.identify(view)", "sk.ainet.model.ModelFamilyRegistry"), - level = DeprecationLevel.ERROR, -) -public fun ModelArchitecture.Companion.fromGguf(arch: String?): ModelArchitecture = - arch?.let { a -> ggufIdMap.entries.firstOrNull { it.key.equals(a, ignoreCase = true) }?.value } - ?: ModelArchitecture.UNKNOWN diff --git a/skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/ModelLoaderRegistry.kt b/skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/ModelLoaderRegistry.kt deleted file mode 100644 index 58a83185..00000000 --- a/skainet-io/skainet-io-gguf/src/commonMain/kotlin/sk/ainet/io/gguf/registry/ModelLoaderRegistry.kt +++ /dev/null @@ -1,93 +0,0 @@ -package sk.ainet.io.gguf.registry - -import sk.ainet.io.RandomAccessSource -import sk.ainet.io.gguf.StreamingGGUFReader -import sk.ainet.io.model.ModelArchitecture - -/** - * Describes how to create a weight loader for a specific model architecture. - * - * Implementations are registered with [ModelLoaderRegistry] and selected - * automatically based on GGUF metadata. - */ -public interface ModelLoaderDescriptor { - /** The architecture this descriptor handles. */ - public val architecture: ModelArchitecture - - /** Human-readable name for logging. */ - public val displayName: String -} - -/** - * Registry for model loaders with GGUF auto-detection. - * - * Reads the `general.architecture` field from a GGUF file header - * and resolves the appropriate [ModelLoaderDescriptor]. - * - * Usage: - * ```kotlin - * // Register loaders (typically at startup) - * ModelLoaderRegistry.register(LlamaLoaderDescriptor) - * ModelLoaderRegistry.register(Gemma3nLoaderDescriptor) - * - * // Auto-detect from file - * val (arch, descriptor) = ModelLoaderRegistry.detect(source) - * ``` - */ -@Deprecated( - "Global mutable registry keyed on an enum; replaced by the immutable sk.ainet.model.ModelFamilyRegistry (SKEEP-006, FE-I-02/FE-I-04). An error since 0.60, removed in 0.61.", - ReplaceWith("ModelFamilyRegistry", "sk.ainet.model.ModelFamilyRegistry"), - level = DeprecationLevel.ERROR, -) -public object ModelLoaderRegistry { - - private val descriptors = mutableMapOf() - - /** - * Register a loader descriptor for an architecture. - * Replaces any previously registered descriptor for the same architecture. - */ - public fun register(descriptor: ModelLoaderDescriptor) { - descriptors[descriptor.architecture] = descriptor - } - - /** Return all registered descriptors. */ - public fun registeredDescriptors(): Map = - descriptors.toMap() - - /** - * Detect the model architecture from a GGUF file. - * - * Opens the file, reads only the metadata header (~1MB), extracts - * `general.architecture`, and resolves to a [ModelArchitecture]. - * - * @param source Random-access source to the GGUF file - * @return Detected architecture - */ - public fun detectArchitecture(source: RandomAccessSource): ModelArchitecture { - return StreamingGGUFReader.open(source).use { reader -> - val arch = reader.fields["general.architecture"] as? String - @Suppress("DEPRECATION", "DEPRECATION_ERROR") - ModelArchitecture.fromGguf(arch) - } - } - - /** - * Detect the architecture and return the matching descriptor. - * - * @param source Random-access source to the GGUF file - * @return Pair of detected architecture and its descriptor, or null descriptor if not registered - */ - public fun detect(source: RandomAccessSource): Pair { - val arch = detectArchitecture(source) - return arch to descriptors[arch] - } - - /** - * Look up the descriptor for a known architecture. - * - * @return The registered descriptor, or null if the architecture is not registered - */ - public fun descriptorFor(architecture: ModelArchitecture): ModelLoaderDescriptor? = - descriptors[architecture] -} From 6fe2a64c4518ccab0bd1089b5124d0638d911195 Mon Sep 17 00:00:00 2001 From: michalharakal Date: Fri, 16 Oct 2026 22:20:31 +0200 Subject: [PATCH 2/2] release: prepare 0.61.0 -- SKEEP-007 browser lane CHANGELOG 0.61.0 section (headline, version note, the scheduled SKEEP-006 removal), VERSION_NAME, CITATION, Antora version, README bom line, What's New and contributors, the Android tutorial coordinates, regenerated operator and kernel matrices, SKEEP-007 marked Implemented. --- CHANGELOG.md | 20 ++++++- CITATION.cff | 4 +- README.md | 52 +++++++++++-------- docs/antora.yml | 2 +- .../reference/kernel-support-matrix.adoc | 2 +- .../reference/operators/generated/index.adoc | 2 +- .../pages/reference/ops-status-matrix.adoc | 2 +- .../android-classifier-getting-started.adoc | 12 ++--- .../modules/skeep/pages/007-browser-lane.adoc | 4 +- docs/modules/skeep/pages/index.adoc | 2 +- gradle.properties | 2 +- 11 files changed, 64 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63f1aafc..adc562c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,25 @@ ## [Unreleased] -Headline: **the browser lane stops falling to the reference kernel (SKEEP-007, Tier 0).** +## [0.61.0] - 2026-10-16 + +Headline: **the browser lane (SKEEP-007): packed SIMD128 kernels, quantize-at-load, and no more +reference-kernel fallbacks on wasm.** Every SKaiNET browser build used to run scalar Kotlin on one +thread and sent canonical GGUF weights to the per-element decoding reference kernel. This release +reorders the packed-weight paths so the scalar packed kernels serve every target (Tier 0), ships the +engine's own C kernels as a SIMD128 sidecar for wasmJs with pthreads on cross-origin-isolated pages +(Tier 1, `skainet-backend-wasm-simd`), and teaches the SafeTensors and GGUF loaders to quantize dense +weights at load (Tier 2), so Hugging Face checkpoints can be served packed on every target. Measured +on Node: Laya's Q8_0 matmuls move from 0.72 GFLOPS to 27 GFLOPS with four threads, one 22-layer +decision from about 29 s to about 1.2 s. + +Version note: the SKEEP-006 compatibility aliases scheduled for removal in 0.61 are removed (see +*Removed*). SKaiNET-transformers follows with the Laya consumer of quantize-at-load and the browser +pack. + +### Removed + +- **The SKEEP-006 compatibility surface**, an error since 0.60 as scheduled: the `sk.ainet.io.tokenizer.*` aliases (`Tokenizer`, `QwenByteLevelBpeTokenizer`, `SentencePieceTokenizer`, `SpecialTokenSplitter`, `TekkenTokenizer`, `ByteToUnicode`), `sk.ainet.io.model.ModelFormat`, `sk.ainet.io.weights.NameMap` / `RoleTableNameMap` / `TransformerNameMaps`, and in `skainet-io-gguf` the `sk.ainet.io.gguf.registry` package (`ModelArchitecture.fromGguf`, `ModelLoaderRegistry`, `ModelLoaderDescriptor`, `LlamaLoaderDescriptor`, `Gemma3nLoaderDescriptor`). The homes are `sk.ainet.frontend.text`, `sk.ainet.frontend.tokenizer` and `sk.ainet.model`; `TokenizerFactory` and `UnsupportedTokenizerException` stay in `sk.ainet.io.tokenizer` for Java callers, and `sk.ainet.io.weights.asWeightNameResolver` stays. ### Added diff --git a/CITATION.cff b/CITATION.cff index a358b09c..490800c9 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -12,8 +12,8 @@ authors: orcid: https://orcid.org/0009-0006-7981-4075 - name: SKaiNET-developers contributors website: https://github.com/SKaiNET-developers -version: "0.60.0" -date-released: "2026-10-15" +version: "0.61.0" +date-released: "2026-10-16" license: MIT repository-code: https://github.com/SKaiNET-developers/SKaiNET url: https://skainet-developers.github.io/SKaiNET/ diff --git a/README.md b/README.md index e0eb1d1f..90dfcbc1 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Add the core dependencies (Gradle Kotlin DSL): ```kotlin dependencies { // Recommended: import the umbrella BOM and drop versions on the engine modules. - implementation(platform("sk.ainet:skainet-bom:0.60.0")) + implementation(platform("sk.ainet:skainet-bom:0.61.0")) implementation("sk.ainet.core:skainet-lang-core") implementation("sk.ainet.core:skainet-backend-cpu") @@ -308,27 +308,26 @@ val withoutLabel = dataPipeline() --- -## What's New in 0.60.0 - -Key-padding lengths and window metadata for attention (#1343): - -- **`scaledDotProductAttention(keyLengths, window, rightContext)`** — padding (a `[batch]` - lengths tensor), the sliding-window band and `causal` are rules over positions defined once in - `AttentionMask` and shared by the CPU kernel, the recorded graph and the StableHLO export. The - CPU kernel computes scores only inside each row's attended key range, so a padded batch equals - the unpadded single passes; the dense additive mask stays the generic fallback. -- **The rules travel as op metadata** — the recorded node carries `maskKind`, `window` and - `rightContext` with the lengths as an operand, so a compiled target regenerates the cheapest - legal form (`iota`/`compare`/`select` on the export) instead of inheriting a dense - `[batch, 1, Sq, Sk]` mask that scales with the batch. -- **`skainet.sdpa` composite** — the composite seam is n-ary (`HloDecomposition` takes every - operand), and `SdpaLowering.spec` emits `stablehlo.composite "skainet.sdpa"` with `scale`, - `causal`, `window`, `rightContext` and `maskKind` attributes when a target's - `OpGranularityPolicy` keeps attention fused, from the same builder as the inline form. -- The SKEEP-006 compatibility aliases (`sk.ainet.io.tokenizer.*`, `sk.ainet.io.model.ModelFormat`, - `sk.ainet.io.weights.NameMap`, the GGUF `ModelLoaderRegistry`) are errors since this release and - are removed in 0.61. Fixed on the way: a batched dense attention mask on the CPU read batch - row 0 for every batch. +## What's New in 0.61.0 + +The browser lane (SKEEP-007): packed SIMD128 kernels, quantize-at-load, no reference-kernel +fallbacks on wasm. + +- **`skainet-backend-wasm-simd`** — the engine's own C kernels compiled by Emscripten into a + SIMD128 sidecar for wasmJs, with pthreads on cross-origin-isolated pages and under Node. One call, + `WasmSimd.install()`, registers the kernel pack and the provider and makes every context built + afterwards use them. Measured on Node: Laya's Q8_0 matmuls from 0.72 GFLOPS (scalar) to 8.5 + single-threaded and 27 with four threads — a 22-layer decision from about 29 s to about 1.2 s. +- **Quantize at load** — `WeightForm(RequantizeTo(Q8_0))` on the SafeTensors loaders (and every + encoding with a quantizer on the GGUF loader) turns dense F32/F16/BF16 weight matrices into packed + Q8_0 or Q4_0 at load; `PlannerProfile.quantizeDenseTo` lets a browser or phone profile ask for it; + `skainet-quantize` converts offline. Hugging Face checkpoints can be served packed on every target. +- **The scalar lane is no longer the reference lane** — a canonical packed weight on a target without + a row-major kernel pack is relaid once into kernel feed order and served by the scalar packed + kernels; dense `[out, in]` weights are multiplied in place without a transpose copy; 16-bit + token tables are read row by row. `ByteArrayRandomAccessSource` is the engine's in-memory source. +- The SKEEP-006 compatibility aliases, errors since 0.60, are removed as scheduled; `TokenizerFactory` + stays in `sk.ainet.io.tokenizer` for Java callers. See [CHANGELOG.md](CHANGELOG.md) for full release notes, including every prior release. @@ -354,6 +353,13 @@ We love contributions! Whether it's a new operator, documentation, or a bug fix: Browse the full codebase documentation on [DeepWiki](https://deepwiki.com/SKaiNET-developers/SKaiNET). +### Contributors (0.61.0) + +- **Michal Harakal** ([@michalharakal](https://github.com/michalharakal)) — SKEEP-007: the Tier 0 + dispatch-order and dense-path fixes, quantize-at-load for the SafeTensors and GGUF loaders with the + Q8_0 quantizer and `skainet-quantize`, the `skainet-backend-wasm-simd` sidecar kernel pack, the + scheduled SKEEP-006 removal, and the release + ### Contributors (0.60.0) - **Michal Harakal** ([@michalharakal](https://github.com/michalharakal)) — key-padding lengths @@ -456,7 +462,7 @@ from it). Ready-to-paste BibTeX: author = {Harakal, Michal and {SKaiNET-developers contributors}}, title = {{SKaiNET}: A {Kotlin} Multiplatform {AI} Framework}, year = {2026}, - note = {Version 0.60.0. MIT License}, + note = {Version 0.61.0. MIT License}, howpublished = {\url{https://github.com/SKaiNET-developers/SKaiNET}} } ``` diff --git a/docs/antora.yml b/docs/antora.yml index e4493875..fa17c175 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -15,7 +15,7 @@ asciidoc: framework_name: SKaiNET # Current SKaiNET release — bump once per release; referenced as # {skainet_version} in dependency snippets (blocks need subs="attributes+"). - skainet_version: 0.60.0 + skainet_version: 0.61.0 ksp_version: 2.2.21-2.0.5 dokka_version: 2.1.0 asciidoctorj_version: 3.0.0 diff --git a/docs/modules/ROOT/pages/reference/kernel-support-matrix.adoc b/docs/modules/ROOT/pages/reference/kernel-support-matrix.adoc index 6ac54e1e..f655ab58 100644 --- a/docs/modules/ROOT/pages/reference/kernel-support-matrix.adoc +++ b/docs/modules/ROOT/pages/reference/kernel-support-matrix.adoc @@ -1,7 +1,7 @@ = Kernel × platform support matrix :description: Which compute-kernel provider serves each weight format on each KMP target. -Generated from `kernel-support.json` (version `0.60.0`) by `KernelSupportMatrixTest` — registry introspection of the registered `KernelProvider` implementations. Do not edit by hand; run `./gradlew generateKernelMatrix` to refresh. +Generated from `kernel-support.json` (version `0.61.0`) by `KernelSupportMatrixTest` — registry introspection of the registered `KernelProvider` implementations. Do not edit by hand; run `./gradlew generateKernelMatrix` to refresh. Each cell is the best (highest-priority) provider that serves `Float32 × format` `matmul` on that platform: *native-ffm* (100) → *panama-vector* (50) → *scalar* (0). An empty cell (`—`) means no provider carries a kernel there (the format is dequant-to-FP32 only). diff --git a/docs/modules/ROOT/pages/reference/operators/generated/index.adoc b/docs/modules/ROOT/pages/reference/operators/generated/index.adoc index 1c802e90..8aad78d1 100644 --- a/docs/modules/ROOT/pages/reference/operators/generated/index.adoc +++ b/docs/modules/ROOT/pages/reference/operators/generated/index.adoc @@ -1,6 +1,6 @@ = AI-NET Operators Reference -Generated from version `0.60.0` on 2026-10-15 +Generated from version `0.61.0` on 2026-10-16 == Operators by Modality diff --git a/docs/modules/ROOT/pages/reference/ops-status-matrix.adoc b/docs/modules/ROOT/pages/reference/ops-status-matrix.adoc index 67ccb075..5c918791 100644 --- a/docs/modules/ROOT/pages/reference/ops-status-matrix.adoc +++ b/docs/modules/ROOT/pages/reference/ops-status-matrix.adoc @@ -1,7 +1,7 @@ = Operator Coverage Matrix :description: Cross-backend status for every operator function in SKaiNET. -Generated from `operators.json` version `0.60.0` on 2026-10-15. +Generated from `operators.json` version `0.61.0` on 2026-10-16. Rows are `Operator.function` pairs. The `Validated` column shows whether the function's documentation has been DARC-validated by a reviewer (see xref:contributing/darc-workflow.adoc[DARC workflow]). The `Lowering` column lists the compile-path forms the function declares with `@Lowering` (`composite` = a `stablehlo.composite` when a target keeps it fused, `decompose` = StableHLO/CHLO primitives, `native` = a 1:1 target op); `—` means undeclared. Remaining columns are backends that appear in any function's `statusByBackend` map — a missing entry means the backend makes no claim about the function (treat it as "unknown", not "not supported"). diff --git a/docs/modules/ROOT/pages/tutorials/android-classifier-getting-started.adoc b/docs/modules/ROOT/pages/tutorials/android-classifier-getting-started.adoc index a81a706b..c5da3dca 100644 --- a/docs/modules/ROOT/pages/tutorials/android-classifier-getting-started.adoc +++ b/docs/modules/ROOT/pages/tutorials/android-classifier-getting-started.adoc @@ -19,12 +19,12 @@ An Android project with Kotlin. Add the SKaiNET modules: [source,kotlin] ---- dependencies { - implementation("sk.ainet.core:skainet-lang-core:0.60.0") // tensors, DSL, training - implementation("sk.ainet.core:skainet-backend-cpu:0.60.0") // CPU ops - implementation("sk.ainet.core:skainet-compile-dag:0.60.0") // autograd (training context) - implementation("sk.ainet.core:skainet-data-api:0.60.0") // Dataset / DataBatch - implementation("sk.ainet.core:skainet-data-simple:0.60.0") // embedded Iris - runtimeOnly("sk.ainet.core:skainet-backend-jni-cpu:0.60.0") // NEON kernels (see below) + implementation("sk.ainet.core:skainet-lang-core:0.61.0") // tensors, DSL, training + implementation("sk.ainet.core:skainet-backend-cpu:0.61.0") // CPU ops + implementation("sk.ainet.core:skainet-compile-dag:0.61.0") // autograd (training context) + implementation("sk.ainet.core:skainet-data-api:0.61.0") // Dataset / DataBatch + implementation("sk.ainet.core:skainet-data-simple:0.61.0") // embedded Iris + runtimeOnly("sk.ainet.core:skainet-backend-jni-cpu:0.61.0") // NEON kernels (see below) } ---- diff --git a/docs/modules/skeep/pages/007-browser-lane.adoc b/docs/modules/skeep/pages/007-browser-lane.adoc index 3d6adb53..087cc1de 100644 --- a/docs/modules/skeep/pages/007-browser-lane.adoc +++ b/docs/modules/skeep/pages/007-browser-lane.adoc @@ -1,8 +1,8 @@ = SKEEP-007: The browser lane — packed SIMD kernels and quantize-at-load :description: SKaiNET proposal for making inference in a browser usable and quick: stop the scalar wasm lane from falling to the decoding reference kernel, serve dense weights without transpose copies, keep token tables narrow, add a SIMD128 kernel pack with pthreads as a sidecar wasm module, and let the SafeTensors loader quantize at load so Hugging Face checkpoints are served packed on every target. -Status: Draft — Tiers 0, 2 and 1 in review + -Implementation: Tier 0 https://github.com/SKaiNET-developers/SKaiNET/pull/1352[#1352]; Tier 2 quantize-at-load https://github.com/SKaiNET-developers/SKaiNET/pull/1353[#1353]; Tier 1 `skainet-backend-wasm-simd` (the pull request stacked on #1353); the consumer work in SKaiNET-transformers follows, see <<_rollout_plan>>. + +Status: Implemented (Tiers 0, 1 and 2 in engine 0.61.0); the resident-activation step, the SKaiNET-transformers consumer and the WebGPU proposal remain open + +Implementation: Tier 0 https://github.com/SKaiNET-developers/SKaiNET/pull/1352[#1352], Tier 2 quantize-at-load https://github.com/SKaiNET-developers/SKaiNET/pull/1353[#1353], Tier 1 `skainet-backend-wasm-simd` https://github.com/SKaiNET-developers/SKaiNET/pull/1354[#1354] — engine 0.61.0; the consumer work in SKaiNET-transformers follows, see <<_rollout_plan>>. + Audience: SKaiNET maintainers and contributors; SKaiNET-transformers maintainers (Laya and BERT in the browser); SKaiNET-examples maintainers (every web sample runs through this lane) + Created: 2026-10-16 + Origin: the SemanticEmojiFilter example ships its 23M-parameter embedding filter to the web but keeps the 322M-parameter Laya verifier on desktop and Android, because a decision in scalar Kotlin/Wasm is estimated at 10–30 s and the FP32 weights at about 2.5 GB resident. Nothing in the repository measures the wasm lane today. diff --git a/docs/modules/skeep/pages/index.adoc b/docs/modules/skeep/pages/index.adoc index b33d4d0c..7381eb8f 100644 --- a/docs/modules/skeep/pages/index.adoc +++ b/docs/modules/skeep/pages/index.adoc @@ -136,6 +136,6 @@ Every proposal should include: | Input front ends and model identity — one resolvable preprocessing contract (tokenizers first) keyed on a string-based family registry | xref:skeep:007-browser-lane.adoc[SKEEP-007] -| Draft +| Implemented | The browser lane — packed SIMD128 kernels as a sidecar module, quantize-at-load for SafeTensors, and the common-code fixes that keep wasm off the reference kernel |=== diff --git a/gradle.properties b/gradle.properties index 9d71342a..a75ada9c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ GROUP=sk.ainet.core -VERSION_NAME=0.60.0 +VERSION_NAME=0.61.0 POM_DESCRIPTION=SKaiNET POM_URL=https://github.com/SKaiNET-developers/skainet/