@tanstack-ai-browser/wllama 0.0.0-stage → 0.2.1

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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +151 -2
  3. package/dist/adapter.d.ts +176 -0
  4. package/dist/adapter.d.ts.map +1 -0
  5. package/dist/adapter.js +195 -0
  6. package/dist/audio.d.ts +16 -0
  7. package/dist/audio.d.ts.map +1 -0
  8. package/dist/audio.js +80 -0
  9. package/dist/catalog.d.ts +104 -0
  10. package/dist/catalog.d.ts.map +1 -0
  11. package/dist/catalog.js +307 -0
  12. package/dist/client.d.ts +64 -0
  13. package/dist/client.d.ts.map +1 -0
  14. package/dist/client.js +148 -0
  15. package/dist/index.d.ts +12 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +7 -0
  18. package/dist/model-memory.d.ts +46 -0
  19. package/dist/model-memory.d.ts.map +1 -0
  20. package/dist/model-memory.js +79 -0
  21. package/dist/model-meta.d.ts +7 -0
  22. package/dist/model-meta.d.ts.map +1 -0
  23. package/dist/model-meta.js +5 -0
  24. package/dist/shared-load.d.ts +48 -0
  25. package/dist/shared-load.d.ts.map +1 -0
  26. package/dist/shared-load.js +103 -0
  27. package/dist/store.d.ts +8 -0
  28. package/dist/store.d.ts.map +1 -0
  29. package/dist/store.js +17 -0
  30. package/dist/template.d.ts +26 -0
  31. package/dist/template.d.ts.map +1 -0
  32. package/dist/template.js +25 -0
  33. package/dist/thinking.d.ts +39 -0
  34. package/dist/thinking.d.ts.map +1 -0
  35. package/dist/thinking.js +140 -0
  36. package/dist/tool-calls.d.ts +33 -0
  37. package/dist/tool-calls.d.ts.map +1 -0
  38. package/dist/tool-calls.js +203 -0
  39. package/package.json +63 -4
  40. package/src/adapter.ts +345 -0
  41. package/src/audio.test.ts +83 -0
  42. package/src/audio.ts +79 -0
  43. package/src/catalog.ts +399 -0
  44. package/src/client.ts +195 -0
  45. package/src/index.ts +11 -0
  46. package/src/runtime.test.ts +220 -0
  47. package/src/wllama-version.test.ts +19 -0
  48. package/src/wrapper.test.ts +186 -0
package/dist/audio.js ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Audio as llama.cpp reads it. Its multimodal library decodes WAV, MP3 and
3
+ * FLAC files itself, and resamples them to the model's rate. Anything else
4
+ * a browser records or plays (Opus in WebM or Ogg, AAC in MP4) is decoded
5
+ * here, with Web Audio, and handed over as WAV.
6
+ */
7
+ /** The rate other formats are decoded at: the audio encoders' own, so llama.cpp resamples nothing. */
8
+ const SAMPLE_RATE = 16000;
9
+ /** Whether `bytes` holds `text` (ASCII) at `at`. */
10
+ const holds = (bytes, at, text) => [...text].every((character, i) => bytes[at + i] === character.charCodeAt(0));
11
+ /** Whether `data` is a WAV, FLAC or MP3 file, by how it starts: the files llama.cpp decodes. */
12
+ export function decodesInLlama(data) {
13
+ const bytes = new Uint8Array(data, 0, Math.min(data.byteLength, 12));
14
+ if (bytes.length < 4)
15
+ return false;
16
+ if (holds(bytes, 0, "RIFF") && holds(bytes, 8, "WAVE"))
17
+ return true;
18
+ if (holds(bytes, 0, "fLaC"))
19
+ return true;
20
+ // An MP3 that opens with an ID3 tag, or with a frame: 11 sync bits, then
21
+ // Layer III (AAC's ADTS frames open the same way, as layer 0).
22
+ if (holds(bytes, 0, "ID3"))
23
+ return true;
24
+ const [first = 0, second = 0] = bytes;
25
+ return first === 0xff && (second & 0xe0) === 0xe0 && (second & 0x06) === 0x02;
26
+ }
27
+ /** Mono samples, each from -1 to 1, as a 16-bit PCM WAV file. */
28
+ export function wavFile(samples, sampleRate) {
29
+ const size = samples.length * 2;
30
+ const file = new DataView(new ArrayBuffer(44 + size));
31
+ const text = (at, value) => {
32
+ for (let i = 0; i < value.length; i++)
33
+ file.setUint8(at + i, value.charCodeAt(i));
34
+ };
35
+ text(0, "RIFF");
36
+ file.setUint32(4, 36 + size, true);
37
+ text(8, "WAVE");
38
+ text(12, "fmt ");
39
+ file.setUint32(16, 16, true);
40
+ // PCM, one channel, 2 bytes a sample.
41
+ file.setUint16(20, 1, true);
42
+ file.setUint16(22, 1, true);
43
+ file.setUint32(24, sampleRate, true);
44
+ file.setUint32(28, sampleRate * 2, true);
45
+ file.setUint16(32, 2, true);
46
+ file.setUint16(34, 16, true);
47
+ text(36, "data");
48
+ file.setUint32(40, size, true);
49
+ samples.forEach((value, i) => {
50
+ const sample = Math.max(-1, Math.min(1, value));
51
+ file.setInt16(44 + i * 2, Math.round(sample < 0 ? sample * 0x8000 : sample * 0x7fff), true);
52
+ });
53
+ return file.buffer;
54
+ }
55
+ /**
56
+ * An audio file's bytes as llama.cpp can decode them: as they are for WAV,
57
+ * MP3 and FLAC, else decoded by the browser and written as mono WAV.
58
+ */
59
+ export async function llamaAudio(data) {
60
+ if (decodesInLlama(data))
61
+ return data;
62
+ if (typeof OfflineAudioContext === "undefined") {
63
+ throw new Error("This audio isn't WAV, MP3 or FLAC, and there's no Web Audio here to decode it.");
64
+ }
65
+ let decoded;
66
+ try {
67
+ // A copy: decoding detaches the buffer it's given.
68
+ decoded = await new OfflineAudioContext(1, 1, SAMPLE_RATE).decodeAudioData(data.slice(0));
69
+ }
70
+ catch {
71
+ throw new Error("The browser couldn't decode this audio. WAV, MP3 and FLAC always work.");
72
+ }
73
+ const mono = new Float32Array(decoded.length);
74
+ for (let channel = 0; channel < decoded.numberOfChannels; channel++) {
75
+ decoded.getChannelData(channel).forEach((sample, i) => {
76
+ mono[i] = (mono[i] ?? 0) + sample / decoded.numberOfChannels;
77
+ });
78
+ }
79
+ return wavFile(mono, decoded.sampleRate);
80
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * The models this package knows: GGUF files on Hugging Face that run on the
3
+ * CPU with wllama, with what each supports, the sampling from its card, and
4
+ * how it writes tool calls.
5
+ */
6
+ import type { ModelReasoning } from "@tanstack/ai";
7
+ import type { ModelReasoningCapability } from "@tanstack/ai/adapter-internals";
8
+ /**
9
+ * Sampling settings from a model's card, in llama.cpp's request fields (the
10
+ * names `modelOptions` takes): used unless a call's `modelOptions` sets them.
11
+ */
12
+ export type Sampling = {
13
+ temperature: number;
14
+ /** Left out where the card names none: llama.cpp's default. */
15
+ top_k?: number;
16
+ /** llama.cpp's repetition penalty (`--repeat-penalty`). */
17
+ repeat_penalty: number;
18
+ /** Nucleus sampling: the likeliest tokens up to this probability. */
19
+ top_p?: number;
20
+ /** Drops tokens less likely than this share of the likeliest one's odds. */
21
+ min_p?: number;
22
+ /** Lowers the odds of tokens that have appeared at all. */
23
+ presence_penalty?: number;
24
+ };
25
+ export type WllamaModelMeta = {
26
+ /** The Hugging Face repository the GGUF file is in. */
27
+ name: string;
28
+ /** The GGUF file in the repository. */
29
+ file: string;
30
+ /** The projector's GGUF file in the repository, for a model that reads images or audio. */
31
+ mmproj?: string;
32
+ supports: {
33
+ /** Text, and images or audio for a model whose projector reads them. */
34
+ input: ReadonlyArray<"text" | "image" | "audio">;
35
+ output: ReadonlyArray<"text">;
36
+ capabilities: ReadonlyArray<"tools" | "thinking">;
37
+ };
38
+ /** Parameter count. */
39
+ size: string;
40
+ /** Reasoning levels for `chat({ reasoning })`, read from the chat template. */
41
+ reasoning?: ModelReasoning;
42
+ /** Sampling from the model card, used unless `modelOptions` overrides it. */
43
+ sampling: Sampling;
44
+ /** The card's sampling while the chat template thinks, where it differs. */
45
+ thinkingSampling?: Sampling;
46
+ };
47
+ export declare const WLLAMA_CHAT_MODELS: readonly ["LiquidAI/LFM3-1.7B-GGUF", "LiquidAI/LFM3-VL-2B-GGUF", "LiquidAI/LFM2.5-VL-3B-GGUF", "LiquidAI/LFM2.5-VL-1.6B-GGUF", "LiquidAI/LFM2.5-VL-450M-GGUF", "LiquidAI/LFM3-Omni-2B-GGUF", "LiquidAI/LFM3-Audio-2B-GGUF", "LiquidAI/LFM2.5-Audio-1.5B-GGUF", "LiquidAI/LFM2.5-1.2B-Instruct-GGUF", "LiquidAI/LFM2.5-1.2B-Thinking-GGUF", "LiquidAI/LFM2.5-350M-GGUF", "unsloth/Qwen3.5-2B-GGUF"];
48
+ /** A model this package lists. */
49
+ export type WllamaChatModel = (typeof WLLAMA_CHAT_MODELS)[number];
50
+ /** Each listed model's metadata, by name. */
51
+ export declare const WLLAMA_MODEL_META: Readonly<Record<WllamaChatModel, WllamaModelMeta>>;
52
+ declare const WLLAMA_REASONING: {
53
+ readonly "LiquidAI/LFM3-1.7B-GGUF": {
54
+ readonly levels: {
55
+ readonly off: "false";
56
+ readonly high: "true";
57
+ };
58
+ readonly budget: true;
59
+ };
60
+ readonly "LiquidAI/LFM3-VL-2B-GGUF": {
61
+ readonly levels: {
62
+ readonly off: "false";
63
+ readonly high: "true";
64
+ };
65
+ readonly budget: true;
66
+ };
67
+ readonly "LiquidAI/LFM2.5-1.2B-Thinking-GGUF": {
68
+ readonly levels: {
69
+ readonly high: "true";
70
+ };
71
+ readonly budget: true;
72
+ };
73
+ readonly "unsloth/Qwen3.5-2B-GGUF": {
74
+ readonly levels: {
75
+ readonly off: "false";
76
+ readonly high: "true";
77
+ };
78
+ readonly budget: true;
79
+ };
80
+ };
81
+ /** The reasoning levels of each listed model that reasons. */
82
+ export type WllamaModelReasoningByName = {
83
+ [K in keyof typeof WLLAMA_REASONING]: ModelReasoningCapability<(typeof WLLAMA_REASONING)[K]>;
84
+ };
85
+ /**
86
+ * Reasoning data of each listed model that reasons, for
87
+ * `supportedReasoningLevels()`. A model missing here doesn't reason.
88
+ */
89
+ export declare const WLLAMA_MODEL_REASONING: Readonly<Record<string, ModelReasoning>>;
90
+ /** A listed model's metadata. */
91
+ export declare const modelMeta: (model: WllamaChatModel) => WllamaModelMeta;
92
+ /** A model the catalog lists, a GGUF file's URL, or a GGUF file on Hugging Face as `owner/repo/file.gguf`. */
93
+ export type WllamaModel = WllamaChatModel | (string & {});
94
+ /** Whether the catalog lists `model`. */
95
+ export declare const listed: (model: string) => model is WllamaChatModel;
96
+ /** Where a listed model's projector downloads from; none for a model that reads text only. */
97
+ export declare const mmprojUrl: (model: WllamaModel) => string | undefined;
98
+ /**
99
+ * Where a model's GGUF file downloads from: a listed model's file, or the
100
+ * named one, at its latest version (main) on Hugging Face.
101
+ */
102
+ export declare const modelUrl: (model: WllamaModel) => string;
103
+ export {};
104
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAA;AAE9E;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,2DAA2D;IAC3D,cAAc,EAAE,MAAM,CAAA;IACtB,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAA;IACZ,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE;QACR,wEAAwE;QACxE,KAAK,EAAE,aAAa,CAAC,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,CAAA;QAChD,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;QAC7B,YAAY,EAAE,aAAa,CAAC,OAAO,GAAG,UAAU,CAAC,CAAA;KAClD,CAAA;IACD,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,+EAA+E;IAC/E,SAAS,CAAC,EAAE,cAAc,CAAA;IAC1B,6EAA6E;IAC7E,QAAQ,EAAE,QAAQ,CAAA;IAClB,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,QAAQ,CAAA;CAC5B,CAAA;AAqQD,eAAO,MAAM,kBAAkB,oYAarB,CAAA;AAEV,kCAAkC;AAClC,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEjE,6CAA6C;AAC7C,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CACtC,MAAM,CAAC,eAAe,EAAE,eAAe,CAAC,CAczC,CAAA;AAED,QAAA,MAAM,gBAAgB;;;qBAlSV,GAAG,EAAE,OAAO;qBAAE,IAAI,EAAE,MAAM;;;;;;;;;;;;;qBAGJ,IAAI,EAAE,MAAM;;;;;;;;;;;CAoSpC,CAAA;AAEV,8DAA8D;AAC9D,MAAM,MAAM,0BAA0B,GAAG;KACtC,CAAC,IAAI,MAAM,OAAO,gBAAgB,GAAG,wBAAwB,CAC5D,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAC7B;CACF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAC1D,CAAA;AAElB,iCAAiC;AACjC,eAAO,MAAM,SAAS,UAAW,eAAe,KAAG,eACzB,CAAA;AAE1B,8GAA8G;AAC9G,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAEzD,yCAAyC;AACzC,eAAO,MAAM,MAAM,UAAW,MAAM,KAAG,KAAK,IAAI,eACgB,CAAA;AAEhE,8FAA8F;AAC9F,eAAO,MAAM,SAAS,UAAW,WAAW,uBAI3C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,UAAW,WAAW,WAW1C,CAAA"}
@@ -0,0 +1,307 @@
1
+ // Reasoning shapes, each level's value being the chat template's
2
+ // `enable_thinking`. LFM3's template takes it (thinking by default), and
3
+ // Qwen3.5's (thinking off by default); LFM2.5 Thinking always thinks and
4
+ // takes no switch.
5
+ const THINKING_SWITCH = {
6
+ levels: { off: "false", high: "true" },
7
+ budget: true
8
+ };
9
+ const ALWAYS_THINKS = { levels: { high: "true" }, budget: true };
10
+ const TOOLS_AND_THINKING = {
11
+ input: ["text"],
12
+ output: ["text"],
13
+ capabilities: ["tools", "thinking"]
14
+ };
15
+ const TOOLS = {
16
+ input: ["text"],
17
+ output: ["text"],
18
+ capabilities: ["tools"]
19
+ };
20
+ const IMAGES_AND_TOOLS = {
21
+ input: ["text", "image"],
22
+ output: ["text"],
23
+ capabilities: ["tools"]
24
+ };
25
+ const IMAGES_TOOLS_AND_THINKING = {
26
+ input: ["text", "image"],
27
+ output: ["text"],
28
+ capabilities: ["tools", "thinking"]
29
+ };
30
+ const AUDIO_AND_TOOLS = {
31
+ input: ["text", "audio"],
32
+ output: ["text"],
33
+ capabilities: ["tools"]
34
+ };
35
+ const IMAGES_AUDIO_AND_TOOLS = {
36
+ input: ["text", "image", "audio"],
37
+ output: ["text"],
38
+ capabilities: ["tools"]
39
+ };
40
+ const AUDIO = {
41
+ input: ["text", "audio"],
42
+ output: ["text"],
43
+ capabilities: []
44
+ };
45
+ /** LFM3's card: `--temp 0.1 --top-k 50 --repeat-penalty 1.1`. */
46
+ const LFM3_SAMPLING = {
47
+ temperature: 0.1,
48
+ top_k: 50,
49
+ repeat_penalty: 1.1
50
+ };
51
+ /** The cards' llama.cpp settings: `--temp 0.1 --top-k 50 --repeat-penalty 1.05`. */
52
+ const LFM2_5_SAMPLING = {
53
+ temperature: 0.1,
54
+ top_k: 50,
55
+ repeat_penalty: 1.05
56
+ };
57
+ /** LFM2.5-VL 450M's and 1.6B's cards: `--temp 0.1 --min-p 0.15 --repeat-penalty 1.05`. */
58
+ const LFM2_5_VL_SAMPLING = {
59
+ temperature: 0.1,
60
+ min_p: 0.15,
61
+ repeat_penalty: 1.05
62
+ };
63
+ /** LFM2.5-VL 3B's card: `--temp 0.2 --top-k 50 --repeat-penalty 1.0`. */
64
+ const LFM2_5_VL_3B_SAMPLING = {
65
+ temperature: 0.2,
66
+ top_k: 50,
67
+ repeat_penalty: 1
68
+ };
69
+ /** LFM2.5 Thinking's card: `--temp 0.05 --top-k 50 --repeat-penalty 1.05`. */
70
+ const LFM2_5_THINKING_SAMPLING = {
71
+ temperature: 0.05,
72
+ top_k: 50,
73
+ repeat_penalty: 1.05
74
+ };
75
+ /** The audio models' cards: greedy decoding (`--temp 0`) for text. */
76
+ const GREEDY_SAMPLING = {
77
+ temperature: 0,
78
+ repeat_penalty: 1
79
+ };
80
+ /** Qwen3.5's card, text tasks without thinking (its default). */
81
+ const QWEN_3_5_SAMPLING = {
82
+ temperature: 1,
83
+ top_k: 20,
84
+ top_p: 1,
85
+ min_p: 0,
86
+ presence_penalty: 2,
87
+ repeat_penalty: 1
88
+ };
89
+ /** Qwen3.5's card, text tasks with thinking. */
90
+ const QWEN_3_5_THINKING_SAMPLING = { ...QWEN_3_5_SAMPLING, top_p: 0.95, presence_penalty: 1.5 };
91
+ // Liquid's models are Q4_K_M: in wllama's WebAssembly build it reads
92
+ // prompts and writes tokens about three times as fast as Q4_0 (llama.cpp
93
+ // has no WebAssembly SIMD kernel for Q4_0 to match its Q4_K one), and
94
+ // faster than Q5_K_M, Q6_K and Q8_0, at 5% more size than Q4_0. LFM2.5
95
+ // 1.2B on an M3 Ultra, 24 threads: Q4_K_M 231 prompt tokens/s and 112
96
+ // tokens/s, Q4_0 (Liquid's QAD build too) 65 and 45.
97
+ /**
98
+ * Liquid AI's LFM3 1.7B, from its official GGUF repository: thinks by
99
+ * default, or answers straight away with reasoning off. It needs wllama
100
+ * 3.13's llama.cpp, the first published with its architecture.
101
+ */
102
+ const LFM3_1_7B = {
103
+ name: "LiquidAI/LFM3-1.7B-GGUF",
104
+ file: "LFM3-1.7B-Q4_K_M.gguf",
105
+ supports: TOOLS_AND_THINKING,
106
+ size: "1.7b",
107
+ reasoning: THINKING_SWITCH,
108
+ sampling: LFM3_SAMPLING
109
+ };
110
+ // The vision models load with their projector in Q8_0, the smallest Liquid
111
+ // publishes: it reads an image as the F16 one does in a third to half the size.
112
+ /**
113
+ * LFM3-VL 2B: LFM3 with a vision encoder, from Liquid's GGUF repository.
114
+ * Thinks by default, as LFM3 does. 1.10 GB, and a 203 MB projector.
115
+ */
116
+ const LFM3_VL_2B = {
117
+ name: "LiquidAI/LFM3-VL-2B-GGUF",
118
+ file: "LFM3-VL-2B-Q4_K_M.gguf",
119
+ mmproj: "mmproj-LFM3-VL-2B-Q8_0.gguf",
120
+ supports: IMAGES_TOOLS_AND_THINKING,
121
+ size: "2b",
122
+ reasoning: THINKING_SWITCH,
123
+ sampling: LFM3_SAMPLING
124
+ };
125
+ /** LFM2.5-VL 3B, from Liquid's GGUF repository: 1.67 GB, and a 583 MB projector. */
126
+ const LFM2_5_VL_3B = {
127
+ name: "LiquidAI/LFM2.5-VL-3B-GGUF",
128
+ file: "LFM2.5-VL-3B-Q4_K_M.gguf",
129
+ mmproj: "mmproj-LFM2.5-VL-3B-Q8_0.gguf",
130
+ supports: IMAGES_AND_TOOLS,
131
+ size: "3b",
132
+ sampling: LFM2_5_VL_3B_SAMPLING
133
+ };
134
+ /** LFM2.5-VL 1.6B, from Liquid's GGUF repository: 731 MB, and a 583 MB projector. */
135
+ const LFM2_5_VL_1_6B = {
136
+ name: "LiquidAI/LFM2.5-VL-1.6B-GGUF",
137
+ file: "LFM2.5-VL-1.6B-Q4_K_M.gguf",
138
+ mmproj: "mmproj-LFM2.5-VL-1.6b-Q8_0.gguf",
139
+ supports: IMAGES_AND_TOOLS,
140
+ size: "1.6b",
141
+ sampling: LFM2_5_VL_SAMPLING
142
+ };
143
+ /** LFM2.5-VL 450M, from Liquid's GGUF repository: 229 MB, and a 103 MB projector. */
144
+ const LFM2_5_VL_450M = {
145
+ name: "LiquidAI/LFM2.5-VL-450M-GGUF",
146
+ file: "LFM2.5-VL-450M-Q4_K_M.gguf",
147
+ mmproj: "mmproj-LFM2.5-VL-450m-Q8_0.gguf",
148
+ supports: IMAGES_AND_TOOLS,
149
+ size: "450m",
150
+ sampling: LFM2_5_VL_SAMPLING
151
+ };
152
+ // The audio models answer in text here: their speech output needs
153
+ // llama.cpp's speech APIs, which this adapter doesn't call. Their projector
154
+ // is the audio encoder (and, for LFM3-Omni, the vision encoder too), in
155
+ // Q8_0, as their cards pair it with the Q4_K_M model.
156
+ /**
157
+ * LFM3-Omni 2B: reads images and audio (speech, or other sound) in one
158
+ * model, from Liquid's GGUF repository. It doesn't reason: every answer
159
+ * opens with an empty thinking block. 1.10 GB, and a 507 MB projector.
160
+ */
161
+ const LFM3_OMNI_2B = {
162
+ name: "LiquidAI/LFM3-Omni-2B-GGUF",
163
+ file: "LFM3-Omni-2B-Q4_K_M.gguf",
164
+ mmproj: "mmproj-LFM3-Omni-2B-Q8_0.gguf",
165
+ supports: IMAGES_AUDIO_AND_TOOLS,
166
+ size: "2b",
167
+ sampling: GREEDY_SAMPLING
168
+ };
169
+ /**
170
+ * LFM3-Audio 2B: reads audio (transcribes and translates speech in 18
171
+ * languages, answers questions about a recording, calls tools from a
172
+ * spoken request), from Liquid's GGUF repository. 1.10 GB, and a 304 MB
173
+ * projector.
174
+ */
175
+ const LFM3_AUDIO_2B = {
176
+ name: "LiquidAI/LFM3-Audio-2B-GGUF",
177
+ file: "LFM3-Audio-2B-Q4_K_M.gguf",
178
+ mmproj: "mmproj-LFM3-Audio-2B-Q8_0.gguf",
179
+ supports: AUDIO_AND_TOOLS,
180
+ size: "2b",
181
+ sampling: GREEDY_SAMPLING
182
+ };
183
+ /**
184
+ * LFM2.5-Audio 1.5B: reads audio, in English, from Liquid's GGUF
185
+ * repository, which has no Q4_K_M: Q4_0, 696 MB, and a 220 MB projector.
186
+ * It calls no tools: told of tools, it answers that none of them can hear
187
+ * a recording, so it's best sent none.
188
+ */
189
+ const LFM2_5_AUDIO_1_5B = {
190
+ name: "LiquidAI/LFM2.5-Audio-1.5B-GGUF",
191
+ file: "LFM2.5-Audio-1.5B-Q4_0.gguf",
192
+ mmproj: "mmproj-LFM2.5-Audio-1.5B-Q4_0.gguf",
193
+ supports: AUDIO,
194
+ size: "1.5b",
195
+ sampling: GREEDY_SAMPLING
196
+ };
197
+ /** Liquid AI's LFM2.5 1.2B Instruct, from its official GGUF repository. */
198
+ const LFM2_5_1_2B = {
199
+ name: "LiquidAI/LFM2.5-1.2B-Instruct-GGUF",
200
+ file: "LFM2.5-1.2B-Instruct-Q4_K_M.gguf",
201
+ supports: TOOLS,
202
+ size: "1.2b",
203
+ sampling: LFM2_5_SAMPLING
204
+ };
205
+ /** LFM2.5 1.2B Thinking: thinks before every answer. */
206
+ const LFM2_5_1_2B_THINKING = {
207
+ name: "LiquidAI/LFM2.5-1.2B-Thinking-GGUF",
208
+ file: "LFM2.5-1.2B-Thinking-Q4_K_M.gguf",
209
+ supports: TOOLS_AND_THINKING,
210
+ size: "1.2b",
211
+ reasoning: ALWAYS_THINKS,
212
+ sampling: LFM2_5_THINKING_SAMPLING
213
+ };
214
+ /**
215
+ * LFM2.5 350M: the smallest and quickest, for short answers, extraction and
216
+ * tool calls rather than knowledge or code.
217
+ */
218
+ const LFM2_5_350M = {
219
+ name: "LiquidAI/LFM2.5-350M-GGUF",
220
+ file: "LFM2.5-350M-Q4_K_M.gguf",
221
+ supports: TOOLS,
222
+ size: "350m",
223
+ sampling: LFM2_5_SAMPLING
224
+ };
225
+ /**
226
+ * Qwen3.5 2B, in Unsloth's GGUF build (Qwen publishes none): linear-attention
227
+ * layers with a full-attention layer every fourth, so the attention cache
228
+ * stays small. Thinking is off unless turned on. Q4_K_M, as Liquid's: 1.28
229
+ * GB.
230
+ */
231
+ const QWEN_3_5_2B = {
232
+ name: "unsloth/Qwen3.5-2B-GGUF",
233
+ file: "Qwen3.5-2B-Q4_K_M.gguf",
234
+ supports: TOOLS_AND_THINKING,
235
+ size: "2b",
236
+ reasoning: THINKING_SWITCH,
237
+ sampling: QWEN_3_5_SAMPLING,
238
+ thinkingSampling: QWEN_3_5_THINKING_SAMPLING
239
+ };
240
+ export const WLLAMA_CHAT_MODELS = [
241
+ LFM3_1_7B.name,
242
+ LFM3_VL_2B.name,
243
+ LFM2_5_VL_3B.name,
244
+ LFM2_5_VL_1_6B.name,
245
+ LFM2_5_VL_450M.name,
246
+ LFM3_OMNI_2B.name,
247
+ LFM3_AUDIO_2B.name,
248
+ LFM2_5_AUDIO_1_5B.name,
249
+ LFM2_5_1_2B.name,
250
+ LFM2_5_1_2B_THINKING.name,
251
+ LFM2_5_350M.name,
252
+ QWEN_3_5_2B.name
253
+ ];
254
+ /** Each listed model's metadata, by name. */
255
+ export const WLLAMA_MODEL_META = {
256
+ [LFM3_1_7B.name]: LFM3_1_7B,
257
+ [LFM3_VL_2B.name]: LFM3_VL_2B,
258
+ [LFM2_5_VL_3B.name]: LFM2_5_VL_3B,
259
+ [LFM2_5_VL_1_6B.name]: LFM2_5_VL_1_6B,
260
+ [LFM2_5_VL_450M.name]: LFM2_5_VL_450M,
261
+ [LFM3_OMNI_2B.name]: LFM3_OMNI_2B,
262
+ [LFM3_AUDIO_2B.name]: LFM3_AUDIO_2B,
263
+ [LFM2_5_AUDIO_1_5B.name]: LFM2_5_AUDIO_1_5B,
264
+ [LFM2_5_1_2B.name]: LFM2_5_1_2B,
265
+ [LFM2_5_1_2B_THINKING.name]: LFM2_5_1_2B_THINKING,
266
+ [LFM2_5_350M.name]: LFM2_5_350M,
267
+ [QWEN_3_5_2B.name]: QWEN_3_5_2B
268
+ };
269
+ const WLLAMA_REASONING = {
270
+ [LFM3_1_7B.name]: LFM3_1_7B.reasoning,
271
+ [LFM3_VL_2B.name]: LFM3_VL_2B.reasoning,
272
+ [LFM2_5_1_2B_THINKING.name]: LFM2_5_1_2B_THINKING.reasoning,
273
+ [QWEN_3_5_2B.name]: QWEN_3_5_2B.reasoning
274
+ };
275
+ /**
276
+ * Reasoning data of each listed model that reasons, for
277
+ * `supportedReasoningLevels()`. A model missing here doesn't reason.
278
+ */
279
+ export const WLLAMA_MODEL_REASONING = WLLAMA_REASONING;
280
+ /** A listed model's metadata. */
281
+ export const modelMeta = (model) => WLLAMA_MODEL_META[model];
282
+ /** Whether the catalog lists `model`. */
283
+ export const listed = (model) => Object.prototype.hasOwnProperty.call(WLLAMA_MODEL_META, model);
284
+ /** Where a listed model's projector downloads from; none for a model that reads text only. */
285
+ export const mmprojUrl = (model) => {
286
+ if (!listed(model))
287
+ return undefined;
288
+ const { name, mmproj } = modelMeta(model);
289
+ return mmproj && `https://huggingface.co/${name}/resolve/main/${mmproj}`;
290
+ };
291
+ /**
292
+ * Where a model's GGUF file downloads from: a listed model's file, or the
293
+ * named one, at its latest version (main) on Hugging Face.
294
+ */
295
+ export const modelUrl = (model) => {
296
+ if (listed(model)) {
297
+ const { name, file } = modelMeta(model);
298
+ return `https://huggingface.co/${name}/resolve/main/${file}`;
299
+ }
300
+ if (/^https?:\/\//.test(model))
301
+ return model;
302
+ const [owner, repo, ...path] = model.split("/");
303
+ if (!owner || !repo || path.length === 0 || !model.endsWith(".gguf")) {
304
+ throw new Error(`${model} isn't a model this package lists: name its GGUF file, as "owner/repo/file.gguf" or a URL.`);
305
+ }
306
+ return `https://huggingface.co/${owner}/${repo}/resolve/main/${path.join("/")}`;
307
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The page's side of wllama: llama.cpp compiled to WebAssembly, which runs
3
+ * in wllama's own Web Worker. One model at a time: loading another frees
4
+ * the previous one. wllama downloads models to the Origin Private File
5
+ * System once, and loads them from there afterwards.
6
+ *
7
+ * wllama is the official `@wllama/wllama` package, unmodified, used through
8
+ * its public API. Its package has no `exports` and its `main` names a file
9
+ * it doesn't ship, so its built ES module is imported by path.
10
+ */
11
+ import { Wllama } from "@wllama/wllama/esm/index.js";
12
+ import type { WllamaCompat } from "@wllama/wllama/esm/index.js";
13
+ import type { WllamaModel } from "./catalog.js";
14
+ /**
15
+ * Where a model stands in this browser, with the Prompt API's values
16
+ * (`LanguageModel.availability()`).
17
+ */
18
+ export type Availability = "unavailable" | "downloadable" | "downloading" | "available";
19
+ /** A download's progress, from 0 to 1. */
20
+ export type DownloadProgressCallback = (progress: number) => void;
21
+ /** What a model loads with. A load with other settings loads the model again. */
22
+ export type LoadSettings = {
23
+ /** Most tokens a chat may hold; undefined for the model's training context. */
24
+ contextLength?: number;
25
+ /** Where wllama's WebAssembly file is served (`WLLAMA_WASM_URL` by default). */
26
+ wasmUrl?: string;
27
+ /** wllama's compat build, for browsers without JSPI (Safari), passed to `Wllama.setCompat`. */
28
+ compat?: WllamaCompat;
29
+ /** CPU threads; wllama's default otherwise. */
30
+ threads?: number;
31
+ /** `false` to run on the CPU only. */
32
+ gpu?: boolean;
33
+ /** wllama's and llama.cpp's logs, in the console. */
34
+ debug?: boolean;
35
+ };
36
+ /** The version of `@wllama/wllama` this package depends on. */
37
+ export declare const WLLAMA_VERSION = "3.13.0";
38
+ /**
39
+ * wllama's WebAssembly file on jsDelivr, for `WLLAMA_VERSION`: the URL of
40
+ * wllama's own `WasmFromCDN`. It has to match wllama's JavaScript, so it's
41
+ * the same version.
42
+ */
43
+ export declare const WLLAMA_WASM_URL = "https://cdn.jsdelivr.net/npm/@wllama/wllama@3.13.0/esm/wasm/wllama.wasm";
44
+ /** Frees the loaded model, stopping its download. Given a model, only if it's the one loaded. */
45
+ export declare function unloadModel(model?: WllamaModel): Promise<void>;
46
+ /**
47
+ * The model, loaded with `settings` (downloading it the first time), in
48
+ * place of any other. `onDownloadProgress` hears the download, 0 to 1,
49
+ * then 1 once the model is ready. Aborting `signal` stops this caller's
50
+ * wait; `unloadModel` stops the load.
51
+ */
52
+ export declare function loadModel(model: WllamaModel, settings: LoadSettings, { onDownloadProgress, signal }?: {
53
+ onDownloadProgress?: DownloadProgressCallback;
54
+ signal?: AbortSignal;
55
+ }): Promise<Wllama>;
56
+ /**
57
+ * Where `model` stands in this browser, with the Prompt API's values:
58
+ * "unavailable" where wllama can't run (no WebAssembly, Web Worker or
59
+ * Origin Private File System), "downloading" while this page downloads it,
60
+ * "available" when it's loaded or its file is in wllama's storage, else
61
+ * "downloadable". It reads storage only.
62
+ */
63
+ export declare function availability(model: WllamaModel): Promise<Availability>;
64
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAoC,MAAM,EAAE,MAAM,6BAA6B,CAAA;AACtF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAG/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE/C;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,cAAc,GAAG,aAAa,GAAG,WAAW,CAAA;AAEvF,0CAA0C;AAC1C,MAAM,MAAM,wBAAwB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;AAEjE,iFAAiF;AACjF,MAAM,MAAM,YAAY,GAAG;IACzB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,+FAA+F;IAC/F,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sCAAsC;IACtC,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,qDAAqD;IACrD,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,+DAA+D;AAC/D,eAAO,MAAM,cAAc,WAAW,CAAA;AAEtC;;;;GAIG;AACH,eAAO,MAAM,eAAe,4EAAuF,CAAA;AAiCnH,iGAAiG;AACjG,wBAAsB,WAAW,CAAC,KAAK,CAAC,EAAE,WAAW,iBAQpD;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,YAAY,EACtB,EAAE,kBAAkB,EAAE,MAAM,EAAE,GAAE;IAAE,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GAC3G,OAAO,CAAC,MAAM,CAAC,CA+DjB;AAKD;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAgB5E"}