@mlx-node/lm 0.0.7 → 0.0.9
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 +56 -38
- package/dist/chat-session.d.ts +372 -104
- package/dist/chat-session.d.ts.map +1 -1
- package/dist/chat-session.js +891 -187
- 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 +175 -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 +72 -0
- package/dist/models/paged-config-override.d.ts.map +1 -0
- package/dist/models/paged-config-override.js +291 -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 +216 -103
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +192 -229
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -18,15 +18,15 @@ npm install @mlx-node/lm
|
|
|
18
18
|
Multi-turn chat runs through `ChatSession`, which owns the server-side KV cache and hides the session bookkeeping behind `send()` / `sendStream()`. The `loadSession()` convenience wrapper loads the model and constructs the session in one step:
|
|
19
19
|
|
|
20
20
|
```typescript
|
|
21
|
-
import { loadSession } from
|
|
21
|
+
import { loadSession } from "@mlx-node/lm";
|
|
22
22
|
|
|
23
|
-
const session = await loadSession(
|
|
23
|
+
const session = await loadSession("./models/Qwen3-0.6B");
|
|
24
24
|
|
|
25
|
-
const result = await session.send(
|
|
25
|
+
const result = await session.send("What is the capital of France?");
|
|
26
26
|
console.log(result.text);
|
|
27
27
|
|
|
28
|
-
// Follow-ups reuse the
|
|
29
|
-
const followUp = await session.send(
|
|
28
|
+
// Follow-ups reuse KV when the template-rendered transcript is an exact prefix extension.
|
|
29
|
+
const followUp = await session.send("And its population?");
|
|
30
30
|
console.log(followUp.text);
|
|
31
31
|
```
|
|
32
32
|
|
|
@@ -35,11 +35,13 @@ console.log(followUp.text);
|
|
|
35
35
|
Every generative model wrapper supports token-by-token streaming via `session.sendStream()`, which yields an `AsyncGenerator<ChatStreamEvent>`:
|
|
36
36
|
|
|
37
37
|
```typescript
|
|
38
|
-
import { loadSession } from
|
|
38
|
+
import { loadSession } from "@mlx-node/lm";
|
|
39
39
|
|
|
40
|
-
const session = await loadSession(
|
|
40
|
+
const session = await loadSession("./models/Qwen3.5-0.8B");
|
|
41
41
|
|
|
42
|
-
for await (const event of session.sendStream(
|
|
42
|
+
for await (const event of session.sendStream(
|
|
43
|
+
"Write a haiku about TypeScript.",
|
|
44
|
+
)) {
|
|
43
45
|
if (!event.done) {
|
|
44
46
|
process.stdout.write(event.text);
|
|
45
47
|
} else {
|
|
@@ -52,25 +54,27 @@ Breaking out of the loop automatically cancels generation. The session tracks it
|
|
|
52
54
|
|
|
53
55
|
## Tool Calling
|
|
54
56
|
|
|
55
|
-
OpenAI-compatible function calling with `createToolDefinition`. Tool-result turns feed back through the same session via `sendToolResult()
|
|
57
|
+
OpenAI-compatible function calling with `createToolDefinition`. Tool-result turns feed back through the same session via `sendToolResult()`. The complete structured transcript is rendered by the checkpoint-provided template, while native code reuses KV only on an exact token-prefix match:
|
|
56
58
|
|
|
57
59
|
```typescript
|
|
58
|
-
import { loadSession, createToolDefinition } from
|
|
60
|
+
import { loadSession, createToolDefinition } from "@mlx-node/lm";
|
|
59
61
|
|
|
60
|
-
const session = await loadSession(
|
|
62
|
+
const session = await loadSession("./models/Qwen3-0.6B");
|
|
61
63
|
|
|
62
64
|
const tools = [
|
|
63
65
|
createToolDefinition(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
"get_weather",
|
|
67
|
+
"Get weather for a city",
|
|
66
68
|
{
|
|
67
|
-
city: { type:
|
|
69
|
+
city: { type: "string", description: "City name" },
|
|
68
70
|
},
|
|
69
|
-
[
|
|
71
|
+
["city"],
|
|
70
72
|
),
|
|
71
73
|
];
|
|
72
74
|
|
|
73
|
-
const result = await session.send(
|
|
75
|
+
const result = await session.send("What is the weather in Tokyo?", {
|
|
76
|
+
config: { tools },
|
|
77
|
+
});
|
|
74
78
|
|
|
75
79
|
// The chat-session API only supports exactly one tool call per assistant turn:
|
|
76
80
|
// each `sendToolResult` dispatch immediately re-opens the assistant turn, so
|
|
@@ -79,7 +83,7 @@ const result = await session.send('What is the weather in Tokyo?', { config: { t
|
|
|
79
83
|
// subsequent `sendToolResult*` after a multi-call turn throws with a clear
|
|
80
84
|
// error — and the caller must refuse multi-call turns up front. Tighten the
|
|
81
85
|
// prompt or tool spec so the model emits at most one call per turn.
|
|
82
|
-
const okCalls = result.toolCalls?.filter((tc) => tc.status ===
|
|
86
|
+
const okCalls = result.toolCalls?.filter((tc) => tc.status === "ok") ?? [];
|
|
83
87
|
if (okCalls.length > 1) {
|
|
84
88
|
throw new Error(
|
|
85
89
|
`ChatSession only supports one tool call per assistant turn; ` +
|
|
@@ -89,7 +93,9 @@ if (okCalls.length > 1) {
|
|
|
89
93
|
const call = okCalls[0];
|
|
90
94
|
if (call) {
|
|
91
95
|
const toolOutput = JSON.stringify(await executeMyTool(call));
|
|
92
|
-
const followUp = await session.sendToolResult(call.id, toolOutput, {
|
|
96
|
+
const followUp = await session.sendToolResult(call.id, toolOutput, {
|
|
97
|
+
config: { tools },
|
|
98
|
+
});
|
|
93
99
|
console.log(followUp.text);
|
|
94
100
|
}
|
|
95
101
|
```
|
|
@@ -99,15 +105,22 @@ if (call) {
|
|
|
99
105
|
`loadModel()` auto-detects the model architecture from `config.json`. Use `loadSession()` when you want an ergonomic `ChatSession` handle in one step, or load a concrete model class and construct `new ChatSession(model)` when you need a reference to both the model and the session (e.g. for `generate()` calls, training, or model metadata):
|
|
100
106
|
|
|
101
107
|
```typescript
|
|
102
|
-
import {
|
|
108
|
+
import {
|
|
109
|
+
loadSession,
|
|
110
|
+
ChatSession,
|
|
111
|
+
Qwen35Model,
|
|
112
|
+
Qwen35MoeModel,
|
|
113
|
+
} from "@mlx-node/lm";
|
|
103
114
|
|
|
104
115
|
// Convenience: auto-detect architecture and wrap in a ChatSession.
|
|
105
|
-
const session = await loadSession(
|
|
116
|
+
const session = await loadSession("./models/Qwen3-0.6B", {
|
|
117
|
+
system: "Be concise.",
|
|
118
|
+
});
|
|
106
119
|
|
|
107
120
|
// Or load a specific architecture directly — every generative model wrapper
|
|
108
121
|
// structurally satisfies ChatSession's SessionCapableModel bound.
|
|
109
|
-
const dense = await Qwen35Model.load(
|
|
110
|
-
const moe = await Qwen35MoeModel.load(
|
|
122
|
+
const dense = await Qwen35Model.load("./models/Qwen3.5-0.8B");
|
|
123
|
+
const moe = await Qwen35MoeModel.load("./models/Qwen3.5-35B-A3B");
|
|
111
124
|
const denseSession = new ChatSession(dense);
|
|
112
125
|
const moeSession = new ChatSession(moe);
|
|
113
126
|
```
|
|
@@ -119,13 +132,18 @@ const moeSession = new ChatSession(moe);
|
|
|
119
132
|
### Pre-defined Configs
|
|
120
133
|
|
|
121
134
|
```typescript
|
|
122
|
-
import {
|
|
135
|
+
import {
|
|
136
|
+
QWEN3_CONFIGS,
|
|
137
|
+
QWEN35_CONFIGS,
|
|
138
|
+
getQwen3Config,
|
|
139
|
+
getQwen35Config,
|
|
140
|
+
} from "@mlx-node/lm";
|
|
123
141
|
|
|
124
142
|
// Available Qwen3 configs: 'qwen3-0.6b', 'qwen3-1.7b', 'qwen3-7b'
|
|
125
|
-
const config = getQwen3Config(
|
|
143
|
+
const config = getQwen3Config("qwen3-0.6b");
|
|
126
144
|
|
|
127
145
|
// Available Qwen3.5 configs: 'qwen3.5-0.6b'
|
|
128
|
-
const config35 = getQwen35Config(
|
|
146
|
+
const config35 = getQwen35Config("qwen3.5-0.6b");
|
|
129
147
|
```
|
|
130
148
|
|
|
131
149
|
## Profiling
|
|
@@ -133,7 +151,7 @@ const config35 = getQwen35Config('qwen3.5-0.6b');
|
|
|
133
151
|
Track per-generation timing, memory usage, and TTFT:
|
|
134
152
|
|
|
135
153
|
```typescript
|
|
136
|
-
import { enableProfiling, disableProfiling } from
|
|
154
|
+
import { enableProfiling, disableProfiling } from "@mlx-node/lm";
|
|
137
155
|
|
|
138
156
|
enableProfiling();
|
|
139
157
|
|
|
@@ -153,10 +171,10 @@ Or set `MLX_PROFILE_DECODE=1` to auto-enable and write a report on exit.
|
|
|
153
171
|
| `loadModel()` | Auto-detect and load any supported model from disk |
|
|
154
172
|
| `loadSession()` | `loadModel()` + `new ChatSession(model)` in one step |
|
|
155
173
|
| `ChatSession<M>` | Multi-turn chat wrapper — `send()`, `sendStream()`, `sendToolResult()`, `reset()` |
|
|
156
|
-
| `Qwen3Model` | Qwen3 inference — `generate()
|
|
157
|
-
| `Qwen35Model` | Qwen3.5 Dense —
|
|
158
|
-
| `Qwen35MoeModel` | Qwen3.5 MoE —
|
|
159
|
-
| `Gemma4Model` | Gemma4 inference —
|
|
174
|
+
| `Qwen3Model` | Qwen3 inference — `generate()` and paged attention |
|
|
175
|
+
| `Qwen35Model` | Qwen3.5 Dense — compiled forward, VLM, paged attention, native MTP |
|
|
176
|
+
| `Qwen35MoeModel` | Qwen3.5 MoE — compiled forward, expert routing, paged attention, native MTP |
|
|
177
|
+
| `Gemma4Model` | Gemma4 inference — multimodal generation and optional external-draft speculation |
|
|
160
178
|
| `Lfm2Model` | LFM2.5 hybrid conv+attention inference — `generate()` |
|
|
161
179
|
|
|
162
180
|
### Streaming Types
|
|
@@ -187,7 +205,7 @@ type ChatStreamEvent = ChatStreamDelta | ChatStreamFinal;
|
|
|
187
205
|
|
|
188
206
|
```typescript
|
|
189
207
|
interface ToolDefinition {
|
|
190
|
-
type:
|
|
208
|
+
type: "function";
|
|
191
209
|
function: FunctionDefinition;
|
|
192
210
|
}
|
|
193
211
|
|
|
@@ -218,13 +236,13 @@ function createToolDefinition(
|
|
|
218
236
|
|
|
219
237
|
Every generative model wrapper exposes the same `ChatSession<M>` surface — `send()`, `sendStream()`, and `sendToolResult()` all work against any of the models below.
|
|
220
238
|
|
|
221
|
-
| Model | `generate()` | `ChatSession` | Training | Notes
|
|
222
|
-
| ------------- | :----------: | :-----------: | :------: |
|
|
223
|
-
| Qwen3 | Yes | Yes | GRPO/SFT | Paged attention
|
|
224
|
-
| Qwen3.5 Dense | Yes | Yes | GRPO/SFT | Compiled
|
|
225
|
-
| Qwen3.5 MoE | Yes | Yes | GRPO/SFT |
|
|
226
|
-
| Gemma4 | Yes | Yes | No |
|
|
227
|
-
| LFM2.5 | Yes | Yes | No | Hybrid conv + attention architecture
|
|
239
|
+
| Model | `generate()` | `ChatSession` | Training | Notes |
|
|
240
|
+
| ------------- | :----------: | :-----------: | :------: | ------------------------------------ |
|
|
241
|
+
| Qwen3 | Yes | Yes | GRPO/SFT | Paged attention |
|
|
242
|
+
| Qwen3.5 Dense | Yes | Yes | GRPO/SFT | Compiled forward, VLM, native MTP |
|
|
243
|
+
| Qwen3.5 MoE | Yes | Yes | GRPO/SFT | Expert routing, paged cache, MTP |
|
|
244
|
+
| Gemma4 | Yes | Yes | No | Multimodal, optional external draft |
|
|
245
|
+
| LFM2.5 | Yes | Yes | No | Hybrid conv + attention architecture |
|
|
228
246
|
|
|
229
247
|
## Performance
|
|
230
248
|
|