@mlx-node/lm 0.0.12 → 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.
- package/dist/chat-session.d.ts +1 -1
- package/dist/chat-session.d.ts.map +1 -1
- package/dist/chat-session.js +2 -2
- package/dist/draft-companion.d.ts +16 -0
- package/dist/draft-companion.d.ts.map +1 -0
- package/dist/draft-companion.js +76 -0
- package/dist/family-data.d.ts +2 -0
- package/dist/family-data.d.ts.map +1 -1
- package/dist/family-data.js +2 -0
- package/dist/gguf-metadata.d.ts +2 -0
- package/dist/gguf-metadata.d.ts.map +1 -0
- package/dist/gguf-metadata.js +128 -0
- package/dist/model-detection.d.ts +6 -0
- package/dist/model-detection.d.ts.map +1 -0
- package/dist/model-detection.js +38 -0
- package/dist/model-discovery.d.ts +24 -0
- package/dist/model-discovery.d.ts.map +1 -0
- package/dist/model-discovery.js +274 -0
- package/dist/models/model-loader.d.ts +6 -0
- package/dist/models/model-loader.d.ts.map +1 -1
- package/dist/models/model-loader.js +10 -32
- package/dist/models/paged-config-override.d.ts.map +1 -1
- package/dist/models/paged-config-override.js +21 -1
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +5 -5
- package/package.json +21 -3
- package/src/chat-session.ts +2369 -0
- package/src/draft-companion.ts +74 -0
- package/src/family-data.ts +542 -0
- package/src/gguf-metadata.ts +117 -0
- package/src/index.ts +151 -0
- package/src/model-detection.ts +46 -0
- package/src/model-discovery.ts +329 -0
- package/src/models/lfm2-configs.ts +110 -0
- package/src/models/model-loader.ts +256 -0
- package/src/models/paged-config-override.ts +387 -0
- package/src/models/qwen3-configs.ts +113 -0
- package/src/models/qwen3_5-configs.ts +60 -0
- package/src/profiling.ts +69 -0
- package/src/stream.ts +960 -0
- package/src/tools/index.ts +58 -0
- 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;
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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'));
|
package/dist/stream.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,IAAI,iBAAiB,EAChC,SAAS,IAAI,eAAe,EAC5B,gBAAgB,IAAI,sBAAsB,EAC1C,cAAc,IAAI,oBAAoB,EAEtC,UAAU,IAAI,gBAAgB,EAC9B,WAAW,IAAI,iBAAiB,EAChC,cAAc,IAAI,oBAAoB,EACvC,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAIV,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAElB,cAAc,EACf,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAkC7D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,KAAK,CAAC;IACZ,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,2EAA2E;IAC3E,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,eAAe,CAAC;AAKhE,KAAK,oBAAoB,GAAG,eAAe,GAAG,gBAAgB,CAAC;AAE/D,UAAU,qBAAqB;IAC7B,KAAK,EAAE,oBAAoB,CAAC;IAC5B;;;OAGG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;
|
|
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.
|
|
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.
|
|
53
|
+
"@mlx-node/core": "0.0.15"
|
|
36
54
|
},
|
|
37
55
|
"devDependencies": {
|
|
38
56
|
"@types/node": "^26.4.0"
|