@qvac/sdk 0.10.1 → 0.11.0

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 (201) hide show
  1. package/NOTICE +92 -54
  2. package/dist/client/api/cancel.d.ts +24 -32
  3. package/dist/client/api/cancel.js +58 -31
  4. package/dist/client/api/client-request-id.d.ts +17 -0
  5. package/dist/client/api/client-request-id.js +27 -0
  6. package/dist/client/api/completion-stream.d.ts +1 -1
  7. package/dist/client/api/completion-stream.js +34 -3
  8. package/dist/client/api/diffusion.d.ts +46 -0
  9. package/dist/client/api/diffusion.js +50 -1
  10. package/dist/client/api/download-asset.d.ts +12 -2
  11. package/dist/client/api/download-asset.js +21 -3
  12. package/dist/client/api/embed.d.ts +8 -4
  13. package/dist/client/api/embed.js +14 -1
  14. package/dist/client/api/get-model-info.d.ts +1 -1
  15. package/dist/client/api/index.d.ts +1 -0
  16. package/dist/client/api/index.js +1 -0
  17. package/dist/client/api/load-model.d.ts +12 -4
  18. package/dist/client/api/load-model.js +25 -1
  19. package/dist/client/api/rag.d.ts +9 -3
  20. package/dist/client/api/rag.js +23 -3
  21. package/dist/client/api/transcribe.d.ts +20 -8
  22. package/dist/client/api/transcribe.js +58 -4
  23. package/dist/client/api/unload-model.d.ts +5 -2
  24. package/dist/client/api/unload-model.js +9 -4
  25. package/dist/client/api/upscale.d.ts +45 -0
  26. package/dist/client/api/upscale.js +99 -0
  27. package/dist/client/rpc/expo-rpc-client.js +2 -1
  28. package/dist/client/rpc/node-rpc-client.js +18 -7
  29. package/dist/client/rpc/rpc-client.d.ts +2 -2
  30. package/dist/client/rpc/rpc-client.js +10 -4
  31. package/dist/client/rpc/rpc-error.d.ts +9 -0
  32. package/dist/client/rpc/rpc-error.js +92 -0
  33. package/dist/examples/cancel-by-request-id.d.ts +38 -0
  34. package/dist/examples/cancel-by-request-id.js +103 -0
  35. package/dist/examples/diffusion-esrgan-upscale.d.ts +2 -0
  36. package/dist/examples/diffusion-esrgan-upscale.js +78 -0
  37. package/dist/examples/download-with-cancel.js +10 -10
  38. package/dist/examples/embed-multi-gpu.d.ts +2 -0
  39. package/dist/examples/embed-multi-gpu.js +46 -0
  40. package/dist/examples/llamacpp-multi-gpu.d.ts +2 -0
  41. package/dist/examples/llamacpp-multi-gpu.js +57 -0
  42. package/dist/examples/rag/rag-hyperdb/cancellation.js +20 -17
  43. package/dist/examples/tools/llamacpp-tools-gemma4.d.ts +2 -0
  44. package/dist/examples/tools/llamacpp-tools-gemma4.js +76 -0
  45. package/dist/examples/tools/llamacpp-tools-qwen35.d.ts +2 -0
  46. package/dist/examples/tools/llamacpp-tools-qwen35.js +70 -0
  47. package/dist/examples/transcription/whispercpp-microphone-conversation.d.ts +2 -0
  48. package/dist/examples/transcription/whispercpp-microphone-conversation.js +102 -0
  49. package/dist/examples/tts/supertonic.js +1 -1
  50. package/dist/expo/plugins/withMobileBundle.d.ts +20 -4
  51. package/dist/expo/plugins/withMobileBundle.js +44 -6
  52. package/dist/index.d.ts +5 -2
  53. package/dist/index.js +21 -1
  54. package/dist/models/registry/models.d.ts +186 -318
  55. package/dist/models/registry/models.js +1522 -1667
  56. package/dist/pear/pre.d.ts +3 -0
  57. package/dist/pear/pre.js +3 -0
  58. package/dist/schemas/cancel.d.ts +167 -28
  59. package/dist/schemas/cancel.js +119 -19
  60. package/dist/schemas/common.d.ts +427 -208
  61. package/dist/schemas/common.js +3 -1
  62. package/dist/schemas/completion-event.d.ts +14 -0
  63. package/dist/schemas/completion-event.js +11 -1
  64. package/dist/schemas/completion-stream.d.ts +15 -1
  65. package/dist/schemas/completion-stream.js +25 -7
  66. package/dist/schemas/download-asset.d.ts +15 -9
  67. package/dist/schemas/download-asset.js +17 -6
  68. package/dist/schemas/embed.d.ts +1 -0
  69. package/dist/schemas/embed.js +5 -0
  70. package/dist/schemas/error.d.ts +31 -0
  71. package/dist/schemas/error.js +29 -1
  72. package/dist/schemas/finetune.d.ts +2 -0
  73. package/dist/schemas/finetune.js +5 -0
  74. package/dist/schemas/get-model-info.d.ts +6 -6
  75. package/dist/schemas/llamacpp-config.d.ts +54 -8
  76. package/dist/schemas/llamacpp-config.js +12 -0
  77. package/dist/schemas/load-model.d.ts +1911 -1260
  78. package/dist/schemas/load-model.js +15 -0
  79. package/dist/schemas/model-src-utils.d.ts +14 -14
  80. package/dist/schemas/model-types.d.ts +10 -10
  81. package/dist/schemas/ocr.d.ts +3 -3
  82. package/dist/schemas/plugin.d.ts +40 -1
  83. package/dist/schemas/plugin.js +10 -1
  84. package/dist/schemas/rag.d.ts +9 -0
  85. package/dist/schemas/rag.js +17 -9
  86. package/dist/schemas/registry.d.ts +30 -30
  87. package/dist/schemas/reload-config.d.ts +14 -14
  88. package/dist/schemas/sdcpp-config.d.ts +131 -17
  89. package/dist/schemas/sdcpp-config.js +163 -1
  90. package/dist/schemas/sdk-config.d.ts +649 -271
  91. package/dist/schemas/sdk-errors-client.d.ts +1 -0
  92. package/dist/schemas/sdk-errors-client.js +5 -0
  93. package/dist/schemas/sdk-errors-server.d.ts +5 -0
  94. package/dist/schemas/sdk-errors-server.js +25 -0
  95. package/dist/schemas/text-to-speech.d.ts +78 -78
  96. package/dist/schemas/transcription-config.d.ts +24 -24
  97. package/dist/schemas/transcription.d.ts +56 -2
  98. package/dist/schemas/transcription.js +27 -0
  99. package/dist/schemas/translate.d.ts +2 -0
  100. package/dist/schemas/translate.js +13 -2
  101. package/dist/schemas/translation-config.d.ts +40 -40
  102. package/dist/schemas/unload-model.d.ts +2 -1
  103. package/dist/schemas/unload-model.js +4 -1
  104. package/dist/scripts/run-unit-tests.js +15 -2
  105. package/dist/server/bare/delegate-rpc-client.js +10 -8
  106. package/dist/server/bare/ops/cancel.d.ts +27 -1
  107. package/dist/server/bare/ops/cancel.js +36 -10
  108. package/dist/server/bare/ops/embed.d.ts +3 -3
  109. package/dist/server/bare/ops/embed.js +144 -28
  110. package/dist/server/bare/ops/kv-cache-utils.d.ts +0 -7
  111. package/dist/server/bare/ops/kv-cache-utils.js +8 -47
  112. package/dist/server/bare/ops/transcribe.d.ts +10 -5
  113. package/dist/server/bare/ops/transcribe.js +196 -41
  114. package/dist/server/bare/ops/translate.d.ts +1 -1
  115. package/dist/server/bare/ops/translate.js +197 -88
  116. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +12 -3
  117. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +170 -172
  118. package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +125 -21
  119. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.d.ts +154 -0
  120. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.js +448 -0
  121. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.d.ts +21 -73
  122. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.js +18 -101
  123. package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +30 -3
  124. package/dist/server/bare/plugins/llamacpp-completion/plugin.js +157 -85
  125. package/dist/server/bare/plugins/llamacpp-completion/transform.d.ts +10 -0
  126. package/dist/server/bare/plugins/llamacpp-completion/transform.js +34 -0
  127. package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +7 -0
  128. package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +10 -1
  129. package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +17 -15
  130. package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +5 -1
  131. package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +3 -3
  132. package/dist/server/bare/plugins/onnx-ocr/plugin.js +3 -0
  133. package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +39 -39
  134. package/dist/server/bare/plugins/onnx-tts/plugin.js +6 -0
  135. package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +29 -21
  136. package/dist/server/bare/plugins/parakeet-transcription/plugin.js +4 -1
  137. package/dist/server/bare/plugins/sdcpp-generation/ops/diffusion.js +8 -0
  138. package/dist/server/bare/plugins/sdcpp-generation/ops/upscale.d.ts +3 -0
  139. package/dist/server/bare/plugins/sdcpp-generation/ops/upscale.js +40 -0
  140. package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +91 -16
  141. package/dist/server/bare/plugins/sdcpp-generation/plugin.js +97 -7
  142. package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +22 -3
  143. package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +50 -14
  144. package/dist/server/bare/rag-hyperdb/index.d.ts +1 -1
  145. package/dist/server/bare/rag-hyperdb/index.js +1 -1
  146. package/dist/server/bare/rag-hyperdb/rag-operation-manager.d.ts +26 -4
  147. package/dist/server/bare/rag-hyperdb/rag-operation-manager.js +65 -28
  148. package/dist/server/bare/runtime/disposable-scope.d.ts +36 -0
  149. package/dist/server/bare/runtime/disposable-scope.js +69 -0
  150. package/dist/server/bare/runtime/index.d.ts +8 -0
  151. package/dist/server/bare/runtime/index.js +3 -0
  152. package/dist/server/bare/runtime/request-context.d.ts +49 -0
  153. package/dist/server/bare/runtime/request-context.js +1 -0
  154. package/dist/server/bare/runtime/request-id.d.ts +12 -0
  155. package/dist/server/bare/runtime/request-id.js +14 -0
  156. package/dist/server/bare/runtime/request-registry-singleton.d.ts +4 -0
  157. package/dist/server/bare/runtime/request-registry-singleton.js +27 -0
  158. package/dist/server/bare/runtime/request-registry.d.ts +136 -0
  159. package/dist/server/bare/runtime/request-registry.js +297 -0
  160. package/dist/server/bare/runtime/with-request-context.d.ts +22 -0
  161. package/dist/server/bare/runtime/with-request-context.js +53 -0
  162. package/dist/server/bare/types/addon-responses.d.ts +11 -1
  163. package/dist/server/rpc/handler-registry.js +26 -7
  164. package/dist/server/rpc/handlers/cancel-delegated.js +28 -31
  165. package/dist/server/rpc/handlers/cancelHandler.d.ts +30 -1
  166. package/dist/server/rpc/handlers/cancelHandler.js +54 -22
  167. package/dist/server/rpc/handlers/delete-cache.d.ts +9 -0
  168. package/dist/server/rpc/handlers/delete-cache.js +13 -15
  169. package/dist/server/rpc/handlers/download-asset.js +133 -40
  170. package/dist/server/rpc/handlers/index.d.ts +2 -0
  171. package/dist/server/rpc/handlers/index.js +2 -0
  172. package/dist/server/rpc/handlers/load-model/download-manager.d.ts +60 -2
  173. package/dist/server/rpc/handlers/load-model/download-manager.js +141 -8
  174. package/dist/server/rpc/handlers/load-model/handler.js +213 -98
  175. package/dist/server/rpc/handlers/load-model/http.js +3 -3
  176. package/dist/server/rpc/handlers/load-model/hyperdrive.js +1 -1
  177. package/dist/server/rpc/handlers/load-model/registry.js +1 -1
  178. package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +20 -1
  179. package/dist/server/rpc/handlers/load-model/resolve-session.js +6 -5
  180. package/dist/server/rpc/handlers/load-model/resolve.d.ts +3 -2
  181. package/dist/server/rpc/handlers/load-model/resolve.js +9 -6
  182. package/dist/server/rpc/handlers/load-model/types.d.ts +24 -0
  183. package/dist/server/rpc/handlers/rag.js +133 -15
  184. package/dist/server/rpc/handlers/upscale-stream.d.ts +3 -0
  185. package/dist/server/rpc/handlers/upscale-stream.js +4 -0
  186. package/dist/server/utils/completion-normalizer.js +8 -0
  187. package/dist/server/utils/tools/dialect.js +4 -0
  188. package/dist/server/utils/tools/parser.js +16 -1
  189. package/dist/server/utils/tools/parsers/gemma4native.d.ts +4 -0
  190. package/dist/server/utils/tools/parsers/gemma4native.js +60 -0
  191. package/dist/server/utils/tools/parsers/qwen35.d.ts +4 -0
  192. package/dist/server/utils/tools/parsers/qwen35.js +120 -0
  193. package/dist/utils/aggregate-events.d.ts +9 -0
  194. package/dist/utils/aggregate-events.js +18 -3
  195. package/dist/utils/decorate-promise.d.ts +34 -0
  196. package/dist/utils/decorate-promise.js +35 -0
  197. package/dist/utils/errors-client.d.ts +3 -0
  198. package/dist/utils/errors-client.js +5 -0
  199. package/dist/utils/errors-server.d.ts +85 -0
  200. package/dist/utils/errors-server.js +97 -0
  201. package/package.json +20 -15
@@ -1,73 +1,19 @@
1
1
  /**
2
- * Pure state + decision helpers for kv-cache bookkeeping used by
3
- * `completion-stream.ts`.
2
+ * Pure decision helper for kv-cache history slicing used by
3
+ * `completion-stream.ts` (via the slice computed from
4
+ * `TurnHandle.savedCount`).
4
5
  *
5
6
  * This module intentionally has **no** `bare-*` imports so it can be
6
- * exercised directly from unit tests running under `bun` without pulling
7
- * in the Bare runtime (which is not available in that environment).
8
- * The file-system-dependent pieces (e.g. `recordCacheSaveCount`) live in
9
- * `completion-stream.ts` and consume the state exported here.
10
- */
11
- /**
12
- * Number of chat messages the kv-cache file on disk is known to cover, keyed
13
- * by cache path. Written after a successful completion records a save, read
14
- * by `prepareMessagesForCache` to slice the history on the next turn so a
15
- * consumer can stage multiple messages between completions (e.g. an
16
- * `[assistant, user]` recovery sequence) without resending the whole history.
17
- *
18
- * INVARIANT: an entry is only present if the corresponding kv-cache file is
19
- * considered trustworthy. On any turn where the SDK cannot prove the saved
20
- * count reflects the on-disk state (cancellation mid-decode, zero-token
21
- * reply, cache file missing after a save attempt), the entry MUST be
22
- * deleted; a stale entry causes the next turn to slice its history down to
23
- * an empty payload and the model returns zero tokens.
24
- *
25
- * Mode notes:
26
- * - Static-mode turns are the only readers of this map. `clearStaleCount`
27
- * in `decideCachedHistorySlice` exists so a stale-but-non-zero entry
28
- * can be detected and dropped on the next read.
29
- * - Dynamic-mode turns do NOT consume this map — the addon trims tools
30
- * and the chain output from the kv-cache after each round, so the
31
- * SDK falls back to role-based dispatch in `prepareMessagesForCache`.
32
- * Writes still happen on dynamic-mode turns, but the recorded count
33
- * reflects the messages the SDK shipped, not the (possibly trimmed)
34
- * on-disk cache shape. The map is internally consistent within a
35
- * single mode; a `kvCache` key should not be reused across modes.
36
- */
37
- export declare const cachedMessageCounts: Map<string, number>;
38
- /**
39
- * Clear bookkeeping entries. With no argument, clears the whole map. With a
40
- * `prefix`, removes any entry whose path is equal to it OR sits beneath it
41
- * as a directory (i.e. `key.startsWith(prefix + sep)`).
7
+ * exercised directly from unit tests running under `bun` without
8
+ * pulling in the Bare runtime (which is not available in that
9
+ * environment).
42
10
  *
43
- * Runtime callers MUST pass the platform path separator (e.g. `path.sep`
44
- * from `bare-path`) so directory-prefix matches are correct on every
45
- * target. The "/" default exists only for unit tests under `bun`, where
46
- * cache paths are POSIX-shaped and importing `bare-path` would pull in
47
- * the Bare runtime. The exported wrapper in `completion-stream.ts`
48
- * injects the real separator for in-process use.
49
- */
50
- export declare function clearCachedMessageCounts(prefix?: string, sep?: string): void;
51
- /**
52
- * Called from `server/bare/ops/cancel.ts` right before `addon.cancel()` so
53
- * that the in-flight `completion()` for this model can detect that its
54
- * current turn is being cancelled and skip poisoning `cachedMessageCounts`
55
- * with a `history.length + 1` entry that does not correspond to a real
56
- * assistant reply.
57
- */
58
- export declare function noteCancelRequested(modelId: string): void;
59
- export declare function snapshotCancelCount(modelId: string): number;
60
- /** Test-only. */
61
- export declare function _resetCancelCountersForTest(): void;
62
- /**
63
- * A completion's `savedCount` should only be recorded when the turn ran to
64
- * completion AND produced at least one token. Any other outcome —
65
- * cancelled mid-decode, legitimate zero-token reply, or an early EOS — must
66
- * clear the entry instead, because there is no guarantee that the kv-cache
67
- * file on disk matches the `history.length + 1` boundary the SDK would
68
- * otherwise record.
11
+ * Cancel detection flows through the per-request `AbortSignal` from
12
+ * `RequestRegistry`; the `cachedMessageCounts` map lives in
13
+ * `kv-cache-session.ts`, which owns all three KV-cache bookkeeping
14
+ * layers (saved counts, init flags, on-disk files). Only the pure
15
+ * slice-decision helper remains here.
69
16
  */
70
- export declare function shouldRecordSavedCount(wasCancelled: boolean, producedTokens: boolean): boolean;
71
17
  export interface HistoryMessage {
72
18
  role: string;
73
19
  content: string;
@@ -80,8 +26,9 @@ export interface HistorySliceDecision {
80
26
  messages: HistoryMessage[];
81
27
  /**
82
28
  * True when the decision path proves the current `savedCount` is stale
83
- * and the caller should `cachedMessageCounts.delete(cachePath)` to avoid
84
- * propagating the bad count to the next turn.
29
+ * and the caller should drop the cached entry (via
30
+ * `KvCacheSession.dropStaleSavedCount(turn)`) to avoid propagating the
31
+ * bad count to the next turn.
85
32
  */
86
33
  clearStaleCount: boolean;
87
34
  }
@@ -89,13 +36,14 @@ export interface HistorySliceDecision {
89
36
  * Pure slice decision for `prepareMessagesForCache`.
90
37
  *
91
38
  * Mirrors the shape of the logic in `completion-stream.ts` but without
92
- * calling `transformMessages` (which depends on `bare-fs` for attachment
93
- * probing). Kept here so the decision can be unit-tested in isolation.
39
+ * calling `transformMessages` (which depends on `bare-fs` for
40
+ * attachment probing). Kept here so the decision can be unit-tested in
41
+ * isolation.
94
42
  *
95
- * The key regression guard: when a non-zero `savedCount` would slice the
96
- * history down to an empty array, it is treated as stale — the caller
97
- * falls back to sending the system-stripped full history rather than
98
- * handing the model an empty payload.
43
+ * The key regression guard: when a non-zero `savedCount` would slice
44
+ * the history down to an empty array, it is treated as stale — the
45
+ * caller falls back to sending the system-stripped full history rather
46
+ * than handing the model an empty payload.
99
47
  */
100
48
  export declare function decideCachedHistorySlice(savedCount: number, cacheExists: boolean, history: HistoryMessage[]): HistorySliceDecision;
101
49
  //# sourceMappingURL=kv-cache-state.d.ts.map
@@ -1,114 +1,31 @@
1
1
  /**
2
- * Pure state + decision helpers for kv-cache bookkeeping used by
3
- * `completion-stream.ts`.
2
+ * Pure decision helper for kv-cache history slicing used by
3
+ * `completion-stream.ts` (via the slice computed from
4
+ * `TurnHandle.savedCount`).
4
5
  *
5
6
  * This module intentionally has **no** `bare-*` imports so it can be
6
- * exercised directly from unit tests running under `bun` without pulling
7
- * in the Bare runtime (which is not available in that environment).
8
- * The file-system-dependent pieces (e.g. `recordCacheSaveCount`) live in
9
- * `completion-stream.ts` and consume the state exported here.
10
- */
11
- /**
12
- * Number of chat messages the kv-cache file on disk is known to cover, keyed
13
- * by cache path. Written after a successful completion records a save, read
14
- * by `prepareMessagesForCache` to slice the history on the next turn so a
15
- * consumer can stage multiple messages between completions (e.g. an
16
- * `[assistant, user]` recovery sequence) without resending the whole history.
17
- *
18
- * INVARIANT: an entry is only present if the corresponding kv-cache file is
19
- * considered trustworthy. On any turn where the SDK cannot prove the saved
20
- * count reflects the on-disk state (cancellation mid-decode, zero-token
21
- * reply, cache file missing after a save attempt), the entry MUST be
22
- * deleted; a stale entry causes the next turn to slice its history down to
23
- * an empty payload and the model returns zero tokens.
24
- *
25
- * Mode notes:
26
- * - Static-mode turns are the only readers of this map. `clearStaleCount`
27
- * in `decideCachedHistorySlice` exists so a stale-but-non-zero entry
28
- * can be detected and dropped on the next read.
29
- * - Dynamic-mode turns do NOT consume this map — the addon trims tools
30
- * and the chain output from the kv-cache after each round, so the
31
- * SDK falls back to role-based dispatch in `prepareMessagesForCache`.
32
- * Writes still happen on dynamic-mode turns, but the recorded count
33
- * reflects the messages the SDK shipped, not the (possibly trimmed)
34
- * on-disk cache shape. The map is internally consistent within a
35
- * single mode; a `kvCache` key should not be reused across modes.
36
- */
37
- export const cachedMessageCounts = new Map();
38
- /**
39
- * Monotonic counter of cancel requests per model. `completion()` snapshots
40
- * the value before running the model and compares afterward to decide
41
- * whether the turn was cancelled mid-decode. A counter (not a boolean) is
42
- * used deliberately: a cancel that lands near a turn boundary cannot bleed
43
- * into the next turn because we always compare snapshots, not values.
44
- */
45
- const modelCancelCounters = new Map();
46
- /**
47
- * Clear bookkeeping entries. With no argument, clears the whole map. With a
48
- * `prefix`, removes any entry whose path is equal to it OR sits beneath it
49
- * as a directory (i.e. `key.startsWith(prefix + sep)`).
7
+ * exercised directly from unit tests running under `bun` without
8
+ * pulling in the Bare runtime (which is not available in that
9
+ * environment).
50
10
  *
51
- * Runtime callers MUST pass the platform path separator (e.g. `path.sep`
52
- * from `bare-path`) so directory-prefix matches are correct on every
53
- * target. The "/" default exists only for unit tests under `bun`, where
54
- * cache paths are POSIX-shaped and importing `bare-path` would pull in
55
- * the Bare runtime. The exported wrapper in `completion-stream.ts`
56
- * injects the real separator for in-process use.
11
+ * Cancel detection flows through the per-request `AbortSignal` from
12
+ * `RequestRegistry`; the `cachedMessageCounts` map lives in
13
+ * `kv-cache-session.ts`, which owns all three KV-cache bookkeeping
14
+ * layers (saved counts, init flags, on-disk files). Only the pure
15
+ * slice-decision helper remains here.
57
16
  */
58
- export function clearCachedMessageCounts(prefix, sep = "/") {
59
- if (!prefix) {
60
- cachedMessageCounts.clear();
61
- return;
62
- }
63
- for (const key of cachedMessageCounts.keys()) {
64
- if (key === prefix) {
65
- cachedMessageCounts.delete(key);
66
- continue;
67
- }
68
- if (!key.startsWith(prefix + sep))
69
- continue;
70
- cachedMessageCounts.delete(key);
71
- }
72
- }
73
- /**
74
- * Called from `server/bare/ops/cancel.ts` right before `addon.cancel()` so
75
- * that the in-flight `completion()` for this model can detect that its
76
- * current turn is being cancelled and skip poisoning `cachedMessageCounts`
77
- * with a `history.length + 1` entry that does not correspond to a real
78
- * assistant reply.
79
- */
80
- export function noteCancelRequested(modelId) {
81
- modelCancelCounters.set(modelId, (modelCancelCounters.get(modelId) ?? 0) + 1);
82
- }
83
- export function snapshotCancelCount(modelId) {
84
- return modelCancelCounters.get(modelId) ?? 0;
85
- }
86
- /** Test-only. */
87
- export function _resetCancelCountersForTest() {
88
- modelCancelCounters.clear();
89
- }
90
- /**
91
- * A completion's `savedCount` should only be recorded when the turn ran to
92
- * completion AND produced at least one token. Any other outcome —
93
- * cancelled mid-decode, legitimate zero-token reply, or an early EOS — must
94
- * clear the entry instead, because there is no guarantee that the kv-cache
95
- * file on disk matches the `history.length + 1` boundary the SDK would
96
- * otherwise record.
97
- */
98
- export function shouldRecordSavedCount(wasCancelled, producedTokens) {
99
- return !wasCancelled && producedTokens;
100
- }
101
17
  /**
102
18
  * Pure slice decision for `prepareMessagesForCache`.
103
19
  *
104
20
  * Mirrors the shape of the logic in `completion-stream.ts` but without
105
- * calling `transformMessages` (which depends on `bare-fs` for attachment
106
- * probing). Kept here so the decision can be unit-tested in isolation.
21
+ * calling `transformMessages` (which depends on `bare-fs` for
22
+ * attachment probing). Kept here so the decision can be unit-tested in
23
+ * isolation.
107
24
  *
108
- * The key regression guard: when a non-zero `savedCount` would slice the
109
- * history down to an empty array, it is treated as stale — the caller
110
- * falls back to sending the system-stripped full history rather than
111
- * handing the model an empty payload.
25
+ * The key regression guard: when a non-zero `savedCount` would slice
26
+ * the history down to an empty array, it is treated as stale — the
27
+ * caller falls back to sending the system-stripped full history rather
28
+ * than handing the model an empty payload.
112
29
  */
113
30
  export function decideCachedHistorySlice(savedCount, cacheExists, history) {
114
31
  if (!cacheExists || history.length === 0) {
@@ -28,7 +28,18 @@ export declare const llmPlugin: {
28
28
  }>>;
29
29
  "cache-type-k": import("zod").ZodOptional<import("zod").ZodString>;
30
30
  "cache-type-v": import("zod").ZodOptional<import("zod").ZodString>;
31
+ "main-gpu": import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodNumber, import("zod").ZodEnum<{
32
+ integrated: "integrated";
33
+ dedicated: "dedicated";
34
+ }>]>>;
35
+ "split-mode": import("zod").ZodOptional<import("zod").ZodEnum<{
36
+ none: "none";
37
+ layer: "layer";
38
+ row: "row";
39
+ }>>;
40
+ "tensor-split": import("zod").ZodOptional<import("zod").ZodString>;
31
41
  openclCacheDir: import("zod").ZodOptional<import("zod").ZodString>;
42
+ reasoning_budget: import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodLiteral<-1>, import("zod").ZodLiteral<0>]>>;
32
43
  projectionModelSrc: import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodString, import("zod").ZodObject<{
33
44
  src: import("zod").ZodString;
34
45
  name: import("zod").ZodOptional<import("zod").ZodString>;
@@ -42,6 +53,9 @@ export declare const llmPlugin: {
42
53
  sha256Checksum: import("zod").ZodOptional<import("zod").ZodString>;
43
54
  addon: import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodEnum<{
44
55
  embeddings: "embeddings";
56
+ diffusion: "diffusion";
57
+ tts: "tts";
58
+ ocr: "ocr";
45
59
  "llamacpp-completion": "llamacpp-completion";
46
60
  "whispercpp-transcription": "whispercpp-transcription";
47
61
  "llamacpp-embedding": "llamacpp-embedding";
@@ -54,9 +68,6 @@ export declare const llmPlugin: {
54
68
  whisper: "whisper";
55
69
  nmt: "nmt";
56
70
  parakeet: "parakeet";
57
- tts: "tts";
58
- ocr: "ocr";
59
- diffusion: "diffusion";
60
71
  }>, import("zod").ZodLiteral<"vad">]>>;
61
72
  }, import("zod/v4/core").$strip>]>>;
62
73
  }, import("zod/v4/core").$strip>;
@@ -83,7 +94,11 @@ export declare const llmPlugin: {
83
94
  toolsMode?: "static" | "dynamic" | undefined;
84
95
  "cache-type-k"?: string | undefined;
85
96
  "cache-type-v"?: string | undefined;
97
+ "main-gpu"?: number | "integrated" | "dedicated" | undefined;
98
+ "split-mode"?: "none" | "layer" | "row" | undefined;
99
+ "tensor-split"?: string | undefined;
86
100
  openclCacheDir?: string | undefined;
101
+ reasoning_budget?: 0 | -1 | undefined;
87
102
  };
88
103
  artifacts?: never;
89
104
  } | {
@@ -109,7 +124,11 @@ export declare const llmPlugin: {
109
124
  toolsMode?: "static" | "dynamic" | undefined;
110
125
  "cache-type-k"?: string | undefined;
111
126
  "cache-type-v"?: string | undefined;
127
+ "main-gpu"?: number | "integrated" | "dedicated" | undefined;
128
+ "split-mode"?: "none" | "layer" | "row" | undefined;
129
+ "tensor-split"?: string | undefined;
112
130
  openclCacheDir?: string | undefined;
131
+ reasoning_budget?: 0 | -1 | undefined;
113
132
  };
114
133
  artifacts: {
115
134
  projectionModelPath: string;
@@ -158,6 +177,7 @@ export declare const llmPlugin: {
158
177
  frequency_penalty: import("zod").ZodOptional<import("zod").ZodNumber>;
159
178
  presence_penalty: import("zod").ZodOptional<import("zod").ZodNumber>;
160
179
  repeat_penalty: import("zod").ZodOptional<import("zod").ZodNumber>;
180
+ reasoning_budget: import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodLiteral<-1>, import("zod").ZodLiteral<0>]>>;
161
181
  }, import("zod/v4/core").$strict>>;
162
182
  captureThinking: import("zod").ZodOptional<import("zod").ZodBoolean>;
163
183
  emitRawDeltas: import("zod").ZodOptional<import("zod").ZodBoolean>;
@@ -166,6 +186,8 @@ export declare const llmPlugin: {
166
186
  pythonic: "pythonic";
167
187
  json: "json";
168
188
  harmony: "harmony";
189
+ qwen35: "qwen35";
190
+ gemma4: "gemma4";
169
191
  }>>;
170
192
  responseFormat: import("zod").ZodOptional<import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
171
193
  type: import("zod").ZodLiteral<"text">;
@@ -180,6 +202,7 @@ export declare const llmPlugin: {
180
202
  strict: import("zod").ZodOptional<import("zod").ZodBoolean>;
181
203
  }, import("zod/v4/core").$strict>;
182
204
  }, import("zod/v4/core").$strict>], "type">>;
205
+ requestId: import("zod").ZodOptional<import("zod").ZodString>;
183
206
  type: import("zod").ZodLiteral<"completionStream">;
184
207
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
185
208
  type: import("zod").ZodLiteral<"completionStream">;
@@ -244,6 +267,7 @@ export declare const llmPlugin: {
244
267
  type: import("zod").ZodLiteral<"completionDone">;
245
268
  seq: import("zod").ZodNumber;
246
269
  stopReason: import("zod").ZodOptional<import("zod").ZodEnum<{
270
+ cancelled: "cancelled";
247
271
  eos: "eos";
248
272
  length: "length";
249
273
  stopSequence: "stopSequence";
@@ -315,6 +339,7 @@ export declare const llmPlugin: {
315
339
  resume: "resume";
316
340
  }>>;
317
341
  withProgress: import("zod").ZodOptional<import("zod").ZodBoolean>;
342
+ requestId: import("zod").ZodOptional<import("zod").ZodString>;
318
343
  }, import("zod/v4/core").$strict>, import("zod").ZodObject<{
319
344
  modelId: import("zod").ZodString;
320
345
  options: import("zod").ZodObject<{
@@ -412,6 +437,7 @@ export declare const llmPlugin: {
412
437
  nmt: "nmt";
413
438
  }>;
414
439
  type: import("zod").ZodLiteral<"translate">;
440
+ requestId: import("zod").ZodOptional<import("zod").ZodString>;
415
441
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
416
442
  modelId: import("zod").ZodString;
417
443
  text: import("zod").ZodString;
@@ -424,6 +450,7 @@ export declare const llmPlugin: {
424
450
  to: import("zod").ZodString;
425
451
  context: import("zod").ZodOptional<import("zod").ZodString>;
426
452
  type: import("zod").ZodLiteral<"translate">;
453
+ requestId: import("zod").ZodOptional<import("zod").ZodString>;
427
454
  }, import("zod/v4/core").$strip>]>, import("zod").ZodObject<{
428
455
  type: import("zod").ZodLiteral<"translate">;
429
456
  token: import("zod").ZodString;
@@ -1,41 +1,71 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
1
53
  import LlmLlamacpp from "@qvac/llm-llamacpp";
2
54
  import llmAddonLogging from "@qvac/llm-llamacpp/addonLogging.js";
3
- import { definePlugin, defineHandler, finetuneRequestSchema, completionStreamRequestSchema, completionStreamResponseSchema, finetuneResponseSchema, translateRequestSchema, translateResponseSchema, ModelType, llmConfigBaseSchema, ADDON_LLM, TOOLS_MODE, } from "../../../../schemas/index.js";
55
+ import { definePlugin, defineHandler, finetuneRequestSchema, completionStreamRequestSchema, completionStreamResponseSchema, finetuneResponseSchema, translateRequestSchema, translateResponseSchema, ModelType, llmConfigBaseSchema, ADDON_LLM, } from "../../../../schemas/index.js";
4
56
  import { createStreamLogger, registerAddonLogger } from "../../../../logging/index.js";
5
57
  import { expandGGUFIntoShards } from "../../../utils/index.js";
6
58
  import { completion } from "./ops/completion-stream.js";
7
59
  import { finetune } from "./ops/finetune.js";
8
60
  import { translate } from "../../ops/translate.js";
61
+ import { transformLlmConfig } from "./transform.js";
9
62
  import { attachModelExecutionMs } from "../../../../profiling/model-execution.js";
10
63
  import { getModelConfig } from "../../registry/model-registry.js";
11
64
  import { createCompletionNormalizer } from "../../../utils/completion-normalizer.js";
12
65
  import { detectToolDialect } from "../../../utils/tool-integration.js";
13
- function transformLlmConfig(llmConfig) {
14
- const transformed = JSON.parse(JSON.stringify(llmConfig, (key, v) => key === "modelType"
15
- ? undefined
16
- : key === "stop_sequences"
17
- ? Array.isArray(v)
18
- ? v.join(", ")
19
- : v
20
- : typeof v === "number" || typeof v === "boolean"
21
- ? String(v)
22
- : v).replace(/"([a-z][A-Za-z]*)":/g, (_, key) => `"${key.replace(/[A-Z]/g, (l) => `_${l.toLowerCase()}`)}":`));
23
- if ("stop_sequences" in transformed) {
24
- transformed["reverse_prompt"] = transformed["stop_sequences"];
25
- delete transformed["stop_sequences"];
26
- }
27
- if ("opencl_cache_dir" in transformed) {
28
- transformed["openclCacheDir"] = transformed["opencl_cache_dir"];
29
- delete transformed["opencl_cache_dir"];
30
- }
31
- if ("tools_mode" in transformed) {
32
- if (transformed["tools_mode"] === TOOLS_MODE.dynamic) {
33
- transformed["tools_compact"] = "true";
34
- }
35
- delete transformed["tools_mode"];
36
- }
37
- return transformed;
38
- }
66
+ import { getRequestRegistry, withRequestContext, } from "../../runtime/index.js";
67
+ import { generateServerRequestId } from "../../runtime/request-id.js";
68
+ import { getServerLogger } from "../../../../logging/index.js";
39
69
  function createLlmModel(modelId, modelPath, llmConfig, projectionModelPath) {
40
70
  const logger = createStreamLogger(modelId, ModelType.llamacppCompletion);
41
71
  registerAddonLogger(modelId, ModelType.llamacppCompletion, logger);
@@ -78,71 +108,104 @@ export const llmPlugin = definePlugin({
78
108
  requestSchema: completionStreamRequestSchema,
79
109
  responseSchema: completionStreamResponseSchema,
80
110
  streaming: true,
111
+ cancel: { scope: "model", hard: true },
81
112
  handler: async function* (request) {
82
- const filteredHistory = request.history.map(({ role, content, attachments }) => ({
83
- role,
84
- content,
85
- attachments: attachments ?? [],
86
- }));
87
- const modelCfg = getModelConfig(request.modelId);
88
- const toolsActive = (request.tools?.length ?? 0) > 0 &&
89
- modelCfg.tools === true;
90
- const capabilities = {
91
- toolCalling: toolsActive ? "textParse" : "none",
92
- thinkingFraming: request.captureThinking ? "thinkTags" : "none",
93
- };
94
- // Dialect runs regardless of tool availability — thinking/content
95
- // stripping is needed even on plain completions.
96
- const dialect = request.toolDialect ?? detectToolDialect(request.modelId);
97
- const normalizer = createCompletionNormalizer({
98
- capabilities,
99
- tools: request.tools ?? [],
100
- captureThinking: request.captureThinking ?? false,
101
- emitRawDeltas: request.emitRawDeltas ?? false,
102
- toolDialect: dialect,
103
- });
104
- const stream = completion({
105
- history: filteredHistory,
106
- modelId: request.modelId,
107
- kvCache: request.kvCache,
108
- ...(toolsActive && request.tools && { tools: request.tools }),
109
- ...(request.generationParams && { generationParams: request.generationParams }),
110
- ...(toolsActive && { toolDialect: dialect }),
111
- ...(request.responseFormat && { responseFormat: request.responseFormat }),
112
- });
113
+ const env_1 = { stack: [], error: void 0, hasError: false };
113
114
  try {
114
- const batchedEvents = [];
115
- let result = await stream.next();
116
- while (!result.done) {
117
- const events = normalizer.push(result.value.token);
118
- if (request.stream) {
119
- yield {
120
- type: "completionStream",
121
- events,
122
- };
115
+ const filteredHistory = request.history.map(({ role, content, attachments }) => ({
116
+ role,
117
+ content,
118
+ attachments: attachments ?? [],
119
+ }));
120
+ const modelCfg = getModelConfig(request.modelId);
121
+ const toolsActive = (request.tools?.length ?? 0) > 0 &&
122
+ modelCfg.tools === true;
123
+ const capabilities = {
124
+ toolCalling: toolsActive ? "textParse" : "none",
125
+ thinkingFraming: request.captureThinking ? "thinkTags" : "none",
126
+ };
127
+ // Dialect runs regardless of tool availability — thinking/content
128
+ // stripping is needed even on plain completions.
129
+ const dialect = request.toolDialect ?? detectToolDialect(request.modelId);
130
+ const normalizer = createCompletionNormalizer({
131
+ capabilities,
132
+ tools: request.tools ?? [],
133
+ captureThinking: request.captureThinking ?? false,
134
+ emitRawDeltas: request.emitRawDeltas ?? false,
135
+ toolDialect: dialect,
136
+ });
137
+ // Open a request-scoped lifecycle. The registry is the single
138
+ // source of truth for "is this turn cancelled?" — we plumb the
139
+ // signal into `completion()` and expose `requestId` so the
140
+ // client can target this run with `cancel({ requestId })`.
141
+ // Falls back to a server-generated id if the client (e.g. an
142
+ // older release) didn't send one.
143
+ const ctx = __addDisposableResource(env_1, getRequestRegistry().begin({
144
+ requestId: request.requestId ?? generateServerRequestId(),
145
+ kind: "completion",
146
+ modelId: request.modelId,
147
+ }), true);
148
+ const requestLogger = withRequestContext(getServerLogger(), ctx);
149
+ const stream = completion({
150
+ history: filteredHistory,
151
+ modelId: request.modelId,
152
+ kvCache: request.kvCache,
153
+ ...(toolsActive && request.tools && { tools: request.tools }),
154
+ ...(request.generationParams && { generationParams: request.generationParams }),
155
+ ...(toolsActive && { toolDialect: dialect }),
156
+ ...(request.responseFormat && { responseFormat: request.responseFormat }),
157
+ }, { signal: ctx.signal, scope: ctx.scope, logger: requestLogger });
158
+ try {
159
+ const batchedEvents = [];
160
+ let result = await stream.next();
161
+ while (!result.done) {
162
+ const events = normalizer.push(result.value.token);
163
+ if (request.stream) {
164
+ yield {
165
+ type: "completionStream",
166
+ events,
167
+ };
168
+ }
169
+ else {
170
+ batchedEvents.push(...events);
171
+ }
172
+ result = await stream.next();
123
173
  }
124
- else {
125
- batchedEvents.push(...events);
174
+ const { modelExecutionMs, stats, toolCalls } = result.value;
175
+ // `stopReason: "cancelled"` rides the success-done path: the
176
+ // events stream ends normally, the cancellation is observable
177
+ // via the last event's `stopReason`, and the client-side
178
+ // `CompletionRun` aggregates (`final` / `text` / `toolCalls` /
179
+ // `stats`) reject with `InferenceCancelledError` carrying the
180
+ // partial state.
181
+ const cancelled = ctx.signal.aborted;
182
+ const terminalEvents = normalizer.finish({
183
+ ...(stats && { stats }),
184
+ ...(toolCalls.length > 0 && { toolCalls }),
185
+ ...(cancelled && { stopReason: "cancelled" }),
186
+ });
187
+ if (!request.stream) {
188
+ batchedEvents.push(...terminalEvents);
126
189
  }
127
- result = await stream.next();
190
+ const finalEvents = request.stream ? terminalEvents : batchedEvents;
191
+ yield attachModelExecutionMs({
192
+ type: "completionStream",
193
+ done: true,
194
+ events: finalEvents,
195
+ }, modelExecutionMs);
128
196
  }
129
- const { modelExecutionMs, stats, toolCalls } = result.value;
130
- const terminalEvents = normalizer.finish({
131
- ...(stats && { stats }),
132
- ...(toolCalls.length > 0 && { toolCalls }),
133
- });
134
- if (!request.stream) {
135
- batchedEvents.push(...terminalEvents);
197
+ finally {
198
+ await stream.return?.(undefined);
136
199
  }
137
- const finalEvents = request.stream ? terminalEvents : batchedEvents;
138
- yield attachModelExecutionMs({
139
- type: "completionStream",
140
- done: true,
141
- events: finalEvents,
142
- }, modelExecutionMs);
200
+ }
201
+ catch (e_1) {
202
+ env_1.error = e_1;
203
+ env_1.hasError = true;
143
204
  }
144
205
  finally {
145
- await stream.return?.(undefined);
206
+ const result_1 = __disposeResources(env_1);
207
+ if (result_1)
208
+ await result_1;
146
209
  }
147
210
  },
148
211
  }),
@@ -150,6 +213,14 @@ export const llmPlugin = definePlugin({
150
213
  requestSchema: finetuneRequestSchema,
151
214
  responseSchema: finetuneResponseSchema,
152
215
  streaming: false,
216
+ // Reality matches addon: llama.cpp exposes `model.cancel()` for
217
+ // the running finetune job, so we flip from `scope: "none"` to
218
+ // `{ scope: "model", hard: true }`. The `startFinetune` op
219
+ // forwards the registry's abort signal to that call; the broad
220
+ // `cancel({ modelId, kind: "finetune" })` and legacy
221
+ // `cancelFinetune(modelId)` paths both flow through the
222
+ // registry.
223
+ cancel: { scope: "model", hard: true },
153
224
  handler: function (request) {
154
225
  return finetune(request);
155
226
  },
@@ -158,8 +229,9 @@ export const llmPlugin = definePlugin({
158
229
  requestSchema: translateRequestSchema,
159
230
  responseSchema: translateResponseSchema,
160
231
  streaming: true,
232
+ cancel: { scope: "model", hard: true },
161
233
  handler: async function* (request) {
162
- const stream = translate(request);
234
+ const stream = translate(request, request.requestId);
163
235
  try {
164
236
  let result = await stream.next();
165
237
  while (!result.done) {