anyformer 0.4.11 → 0.4.13

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/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.13 — 2026-10-30
4
+
5
+ ### Added
6
+ - `diskformer-onnx` back end: ONNX exports that transformers.js has no model class for, run by a diskformer hand
7
+ engine on onnxruntime-web WebGPU (the engine pins onnxruntime-web 1.30.0). Ranks second, after `transformers`, so
8
+ transformers.js takes a model over once it adds the class. Streams text, passes `signal` (an abort ends the reply
9
+ quietly), `thinking` (off by default), `tools` (tool calls stay raw text) and `repetitionPenalty`; image blocks take
10
+ ImageData, ImageBitmap, a URL or `{ data, width, height }`, audio blocks a 16 kHz Float32Array of at most 30 s.
11
+ `session.baseUrl` names the URLs its Cache Storage entries ('diskformer-models', listed by `stored()`) are keyed by.
12
+ - `MODELS.chat.LFM3_OMNI_2B`: `LiquidAI/LFM3-Omni-2B-ONNX` (task `any-to-any`, 2.14 GB: decoder_q4f16,
13
+ vision_encoder_q8, audio_encoder) on `diskformer-onnx`, asked for by LocalMind for Chirag. Browser test `omni`
14
+ (Chrome, this Mac, 2026-10-30), from local files and from Hugging Face: diskformer's three gate cases through
15
+ `session.generate()` equal the text Liquid's runner gives on onnxruntime's CPU EP (text 7 tokens, image 32, speech
16
+ 32); the image reply streams in 32 chunks; an abort after 3 chunks ends the reply; a `get_weather` tool list gives a
17
+ Qwen3-Coder XML tool call.
18
+
19
+ ### Changed
20
+ - diskformer pin 53c976e (v0.6.9) → 48f2c2a (`engines/lfm3_omni.js`; adds files only, no other engine changed).
21
+ - `test/browser/run.mjs --models` serves subfolders (a repo layout), still only inside the named folders.
22
+
23
+ ## 0.4.12 — 2026-10-29
24
+
25
+ ### Changed
26
+ - transformers.js pin 4.3.1 → 4.3.2 (in step with LocalMind), which adds `model_type` `lfm3` and `lfm3_vl`.
27
+ - transformers back end: a graph whose last-position input is named `logits_to_keep` (LiquidAI's LFM3 ONNX export)
28
+ gets it fed at the session's run; transformers.js 4.3.2 feeds only `num_logits_to_keep` and stopped with "Missing
29
+ the following inputs". `LiquidAI/LFM3-1.7B-ONNX` now loads and generates through `chat.html` (64 tokens at
30
+ 67 tok/s, Chrome, this Mac, 2026-10-28).
31
+ - transformers back end, text pipeline path: `thinking: false` by default, as on every other back end. The template
32
+ renders with `enable_thinking` off (through `tokenizer_encode_kwargs`), so LFM3 answers directly; `thinking: true`
33
+ per request turns it on. Templates without the switch ignore it.
34
+ - `test/browser/chat.html` takes `?dtype=` and fails a reply that contains `<think>` text.
35
+
36
+ ### Added
37
+ - `MODELS.chat.LFM3_1_7B`: `LiquidAI/LFM3-1.7B-ONNX` on `transformers` (asked for by LocalMind for Chirag). Browser
38
+ chat check on 2026-10-29, Chrome, this Mac: on topic, no `<think>` text, on q4 (anyformer's pick, 1.63 GB; 82.4
39
+ tok/s) and on q4f16 (`dtype: 'q4f16'`, LocalMind's precision, 1.38 GB; 76.6 tok/s), the same 61-token reply.
40
+
3
41
  ## 0.4.11 — 2026-10-24
4
42
 
5
43
  ### Added
package/README.md CHANGED
@@ -54,7 +54,8 @@ back end's answer: yes with an estimate (GPU bytes, disk bytes, tok/s when measu
54
54
 
55
55
  | Back end | Runs | Models |
56
56
  |---|---|---|
57
- | `transformers` | transformers.js 4.3.1: text generation (Nemotron 3.5 Bolt E4B among them), image and audio chat (Gemma 4, Qwen3.5), embeddings, speech to text, detection, classification, depth, segmentation | ONNX repos in the onnx-community / Xenova layout |
57
+ | `transformers` | transformers.js 4.3.2: text generation (Nemotron 3.5 Bolt E4B and LFM3 1.7B among them), image and audio chat (Gemma 4, Qwen3.5), embeddings, speech to text, detection, classification, depth, segmentation | ONNX repos in the onnx-community / Xenova layout |
58
+ | `diskformer-onnx` | diskformer's hand engines on onnxruntime-web WebGPU, for ONNX models transformers.js has no class for | LFM3-Omni-2B: text, image and speech in, text out |
58
59
  | `kohra` | kohra, text-diffusion over onnxruntime-web | MDLM and BD3LM repos tagged `text-diffusion`; Nemotron-Labs-Diffusion-3B |
59
60
  | `localmind` | LocalMind's hand-written WebGPU kernels (shipped in diskformer), the whole model on the GPU | any LFM2 / LFM2.5 GGUF; Gemma 4 E2B / E4B QAT-mobile; Ternary Bonsai 2 |
60
61
  | `wllama` | llama.cpp in WebAssembly: WebGPU when the budget holds the model, the CPU otherwise (no WebGPU needed) | any GGUF up to about 3.6 GB that no engine above covers; files over 2 GB are split into OPFS shards once |
package/index.js CHANGED
@@ -14,4 +14,5 @@ export { stored, remove } from './src/store.js';
14
14
  export { pipeline } from './src/pipeline.js';
15
15
  export { localmindBackend } from './src/backends/localmind.js';
16
16
  export { wllamaBackend } from './src/backends/wllama.js';
17
+ export { diskformerOnnxBackend, ONNX_ENGINES } from './src/backends/diskformer-onnx.js';
17
18
  export { PINS, SOURCES } from './src/sources.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anyformer",
3
- "version": "0.4.11",
3
+ "version": "0.4.13",
4
4
  "description": "One call to run a model in a browser tab: anyformer reads the Hub repo, probes the browser, and picks the back end (transformers.js, LocalMind's WebGPU kernels, diskformer, kohra) that fits the model's format, architecture and your GPU budget.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -0,0 +1,96 @@
1
+ // diskformer-onnx: ONNX exports that transformers.js has no model class for, run by a diskformer hand engine on
2
+ // onnxruntime-web's WebGPU EP. The engine loads from the CDN at load() time, at the commit src/sources.js pins (it
3
+ // pins its own onnxruntime-web and transformers.js tokenizer versions); set diskformerOnnxBackend.source to load
4
+ // another copy. Model files go to Cache Storage 'diskformer-models' on the first load and come from there later.
5
+ //
6
+ // When transformers.js adds a model class for one of these architectures, its entry moves to the transformers back
7
+ // end (which ranks first in BACKENDS) and leaves this table.
8
+
9
+ import { graphBytes } from './transformers.js';
10
+ import { SOURCES } from '../sources.js';
11
+
12
+ // Keyed by config.model_type. graphs: the ONNX files the engine reads (each with its _data file); json: the small files.
13
+ export const ONNX_ENGINES = {
14
+ // LiquidAI LFM3-Omni-2B: text, image and speech in (16 kHz Float32Array, at most 30 s a clip), text out. transformers.js
15
+ // 4.3.2 has no lfm3_omni class; swap to transformers.js when it adds the model. diskformer's gate
16
+ // (test/browser/lfm3-omni.html, onnxruntime-web 1.30.0): prompt ids and 32 greedy tokens equal Liquid's runner on
17
+ // onnxruntime's CPU EP for text, image and speech, 3/3.
18
+ lfm3_omni: {
19
+ engine: 'lfm3_omni.js', Engine: 'Lfm3Omni',
20
+ graphs: ['onnx/decoder_q4f16.onnx', 'onnx/vision_encoder_q8.onnx', 'onnx/audio_encoder.onnx'],
21
+ json: ['config.json', 'generation_config.json', 'processor_config.json', 'tokenizer.json', 'tokenizer_config.json', 'chat_template.jinja'],
22
+ },
23
+ };
24
+
25
+ const missing = (model, e) => {
26
+ const have = new Set(model.files.all.map((f) => f.name));
27
+ return [...e.graphs.flatMap((g) => [g, `${g}_data`]), ...e.json].filter((f) => !have.has(f));
28
+ };
29
+ const bytesOf = (model, e) => e.graphs.reduce((a, g) => a + graphBytes(model, g), 0) +
30
+ model.files.all.filter((f) => e.json.includes(f.name)).reduce((a, f) => a + (f.size || 0), 0);
31
+
32
+ // An image URL becomes an ImageBitmap (the engine takes ImageData, { data, width, height } or anything drawImage takes).
33
+ const toImage = async (x) => (typeof x === 'string' ? createImageBitmap(await (await fetch(x)).blob()) : x);
34
+ const media = (messages) => Promise.all(messages.map(async (m) => (typeof m.content === 'string' ? m : {
35
+ ...m, content: await Promise.all(m.content.map(async (b) => (b.type === 'image' ? { ...b, image: await toImage(b.image) } : b))),
36
+ })));
37
+
38
+ export const diskformerOnnxBackend = {
39
+ id: 'diskformer-onnx',
40
+ // LFM3-Omni's repo is tagged any-to-any; the engine writes text only.
41
+ tasks: ['any-to-any', 'text-generation', 'image-text-to-text'],
42
+ source: SOURCES.diskformer,
43
+ // The folder the engine reads the repo layout from; null is Hugging Face at the model's revision. A test points it at
44
+ // a local server.
45
+ url: null,
46
+
47
+ canRun(model, caps, { task } = {}) {
48
+ const e = ONNX_ENGINES[model.architecture];
49
+ if (!e) return { ok: false, code: 'model', reason: `no diskformer ONNX engine for ${model.architecture || 'this architecture'}; engines cover ${Object.keys(ONNX_ENGINES).join(', ')}` };
50
+ if (!model.formats.includes('onnx')) return { ok: false, code: 'format', reason: 'no ONNX graphs in the repo' };
51
+ if (task && !diskformerOnnxBackend.tasks.includes(task)) return { ok: false, code: 'task', reason: `task ${task}; this engine writes text (${diskformerOnnxBackend.tasks.join(', ')})` };
52
+ const gone = missing(model, e);
53
+ if (gone.length) return { ok: false, code: 'format', reason: `the engine reads ${gone.join(', ')}, which the repo lacks` };
54
+ if (!caps.webgpu) return { ok: false, code: 'webgpu', reason: 'no WebGPU adapter' };
55
+ if (!caps.f16) return { ok: false, code: 'f16', reason: 'the GPU lacks shader-f16 (the q4f16 decoder runs fp16 activations)' };
56
+ const bytes = bytesOf(model, e);
57
+ return { ok: true, estimate: { gpuBytes: bytes, diskBytes: bytes, tokPerSec: null } };
58
+ },
59
+
60
+ async load(model, { onProgress, signal }) {
61
+ const e = ONNX_ENGINES[model.architecture];
62
+ const mod = await import(`${diskformerOnnxBackend.source}/${e.engine}`);
63
+ const baseUrl = diskformerOnnxBackend.url ? diskformerOnnxBackend.url(model) : `https://huggingface.co/${model.repo}/resolve/${model.revision || 'main'}/`;
64
+ const engine = await mod[e.Engine].load({ baseUrl, onProgress: onProgress && ((p) => onProgress({ backend: 'diskformer-onnx', ...p })) });
65
+ if (signal?.aborted) { await engine.dispose(); signal.throwIfAborted(); }
66
+ return {
67
+ task: 'text-generation',
68
+ engine,
69
+ // Where the files came from: the engine's Cache Storage entries are keyed by these URLs
70
+ // (s.engine.constructor.cached({ baseUrl }) / deleteCache({ baseUrl })).
71
+ baseUrl,
72
+ // Content is a string or blocks: { type: 'text', text }, { type: 'image', image } (ImageData, ImageBitmap, a URL,
73
+ // or { data, width, height }) and { type: 'audio', audio } (16 kHz mono Float32Array). thinking: false (the
74
+ // default, as on every back end here) answers directly. tools: the chat template's tool list; tool calls stay
75
+ // raw text in the reply (LFM3 writes Qwen3-Coder XML), for the caller to parse. An abort ends the reply quietly.
76
+ async *generate(messages, { maxNewTokens = 512, signal, thinking = false, tools, repetitionPenalty } = {}) {
77
+ const queue = [];
78
+ let wake = () => {}, done = false, failed = null, text = '', tokens = 0;
79
+ const msgs = await media(messages);
80
+ engine.generate(msgs, {
81
+ max_new_tokens: maxNewTokens, do_sample: false, enable_thinking: thinking === true, signal,
82
+ ...(tools && { tools }), ...(repetitionPenalty && { repetition_penalty: repetitionPenalty }),
83
+ onToken: () => { tokens++; },
84
+ onText: (delta) => { text += delta; queue.push({ text, tokens }); wake(); },
85
+ }).catch((err) => { failed = err; }).finally(() => { done = true; wake(); });
86
+ while (true) {
87
+ if (queue.length) { yield queue.shift(); continue; }
88
+ if (done) break;
89
+ await new Promise((r) => { wake = r; });
90
+ }
91
+ if (failed) throw failed;
92
+ },
93
+ dispose: () => engine.dispose(),
94
+ };
95
+ },
96
+ };
@@ -120,16 +120,33 @@ export const transformersBackend = {
120
120
  const gen = await lib.pipeline('text-generation', model.repo, {
121
121
  device: 'webgpu', dtype, revision: model.revision || 'main', progress_callback,
122
122
  });
123
+ feedLogitsToKeep(lib, gen.model);
123
124
  signal?.throwIfAborted();
124
125
  return {
125
126
  task: 'text-generation',
126
- generate: (messages, { maxNewTokens = 256, repetitionPenalty, signal } = {}) => stream(lib, gen.tokenizer, signal,
127
- (streamer, stopping_criteria) => gen(messages, { max_new_tokens: maxNewTokens, do_sample: false, ...(repetitionPenalty && { repetition_penalty: repetitionPenalty }), streamer, stopping_criteria })),
127
+ // thinking: false (the default, as on every back end here) renders the template with enable_thinking off, so a
128
+ // model that reasons by default (LFM3) answers directly; templates without the switch ignore it.
129
+ generate: (messages, { maxNewTokens = 256, repetitionPenalty, signal, thinking = false } = {}) => stream(lib, gen.tokenizer, signal,
130
+ (streamer, stopping_criteria) => gen(messages, { max_new_tokens: maxNewTokens, do_sample: false, ...(repetitionPenalty && { repetition_penalty: repetitionPenalty }), tokenizer_encode_kwargs: { enable_thinking: thinking === true }, streamer, stopping_criteria })),
128
131
  dispose: () => gen.dispose?.(),
129
132
  };
130
133
  },
131
134
  };
132
135
 
136
+ // LiquidAI's LFM3 ONNX export names its last-position input logits_to_keep (shape [1]); transformers.js 4.3.2 feeds
137
+ // only num_logits_to_keep and stops with "Missing the following inputs: logits_to_keep". Hide the input from
138
+ // transformers.js's check and add it at the session's run, set to 1: generate() reads only the last position's logits.
139
+ // LocalMind carries the same shim (index.html feedLogitsToKeep).
140
+ function feedLogitsToKeep(lib, net) {
141
+ const sess = net?.sessions?.model;
142
+ if (!sess?.inputNames.includes('logits_to_keep')) return;
143
+ const names = sess.inputNames.filter((n) => n !== 'logits_to_keep');
144
+ const one = new lib.Tensor('int64', [1n], [1]).ort_tensor;
145
+ const run = sess.run.bind(sess);
146
+ Object.defineProperty(sess, 'inputNames', { value: names });
147
+ sess.run = (feeds, opts) => run({ ...feeds, logits_to_keep: one }, opts);
148
+ }
149
+
133
150
  // A non-generation task: one pipeline, run(input, opts) calls it. ImageData (from a canvas) becomes a RawImage; URLs,
134
151
  // RawImage and, for speech, 16 kHz Float32Array samples pass through as transformers.js takes them.
135
152
  async function loadPipeline(lib, model, task, { progress_callback, signal }) {
package/src/load.js CHANGED
@@ -7,12 +7,14 @@ import { diskformerBackend } from './backends/diskformer.js';
7
7
  import { diskformerLlamaBackend } from './backends/diskformer-llama.js';
8
8
  import { localmindBackend } from './backends/localmind.js';
9
9
  import { wllamaBackend } from './backends/wllama.js';
10
+ import { diskformerOnnxBackend } from './backends/diskformer-onnx.js';
10
11
  import { AnyformerError, wrap } from './errors.js';
11
12
 
12
13
  // Back ends land here as each one passes its browser test (test/browser/). The list order breaks ties: a
13
14
  // diskformer hand engine carries a measured speed and outranks diskformer-llama for the same model; diskformer-llama
14
- // comes before wllama because it pages experts instead of holding the whole model on the GPU.
15
- export const BACKENDS = [transformersBackend, kohraBackend, diskformerBackend, localmindBackend, diskformerLlamaBackend, wllamaBackend];
15
+ // comes before wllama because it pages experts instead of holding the whole model on the GPU. diskformer-onnx runs only
16
+ // the ONNX architectures transformers.js has no class for, so transformers.js wins once it adds one.
17
+ export const BACKENDS = [transformersBackend, diskformerOnnxBackend, kohraBackend, diskformerBackend, localmindBackend, diskformerLlamaBackend, wllamaBackend];
16
18
 
17
19
  // 2.5 GB: the smallest budget diskformer's chat gate passes at. Browsers do not report GPU memory, so a caller that
18
20
  // knows more passes { budget }.
@@ -128,7 +130,8 @@ function serialize(session) {
128
130
  };
129
131
  // Read live from the back end's session, never copied: the engine instance it built (diskformer and localmind back
130
132
  // ends; read-only, for token-level work a caller already does with the engine; calls on it bypass the queue),
131
- // Gemma 4's PLE mode, and Bonsai's speculative decoding state, which changes after load.
132
- for (const k of ['engine', 'pleMode', 'speculative']) if (k in session) Object.defineProperty(out, k, { get: () => session[k], enumerable: true });
133
+ // Gemma 4's PLE mode, Bonsai's speculative decoding state, which changes after load, and diskformer-onnx's baseUrl
134
+ // (its Cache Storage keys).
135
+ for (const k of ['engine', 'pleMode', 'speculative', 'baseUrl']) if (k in session) Object.defineProperty(out, k, { get: () => session[k], enumerable: true });
133
136
  return out;
134
137
  }
package/src/models.js CHANGED
@@ -20,6 +20,10 @@ const MINICPM5_2B = M('openbmb/MiniCPM5-2B-GGUF', 'wllama', 'text-generation', '
20
20
  const QWEN3_0_6B_MDLM = M('naklitechie/Qwen3-0.6B-diffusion-mdlm-ONNX', 'kohra', 'text-generation', 'kohra', { downloadBytes: 1.54e9, diffusion: true });
21
21
  const NEMOTRON_DIFFUSION_3B = M('naklitechie/Nemotron-Labs-Diffusion-3B-ONNX', 'kohra', 'text-generation', 'kohra', { downloadBytes: 2.77e9, diffusion: true, experimental: true });
22
22
  const GEMMA4_26B_A4B = M('google/gemma-4-26B-A4B-it-qat-q4_0-gguf', 'diskformer', 'text-generation', 'diskformer-chat', { budget: 2.5e9, downloadBytes: 14.44e9 });
23
+ const LFM3_1_7B = M('LiquidAI/LFM3-1.7B-ONNX', 'transformers', 'text-generation', 'chat', { downloadBytes: 1.63e9 });
24
+ // transformers.js 4.3.2 has no lfm3_omni class, so diskformer's hand engine runs it; swap the entry to transformers.js
25
+ // when it adds the model.
26
+ const LFM3_OMNI_2B = M('LiquidAI/LFM3-Omni-2B-ONNX', 'diskformer-onnx', 'any-to-any', 'omni', { downloadBytes: 2.14e9 });
23
27
  const NEMOTRON_3_5_BOLT_E4B = M('danyalmalik/NVIDIA-Nemotron-3.5-Bolt-E4B-ONNX-WebGPU', 'transformers', 'text-generation', 'transformers', { budget: 5e9, downloadBytes: 4.94e9 });
24
28
  // A tech demo, not a gated model: 231 GB on disk (4 IQ1_M parts plus the 110.6 GB Engram side file), about 0.4 tok/s.
25
29
  // diskformer checked one hand-written prompt through complete(); generate() renders the GGUF's chat template, which no
@@ -54,6 +58,10 @@ export const MODELS = {
54
58
  GEMMA4_26B_A4B,
55
59
  /** NVIDIA's Nemotron 3.5 Bolt E4B (dense, text only), a community q4f16 ONNX export in Gemma 4 E4B's graph. */
56
60
  NEMOTRON_3_5_BOLT_E4B,
61
+ /** LiquidAI's LFM3 1.7B (dense), LiquidAI's own ONNX export; thinking off unless a request asks for it. */
62
+ LFM3_1_7B,
63
+ /** LiquidAI's LFM3-Omni-2B: text, image and speech in (16 kHz, at most 30 s a clip), text out (diskformer's engine). */
64
+ LFM3_OMNI_2B,
57
65
  /** DeepSeek V4.1 Flash (763B MoE), a tech demo on diskformer's llama.cpp path: 231 GB on disk, very slow. */
58
66
  DEEPSEEK_V4_1_FLASH,
59
67
  },
package/src/sources.js CHANGED
@@ -4,17 +4,19 @@
4
4
  // transformers.js and wllama move only by hand, in step with LocalMind's pins (plan/history.md, 2026-10-08).
5
5
 
6
6
  export const PINS = {
7
- transformers: '4.3.1',
7
+ transformers: '4.3.2', // 4.3.2 adds model_type lfm3 (LFM3 1.7B)
8
8
  wllama: '3.4.1', // LocalMind's pin; its stream-drain workaround (src/backends/wllama.js) is for this version
9
9
  // v0.3.1: seeded block-KV graphs (Nemotron-Labs-Diffusion-3B), chatFormat, split external data; download progress
10
10
  // (progress_callback) and the graph files cached in Cache Storage 'kohra-models'.
11
11
  kohra: 'bf95d44c3e3cfc001201baa03fa121c2bf9c7d73',
12
+ // 48f2c2a (main after v0.6.9): engines/lfm3_omni.js, LFM3-Omni-2B on onnxruntime-web 1.30.0 (text, image and speech
13
+ // in; text out; files in Cache Storage 'diskformer-models'). Adds files only: no other engine or ggml/ changed.
12
14
  // v0.6.9: llama.load({ extra }) mounts side files beside the GGUF (DeepSeek V4.1 Flash's Engram tables).
13
15
  // v0.6.8: every engine (the disk-tier MoE engines, Ling 3.0 Flash, and LocalMind's whole-model engines LFM2.5,
14
16
  // Gemma 4 E2B/E4B and Ternary Bonsai 2) under engines/, and the llama.cpp path under ggml/, which loads split GGUFs
15
17
  // and drafters, and reads past 2 GiB (Nemotron 3.5 Bolt E4B needs v0.6.8 or later). The hand engines' ingest
16
18
  // streams the GGUF header with progress and resumes a dropped range.
17
- diskformer: '53c976e43d34ac2d2601a731124de81cb07fdab4',
19
+ diskformer: '48f2c2a2f5bf59b16db8f3d57653e7c44705ce6c',
18
20
  };
19
21
 
20
22
  const gh = (repo, sha, path) => `https://cdn.jsdelivr.net/gh/NakliTechie/${repo}@${sha}/${path}`;
package/src/store.js CHANGED
@@ -3,7 +3,8 @@
3
3
  // inventoryModelCaches() (index.html), which covers the same engines' stores.
4
4
  // Cache Storage 'transformers-cache' (transformers.js; kohra's tokenizer too); 'gguf-v1', 'gguf-v1-headers' and
5
5
  // 'bonsai-pipeline-v1' (LocalMind's engines; 'gguf-v1' holds the LFM2
6
- // engine), 'kohra-models' (kohra v0.3.1+: its ONNX graph and data files): one entry per file URL
6
+ // engine), 'kohra-models' (kohra v0.3.1+: its ONNX graph and data files), 'diskformer-models' (diskformer's
7
+ // ONNX engines, LFM3-Omni): one entry per file URL
7
8
  // (…/<repo>/resolve/<rev>/<file>). Older kohra left its graph in the HTTP cache, which a page cannot list.
8
9
  // IndexedDB 'gguf-cache-v1' (Bonsai 2) and 'safetensors-cache-v1' (Gemma 4 QAT-mobile): a store keyed by URL
9
10
  // with { size } and a chunk store keyed [url, start, end]; 'webgpu-prefix-snapshots' (Bonsai 2's
@@ -18,7 +19,7 @@ import { DISKFORMER_MODELS } from './backends/diskformer.js';
18
19
  import { SPLIT_ROOT } from './gguf-split.js';
19
20
  import { readLlamaIndex, LLAMA_INDEX } from './backends/diskformer-llama.js';
20
21
 
21
- const CACHE_BACKEND = { 'transformers-cache': 'transformers', 'gguf-v1': 'localmind', 'gguf-v1-headers': 'localmind', 'bonsai-pipeline-v1': 'localmind', 'kohra-models': 'kohra' };
22
+ const CACHE_BACKEND = { 'transformers-cache': 'transformers', 'gguf-v1': 'localmind', 'gguf-v1-headers': 'localmind', 'bonsai-pipeline-v1': 'localmind', 'kohra-models': 'kohra', 'diskformer-models': 'diskformer-onnx' };
22
23
  const IDB_STORES = ['gguf-cache-v1', 'safetensors-cache-v1'];
23
24
  const BONSAI2 = 'prism-ml/Ternary-Bonsai-2-27B-gguf';
24
25