@tryhamster/gerbil 1.1.2 → 1.1.3
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 +18 -16
- package/dist/cli.mjs +1 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/gpu/hooks.d.mts +3 -3
- package/dist/gpu/hooks.mjs +5 -5
- package/dist/gpu/hooks.mjs.map +1 -1
- package/dist/index-Dgmb2kE3.d.mts.map +1 -1
- package/package.json +1 -2
- package/docs/PROJECT-STATE.md +0 -321
- package/docs/adding-a-model-family.md +0 -280
- package/docs/ai-sdk.md +0 -205
- package/docs/architecture/README.md +0 -84
- package/docs/architecture/caching.md +0 -227
- package/docs/architecture/inference.md +0 -176
- package/docs/architecture/overview.md +0 -189
- package/docs/architecture/streaming.md +0 -261
- package/docs/architecture/webgpu.md +0 -213
- package/docs/browser.md +0 -762
- package/docs/cli.md +0 -155
- package/docs/embeddings.md +0 -156
- package/docs/frameworks.md +0 -90
- package/docs/gerbil-site-native-migration.md +0 -217
- package/docs/gpu-engine/architectures.md +0 -398
- package/docs/gpu-engine/ir.md +0 -372
- package/docs/gpu-engine/kernels.md +0 -718
- package/docs/gpu-engine/paper.html +0 -1759
- package/docs/gpu-engine/paper.md +0 -2109
- package/docs/gpu-engine/safetensors.md +0 -312
- package/docs/gpu-engine/tokenizer.md +0 -302
- package/docs/kernel-research-queue.md +0 -85
- package/docs/mcp-client.md +0 -224
- package/docs/mcp.md +0 -109
- package/docs/memory-rag.md +0 -91
- package/docs/memory.md +0 -301
- package/docs/metal-safari-intel.md +0 -190
- package/docs/mobile-failure-diagnosis.md +0 -124
- package/docs/mobile.md +0 -99
- package/docs/observability.md +0 -230
- package/docs/onnx-removal-plan.md +0 -339
- package/docs/repl.md +0 -473
- package/docs/research/autoresearch-portable.md +0 -933
- package/docs/research/dispatch-reduction-hivemind.md +0 -84
- package/docs/research/ios-safari-model-caching.md +0 -117
- package/docs/research/mobile-webgpu-speed-fusion.md +0 -135
- package/docs/research/native-stt-model-selection.md +0 -49
- package/docs/research/native-tts-model-selection.md +0 -90
- package/docs/research/native-vs-chromium-decision.md +0 -152
- package/docs/research/nemotron-mamba2-inference.md +0 -910
- package/docs/research/qwen35-multimodal.md +0 -293
- package/docs/research/qwen36-gemma4-targets.md +0 -337
- package/docs/research/sota-embedding-models.md +0 -179
- package/docs/research/sota-mobile-models-2026.md +0 -263
- package/docs/research/sota-modality-models.md +0 -202
- package/docs/research/tps-baselines.md +0 -71
- package/docs/research/webgpu-m4-reference.md +0 -104
- package/docs/site-update-plan.md +0 -155
- package/docs/skills.md +0 -261
- package/docs/structured-output.md +0 -123
- package/docs/stt.md +0 -111
- package/docs/tools.md +0 -304
- package/docs/tts.md +0 -147
- package/docs/vision.md +0 -158
package/docs/ai-sdk.md
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
# Gerbil + AI SDK
|
|
2
|
-
|
|
3
|
-
Gerbil works as a [Vercel AI SDK v5](https://sdk.vercel.ai/) provider, supporting text generation, embeddings, speech synthesis (TTS), and transcription (STT).
|
|
4
|
-
|
|
5
|
-
> **Pre-1.0 note.** The AI SDK provider routes through the `Gerbil` class, which now runs on
|
|
6
|
-
> the native WebGPU engine (no ONNX / transformers.js). TTS uses Kani-TTS-2, STT uses
|
|
7
|
-
> Moonshine, and embeddings use EmbeddingGemma regardless of the model-id string you pass —
|
|
8
|
-
> legacy ids like `kokoro-82m` / `whisper-tiny.en` are vestigial labels and the device must
|
|
9
|
-
> have WebGPU (there is no CPU/WASM fallback). The first-class surface for the engine is
|
|
10
|
-
> `WebGPUEngine` / `useEngine` (see the [README](../README.md), [TTS](./tts.md),
|
|
11
|
-
> [STT](./stt.md), [Embeddings](./embeddings.md) docs).
|
|
12
|
-
|
|
13
|
-
## Setup
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
import { generateText, streamText } from "ai";
|
|
17
|
-
import { gerbil } from "@tryhamster/gerbil/ai";
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Text Generation
|
|
21
|
-
|
|
22
|
-
### Generate Text
|
|
23
|
-
|
|
24
|
-
```typescript
|
|
25
|
-
const { text } = await generateText({
|
|
26
|
-
model: gerbil("qwen3-0.6b"),
|
|
27
|
-
prompt: "Write a commit message for adding dark mode",
|
|
28
|
-
});
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### Stream Text
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
const stream = streamText({
|
|
35
|
-
model: gerbil("qwen3-0.6b"),
|
|
36
|
-
prompt: "Explain React hooks",
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
for await (const chunk of stream.textStream) {
|
|
40
|
-
process.stdout.write(chunk);
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### With System Prompt
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
const { text } = await generateText({
|
|
48
|
-
model: gerbil("qwen3-0.6b"),
|
|
49
|
-
system: "You are a helpful coding assistant.",
|
|
50
|
-
prompt: "How do I handle errors in async/await?",
|
|
51
|
-
});
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Thinking Mode
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
import { createGerbil } from "@tryhamster/gerbil/ai";
|
|
58
|
-
|
|
59
|
-
const local = createGerbil({ device: "gpu" });
|
|
60
|
-
|
|
61
|
-
const { text } = await generateText({
|
|
62
|
-
model: local("qwen3-0.6b", { thinking: true }),
|
|
63
|
-
prompt: "What is 127 × 43?",
|
|
64
|
-
});
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Embeddings
|
|
68
|
-
|
|
69
|
-
> **Native.** `gerbil.embedding()` runs native EmbeddingGemma-300M on the WebGPU engine
|
|
70
|
-
> (768-dim; the `all-MiniLM-L6-v2` default id is a vestigial label — the old ONNX
|
|
71
|
-
> MiniLM/BGE/GTE lane has been removed). For direct control use `engine.embed()` — see
|
|
72
|
-
> [Embeddings docs](./embeddings.md). Requires WebGPU.
|
|
73
|
-
|
|
74
|
-
Generate text embeddings for semantic search, similarity, and RAG:
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
import { embed, embedMany } from "ai";
|
|
78
|
-
import { gerbil } from "@tryhamster/gerbil/ai";
|
|
79
|
-
|
|
80
|
-
// Single embedding (768-dim EmbeddingGemma vector)
|
|
81
|
-
const { embedding } = await embed({
|
|
82
|
-
model: gerbil.embedding(),
|
|
83
|
-
value: "Hello world",
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// Multiple embeddings
|
|
87
|
-
const { embeddings } = await embedMany({
|
|
88
|
-
model: gerbil.embedding(),
|
|
89
|
-
values: ["Hello", "World", "How are you?"],
|
|
90
|
-
});
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## Speech Generation (TTS)
|
|
94
|
-
|
|
95
|
-
> **Native.** `gerbil.speech()` runs Kani-TTS-2 on the native engine (the `kokoro-82m`
|
|
96
|
-
> default id is a vestigial label). For direct control use `engine.speak()` — see
|
|
97
|
-
> [TTS docs](./tts.md). Requires WebGPU.
|
|
98
|
-
|
|
99
|
-
Generate speech from text:
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
import { experimental_generateSpeech as generateSpeech } from "ai";
|
|
103
|
-
import { gerbil } from "@tryhamster/gerbil/ai";
|
|
104
|
-
|
|
105
|
-
const result = await generateSpeech({
|
|
106
|
-
model: gerbil.speech(), // native Kani-TTS-2
|
|
107
|
-
text: "Hello, welcome to Gerbil!",
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// result.audio is the synthesized PCM clip
|
|
111
|
-
await writeFile("output.wav", result.audio);
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
> Voice/speed selection from the old Kokoro lane no longer applies — Kani-TTS-2 uses its own
|
|
115
|
-
> default voice. For full control over the native speech path, use `engine.speak()` directly
|
|
116
|
-
> (see [TTS docs](./tts.md)).
|
|
117
|
-
|
|
118
|
-
## Transcription (STT)
|
|
119
|
-
|
|
120
|
-
> **Native.** `gerbil.transcription()` runs Moonshine on the native engine (the
|
|
121
|
-
> `whisper-tiny.en` default id is a vestigial label; English only). For direct control use
|
|
122
|
-
> `MoonshineSTT` — see [STT docs](./stt.md). Requires WebGPU.
|
|
123
|
-
|
|
124
|
-
Transcribe audio to text:
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
import { experimental_transcribe as transcribe } from "ai";
|
|
128
|
-
import { gerbil } from "@tryhamster/gerbil/ai";
|
|
129
|
-
import { readFile } from "fs/promises";
|
|
130
|
-
|
|
131
|
-
const result = await transcribe({
|
|
132
|
-
model: gerbil.transcription(), // native Moonshine (English)
|
|
133
|
-
audio: await readFile("audio.wav"),
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
console.log(result.text); // "Hello world"
|
|
137
|
-
console.log(result.language); // "en"
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
> The Whisper model family (multilingual variants, timestamped segments) has been removed.
|
|
141
|
-
> The native path is Moonshine, English-only, and does not produce timestamps. For full
|
|
142
|
-
> control use `MoonshineSTT` directly (see [STT docs](./stt.md)).
|
|
143
|
-
|
|
144
|
-
## Custom Provider
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
import { createGerbil } from "@tryhamster/gerbil/ai";
|
|
148
|
-
|
|
149
|
-
const local = createGerbil({
|
|
150
|
-
device: "gpu",
|
|
151
|
-
dtype: "q4",
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Text generation
|
|
155
|
-
const { text } = await generateText({
|
|
156
|
-
model: local("qwen3-0.6b"),
|
|
157
|
-
prompt: "Hello",
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// Speech
|
|
161
|
-
const speech = await generateSpeech({
|
|
162
|
-
model: local.speech(),
|
|
163
|
-
text: "Hello",
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// Transcription
|
|
167
|
-
const transcript = await transcribe({
|
|
168
|
-
model: local.transcription(),
|
|
169
|
-
audio: audioData,
|
|
170
|
-
});
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
## Model Preloading
|
|
174
|
-
|
|
175
|
-
Download models ahead of time via the provider:
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
import { gerbil } from "@tryhamster/gerbil/ai";
|
|
179
|
-
|
|
180
|
-
// Check if cached
|
|
181
|
-
if (!(await gerbil.isCached("qwen3-0.6b"))) {
|
|
182
|
-
// Preload during app init
|
|
183
|
-
await gerbil.preload("qwen3-0.6b", {
|
|
184
|
-
onProgress: (p) => console.log(p.status, p.progress),
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Later: generateText loads instantly from cache
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
## Specification
|
|
192
|
-
|
|
193
|
-
Gerbil implements the following AI SDK v5 interfaces:
|
|
194
|
-
|
|
195
|
-
| Interface | Purpose | Method |
|
|
196
|
-
|-----------|---------|--------|
|
|
197
|
-
| `LanguageModelV2` | Text generation | `gerbil(modelId)` |
|
|
198
|
-
| `SpeechModelV2` | Text-to-Speech | `gerbil.speech()` |
|
|
199
|
-
| `TranscriptionModelV2` | Speech-to-Text | `gerbil.transcription()` |
|
|
200
|
-
|
|
201
|
-
All models support:
|
|
202
|
-
- `specificationVersion: "v2"`
|
|
203
|
-
- Proper warning reporting
|
|
204
|
-
- Request/response metadata
|
|
205
|
-
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
# Gerbil Architecture
|
|
2
|
-
|
|
3
|
-
Technical deep-dive into how Gerbil works under the hood.
|
|
4
|
-
|
|
5
|
-
## Contents
|
|
6
|
-
|
|
7
|
-
| Document | Description |
|
|
8
|
-
|----------|-------------|
|
|
9
|
-
| [Overview](./overview.md) | High-level architecture and design decisions |
|
|
10
|
-
| [Inference Pipeline](./inference.md) | ONNX Runtime, transformers.js, quantization |
|
|
11
|
-
| [WebGPU](./webgpu.md) | GPU acceleration in browser and Node.js |
|
|
12
|
-
| [Streaming](./streaming.md) | Web Worker architecture and token streaming |
|
|
13
|
-
| [Caching](./caching.md) | Model caching strategies |
|
|
14
|
-
|
|
15
|
-
## Quick Overview
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
┌───────────────────────────────────────────────────────────────────┐
|
|
19
|
-
│ Your Application │
|
|
20
|
-
├───────────────────────────────────────────────────────────────────┤
|
|
21
|
-
│ │
|
|
22
|
-
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
|
23
|
-
│ │ Browser │ │ Node.js │ │ CLI/REPL │ │
|
|
24
|
-
│ │ (WebGPU) │ │ (CPU/WebGPU)│ │ (CPU/WebGPU) │ │
|
|
25
|
-
│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
|
|
26
|
-
│ │ │ │ │
|
|
27
|
-
│ ▼ ▼ ▼ │
|
|
28
|
-
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
29
|
-
│ │ Gerbil Core │ │
|
|
30
|
-
│ │ ┌─────────┐ ┌──────────┐ ┌────────┐ ┌───────────────┐ │ │
|
|
31
|
-
│ │ │ Models │ │ Generate │ │ Stream │ │ JSON/Embed │ │ │
|
|
32
|
-
│ │ └────┬────┘ └────┬─────┘ └───┬────┘ └───────┬───────┘ │ │
|
|
33
|
-
│ └───────┼────────────┼────────────┼───────────────┼───────────┘ │
|
|
34
|
-
│ │ │ │ │ │
|
|
35
|
-
│ ▼ ▼ ▼ ▼ │
|
|
36
|
-
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
37
|
-
│ │ transformers.js │ │
|
|
38
|
-
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
|
|
39
|
-
│ │ │ Tokenizer │ │ Model │ │ TextStreamer │ │ │
|
|
40
|
-
│ │ └─────────────┘ └──────┬──────┘ └─────────────────────┘ │ │
|
|
41
|
-
│ └──────────────────────────┼──────────────────────────────────┘ │
|
|
42
|
-
│ │ │
|
|
43
|
-
│ ▼ │
|
|
44
|
-
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
45
|
-
│ │ ONNX Runtime │ │
|
|
46
|
-
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
|
|
47
|
-
│ │ │ WebGPU │ │ CPU │ │ WASM │ │ │
|
|
48
|
-
│ │ │ (Browser) │ │ (Node.js) │ │ (Fallback) │ │ │
|
|
49
|
-
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │
|
|
50
|
-
│ └─────────────────────────────────────────────────────────────┘ │
|
|
51
|
-
│ │
|
|
52
|
-
└───────────────────────────────────────────────────────────────────┘
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Key Design Decisions
|
|
56
|
-
|
|
57
|
-
### 1. transformers.js as the Foundation
|
|
58
|
-
|
|
59
|
-
We use [Hugging Face transformers.js](https://huggingface.co/docs/transformers.js) which provides:
|
|
60
|
-
- Pre-converted ONNX models from Hugging Face Hub
|
|
61
|
-
- Tokenizers that match the original models exactly
|
|
62
|
-
- Unified API across CPU/GPU/WASM backends
|
|
63
|
-
|
|
64
|
-
### 2. WebGPU First
|
|
65
|
-
|
|
66
|
-
WebGPU provides 5-10x speedup over CPU for inference:
|
|
67
|
-
- **Browser**: Native WebGPU via `navigator.gpu`
|
|
68
|
-
- **Node.js**: Headless Chrome as a WebGPU accelerator (ChromeGPUBackend)
|
|
69
|
-
|
|
70
|
-
### 3. Quantization for Speed
|
|
71
|
-
|
|
72
|
-
All models use quantized weights:
|
|
73
|
-
- **q4f16**: 4-bit weights, fp16 compute (WebGPU)
|
|
74
|
-
- **q4**: 4-bit weights, fp32 compute (CPU/WASM)
|
|
75
|
-
|
|
76
|
-
This reduces model size by ~4x and improves inference speed.
|
|
77
|
-
|
|
78
|
-
### 4. Streaming via Web Workers
|
|
79
|
-
|
|
80
|
-
Browser inference runs in a Web Worker to:
|
|
81
|
-
- Keep the UI responsive during model loading
|
|
82
|
-
- Stream tokens in real-time without blocking
|
|
83
|
-
- Isolate GPU memory from the main thread
|
|
84
|
-
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
# Model Caching
|
|
2
|
-
|
|
3
|
-
How Gerbil caches models for fast subsequent loads.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
Model files are large (100MB - 500MB). Gerbil caches them locally to avoid re-downloading:
|
|
8
|
-
|
|
9
|
-
| Environment | Cache Location | Mechanism |
|
|
10
|
-
|-------------|----------------|-----------|
|
|
11
|
-
| Browser | IndexedDB | transformers.js built-in |
|
|
12
|
-
| Node.js (CPU) | `~/.cache/huggingface/hub` | transformers.js built-in |
|
|
13
|
-
| Node.js (WebGPU) | Chrome's IndexedDB | Via ChromeGPUBackend |
|
|
14
|
-
|
|
15
|
-
## Browser Caching
|
|
16
|
-
|
|
17
|
-
### IndexedDB
|
|
18
|
-
|
|
19
|
-
transformers.js automatically caches model files in IndexedDB:
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
IndexedDB
|
|
23
|
-
└── transformers-cache
|
|
24
|
-
└── onnx-community/Qwen3-0.6B-ONNX
|
|
25
|
-
├── tokenizer.json
|
|
26
|
-
├── config.json
|
|
27
|
-
├── model_q4f16.onnx
|
|
28
|
-
└── ...
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### Cache Behavior
|
|
32
|
-
|
|
33
|
-
1. **First load**: Downloads from Hugging Face Hub (~15-30s)
|
|
34
|
-
2. **Subsequent loads**: Reads from IndexedDB (~1-2s)
|
|
35
|
-
|
|
36
|
-
### Checking Cache
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
import { getWebGPUInfo } from "@tryhamster/gerbil/browser";
|
|
40
|
-
|
|
41
|
-
// Models are cached per-origin
|
|
42
|
-
// Same origin = same cache
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### Clearing Cache
|
|
46
|
-
|
|
47
|
-
```javascript
|
|
48
|
-
// In browser DevTools:
|
|
49
|
-
indexedDB.deleteDatabase("transformers-cache");
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Node.js CPU Caching
|
|
53
|
-
|
|
54
|
-
### Hugging Face Hub Cache
|
|
55
|
-
|
|
56
|
-
transformers.js uses the standard HF cache directory:
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
~/.cache/huggingface/hub/
|
|
60
|
-
└── models--onnx-community--Qwen3-0.6B-ONNX/
|
|
61
|
-
├── blobs/
|
|
62
|
-
│ └── [sha256 hashes]
|
|
63
|
-
├── refs/
|
|
64
|
-
│ └── main
|
|
65
|
-
└── snapshots/
|
|
66
|
-
└── [commit hash]/
|
|
67
|
-
├── tokenizer.json
|
|
68
|
-
├── config.json
|
|
69
|
-
└── model_q4.onnx
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Environment Variables
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
# Custom cache directory
|
|
76
|
-
export HF_HOME=/path/to/cache
|
|
77
|
-
export TRANSFORMERS_CACHE=/path/to/cache
|
|
78
|
-
|
|
79
|
-
# Offline mode (use cache only)
|
|
80
|
-
export TRANSFORMERS_OFFLINE=1
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### CLI Cache Management
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
# View cache
|
|
87
|
-
npx @tryhamster/gerbil cache
|
|
88
|
-
|
|
89
|
-
# Clear cache
|
|
90
|
-
npx @tryhamster/gerbil cache --clean
|
|
91
|
-
|
|
92
|
-
# Clear old models
|
|
93
|
-
npx @tryhamster/gerbil cache --older-than 30
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Node.js WebGPU Caching
|
|
97
|
-
|
|
98
|
-
### ChromeGPUBackend Cache
|
|
99
|
-
|
|
100
|
-
When using WebGPU in Node.js, models are cached in Chrome's IndexedDB:
|
|
101
|
-
|
|
102
|
-
```
|
|
103
|
-
~/.gerbil/chrome-cache/
|
|
104
|
-
└── Default/
|
|
105
|
-
└── IndexedDB/
|
|
106
|
-
└── http_127.0.0.1_43724.indexeddb.leveldb/
|
|
107
|
-
└── [model cache]
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### Why a Fixed Port?
|
|
111
|
-
|
|
112
|
-
The ChromeGPUBackend uses port 43724 ("GERBI") for a critical reason:
|
|
113
|
-
|
|
114
|
-
IndexedDB caches are **origin-specific**. The origin includes:
|
|
115
|
-
- Protocol: `http://`
|
|
116
|
-
- Host: `127.0.0.1`
|
|
117
|
-
- Port: `43724`
|
|
118
|
-
|
|
119
|
-
A fixed port ensures the same origin every time → same cache.
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
const GERBIL_LOCAL_PORT = 43724; // "GERBI" on phone keypad
|
|
123
|
-
|
|
124
|
-
// Always same origin:
|
|
125
|
-
// http://127.0.0.1:43724
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### Cache Persistence
|
|
129
|
-
|
|
130
|
-
The Chrome user data directory persists between runs:
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
this.userDataDir = join(homedir(), ".gerbil", "chrome-cache");
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
This means:
|
|
137
|
-
- Model downloads are cached
|
|
138
|
-
- Shader compilations are cached
|
|
139
|
-
- ~1.5s startup when cached vs ~20s first run
|
|
140
|
-
|
|
141
|
-
## Cache Sizes
|
|
142
|
-
|
|
143
|
-
| Model | Download Size | Cache Size |
|
|
144
|
-
|-------|--------------|------------|
|
|
145
|
-
| qwen3-0.6b | ~400MB | ~400MB |
|
|
146
|
-
| smollm2-360m | ~250MB | ~250MB |
|
|
147
|
-
| smollm2-135m | ~100MB | ~100MB |
|
|
148
|
-
|
|
149
|
-
## Preloading Models
|
|
150
|
-
|
|
151
|
-
### Browser
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
// Preload during idle time
|
|
155
|
-
const gerbil = await createGerbilWorker({
|
|
156
|
-
modelId: "qwen3-0.6b",
|
|
157
|
-
onProgress: (p) => {
|
|
158
|
-
if (p.status === "ready") {
|
|
159
|
-
console.log("Model cached and ready");
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
// Model is now in IndexedDB for instant loads
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### Node.js
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
// Preload in background
|
|
171
|
-
const g = new Gerbil();
|
|
172
|
-
await g.loadModel("qwen3-0.6b");
|
|
173
|
-
// Model is now in HF cache
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### CLI
|
|
177
|
-
|
|
178
|
-
```bash
|
|
179
|
-
# Download without running
|
|
180
|
-
npx @tryhamster/gerbil info -m qwen3-0.6b
|
|
181
|
-
# Model is now cached
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
## Offline Usage
|
|
185
|
-
|
|
186
|
-
Once cached, models work offline:
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
// Browser: Works if model is in IndexedDB
|
|
190
|
-
const gerbil = await createGerbilWorker({ modelId: "qwen3-0.6b" });
|
|
191
|
-
|
|
192
|
-
// Node.js: Set offline mode
|
|
193
|
-
process.env.TRANSFORMERS_OFFLINE = "1";
|
|
194
|
-
const g = new Gerbil();
|
|
195
|
-
await g.loadModel("qwen3-0.6b"); // Uses cache only
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
## Troubleshooting
|
|
199
|
-
|
|
200
|
-
### "Model not found" after cache clear
|
|
201
|
-
|
|
202
|
-
Re-download by loading the model:
|
|
203
|
-
|
|
204
|
-
```typescript
|
|
205
|
-
await g.loadModel("qwen3-0.6b"); // Will re-download
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
### Cache taking too much space
|
|
209
|
-
|
|
210
|
-
```bash
|
|
211
|
-
# View cache size
|
|
212
|
-
npx @tryhamster/gerbil cache
|
|
213
|
-
|
|
214
|
-
# Clear old models
|
|
215
|
-
npx @tryhamster/gerbil cache --older-than 7
|
|
216
|
-
|
|
217
|
-
# Clear everything
|
|
218
|
-
npx @tryhamster/gerbil cache --clean
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### Browser cache not persisting
|
|
222
|
-
|
|
223
|
-
Check browser settings:
|
|
224
|
-
- Cookies/site data must be allowed
|
|
225
|
-
- IndexedDB must not be blocked
|
|
226
|
-
- Storage quota must not be exceeded
|
|
227
|
-
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
# Inference Pipeline
|
|
2
|
-
|
|
3
|
-
How Gerbil runs LLM inference using ONNX Runtime and transformers.js.
|
|
4
|
-
|
|
5
|
-
## The Stack
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
┌─────────────────────────────────────┐
|
|
9
|
-
│ transformers.js │ ← Tokenization, model loading, generation
|
|
10
|
-
├─────────────────────────────────────┤
|
|
11
|
-
│ ONNX Runtime │ ← Neural network execution
|
|
12
|
-
├─────────────────────────────────────┤
|
|
13
|
-
│ WebGPU │ CPU │ WASM │ ← Execution backends
|
|
14
|
-
└─────────────────────────────────────┘
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## transformers.js
|
|
18
|
-
|
|
19
|
-
[transformers.js](https://huggingface.co/docs/transformers.js) is a JavaScript port of Hugging Face Transformers that runs ONNX models in the browser and Node.js.
|
|
20
|
-
|
|
21
|
-
### Model Loading
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
import { AutoModelForCausalLM, AutoTokenizer } from "@huggingface/transformers";
|
|
25
|
-
|
|
26
|
-
const tokenizer = await AutoTokenizer.from_pretrained(modelId);
|
|
27
|
-
const model = await AutoModelForCausalLM.from_pretrained(modelId, {
|
|
28
|
-
dtype: "q4f16", // Quantization
|
|
29
|
-
device: "webgpu", // Backend
|
|
30
|
-
});
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Generation
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
const inputs = tokenizer.apply_chat_template(messages, {
|
|
37
|
-
add_generation_prompt: true,
|
|
38
|
-
return_dict: true,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const output = await model.generate({
|
|
42
|
-
...inputs,
|
|
43
|
-
max_new_tokens: 256,
|
|
44
|
-
temperature: 0.7,
|
|
45
|
-
do_sample: true,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const text = tokenizer.decode(output[0], { skip_special_tokens: true });
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Streaming with TextStreamer
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
const streamer = new TextStreamer(tokenizer, {
|
|
55
|
-
skip_prompt: true,
|
|
56
|
-
skip_special_tokens: true,
|
|
57
|
-
callback_function: (text) => {
|
|
58
|
-
console.log(text); // Called for each token
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
await model.generate({ ...inputs, streamer });
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## ONNX Runtime
|
|
66
|
-
|
|
67
|
-
ONNX Runtime is the execution engine that runs the neural network operations.
|
|
68
|
-
|
|
69
|
-
### Backends
|
|
70
|
-
|
|
71
|
-
| Backend | Environment | Speed | Notes |
|
|
72
|
-
|---------|-------------|-------|-------|
|
|
73
|
-
| **WebGPU** | Browser, Chrome | ~70-100 tok/s | Fastest, requires GPU |
|
|
74
|
-
| **CPU** | Node.js | ~10-30 tok/s | Uses SIMD, good on Apple Silicon |
|
|
75
|
-
| **WASM** | Browser fallback | ~5-10 tok/s | Works everywhere |
|
|
76
|
-
|
|
77
|
-
### Execution Providers
|
|
78
|
-
|
|
79
|
-
ONNX Runtime selects execution providers based on availability:
|
|
80
|
-
|
|
81
|
-
```
|
|
82
|
-
WebGPU EP → WASM EP → CPU EP
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Gerbil explicitly requests the desired backend:
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
// For WebGPU
|
|
89
|
-
await AutoModelForCausalLM.from_pretrained(modelId, { device: "webgpu" });
|
|
90
|
-
|
|
91
|
-
// For CPU
|
|
92
|
-
await pipeline("text-generation", modelId, { device: "cpu" });
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Quantization
|
|
96
|
-
|
|
97
|
-
Quantization reduces model size and improves inference speed by using lower-precision numbers.
|
|
98
|
-
|
|
99
|
-
### Quantization Types
|
|
100
|
-
|
|
101
|
-
| Type | Weights | Compute | Size Reduction | Use Case |
|
|
102
|
-
|------|---------|---------|----------------|----------|
|
|
103
|
-
| **fp32** | 32-bit float | 32-bit | 1x (baseline) | Training |
|
|
104
|
-
| **fp16** | 16-bit float | 16-bit | 2x | GPU inference |
|
|
105
|
-
| **q4f16** | 4-bit int | 16-bit | ~4x | WebGPU inference |
|
|
106
|
-
| **q4** | 4-bit int | 32-bit | ~4x | CPU inference |
|
|
107
|
-
|
|
108
|
-
### Why q4f16 for WebGPU?
|
|
109
|
-
|
|
110
|
-
WebGPU shaders work best with fp16 compute. The `q4f16` format:
|
|
111
|
-
- Stores weights as 4-bit integers (small download)
|
|
112
|
-
- Dequantizes to fp16 during inference (fast on GPU)
|
|
113
|
-
- Maintains good quality for small models
|
|
114
|
-
|
|
115
|
-
### Model Sizes
|
|
116
|
-
|
|
117
|
-
| Model | Original | q4f16 | Download |
|
|
118
|
-
|-------|----------|-------|----------|
|
|
119
|
-
| Qwen3-0.6B | ~2.4GB | ~400MB | ~400MB |
|
|
120
|
-
| SmolLM2-360M | ~1.4GB | ~250MB | ~250MB |
|
|
121
|
-
| SmolLM2-135M | ~540MB | ~100MB | ~100MB |
|
|
122
|
-
|
|
123
|
-
## Tokenization
|
|
124
|
-
|
|
125
|
-
### Chat Templates
|
|
126
|
-
|
|
127
|
-
Gerbil uses model-specific chat templates to format conversations:
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
const messages = [
|
|
131
|
-
{ role: "system", content: "You are helpful." },
|
|
132
|
-
{ role: "user", content: "Hello!" },
|
|
133
|
-
];
|
|
134
|
-
|
|
135
|
-
const inputs = tokenizer.apply_chat_template(messages, {
|
|
136
|
-
add_generation_prompt: true, // Add assistant turn start
|
|
137
|
-
return_dict: true, // Return input_ids + attention_mask
|
|
138
|
-
enable_thinking: true, // Qwen3 thinking mode
|
|
139
|
-
});
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Thinking Mode (Qwen3)
|
|
143
|
-
|
|
144
|
-
Qwen3 models support a "thinking" mode where the model shows reasoning:
|
|
145
|
-
|
|
146
|
-
```
|
|
147
|
-
<think>
|
|
148
|
-
Let me work through this step by step...
|
|
149
|
-
127 × 43 = 127 × 40 + 127 × 3 = 5080 + 381 = 5461
|
|
150
|
-
</think>
|
|
151
|
-
The answer is 5461.
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Enabled via `enable_thinking: true` in the chat template.
|
|
155
|
-
|
|
156
|
-
## KV Cache
|
|
157
|
-
|
|
158
|
-
The Key-Value cache stores intermediate attention states to speed up autoregressive generation:
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
const { past_key_values, sequences } = await model.generate({
|
|
162
|
-
...inputs,
|
|
163
|
-
past_key_values: previousCache, // Reuse from last turn
|
|
164
|
-
return_dict_in_generate: true,
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
// Save for next turn
|
|
168
|
-
cache = past_key_values;
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
Benefits:
|
|
172
|
-
- Faster multi-turn conversations
|
|
173
|
-
- Reduced compute for long contexts
|
|
174
|
-
|
|
175
|
-
Gerbil manages the KV cache automatically for multi-turn chat.
|
|
176
|
-
|