@mlx-node/lm 0.0.13 → 0.0.15

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 (42) hide show
  1. package/dist/chat-session.d.ts +1 -1
  2. package/dist/chat-session.d.ts.map +1 -1
  3. package/dist/chat-session.js +2 -2
  4. package/dist/draft-companion.d.ts +16 -0
  5. package/dist/draft-companion.d.ts.map +1 -0
  6. package/dist/draft-companion.js +76 -0
  7. package/dist/family-data.d.ts +2 -0
  8. package/dist/family-data.d.ts.map +1 -1
  9. package/dist/family-data.js +2 -0
  10. package/dist/gguf-metadata.d.ts +2 -0
  11. package/dist/gguf-metadata.d.ts.map +1 -0
  12. package/dist/gguf-metadata.js +128 -0
  13. package/dist/model-detection.d.ts +6 -0
  14. package/dist/model-detection.d.ts.map +1 -0
  15. package/dist/model-detection.js +38 -0
  16. package/dist/model-discovery.d.ts +24 -0
  17. package/dist/model-discovery.d.ts.map +1 -0
  18. package/dist/model-discovery.js +274 -0
  19. package/dist/models/model-loader.d.ts +6 -0
  20. package/dist/models/model-loader.d.ts.map +1 -1
  21. package/dist/models/model-loader.js +10 -32
  22. package/dist/models/paged-config-override.d.ts.map +1 -1
  23. package/dist/models/paged-config-override.js +21 -1
  24. package/dist/stream.d.ts.map +1 -1
  25. package/dist/stream.js +5 -5
  26. package/package.json +21 -3
  27. package/src/chat-session.ts +2369 -0
  28. package/src/draft-companion.ts +74 -0
  29. package/src/family-data.ts +542 -0
  30. package/src/gguf-metadata.ts +117 -0
  31. package/src/index.ts +151 -0
  32. package/src/model-detection.ts +46 -0
  33. package/src/model-discovery.ts +329 -0
  34. package/src/models/lfm2-configs.ts +110 -0
  35. package/src/models/model-loader.ts +256 -0
  36. package/src/models/paged-config-override.ts +387 -0
  37. package/src/models/qwen3-configs.ts +113 -0
  38. package/src/models/qwen3_5-configs.ts +60 -0
  39. package/src/profiling.ts +69 -0
  40. package/src/stream.ts +960 -0
  41. package/src/tools/index.ts +58 -0
  42. package/src/tools/types.ts +215 -0
@@ -0,0 +1,274 @@
1
+ /** Shared, native-free discovery of local chat checkpoints. */
2
+ import { readdir, stat } from 'node:fs/promises';
3
+ import { basename, join } from 'node:path';
4
+ import { launchPresetFor, familyTraitsFor, NON_GENERATIVE_FAMILY_IDS, MODEL_FAMILY_DATA, } from './family-data.js';
5
+ import { detectModelType, readGgufArchitecture, readModelConfig } from './model-detection.js';
6
+ /**
7
+ * The Qwen3.5/Qwen3.8 discovery filter retains its XL policy. Gemma4 and Muse
8
+ * accept all supported tensor formats, including Q4_0 QAT checkpoints.
9
+ * Match the Unsloth Dynamic XL target names users download, while excluding
10
+ * ordinary Q4_K_M files and companion artifacts such as imatrix/mmproj/draft.
11
+ */
12
+ const QWEN35_XL_GGUF = /(?:^|[-_.])Q\d+_K_XL\.gguf$/i;
13
+ const GGUF_COMPANION_NAME = /(?:^|[-_.])(?:imatrix|mmproj|dflash|draft)(?:[-_.]|$)/i;
14
+ // Match the native loaders' primary files/shards. A draft or projector
15
+ // SafeTensors file beside a GGUF is not a converted target checkpoint.
16
+ const PRIMARY_SAFETENSORS = /^(?:model|weights)\.safetensors$|^model(?:-|\.safetensors-).+-of-.+\.safetensors$/;
17
+ function isQwen35XlGguf(name) {
18
+ return QWEN35_XL_GGUF.test(name) && !GGUF_COMPANION_NAME.test(name);
19
+ }
20
+ function ggufModelName(name) {
21
+ return name.slice(0, -'.gguf'.length);
22
+ }
23
+ function requiresGgufAssets(modelType) {
24
+ return modelType === 'gemma4' || modelType === 'muse_glimmer';
25
+ }
26
+ function matchesGgufFamily(path, modelType) {
27
+ const architecture = readGgufArchitecture(path);
28
+ return MODEL_FAMILY_DATA.some((family) => family.id === modelType &&
29
+ 'ggufArchitectures' in family &&
30
+ family.ggufArchitectures.some((supported) => supported === architecture));
31
+ }
32
+ async function hasGgufAssets(modelDir) {
33
+ try {
34
+ const assets = await Promise.all(['config.json', 'tokenizer.json'].map((name) => stat(join(modelDir, name))));
35
+ return assets.every((asset) => asset.isFile());
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }
41
+ async function modelFileInventory(modelDir) {
42
+ try {
43
+ const files = (await readdir(modelDir, { withFileTypes: true }))
44
+ .filter((entry) => entry.isFile())
45
+ .map((entry) => entry.name);
46
+ return {
47
+ xlGgufs: files.filter(isQwen35XlGguf).sort(),
48
+ targetGgufs: files
49
+ .filter((name) => name.toLowerCase().endsWith('.gguf') && !GGUF_COMPANION_NAME.test(name))
50
+ .sort(),
51
+ hasGguf: files.some((name) => name.toLowerCase().endsWith('.gguf')),
52
+ hasSafetensors: files.some((name) => name.toLowerCase().endsWith('.safetensors')),
53
+ hasPrimarySafetensors: files.some((name) => PRIMARY_SAFETENSORS.test(name)),
54
+ };
55
+ }
56
+ catch {
57
+ return { xlGgufs: [], targetGgufs: [], hasGguf: false, hasSafetensors: false, hasPrimarySafetensors: false };
58
+ }
59
+ }
60
+ function positiveInteger(value) {
61
+ return typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : undefined;
62
+ }
63
+ function nonEmptyRecord(value) {
64
+ return typeof value === 'object' && value !== null && !Array.isArray(value) && Object.keys(value).length > 0;
65
+ }
66
+ /**
67
+ * Read cheap discovery metadata from `<modelPath>/config.json`.
68
+ *
69
+ * The trained context window comes from:
70
+ * root `max_position_embeddings` first (qwen3, lfm2), then
71
+ * `text_config.max_position_embeddings` (qwen3_5, qwen3_5_moe, gemma4
72
+ * unified), else the family fallback.
73
+ *
74
+ * Image support is advertised only when a family with a native multimodal
75
+ * implementation carries its valid, non-empty vision marker: `vision_config`
76
+ * for Qwen, and either `vision_config` or `unified_vision_config` for Gemma.
77
+ * This lets Pi's model picker and `--list-models` expose checkpoint capability
78
+ * without loading weights. The first resident load remains authoritative and
79
+ * reconciles this optimistic config-level advertisement via
80
+ * `session.supportsImages()` (for example, when conversion stripped an
81
+ * incompatible vision tower).
82
+ *
83
+ * `detectModelType` already parsed this file, so a read/parse failure here
84
+ * (e.g. a racing rewrite) lands on the context fallback and text-only input
85
+ * instead of dropping the model or guessing a positive capability.
86
+ */
87
+ async function readDiscoveryMetadata(modelPath, modelType, fallbackContextWindow) {
88
+ try {
89
+ const config = (await readModelConfig(modelPath));
90
+ const root = positiveInteger(config.max_position_embeddings);
91
+ const textConfig = config.text_config;
92
+ const nested = nonEmptyRecord(textConfig) ? positiveInteger(textConfig.max_position_embeddings) : undefined;
93
+ const hasVisionConfig = nonEmptyRecord(config.vision_config);
94
+ const supportsImages = modelType === 'gemma4'
95
+ ? hasVisionConfig || nonEmptyRecord(config.unified_vision_config)
96
+ : (modelType === 'qwen3_5' || modelType === 'qwen3_5_moe') && hasVisionConfig;
97
+ const draftOnly = Array.isArray(config.architectures) && config.architectures.includes('DFlash2DraftModel');
98
+ return {
99
+ contextWindow: root ?? nested ?? fallbackContextWindow,
100
+ supportsImages,
101
+ draftOnly,
102
+ };
103
+ }
104
+ catch {
105
+ return { contextWindow: fallbackContextWindow, supportsImages: false, draftOnly: false };
106
+ }
107
+ }
108
+ /**
109
+ * Scan `modelsDir` for chat-capable model subdirectories, Gemma4/Muse GGUFs, and
110
+ * dense Qwen3.5/Qwen3.8 `Q<number>_K_XL.gguf` files. GGUF files may live directly
111
+ * under `modelsDir` or one level inside a downloaded GGUF repository. Each is
112
+ * registered by filename stem so quant variants remain independently selectable.
113
+ *
114
+ * An unreadable dir yields `[]`. Entries with an undetectable config, a
115
+ * non-generative type, or no launch preset are skipped silently (warnings only
116
+ * when `MLX_DEBUG` is set). No weights are loaded. Results are sorted by name.
117
+ */
118
+ export async function discoverLocalChatModels(modelsDir) {
119
+ const debug = Boolean(process.env.MLX_DEBUG);
120
+ let entries;
121
+ try {
122
+ entries = await readdir(modelsDir, { withFileTypes: true });
123
+ }
124
+ catch {
125
+ return [];
126
+ }
127
+ // Collision resolution below gives the first occurrence the bare filename
128
+ // stem. Directory enumeration order is unspecified, so sort before assigning
129
+ // IDs to keep persisted `mlx/<id>` selections stable across filesystems.
130
+ entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
131
+ const out = [];
132
+ const usedNames = new Set();
133
+ const append = async (preferredName, path, metadataRoot, modelType, scopeName) => {
134
+ if (NON_GENERATIVE_FAMILY_IDS.has(modelType))
135
+ return;
136
+ // Fail-closed guards: dead-by-construction for chat families (the
137
+ // family-data row type requires traits + a preset), live for any foreign
138
+ // string that slips through detection.
139
+ const preset = launchPresetFor(modelType);
140
+ if (!preset) {
141
+ if (debug)
142
+ console.warn(`[mlx] skip ${path}: no launch preset for ${modelType}`);
143
+ return;
144
+ }
145
+ const traits = familyTraitsFor(modelType);
146
+ if (!traits) {
147
+ if (debug)
148
+ console.warn(`[mlx] skip ${path}: no FAMILY_TRAITS entry for ${modelType}`);
149
+ return;
150
+ }
151
+ const metadata = await readDiscoveryMetadata(metadataRoot, modelType, traits.fallbackContextWindow);
152
+ if (metadata.draftOnly) {
153
+ if (debug)
154
+ console.warn(`[mlx] skip ${path}: companion draft checkpoint is not a chat model`);
155
+ return;
156
+ }
157
+ let name = preferredName;
158
+ if (usedNames.has(name)) {
159
+ name = `${scopeName}-${preferredName}`;
160
+ let suffix = 2;
161
+ while (usedNames.has(name))
162
+ name = `${scopeName}-${preferredName}-${suffix++}`;
163
+ }
164
+ usedNames.add(name);
165
+ // Automatic companions can be installed or removed after this startup
166
+ // scan. The host resolves them on each load; draftModelPath is reserved
167
+ // for caller-supplied paths that the loader must treat as authoritative.
168
+ out.push({
169
+ name,
170
+ path,
171
+ modelType,
172
+ preset,
173
+ traits,
174
+ contextWindow: metadata.contextWindow,
175
+ supportsImages: metadata.supportsImages,
176
+ });
177
+ };
178
+ for (const entry of entries) {
179
+ if (entry.isFile() && entry.name.toLowerCase().endsWith('.gguf') && !GGUF_COMPANION_NAME.test(entry.name)) {
180
+ const full = join(modelsDir, entry.name);
181
+ try {
182
+ const modelType = await detectModelType(full);
183
+ // A shared sibling config can describe another target or a projector.
184
+ // Never advertise a file under a loader that disagrees with its header.
185
+ if (!matchesGgufFamily(full, modelType))
186
+ continue;
187
+ if (requiresGgufAssets(modelType) && !(await hasGgufAssets(modelsDir))) {
188
+ if (debug)
189
+ console.warn(`[mlx] skip ${full}: native ${modelType} GGUF requires sibling config.json and tokenizer.json`);
190
+ continue;
191
+ }
192
+ if (modelType === 'gemma4' ||
193
+ modelType === 'muse_glimmer' ||
194
+ (modelType === 'qwen3_5' && isQwen35XlGguf(entry.name))) {
195
+ await append(ggufModelName(entry.name), full, modelsDir, modelType, basename(modelsDir));
196
+ }
197
+ else if (debug) {
198
+ console.warn(`[mlx] skip ${full}: no supported direct GGUF target for ${modelType}`);
199
+ }
200
+ }
201
+ catch (err) {
202
+ if (debug)
203
+ console.warn(`[mlx] skip ${full}: ${err.message}`);
204
+ }
205
+ continue;
206
+ }
207
+ if (!entry.isDirectory())
208
+ continue;
209
+ const full = join(modelsDir, entry.name);
210
+ let modelType;
211
+ try {
212
+ modelType = await detectModelType(full);
213
+ }
214
+ catch (err) {
215
+ if (debug)
216
+ console.warn(`[mlx] skip ${full}: ${err.message}`);
217
+ continue;
218
+ }
219
+ const inventory = await modelFileInventory(full);
220
+ const hasModelWeights = requiresGgufAssets(modelType) ? inventory.hasPrimarySafetensors : inventory.hasSafetensors;
221
+ if (requiresGgufAssets(modelType) && !hasModelWeights && inventory.targetGgufs.length > 0) {
222
+ if (!(await hasGgufAssets(full))) {
223
+ if (debug)
224
+ console.warn(`[mlx] skip ${full}: native ${modelType} GGUF requires sibling config.json and tokenizer.json`);
225
+ continue;
226
+ }
227
+ for (const gguf of inventory.targetGgufs) {
228
+ const path = join(full, gguf);
229
+ try {
230
+ if (!matchesGgufFamily(path, modelType))
231
+ continue;
232
+ await append(ggufModelName(gguf), path, full, modelType, entry.name);
233
+ }
234
+ catch (err) {
235
+ if (debug)
236
+ console.warn(`[mlx] skip ${path}: ${err.message}`);
237
+ }
238
+ }
239
+ continue;
240
+ }
241
+ const { xlGgufs } = inventory;
242
+ if (xlGgufs.length > 0 && !inventory.hasPrimarySafetensors) {
243
+ if (modelType !== 'qwen3_5') {
244
+ if (debug) {
245
+ console.warn(`[mlx] skip ${full}: direct XL GGUF loading is not supported for ${modelType}`);
246
+ }
247
+ continue;
248
+ }
249
+ for (const gguf of xlGgufs) {
250
+ const path = join(full, gguf);
251
+ try {
252
+ if (matchesGgufFamily(path, modelType))
253
+ await append(ggufModelName(gguf), path, full, modelType, entry.name);
254
+ }
255
+ catch (err) {
256
+ if (debug)
257
+ console.warn(`[mlx] skip ${path}: ${err.message}`);
258
+ }
259
+ }
260
+ continue;
261
+ }
262
+ // Present each supported GGUF variant separately in the picker. Keep
263
+ // converted model directories discoverable when they retain
264
+ // an imatrix/source GGUF beside their actual SafeTensors weights.
265
+ if (inventory.hasGguf && !hasModelWeights) {
266
+ if (debug)
267
+ console.warn(`[mlx] skip ${full}: no supported direct GGUF target`);
268
+ continue;
269
+ }
270
+ await append(basename(full), full, full, modelType, entry.name);
271
+ }
272
+ out.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
273
+ return out;
274
+ }
@@ -7,6 +7,8 @@ import { ChatSession, type SessionCapableModel } from '../chat-session.js';
7
7
  import { type ModelType, type TrainableFamilyId } from '../family-data.js';
8
8
  /** Optional settings for {@link loadModel} / {@link loadSession}. */
9
9
  export interface LoadModelOptions {
10
+ /** Discover a Qwen DFlash2 companion on disk (default true). Explicit draftModelPath wins. */
11
+ autoLoadDraft?: boolean;
10
12
  /**
11
13
  * Directory of an external draft checkpoint (config.json +
12
14
  * model.safetensors) loaded alongside the target for speculative decoding.
@@ -125,6 +127,8 @@ export type TrainableModel = Awaited<ReturnType<LoaderBindings[TrainableFamilyId
125
127
  * rejects it.
126
128
  * Without the option, Gemma4 loads `<modelPath>/draft/` automatically when
127
129
  * that embedded checkpoint is present.
130
+ * Qwen also discovers `draft/` or a shared `qwen3.8-27b-dflash2` directory
131
+ * beside a Qwen3.8-27B target. Set `autoLoadDraft: false` to disable discovery.
128
132
  */
129
133
  export declare function loadModel(modelPath: string, options?: LoadModelOptions): Promise<LoadableModel>;
130
134
  /**
@@ -148,6 +152,8 @@ export declare function loadModel(modelPath: string, options?: LoadModelOptions)
148
152
  * rejects it.
149
153
  * Without the option, Gemma4 loads `<modelPath>/draft/` automatically when
150
154
  * that embedded checkpoint is present.
155
+ * Qwen uses the same companion discovery as `loadModel()` unless
156
+ * `autoLoadDraft: false` is passed.
151
157
  * The resulting session auto-enables the speculative path when the model
152
158
  * reports `hasMtpWeights()` AND does not opt out of the auto-default; pass
153
159
  * `enableMtp: false` per call to suppress it, or `enableMtp: true` to force
@@ -1 +1 @@
1
- {"version":3,"file":"model-loader.d.ts","sourceRoot":"","sources":["../../src/models/model-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EACL,WAAW,IAAI,iBAAiB,EAEhC,YAAY,EACZ,SAAS,IAAI,eAAe,EAC5B,gBAAgB,IAAI,sBAAsB,EAC1C,cAAc,IAAI,oBAAoB,EACtC,eAAe,EACf,UAAU,IAAI,gBAAgB,EAC9B,WAAW,IAAI,iBAAiB,EAChC,cAAc,IAAI,oBAAoB,EACvC,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAML,KAAK,SAAS,EACd,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAW3B,qEAAqE;AACrE,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;;;;;;;;;OAiBG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAmBD;;;;;;GAMG;AACH,QAAA,MAAM,eAAe;;iBAEjB,IAAI,cAAc,MAAM,YAAY,gBAAgB;;;iBAKpD,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;;;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;;;;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM,YAAY,gBAAgB;;;iBAKpD,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;;;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;;;;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;;;;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;;;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;iBACxB,gBAAgB;;CAEiC,CAAC;AAEtD,KAAK,cAAc,GAAG,OAAO,eAAe,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAExF;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAC9D,YAAY,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAsCxE;;;;;;;;;;;GAWG;AACH,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,CAGrG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC,CAa3C;AAED,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAwB3E"}
1
+ {"version":3,"file":"model-loader.d.ts","sourceRoot":"","sources":["../../src/models/model-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,WAAW,IAAI,iBAAiB,EAEhC,YAAY,EACZ,SAAS,IAAI,eAAe,EAC5B,gBAAgB,IAAI,sBAAsB,EAC1C,cAAc,IAAI,oBAAoB,EACtC,eAAe,EACf,UAAU,IAAI,gBAAgB,EAC9B,WAAW,IAAI,iBAAiB,EAChC,cAAc,IAAI,oBAAoB,EACvC,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE3E,OAAO,EAAiB,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAY1F,qEAAqE;AACrE,MAAM,WAAW,gBAAgB;IAC/B,8FAA8F;IAC9F,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;;;;;;;;;;OAiBG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAmBD;;;;;;GAMG;AACH,QAAA,MAAM,eAAe;;iBAEjB,IAAI,cAAc,MAAM,YAAY,gBAAgB;;;iBAKpD,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;;;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;;;;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM,YAAY,gBAAgB;;;iBAKpD,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;;;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;;;;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;;;;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;;;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;iBACxB,gBAAgB;;;iBAGhB,IAAI,cAAc,MAAM;iBACxB,gBAAgB;;CAEiC,CAAC;AAEtD,KAAK,cAAc,GAAG,OAAO,eAAe,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAExF;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAC9D,YAAY,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAgCxE;;;;;;;;;;;;;GAaG;AACH,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,CAGrG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC,CAa3C;AAED,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAE3E"}
@@ -2,11 +2,11 @@
2
2
  * Native half of the family registry: one loader binding per
3
3
  * `MODEL_FAMILY_DATA` row, plus `detectModelType` (filesystem + GGUF).
4
4
  */
5
- import { readFile } from 'node:fs/promises';
6
- import { dirname, extname, join } from 'node:path';
7
5
  import { Gemma4Model as NativeGemma4Model, ggufArchitecture, HarrierModel, Lfm2Model as NativeLfm2Model, MuseGlimmerModel as NativeMuseGlimmerModel, NemotronHModel as NativeNemotronHModel, QianfanOCRModel, Qwen3Model as NativeQwen3Model, Qwen35Model as NativeQwen35Model, Qwen35MoeModel as NativeQwen35MoeModel, } from '@mlx-node/core';
8
6
  import { ChatSession } from '../chat-session.js';
9
- import { familyDataFor, MalformedModelConfigError, matchFamily, MODEL_FAMILY_DATA, UnsupportedModelTypeError, } from '../family-data.js';
7
+ import { findDFlash2Draft } from '../draft-companion.js';
8
+ import { familyDataFor } from '../family-data.js';
9
+ import { detectModelType as detectLocalModelType } from '../model-detection.js';
10
10
  import { Gemma4Model, Lfm2Model, MuseGlimmerModel, NemotronHModel, Qwen3Model, Qwen35Model, Qwen35MoeModel, } from '../stream.js';
11
11
  /**
12
12
  * Native half of the family registry: one loader + native class per
@@ -61,9 +61,6 @@ const LOADER_BINDINGS = {
61
61
  nativeModelClass: QianfanOCRModel,
62
62
  },
63
63
  };
64
- // Only families whose native `load(path)` accepts a GGUF file carry a
65
- // `ggufArchitectures` row entry (see family-data.ts).
66
- const GGUF_ARCHITECTURE_MODEL_TYPES = new Map(MODEL_FAMILY_DATA.flatMap((row) => 'ggufArchitectures' in row ? row.ggufArchitectures.map((architecture) => [architecture, row.id]) : []));
67
64
  function requireFamilyData(modelType) {
68
65
  const family = familyDataFor(modelType);
69
66
  if (family === undefined) {
@@ -82,7 +79,8 @@ function dispatchLoad(modelType, modelPath, options) {
82
79
  `${modelPath} has model_type "${modelType}"`);
83
80
  }
84
81
  const binding = LOADER_BINDINGS[modelType];
85
- return binding.load(modelPath, options);
82
+ const draftModelPath = options?.draftModelPath ?? (options?.autoLoadDraft === false ? undefined : findDFlash2Draft(modelPath, modelType));
83
+ return binding.load(modelPath, draftModelPath === undefined ? options : { ...options, draftModelPath });
86
84
  }
87
85
  /**
88
86
  * Load a model from disk, auto-detecting architecture from config.json.
@@ -95,6 +93,8 @@ function dispatchLoad(modelType, modelPath, options) {
95
93
  * rejects it.
96
94
  * Without the option, Gemma4 loads `<modelPath>/draft/` automatically when
97
95
  * that embedded checkpoint is present.
96
+ * Qwen also discovers `draft/` or a shared `qwen3.8-27b-dflash2` directory
97
+ * beside a Qwen3.8-27B target. Set `autoLoadDraft: false` to disable discovery.
98
98
  */
99
99
  export async function loadModel(modelPath, options) {
100
100
  const modelType = await detectModelType(modelPath);
@@ -121,6 +121,8 @@ export async function loadModel(modelPath, options) {
121
121
  * rejects it.
122
122
  * Without the option, Gemma4 loads `<modelPath>/draft/` automatically when
123
123
  * that embedded checkpoint is present.
124
+ * Qwen uses the same companion discovery as `loadModel()` unless
125
+ * `autoLoadDraft: false` is passed.
124
126
  * The resulting session auto-enables the speculative path when the model
125
127
  * reports `hasMtpWeights()` AND does not opt out of the auto-default; pass
126
128
  * `enableMtp: false` per call to suppress it, or `enableMtp: true` to force
@@ -141,29 +143,5 @@ export async function loadSession(modelPath, options) {
141
143
  return new ChatSession(m);
142
144
  }
143
145
  export async function detectModelType(modelPath) {
144
- const isGguf = extname(modelPath).toLowerCase() === '.gguf';
145
- const configPath = isGguf ? join(dirname(modelPath), 'config.json') : join(modelPath, 'config.json');
146
- let raw;
147
- try {
148
- raw = await readFile(configPath, 'utf-8');
149
- }
150
- catch (e) {
151
- if (isGguf && typeof e === 'object' && e !== null && 'code' in e && e.code === 'ENOENT') {
152
- const architecture = ggufArchitecture(modelPath);
153
- const modelType = GGUF_ARCHITECTURE_MODEL_TYPES.get(architecture);
154
- if (modelType === undefined) {
155
- throw new Error(`Unsupported GGUF architecture "${architecture}" in ${modelPath}`);
156
- }
157
- return modelType;
158
- }
159
- throw new Error(`Cannot detect model type: config.json not found in ${modelPath}`);
160
- }
161
- try {
162
- return matchFamily(modelPath, JSON.parse(raw));
163
- }
164
- catch (e) {
165
- if (e instanceof UnsupportedModelTypeError || e instanceof MalformedModelConfigError)
166
- throw e;
167
- throw new Error(`Cannot detect model type: config.json not found in ${modelPath}`);
168
- }
146
+ return detectLocalModelType(modelPath, ggufArchitecture);
169
147
  }
@@ -1 +1 @@
1
- {"version":3,"file":"paged-config-override.d.ts","sourceRoot":"","sources":["../../src/models/paged-config-override.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,iEAAiE;AACjE,eAAO,MAAM,wBAAwB,YAAI,SAAS,EAAE,aAAa,CAAU,CAAC;AAQ5E,MAAM,WAAW,iCAAiC;IAChD,qFAAqF;IACrF,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,0EAA0E;IAC1E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC;AAED;;;;;;;GAOG;AACH,qBAAa,0BAA0B;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAU;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;IAChE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA8B;IAC7D,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,QAAQ,CAAS;IAEzB,YAAY,OAAO,GAAE,iCAAsC,EAI1D;IAED;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAY1G;YAEa,eAAe;IAgF7B,6EAA6E;IAC7E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAKvB;YAEa,cAAc;YAad,cAAc;IA+D5B,OAAO,CAAC,OAAO;CAIhB"}
1
+ {"version":3,"file":"paged-config-override.d.ts","sourceRoot":"","sources":["../../src/models/paged-config-override.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,iEAAiE;AACjE,eAAO,MAAM,wBAAwB,YAAI,SAAS,EAAE,aAAa,CAAU,CAAC;AAQ5E,MAAM,WAAW,iCAAiC;IAChD,qFAAqF;IACrF,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,0EAA0E;IAC1E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC;AAED;;;;;;;GAOG;AACH,qBAAa,0BAA0B;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAU;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;IAChE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA8B;IAC7D,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,QAAQ,CAAS;IAEzB,YAAY,OAAO,GAAE,iCAAsC,EAI1D;IAED;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAY1G;YAEa,eAAe;IAgG7B,6EAA6E;IAC7E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAKvB;YAEa,cAAc;YAad,cAAc;IA+D5B,OAAO,CAAC,OAAO;CAIhB"}
@@ -11,7 +11,8 @@
11
11
  */
12
12
  import { mkdir, mkdtemp, readFile, readdir, rm, stat, symlink, writeFile } from 'node:fs/promises';
13
13
  import { tmpdir } from 'node:os';
14
- import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
14
+ import { dirname, extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
15
+ import { ggufArchitecture, prepareMuseGlimmerGguf } from '@mlx-node/core';
15
16
  import { CHAT_FAMILY_IDS } from '../family-data.js';
16
17
  /** Families historically forced paged by `mlx launch claude`. */
17
18
  export const QWEN35_PAGED_MODEL_TYPES = ['qwen3_5', 'qwen3_5_moe'];
@@ -75,6 +76,25 @@ export class PagedConfigOverrideManager {
75
76
  }
76
77
  async resolveInternal(modelPath, canonicalModelType, persistPagedCache) {
77
78
  const sourcePath = isAbsolute(modelPath) ? modelPath : resolve(modelPath);
79
+ if (extname(sourcePath).toLowerCase() === '.gguf') {
80
+ let modelType = canonicalModelType;
81
+ if (modelType === undefined) {
82
+ try {
83
+ if (ggufArchitecture(sourcePath) === 'muse-glimmer')
84
+ modelType = 'muse_glimmer';
85
+ }
86
+ catch {
87
+ return modelPath;
88
+ }
89
+ }
90
+ if (modelType !== 'muse_glimmer' || !this.modelTypes.has(modelType))
91
+ return modelPath;
92
+ // A symlink to the GGUF would canonicalize back to the original config.
93
+ // Prepare once, then overlay the packed cache so paging/persistence
94
+ // directives reach the loader and the DFlash sidecar stays attached.
95
+ const prepared = await prepareMuseGlimmerGguf(sourcePath);
96
+ return this.resolveInternal(prepared, modelType, persistPagedCache);
97
+ }
78
98
  let config;
79
99
  try {
80
100
  config = JSON.parse(await readFile(join(sourcePath, 'config.json'), 'utf-8'));
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}
package/dist/stream.js CHANGED
@@ -5,7 +5,7 @@ const tokenizerPromises = new WeakMap();
5
5
  function getNativeIsReasoning(chunk) {
6
6
  return typeof chunk.isReasoning === "boolean" ? chunk.isReasoning : undefined;
7
7
  }
8
- async function applyChatTemplateFromModelPath(model, messages, addGenerationPrompt, tools, enableThinking, contentPolicy) {
8
+ async function applyChatTemplateFromModelPath(model, messages, addGenerationPrompt, tools, enableThinking, contentPolicy, reasoningEffort) {
9
9
  const modelPath = modelPathsForTokenizers.get(model);
10
10
  if (modelPath == null) {
11
11
  throw new Error("applyChatTemplate unavailable: model path was not recorded when this model was loaded");
@@ -17,9 +17,9 @@ async function applyChatTemplateFromModelPath(model, messages, addGenerationProm
17
17
  }
18
18
  const tokenizer = await tokenizerPromise;
19
19
  if (contentPolicy == null) {
20
- return tokenizer.applyChatTemplate(messages, addGenerationPrompt, tools, enableThinking);
20
+ return tokenizer.applyChatTemplate(messages, addGenerationPrompt, tools, enableThinking, undefined, undefined, reasoningEffort);
21
21
  }
22
- return tokenizer.applyChatTemplate(messages, addGenerationPrompt, tools, enableThinking, contentPolicy.order, contentPolicy.existingImagePlaceholder);
22
+ return tokenizer.applyChatTemplate(messages, addGenerationPrompt, tools, enableThinking, contentPolicy.order, contentPolicy.existingImagePlaceholder, reasoningEffort);
23
23
  }
24
24
  /**
25
25
  * Shared AsyncGenerator adapter for callback-based native streaming methods.
@@ -368,8 +368,8 @@ export function makeStreamingModel(NativeClass, opts) {
368
368
  Object.defineProperty(StreamingModelImpl.prototype, "applyChatTemplate", {
369
369
  configurable: true,
370
370
  writable: true,
371
- value(messages, addGenerationPrompt, tools, enableThinking) {
372
- return applyChatTemplateFromModelPath(this, messages, addGenerationPrompt, tools, enableThinking, templateContentPolicy);
371
+ value(messages, addGenerationPrompt, tools, enableThinking, reasoningEffort) {
372
+ return applyChatTemplateFromModelPath(this, messages, addGenerationPrompt, tools, enableThinking, templateContentPolicy, reasoningEffort);
373
373
  },
374
374
  });
375
375
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mlx-node/lm",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "homepage": "https://github.com/mlx-node/mlx-node",
5
5
  "bugs": {
6
6
  "url": "https://github.com/mlx-node/mlx-node/issues"
@@ -12,19 +12,37 @@
12
12
  "directory": "packages/lm"
13
13
  },
14
14
  "files": [
15
- "dist"
15
+ "dist",
16
+ "src"
16
17
  ],
17
18
  "type": "module",
18
19
  "main": "./dist/index.js",
19
20
  "types": "./dist/index.d.ts",
20
21
  "exports": {
21
22
  ".": {
23
+ "@mlx-node/source": "./src/index.ts",
22
24
  "types": "./dist/index.d.ts",
23
25
  "import": "./dist/index.js"
24
26
  },
27
+ "./draft-companion": {
28
+ "@mlx-node/source": "./src/draft-companion.ts",
29
+ "types": "./dist/draft-companion.d.ts",
30
+ "import": "./dist/draft-companion.js"
31
+ },
25
32
  "./family-data": {
33
+ "@mlx-node/source": "./src/family-data.ts",
26
34
  "types": "./dist/family-data.d.ts",
27
35
  "import": "./dist/family-data.js"
36
+ },
37
+ "./model-detection": {
38
+ "@mlx-node/source": "./src/model-detection.ts",
39
+ "types": "./dist/model-detection.d.ts",
40
+ "import": "./dist/model-detection.js"
41
+ },
42
+ "./model-discovery": {
43
+ "@mlx-node/source": "./src/model-discovery.ts",
44
+ "types": "./dist/model-discovery.d.ts",
45
+ "import": "./dist/model-discovery.js"
28
46
  }
29
47
  },
30
48
  "scripts": {
@@ -32,7 +50,7 @@
32
50
  "test": "vite test run"
33
51
  },
34
52
  "dependencies": {
35
- "@mlx-node/core": "0.0.13"
53
+ "@mlx-node/core": "0.0.15"
36
54
  },
37
55
  "devDependencies": {
38
56
  "@types/node": "^26.4.0"