@omnid/tinyemo 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,45 @@
1
- # Temporary Holding Version
1
+ # `@omnid/tinyemo`
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Zero-dependency, pure **WebGPU** TypeScript inference SDK for the **TinyEmo** English emoji suggestion model (`812` emoji classes).
4
+
5
+ - **Pure WebGPU compute pipeline**: Custom WGSL compute kernels for hashed n-gram embeddings, LayerNorm, token embeddings, multi-head self-attention, GELU FFN, attention pooling, and classification head.
6
+ - **Self-contained compressed weights**: Packs `model.safetensors` and `tiny_tokenizer.bin` compressed via Web `CompressionStream("gzip")` and decodes them at runtime via `DecompressionStream("gzip")`. Also supports loading raw or compressed `safetensors` and `onnx` (`model.onnx`) weight buffers.
7
+ - **0 shipped dependencies**: Uses only standard Web APIs (`navigator.gpu`, `CompressionStream`, `DecompressionStream`, `TypedArray`, `TextEncoder`/`TextDecoder`).
8
+
9
+ ## Usage
10
+
11
+ ```ts
12
+ import { TinyEmo } from "@omnid/tinyemo";
13
+
14
+ const model = await TinyEmo.create();
15
+
16
+ // Suggest top-3 emojis (returns EmojiSuggestion[], supporting both object and tuple access)
17
+ const suggestions = await model.suggest("walk the dog", 3);
18
+ for (const { emoji, probability } of suggestions) {
19
+ console.log(emoji, probability);
20
+ }
21
+ // Output:
22
+ // 🐕 0.771414
23
+ // 🚶 0.121709
24
+ // 🌳 0.041397
25
+
26
+ // Tuple destructuring also works directly:
27
+ const [[topEmoji, topScore]] = await model.suggest("happy birthday!!", 1);
28
+
29
+ // Batch inference on WebGPU:
30
+ const batch = await model.suggestBatch(["walk the dog", "broken heart crying"], 3);
31
+
32
+ // Raw float32 logits (Float32Array of length 812):
33
+ const logits = await model.logits("walk the dog");
34
+
35
+ // Clean up GPU buffers when done:
36
+ model.destroy();
37
+ ```
38
+
39
+ ## Scripts
40
+
41
+ ```bash
42
+ npm run pack-assets # Compress ../tinyemo/model.safetensors & tiny_tokenizer.bin via CompressionStream
43
+ npm run build # Pack assets and compile TypeScript to ./dist
44
+ npm test # Run unit + WebGPU (Dawn & Headless Chrome Metal) test suite
45
+ ```
@@ -0,0 +1,31 @@
1
+ import type { StreamCompressionFormat } from "./types.js";
2
+ /**
3
+ * Convert a base64 string into a Uint8Array without any external dependencies.
4
+ * Works in browsers, Node.js, Deno, Bun, and Cloudflare Workers.
5
+ */
6
+ export declare function base64ToBytes(base64: string): Uint8Array;
7
+ /**
8
+ * Convert a Uint8Array into a base64 string without any external dependencies.
9
+ */
10
+ export declare function bytesToBase64(bytes: Uint8Array): string;
11
+ /**
12
+ * Compress a Uint8Array using the standard Web `CompressionStream` API.
13
+ */
14
+ export declare function compressStream(data: Uint8Array | ArrayBuffer, format?: StreamCompressionFormat): Promise<Uint8Array>;
15
+ /**
16
+ * Decompress a Uint8Array using the standard Web `DecompressionStream` API.
17
+ */
18
+ export declare function decompressStream(data: Uint8Array | ArrayBuffer, format?: StreamCompressionFormat): Promise<Uint8Array>;
19
+ /**
20
+ * Compress binary data with `CompressionStream` and encode as base64 for SDK packing.
21
+ */
22
+ export declare function encodePackedAsset(data: Uint8Array | ArrayBuffer, format?: StreamCompressionFormat): Promise<string>;
23
+ /**
24
+ * Decode a base64 packed asset and decompress it with `DecompressionStream`.
25
+ */
26
+ export declare function decodePackedAsset(base64: string, format?: StreamCompressionFormat): Promise<Uint8Array>;
27
+ /**
28
+ * Check if a buffer starts with the gzip magic bytes (0x1f, 0x8b) and decompress if so.
29
+ */
30
+ export declare function maybeDecompress(data: Uint8Array | ArrayBuffer): Promise<Uint8Array>;
31
+ //# sourceMappingURL=compression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compression.d.ts","sourceRoot":"","sources":["../src/compression.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAQxD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAQvD;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,UAAU,GAAG,WAAW,EAC9B,MAAM,GAAE,uBAAgC,GACvC,OAAO,CAAC,UAAU,CAAC,CAUrB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,UAAU,GAAG,WAAW,EAC9B,MAAM,GAAE,uBAAgC,GACvC,OAAO,CAAC,UAAU,CAAC,CAUrB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,UAAU,GAAG,WAAW,EAC9B,MAAM,GAAE,uBAAgC,GACvC,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,uBAAgC,GACvC,OAAO,CAAC,UAAU,CAAC,CAGrB;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAMzF"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Convert a base64 string into a Uint8Array without any external dependencies.
3
+ * Works in browsers, Node.js, Deno, Bun, and Cloudflare Workers.
4
+ */
5
+ export function base64ToBytes(base64) {
6
+ const binString = atob(base64);
7
+ const len = binString.length;
8
+ const bytes = new Uint8Array(len);
9
+ for (let i = 0; i < len; i++) {
10
+ bytes[i] = binString.charCodeAt(i);
11
+ }
12
+ return bytes;
13
+ }
14
+ /**
15
+ * Convert a Uint8Array into a base64 string without any external dependencies.
16
+ */
17
+ export function bytesToBase64(bytes) {
18
+ let binString = "";
19
+ const chunkSize = 0x8000;
20
+ for (let i = 0; i < bytes.length; i += chunkSize) {
21
+ const chunk = bytes.subarray(i, i + chunkSize);
22
+ binString += String.fromCharCode(...chunk);
23
+ }
24
+ return btoa(binString);
25
+ }
26
+ /**
27
+ * Compress a Uint8Array using the standard Web `CompressionStream` API.
28
+ */
29
+ export async function compressStream(data, format = "gzip") {
30
+ if (typeof CompressionStream === "undefined") {
31
+ throw new Error("Web CompressionStream API is not available in this runtime.");
32
+ }
33
+ const input = data instanceof Uint8Array ? data : new Uint8Array(data);
34
+ const stream = new Blob([input])
35
+ .stream()
36
+ .pipeThrough(new CompressionStream(format));
37
+ const ab = await new Response(stream).arrayBuffer();
38
+ return new Uint8Array(ab);
39
+ }
40
+ /**
41
+ * Decompress a Uint8Array using the standard Web `DecompressionStream` API.
42
+ */
43
+ export async function decompressStream(data, format = "gzip") {
44
+ if (typeof DecompressionStream === "undefined") {
45
+ throw new Error("Web DecompressionStream API is not available in this runtime.");
46
+ }
47
+ const input = data instanceof Uint8Array ? data : new Uint8Array(data);
48
+ const stream = new Blob([input])
49
+ .stream()
50
+ .pipeThrough(new DecompressionStream(format));
51
+ const ab = await new Response(stream).arrayBuffer();
52
+ return new Uint8Array(ab);
53
+ }
54
+ /**
55
+ * Compress binary data with `CompressionStream` and encode as base64 for SDK packing.
56
+ */
57
+ export async function encodePackedAsset(data, format = "gzip") {
58
+ const compressed = await compressStream(data, format);
59
+ return bytesToBase64(compressed);
60
+ }
61
+ /**
62
+ * Decode a base64 packed asset and decompress it with `DecompressionStream`.
63
+ */
64
+ export async function decodePackedAsset(base64, format = "gzip") {
65
+ const compressed = base64ToBytes(base64);
66
+ return decompressStream(compressed, format);
67
+ }
68
+ /**
69
+ * Check if a buffer starts with the gzip magic bytes (0x1f, 0x8b) and decompress if so.
70
+ */
71
+ export async function maybeDecompress(data) {
72
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
73
+ if (bytes.byteLength >= 2 && bytes[0] === 0x1f && bytes[1] === 0x8b) {
74
+ return decompressStream(bytes, "gzip");
75
+ }
76
+ return bytes;
77
+ }
78
+ //# sourceMappingURL=compression.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compression.js","sourceRoot":"","sources":["../src/compression.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ import { TinyTokenizer } from "./tokenizer.js";
2
+ import type { EmojiSuggestion, EncodedBatch, EncodedInput, TinyEmoModelConfig, TinyEmoOptions } from "./types.js";
3
+ export declare const MAX_OUTPUT = 3;
4
+ /**
5
+ * Numerically stable softmax over a Float32Array.
6
+ */
7
+ export declare function softmax(logits: Float32Array): Float32Array;
8
+ /**
9
+ * Create an EmojiSuggestion object supporting both `{ emoji, probability }` and `[emoji, probability]`.
10
+ */
11
+ export declare function createSuggestion(emoji: string, probability: number, index: number): EmojiSuggestion;
12
+ /**
13
+ * Request a WebGPU `GPUDevice` from the global `navigator.gpu`.
14
+ */
15
+ export declare function requestWebGpuDevice(adapterOptions?: GPURequestAdapterOptions): Promise<GPUDevice>;
16
+ /**
17
+ * Pure WebGPU inference engine for the TinyEmo emoji suggestion model.
18
+ */
19
+ export declare class TinyEmo {
20
+ readonly device: GPUDevice;
21
+ readonly config: TinyEmoModelConfig;
22
+ readonly labels: readonly string[];
23
+ readonly tokenizer: TinyTokenizer;
24
+ private readonly packedWeights;
25
+ private readonly weightsBuffer;
26
+ private readonly bindGroupLayout;
27
+ private readonly pipelines;
28
+ private readonly layerParamsBuffers;
29
+ private allocatedBatchCap;
30
+ private inputsIntBuffer;
31
+ private inputsFloatBuffer;
32
+ private workspaceBuffer;
33
+ private logitsBuffer;
34
+ private readbackBuffer;
35
+ private layerBindGroups;
36
+ private destroyed;
37
+ private queueLock;
38
+ private constructor();
39
+ /**
40
+ * Create and initialize a new TinyEmo WebGPU inference instance.
41
+ * Decompresses the packed safetensors weights and tokenizer using Web `DecompressionStream`
42
+ * unless custom weights/tokenizer buffers are provided.
43
+ */
44
+ static create(options?: TinyEmoOptions): Promise<TinyEmo>;
45
+ private ensureBatchBuffers;
46
+ /**
47
+ * Normalize input phrase using TinyEmo's normalization rules.
48
+ */
49
+ normalize(text: string, maxWords?: number): string;
50
+ /**
51
+ * Encode a single text phrase into token IDs, attention mask, and hashed n-gram features.
52
+ */
53
+ encode(text: string): EncodedInput;
54
+ /**
55
+ * Encode a batch of text phrases into contiguous typed arrays for WebGPU execution.
56
+ */
57
+ encodeBatch(texts: readonly string[]): EncodedBatch;
58
+ /**
59
+ * Execute the WebGPU forward pass on an encoded batch, returning a flattened
60
+ * `Float32Array` of shape `[batchSize * n_labels]`.
61
+ */
62
+ forward(batch: EncodedBatch): Promise<Float32Array>;
63
+ /**
64
+ * Compute raw logits (`Float32Array` of length 812) for a single text phrase on WebGPU.
65
+ */
66
+ logits(text: string): Promise<Float32Array>;
67
+ /**
68
+ * Compute raw logits for a batch of text phrases on WebGPU.
69
+ */
70
+ logitsBatch(texts: readonly string[], batchSize?: number): Promise<Float32Array[]>;
71
+ /**
72
+ * Compute softmax probability distribution (`Float32Array` of length 812) for a text phrase.
73
+ */
74
+ probabilities(text: string): Promise<Float32Array>;
75
+ /**
76
+ * Suggest up to `limit` emojis (clamped to 1..3, default 1) for an input phrase.
77
+ * Returns `[]` if the phrase normalizes to empty string, matching `TinyEmoRuntime.suggest`.
78
+ */
79
+ suggest(text: string, limit?: number): Promise<EmojiSuggestion[]>;
80
+ /**
81
+ * Suggest up to `limit` emojis (clamped to 1..3, default 1) for a batch of phrases
82
+ * in a single WebGPU dispatch.
83
+ */
84
+ suggestBatch(texts: readonly string[], limit?: number): Promise<EmojiSuggestion[][]>;
85
+ private topKSuggestionsFromProbs;
86
+ /**
87
+ * Release all GPU buffers allocated by this TinyEmo instance.
88
+ */
89
+ destroy(): void;
90
+ }
91
+ /**
92
+ * Convenience factory function to create a TinyEmo WebGPU inference instance.
93
+ */
94
+ export declare function createTinyEmo(options?: TinyEmoOptions): Promise<TinyEmo>;
95
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAC;AASpB,eAAO,MAAM,UAAU,IAAI,CAAC;AA2B5B;;GAEG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAsB1D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GACZ,eAAe,CAcjB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,cAAc,CAAC,EAAE,wBAAwB,GACxC,OAAO,CAAC,SAAS,CAAC,CAepB;AAED;;GAEG;AACH,qBAAa,OAAO;IAClB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAElC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmB;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAY;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAUxB;IAEF,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAmB;IACtD,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,SAAS,CAAS;IAG1B,OAAO,CAAC,SAAS,CAAuC;IAExD,OAAO;IA4GP;;;;OAIG;WACU,MAAM,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBnE,OAAO,CAAC,kBAAkB;IAwD1B;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;IAIlD;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;IA+BlC;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,YAAY;IAiCnD;;;OAGG;IACG,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAuIzD;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAKjD;;OAEG;IACG,WAAW,CACf,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,SAAS,SAAM,GACd,OAAO,CAAC,YAAY,EAAE,CAAC;IAgB1B;;OAEG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAKxD;;;OAGG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IASlE;;;OAGG;IACG,YAAY,CAChB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,SAAI,GACR,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC;IAe/B,OAAO,CAAC,wBAAwB;IAYhC;;OAEG;IACH,OAAO,IAAI,IAAI;CAahB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAE9E"}