@qvac/sdk 0.10.2 → 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 (200) 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/ops/cancel.d.ts +27 -1
  106. package/dist/server/bare/ops/cancel.js +36 -10
  107. package/dist/server/bare/ops/embed.d.ts +3 -3
  108. package/dist/server/bare/ops/embed.js +144 -28
  109. package/dist/server/bare/ops/kv-cache-utils.d.ts +0 -7
  110. package/dist/server/bare/ops/kv-cache-utils.js +8 -47
  111. package/dist/server/bare/ops/transcribe.d.ts +10 -5
  112. package/dist/server/bare/ops/transcribe.js +196 -41
  113. package/dist/server/bare/ops/translate.d.ts +1 -1
  114. package/dist/server/bare/ops/translate.js +197 -88
  115. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +12 -3
  116. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +170 -172
  117. package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +125 -21
  118. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.d.ts +154 -0
  119. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.js +448 -0
  120. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.d.ts +21 -73
  121. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.js +18 -101
  122. package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +30 -3
  123. package/dist/server/bare/plugins/llamacpp-completion/plugin.js +157 -85
  124. package/dist/server/bare/plugins/llamacpp-completion/transform.d.ts +10 -0
  125. package/dist/server/bare/plugins/llamacpp-completion/transform.js +34 -0
  126. package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +7 -0
  127. package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +10 -1
  128. package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +17 -15
  129. package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +5 -1
  130. package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +3 -3
  131. package/dist/server/bare/plugins/onnx-ocr/plugin.js +3 -0
  132. package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +39 -39
  133. package/dist/server/bare/plugins/onnx-tts/plugin.js +6 -0
  134. package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +29 -21
  135. package/dist/server/bare/plugins/parakeet-transcription/plugin.js +4 -1
  136. package/dist/server/bare/plugins/sdcpp-generation/ops/diffusion.js +8 -0
  137. package/dist/server/bare/plugins/sdcpp-generation/ops/upscale.d.ts +3 -0
  138. package/dist/server/bare/plugins/sdcpp-generation/ops/upscale.js +40 -0
  139. package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +91 -16
  140. package/dist/server/bare/plugins/sdcpp-generation/plugin.js +97 -7
  141. package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +22 -3
  142. package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +50 -14
  143. package/dist/server/bare/rag-hyperdb/index.d.ts +1 -1
  144. package/dist/server/bare/rag-hyperdb/index.js +1 -1
  145. package/dist/server/bare/rag-hyperdb/rag-operation-manager.d.ts +26 -4
  146. package/dist/server/bare/rag-hyperdb/rag-operation-manager.js +65 -28
  147. package/dist/server/bare/runtime/disposable-scope.d.ts +36 -0
  148. package/dist/server/bare/runtime/disposable-scope.js +69 -0
  149. package/dist/server/bare/runtime/index.d.ts +8 -0
  150. package/dist/server/bare/runtime/index.js +3 -0
  151. package/dist/server/bare/runtime/request-context.d.ts +49 -0
  152. package/dist/server/bare/runtime/request-context.js +1 -0
  153. package/dist/server/bare/runtime/request-id.d.ts +12 -0
  154. package/dist/server/bare/runtime/request-id.js +14 -0
  155. package/dist/server/bare/runtime/request-registry-singleton.d.ts +4 -0
  156. package/dist/server/bare/runtime/request-registry-singleton.js +27 -0
  157. package/dist/server/bare/runtime/request-registry.d.ts +136 -0
  158. package/dist/server/bare/runtime/request-registry.js +297 -0
  159. package/dist/server/bare/runtime/with-request-context.d.ts +22 -0
  160. package/dist/server/bare/runtime/with-request-context.js +53 -0
  161. package/dist/server/bare/types/addon-responses.d.ts +11 -1
  162. package/dist/server/rpc/handler-registry.js +26 -7
  163. package/dist/server/rpc/handlers/cancel-delegated.js +28 -31
  164. package/dist/server/rpc/handlers/cancelHandler.d.ts +30 -1
  165. package/dist/server/rpc/handlers/cancelHandler.js +54 -22
  166. package/dist/server/rpc/handlers/delete-cache.d.ts +9 -0
  167. package/dist/server/rpc/handlers/delete-cache.js +13 -15
  168. package/dist/server/rpc/handlers/download-asset.js +133 -40
  169. package/dist/server/rpc/handlers/index.d.ts +2 -0
  170. package/dist/server/rpc/handlers/index.js +2 -0
  171. package/dist/server/rpc/handlers/load-model/download-manager.d.ts +60 -2
  172. package/dist/server/rpc/handlers/load-model/download-manager.js +141 -8
  173. package/dist/server/rpc/handlers/load-model/handler.js +213 -98
  174. package/dist/server/rpc/handlers/load-model/http.js +3 -3
  175. package/dist/server/rpc/handlers/load-model/hyperdrive.js +1 -1
  176. package/dist/server/rpc/handlers/load-model/registry.js +1 -1
  177. package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +20 -1
  178. package/dist/server/rpc/handlers/load-model/resolve-session.js +6 -5
  179. package/dist/server/rpc/handlers/load-model/resolve.d.ts +3 -2
  180. package/dist/server/rpc/handlers/load-model/resolve.js +9 -6
  181. package/dist/server/rpc/handlers/load-model/types.d.ts +24 -0
  182. package/dist/server/rpc/handlers/rag.js +133 -15
  183. package/dist/server/rpc/handlers/upscale-stream.d.ts +3 -0
  184. package/dist/server/rpc/handlers/upscale-stream.js +4 -0
  185. package/dist/server/utils/completion-normalizer.js +8 -0
  186. package/dist/server/utils/tools/dialect.js +4 -0
  187. package/dist/server/utils/tools/parser.js +16 -1
  188. package/dist/server/utils/tools/parsers/gemma4native.d.ts +4 -0
  189. package/dist/server/utils/tools/parsers/gemma4native.js +60 -0
  190. package/dist/server/utils/tools/parsers/qwen35.d.ts +4 -0
  191. package/dist/server/utils/tools/parsers/qwen35.js +120 -0
  192. package/dist/utils/aggregate-events.d.ts +9 -0
  193. package/dist/utils/aggregate-events.js +18 -3
  194. package/dist/utils/decorate-promise.d.ts +34 -0
  195. package/dist/utils/decorate-promise.js +35 -0
  196. package/dist/utils/errors-client.d.ts +3 -0
  197. package/dist/utils/errors-client.js +5 -0
  198. package/dist/utils/errors-server.d.ts +85 -0
  199. package/dist/utils/errors-server.js +97 -0
  200. package/package.json +20 -15
@@ -2,8 +2,10 @@ import { transcribeResponseSchema, transcribeStreamResponseSchema, } from "../..
2
2
  import { stream, duplex } from "../rpc/rpc-client.js";
3
3
  import { getClientLogger } from "../../logging/index.js";
4
4
  import { TranscriptionFailedError } from "../../utils/errors-client.js";
5
+ import { decoratePromise } from "../../utils/decorate-promise.js";
6
+ import { generateClientRequestId } from "./client-request-id.js";
5
7
  const logger = getClientLogger();
6
- function buildTranscribeRequest(params) {
8
+ function buildTranscribeRequest(params, requestId) {
7
9
  return {
8
10
  type: "transcribe",
9
11
  modelId: params.modelId,
@@ -12,10 +14,21 @@ function buildTranscribeRequest(params) {
12
14
  : { type: "base64", value: params.audioChunk.toString("base64") },
13
15
  ...(params.prompt && { prompt: params.prompt }),
14
16
  ...(params.metadata === true && { metadata: true }),
17
+ requestId,
15
18
  };
16
19
  }
17
- export async function transcribe(params, options) {
18
- const request = buildTranscribeRequest(params);
20
+ export function transcribe(params, options) {
21
+ // Client-generated id surfaced synchronously on the returned promise
22
+ // — same shape as `loadModel` / `downloadAsset` / `completion`. The
23
+ // CLI cancel bridge in `qvac serve` binds `req.on('close')` to
24
+ // `cancel({ requestId })` immediately after the call returns so a
25
+ // client disconnect aborts the in-flight transcription.
26
+ const requestId = generateClientRequestId();
27
+ const inner = runTranscribe(params, requestId, options);
28
+ return decoratePromise(inner, { requestId });
29
+ }
30
+ async function runTranscribe(params, requestId, options) {
31
+ const request = buildTranscribeRequest(params, requestId);
19
32
  if (params.metadata === true) {
20
33
  const segments = [];
21
34
  for await (const response of stream(request, options)) {
@@ -54,6 +67,9 @@ export function transcribeStream(params, options) {
54
67
  return transcribeStreamWithAudio(params, options);
55
68
  }
56
69
  const streamParams = params;
70
+ if (streamParams.emitVadEvents === true) {
71
+ return transcribeStreamDuplexConversation(streamParams, options);
72
+ }
57
73
  if (streamParams.metadata === true) {
58
74
  return transcribeStreamDuplexMetadata(streamParams, options);
59
75
  }
@@ -64,7 +80,7 @@ export function transcribeStream(params, options) {
64
80
  * the value extracted from each response frame until `done` is seen.
65
81
  */
66
82
  async function* streamTranscribeValues(params, options, extract) {
67
- const request = buildTranscribeRequest(params);
83
+ const request = buildTranscribeRequest(params, generateClientRequestId());
68
84
  for await (const response of stream(request, options)) {
69
85
  if (response.type === "transcribe") {
70
86
  const parsed = transcribeResponseSchema.parse(response);
@@ -88,6 +104,13 @@ function buildTranscribeStreamRequest(params) {
88
104
  modelId: params.modelId,
89
105
  ...(params.prompt && { prompt: params.prompt }),
90
106
  ...(params.metadata === true && { metadata: true }),
107
+ ...(params.emitVadEvents === true && { emitVadEvents: true }),
108
+ ...(params.endOfTurnSilenceMs !== undefined && {
109
+ endOfTurnSilenceMs: params.endOfTurnSilenceMs,
110
+ }),
111
+ ...(params.vadRunIntervalMs !== undefined && {
112
+ vadRunIntervalMs: params.vadRunIntervalMs,
113
+ }),
91
114
  };
92
115
  }
93
116
  /**
@@ -126,6 +149,10 @@ function transcribeStreamDuplex(params, options) {
126
149
  function transcribeStreamDuplexMetadata(params, options) {
127
150
  return createTranscribeStreamSession(params, options, processLineMetadata, "TranscribeStreamMetadataSession");
128
151
  }
152
+ function transcribeStreamDuplexConversation(params, options) {
153
+ const wantsMetadata = params.metadata === true;
154
+ return createTranscribeStreamSession(params, options, (line) => processLineConversation(line, wantsMetadata), "TranscribeStreamConversationSession");
155
+ }
129
156
  /**
130
157
  * Line-delimited parser: reads newline-separated frames from a duplex
131
158
  * response stream, passes each non-empty line through `process`, and yields
@@ -190,3 +217,30 @@ function processLine(line) {
190
217
  function processLineMetadata(line) {
191
218
  return processWith(line, (response) => response.segment);
192
219
  }
220
+ function processLineConversation(line, wantsMetadata) {
221
+ return processWith(line, (response) => {
222
+ if (response.vad) {
223
+ return {
224
+ type: "vad",
225
+ speaking: response.vad.speaking,
226
+ probability: response.vad.probability,
227
+ };
228
+ }
229
+ if (response.endOfTurn) {
230
+ return {
231
+ type: "endOfTurn",
232
+ silenceDurationMs: response.endOfTurn.silenceDurationMs,
233
+ };
234
+ }
235
+ if (wantsMetadata) {
236
+ if (response.segment) {
237
+ return { type: "segment", segment: response.segment };
238
+ }
239
+ return undefined;
240
+ }
241
+ if (response.text && response.text.trim()) {
242
+ return { type: "text", text: response.text };
243
+ }
244
+ return undefined;
245
+ });
246
+ }
@@ -3,12 +3,15 @@ import { type UnloadModelParams } from "../../schemas/index";
3
3
  * Unloads a previously loaded model from the server.
4
4
  *
5
5
  * When the last model is unloaded (no more models remain), this function
6
- * automatically closes the RPC connection, allowing the process to exit
7
- * naturally without requiring manual cleanup.
6
+ * automatically closes the RPC connection on Node/Electron, allowing the
7
+ * process to exit naturally without requiring manual cleanup. On Bare the
8
+ * connection is left open by default so long-lived workers survive a routine
9
+ * unload; pass `autoClose: true` to opt in to closing.
8
10
  *
9
11
  * @param params - The parameters for unloading the model
10
12
  * @param params.modelId - The unique identifier of the model to unload
11
13
  * @param params.clearStorage - Whether to clear the storage for the model
14
+ * @param params.autoClose - Override the runtime-default auto-close behavior
12
15
  * @throws {QvacErrorBase} When the response type is invalid or when the unload operation fails
13
16
  */
14
17
  export declare function unloadModel(params: UnloadModelParams): Promise<void>;
@@ -1,3 +1,4 @@
1
+ import { isBare } from "which-runtime";
1
2
  import {} from "../../schemas/index.js";
2
3
  import { send, close } from "../rpc/rpc-client.js";
3
4
  import { stopLoggingStreamForModel } from "../logging-stream-registry.js";
@@ -8,12 +9,15 @@ const logger = getClientLogger();
8
9
  * Unloads a previously loaded model from the server.
9
10
  *
10
11
  * When the last model is unloaded (no more models remain), this function
11
- * automatically closes the RPC connection, allowing the process to exit
12
- * naturally without requiring manual cleanup.
12
+ * automatically closes the RPC connection on Node/Electron, allowing the
13
+ * process to exit naturally without requiring manual cleanup. On Bare the
14
+ * connection is left open by default so long-lived workers survive a routine
15
+ * unload; pass `autoClose: true` to opt in to closing.
13
16
  *
14
17
  * @param params - The parameters for unloading the model
15
18
  * @param params.modelId - The unique identifier of the model to unload
16
19
  * @param params.clearStorage - Whether to clear the storage for the model
20
+ * @param params.autoClose - Override the runtime-default auto-close behavior
17
21
  * @throws {QvacErrorBase} When the response type is invalid or when the unload operation fails
18
22
  */
19
23
  export async function unloadModel(params) {
@@ -30,8 +34,9 @@ export async function unloadModel(params) {
30
34
  throw new ModelUnloadFailedError(params.modelId);
31
35
  }
32
36
  stopLoggingStreamForModel(params.modelId);
33
- // Auto-close when no models remain AND no providers are active
34
- if (response.hasActiveModels === false &&
37
+ const shouldAutoClose = params.autoClose ?? !isBare;
38
+ if (shouldAutoClose &&
39
+ response.hasActiveModels === false &&
35
40
  response.hasActiveProviders === false) {
36
41
  logger.info("🧹 No models or providers active, automatically closing RPC connection...");
37
42
  await close();
@@ -0,0 +1,45 @@
1
+ import { type UpscaleClientParams, type UpscaleStats } from "../../schemas/sdcpp-config";
2
+ interface UpscaleResult {
3
+ outputs: Promise<Uint8Array[]>;
4
+ stats: Promise<UpscaleStats | undefined>;
5
+ }
6
+ /**
7
+ * Runs standalone ESRGAN upscaling on an arbitrary PNG/JPEG image.
8
+ *
9
+ * The model must have been loaded with `modelType: "diffusion"` and
10
+ * `modelConfig.mode: "upscale"` — calling `upscale()` against a model
11
+ * loaded in default (`mode: "diffusion"`) mode throws
12
+ * `ModelOperationNotSupportedError` upfront.
13
+ *
14
+ * `outputs` always resolves to length 1: `repeats` runs N passes
15
+ * internally and emits a single final image at `source * scale^repeats`
16
+ * dimensions. The `Uint8Array[]` shape reserves headroom for future
17
+ * multi-output variants.
18
+ *
19
+ * @param params - `{ modelId, image, repeats? }`. `image` is raw PNG/JPEG
20
+ * bytes; the client base64-encodes them on the wire.
21
+ * @returns `{ outputs, stats }` — `outputs` resolves to a single-element
22
+ * array containing the final upscaled PNG; `stats` resolves to
23
+ * addon-side stats (load/upscale ms, final width/height, repeats
24
+ * actually executed, etc.).
25
+ * @throws {ModelOperationNotSupportedError} If the model was not loaded
26
+ * with `mode: "upscale"`.
27
+ * @throws {StreamEndedError} If the RPC stream closes without emitting a
28
+ * terminal `done` chunk.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const modelId = await loadModel(REALESRGAN_X4PLUS_ANIME_6B, {
33
+ * modelType: "diffusion",
34
+ * modelConfig: { mode: "upscale", upscaler: { tile_size: 128 } },
35
+ * });
36
+ * const pngBytes = fs.readFileSync("input.png");
37
+ * const { outputs, stats } = upscale({ modelId, image: pngBytes, repeats: 2 });
38
+ * const [upscaledPng] = await outputs;
39
+ * fs.writeFileSync("upscaled.png", upscaledPng);
40
+ * console.log(await stats);
41
+ * ```
42
+ */
43
+ export declare function upscale(params: UpscaleClientParams): UpscaleResult;
44
+ export {};
45
+ //# sourceMappingURL=upscale.d.ts.map
@@ -0,0 +1,99 @@
1
+ import { upscaleStreamResponseSchema, } from "../../schemas/sdcpp-config.js";
2
+ import { stream as streamRpc } from "../rpc/rpc-client.js";
3
+ import { decodeBase64, encodeBase64 } from "../../utils/encoding.js";
4
+ import { StreamEndedError } from "../../utils/errors-client.js";
5
+ /**
6
+ * Runs standalone ESRGAN upscaling on an arbitrary PNG/JPEG image.
7
+ *
8
+ * The model must have been loaded with `modelType: "diffusion"` and
9
+ * `modelConfig.mode: "upscale"` — calling `upscale()` against a model
10
+ * loaded in default (`mode: "diffusion"`) mode throws
11
+ * `ModelOperationNotSupportedError` upfront.
12
+ *
13
+ * `outputs` always resolves to length 1: `repeats` runs N passes
14
+ * internally and emits a single final image at `source * scale^repeats`
15
+ * dimensions. The `Uint8Array[]` shape reserves headroom for future
16
+ * multi-output variants.
17
+ *
18
+ * @param params - `{ modelId, image, repeats? }`. `image` is raw PNG/JPEG
19
+ * bytes; the client base64-encodes them on the wire.
20
+ * @returns `{ outputs, stats }` — `outputs` resolves to a single-element
21
+ * array containing the final upscaled PNG; `stats` resolves to
22
+ * addon-side stats (load/upscale ms, final width/height, repeats
23
+ * actually executed, etc.).
24
+ * @throws {ModelOperationNotSupportedError} If the model was not loaded
25
+ * with `mode: "upscale"`.
26
+ * @throws {StreamEndedError} If the RPC stream closes without emitting a
27
+ * terminal `done` chunk.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * const modelId = await loadModel(REALESRGAN_X4PLUS_ANIME_6B, {
32
+ * modelType: "diffusion",
33
+ * modelConfig: { mode: "upscale", upscaler: { tile_size: 128 } },
34
+ * });
35
+ * const pngBytes = fs.readFileSync("input.png");
36
+ * const { outputs, stats } = upscale({ modelId, image: pngBytes, repeats: 2 });
37
+ * const [upscaledPng] = await outputs;
38
+ * fs.writeFileSync("upscaled.png", upscaledPng);
39
+ * console.log(await stats);
40
+ * ```
41
+ */
42
+ export function upscale(params) {
43
+ const request = {
44
+ modelId: params.modelId,
45
+ image: encodeBase64(params.image),
46
+ ...(params.repeats !== undefined && { repeats: params.repeats }),
47
+ type: "upscaleStream",
48
+ };
49
+ let statsResolver = () => { };
50
+ let statsRejecter = () => { };
51
+ const statsPromise = new Promise((resolve, reject) => {
52
+ statsResolver = resolve;
53
+ statsRejecter = reject;
54
+ });
55
+ statsPromise.catch(() => { });
56
+ let outputsResolver = () => { };
57
+ let outputsRejecter = () => { };
58
+ const outputsPromise = new Promise((resolve, reject) => {
59
+ outputsResolver = resolve;
60
+ outputsRejecter = reject;
61
+ });
62
+ outputsPromise.catch(() => { });
63
+ const collectedBuffers = [];
64
+ async function processResponses() {
65
+ let sawDone = false;
66
+ try {
67
+ for await (const response of streamRpc(request)) {
68
+ if (response &&
69
+ typeof response === "object" &&
70
+ "type" in response &&
71
+ response.type === "upscaleStream") {
72
+ const parsed = upscaleStreamResponseSchema.parse(response);
73
+ if (parsed.data) {
74
+ collectedBuffers.push(decodeBase64(parsed.data));
75
+ }
76
+ if (parsed.done) {
77
+ sawDone = true;
78
+ statsResolver(parsed.stats);
79
+ outputsResolver(collectedBuffers);
80
+ }
81
+ }
82
+ }
83
+ if (!sawDone) {
84
+ const error = new StreamEndedError();
85
+ statsRejecter(error);
86
+ outputsRejecter(error);
87
+ }
88
+ }
89
+ catch (error) {
90
+ statsRejecter(error);
91
+ outputsRejecter(error);
92
+ }
93
+ }
94
+ void processResponses();
95
+ return {
96
+ outputs: outputsPromise,
97
+ stats: statsPromise,
98
+ };
99
+ }
@@ -195,6 +195,7 @@ export async function createDuplexSession(payload, commandId) {
195
195
  const req = rpc.request(commandId);
196
196
  const requestStream = req.createRequestStream();
197
197
  const responseStream = req.createResponseStream({ encoding: "utf-8" });
198
- requestStream.write(payload, "utf-8");
198
+ // Pre-encode payload — RN/Hermes lacks a global `Buffer`, so `write(string, "utf-8")` would throw.
199
+ requestStream.write(new TextEncoder().encode(payload));
199
200
  return { requestStream, responseStream };
200
201
  }
@@ -13,8 +13,6 @@ import { getClientLogger } from "../../logging/index.js";
13
13
  import { RPCInitTimeoutError } from "../../utils/errors-client.js";
14
14
  const RPC_INIT_TIMEOUT_MS = 30_000;
15
15
  const logger = getClientLogger();
16
- const __filename = fileURLToPath(import.meta.url);
17
- const __dirname = path.dirname(__filename);
18
16
  let rpcInstance = null;
19
17
  let rpcPromise = null;
20
18
  let bareWorkerProc = null;
@@ -56,14 +54,27 @@ function resolvePackagedWorkerPath() {
56
54
  return undefined;
57
55
  }
58
56
  /**
59
- * Get default worker path from SDK dist
57
+ * Resolve the SDK's default worker entry via bundler-visible asset references.
58
+ *
59
+ * `path.resolve(__dirname, ...)` is invisible to static analysis, so packaged
60
+ * consumers ship without worker.js. We use `import.meta.asset(<literal>)` on
61
+ * Bare (detected by bare-module-lexer) and fall back to
62
+ * `new URL(<literal>, import.meta.url)` elsewhere. Specs must be string
63
+ * literals at the call site.
60
64
  */
61
65
  function getDefaultWorkerPath() {
62
- const packagedWorker = path.resolve(__dirname, "../../server/worker.js");
63
- if (fs.existsSync(packagedWorker))
64
- return packagedWorker;
66
+ const hasAsset = typeof import.meta.asset === "function";
67
+ const packagedUrl = hasAsset
68
+ ? new URL(import.meta.asset("../../server/worker.js"))
69
+ : new URL("../../server/worker.js", import.meta.url);
70
+ const packaged = fileURLToPath(packagedUrl);
71
+ if (fs.existsSync(packaged))
72
+ return packaged;
65
73
  // Dev/source layout fallback
66
- return path.resolve(__dirname, "../../dist/server/worker.js");
74
+ const devUrl = hasAsset
75
+ ? new URL(import.meta.asset("../../dist/server/worker.js"))
76
+ : new URL("../../dist/server/worker.js", import.meta.url);
77
+ return fileURLToPath(devUrl);
67
78
  }
68
79
  /**
69
80
  * Resolve worker path with priority:
@@ -3,11 +3,11 @@ import { type Request, type Response, type RPCOptions } from "../../schemas/inde
3
3
  export declare function send<T extends Request>(request: T, options?: RPCOptions, rpc?: RPC): Promise<Response>;
4
4
  export declare function stream<T extends Request>(request: T, options?: RPCOptions, rpc?: RPC): AsyncGenerator<Response>;
5
5
  export interface DuplexWritable {
6
- write(chunk: Buffer): void;
6
+ write(chunk: Uint8Array): void;
7
7
  end(): void;
8
8
  destroy(): void;
9
9
  }
10
- export interface DuplexReadable extends AsyncIterable<Buffer> {
10
+ export interface DuplexReadable extends AsyncIterable<Buffer | string> {
11
11
  destroy(): void;
12
12
  }
13
13
  export interface DuplexSession {
@@ -1,5 +1,5 @@
1
1
  import { requestSchema, responseSchema, PROFILING_TRAILER_KEY, } from "../../schemas/index.js";
2
- import { RPCError } from "./rpc-error.js";
2
+ import { reconstructError } from "./rpc-error.js";
3
3
  import { withTimeout, withTimeoutStream } from "../../utils/withTimeout.js";
4
4
  import { getClientLogger, summarizeRequest } from "../../logging/index.js";
5
5
  import { getRPC, close as closeRPC, createDuplexSession } from "#rpc";
@@ -15,7 +15,12 @@ function getNextCommandId() {
15
15
  }
16
16
  function checkAndThrowError(response) {
17
17
  if (response.type === "error") {
18
- throw new RPCError(response);
18
+ // Use the typed-error reconstructor map in `rpc-error.ts` so the
19
+ // original class (e.g. `RequestRejectedByPolicyError`) survives
20
+ // the RPC boundary intact and `err instanceof <Class>` works in
21
+ // consumer `catch` blocks. Unknown error names fall back to a
22
+ // plain `RPCError` wrapper inside `reconstructError`.
23
+ throw reconstructError(response);
19
24
  }
20
25
  }
21
26
  async function getRPCInstance() {
@@ -311,12 +316,13 @@ async function duplexProfiled(request, options = {}) {
311
316
  }
312
317
  if (outputParts.length > 0) {
313
318
  timings.chunkCount += outputParts.length;
314
- yield Buffer.from(outputParts.join("\n") + "\n");
319
+ // Yield strings (not Buffer) — RN/Hermes has no global `Buffer`; consumers call `.toString()` either way.
320
+ yield outputParts.join("\n") + "\n";
315
321
  }
316
322
  }
317
323
  if (lineBuffer.trim()) {
318
324
  timings.chunkCount++;
319
- yield Buffer.from(lineBuffer + "\n");
325
+ yield lineBuffer + "\n";
320
326
  }
321
327
  }
322
328
  catch (error) {
@@ -16,4 +16,13 @@ export declare class RPCError extends Error {
16
16
  isQvacError: boolean;
17
17
  };
18
18
  }
19
+ /**
20
+ * Rebuild the original server-thrown typed error from its serialised
21
+ * envelope so consumer code can do
22
+ * `if (err instanceof RequestRejectedByPolicyError) { ... }` across
23
+ * the RPC boundary. Unknown error names fall through to the legacy
24
+ * `RPCError` wrapper, which preserves `name`/`code`/`message` for
25
+ * code-based predicates.
26
+ */
27
+ export declare function reconstructError(response: ErrorResponse): Error;
19
28
  //# sourceMappingURL=rpc-error.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { RequestIdConflictError, RequestNotFoundError, RequestRejectedByPolicyError, } from "../../utils/errors-server.js";
1
2
  export class RPCError extends Error {
2
3
  timestamp;
3
4
  remoteStack;
@@ -37,3 +38,94 @@ export class RPCError extends Error {
37
38
  };
38
39
  }
39
40
  }
41
+ /**
42
+ * Attach the remote stack onto a reconstructed typed error so the
43
+ * client-side trace points at the consumer call site and the worker-
44
+ * side trace is preserved for debugging. Mirrors the behaviour
45
+ * `RPCError`'s constructor applies in the fall-through path.
46
+ */
47
+ function attachRemoteContext(err, response) {
48
+ if (response.stack) {
49
+ err.remoteStack = response.stack;
50
+ err.stack = `${err.stack}\n--- Worker Stack ---\n${response.stack}`;
51
+ }
52
+ if (response.timestamp) {
53
+ err.timestamp = response.timestamp;
54
+ }
55
+ return err;
56
+ }
57
+ /** Read a string field from the `typedFields` envelope, defaulting to `fallback` if missing or non-string. */
58
+ function readStringField(fields, key, fallback) {
59
+ const value = fields?.[key];
60
+ return typeof value === "string" ? value : fallback;
61
+ }
62
+ /**
63
+ * Map of server-thrown `QvacErrorBase` subclasses that need to survive
64
+ * the RPC boundary as their original class — `err instanceof
65
+ * RequestRejectedByPolicyError` on the consumer side must match the
66
+ * class re-exported from `@qvac/sdk`.
67
+ *
68
+ * **Key shape.** The key is the `name` value `QvacErrorBase` sets at
69
+ * construction time, which `createErrorResponse` forwards onto
70
+ * `response.name`. That value is the SCREAMING_SNAKE_CASE error-code
71
+ * name from `sdk-errors-server.ts` (`"REQUEST_REJECTED_BY_POLICY"`),
72
+ * **not** the JS class name (`"RequestRejectedByPolicyError"`). The
73
+ * mismatch was a 0.11.0 bring-up bug fixed alongside the reconstructor
74
+ * unit tests in `rpc-error-reconstruct.test.ts`.
75
+ *
76
+ * **Maintenance contract.** Every new cross-RPC server-thrown typed
77
+ * error class added in `errors-server.ts` adds a row here in the same
78
+ * PR, keyed by its `SDK_SERVER_ERROR_CODES.<NAME>` constant. The
79
+ * class must (a) implement `toErrorResponseFields(): Record<…>` so
80
+ * the wire envelope carries its named constructor arguments and (b)
81
+ * be re-exported from `@qvac/sdk` so consumers can `import` it.
82
+ * Forgetting either side means `instanceof` regresses for that class.
83
+ *
84
+ * Client-constructed typed errors (e.g. `InferenceCancelledError`
85
+ * built in `client/api/completion-stream.ts` from the aggregated
86
+ * partial state) are NOT registered here — they never round-trip the
87
+ * envelope, and adding a reconstructor for one would create a
88
+ * parallel construction path that fires whenever the server happens
89
+ * to throw the same class name.
90
+ */
91
+ const RECONSTRUCTORS = {
92
+ REQUEST_ID_CONFLICT: (response) => {
93
+ return new RequestIdConflictError(readStringField(response.typedFields, "requestId", ""), response.cause);
94
+ },
95
+ REQUEST_NOT_FOUND: (response) => {
96
+ return new RequestNotFoundError(readStringField(response.typedFields, "requestId", ""), response.cause);
97
+ },
98
+ REQUEST_REJECTED_BY_POLICY: (response) => {
99
+ return new RequestRejectedByPolicyError(readStringField(response.typedFields, "requestId", ""), readStringField(response.typedFields, "kind", ""), readStringField(response.typedFields, "modelId", ""), readStringField(response.typedFields, "reason", response.message), response.cause);
100
+ },
101
+ };
102
+ /**
103
+ * Rebuild the original server-thrown typed error from its serialised
104
+ * envelope so consumer code can do
105
+ * `if (err instanceof RequestRejectedByPolicyError) { ... }` across
106
+ * the RPC boundary. Unknown error names fall through to the legacy
107
+ * `RPCError` wrapper, which preserves `name`/`code`/`message` for
108
+ * code-based predicates.
109
+ */
110
+ export function reconstructError(response) {
111
+ const reconstructor = response.name
112
+ ? RECONSTRUCTORS[response.name]
113
+ : undefined;
114
+ if (!reconstructor) {
115
+ return new RPCError(response);
116
+ }
117
+ try {
118
+ return attachRemoteContext(reconstructor(response), response);
119
+ }
120
+ catch {
121
+ // Defensive fall-through: if a reconstructor throws (e.g. a
122
+ // future class adds a required constructor field and an older
123
+ // server doesn't ship `typedFields`), surface the original error
124
+ // via `RPCError` so consumers never see "couldn't reconstruct
125
+ // error" obscuring the real one. The reconstructors here are
126
+ // intentionally written to coerce missing fields to defensible
127
+ // defaults (`String(x ?? "")`), so reaching this branch is the
128
+ // edge case, not the norm.
129
+ return new RPCError(response);
130
+ }
131
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Cancel a specific in-flight completion by `requestId`.
3
+ *
4
+ * `completion(...)` exposes a stable `requestId` (UUIDv4, generated
5
+ * client-side) on the returned `CompletionRun`. Pass it to
6
+ * `cancel({ requestId })` to abort that exact run without affecting any
7
+ * other inference happening on the same model.
8
+ *
9
+ * Two cancel paths exist:
10
+ *
11
+ * 1. `cancel({ requestId })` — targeted cancel, the primary path
12
+ * introduced in 0.11.0. The `requestId` is available synchronously
13
+ * on the `CompletionRun`. Same-tick cancels (issued before the
14
+ * server has registered the request) are recorded and applied
15
+ * retroactively when `begin(...)` arrives, so they aren't silently
16
+ * dropped.
17
+ * 2. `cancel({ operation: "inference", modelId })` — broad cancel
18
+ * (escape hatch, kept indefinitely). Cancels every inference running
19
+ * on the model. Useful for unload, app shutdown, admin sweeps when
20
+ * the caller doesn't have a `requestId` to hand.
21
+ *
22
+ * --- Cancel outcomes (0.11.0+) ---
23
+ *
24
+ * A cancel surfaces on two channels:
25
+ *
26
+ * - `run.events` ends *normally* with a `completionDone` event carrying
27
+ * `stopReason: "cancelled"`. The loop exits cleanly, no thrown error.
28
+ * - `run.text` / `run.final` / `run.stats` / `run.toolCalls` reject
29
+ * with `InferenceCancelledError(requestId, partial)`, where `partial`
30
+ * holds whatever the model produced before the cancel landed
31
+ * (accumulated `text`, completed `toolCalls`, last-known `stats`).
32
+ *
33
+ * Pick the channel that matches how you consume the run: event-loop
34
+ * consumers don't need to catch anything; promise-aggregate consumers
35
+ * pattern-match on `instanceof InferenceCancelledError`.
36
+ */
37
+ export {};
38
+ //# sourceMappingURL=cancel-by-request-id.d.ts.map
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Cancel a specific in-flight completion by `requestId`.
3
+ *
4
+ * `completion(...)` exposes a stable `requestId` (UUIDv4, generated
5
+ * client-side) on the returned `CompletionRun`. Pass it to
6
+ * `cancel({ requestId })` to abort that exact run without affecting any
7
+ * other inference happening on the same model.
8
+ *
9
+ * Two cancel paths exist:
10
+ *
11
+ * 1. `cancel({ requestId })` — targeted cancel, the primary path
12
+ * introduced in 0.11.0. The `requestId` is available synchronously
13
+ * on the `CompletionRun`. Same-tick cancels (issued before the
14
+ * server has registered the request) are recorded and applied
15
+ * retroactively when `begin(...)` arrives, so they aren't silently
16
+ * dropped.
17
+ * 2. `cancel({ operation: "inference", modelId })` — broad cancel
18
+ * (escape hatch, kept indefinitely). Cancels every inference running
19
+ * on the model. Useful for unload, app shutdown, admin sweeps when
20
+ * the caller doesn't have a `requestId` to hand.
21
+ *
22
+ * --- Cancel outcomes (0.11.0+) ---
23
+ *
24
+ * A cancel surfaces on two channels:
25
+ *
26
+ * - `run.events` ends *normally* with a `completionDone` event carrying
27
+ * `stopReason: "cancelled"`. The loop exits cleanly, no thrown error.
28
+ * - `run.text` / `run.final` / `run.stats` / `run.toolCalls` reject
29
+ * with `InferenceCancelledError(requestId, partial)`, where `partial`
30
+ * holds whatever the model produced before the cancel landed
31
+ * (accumulated `text`, completed `toolCalls`, last-known `stats`).
32
+ *
33
+ * Pick the channel that matches how you consume the run: event-loop
34
+ * consumers don't need to catch anything; promise-aggregate consumers
35
+ * pattern-match on `instanceof InferenceCancelledError`.
36
+ */
37
+ import { cancel, completion, loadModel, unloadModel, InferenceCancelledError, QWEN3_600M_INST_Q4, } from "@qvac/sdk";
38
+ try {
39
+ const modelId = await loadModel({
40
+ modelSrc: QWEN3_600M_INST_Q4,
41
+ modelType: "llm",
42
+ modelConfig: { ctx_size: 4096 },
43
+ });
44
+ const run = completion({
45
+ modelId,
46
+ history: [
47
+ {
48
+ role: "user",
49
+ content: "Write a long, detailed essay about the history of the Roman Empire.",
50
+ },
51
+ ],
52
+ stream: true,
53
+ });
54
+ console.log(`requestId: ${run.requestId}`);
55
+ // Cancel after a short delay so we exercise the cancel-mid-decode path.
56
+ setTimeout(() => {
57
+ void cancel({ requestId: run.requestId });
58
+ console.log("(cancel issued)");
59
+ }, 250);
60
+ // Channel 1: the events stream ends normally on cancel. The
61
+ // `completionDone` event's `stopReason` tells you why the loop is
62
+ // about to exit ("eos" | "length" | "cancelled" | "error" | ...).
63
+ let tokenCount = 0;
64
+ let endReason;
65
+ for await (const event of run.events) {
66
+ if (event.type === "contentDelta") {
67
+ tokenCount++;
68
+ process.stdout.write(event.text);
69
+ }
70
+ else if (event.type === "completionDone") {
71
+ endReason = event.stopReason;
72
+ }
73
+ }
74
+ console.log(`\n\nstreamed ${tokenCount} content deltas, stopReason=${endReason}.`);
75
+ // Channel 2: promise-aggregates reject with InferenceCancelledError
76
+ // on cancel. The accumulated state up to the cancel point is preserved
77
+ // on `err.partial`.
78
+ try {
79
+ const text = await run.text;
80
+ console.log(`completed normally (${text.length} chars).`);
81
+ }
82
+ catch (err) {
83
+ if (err instanceof InferenceCancelledError) {
84
+ console.log(`run.text rejected: cancelled (requestId=${err.requestId})`);
85
+ console.log(`partial text length: ${(err.partial.text ?? "").length}`);
86
+ if (err.partial.stats?.tokensPerSecond !== undefined) {
87
+ console.log(`partial stats: ${err.partial.stats.tokensPerSecond.toFixed(1)} tok/s`);
88
+ }
89
+ if (err.partial.toolCalls && err.partial.toolCalls.length > 0) {
90
+ console.log(`partial tool calls: ${err.partial.toolCalls.length}`);
91
+ }
92
+ }
93
+ else {
94
+ throw err;
95
+ }
96
+ }
97
+ await unloadModel({ modelId });
98
+ process.exit(0);
99
+ }
100
+ catch (error) {
101
+ console.error("Error:", error);
102
+ process.exit(1);
103
+ }