@mlx-node/core 0.0.8 → 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.
Files changed (3) hide show
  1. package/index.cjs +58 -52
  2. package/index.d.cts +370 -387
  3. package/package.json +2 -2
package/index.d.cts CHANGED
@@ -195,50 +195,24 @@ export declare class Gemma4Model {
195
195
  /**
196
196
  * Start a new chat session.
197
197
  *
198
- * Runs the full jinja chat template once, decodes until the
199
- * family's session stop token, and leaves the KV caches on a
200
- * clean turn boundary so subsequent `chatSessionContinue` /
201
- * `chatSessionContinueTool` calls can append a raw delta on
202
- * top without re-rendering the chat template.
198
+ * Renders the complete conversation through the loaded chat
199
+ * template, decodes until the family's session stop token, and
200
+ * preserves the resulting KV state for exact-prefix reuse.
203
201
  */
204
202
  chatSessionStart(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
205
203
  /**
206
- * Continue an existing chat session with a new user message.
207
- *
208
- * Appends a raw user/assistant delta to the session's cached
209
- * KV state, then decodes the assistant reply, stopping on the
210
- * family's session boundary token.
211
- *
212
- * `images` is an opt-in guard parameter: when non-empty the
213
- * native side returns an error whose message begins with
214
- * `IMAGE_CHANGE_REQUIRES_SESSION_RESTART:` so the TypeScript
215
- * `ChatSession` layer can route image-changes back through a
216
- * fresh `chatSessionStart`.
204
+ * Continue an existing chat session from the complete
205
+ * structured conversation. The loaded model template is the
206
+ * sole authority for the rendered suffix; native cache reuse
207
+ * occurs only after the completed structured history is verified
208
+ * against the saved token history.
217
209
  */
218
- chatSessionContinue(
219
- userMessage: string,
220
- images: Uint8Array[] | null | undefined,
221
- audio: Uint8Array[] | null | undefined,
222
- config: ChatConfig | null | undefined,
223
- ): Promise<ChatResult>;
210
+ chatSessionContinue(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
224
211
  /**
225
- * Continue an existing chat session with a tool-result turn.
226
- *
227
- * Builds the family's tool-result delta from `content` and
228
- * prefills it on top of the live session caches, then decodes
229
- * the assistant reply.
230
- *
231
- * `is_error` is the structured tool-error signal. When
232
- * `Some(true)`, the renderer prepends the shared
233
- * [`crate::tokenizer::TOOL_ERROR_MARKER`] inside the
234
- * rendered tool block.
212
+ * Continue an existing chat session from a complete
213
+ * structured conversation ending in a tool-role message.
235
214
  */
236
- chatSessionContinueTool(
237
- toolCallId: string,
238
- content: string,
239
- config?: ChatConfig | undefined | null,
240
- isError?: boolean | undefined | null,
241
- ): Promise<ChatResult>;
215
+ chatSessionContinueTool(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
242
216
  /** Streaming variant of `chatSessionStart`. */
243
217
  chatStreamSessionStart(
244
218
  messages: ChatMessage[],
@@ -247,26 +221,15 @@ export declare class Gemma4Model {
247
221
  ): Promise<ChatStreamHandle>;
248
222
  /** Streaming variant of `chatSessionContinue`. */
249
223
  chatStreamSessionContinue(
250
- userMessage: string,
251
- images: Uint8Array[] | null | undefined,
252
- audio: Uint8Array[] | null | undefined,
224
+ messages: ChatMessage[],
253
225
  config: ChatConfig | null | undefined,
254
226
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
255
227
  ): Promise<ChatStreamHandle>;
256
- /**
257
- * Streaming variant of `chatSessionContinueTool`.
258
- *
259
- * `is_error` mirrors the non-streaming entry point — when
260
- * `Some(true)`, the renderer prepends the shared
261
- * [`crate::tokenizer::TOOL_ERROR_MARKER`] inside the rendered
262
- * tool block.
263
- */
228
+ /** Streaming variant of `chatSessionContinueTool`. */
264
229
  chatStreamSessionContinueTool(
265
- toolCallId: string,
266
- content: string,
230
+ messages: ChatMessage[],
267
231
  config: ChatConfig | null | undefined,
268
232
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
269
- isError?: boolean | null | undefined,
270
233
  ): Promise<ChatStreamHandle>;
271
234
  }
272
235
 
@@ -553,50 +516,24 @@ export declare class Lfm2Model {
553
516
  /**
554
517
  * Start a new chat session.
555
518
  *
556
- * Runs the full jinja chat template once, decodes until the
557
- * family's session stop token, and leaves the KV caches on a
558
- * clean turn boundary so subsequent `chatSessionContinue` /
559
- * `chatSessionContinueTool` calls can append a raw delta on
560
- * top without re-rendering the chat template.
519
+ * Renders the complete conversation through the loaded chat
520
+ * template, decodes until the family's session stop token, and
521
+ * preserves the resulting KV state for exact-prefix reuse.
561
522
  */
562
523
  chatSessionStart(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
563
524
  /**
564
- * Continue an existing chat session with a new user message.
565
- *
566
- * Appends a raw user/assistant delta to the session's cached
567
- * KV state, then decodes the assistant reply, stopping on the
568
- * family's session boundary token.
569
- *
570
- * `images` is an opt-in guard parameter: when non-empty the
571
- * native side returns an error whose message begins with
572
- * `IMAGE_CHANGE_REQUIRES_SESSION_RESTART:` so the TypeScript
573
- * `ChatSession` layer can route image-changes back through a
574
- * fresh `chatSessionStart`.
525
+ * Continue an existing chat session from the complete
526
+ * structured conversation. The loaded model template is the
527
+ * sole authority for the rendered suffix; native cache reuse
528
+ * occurs only after the completed structured history is verified
529
+ * against the saved token history.
575
530
  */
576
- chatSessionContinue(
577
- userMessage: string,
578
- images: Uint8Array[] | null | undefined,
579
- audio: Uint8Array[] | null | undefined,
580
- config: ChatConfig | null | undefined,
581
- ): Promise<ChatResult>;
531
+ chatSessionContinue(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
582
532
  /**
583
- * Continue an existing chat session with a tool-result turn.
584
- *
585
- * Builds the family's tool-result delta from `content` and
586
- * prefills it on top of the live session caches, then decodes
587
- * the assistant reply.
588
- *
589
- * `is_error` is the structured tool-error signal. When
590
- * `Some(true)`, the renderer prepends the shared
591
- * [`crate::tokenizer::TOOL_ERROR_MARKER`] inside the
592
- * rendered tool block.
533
+ * Continue an existing chat session from a complete
534
+ * structured conversation ending in a tool-role message.
593
535
  */
594
- chatSessionContinueTool(
595
- toolCallId: string,
596
- content: string,
597
- config?: ChatConfig | undefined | null,
598
- isError?: boolean | undefined | null,
599
- ): Promise<ChatResult>;
536
+ chatSessionContinueTool(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
600
537
  /** Streaming variant of `chatSessionStart`. */
601
538
  chatStreamSessionStart(
602
539
  messages: ChatMessage[],
@@ -605,26 +542,15 @@ export declare class Lfm2Model {
605
542
  ): Promise<ChatStreamHandle>;
606
543
  /** Streaming variant of `chatSessionContinue`. */
607
544
  chatStreamSessionContinue(
608
- userMessage: string,
609
- images: Uint8Array[] | null | undefined,
610
- audio: Uint8Array[] | null | undefined,
545
+ messages: ChatMessage[],
611
546
  config: ChatConfig | null,
612
547
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
613
548
  ): Promise<ChatStreamHandle>;
614
- /**
615
- * Streaming variant of `chatSessionContinueTool`.
616
- *
617
- * `is_error` mirrors the non-streaming entry point — when
618
- * `Some(true)`, the renderer prepends the shared
619
- * [`crate::tokenizer::TOOL_ERROR_MARKER`] inside the rendered
620
- * tool block.
621
- */
549
+ /** Streaming variant of `chatSessionContinueTool`. */
622
550
  chatStreamSessionContinueTool(
623
- toolCallId: string,
624
- content: string,
551
+ messages: ChatMessage[],
625
552
  config: ChatConfig | null,
626
553
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
627
- isError?: boolean | null | undefined,
628
554
  ): Promise<ChatStreamHandle>;
629
555
  }
630
556
 
@@ -1108,11 +1034,9 @@ export declare class QianfanOCRModel {
1108
1034
  /**
1109
1035
  * Start a new chat session.
1110
1036
  *
1111
- * Runs the full chat template once, decodes until `<|im_end|>`,
1112
- * and leaves the KV caches on a clean turn boundary so subsequent
1113
- * `chatSessionContinue` / `chatSessionContinueTool` calls can
1114
- * append a raw ChatML delta on top without re-rendering the chat
1115
- * template.
1037
+ * Renders the complete structured conversation through the checkpoint
1038
+ * template, decodes until `<|im_end|>`, and preserves KV state for an
1039
+ * exact-prefix check against the next complete template render.
1116
1040
  *
1117
1041
  * Qianfan-OCR is always a VLM (InternViT + Qwen3 language model), so
1118
1042
  * this entry point accepts images in `messages` without the text-only
@@ -1120,97 +1044,33 @@ export declare class QianfanOCRModel {
1120
1044
  */
1121
1045
  chatSessionStart(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
1122
1046
  /**
1123
- * Continue an existing chat session with a new user message.
1124
- *
1125
- * Appends a raw ChatML user/assistant delta to the session's cached
1126
- * KV state, then decodes the model reply. Stops on `<|im_end|>` so
1127
- * the cache remains on a clean turn boundary for the next turn.
1128
- *
1129
- * Requires a live session started via `chatSessionStart`. Errors
1130
- * if the session is empty or if `config.reuse_cache` is
1131
- * explicitly set to `false`.
1132
- *
1133
- * `images` is an opt-in guard parameter: when non-empty the native
1134
- * side returns an error whose message begins with
1135
- * `IMAGE_CHANGE_REQUIRES_SESSION_RESTART:` so the TypeScript
1136
- * `ChatSession` layer can catch the prefix and route image-changes
1137
- * back through a fresh `chatSessionStart` uniformly across all
1138
- * model backends. Qianfan-OCR is a VLM but the continue path cannot
1139
- * splice new vision features into a live KV cache — image changes
1140
- * always require a fresh session start.
1141
- *
1142
- * `audio` exists only to keep this method's positional ABI aligned
1143
- * with the shared chat surface every other family exposes (the
1144
- * `chat_napi_surface!` macro inserts `audio` between `images` and
1145
- * `config`). Qianfan-OCR has no audio support, so a non-empty
1146
- * `audio` is rejected at the boundary with the shared no-audio
1147
- * error; `None` / empty is a complete no-op and audio is never
1148
- * threaded into the model thread.
1149
- */
1150
- chatSessionContinue(
1151
- userMessage: string,
1152
- images: Uint8Array[] | null | undefined,
1153
- audio: Uint8Array[] | null | undefined,
1154
- config: ChatConfig | null | undefined,
1155
- ): Promise<ChatResult>;
1156
- /**
1157
- * Continue an existing chat session with a tool-result turn.
1158
- *
1159
- * Builds a ChatML `<tool_response>` delta from `tool_call_id` and
1160
- * `content` and prefills it on top of the live session caches, then
1161
- * decodes the model reply. Stops on `<|im_end|>` so the cache stays
1162
- * on a clean turn boundary for the next turn.
1163
- *
1164
- * `is_error` is the structured tool-error signal. When `Some(true)`,
1165
- * the renderer prepends the shared
1166
- * [`crate::tokenizer::TOOL_ERROR_MARKER`] inside the
1167
- * `<tool_response>` wrapper so the model receives a clear text-level
1168
- * cue. `None` / `Some(false)` keep the wire bytes byte-equal to the
1169
- * pre-feature output.
1170
- *
1171
- * Requires a live session started via `chatSessionStart`.
1172
- */
1173
- chatSessionContinueTool(
1174
- toolCallId: string,
1175
- content: string,
1176
- config?: ChatConfig | undefined | null,
1177
- isError?: boolean | undefined | null,
1178
- ): Promise<ChatResult>;
1047
+ * Continue from the caller's complete conversation history. The
1048
+ * checkpoint's chat template is rendered again and exact prefix matching
1049
+ * decides whether the live cache can be reused.
1050
+ */
1051
+ chatSessionContinue(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
1052
+ /**
1053
+ * Tool-result continuation over a full history. Tool representation is
1054
+ * owned entirely by the model-provided template.
1055
+ */
1056
+ chatSessionContinueTool(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
1179
1057
  /** Streaming variant of `chatSessionStart`. */
1180
1058
  chatStreamSessionStart(
1181
1059
  messages: ChatMessage[],
1182
1060
  config: ChatConfig | null | undefined,
1183
1061
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
1184
1062
  ): Promise<ChatStreamHandle>;
1185
- /**
1186
- * Streaming variant of `chatSessionContinue`.
1187
- *
1188
- * `audio` mirrors the non-streaming entry point: it exists only to
1189
- * keep the positional ABI aligned with the shared chat surface, and
1190
- * a non-empty value is rejected at the boundary with the shared
1191
- * no-audio error. `None` / empty is a complete no-op.
1192
- */
1063
+ /** Streaming continuation over a complete conversation history. */
1193
1064
  chatStreamSessionContinue(
1194
- userMessage: string,
1195
- images: Uint8Array[] | null | undefined,
1196
- audio: Uint8Array[] | null | undefined,
1065
+ messages: ChatMessage[],
1197
1066
  config: ChatConfig | null | undefined,
1198
1067
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
1199
1068
  ): Promise<ChatStreamHandle>;
1200
- /**
1201
- * Streaming variant of `chatSessionContinueTool`.
1202
- *
1203
- * `is_error` mirrors the non-streaming entry point — when
1204
- * `Some(true)`, the renderer prepends the shared
1205
- * [`crate::tokenizer::TOOL_ERROR_MARKER`] inside the
1206
- * `<tool_response>` wrapper.
1207
- */
1069
+ /** Streaming tool-result continuation over a complete history. */
1208
1070
  chatStreamSessionContinueTool(
1209
- toolCallId: string,
1210
- content: string,
1071
+ messages: ChatMessage[],
1211
1072
  config: ChatConfig | null | undefined,
1212
1073
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
1213
- isError?: boolean | null | undefined,
1214
1074
  ): Promise<ChatStreamHandle>;
1215
1075
  }
1216
1076
 
@@ -1303,50 +1163,24 @@ export declare class Qwen35Model {
1303
1163
  /**
1304
1164
  * Start a new chat session.
1305
1165
  *
1306
- * Runs the full jinja chat template once, decodes until the
1307
- * family's session stop token, and leaves the KV caches on a
1308
- * clean turn boundary so subsequent `chatSessionContinue` /
1309
- * `chatSessionContinueTool` calls can append a raw delta on
1310
- * top without re-rendering the chat template.
1166
+ * Renders the complete conversation through the loaded chat
1167
+ * template, decodes until the family's session stop token, and
1168
+ * preserves the resulting KV state for exact-prefix reuse.
1311
1169
  */
1312
1170
  chatSessionStart(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
1313
1171
  /**
1314
- * Continue an existing chat session with a new user message.
1315
- *
1316
- * Appends a raw user/assistant delta to the session's cached
1317
- * KV state, then decodes the assistant reply, stopping on the
1318
- * family's session boundary token.
1319
- *
1320
- * `images` is an opt-in guard parameter: when non-empty the
1321
- * native side returns an error whose message begins with
1322
- * `IMAGE_CHANGE_REQUIRES_SESSION_RESTART:` so the TypeScript
1323
- * `ChatSession` layer can route image-changes back through a
1324
- * fresh `chatSessionStart`.
1172
+ * Continue an existing chat session from the complete
1173
+ * structured conversation. The loaded model template is the
1174
+ * sole authority for the rendered suffix; native cache reuse
1175
+ * occurs only after the completed structured history is verified
1176
+ * against the saved token history.
1325
1177
  */
1326
- chatSessionContinue(
1327
- userMessage: string,
1328
- images: Uint8Array[] | null | undefined,
1329
- audio: Uint8Array[] | null | undefined,
1330
- config: ChatConfig | null | undefined,
1331
- ): Promise<ChatResult>;
1178
+ chatSessionContinue(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
1332
1179
  /**
1333
- * Continue an existing chat session with a tool-result turn.
1334
- *
1335
- * Builds the family's tool-result delta from `content` and
1336
- * prefills it on top of the live session caches, then decodes
1337
- * the assistant reply.
1338
- *
1339
- * `is_error` is the structured tool-error signal. When
1340
- * `Some(true)`, the renderer prepends the shared
1341
- * [`crate::tokenizer::TOOL_ERROR_MARKER`] inside the
1342
- * rendered tool block.
1180
+ * Continue an existing chat session from a complete
1181
+ * structured conversation ending in a tool-role message.
1343
1182
  */
1344
- chatSessionContinueTool(
1345
- toolCallId: string,
1346
- content: string,
1347
- config?: ChatConfig | undefined | null,
1348
- isError?: boolean | undefined | null,
1349
- ): Promise<ChatResult>;
1183
+ chatSessionContinueTool(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
1350
1184
  /** Streaming variant of `chatSessionStart`. */
1351
1185
  chatStreamSessionStart(
1352
1186
  messages: ChatMessage[],
@@ -1355,26 +1189,15 @@ export declare class Qwen35Model {
1355
1189
  ): Promise<ChatStreamHandle>;
1356
1190
  /** Streaming variant of `chatSessionContinue`. */
1357
1191
  chatStreamSessionContinue(
1358
- userMessage: string,
1359
- images: Uint8Array[] | null | undefined,
1360
- audio: Uint8Array[] | null | undefined,
1192
+ messages: ChatMessage[],
1361
1193
  config: ChatConfig | null,
1362
1194
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
1363
1195
  ): Promise<ChatStreamHandle>;
1364
- /**
1365
- * Streaming variant of `chatSessionContinueTool`.
1366
- *
1367
- * `is_error` mirrors the non-streaming entry point — when
1368
- * `Some(true)`, the renderer prepends the shared
1369
- * [`crate::tokenizer::TOOL_ERROR_MARKER`] inside the rendered
1370
- * tool block.
1371
- */
1196
+ /** Streaming variant of `chatSessionContinueTool`. */
1372
1197
  chatStreamSessionContinueTool(
1373
- toolCallId: string,
1374
- content: string,
1198
+ messages: ChatMessage[],
1375
1199
  config: ChatConfig | null,
1376
1200
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
1377
- isError?: boolean | null | undefined,
1378
1201
  ): Promise<ChatStreamHandle>;
1379
1202
  }
1380
1203
  export type Qwen3_5Model = Qwen35Model;
@@ -1455,50 +1278,24 @@ export declare class Qwen35MoeModel {
1455
1278
  /**
1456
1279
  * Start a new chat session.
1457
1280
  *
1458
- * Runs the full jinja chat template once, decodes until the
1459
- * family's session stop token, and leaves the KV caches on a
1460
- * clean turn boundary so subsequent `chatSessionContinue` /
1461
- * `chatSessionContinueTool` calls can append a raw delta on
1462
- * top without re-rendering the chat template.
1281
+ * Renders the complete conversation through the loaded chat
1282
+ * template, decodes until the family's session stop token, and
1283
+ * preserves the resulting KV state for exact-prefix reuse.
1463
1284
  */
1464
1285
  chatSessionStart(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
1465
1286
  /**
1466
- * Continue an existing chat session with a new user message.
1467
- *
1468
- * Appends a raw user/assistant delta to the session's cached
1469
- * KV state, then decodes the assistant reply, stopping on the
1470
- * family's session boundary token.
1471
- *
1472
- * `images` is an opt-in guard parameter: when non-empty the
1473
- * native side returns an error whose message begins with
1474
- * `IMAGE_CHANGE_REQUIRES_SESSION_RESTART:` so the TypeScript
1475
- * `ChatSession` layer can route image-changes back through a
1476
- * fresh `chatSessionStart`.
1287
+ * Continue an existing chat session from the complete
1288
+ * structured conversation. The loaded model template is the
1289
+ * sole authority for the rendered suffix; native cache reuse
1290
+ * occurs only after the completed structured history is verified
1291
+ * against the saved token history.
1477
1292
  */
1478
- chatSessionContinue(
1479
- userMessage: string,
1480
- images: Uint8Array[] | null | undefined,
1481
- audio: Uint8Array[] | null | undefined,
1482
- config: ChatConfig | null | undefined,
1483
- ): Promise<ChatResult>;
1293
+ chatSessionContinue(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
1484
1294
  /**
1485
- * Continue an existing chat session with a tool-result turn.
1486
- *
1487
- * Builds the family's tool-result delta from `content` and
1488
- * prefills it on top of the live session caches, then decodes
1489
- * the assistant reply.
1490
- *
1491
- * `is_error` is the structured tool-error signal. When
1492
- * `Some(true)`, the renderer prepends the shared
1493
- * [`crate::tokenizer::TOOL_ERROR_MARKER`] inside the
1494
- * rendered tool block.
1295
+ * Continue an existing chat session from a complete
1296
+ * structured conversation ending in a tool-role message.
1495
1297
  */
1496
- chatSessionContinueTool(
1497
- toolCallId: string,
1498
- content: string,
1499
- config?: ChatConfig | undefined | null,
1500
- isError?: boolean | undefined | null,
1501
- ): Promise<ChatResult>;
1298
+ chatSessionContinueTool(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
1502
1299
  /** Streaming variant of `chatSessionStart`. */
1503
1300
  chatStreamSessionStart(
1504
1301
  messages: ChatMessage[],
@@ -1507,26 +1304,15 @@ export declare class Qwen35MoeModel {
1507
1304
  ): Promise<ChatStreamHandle>;
1508
1305
  /** Streaming variant of `chatSessionContinue`. */
1509
1306
  chatStreamSessionContinue(
1510
- userMessage: string,
1511
- images: Uint8Array[] | null | undefined,
1512
- audio: Uint8Array[] | null | undefined,
1307
+ messages: ChatMessage[],
1513
1308
  config: ChatConfig | null,
1514
1309
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
1515
1310
  ): Promise<ChatStreamHandle>;
1516
- /**
1517
- * Streaming variant of `chatSessionContinueTool`.
1518
- *
1519
- * `is_error` mirrors the non-streaming entry point — when
1520
- * `Some(true)`, the renderer prepends the shared
1521
- * [`crate::tokenizer::TOOL_ERROR_MARKER`] inside the rendered
1522
- * tool block.
1523
- */
1311
+ /** Streaming variant of `chatSessionContinueTool`. */
1524
1312
  chatStreamSessionContinueTool(
1525
- toolCallId: string,
1526
- content: string,
1313
+ messages: ChatMessage[],
1527
1314
  config: ChatConfig | null,
1528
1315
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
1529
- isError?: boolean | null | undefined,
1530
1316
  ): Promise<ChatStreamHandle>;
1531
1317
  }
1532
1318
  export type Qwen3_5MoeModel = Qwen35MoeModel;
@@ -1656,50 +1442,24 @@ export declare class Qwen3Model {
1656
1442
  /**
1657
1443
  * Start a new chat session.
1658
1444
  *
1659
- * Runs the full jinja chat template once, decodes until the
1660
- * family's session stop token, and leaves the KV caches on a
1661
- * clean turn boundary so subsequent `chatSessionContinue` /
1662
- * `chatSessionContinueTool` calls can append a raw delta on
1663
- * top without re-rendering the chat template.
1445
+ * Renders the complete conversation through the loaded chat
1446
+ * template, decodes until the family's session stop token, and
1447
+ * preserves the resulting KV state for exact-prefix reuse.
1664
1448
  */
1665
1449
  chatSessionStart(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
1666
1450
  /**
1667
- * Continue an existing chat session with a new user message.
1668
- *
1669
- * Appends a raw user/assistant delta to the session's cached
1670
- * KV state, then decodes the assistant reply, stopping on the
1671
- * family's session boundary token.
1672
- *
1673
- * `images` is an opt-in guard parameter: when non-empty the
1674
- * native side returns an error whose message begins with
1675
- * `IMAGE_CHANGE_REQUIRES_SESSION_RESTART:` so the TypeScript
1676
- * `ChatSession` layer can route image-changes back through a
1677
- * fresh `chatSessionStart`.
1451
+ * Continue an existing chat session from the complete
1452
+ * structured conversation. The loaded model template is the
1453
+ * sole authority for the rendered suffix; native cache reuse
1454
+ * occurs only after the completed structured history is verified
1455
+ * against the saved token history.
1678
1456
  */
1679
- chatSessionContinue(
1680
- userMessage: string,
1681
- images: Uint8Array[] | null | undefined,
1682
- audio: Uint8Array[] | null | undefined,
1683
- config: ChatConfig | null | undefined,
1684
- ): Promise<ChatResult>;
1457
+ chatSessionContinue(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
1685
1458
  /**
1686
- * Continue an existing chat session with a tool-result turn.
1687
- *
1688
- * Builds the family's tool-result delta from `content` and
1689
- * prefills it on top of the live session caches, then decodes
1690
- * the assistant reply.
1691
- *
1692
- * `is_error` is the structured tool-error signal. When
1693
- * `Some(true)`, the renderer prepends the shared
1694
- * [`crate::tokenizer::TOOL_ERROR_MARKER`] inside the
1695
- * rendered tool block.
1459
+ * Continue an existing chat session from a complete
1460
+ * structured conversation ending in a tool-role message.
1696
1461
  */
1697
- chatSessionContinueTool(
1698
- toolCallId: string,
1699
- content: string,
1700
- config?: ChatConfig | undefined | null,
1701
- isError?: boolean | undefined | null,
1702
- ): Promise<ChatResult>;
1462
+ chatSessionContinueTool(messages: Array<ChatMessage>, config?: ChatConfig | undefined | null): Promise<ChatResult>;
1703
1463
  /** Streaming variant of `chatSessionStart`. */
1704
1464
  chatStreamSessionStart(
1705
1465
  messages: ChatMessage[],
@@ -1708,26 +1468,15 @@ export declare class Qwen3Model {
1708
1468
  ): Promise<ChatStreamHandle>;
1709
1469
  /** Streaming variant of `chatSessionContinue`. */
1710
1470
  chatStreamSessionContinue(
1711
- userMessage: string,
1712
- images: Uint8Array[] | null | undefined,
1713
- audio: Uint8Array[] | null | undefined,
1471
+ messages: ChatMessage[],
1714
1472
  config: ChatConfig | null,
1715
1473
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
1716
1474
  ): Promise<ChatStreamHandle>;
1717
- /**
1718
- * Streaming variant of `chatSessionContinueTool`.
1719
- *
1720
- * `is_error` mirrors the non-streaming entry point — when
1721
- * `Some(true)`, the renderer prepends the shared
1722
- * [`crate::tokenizer::TOOL_ERROR_MARKER`] inside the rendered
1723
- * tool block.
1724
- */
1475
+ /** Streaming variant of `chatSessionContinueTool`. */
1725
1476
  chatStreamSessionContinueTool(
1726
- toolCallId: string,
1727
- content: string,
1477
+ messages: ChatMessage[],
1728
1478
  config: ChatConfig | null,
1729
1479
  callback: (err: Error | null, chunk: ChatStreamChunk) => void,
1730
- isError?: boolean | null | undefined,
1731
1480
  ): Promise<ChatStreamHandle>;
1732
1481
  /**
1733
1482
  * Load a pretrained model from disk
@@ -1862,6 +1611,9 @@ export declare class Qwen3Tokenizer {
1862
1611
  * * `add_generation_prompt` - Whether to add assistant prompt at end (default: true)
1863
1612
  * * `tools` - Optional array of tool definitions for function calling
1864
1613
  * * `enable_thinking` - Optional flag to enable thinking mode (<think> tags)
1614
+ * * `content_order` - Optional structured multimodal content ordering
1615
+ * * `existing_image_placeholder` - Optional model marker that suppresses
1616
+ * synthetic image parts when it is already present in sanitized text
1865
1617
  *
1866
1618
  * # Returns
1867
1619
  * Encoded token IDs ready for model input
@@ -1887,6 +1639,8 @@ export declare class Qwen3Tokenizer {
1887
1639
  addGenerationPrompt?: boolean | undefined | null,
1888
1640
  tools?: Array<ToolDefinition> | undefined | null,
1889
1641
  enableThinking?: boolean | undefined | null,
1642
+ contentOrder?: MultimodalContentOrder | undefined | null,
1643
+ existingImagePlaceholder?: string | undefined | null,
1890
1644
  ): Promise<Uint32Array>;
1891
1645
  /** Get vocabulary size */
1892
1646
  vocabSize(): number;
@@ -2169,17 +1923,11 @@ export declare class TextRecModel {
2169
1923
  recognizeCrop(rgbData: Uint8Array, width: number, height: number): RecResult;
2170
1924
  }
2171
1925
 
2172
- /** Result from VLM chat */
2173
1926
  export declare class VlmChatResult {
2174
- /** Get the response text */
2175
1927
  get text(): string;
2176
- /** Get the generated tokens */
2177
1928
  get tokens(): MxArray;
2178
- /** Get the log probabilities */
2179
1929
  get logprobs(): MxArray;
2180
- /** Get the finish reason */
2181
1930
  get finishReason(): 'stop' | 'length' | 'repetition';
2182
- /** Get the number of tokens generated */
2183
1931
  get numTokens(): number;
2184
1932
  }
2185
1933
  export type VLMChatResult = VlmChatResult;
@@ -2519,15 +2267,9 @@ export interface ChatMessage {
2519
2267
  * Authoritative, structured signal of tool-call failure. Set to
2520
2268
  * `Some(true)` when the caller (e.g. the Anthropic
2521
2269
  * `tool_result.is_error === true` translator) wants the model to
2522
- * treat the tool output as an error. The renderer prepends a short
2523
- * `[tool error]` prefix to `content` when emitting the wire-format
2524
- * tool response so the model receives a clear text-level cue, but
2525
- * the original `content` stays byte-for-byte intact in the
2526
- * structured form — no JSON wrapping, no in-band marker that could
2527
- * collide with a successful tool result whose literal content
2528
- * happens to start with the same prefix.
2529
- *
2530
- * `None` / `Some(false)` produce the unmarked wire format.
2270
+ * treat the tool output as an error. It is exposed to the model's
2271
+ * chat template as `message.is_error`; Rust never rewrites `content`
2272
+ * or invents a model-facing error marker.
2531
2273
  */
2532
2274
  isError?: boolean;
2533
2275
  /** Reasoning content for thinking mode (used with <think> tags) */
@@ -2554,11 +2296,25 @@ export interface ChatResult {
2554
2296
  text: string;
2555
2297
  toolCalls: Array<ToolCallResult>;
2556
2298
  thinking?: string;
2299
+ /**
2300
+ * Effective `enable_thinking` boolean passed to the model-provided
2301
+ * chat template for this turn. This is replay provenance and is
2302
+ * intentionally distinct from the family's decode-time
2303
+ * [`ThinkingSetup`](crate::engine::ThinkingSetup).
2304
+ */
2305
+ thinkingEnabled: boolean;
2557
2306
  numTokens: number;
2558
2307
  promptTokens: number;
2559
2308
  reasoningTokens: number;
2560
2309
  finishReason: string;
2561
2310
  rawText: string;
2311
+ /**
2312
+ * Reasoning-redacted raw output computed from the same token-aware
2313
+ * boundary as `raw_text`. Session wrappers may request full reasoning
2314
+ * internally for deterministic replay, then expose this safe view to a
2315
+ * caller that set `includeReasoning: false`.
2316
+ */
2317
+ publicRawText?: string | undefined;
2562
2318
  /**
2563
2319
  * Number of prompt tokens served from the reused KV-cache prefix.
2564
2320
  *
@@ -2574,15 +2330,11 @@ export interface ChatResult {
2574
2330
  performance?: PerformanceMetrics;
2575
2331
  }
2576
2332
 
2577
- /** Chat message role (lowercase values matching standard convention) */
2333
+ /** Chat message role (lowercase values matching standard convention). */
2578
2334
  export declare const enum ChatRole {
2579
- /** User message */
2580
2335
  User = 'user',
2581
- /** Assistant response */
2582
2336
  Assistant = 'assistant',
2583
- /** System prompt */
2584
2337
  System = 'system',
2585
- /** Tool response */
2586
2338
  Tool = 'tool',
2587
2339
  }
2588
2340
 
@@ -2593,10 +2345,23 @@ export interface ChatStreamChunk {
2593
2345
  finishReason?: string;
2594
2346
  toolCalls?: Array<ToolCallResult>;
2595
2347
  thinking?: string;
2348
+ /**
2349
+ * Effective template `enable_thinking` value. Present on the terminal
2350
+ * chunk only; incremental text/reasoning chunks leave it unset.
2351
+ */
2352
+ thinkingEnabled?: boolean | undefined;
2596
2353
  numTokens?: number;
2597
2354
  promptTokens?: number;
2598
2355
  reasoningTokens?: number;
2599
2356
  rawText?: string;
2357
+ /** Reasoning-redacted counterpart to `raw_text` on terminal chunks. */
2358
+ publicRawText?: string | undefined;
2359
+ /**
2360
+ * Whether terminal `text` is the authoritative parsed assistant content.
2361
+ * Generic emitters set true; Gemma streams visible content exclusively as
2362
+ * deltas and set false so session history commits the accumulated text.
2363
+ */
2364
+ textAuthoritative?: boolean | undefined;
2600
2365
  /**
2601
2366
  * Number of prompt tokens served from the reused KV-cache prefix on
2602
2367
  * this turn. Populated on the terminal chunk (`done == true`) only;
@@ -2644,6 +2409,171 @@ export interface CleanupStats {
2644
2409
  logsDeleted: number;
2645
2410
  }
2646
2411
 
2412
+ /**
2413
+ * Flush every accepted cold-tier block to disk, blocking until the
2414
+ * background writer has fsync+renamed each write enqueued before this call,
2415
+ * or `timeout_ms` elapses. Returns `true` when the drain completed — or when
2416
+ * the tier was never opened (nothing to flush) — and `false` on timeout.
2417
+ *
2418
+ * Called from the agent's one-shot (`mlx agent -p`) shutdown so a prompt's
2419
+ * just-persisted prefix blocks reach the drive before the process exits,
2420
+ * rather than being abandoned in the write queue.
2421
+ *
2422
+ * The payload flush is `fsync(2)`, not `F_FULLFSYNC`: a drained block
2423
+ * survives process death and kernel panic, but a sudden power loss can leave
2424
+ * it torn. Torn objects fail the payload checksum on the next read and are
2425
+ * pruned as a miss, so the cost is a recomputed prefix, never wrong state.
2426
+ */
2427
+ export declare function coldCacheDrain(timeoutMs: number): boolean;
2428
+
2429
+ /**
2430
+ * Return a snapshot of the process-wide cold tier. Read-only: never opens
2431
+ * the tier itself, so it reports `enabled: false` until inference first
2432
+ * initializes the tier.
2433
+ *
2434
+ * The source snapshot is DESTRUCTURED rather than field-accessed, so a
2435
+ * counter added to `ColdCacheStats` and forgotten here fails to compile.
2436
+ * The failure this guards is silent and has already happened twice: a native
2437
+ * counter that never reaches this struct reaches no JS consumer either, and
2438
+ * nothing downstream can tell "the counter is zero" from "the counter was
2439
+ * never carried across".
2440
+ */
2441
+ export declare function coldCacheStats(): ColdCacheStats;
2442
+
2443
+ /**
2444
+ * Snapshot of the process-wide SSD cold tier for paged prefix blocks.
2445
+ * Counters are cumulative since the tier was opened; all numeric values
2446
+ * are returned as `f64` to avoid BigInt round-trips in JS.
2447
+ */
2448
+ export interface ColdCacheStats {
2449
+ /**
2450
+ * `false` until the tier is first opened by inference, or when opening
2451
+ * failed (fail-open: inference then runs without persistence).
2452
+ */
2453
+ enabled: boolean;
2454
+ /** Cache root directory (empty while disabled). */
2455
+ root: string;
2456
+ /** Disk quota in bytes. */
2457
+ quotaBytes: number;
2458
+ /** Blocks restored from disk after validation. */
2459
+ hits: number;
2460
+ /** Lookups that found no usable block (includes corrupt entries). */
2461
+ misses: number;
2462
+ /**
2463
+ * Objects accepted onto the background write queue — K/V blocks and
2464
+ * family state sidecars alike, since both take a slot in the same queue.
2465
+ * `coldSidecarEnqueued` counts a subset of this; never sum them.
2466
+ */
2467
+ enqueued: number;
2468
+ /**
2469
+ * Writes REFUSED at admission because the bounded queue was full. Same
2470
+ * object scope as `enqueued`. Disjoint from `writeErrors`, which counts
2471
+ * accepted writes that then failed to land — never sum the two into one
2472
+ * "lost writes" number.
2473
+ */
2474
+ queueDrops: number;
2475
+ /**
2476
+ * Bytes that LANDED, credited after the payload sync, the commit rename
2477
+ * and the directory fsync all succeeded. Not an enqueue-time estimate:
2478
+ * a failed write credits nothing here and one `writeErrors` instead.
2479
+ */
2480
+ bytesWritten: number;
2481
+ /** Total bytes read back on validated hits. */
2482
+ bytesRestored: number;
2483
+ /** Entries evicted to respect the quota / free-space reserve. */
2484
+ evictions: number;
2485
+ /** Entries that failed checksum/identity validation and were removed. */
2486
+ corruptions: number;
2487
+ /**
2488
+ * Writes the queue accepted that never reached disk — a read-only, full
2489
+ * or unmounted cache root, a failed rename, a failed fsync. The writer is
2490
+ * fail-open and reports the error to nobody, so without this a cache that
2491
+ * stores nothing at all still looks perfectly healthy.
2492
+ */
2493
+ writeErrors: number;
2494
+ /**
2495
+ * Restores refused before any block was looked up. Neither a hit nor a
2496
+ * miss — so a refused restore reads as `0/0`, exactly like a turn that
2497
+ * never consulted the tier.
2498
+ */
2499
+ restoreDeclines: number;
2500
+ }
2501
+
2502
+ /**
2503
+ * The native cold-restore allowlist, exposed so a test can assert it agrees
2504
+ * exactly with the TypeScript `COLD_TIER_RESTORE_FAMILIES` set.
2505
+ */
2506
+ export declare function coldRestoreFamilies(): Array<string>;
2507
+
2508
+ /**
2509
+ * Return the process-wide sidecar counters. Unlike [`cold_cache_stats`] this
2510
+ * never consults the tier at all, so it is valid before any inference has run
2511
+ * and reports honestly even when the tier failed to open.
2512
+ *
2513
+ * The plain-Rust [`cold_sidecar_telemetry`] stays as it is: the parity harness
2514
+ * and the in-crate tests destructure `ColdSidecarTelemetry`, and a napi
2515
+ * `#[napi(object)]` return type cannot serve both.
2516
+ */
2517
+ export declare function coldSidecarStats(): ColdSidecarStats;
2518
+
2519
+ /**
2520
+ * Snapshot of [`ColdSidecarTelemetry`] for JS. Counters are cumulative since
2521
+ * process start; all values are `f64` to avoid BigInt round-trips.
2522
+ *
2523
+ * Separate from [`ColdCacheStatsJs`] because this one isolates SIDECARS — the
2524
+ * recurrent and sliding-window state that lives outside the pool — while that
2525
+ * one is scoped to the write queue as a whole. Both structs carry an
2526
+ * `enqueued` and a `queue_drops`, and they are NOT disjoint: a sidecar
2527
+ * admission bumps both, so these are a subset of those and summing them
2528
+ * double-counts. The JS side keeps them apart by prefix (`coldEnqueued` vs
2529
+ * `coldSidecarEnqueued`); read the sidecar pair when you need "did the family
2530
+ * state persist?", the block pair when you need "is the writer keeping up?".
2531
+ */
2532
+ export interface ColdSidecarStats {
2533
+ /**
2534
+ * Turns that reached a family's sidecar capture at all. Every other
2535
+ * counter here is a sub-count of this one, so `captureReached == 0`
2536
+ * separates "the finalize path never calls the capture" from "the capture
2537
+ * ran and declined".
2538
+ */
2539
+ captureReached: number;
2540
+ /**
2541
+ * Turns whose persisted K/V chain covered no whole block, so there was no
2542
+ * prefix to anchor recurrent state under.
2543
+ */
2544
+ chainEmpty: number;
2545
+ /**
2546
+ * Turns whose chain covered blocks but where no retained checkpoint sat at
2547
+ * or below its reach.
2548
+ */
2549
+ boundarySkips: number;
2550
+ /**
2551
+ * Turns that selected a boundary already on disk — nothing written, and
2552
+ * nothing needed to be. The steady state of a repeated prompt, and the
2553
+ * only thing that tells a healthy run from a collapsed ladder.
2554
+ */
2555
+ alreadyPersisted: number;
2556
+ /** Sidecars handed to the bounded writer queue. */
2557
+ enqueued: number;
2558
+ /** Sidecars the bounded writer queue refused because it was full. */
2559
+ queueDrops: number;
2560
+ /**
2561
+ * Restored sidecars a family actually INSTALLED as its live per-turn
2562
+ * state. The one read-side counter, and the only signal that separates
2563
+ * "restored and used" from "restored and silently re-derived by a full
2564
+ * O(prefix) replay" — every other counter, and text parity itself, is
2565
+ * satisfied by the replay.
2566
+ */
2567
+ installed: number;
2568
+ /**
2569
+ * Restores a family THREW AWAY after the walk served them, restarting the
2570
+ * turn cold. Unlike `ColdCacheStats.restoreDeclines` this one comes AFTER
2571
+ * real `coldHits` and `coldBytesRestored`, so the turn looks like it
2572
+ * reused a prefix right up to the point where it recomputed all of it.
2573
+ */
2574
+ restoreSuppressed: number;
2575
+ }
2576
+
2647
2577
  /**
2648
2578
  * Structured completion information aligned with ChatResult.
2649
2579
  * Contains pre-parsed tool calls, thinking, and clean text.
@@ -2945,6 +2875,26 @@ export interface FunctionParameters {
2945
2875
  required?: Array<string>;
2946
2876
  }
2947
2877
 
2878
+ /**
2879
+ * How many GDN prefix checkpoints the native store holds across all owners
2880
+ * ([`GDN_PREFIX_CHECKPOINT_LIMIT`]), exposed for the same reason
2881
+ * [`cold_restore_families`] is: it is one half of a cross-language invariant
2882
+ * and nothing else carries it over the boundary.
2883
+ *
2884
+ * The other half is `MAX_CONCURRENCY` in
2885
+ * `packages/agent/src/extensions/subagent.ts`. The store's demand is
2886
+ * `MAX_CONCURRENCY + 1` — one owner per concurrent child loop plus the root
2887
+ * session — and the cliff sits exactly one owner past the cap, where every
2888
+ * owner holds a single entry and the store is still over it, so each publish
2889
+ * takes somebody's last checkpoint. `retention_sim` measures that as 0 blind
2890
+ * turns at five owners and 28 of 40 at six.
2891
+ *
2892
+ * No Rust gate can see a TypeScript-only edit, so raising the fleet alone
2893
+ * would land in that regime with every Rust gate still green.
2894
+ * `packages/agent/__test__/gdn-checkpoint-capacity.test.ts` is what stops it.
2895
+ */
2896
+ export declare function gdnPrefixCheckpointLimit(): number;
2897
+
2948
2898
  /**
2949
2899
  * Gemma 4 model configuration (dense variant).
2950
2900
  *
@@ -3078,6 +3028,15 @@ export interface Gemma4Config {
3078
3028
  * real Gemma-4-E2B weights.
3079
3029
  */
3080
3030
  useBlockPagedCache?: boolean | undefined;
3031
+ /**
3032
+ * Persist full paged KV blocks — and gemma4's out-of-pool sliding-window
3033
+ * state, as a cold-tier sidecar — to the SSD cold tier so warm prefixes
3034
+ * survive process restarts. Off unless explicitly enabled.
3035
+ *
3036
+ * An EXPLICIT value here is authoritative and beats the ambient
3037
+ * `MLX_PERSIST_PAGED_CACHE` default (`cold_tier::resolve_persist_cold`).
3038
+ */
3039
+ persistPagedCache?: boolean | undefined;
3081
3040
  }
3082
3041
 
3083
3042
  /** Optional load-time settings for [`Gemma4Model::load`]. */
@@ -3321,6 +3280,14 @@ export interface GgufConversionOptions {
3321
3280
  * Forces `group_size = 32` for upgraded layers.
3322
3281
  */
3323
3282
  quantMxfp?: boolean;
3283
+ /**
3284
+ * Import ggml Q4_K / Q5_K / Q6_K tensors as MLX K-quant arrays instead of
3285
+ * rejecting them (default: false). The blocks are repacked, never
3286
+ * dequantized, so the output keeps the source file's weights and byte size.
3287
+ * With this off, Q6_K remains the Gemma4 token-embedding BF16 fallback and
3288
+ * Q4_K / Q5_K are an error.
3289
+ */
3290
+ importKQuants?: boolean;
3324
3291
  }
3325
3292
 
3326
3293
  export interface GgufConversionResult {
@@ -3733,6 +3700,21 @@ export interface ModelConfig {
3733
3700
  eosTokenId: number;
3734
3701
  }
3735
3702
 
3703
+ /**
3704
+ * Ordering policy for structured multimodal content parts handed to a
3705
+ * checkpoint-provided Jinja template.
3706
+ *
3707
+ * The default preserves the generic serializer's existing text-before-media
3708
+ * behavior. PaddleOCR-VL and Qianfan-OCR were trained with image placeholders
3709
+ * before the instruction and opt into
3710
+ * [`MultimodalContentOrder::ImagesThenText`] at their adapter boundaries.
3711
+ * Audio remains after text in both modes.
3712
+ */
3713
+ export declare const enum MultimodalContentOrder {
3714
+ TextThenMedia = 'textThenMedia',
3715
+ ImagesThenText = 'imagesThenText',
3716
+ }
3717
+
3736
3718
  /** Result from document orientation classification. */
3737
3719
  export interface OrientationResult {
3738
3720
  /** Detected rotation angle (0, 90, 180, or 270 degrees) */
@@ -4120,6 +4102,13 @@ export interface Qwen35Config {
4120
4102
  * `Some(true)` for VLM checkpoints (block-paged, set in `parse_config`).
4121
4103
  */
4122
4104
  useBlockPagedCache?: boolean | undefined;
4105
+ /**
4106
+ * Persist the out-of-pool GDN recurrent state (and the paged KV blocks it
4107
+ * gates) to the SSD cold tier so warm prefixes survive process restarts.
4108
+ * Off unless explicitly enabled. See `crate::models::qwen3_5::gdn_sidecar`
4109
+ * and `crate::cold_tier::resolve_persist_cold`.
4110
+ */
4111
+ persistPagedCache?: boolean | undefined;
4123
4112
  /**
4124
4113
  * Number of MTP (Multi-Token Prediction) head layers shipped with the
4125
4114
  * checkpoint. Populated from `mtp_num_hidden_layers` /
@@ -4227,6 +4216,14 @@ export interface Qwen35MoeConfig {
4227
4216
  * Default: `None` / `false`.
4228
4217
  */
4229
4218
  useBlockPagedCache?: boolean | undefined;
4219
+ /**
4220
+ * Persist the out-of-pool GDN recurrent state (and the paged KV blocks it
4221
+ * gates) to the SSD cold tier so warm prefixes survive process restarts.
4222
+ * Off unless explicitly enabled. Shares the dense qwen3_5 GDN sidecar codec
4223
+ * (`crate::models::qwen3_5::gdn_sidecar`) via `to_dense_config`; the
4224
+ * precedence rules live in `crate::cold_tier::resolve_persist_cold`.
4225
+ */
4226
+ persistPagedCache?: boolean | undefined;
4230
4227
  /**
4231
4228
  * Number of MTP (Multi-Token Prediction) head layers shipped with
4232
4229
  * the checkpoint. Populated from `mtp_num_hidden_layers` /
@@ -4294,6 +4291,11 @@ export interface Qwen3Config {
4294
4291
  * Default: true.
4295
4292
  */
4296
4293
  useBlockPagedCache?: boolean | undefined;
4294
+ /**
4295
+ * Persist full paged KV blocks to the SSD cold tier so warm prefixes
4296
+ * survive process restarts. Off unless explicitly enabled.
4297
+ */
4298
+ persistPagedCache?: boolean | undefined;
4297
4299
  }
4298
4300
 
4299
4301
  /** Qwen3 language model configuration */
@@ -4750,51 +4752,32 @@ export interface VisionConfig {
4750
4752
  spatialMergeSize: number;
4751
4753
  }
4752
4754
 
4753
- /** A batch item for VLM batch inference */
4754
4755
  export interface VlmBatchItem {
4755
- /** Chat messages for this item */
4756
4756
  messages: Array<VlmChatMessage>;
4757
- /** Encoded image buffers for this item (one image per item for OCR) */
4758
4757
  images?: Array<Buffer>;
4759
4758
  }
4760
4759
 
4761
- /** Configuration for VLM chat */
4762
4760
  export interface VlmChatConfig {
4763
- /** Encoded image buffers to process (PNG/JPEG bytes) */
4764
4761
  images?: Array<Buffer>;
4765
- /** Maximum number of new tokens to generate (default: 512) */
4766
4762
  maxNewTokens?: number;
4767
- /** Sampling temperature (0 = greedy, higher = more random) (default: 0.0 for OCR) */
4768
4763
  temperature?: number;
4769
- /** Top-k sampling (default: 0) */
4770
4764
  topK?: number;
4771
- /** Top-p (nucleus) sampling (default: 1.0) */
4772
4765
  topP?: number;
4773
- /** Repetition penalty (default: 1.5) */
4774
4766
  repetitionPenalty?: number;
4775
- /**
4776
- * Presence penalty (0.0 = disabled). Subtracts a flat penalty from logits of any
4777
- * token that appeared at least once in context. Matches OpenAI API semantics.
4778
- */
4779
4767
  presencePenalty?: number;
4780
- /** Number of recent tokens to consider for presence penalty (default: 20) */
4781
4768
  presenceContextSize?: number;
4782
- /**
4783
- * Frequency penalty (0.0 = disabled). Subtracts penalty * occurrence_count from
4784
- * logits of each token in context. Matches OpenAI API semantics.
4785
- */
4786
4769
  frequencyPenalty?: number;
4787
- /** Number of recent tokens to consider for frequency penalty (default: 20) */
4788
4770
  frequencyContextSize?: number;
4789
- /** Whether to return log probabilities (default: false) */
4790
4771
  returnLogprobs?: boolean;
4791
4772
  }
4792
4773
 
4793
- /** A chat message with optional image */
4774
+ /**
4775
+ * A chat message with textual content. Images are supplied through
4776
+ * [`VLMChatConfig`] and attached to the first user content part before the
4777
+ * model template is rendered.
4778
+ */
4794
4779
  export interface VlmChatMessage {
4795
- /** Role of the message sender */
4796
4780
  role: ChatRole;
4797
- /** Text content of the message */
4798
4781
  content: string;
4799
4782
  }
4800
4783