@mlx-node/lm 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/dist/chat-session.d.ts +340 -95
- package/dist/chat-session.d.ts.map +1 -1
- package/dist/chat-session.js +704 -157
- package/dist/index.d.ts +12 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -7
- package/dist/interfaces.d.ts +2 -17
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/models/lfm2-configs.d.ts.map +1 -1
- package/dist/models/lfm2-configs.js +59 -0
- package/dist/models/model-loader.d.ts +173 -4
- package/dist/models/model-loader.d.ts.map +1 -1
- package/dist/models/model-loader.js +226 -49
- package/dist/models/paged-config-override.d.ts +60 -0
- package/dist/models/paged-config-override.d.ts.map +1 -0
- package/dist/models/paged-config-override.js +254 -0
- package/dist/models/qwen3_5-configs.d.ts.map +1 -1
- package/dist/models/qwen3_5-configs.js +5 -0
- package/dist/stream.d.ts +181 -104
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +160 -227
- package/package.json +3 -3
package/dist/stream.js
CHANGED
|
@@ -1,48 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const _nativeMoeChatStreamSessionContinueTool = Qwen35MoeModelNative.prototype.chatStreamSessionContinueTool;
|
|
25
|
-
// LFM2
|
|
26
|
-
// oxlint-disable-next-line @typescript-eslint/unbound-method
|
|
27
|
-
const _nativeLfm2ChatStreamSessionStart = Lfm2ModelNative.prototype.chatStreamSessionStart;
|
|
28
|
-
// oxlint-disable-next-line @typescript-eslint/unbound-method
|
|
29
|
-
const _nativeLfm2ChatStreamSessionContinue = Lfm2ModelNative.prototype.chatStreamSessionContinue;
|
|
30
|
-
// oxlint-disable-next-line @typescript-eslint/unbound-method
|
|
31
|
-
const _nativeLfm2ChatStreamSessionContinueTool = Lfm2ModelNative.prototype.chatStreamSessionContinueTool;
|
|
32
|
-
// Gemma4
|
|
33
|
-
// oxlint-disable-next-line @typescript-eslint/unbound-method
|
|
34
|
-
const _nativeGemma4ChatStreamSessionStart = Gemma4ModelNative.prototype.chatStreamSessionStart;
|
|
35
|
-
// oxlint-disable-next-line @typescript-eslint/unbound-method
|
|
36
|
-
const _nativeGemma4ChatStreamSessionContinue = Gemma4ModelNative.prototype.chatStreamSessionContinue;
|
|
37
|
-
// oxlint-disable-next-line @typescript-eslint/unbound-method
|
|
38
|
-
const _nativeGemma4ChatStreamSessionContinueTool = Gemma4ModelNative.prototype.chatStreamSessionContinueTool;
|
|
39
|
-
// Qwen3 (legacy, text-only)
|
|
40
|
-
// oxlint-disable-next-line @typescript-eslint/unbound-method
|
|
41
|
-
const _nativeQwen3ChatStreamSessionStart = Qwen3ModelNative.prototype.chatStreamSessionStart;
|
|
42
|
-
// oxlint-disable-next-line @typescript-eslint/unbound-method
|
|
43
|
-
const _nativeQwen3ChatStreamSessionContinue = Qwen3ModelNative.prototype.chatStreamSessionContinue;
|
|
44
|
-
// oxlint-disable-next-line @typescript-eslint/unbound-method
|
|
45
|
-
const _nativeQwen3ChatStreamSessionContinueTool = Qwen3ModelNative.prototype.chatStreamSessionContinueTool;
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { Gemma4Model as Gemma4ModelNative, Lfm2Model as Lfm2ModelNative, Qwen3Tokenizer, Qwen3Model as Qwen3ModelNative, Qwen35Model as Qwen35ModelNative, Qwen35MoeModel as Qwen35MoeModelNative, } from '@mlx-node/core';
|
|
3
|
+
const modelPathsForTokenizers = new WeakMap();
|
|
4
|
+
const tokenizerPromises = new WeakMap();
|
|
5
|
+
function getNativeIsReasoning(chunk) {
|
|
6
|
+
return typeof chunk.isReasoning === 'boolean' ? chunk.isReasoning : undefined;
|
|
7
|
+
}
|
|
8
|
+
function rememberModelPath(model, modelPath) {
|
|
9
|
+
modelPathsForTokenizers.set(model, modelPath);
|
|
10
|
+
}
|
|
11
|
+
async function applyChatTemplateFromModelPath(model, messages, addGenerationPrompt, tools, enableThinking) {
|
|
12
|
+
const modelPath = modelPathsForTokenizers.get(model);
|
|
13
|
+
if (modelPath == null) {
|
|
14
|
+
throw new Error('applyChatTemplate unavailable: model path was not recorded when this model was loaded');
|
|
15
|
+
}
|
|
16
|
+
let tokenizerPromise = tokenizerPromises.get(model);
|
|
17
|
+
if (tokenizerPromise == null) {
|
|
18
|
+
tokenizerPromise = Qwen3Tokenizer.fromPretrained(join(modelPath, 'tokenizer.json'));
|
|
19
|
+
tokenizerPromises.set(model, tokenizerPromise);
|
|
20
|
+
}
|
|
21
|
+
const tokenizer = await tokenizerPromise;
|
|
22
|
+
return tokenizer.applyChatTemplate(messages, addGenerationPrompt, tools, enableThinking);
|
|
23
|
+
}
|
|
46
24
|
/**
|
|
47
25
|
* Shared AsyncGenerator adapter for callback-based native streaming methods.
|
|
48
26
|
*
|
|
@@ -171,7 +149,16 @@ export async function* _runChatStream(startCall, signal) {
|
|
|
171
149
|
throw item.error;
|
|
172
150
|
const chunk = item.chunk;
|
|
173
151
|
if (chunk.done) {
|
|
174
|
-
|
|
152
|
+
// The native `ChatStreamChunk` carries `cachedTokens` on the
|
|
153
|
+
// terminal (`done == true`) chunk for every streaming entry
|
|
154
|
+
// point. Emit it on the final event verbatim — undefined means
|
|
155
|
+
// the native dispatch did not populate it (e.g. a bridge-level
|
|
156
|
+
// mock or an in-process driver), in which case downstream
|
|
157
|
+
// consumers treat the absence as "unknown / not plumbed" and
|
|
158
|
+
// skip emitting e.g. `X-Cached-Tokens` rather than reporting a
|
|
159
|
+
// fabricated `0`.
|
|
160
|
+
const chunkWithCached = chunk;
|
|
161
|
+
const finalEvent = {
|
|
175
162
|
text: chunk.text,
|
|
176
163
|
done: true,
|
|
177
164
|
finishReason: chunk.finishReason,
|
|
@@ -183,9 +170,18 @@ export async function* _runChatStream(startCall, signal) {
|
|
|
183
170
|
rawText: chunk.rawText,
|
|
184
171
|
performance: chunk.performance ?? undefined,
|
|
185
172
|
};
|
|
173
|
+
if (typeof chunkWithCached.cachedTokens === 'number') {
|
|
174
|
+
finalEvent.cachedTokens = chunkWithCached.cachedTokens;
|
|
175
|
+
}
|
|
176
|
+
yield finalEvent;
|
|
186
177
|
return;
|
|
187
178
|
}
|
|
188
|
-
|
|
179
|
+
const delta = { text: chunk.text, done: false };
|
|
180
|
+
const isReasoning = getNativeIsReasoning(chunk);
|
|
181
|
+
if (isReasoning !== undefined) {
|
|
182
|
+
delta.isReasoning = isReasoning;
|
|
183
|
+
}
|
|
184
|
+
yield delta;
|
|
189
185
|
}
|
|
190
186
|
}
|
|
191
187
|
}
|
|
@@ -203,205 +199,116 @@ export async function* _runChatStream(startCall, signal) {
|
|
|
203
199
|
}
|
|
204
200
|
}
|
|
205
201
|
/**
|
|
206
|
-
*
|
|
202
|
+
* Build the streaming-model subclass for a native chat model class.
|
|
207
203
|
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
* `
|
|
212
|
-
*
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return instance;
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Streaming variant of {@link Qwen35Model#chatSessionStart}.
|
|
222
|
-
*
|
|
223
|
-
* Resets the KV caches, runs the jinja chat template, prefills on
|
|
224
|
-
* top of the fresh caches, and streams the decoded reply token-by-
|
|
225
|
-
* token. Stops on `<|im_end|>` so the cached history ends on a
|
|
226
|
-
* clean ChatML boundary that subsequent `chatStreamSessionContinue`
|
|
227
|
-
* deltas can append to. Text-only.
|
|
228
|
-
*
|
|
229
|
-
* The optional `signal` parameter wires an AbortSignal into the
|
|
230
|
-
* `_runChatStream` adapter's fast-abort path. Callers that need
|
|
231
|
-
* client-disconnect-aware cancellation (e.g. HTTP endpoints) pass
|
|
232
|
-
* one here and the native decode winds down at the next safepoint.
|
|
233
|
-
*/
|
|
234
|
-
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
235
|
-
async *chatStreamSessionStart(messages, config, signal) {
|
|
236
|
-
yield* _runChatStream((callback) => _nativeDenseChatStreamSessionStart.call(this, messages, config ?? null, callback), signal);
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Streaming variant of {@link Qwen35Model#chatSessionContinue}.
|
|
240
|
-
*
|
|
241
|
-
* Builds a raw ChatML delta on top of the live session caches,
|
|
242
|
-
* tokenizes it, prefills the delta, and streams the decoded reply.
|
|
243
|
-
* Requires a live session started via `chatSessionStart` or
|
|
244
|
-
* `chatStreamSessionStart`. Stops on `<|im_end|>`.
|
|
245
|
-
*
|
|
246
|
-
* `images` is the native opt-in guard parameter — callers that
|
|
247
|
-
* attach a new image set must restart the session via
|
|
248
|
-
* `chatStreamSessionStart` with the full history. The high-level
|
|
249
|
-
* `ChatSession` wrapper handles that routing; callers that drive
|
|
250
|
-
* the wrapper directly should pass `null` for text-only continues.
|
|
251
|
-
*/
|
|
252
|
-
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
253
|
-
async *chatStreamSessionContinue(userMessage, images, config, signal) {
|
|
254
|
-
yield* _runChatStream((callback) => _nativeDenseChatStreamSessionContinue.call(this, userMessage, images, config ?? null, callback), signal);
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Streaming variant of {@link Qwen35Model#chatSessionContinueTool}.
|
|
258
|
-
*
|
|
259
|
-
* Builds a ChatML `<tool_response>` delta on top of the live
|
|
260
|
-
* session caches and streams the decoded assistant reply. Requires
|
|
261
|
-
* a live session started via `chatSessionStart` /
|
|
262
|
-
* `chatStreamSessionStart`.
|
|
263
|
-
*/
|
|
264
|
-
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
265
|
-
async *chatStreamSessionContinueTool(toolCallId, content, config, signal) {
|
|
266
|
-
yield* _runChatStream((callback) => _nativeDenseChatStreamSessionContinueTool.call(this, toolCallId, content, config ?? null, callback), signal);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Qwen3.5 MoE model wrapper.
|
|
204
|
+
* The returned class:
|
|
205
|
+
* - captures the three native callback-based session-streaming methods
|
|
206
|
+
* from `NativeClass.prototype`,
|
|
207
|
+
* - overrides them as `async *` generators delegating to
|
|
208
|
+
* {@link _runChatStream} with identical argument plumbing (including
|
|
209
|
+
* `config ?? null`, `images`, `isError ?? null`, and the `signal`),
|
|
210
|
+
* - overrides `static load` to re-prototype the native instance onto
|
|
211
|
+
* the concrete subclass (`this`) and optionally record the path,
|
|
212
|
+
* - installs a path-backed `applyChatTemplate` when `opts.applyTemplate`
|
|
213
|
+
* (defaulting to `opts.recordModelPath`).
|
|
271
214
|
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* `AsyncGenerator<ChatStreamEvent>` so the wrapper structurally
|
|
275
|
-
* satisfies `SessionCapableModel`.
|
|
215
|
+
* @internal Exported so the VLM wrapper (`@mlx-node/vlm`) builds its
|
|
216
|
+
* `QianfanOCRModel` from the same factory. Not part of the public API.
|
|
276
217
|
*/
|
|
277
|
-
export
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
218
|
+
export function makeStreamingModel(NativeClass, opts) {
|
|
219
|
+
const recordPath = opts.recordModelPath;
|
|
220
|
+
const applyTemplate = opts.applyTemplate ?? recordPath;
|
|
221
|
+
// Capture the native callback-based methods before the subclass
|
|
222
|
+
// overrides below shadow them on the prototype.
|
|
223
|
+
const nativeStart = NativeClass.prototype.chatStreamSessionStart;
|
|
224
|
+
const nativeContinue = NativeClass.prototype.chatStreamSessionContinue;
|
|
225
|
+
const nativeContinueTool = NativeClass.prototype.chatStreamSessionContinueTool;
|
|
226
|
+
// `NativeClass` is structurally a constructor; cast to a concrete
|
|
227
|
+
// constructor type so `class extends` accepts it. Runtime behavior is
|
|
228
|
+
// unchanged — we extend the real native class.
|
|
229
|
+
const Base = NativeClass;
|
|
230
|
+
class StreamingModelImpl extends Base {
|
|
231
|
+
static async load(modelPath, ...rest) {
|
|
232
|
+
// Forward any trailing family-specific load options verbatim (e.g.
|
|
233
|
+
// Gemma4's `Gemma4LoadOptions` with `draftModelPath`); families whose
|
|
234
|
+
// native `load` takes only the path receive no extras. The public
|
|
235
|
+
// signature is re-narrowed per family via `Parameters<C['load']>` in
|
|
236
|
+
// the factory return type below.
|
|
237
|
+
const instance = await NativeClass.load(modelPath, ...rest);
|
|
238
|
+
// Use `this.prototype` (not `StreamingModelImpl.prototype`) so the
|
|
239
|
+
// concrete subclass declared per family supplies the prototype and
|
|
240
|
+
// `instanceof ConcreteSubclass` holds.
|
|
241
|
+
Object.setPrototypeOf(instance, this.prototype);
|
|
242
|
+
if (recordPath)
|
|
243
|
+
rememberModelPath(instance, modelPath);
|
|
244
|
+
return instance;
|
|
245
|
+
}
|
|
246
|
+
// The native methods are callback-based, but `Base` is typed as a
|
|
247
|
+
// `SessionCapableModel` constructor (whose streaming methods already
|
|
248
|
+
// return `AsyncGenerator<ChatStreamEvent>`), so these overrides are
|
|
249
|
+
// type-compatible and need no `@ts-expect-error` suppression. The
|
|
250
|
+
// callback bridging happens at runtime via the captured natives.
|
|
251
|
+
async *chatStreamSessionStart(messages, config, signal) {
|
|
252
|
+
yield* _runChatStream((callback) => nativeStart.call(this, messages, (config ?? null), callback), signal);
|
|
253
|
+
}
|
|
254
|
+
async *chatStreamSessionContinue(userMessage, images, audio, config, signal) {
|
|
255
|
+
yield* _runChatStream((callback) => nativeContinue.call(this, userMessage, images, audio, (config ?? null), callback), signal);
|
|
256
|
+
}
|
|
257
|
+
async *chatStreamSessionContinueTool(toolCallId, content, config, signal, isError) {
|
|
258
|
+
yield* _runChatStream((callback) => nativeContinueTool.call(this, toolCallId, content, (config ?? null), callback, (isError ?? null)), signal);
|
|
259
|
+
}
|
|
292
260
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
261
|
+
if (applyTemplate) {
|
|
262
|
+
Object.defineProperty(StreamingModelImpl.prototype, 'applyChatTemplate', {
|
|
263
|
+
configurable: true,
|
|
264
|
+
writable: true,
|
|
265
|
+
value(messages, addGenerationPrompt, tools, enableThinking) {
|
|
266
|
+
return applyChatTemplateFromModelPath(this, messages, addGenerationPrompt, tools, enableThinking);
|
|
267
|
+
},
|
|
268
|
+
});
|
|
297
269
|
}
|
|
270
|
+
return StreamingModelImpl;
|
|
298
271
|
}
|
|
299
272
|
/**
|
|
300
|
-
*
|
|
273
|
+
* Qwen3.5 dense model with AsyncGenerator-based session streaming.
|
|
301
274
|
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
* `
|
|
305
|
-
*
|
|
306
|
-
* `images` guard rejects non-empty image sets with an
|
|
307
|
-
* `IMAGE_CHANGE_REQUIRES_SESSION_RESTART:` prefix.
|
|
275
|
+
* The empty `extends` inherits the factory's streaming overrides,
|
|
276
|
+
* `static load`, and `applyChatTemplate`, and supplies the concrete
|
|
277
|
+
* `.name === 'Qwen35Model'` and a working `instanceof`. Records its
|
|
278
|
+
* model path so `applyChatTemplate` can serve a lazily built tokenizer.
|
|
308
279
|
*/
|
|
309
|
-
export class
|
|
310
|
-
static async load(modelPath) {
|
|
311
|
-
const instance = await Lfm2ModelNative.load(modelPath);
|
|
312
|
-
Object.setPrototypeOf(instance, Lfm2Model.prototype);
|
|
313
|
-
return instance;
|
|
314
|
-
}
|
|
315
|
-
/** Streaming variant of {@link Lfm2Model#chatSessionStart}. */
|
|
316
|
-
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
317
|
-
async *chatStreamSessionStart(messages, config, signal) {
|
|
318
|
-
yield* _runChatStream((callback) => _nativeLfm2ChatStreamSessionStart.call(this, messages, config ?? null, callback), signal);
|
|
319
|
-
}
|
|
320
|
-
/** Streaming variant of {@link Lfm2Model#chatSessionContinue}. */
|
|
321
|
-
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
322
|
-
async *chatStreamSessionContinue(userMessage, images, config, signal) {
|
|
323
|
-
yield* _runChatStream((callback) => _nativeLfm2ChatStreamSessionContinue.call(this, userMessage, images, config ?? null, callback), signal);
|
|
324
|
-
}
|
|
325
|
-
/** Streaming variant of {@link Lfm2Model#chatSessionContinueTool}. */
|
|
326
|
-
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
327
|
-
async *chatStreamSessionContinueTool(toolCallId, content, config, signal) {
|
|
328
|
-
yield* _runChatStream((callback) => _nativeLfm2ChatStreamSessionContinueTool.call(this, toolCallId, content, config ?? null, callback), signal);
|
|
329
|
-
}
|
|
280
|
+
export class Qwen35Model extends makeStreamingModel(Qwen35ModelNative, { recordModelPath: true }) {
|
|
330
281
|
}
|
|
331
|
-
/**
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
* image sets with an `IMAGE_CHANGE_REQUIRES_SESSION_RESTART:` prefix.
|
|
340
|
-
*/
|
|
341
|
-
export class Gemma4Model extends Gemma4ModelNative {
|
|
342
|
-
static async load(modelPath) {
|
|
343
|
-
const instance = await Gemma4ModelNative.load(modelPath);
|
|
344
|
-
Object.setPrototypeOf(instance, Gemma4Model.prototype);
|
|
345
|
-
return instance;
|
|
346
|
-
}
|
|
347
|
-
/** Streaming variant of {@link Gemma4Model#chatSessionStart}. */
|
|
348
|
-
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
349
|
-
async *chatStreamSessionStart(messages, config, signal) {
|
|
350
|
-
yield* _runChatStream((callback) => _nativeGemma4ChatStreamSessionStart.call(this, messages, config ?? null, callback), signal);
|
|
351
|
-
}
|
|
352
|
-
/** Streaming variant of {@link Gemma4Model#chatSessionContinue}. */
|
|
353
|
-
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
354
|
-
async *chatStreamSessionContinue(userMessage, images, config, signal) {
|
|
355
|
-
yield* _runChatStream((callback) => _nativeGemma4ChatStreamSessionContinue.call(this, userMessage, images, config ?? null, callback), signal);
|
|
356
|
-
}
|
|
357
|
-
/** Streaming variant of {@link Gemma4Model#chatSessionContinueTool}. */
|
|
358
|
-
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
359
|
-
async *chatStreamSessionContinueTool(toolCallId, content, config, signal) {
|
|
360
|
-
yield* _runChatStream((callback) => _nativeGemma4ChatStreamSessionContinueTool.call(this, toolCallId, content, config ?? null, callback), signal);
|
|
361
|
-
}
|
|
282
|
+
/** Qwen3.5 MoE model — see {@link Qwen35Model} for the wrapper shape. */
|
|
283
|
+
export class Qwen35MoeModel extends makeStreamingModel(Qwen35MoeModelNative, { recordModelPath: true }) {
|
|
284
|
+
}
|
|
285
|
+
/** LFM2 model (text-only) — see {@link Qwen35Model} for the wrapper shape. */
|
|
286
|
+
export class Lfm2Model extends makeStreamingModel(Lfm2ModelNative, { recordModelPath: true }) {
|
|
287
|
+
}
|
|
288
|
+
/** Gemma4 model (text-only) — see {@link Qwen35Model} for the wrapper shape. */
|
|
289
|
+
export class Gemma4Model extends makeStreamingModel(Gemma4ModelNative, { recordModelPath: true }) {
|
|
362
290
|
}
|
|
363
291
|
/**
|
|
364
|
-
* Qwen3 (
|
|
292
|
+
* Qwen3 (first-gen, text-only) model.
|
|
365
293
|
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
* `
|
|
369
|
-
* satisfies `SessionCapableModel`. Qwen3 legacy is text-only; the
|
|
370
|
-
* native `images` guard rejects non-empty image sets with an
|
|
371
|
-
* `IMAGE_CHANGE_REQUIRES_SESSION_RESTART:` prefix.
|
|
294
|
+
* Records its model path (so prototype-set + path-recording match the
|
|
295
|
+
* other families) but does not install the factory's path-backed
|
|
296
|
+
* `applyChatTemplate`; it retains the native tokenizer-backed method.
|
|
372
297
|
*/
|
|
373
|
-
export class Qwen3Model extends Qwen3ModelNative {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
return instance;
|
|
378
|
-
}
|
|
379
|
-
/** Streaming variant of {@link Qwen3Model#chatSessionStart}. */
|
|
380
|
-
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
381
|
-
async *chatStreamSessionStart(messages, config, signal) {
|
|
382
|
-
yield* _runChatStream((callback) => _nativeQwen3ChatStreamSessionStart.call(this, messages, config ?? null, callback), signal);
|
|
383
|
-
}
|
|
384
|
-
/** Streaming variant of {@link Qwen3Model#chatSessionContinue}. */
|
|
385
|
-
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
386
|
-
async *chatStreamSessionContinue(userMessage, images, config, signal) {
|
|
387
|
-
yield* _runChatStream((callback) => _nativeQwen3ChatStreamSessionContinue.call(this, userMessage, images, config ?? null, callback), signal);
|
|
388
|
-
}
|
|
389
|
-
/** Streaming variant of {@link Qwen3Model#chatSessionContinueTool}. */
|
|
390
|
-
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
391
|
-
async *chatStreamSessionContinueTool(toolCallId, content, config, signal) {
|
|
392
|
-
yield* _runChatStream((callback) => _nativeQwen3ChatStreamSessionContinueTool.call(this, toolCallId, content, config ?? null, callback), signal);
|
|
393
|
-
}
|
|
298
|
+
export class Qwen3Model extends makeStreamingModel(Qwen3ModelNative, {
|
|
299
|
+
recordModelPath: true,
|
|
300
|
+
applyTemplate: false,
|
|
301
|
+
}) {
|
|
394
302
|
}
|
|
395
303
|
// -------------------------------------------------------------------
|
|
396
304
|
// Compile-time conformance check
|
|
397
305
|
// -------------------------------------------------------------------
|
|
398
306
|
//
|
|
399
|
-
// Ensures each
|
|
400
|
-
// `SessionCapableModel` so `ChatSession<XxxModel>`
|
|
401
|
-
// downstream code.
|
|
402
|
-
//
|
|
403
|
-
//
|
|
404
|
-
// fail to compile this block, surfacing the regression at build time.
|
|
307
|
+
// Ensures each family class structurally satisfies
|
|
308
|
+
// `SessionCapableModel` so `ChatSession<XxxModel>` type-checks in
|
|
309
|
+
// downstream code. Compile-only — the `null as unknown as T`
|
|
310
|
+
// placeholder never runs. If a factory override signature drifts away
|
|
311
|
+
// from the interface, this block fails to compile.
|
|
405
312
|
function _assertSessionCapable() {
|
|
406
313
|
const _qwen35 = null;
|
|
407
314
|
const _moe = null;
|
|
@@ -415,3 +322,29 @@ function _assertSessionCapable() {
|
|
|
415
322
|
void _qwen3;
|
|
416
323
|
}
|
|
417
324
|
void _assertSessionCapable;
|
|
325
|
+
/** Compile-time guard that both Qwen3.5 native classes and wrappers retain the exact media planner. */
|
|
326
|
+
function _assertExpandedPromptPlannerSurfaces() {
|
|
327
|
+
const _nativeDense = null;
|
|
328
|
+
const _nativeMoe = null;
|
|
329
|
+
const _wrappedDense = null;
|
|
330
|
+
const _wrappedMoe = null;
|
|
331
|
+
void _nativeDense;
|
|
332
|
+
void _nativeMoe;
|
|
333
|
+
void _wrappedDense;
|
|
334
|
+
void _wrappedMoe;
|
|
335
|
+
}
|
|
336
|
+
void _assertExpandedPromptPlannerSurfaces;
|
|
337
|
+
/** Compile-time guard that the factory preserves every non-streaming native member. */
|
|
338
|
+
function _assertPreservedNativeSurfaces() {
|
|
339
|
+
const _qwen3 = null;
|
|
340
|
+
const _qwen35 = null;
|
|
341
|
+
const _moe = null;
|
|
342
|
+
const _lfm2 = null;
|
|
343
|
+
const _gemma4 = null;
|
|
344
|
+
void _qwen3;
|
|
345
|
+
void _qwen35;
|
|
346
|
+
void _moe;
|
|
347
|
+
void _lfm2;
|
|
348
|
+
void _gemma4;
|
|
349
|
+
}
|
|
350
|
+
void _assertPreservedNativeSurfaces;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mlx-node/lm",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"homepage": "https://github.com/mlx-node/mlx-node",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/mlx-node/mlx-node/issues"
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"test": "vite test run"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@mlx-node/core": "0.0.
|
|
31
|
+
"@mlx-node/core": "0.0.8"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@types/node": "^
|
|
34
|
+
"@types/node": "^26.0.0"
|
|
35
35
|
}
|
|
36
36
|
}
|