@missionsquad/browserai 0.1.0 → 0.2.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 +5 -0
- package/dist/client.d.ts +11 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +33 -12
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/hardware.d.ts +10 -0
- package/dist/hardware.d.ts.map +1 -1
- package/dist/hardware.js +15 -0
- package/dist/hardware.js.map +1 -1
- package/dist/history.d.ts +1 -1
- package/dist/history.d.ts.map +1 -1
- package/dist/history.js +1 -1
- package/dist/history.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/inference.d.ts.map +1 -1
- package/dist/inference.js +8 -6
- package/dist/inference.js.map +1 -1
- package/dist/metrics.js +2 -2
- package/dist/metrics.js.map +1 -1
- package/dist/models.d.ts +12 -2
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +142 -0
- package/dist/models.js.map +1 -1
- package/dist/multimodal.d.ts +21 -1
- package/dist/multimodal.d.ts.map +1 -1
- package/dist/multimodal.js +69 -11
- package/dist/multimodal.js.map +1 -1
- package/dist/tdt/frontend.d.ts +7 -0
- package/dist/tdt/frontend.d.ts.map +1 -0
- package/dist/tdt/frontend.js +73 -0
- package/dist/tdt/frontend.js.map +1 -0
- package/dist/tdt/index.d.ts +37 -0
- package/dist/tdt/index.d.ts.map +1 -0
- package/dist/tdt/index.js +318 -0
- package/dist/tdt/index.js.map +1 -0
- package/dist/tdt/nemo-frontend.d.ts +6 -0
- package/dist/tdt/nemo-frontend.d.ts.map +1 -0
- package/dist/tdt/nemo-frontend.js +53 -0
- package/dist/tdt/nemo-frontend.js.map +1 -0
- package/package.json +6 -3
- package/src/client.ts +35 -10
- package/src/config.ts +7 -0
- package/src/hardware.ts +15 -0
- package/src/history.ts +2 -2
- package/src/index.ts +3 -1
- package/src/inference.ts +12 -6
- package/src/metrics.ts +2 -2
- package/src/models.ts +152 -1
- package/src/multimodal.ts +64 -10
- package/src/tdt/frontend.ts +61 -0
- package/src/tdt/index.ts +230 -0
- package/src/tdt/nemo-frontend.ts +43 -0
- package/src/tdt/runtime-types.d.ts +5 -0
- package/worker-template/cloudflare-worker.ts +1 -1
package/src/client.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MLCEngineInterface } from "@mlc-ai/web-llm";
|
|
2
|
-
import type { ModelPreset, ModelTask } from "./models.js";
|
|
3
|
-
import { getModelPreset, MODEL_PRESETS, modelRepoLabel, modelSlots } from "./models.js";
|
|
2
|
+
import type { ModelPreset, ModelTask, ModelBackend } from "./models.js";
|
|
3
|
+
import { getModelPreset, MODEL_PRESETS, modelRepoLabel, modelSlots, supportsWasmFallback } from "./models.js";
|
|
4
4
|
import type {
|
|
5
5
|
ChatMessage,
|
|
6
6
|
EngineHandles,
|
|
@@ -13,7 +13,6 @@ import type {
|
|
|
13
13
|
import { EMPTY_RESPONSE, generate } from "./inference.js";
|
|
14
14
|
import type { RuntimeParameters } from "./generation.js";
|
|
15
15
|
import { clampNumber, runtimeParamsFromPreset } from "./generation.js";
|
|
16
|
-
import type { MultimodalRuntime } from "./models.js";
|
|
17
16
|
import type {
|
|
18
17
|
MultimodalDevice,
|
|
19
18
|
MultimodalHandle,
|
|
@@ -22,9 +21,11 @@ import type {
|
|
|
22
21
|
SttResult,
|
|
23
22
|
TtsTextSplitter,
|
|
24
23
|
VlmResult,
|
|
24
|
+
ImageClassificationResult,
|
|
25
25
|
} from "./multimodal.js";
|
|
26
26
|
import {
|
|
27
27
|
describeImage as describeImageWithHandle,
|
|
28
|
+
classifyImage as classifyImageWithHandle,
|
|
28
29
|
disposeMultimodal,
|
|
29
30
|
generateGemmaText,
|
|
30
31
|
loadMultimodal,
|
|
@@ -39,7 +40,7 @@ import type { ModelSourceMode } from "./model-source.js";
|
|
|
39
40
|
import { applyContextOverride, buildWebLLMAppConfig, TRANSFORMERS_CACHE_KEY } from "./model-source.js";
|
|
40
41
|
import { verifyTransformersProxy, verifyWebLLMProxy } from "./proxy-verify.js";
|
|
41
42
|
import type { HardwareSnapshot } from "./hardware.js";
|
|
42
|
-
import { collectHardwareSnapshot } from "./hardware.js";
|
|
43
|
+
import { collectHardwareSnapshot, isChromiumBased } from "./hardware.js";
|
|
43
44
|
import type { CacheCleanupResult, CachedModelStatus, IndividualModelCleanupResult, ModelCacheTarget, StorageEstimateSnapshot } from "./model-cache.js";
|
|
44
45
|
import {
|
|
45
46
|
deleteOneModelArtifactsFromBrowserStorage,
|
|
@@ -65,7 +66,7 @@ import { synthesizeClip } from "./audio/clip.js";
|
|
|
65
66
|
export type LoadedModel = {
|
|
66
67
|
modelId: string;
|
|
67
68
|
slots: ModelTask[];
|
|
68
|
-
backend:
|
|
69
|
+
backend: ModelBackend;
|
|
69
70
|
engine: MLCEngineInterface | null;
|
|
70
71
|
worker: Worker | null;
|
|
71
72
|
transformersGenerator: TransformersTextGenerationPipeline | null;
|
|
@@ -81,7 +82,6 @@ export type LoadProgress = { modelId: string; progress: number; status?: string;
|
|
|
81
82
|
* Multimodal runtimes whose loaders honor a wasm device. The VLM/Gemma/audio-LLM recipes hardcode
|
|
82
83
|
* WebGPU (their verified recipes only run there), so they cannot fall back.
|
|
83
84
|
*/
|
|
84
|
-
const WASM_CAPABLE_RUNTIMES: ReadonlySet<MultimodalRuntime> = new Set(["stt", "tts-pipeline", "tts-kokoro"]);
|
|
85
85
|
|
|
86
86
|
export type BrowserAIEvents = {
|
|
87
87
|
/** Download/initialization progress for the model currently loading (progress 0–1). */
|
|
@@ -261,11 +261,16 @@ export class BrowserAI extends TypedEmitter<BrowserAIEvents> {
|
|
|
261
261
|
try {
|
|
262
262
|
this.#hardware = await collectHardwareSnapshot();
|
|
263
263
|
let device: MultimodalDevice = "webgpu";
|
|
264
|
+
const wasmCapable = supportsWasmFallback(preset);
|
|
264
265
|
if (!this.#hardware.webgpuSupported) {
|
|
265
266
|
// Only the runtimes whose loaders honor a wasm device (ASR pipelines, TTS) can fall back;
|
|
266
267
|
// VLM/Gemma/audio-LLM recipes hardcode WebGPU, and text engines require it outright.
|
|
267
|
-
if (
|
|
268
|
+
if (wasmCapable) device = "wasm";
|
|
268
269
|
else throw new WebGPUUnavailableError(this.#hardware.webgpuReason);
|
|
270
|
+
} else if (wasmCapable && !isChromiumBased()) {
|
|
271
|
+
// Firefox and Safari now pass the WebGPU probe, but onnxruntime-web's WebGPU backend only
|
|
272
|
+
// reliably runs on Chromium (shader miscompiles / quantized-session failures elsewhere).
|
|
273
|
+
device = "wasm";
|
|
269
274
|
}
|
|
270
275
|
|
|
271
276
|
// Free any loaded models occupying the slots this one needs, waiting out their in-flight runs.
|
|
@@ -437,6 +442,18 @@ export class BrowserAI extends TypedEmitter<BrowserAIEvents> {
|
|
|
437
442
|
}
|
|
438
443
|
|
|
439
444
|
async #loadMultimodalModel(preset: ModelPreset, device: MultimodalDevice, progress: (info: Omit<LoadProgress, "modelId">) => void): Promise<LoadedModel> {
|
|
445
|
+
if (preset.mmRuntime === "stt-tdt") {
|
|
446
|
+
this.#status(`Loading ${preset.shortName} with ONNX Runtime…`);
|
|
447
|
+
const root = this.#modelSourceMode() === "same-origin-proxy"
|
|
448
|
+
? `${this.#requireProxyOrigin()}/hf-transformers/` : "https://huggingface.co/";
|
|
449
|
+
if (this.#modelSourceMode() === "same-origin-proxy" && this.config.verifyProxy) {
|
|
450
|
+
await verifyTransformersProxy(this.#requireProxyOrigin(), preset.id);
|
|
451
|
+
}
|
|
452
|
+
const { loadTdtModel } = await import("./tdt/index.js");
|
|
453
|
+
const model = await loadTdtModel(preset.id, { ...this.config.tdt, device, baseUrl: `${root}${preset.id}/resolve/main/`, onProgress: this.#makeTransformersProgressForwarder(progress) });
|
|
454
|
+
progress({ progress: 1 });
|
|
455
|
+
return { ...newLoadedModel(preset), multimodal: { kind: "tdt", model } };
|
|
456
|
+
}
|
|
440
457
|
this.#status(`Loading ${preset.shortName} with Transformers.js/WebGPU…`);
|
|
441
458
|
progress({ progress: 0.05 });
|
|
442
459
|
|
|
@@ -542,12 +559,12 @@ export class BrowserAI extends TypedEmitter<BrowserAIEvents> {
|
|
|
542
559
|
});
|
|
543
560
|
}
|
|
544
561
|
|
|
545
|
-
/** Transcribe 16 kHz mono PCM
|
|
546
|
-
async transcribe(audio: Float32Array | string, onDelta?: (text: string) => void): Promise<SttResult> {
|
|
562
|
+
/** Transcribe 16 kHz mono PCM. ASR pipelines also accept URLs; TDT and generative models require decoded PCM. The optional signal cooperatively cancels TDT decoding between inference calls. */
|
|
563
|
+
async transcribe(audio: Float32Array | string, onDelta?: (text: string) => void, signal?: AbortSignal): Promise<SttResult> {
|
|
547
564
|
const model = this.sttModel();
|
|
548
565
|
if (!model?.multimodal) throw new MissingModelError("stt");
|
|
549
566
|
const handle = model.multimodal;
|
|
550
|
-
return this.#runExclusive(model, () => transcribeWithHandle(handle, audio, onDelta));
|
|
567
|
+
return this.#runExclusive(model, () => transcribeWithHandle(handle, audio, onDelta, signal));
|
|
551
568
|
}
|
|
552
569
|
|
|
553
570
|
/** Synthesize speech with the loaded Speech-slot model. Returns the raw model audio. */
|
|
@@ -620,6 +637,14 @@ export class BrowserAI extends TypedEmitter<BrowserAIEvents> {
|
|
|
620
637
|
};
|
|
621
638
|
}
|
|
622
639
|
|
|
640
|
+
/** Return scored labels with the loaded Image-slot classifier. */
|
|
641
|
+
async classifyImage(imageSrc: string): Promise<ImageClassificationResult> {
|
|
642
|
+
const model = this.visionModel();
|
|
643
|
+
if (!model?.multimodal) throw new MissingModelError("vision");
|
|
644
|
+
const handle = model.multimodal;
|
|
645
|
+
return this.#runExclusive(model, () => classifyImageWithHandle(handle, imageSrc));
|
|
646
|
+
}
|
|
647
|
+
|
|
623
648
|
/** Ask a question about an image with the loaded Image-slot model (VLM or Gemma). */
|
|
624
649
|
async describeImage(imageSrc: string, prompt: string, onDelta?: (text: string) => void): Promise<VlmResult> {
|
|
625
650
|
const model = this.visionModel();
|
package/src/config.ts
CHANGED
|
@@ -26,6 +26,9 @@ export type KokoroOptions = {
|
|
|
26
26
|
load?: () => Promise<KokoroModule>;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
/** Optional location of the matching ORT 1.31.1 asyncify MJS/WASM files. */
|
|
30
|
+
export type TdtOptions = { wasmPaths?: string | { mjs: string; wasm: string } };
|
|
31
|
+
|
|
29
32
|
export type TtsDefaults = {
|
|
30
33
|
/** Voice id for the loaded TTS model's roster (Kokoro: "af_heart" etc.; Supertonic: "F1"–"M5"). */
|
|
31
34
|
voice: string;
|
|
@@ -69,6 +72,8 @@ export type BrowserAIConfig = {
|
|
|
69
72
|
cacheBackend?: BrowserCacheBackend;
|
|
70
73
|
webllm?: WebLLMOptions;
|
|
71
74
|
kokoro?: KokoroOptions;
|
|
75
|
+
/** Override speech-runtime assets for self-hosted/offline applications. */
|
|
76
|
+
tdt?: TdtOptions;
|
|
72
77
|
/** Default TTS voice/speed/pitch used by synthesize helpers and the voice pipeline. */
|
|
73
78
|
tts?: Partial<TtsDefaults>;
|
|
74
79
|
history?: HistoryOptions;
|
|
@@ -81,6 +86,7 @@ export type ResolvedConfig = {
|
|
|
81
86
|
cacheBackend: BrowserCacheBackend;
|
|
82
87
|
webllm: WebLLMOptions;
|
|
83
88
|
kokoro: KokoroOptions;
|
|
89
|
+
tdt: TdtOptions;
|
|
84
90
|
tts: TtsDefaults;
|
|
85
91
|
history: HistoryOptions;
|
|
86
92
|
};
|
|
@@ -98,6 +104,7 @@ export function resolveConfig(config: BrowserAIConfig = {}): ResolvedConfig {
|
|
|
98
104
|
cacheBackend: config.cacheBackend ?? "indexeddb",
|
|
99
105
|
webllm: config.webllm ?? {},
|
|
100
106
|
kokoro: config.kokoro ?? {},
|
|
107
|
+
tdt: config.tdt ?? {},
|
|
101
108
|
tts: { voice: "af_heart", speed: 1, pitch: 1, ...config.tts },
|
|
102
109
|
history: config.history ?? {},
|
|
103
110
|
};
|
package/src/hardware.ts
CHANGED
|
@@ -19,6 +19,21 @@ export type HardwareSnapshot = {
|
|
|
19
19
|
platform?: string;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Whether this is a Chromium-based browser (Chrome, Edge, Opera, Brave, …), per the UA-Client-Hints
|
|
24
|
+
* brand list. onnxruntime-web's WebGPU backend is developed and tested against Chromium; Firefox's
|
|
25
|
+
* and Safari's newer WebGPU implementations still fail on some of its generated WGSL and its
|
|
26
|
+
* quantized-weight session transforms (errors that only surface at load/inference time), so
|
|
27
|
+
* wasm-capable runtimes prefer wasm off-Chromium even when the WebGPU probe passes.
|
|
28
|
+
* navigator.userAgentData is itself Chromium-only, and every Chromium new enough for WebGPU (113+)
|
|
29
|
+
* ships it, so the brand list is a reliable signal.
|
|
30
|
+
*/
|
|
31
|
+
export function isChromiumBased(): boolean {
|
|
32
|
+
if (typeof navigator === "undefined") return false;
|
|
33
|
+
const nav = navigator as Navigator & { userAgentData?: { brands?: Array<{ brand: string }> } };
|
|
34
|
+
return nav.userAgentData?.brands?.some((entry) => entry.brand === "Chromium") ?? false;
|
|
35
|
+
}
|
|
36
|
+
|
|
22
37
|
const LIMIT_NAMES = [
|
|
23
38
|
"maxBufferSize",
|
|
24
39
|
"maxStorageBufferBindingSize",
|
package/src/history.ts
CHANGED
|
@@ -36,7 +36,7 @@ export type RunRecord = {
|
|
|
36
36
|
/** Derived vendor bucket for filtering/labels. */
|
|
37
37
|
vendor: ModelVendor;
|
|
38
38
|
/** Backend used for the run. */
|
|
39
|
-
backend: "webllm" | "transformers-js";
|
|
39
|
+
backend: "webllm" | "transformers-js" | "onnx-runtime";
|
|
40
40
|
/** Task key ({@link RunCaseKey} suggested; any non-empty string accepted). */
|
|
41
41
|
case: string;
|
|
42
42
|
promptTokens: number;
|
|
@@ -111,7 +111,7 @@ function coerceRecord(value: unknown): RunRecord | null {
|
|
|
111
111
|
ts: record.ts as number,
|
|
112
112
|
model: record.model,
|
|
113
113
|
vendor: VALID_VENDORS.has(record.vendor as ModelVendor) ? (record.vendor as ModelVendor) : "other",
|
|
114
|
-
backend: record.backend === "transformers-js" ? "transformers-js" : "webllm",
|
|
114
|
+
backend: record.backend === "onnx-runtime" ? "onnx-runtime" : record.backend === "transformers-js" ? "transformers-js" : "webllm",
|
|
115
115
|
case: record.case,
|
|
116
116
|
promptTokens: record.promptTokens as number,
|
|
117
117
|
completionTokens: record.completionTokens as number,
|
package/src/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type { BrowserAIEvents, GenerateTextOptions, LoadOptions, LoadProgress, L
|
|
|
12
12
|
|
|
13
13
|
// Configuration
|
|
14
14
|
export { DEFAULT_CONTEXT_LENGTH, resolveConfig } from "./config.js";
|
|
15
|
-
export type { BrowserAIConfig, HistoryOptions, KokoroOptions, ResolvedConfig, StorageLike, TtsDefaults, WebLLMOptions } from "./config.js";
|
|
15
|
+
export type { BrowserAIConfig, HistoryOptions, KokoroOptions, ResolvedConfig, StorageLike, TdtOptions, TtsDefaults, WebLLMOptions } from "./config.js";
|
|
16
16
|
|
|
17
17
|
// Errors
|
|
18
18
|
export {
|
|
@@ -81,6 +81,7 @@ export type {
|
|
|
81
81
|
// Multimodal runtimes (STT / TTS / VLM / Gemma)
|
|
82
82
|
export {
|
|
83
83
|
capSrc,
|
|
84
|
+
classifyImage,
|
|
84
85
|
DEFAULT_KOKORO_CDN,
|
|
85
86
|
describeImage,
|
|
86
87
|
disposeMultimodal,
|
|
@@ -99,6 +100,7 @@ export {
|
|
|
99
100
|
export type {
|
|
100
101
|
AudioWindow,
|
|
101
102
|
GenStats,
|
|
103
|
+
ImageClassificationResult,
|
|
102
104
|
KokoroModule,
|
|
103
105
|
LoadMultimodalOptions,
|
|
104
106
|
MultimodalDevice,
|
package/src/inference.ts
CHANGED
|
@@ -305,9 +305,10 @@ async function runTransformers(
|
|
|
305
305
|
): Promise<TransformersRunResult> {
|
|
306
306
|
const generator = handles.transformersGenerator!;
|
|
307
307
|
const tokenizer = handles.transformersTokenizer ?? generator.tokenizer ?? null;
|
|
308
|
+
const chatTemplateOptions = preset.disableThinkingSupported ? { enable_thinking: !runtime.disableThinking } : undefined;
|
|
308
309
|
|
|
309
310
|
const telemetry: TransformersGenerationTelemetry = {
|
|
310
|
-
promptTokens: countPromptTokens(tokenizer, messages),
|
|
311
|
+
promptTokens: countPromptTokens(tokenizer, messages, chatTemplateOptions),
|
|
311
312
|
tokenCallbackIntervalSeconds: [],
|
|
312
313
|
tokenCallbackCount: 0,
|
|
313
314
|
tokenCountSource: "unavailable",
|
|
@@ -323,8 +324,8 @@ async function runTransformers(
|
|
|
323
324
|
};
|
|
324
325
|
if (runtime.temperature > 0) generationOptions.temperature = runtime.temperature;
|
|
325
326
|
if (runtime.seed !== null) generationOptions.seed = runtime.seed;
|
|
326
|
-
if (
|
|
327
|
-
generationOptions.tokenizer_encode_kwargs =
|
|
327
|
+
if (chatTemplateOptions) {
|
|
328
|
+
generationOptions.tokenizer_encode_kwargs = chatTemplateOptions;
|
|
328
329
|
}
|
|
329
330
|
|
|
330
331
|
const Streamer = handles.transformersModule?.TextStreamer;
|
|
@@ -438,7 +439,7 @@ export function collectGenericStats(model: string, backend: ModelBackend | "unkn
|
|
|
438
439
|
/* ------------------------------------------------------------------ */
|
|
439
440
|
|
|
440
441
|
export function buildRuntimeStatsText(stats: RuntimeStats): string {
|
|
441
|
-
const backendLabel = stats.backend === "transformers-js" ? "Transformers.js" : stats.backend === "webllm" ? "WebLLM" : "unknown";
|
|
442
|
+
const backendLabel = stats.backend === "onnx-runtime" ? "ONNX Runtime" : stats.backend === "transformers-js" ? "Transformers.js" : stats.backend === "webllm" ? "WebLLM" : "unknown";
|
|
442
443
|
const lines = [
|
|
443
444
|
`model: ${stats.model}`,
|
|
444
445
|
`backend: ${backendLabel}`,
|
|
@@ -514,11 +515,16 @@ function extractGeneratedText(output: unknown): string {
|
|
|
514
515
|
return JSON.stringify(output, null, 2);
|
|
515
516
|
}
|
|
516
517
|
|
|
517
|
-
function countPromptTokens(
|
|
518
|
+
function countPromptTokens(
|
|
519
|
+
tokenizer: TransformersTokenizer | null,
|
|
520
|
+
messages: ChatMessage[],
|
|
521
|
+
chatTemplateOptions?: Record<string, unknown>,
|
|
522
|
+
): number | undefined {
|
|
518
523
|
if (!tokenizer) return undefined;
|
|
519
524
|
try {
|
|
520
525
|
if (typeof tokenizer.apply_chat_template === "function") {
|
|
521
|
-
|
|
526
|
+
// Count the same prompt the pipeline generates, including MiniCPM5's optional empty think block.
|
|
527
|
+
const tokenized = tokenizer.apply_chat_template(messages, { tokenize: true, add_generation_prompt: true, ...chatTemplateOptions });
|
|
522
528
|
const count = extractTokenCount(tokenized);
|
|
523
529
|
if (typeof count === "number") return count;
|
|
524
530
|
}
|
package/src/metrics.ts
CHANGED
|
@@ -14,7 +14,7 @@ export function buildMultimodalStats(gen: GenStats, modelId: string): RuntimeSta
|
|
|
14
14
|
return {
|
|
15
15
|
rawText: "",
|
|
16
16
|
model: modelId,
|
|
17
|
-
backend:
|
|
17
|
+
backend: getModelPreset(modelId).backend,
|
|
18
18
|
finishReason: "stop",
|
|
19
19
|
measuredElapsedMs: elapsedMs,
|
|
20
20
|
completionTokens,
|
|
@@ -41,7 +41,7 @@ export function runRecordFromStats(caseKey: string, stats: RuntimeStats): RunRec
|
|
|
41
41
|
ts: Date.now(),
|
|
42
42
|
model: preset.shortName,
|
|
43
43
|
vendor: vendorForModel(preset),
|
|
44
|
-
backend: stats.backend === "transformers-js" ? "transformers-js" : "webllm",
|
|
44
|
+
backend: stats.backend === "onnx-runtime" ? "onnx-runtime" : stats.backend === "transformers-js" ? "transformers-js" : "webllm",
|
|
45
45
|
case: caseKey,
|
|
46
46
|
promptTokens: stats.promptTokens ?? 0,
|
|
47
47
|
completionTokens: stats.completionTokens ?? 0,
|
package/src/models.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type RuntimePreset = {
|
|
|
11
11
|
disableThinking: boolean;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
export type ModelBackend = "webllm" | "transformers-js";
|
|
14
|
+
export type ModelBackend = "webllm" | "transformers-js" | "onnx-runtime";
|
|
15
15
|
|
|
16
16
|
/** Input/output modality of a model, used for the library modality-flow line + Playground gating. */
|
|
17
17
|
export type Modality = "text" | "image" | "audio";
|
|
@@ -23,9 +23,11 @@ export type ModelTask = "text" | "vision" | "stt" | "tts";
|
|
|
23
23
|
* inference recipe in llm/multimodal.ts.
|
|
24
24
|
*/
|
|
25
25
|
export type MultimodalRuntime =
|
|
26
|
+
| "stt-tdt" // ONNX Runtime TDT speech recognition
|
|
26
27
|
| "stt" // automatic-speech-recognition pipeline (Whisper / Moonshine)
|
|
27
28
|
| "tts-kokoro" // kokoro-js KokoroTTS
|
|
28
29
|
| "tts-pipeline" // native text-to-speech pipeline (Supertonic)
|
|
30
|
+
| "image-classification" // scored image labels (NSFW detection)
|
|
29
31
|
| "vlm-vision2seq" // AutoModelForVision2Seq (SmolVLM)
|
|
30
32
|
| "vlm-imagetext" // AutoModelForImageTextToText (Qwen3-VL, GLM-OCR)
|
|
31
33
|
| "audio-text-to-text" // AutoModelForAudioTextToText with an <audio> token in the prompt (Granite Speech, Ultravox, non-realtime Voxtral)
|
|
@@ -60,6 +62,8 @@ export type ModelPreset = {
|
|
|
60
62
|
modalityOut?: Modality[];
|
|
61
63
|
/** How a multimodal (non-LLM-text) model loads + runs. Undefined for plain text LLMs. */
|
|
62
64
|
mmRuntime?: MultimodalRuntime;
|
|
65
|
+
/** Per-model image processing and generation behavior. */
|
|
66
|
+
visionOptions?: { defaultPrompt?: string; maxNewTokens?: number; preserveSpecialTokens?: boolean; doImageSplitting?: boolean; outputFormat?: "doctags" };
|
|
63
67
|
/** Optional display brand for the vendor tag (e.g. "Moonshine", "Kokoro", "Zhipu"). Falls back to the vendor label. */
|
|
64
68
|
brand?: string;
|
|
65
69
|
};
|
|
@@ -322,6 +326,80 @@ export const MODEL_PRESETS: ModelPreset[] = [
|
|
|
322
326
|
officialRepo: "https://huggingface.co/google/gemma-4-E4B-it",
|
|
323
327
|
artifactRepo: "https://huggingface.co/welcoma/gemma-4-E4B-it-q4f16_1-MLC",
|
|
324
328
|
},
|
|
329
|
+
{
|
|
330
|
+
id: "enriqueag/phonon-2.5-onnx",
|
|
331
|
+
label: "Phonon 2.5 — compact speech recognition via WebGPU + WASM",
|
|
332
|
+
shortName: "Phonon 2.5",
|
|
333
|
+
description: "Fermion speech recognition with a compact quantized WebGPU encoder and a WASM token-duration decoder. Audio stays on device.",
|
|
334
|
+
approximateDownload: "~381 MB encoder, decoder and vocabulary",
|
|
335
|
+
vramRequiredMB: 1536,
|
|
336
|
+
vramSource: "estimated",
|
|
337
|
+
lowResourceRequired: true,
|
|
338
|
+
disableThinkingSupported: false,
|
|
339
|
+
defaultRuntime: extractionPreset(),
|
|
340
|
+
source: "transformers-onnx",
|
|
341
|
+
backend: "onnx-runtime",
|
|
342
|
+
stability: "experimental",
|
|
343
|
+
officialRepo: "https://huggingface.co/FermionResearch/Phonon-2.5",
|
|
344
|
+
artifactRepo: "https://huggingface.co/enriqueag/phonon-2.5-onnx",
|
|
345
|
+
tasks: ["stt"], modalityIn: ["audio"], modalityOut: ["text"],
|
|
346
|
+
mmRuntime: "stt-tdt",
|
|
347
|
+
brand: "Fermion",
|
|
348
|
+
notes: "Requires WebGPU for the compact encoder; the decoder runs on CPU/WASM. Long audio is split near quiet intervals into chunks of at most 30 seconds. Memory is estimated.",
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
id: "istupakov/parakeet-tdt-0.6b-v3-onnx",
|
|
352
|
+
label: "Parakeet TDT 0.6B v3 — multilingual speech recognition via WASM",
|
|
353
|
+
shortName: "Parakeet TDT v3",
|
|
354
|
+
description: "NVIDIA multilingual token-duration speech recognition, using the published int8 ONNX encoder and decoder on CPU/WASM.",
|
|
355
|
+
approximateDownload: "~671 MB int8 encoder, decoder, frontend and vocabulary",
|
|
356
|
+
vramRequiredMB: 0,
|
|
357
|
+
vramSource: "estimated",
|
|
358
|
+
lowResourceRequired: false,
|
|
359
|
+
disableThinkingSupported: false,
|
|
360
|
+
defaultRuntime: extractionPreset(),
|
|
361
|
+
source: "transformers-onnx",
|
|
362
|
+
backend: "onnx-runtime",
|
|
363
|
+
stability: "experimental",
|
|
364
|
+
officialRepo: "https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3",
|
|
365
|
+
artifactRepo: "https://huggingface.co/istupakov/parakeet-tdt-0.6b-v3-onnx",
|
|
366
|
+
tasks: ["stt"], modalityIn: ["audio"], modalityOut: ["text"],
|
|
367
|
+
mmRuntime: "stt-tdt",
|
|
368
|
+
brand: "NVIDIA",
|
|
369
|
+
notes: "Runs on CPU/WASM and consumes system memory despite requiring no GPU memory. Long audio is split near quiet intervals into chunks of at most 30 seconds.",
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: "onnx-community/granite-docling-258M-ONNX",
|
|
373
|
+
label: "Granite Docling 258M — document conversion to DocTags",
|
|
374
|
+
shortName: "Granite Docling 258M",
|
|
375
|
+
description: "IBM Granite document vision model. Converts page images into structured DocTags, preserving layout and document markup.",
|
|
376
|
+
approximateDownload: "~1.27 GB FP32 ONNX modules plus tokenizer/config files",
|
|
377
|
+
vramRequiredMB: 3072, vramSource: "estimated", lowResourceRequired: false,
|
|
378
|
+
disableThinkingSupported: false, defaultRuntime: extractionPreset({ maxTokens: 4096 }),
|
|
379
|
+
source: "transformers-onnx", backend: "transformers-js", stability: "experimental",
|
|
380
|
+
officialRepo: "https://huggingface.co/ibm-granite/granite-docling-258M",
|
|
381
|
+
artifactRepo: "https://huggingface.co/onnx-community/granite-docling-258M-ONNX",
|
|
382
|
+
transformersDtype: "fp32", tasks: ["vision"], modalityIn: ["image", "text"], modalityOut: ["text"],
|
|
383
|
+
mmRuntime: "vlm-vision2seq", brand: "IBM",
|
|
384
|
+
visionOptions: { defaultPrompt: "Convert this page to docling.", maxNewTokens: 4096, preserveSpecialTokens: true, doImageSplitting: true, outputFormat: "doctags" },
|
|
385
|
+
notes: "FP32 is intentional: the FP16/q4f16 WebGPU paths failed accuracy smoke checks on Apple GPU. FP32 produced the expected result.",
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
id: "onnx-community/nsfw_image_detection-ONNX",
|
|
389
|
+
label: "NSFW image detection — scored normal / nsfw classification",
|
|
390
|
+
shortName: "NSFW image detection",
|
|
391
|
+
description: "ViT image classifier returning normal and nsfw scores. Scores are model predictions, not a guarantee of content safety.",
|
|
392
|
+
approximateDownload: "~343 MB FP32 ONNX weights plus image processor/config files",
|
|
393
|
+
vramRequiredMB: 768, vramSource: "estimated", lowResourceRequired: true,
|
|
394
|
+
disableThinkingSupported: false, defaultRuntime: extractionPreset({}),
|
|
395
|
+
source: "transformers-onnx", backend: "transformers-js", stability: "experimental",
|
|
396
|
+
officialRepo: "https://huggingface.co/Falconsai/nsfw_image_detection",
|
|
397
|
+
artifactRepo: "https://huggingface.co/onnx-community/nsfw_image_detection-ONNX",
|
|
398
|
+
transformersDtype: "fp32", tasks: ["vision"], modalityIn: ["image"], modalityOut: ["text"],
|
|
399
|
+
mmRuntime: "image-classification", brand: "Falconsai",
|
|
400
|
+
notes: "FP32 is intentional: the FP16/q4f16 WebGPU paths failed accuracy smoke checks on Apple GPU. FP32 produced the expected result.",
|
|
401
|
+
},
|
|
402
|
+
|
|
325
403
|
{
|
|
326
404
|
id: "onnx-community/whisper-base",
|
|
327
405
|
label: "Whisper Base — multilingual speech-to-text via Transformers.js/WebGPU",
|
|
@@ -948,6 +1026,48 @@ export const MODEL_PRESETS: ModelPreset[] = [
|
|
|
948
1026
|
artifactRepo: "https://huggingface.co/onnx-community/granite-4.0-h-micro-ONNX",
|
|
949
1027
|
transformersDtype: "q4f16",
|
|
950
1028
|
},
|
|
1029
|
+
{
|
|
1030
|
+
id: "RASMUS/MiniCPM5-2B-ONNX",
|
|
1031
|
+
label: "MiniCPM5 2B q4f16 — Transformers.js/WebGPU (~3 GB VRAM est.)",
|
|
1032
|
+
shortName: "MiniCPM5 2B",
|
|
1033
|
+
description:
|
|
1034
|
+
"OpenBMB's regular MiniCPM5 2B instruction model, quantized for browser GPU inference with streaming, optional thinking and measured token/timing metrics.",
|
|
1035
|
+
approximateDownload: "~1.83 GB ONNX weights plus tokenizer and browser cache overhead",
|
|
1036
|
+
vramRequiredMB: 3072,
|
|
1037
|
+
vramSource: "estimated",
|
|
1038
|
+
lowResourceRequired: false,
|
|
1039
|
+
disableThinkingSupported: true,
|
|
1040
|
+
defaultRuntime: extractionPreset({ maxTokens: 768, disableThinking: true }),
|
|
1041
|
+
source: "transformers-onnx",
|
|
1042
|
+
backend: "transformers-js",
|
|
1043
|
+
stability: "experimental",
|
|
1044
|
+
officialRepo: "https://huggingface.co/openbmb/MiniCPM5-2B",
|
|
1045
|
+
artifactRepo: "https://huggingface.co/RASMUS/MiniCPM5-2B-ONNX",
|
|
1046
|
+
transformersDtype: "q4f16",
|
|
1047
|
+
brand: "OpenBMB",
|
|
1048
|
+
notes: "Requires WebGPU shader-f16. JSON schema mode is prompt-only. Browser memory depends on context length; the VRAM figure is an estimate.",
|
|
1049
|
+
},
|
|
1050
|
+
{
|
|
1051
|
+
id: "j4ys0n/MiniCPM5-2B-heretic-abliterated-ONNX",
|
|
1052
|
+
label: "MiniCPM5 2B Heretic q4f16 — Transformers.js/WebGPU (~3 GB VRAM est.)",
|
|
1053
|
+
shortName: "MiniCPM5 2B Heretic",
|
|
1054
|
+
description:
|
|
1055
|
+
"Heretic-abliterated MiniCPM5 2B, converted from the original insraq safetensors checkpoint to browser-ready ONNX with streaming, optional thinking and measured token/timing metrics.",
|
|
1056
|
+
approximateDownload: "~1.83 GB ONNX weights plus tokenizer and browser cache overhead",
|
|
1057
|
+
vramRequiredMB: 3072,
|
|
1058
|
+
vramSource: "estimated",
|
|
1059
|
+
lowResourceRequired: false,
|
|
1060
|
+
disableThinkingSupported: true,
|
|
1061
|
+
defaultRuntime: extractionPreset({ maxTokens: 768, disableThinking: true }),
|
|
1062
|
+
source: "transformers-onnx",
|
|
1063
|
+
backend: "transformers-js",
|
|
1064
|
+
stability: "experimental",
|
|
1065
|
+
officialRepo: "https://huggingface.co/insraq/MiniCPM5-2B-heretic-abliterated",
|
|
1066
|
+
artifactRepo: "https://huggingface.co/j4ys0n/MiniCPM5-2B-heretic-abliterated-ONNX",
|
|
1067
|
+
transformersDtype: "q4f16",
|
|
1068
|
+
brand: "OpenBMB",
|
|
1069
|
+
notes: "Original Heretic checkpoint referenced by Abiray's model card. Requires WebGPU shader-f16. JSON schema mode is prompt-only; VRAM usage is estimated.",
|
|
1070
|
+
},
|
|
951
1071
|
{
|
|
952
1072
|
id: "Mike0021/MiniCPM5-1B-ONNX-Web",
|
|
953
1073
|
label: "MiniCPM5 1B ONNX-web — community q4 Transformers.js/WebGPU export",
|
|
@@ -986,6 +1106,31 @@ export const MODEL_PRESETS: ModelPreset[] = [
|
|
|
986
1106
|
artifactRepo: "https://huggingface.co/HuggingFaceTB/SmolLM3-3B-ONNX",
|
|
987
1107
|
transformersDtype: "q4",
|
|
988
1108
|
},
|
|
1109
|
+
{
|
|
1110
|
+
id: "j4ys0n/NVIDIA-Nemotron-3.5-Bolt-E4B-ONNX",
|
|
1111
|
+
label: "NVIDIA Nemotron 3.5 Bolt E4B ONNX — text, image and audio input; text output (~5.21 GB)",
|
|
1112
|
+
shortName: "Nemotron 3.5 Bolt E4B",
|
|
1113
|
+
description:
|
|
1114
|
+
"NVIDIA's post-trained Gemma 4 E4B, exported for Transformers.js/WebGPU with text, image, and audio input and text output.",
|
|
1115
|
+
approximateDownload: "~5.21 GB q4f16 ONNX artifacts plus tokenizer/config files",
|
|
1116
|
+
vramRequiredMB: 8192,
|
|
1117
|
+
vramSource: "estimated",
|
|
1118
|
+
lowResourceRequired: false,
|
|
1119
|
+
disableThinkingSupported: false,
|
|
1120
|
+
defaultRuntime: extractionPreset({ maxTokens: 768, topP: 0.9, repetitionPenalty: 1.05 }),
|
|
1121
|
+
source: "transformers-onnx",
|
|
1122
|
+
backend: "transformers-js",
|
|
1123
|
+
stability: "experimental",
|
|
1124
|
+
officialRepo: "https://huggingface.co/nvidia/NVIDIA-Nemotron-3.5-Bolt-E4B-BF16",
|
|
1125
|
+
artifactRepo: "https://huggingface.co/j4ys0n/NVIDIA-Nemotron-3.5-Bolt-E4B-ONNX",
|
|
1126
|
+
notes: "Text, image, and audio browser inference smoke-tested on Apple WebGPU. Output quality and GPU memory have not been benchmarked; GPU memory is an estimate, not a measured minimum.",
|
|
1127
|
+
transformersDtype: "q4f16",
|
|
1128
|
+
tasks: ["text", "vision", "stt"],
|
|
1129
|
+
modalityIn: ["text", "image", "audio"],
|
|
1130
|
+
modalityOut: ["text"],
|
|
1131
|
+
mmRuntime: "gemma-mm",
|
|
1132
|
+
brand: "NVIDIA",
|
|
1133
|
+
},
|
|
989
1134
|
{
|
|
990
1135
|
id: "onnx-community/gemma-4-E2B-it-ONNX",
|
|
991
1136
|
label: "Gemma 4 E2B ONNX — experimental Transformers.js/WebGPU alternative",
|
|
@@ -1169,3 +1314,9 @@ export function modelRepoLabel(preset: ModelPreset): string {
|
|
|
1169
1314
|
return preset.id;
|
|
1170
1315
|
}
|
|
1171
1316
|
}
|
|
1317
|
+
|
|
1318
|
+
/** Whether this preset has an implemented CPU/WASM load path. */
|
|
1319
|
+
export function supportsWasmFallback(preset: ModelPreset): boolean {
|
|
1320
|
+
return preset.id === "istupakov/parakeet-tdt-0.6b-v3-onnx" ||
|
|
1321
|
+
(preset.mmRuntime !== undefined && ["stt", "tts-pipeline", "tts-kokoro", "image-classification"].includes(preset.mmRuntime));
|
|
1322
|
+
}
|