kjarni-wasm 0.1.4
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 +123 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +165 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +63 -0
- package/dist/protocol.js +8 -0
- package/dist/protocol.js.map +1 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +109 -0
- package/dist/worker.js.map +1 -0
- package/package.json +46 -0
- package/pkg/kjarni_wasm.d.ts +259 -0
- package/pkg/kjarni_wasm.js +1602 -0
- package/pkg/kjarni_wasm_bg.wasm +0 -0
- package/pkg/kjarni_wasm_bg.wasm.d.ts +53 -0
- package/pkg/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# kjarni-wasm
|
|
2
|
+
|
|
3
|
+
Local AI inference in the browser. Embeddings, classification, reranking and chat,
|
|
4
|
+
running in a Web Worker so the page never freezes.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm i kjarni-wasm
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Or without a bundler, straight from a CDN:
|
|
11
|
+
|
|
12
|
+
```html
|
|
13
|
+
<script type="module">
|
|
14
|
+
import { Kjarni } from "https://cdn.jsdelivr.net/npm/kjarni-wasm@0.1.4/dist/index.js";
|
|
15
|
+
</script>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick tour
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { Kjarni } from "kjarni-wasm";
|
|
22
|
+
|
|
23
|
+
const kjarni = await Kjarni.load({ encoder: "/models/minilm-l6-v2-q8.kjq" });
|
|
24
|
+
|
|
25
|
+
const [doctor, physician] = await kjarni.encode(["doctor", "physician"]);
|
|
26
|
+
console.log(Kjarni.cosine(doctor, physician)); // 0.8598
|
|
27
|
+
|
|
28
|
+
// or in one call
|
|
29
|
+
console.log(await kjarni.similarity("doctor", "physician"));
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Every method returns a promise and the work happens on a worker thread. A model
|
|
33
|
+
loads or a reply generates while the page keeps painting.
|
|
34
|
+
|
|
35
|
+
## Why a worker
|
|
36
|
+
|
|
37
|
+
Inference is compute-bound and synchronous. On the main thread, encoding a batch
|
|
38
|
+
freezes the tab for as long as it takes, and generating a chat reply freezes it for
|
|
39
|
+
seconds. That is the single most common way a browser AI feature ends up feeling
|
|
40
|
+
broken.
|
|
41
|
+
|
|
42
|
+
This package puts all of it on a worker and gives you promises, so you never have
|
|
43
|
+
to think about it.
|
|
44
|
+
|
|
45
|
+
## Loading models
|
|
46
|
+
|
|
47
|
+
Models are `.kjq` files: config, tokenizer and int8 weights in one download.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
const kjarni = await Kjarni.load({
|
|
51
|
+
encoder: "/models/minilm-l6-v2-q8.kjq",
|
|
52
|
+
classifier: "/models/distilbert-sentiment-q8.kjq",
|
|
53
|
+
reranker: "/models/ms-marco-MiniLM-L-6-v2-q8.kjq",
|
|
54
|
+
onProgress: (capability, loaded, total) => {
|
|
55
|
+
console.log(`${capability}: ${Math.round((loaded / total) * 100)}%`);
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Load only what you use. Each model is a real download, from 23 MB for MiniLM to
|
|
61
|
+
several hundred for a chat model, so `onProgress` is worth wiring to a progress bar.
|
|
62
|
+
|
|
63
|
+
Bytes are transferred to the worker rather than copied, which matters: copying a
|
|
64
|
+
500 MB model is a visible pause and doubles peak memory.
|
|
65
|
+
|
|
66
|
+
## Classification
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
const [result] = await kjarni.classify("This is wonderful");
|
|
70
|
+
console.log(result.label, result.score); // POSITIVE 0.9999
|
|
71
|
+
|
|
72
|
+
const batch = await kjarni.classify(["Great service", "Worst purchase ever"]);
|
|
73
|
+
console.log(await kjarni.labels()); // ["NEGATIVE", "POSITIVE"]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Reranking
|
|
77
|
+
|
|
78
|
+
Scores query and document together, which is markedly more accurate than comparing
|
|
79
|
+
embeddings. Indices point back into your input, so you keep your own ids.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const ranked = await kjarni.rerank("how do I cancel?", documents, 5);
|
|
83
|
+
for (const { index, score } of ranked) console.log(score, documents[index]);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Chat
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const kjarni = await Kjarni.load({
|
|
90
|
+
chat: "/models/qwen05b-q8.kjq",
|
|
91
|
+
chatModelId: "qwen2.5-0.5b-instruct",
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
for await (const piece of kjarni.chat("Explain RAG in one sentence.")) {
|
|
95
|
+
output.textContent += piece;
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Tokens arrive as they are decoded, not in one lump at the end. The worker is what
|
|
100
|
+
makes that useful: generation occupies its thread completely, so the same call on
|
|
101
|
+
the main thread would freeze the tab until the reply finished.
|
|
102
|
+
|
|
103
|
+
`temperature: 0` selects greedy decoding, which is deterministic.
|
|
104
|
+
|
|
105
|
+
## Cleaning up
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
await kjarni.close();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Wasm handles are not reachable by the JavaScript garbage collector, so dropping the
|
|
112
|
+
reference leaks the weights. `close()` frees the models and stops the worker.
|
|
113
|
+
|
|
114
|
+
## Requirements
|
|
115
|
+
|
|
116
|
+
A browser with WebAssembly and module workers: Chrome and Edge 80+, Firefox 114+,
|
|
117
|
+
Safari 15+. No WebGPU required, inference runs on the CPU.
|
|
118
|
+
|
|
119
|
+
## Making your own `.kjq`
|
|
120
|
+
|
|
121
|
+
See [the format guide](../scripts/README.md). Any BERT-family encoder,
|
|
122
|
+
cross-encoder, classifier or small decoder from Hugging Face converts with one
|
|
123
|
+
script.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Capability, ClassifyResult, ModelSource, RankedDocument } from "./protocol.js";
|
|
2
|
+
export type { ClassifyResult, ModelSource, RankedDocument };
|
|
3
|
+
export interface LoadOptions {
|
|
4
|
+
/** Embeddings and semantic similarity. A `.kjq` file. */
|
|
5
|
+
encoder?: ModelSource;
|
|
6
|
+
/** Sentiment, emotion, toxicity. A `.kjq` file. */
|
|
7
|
+
classifier?: ModelSource;
|
|
8
|
+
/** Cross-encoder reranking. A `.kjq` file. */
|
|
9
|
+
reranker?: ModelSource;
|
|
10
|
+
/** A local language model. A `.kjq` file. */
|
|
11
|
+
chat?: ModelSource;
|
|
12
|
+
/** Chat only: the registry id whose prompt template should be used. */
|
|
13
|
+
chatModelId?: string;
|
|
14
|
+
/** Called as each model is fetched, for progress UI. */
|
|
15
|
+
onProgress?: (capability: Capability, loaded: number, total: number) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface ChatOptions {
|
|
18
|
+
maxNewTokens?: number;
|
|
19
|
+
/** 0 selects greedy decoding, which is deterministic. */
|
|
20
|
+
temperature?: number;
|
|
21
|
+
}
|
|
22
|
+
export declare class Kjarni {
|
|
23
|
+
#private;
|
|
24
|
+
private constructor();
|
|
25
|
+
/** Starts a worker and loads whichever models you name. */
|
|
26
|
+
static load(options: LoadOptions): Promise<Kjarni>;
|
|
27
|
+
/** One vector per input, in the order given. */
|
|
28
|
+
encode(texts: string[], normalize?: boolean): Promise<Float32Array[]>;
|
|
29
|
+
/** Cosine similarity between two texts, from -1 to 1. */
|
|
30
|
+
similarity(a: string, b: string): Promise<number>;
|
|
31
|
+
/** The best label for each input. */
|
|
32
|
+
classify(texts: string | string[]): Promise<ClassifyResult[]>;
|
|
33
|
+
/** Every label this classifier can predict, in index order. */
|
|
34
|
+
labels(): Promise<string[]>;
|
|
35
|
+
/** Documents reordered by relevance, best first. Indices point into your input. */
|
|
36
|
+
rerank(query: string, documents: string[], limit?: number): Promise<RankedDocument[]>;
|
|
37
|
+
/** The chat model's context window, in tokens. */
|
|
38
|
+
contextSize(): Promise<number>;
|
|
39
|
+
/**
|
|
40
|
+
* Generates a reply, yielding text as it arrives.
|
|
41
|
+
*
|
|
42
|
+
* for await (const piece of kjarni.chat("Explain RAG")) out.textContent += piece;
|
|
43
|
+
*/
|
|
44
|
+
chat(prompt: string, options?: ChatOptions): AsyncGenerator<string, void, unknown>;
|
|
45
|
+
/** Releases the models and stops the worker. */
|
|
46
|
+
close(): Promise<void>;
|
|
47
|
+
/** Cosine similarity between two vectors. */
|
|
48
|
+
static cosine(a: Float32Array, b: Float32Array): number;
|
|
49
|
+
}
|
|
50
|
+
export default Kjarni;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Local AI inference in the browser, off the main thread.
|
|
2
|
+
//
|
|
3
|
+
// const kjarni = await Kjarni.load({ encoder: "/models/minilm.kjq" });
|
|
4
|
+
// const [a, b] = await kjarni.encode(["doctor", "physician"]);
|
|
5
|
+
// console.log(Kjarni.cosine(a, b)); // 0.8598
|
|
6
|
+
//
|
|
7
|
+
// Every method returns a promise and the work happens in a worker, so the page
|
|
8
|
+
// keeps painting while a model loads or a reply generates.
|
|
9
|
+
async function toBytes(src, capability, onProgress) {
|
|
10
|
+
if (src instanceof Uint8Array)
|
|
11
|
+
return src;
|
|
12
|
+
if (src instanceof ArrayBuffer)
|
|
13
|
+
return new Uint8Array(src);
|
|
14
|
+
const response = await fetch(String(src));
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
throw new Error(`fetching ${src} failed: HTTP ${response.status}`);
|
|
17
|
+
}
|
|
18
|
+
// Models run to hundreds of megabytes, so report progress rather than leaving
|
|
19
|
+
// the page silent for a minute. Falls back to a plain read when the server
|
|
20
|
+
// sends no length or the body cannot be streamed.
|
|
21
|
+
const total = Number(response.headers.get("content-length") ?? 0);
|
|
22
|
+
if (!onProgress || !total || !response.body) {
|
|
23
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
24
|
+
}
|
|
25
|
+
const reader = response.body.getReader();
|
|
26
|
+
const chunks = [];
|
|
27
|
+
let loaded = 0;
|
|
28
|
+
for (;;) {
|
|
29
|
+
const { done, value } = await reader.read();
|
|
30
|
+
if (done)
|
|
31
|
+
break;
|
|
32
|
+
chunks.push(value);
|
|
33
|
+
loaded += value.length;
|
|
34
|
+
onProgress(capability, loaded, total);
|
|
35
|
+
}
|
|
36
|
+
const bytes = new Uint8Array(loaded);
|
|
37
|
+
let offset = 0;
|
|
38
|
+
for (const chunk of chunks) {
|
|
39
|
+
bytes.set(chunk, offset);
|
|
40
|
+
offset += chunk.length;
|
|
41
|
+
}
|
|
42
|
+
return bytes;
|
|
43
|
+
}
|
|
44
|
+
export class Kjarni {
|
|
45
|
+
#worker;
|
|
46
|
+
#next = 1;
|
|
47
|
+
#pending = new Map();
|
|
48
|
+
constructor(worker) {
|
|
49
|
+
this.#worker = worker;
|
|
50
|
+
this.#worker.onmessage = (event) => {
|
|
51
|
+
const msg = event.data;
|
|
52
|
+
const entry = this.#pending.get(msg.id);
|
|
53
|
+
if (!entry)
|
|
54
|
+
return;
|
|
55
|
+
if (msg.kind === "token") {
|
|
56
|
+
entry.onToken?.(msg.text);
|
|
57
|
+
return; // more to come; the promise settles on ok or err
|
|
58
|
+
}
|
|
59
|
+
this.#pending.delete(msg.id);
|
|
60
|
+
if (msg.kind === "err")
|
|
61
|
+
entry.reject(new Error(msg.message));
|
|
62
|
+
else
|
|
63
|
+
entry.resolve(msg.value);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Starts a worker and loads whichever models you name. */
|
|
67
|
+
static async load(options) {
|
|
68
|
+
const worker = new Worker(new URL("./worker.js", import.meta.url), { type: "module" });
|
|
69
|
+
const kjarni = new Kjarni(worker);
|
|
70
|
+
const capabilities = ["encoder", "classifier", "reranker", "chat"];
|
|
71
|
+
for (const capability of capabilities) {
|
|
72
|
+
const source = options[capability];
|
|
73
|
+
if (!source)
|
|
74
|
+
continue;
|
|
75
|
+
const bytes = await toBytes(source, capability, options.onProgress);
|
|
76
|
+
await kjarni.#send({ kind: "load", capability, bytes, modelId: options.chatModelId },
|
|
77
|
+
// The buffer is handed to the worker rather than copied. A 500MB model
|
|
78
|
+
// copied per load is a real pause and a doubled peak memory footprint.
|
|
79
|
+
[bytes.buffer]);
|
|
80
|
+
}
|
|
81
|
+
return kjarni;
|
|
82
|
+
}
|
|
83
|
+
#send(req, transfer = [], onToken) {
|
|
84
|
+
const id = this.#next++;
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
this.#pending.set(id, { resolve, reject, onToken });
|
|
87
|
+
this.#worker.postMessage({ ...req, id }, transfer);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/** One vector per input, in the order given. */
|
|
91
|
+
async encode(texts, normalize = true) {
|
|
92
|
+
return (await this.#send({ kind: "encode", texts, normalize }));
|
|
93
|
+
}
|
|
94
|
+
/** Cosine similarity between two texts, from -1 to 1. */
|
|
95
|
+
async similarity(a, b) {
|
|
96
|
+
const [va, vb] = await this.encode([a, b]);
|
|
97
|
+
return Kjarni.cosine(va, vb);
|
|
98
|
+
}
|
|
99
|
+
/** The best label for each input. */
|
|
100
|
+
async classify(texts) {
|
|
101
|
+
const batch = Array.isArray(texts) ? texts : [texts];
|
|
102
|
+
return (await this.#send({ kind: "classify", texts: batch }));
|
|
103
|
+
}
|
|
104
|
+
/** Every label this classifier can predict, in index order. */
|
|
105
|
+
async labels() {
|
|
106
|
+
return (await this.#send({ kind: "labels" }));
|
|
107
|
+
}
|
|
108
|
+
/** Documents reordered by relevance, best first. Indices point into your input. */
|
|
109
|
+
async rerank(query, documents, limit = documents.length) {
|
|
110
|
+
return (await this.#send({ kind: "rerank", query, documents, limit }));
|
|
111
|
+
}
|
|
112
|
+
/** The chat model's context window, in tokens. */
|
|
113
|
+
async contextSize() {
|
|
114
|
+
return (await this.#send({ kind: "contextSize" }));
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Generates a reply, yielding text as it arrives.
|
|
118
|
+
*
|
|
119
|
+
* for await (const piece of kjarni.chat("Explain RAG")) out.textContent += piece;
|
|
120
|
+
*/
|
|
121
|
+
async *chat(prompt, options = {}) {
|
|
122
|
+
const queue = [];
|
|
123
|
+
let wake = null;
|
|
124
|
+
const done = this.#send({
|
|
125
|
+
kind: "chat",
|
|
126
|
+
prompt,
|
|
127
|
+
maxNewTokens: options.maxNewTokens ?? 256,
|
|
128
|
+
temperature: options.temperature ?? 0,
|
|
129
|
+
}, [], (text) => {
|
|
130
|
+
queue.push(text);
|
|
131
|
+
wake?.();
|
|
132
|
+
});
|
|
133
|
+
let finished = false;
|
|
134
|
+
const settled = done.then(() => { finished = true; wake?.(); }, (e) => { finished = true; wake?.(); throw e; });
|
|
135
|
+
while (!finished || queue.length) {
|
|
136
|
+
if (queue.length) {
|
|
137
|
+
yield queue.shift();
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
await new Promise((r) => (wake = r));
|
|
141
|
+
}
|
|
142
|
+
await settled; // surfaces a generation error rather than ending quietly
|
|
143
|
+
}
|
|
144
|
+
/** Releases the models and stops the worker. */
|
|
145
|
+
async close() {
|
|
146
|
+
try {
|
|
147
|
+
await this.#send({ kind: "close" });
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
this.#worker.terminate();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Cosine similarity between two vectors. */
|
|
154
|
+
static cosine(a, b) {
|
|
155
|
+
let dot = 0, na = 0, nb = 0;
|
|
156
|
+
for (let i = 0; i < a.length; i++) {
|
|
157
|
+
dot += a[i] * b[i];
|
|
158
|
+
na += a[i] * a[i];
|
|
159
|
+
nb += b[i] * b[i];
|
|
160
|
+
}
|
|
161
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb) || 1e-9);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
export default Kjarni;
|
|
165
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** A model to load, either already in memory or somewhere to fetch it from. */
|
|
2
|
+
export type ModelSource = string | URL | ArrayBuffer | Uint8Array;
|
|
3
|
+
export type Capability = "encoder" | "classifier" | "reranker" | "chat";
|
|
4
|
+
export interface ClassifyResult {
|
|
5
|
+
label: string;
|
|
6
|
+
score: number;
|
|
7
|
+
index: number;
|
|
8
|
+
}
|
|
9
|
+
export interface RankedDocument {
|
|
10
|
+
index: number;
|
|
11
|
+
score: number;
|
|
12
|
+
text: string;
|
|
13
|
+
}
|
|
14
|
+
export type Request = {
|
|
15
|
+
id: number;
|
|
16
|
+
kind: "load";
|
|
17
|
+
capability: Capability;
|
|
18
|
+
bytes: Uint8Array;
|
|
19
|
+
modelId?: string;
|
|
20
|
+
} | {
|
|
21
|
+
id: number;
|
|
22
|
+
kind: "encode";
|
|
23
|
+
texts: string[];
|
|
24
|
+
normalize: boolean;
|
|
25
|
+
} | {
|
|
26
|
+
id: number;
|
|
27
|
+
kind: "classify";
|
|
28
|
+
texts: string[];
|
|
29
|
+
} | {
|
|
30
|
+
id: number;
|
|
31
|
+
kind: "labels";
|
|
32
|
+
} | {
|
|
33
|
+
id: number;
|
|
34
|
+
kind: "rerank";
|
|
35
|
+
query: string;
|
|
36
|
+
documents: string[];
|
|
37
|
+
limit: number;
|
|
38
|
+
} | {
|
|
39
|
+
id: number;
|
|
40
|
+
kind: "chat";
|
|
41
|
+
prompt: string;
|
|
42
|
+
maxNewTokens: number;
|
|
43
|
+
temperature: number;
|
|
44
|
+
} | {
|
|
45
|
+
id: number;
|
|
46
|
+
kind: "contextSize";
|
|
47
|
+
} | {
|
|
48
|
+
id: number;
|
|
49
|
+
kind: "close";
|
|
50
|
+
};
|
|
51
|
+
export type Response = {
|
|
52
|
+
id: number;
|
|
53
|
+
kind: "ok";
|
|
54
|
+
value: unknown;
|
|
55
|
+
} | {
|
|
56
|
+
id: number;
|
|
57
|
+
kind: "token";
|
|
58
|
+
text: string;
|
|
59
|
+
} | {
|
|
60
|
+
id: number;
|
|
61
|
+
kind: "err";
|
|
62
|
+
message: string;
|
|
63
|
+
};
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Messages between the page and the worker.
|
|
2
|
+
//
|
|
3
|
+
// Every request carries an id and is answered by exactly one Ok or Err with the
|
|
4
|
+
// same id, except `chat`, which streams any number of Token messages before its
|
|
5
|
+
// Ok. Keeping that contract in one file is what lets both sides be type-checked
|
|
6
|
+
// against the same shapes rather than agreeing by convention.
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAChF,8DAA8D"}
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// The worker. Everything expensive happens here so the page never janks.
|
|
2
|
+
//
|
|
3
|
+
// Inference in this build is synchronous: `encode` and `generate` occupy the
|
|
4
|
+
// thread until they return. On the main thread that is a frozen tab for the
|
|
5
|
+
// duration, which for a chat reply is seconds. Here it costs nothing, because
|
|
6
|
+
// this thread has nothing else to do.
|
|
7
|
+
import init, { WasmModel, WasmClassifier, WasmReranker, WasmChat, } from "../pkg/kjarni_wasm.js";
|
|
8
|
+
let model = null;
|
|
9
|
+
let classifier = null;
|
|
10
|
+
let reranker = null;
|
|
11
|
+
let chat = null;
|
|
12
|
+
let ready = null;
|
|
13
|
+
const post = (msg) => self.postMessage(msg);
|
|
14
|
+
/** Loads the wasm binary once, however many times this is called. */
|
|
15
|
+
function ensureWasm() {
|
|
16
|
+
ready ??= init();
|
|
17
|
+
return ready;
|
|
18
|
+
}
|
|
19
|
+
function handle(req) {
|
|
20
|
+
switch (req.kind) {
|
|
21
|
+
case "load": {
|
|
22
|
+
// Loading a second model of the same kind replaces the first, and the old
|
|
23
|
+
// one has to be freed explicitly: wasm-bindgen handles cannot be collected
|
|
24
|
+
// by the JS garbage collector, so dropping the reference leaks the weights.
|
|
25
|
+
switch (req.capability) {
|
|
26
|
+
case "encoder":
|
|
27
|
+
model?.free();
|
|
28
|
+
model = WasmModel.from_quantized(req.bytes);
|
|
29
|
+
return null;
|
|
30
|
+
case "classifier":
|
|
31
|
+
classifier?.free();
|
|
32
|
+
classifier = WasmClassifier.load(req.bytes);
|
|
33
|
+
return null;
|
|
34
|
+
case "reranker":
|
|
35
|
+
reranker?.free();
|
|
36
|
+
reranker = WasmReranker.load(req.bytes);
|
|
37
|
+
return null;
|
|
38
|
+
case "chat":
|
|
39
|
+
chat?.free();
|
|
40
|
+
chat = WasmChat.load(req.bytes, req.modelId ?? null);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
case "encode": {
|
|
45
|
+
if (!model)
|
|
46
|
+
throw new Error("no encoder loaded; call load({ encoder }) first");
|
|
47
|
+
// The binding returns every vector concatenated; split it back out here so
|
|
48
|
+
// callers get one array per input rather than doing the arithmetic.
|
|
49
|
+
const flat = model.encode(req.texts, req.normalize);
|
|
50
|
+
const dim = flat.length / req.texts.length;
|
|
51
|
+
const out = [];
|
|
52
|
+
for (let i = 0; i < req.texts.length; i++) {
|
|
53
|
+
out.push(flat.slice(i * dim, (i + 1) * dim));
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
case "classify": {
|
|
58
|
+
if (!classifier)
|
|
59
|
+
throw new Error("no classifier loaded; call load({ classifier }) first");
|
|
60
|
+
return req.texts.length === 1
|
|
61
|
+
? [classifier.classify(req.texts[0])]
|
|
62
|
+
: classifier.classify_batch(req.texts);
|
|
63
|
+
}
|
|
64
|
+
case "labels":
|
|
65
|
+
if (!classifier)
|
|
66
|
+
throw new Error("no classifier loaded");
|
|
67
|
+
return classifier.labels();
|
|
68
|
+
case "rerank":
|
|
69
|
+
if (!reranker)
|
|
70
|
+
throw new Error("no reranker loaded; call load({ reranker }) first");
|
|
71
|
+
return reranker.rerank(req.query, req.documents, req.limit);
|
|
72
|
+
case "contextSize":
|
|
73
|
+
if (!chat)
|
|
74
|
+
throw new Error("no chat model loaded");
|
|
75
|
+
return chat.context_size();
|
|
76
|
+
case "chat": {
|
|
77
|
+
if (!chat)
|
|
78
|
+
throw new Error("no chat model loaded; call load({ chat }) first");
|
|
79
|
+
// Genuinely incremental: the callback fires between decoding steps, and
|
|
80
|
+
// each postMessage reaches the page while the model is still generating.
|
|
81
|
+
// This is the reason the whole wrapper is worth having, since the same
|
|
82
|
+
// call on the main thread would freeze the tab until the reply finished.
|
|
83
|
+
chat.generate_stream(req.prompt, req.maxNewTokens, req.temperature, (piece) => {
|
|
84
|
+
post({ id: req.id, kind: "token", text: piece });
|
|
85
|
+
});
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
case "close": {
|
|
89
|
+
model?.free();
|
|
90
|
+
classifier?.free();
|
|
91
|
+
reranker?.free();
|
|
92
|
+
chat?.free();
|
|
93
|
+
model = classifier = reranker = chat = null;
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
self.onmessage = async (event) => {
|
|
99
|
+
const req = event.data;
|
|
100
|
+
try {
|
|
101
|
+
await ensureWasm();
|
|
102
|
+
const value = handle(req);
|
|
103
|
+
post({ id: req.id, kind: "ok", value });
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
post({ id: req.id, kind: "err", message: e instanceof Error ? e.message : String(e) });
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kjarni-wasm",
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "Local AI inference in the browser: embeddings, classification, reranking, semantic search and chat, running in a Web Worker.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"pkg"
|
|
17
|
+
],
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"scripts": {
|
|
20
|
+
"prebuild": "rm -rf pkg && cp -r ../pkg pkg && rm -f pkg/.gitignore",
|
|
21
|
+
"build": "tsc -p tsconfig.json",
|
|
22
|
+
"prepack": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"typescript": "^5.6.0"
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/olafurjohannsson/kjarni.git",
|
|
31
|
+
"directory": "crates/kjarni-wasm"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://kjarni.ai",
|
|
34
|
+
"keywords": [
|
|
35
|
+
"ai",
|
|
36
|
+
"embeddings",
|
|
37
|
+
"webassembly",
|
|
38
|
+
"wasm",
|
|
39
|
+
"local",
|
|
40
|
+
"offline",
|
|
41
|
+
"bert",
|
|
42
|
+
"rag",
|
|
43
|
+
"semantic-search",
|
|
44
|
+
"web-worker"
|
|
45
|
+
]
|
|
46
|
+
}
|