@qvac/sdk 0.11.0 ā 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +1423 -43
- package/README.md +2 -0
- package/dist/client/api/classify.d.ts +23 -0
- package/dist/client/api/classify.js +43 -0
- package/dist/client/api/get-model-info.d.ts +1 -1
- package/dist/client/api/index.d.ts +4 -0
- package/dist/client/api/index.js +4 -0
- package/dist/client/api/load-model.js +24 -1
- package/dist/client/api/transcribe.d.ts +3 -0
- package/dist/client/api/transcribe.js +13 -2
- package/dist/client/api/video.d.ts +64 -0
- package/dist/client/api/video.js +147 -0
- package/dist/client/api/vla-helpers.d.ts +45 -0
- package/dist/client/api/vla-helpers.js +130 -0
- package/dist/client/api/vla.d.ts +65 -0
- package/dist/client/api/vla.js +109 -0
- package/dist/client/config-loader/resolve-config.node.d.ts +16 -0
- package/dist/client/config-loader/resolve-config.node.js +77 -23
- package/dist/client/plugins-factory.d.ts +8 -0
- package/dist/client/plugins-factory.js +10 -0
- package/dist/client/rpc/bare-client.d.ts +1 -0
- package/dist/client/rpc/bare-client.js +66 -66
- package/dist/client/rpc/ensure-worker-ready.d.ts +9 -0
- package/dist/client/rpc/ensure-worker-ready.js +17 -0
- package/dist/client/rpc/expo-rpc-client.d.ts +1 -0
- package/dist/client/rpc/expo-rpc-client.js +4 -0
- package/dist/client/rpc/node-rpc-client.d.ts +1 -0
- package/dist/client/rpc/node-rpc-client.js +97 -5
- package/dist/client/rpc/rpc-client.js +115 -8
- package/dist/client/rpc/rpc-error.js +14 -1
- package/dist/commands/bundle/bare-pack.d.ts +13 -0
- package/dist/commands/bundle/bare-pack.js +54 -0
- package/dist/commands/bundle/constants.d.ts +7 -0
- package/dist/commands/bundle/constants.js +23 -0
- package/dist/commands/bundle/entry-gen.d.ts +2 -0
- package/dist/commands/bundle/entry-gen.js +48 -0
- package/dist/commands/bundle/index.d.ts +20 -0
- package/dist/commands/bundle/index.js +141 -0
- package/dist/commands/bundle/manifest.d.ts +21 -0
- package/dist/commands/bundle/manifest.js +193 -0
- package/dist/commands/bundle/plugins.d.ts +9 -0
- package/dist/commands/bundle/plugins.js +51 -0
- package/dist/commands/index.d.ts +5 -0
- package/dist/commands/index.js +2 -0
- package/dist/commands/verify/abi.d.ts +52 -0
- package/dist/commands/verify/abi.js +125 -0
- package/dist/commands/verify/addon-source.d.ts +43 -0
- package/dist/commands/verify/addon-source.js +75 -0
- package/dist/commands/verify/bundle-source.d.ts +12 -0
- package/dist/commands/verify/bundle-source.js +53 -0
- package/dist/commands/verify/index.d.ts +59 -0
- package/dist/commands/verify/index.js +371 -0
- package/dist/commands/verify/node-modules-source.d.ts +11 -0
- package/dist/commands/verify/node-modules-source.js +76 -0
- package/dist/commands/verify/prebuilds.d.ts +15 -0
- package/dist/commands/verify/prebuilds.js +39 -0
- package/dist/examples/classification/classify-image.d.ts +2 -0
- package/dist/examples/classification/classify-image.js +23 -0
- package/dist/examples/diffusion-txt2vid.d.ts +2 -0
- package/dist/examples/diffusion-txt2vid.js +81 -0
- package/dist/examples/plugins.js +3 -7
- package/dist/examples/tools/llamacpp-tools-gemma4.js +2 -5
- package/dist/examples/tools/llamacpp-tools-qwen35.js +2 -3
- package/dist/examples/transcription/parakeet-ctc-filesystem.js +18 -12
- package/dist/examples/transcription/parakeet-microphone-record.js +7 -12
- package/dist/examples/transcription/parakeet-microphone-stream.d.ts +2 -0
- package/dist/examples/transcription/parakeet-microphone-stream.js +79 -0
- package/dist/examples/transcription/parakeet-sortformer-streaming.d.ts +2 -0
- package/dist/examples/transcription/parakeet-sortformer-streaming.js +121 -0
- package/dist/examples/transcription/parakeet-sortformer.js +58 -55
- package/dist/examples/transcription/parakeet-tdt-filesystem.js +18 -15
- package/dist/examples/transcription/whispercpp-microphone-conversation.js +6 -1
- package/dist/examples/tts/chatterbox.js +7 -16
- package/dist/examples/tts/llm-to-tts-streaming.js +3 -10
- package/dist/examples/tts/supertonic-multilingual.js +4 -12
- package/dist/examples/tts/supertonic.js +5 -12
- package/dist/examples/vla-smolvla.d.ts +2 -0
- package/dist/examples/vla-smolvla.js +75 -0
- package/dist/examples/voice-assistant/voice-assistant.js +3 -10
- package/dist/expo/plugins/find-in-ancestor-node-modules.d.ts +14 -0
- package/dist/expo/plugins/find-in-ancestor-node-modules.js +24 -0
- package/dist/expo/plugins/get-project-root.d.ts +22 -0
- package/dist/expo/plugins/get-project-root.js +23 -0
- package/dist/expo/plugins/index.d.ts +4 -0
- package/dist/expo/plugins/index.js +4 -0
- package/dist/expo/plugins/resolve-sdk-package-dir.d.ts +9 -2
- package/dist/expo/plugins/resolve-sdk-package-dir.js +38 -11
- package/dist/expo/plugins/withDeviceInfo.js +2 -1
- package/dist/expo/plugins/withMobileBundle.d.ts +2 -17
- package/dist/expo/plugins/withMobileBundle.js +31 -85
- package/dist/index.d.ts +5 -3
- package/dist/index.js +9 -3
- package/dist/models/registry/models.d.ts +3711 -368
- package/dist/models/registry/models.js +7066 -3562
- package/dist/models/update-models/naming.js +22 -8
- package/dist/pear/pre.js +6 -2
- package/dist/schemas/cancel.d.ts +5 -0
- package/dist/schemas/cancel.js +1 -0
- package/dist/schemas/classification.d.ts +53 -0
- package/dist/schemas/classification.js +33 -0
- package/dist/schemas/common.d.ts +1001 -1739
- package/dist/schemas/common.js +6 -1
- package/dist/schemas/completion-event.d.ts +3 -0
- package/dist/schemas/completion-event.js +1 -0
- package/dist/schemas/completion-stream.d.ts +1 -0
- package/dist/schemas/download-asset.d.ts +13 -3
- package/dist/schemas/engine-addon-map.d.ts +3 -0
- package/dist/schemas/engine-addon-map.js +18 -5
- package/dist/schemas/get-model-info.d.ts +4 -0
- package/dist/schemas/get-model-info.js +2 -0
- package/dist/schemas/index.d.ts +3 -0
- package/dist/schemas/index.js +3 -0
- package/dist/schemas/llamacpp-config.d.ts +12 -2
- package/dist/schemas/load-model.d.ts +6152 -10764
- package/dist/schemas/load-model.js +75 -7
- package/dist/schemas/model-src-utils.d.ts +22 -2
- package/dist/schemas/model-types.d.ts +46 -6
- package/dist/schemas/model-types.js +28 -5
- package/dist/schemas/ocr.d.ts +5 -0
- package/dist/schemas/plugin.d.ts +20 -5
- package/dist/schemas/plugin.js +21 -4
- package/dist/schemas/registry.d.ts +44 -0
- package/dist/schemas/registry.js +5 -0
- package/dist/schemas/reload-config.d.ts +22 -2
- package/dist/schemas/sdcpp-config.d.ts +316 -1
- package/dist/schemas/sdcpp-config.js +232 -33
- package/dist/schemas/sdk-config.d.ts +654 -1324
- package/dist/schemas/sdk-config.js +16 -1
- package/dist/schemas/sdk-errors-client.d.ts +7 -0
- package/dist/schemas/sdk-errors-client.js +36 -1
- package/dist/schemas/sdk-errors-server.d.ts +3 -1
- package/dist/schemas/sdk-errors-server.js +20 -5
- package/dist/schemas/text-to-speech.d.ts +79 -696
- package/dist/schemas/text-to-speech.js +46 -19
- package/dist/schemas/transcription-config.d.ts +37 -226
- package/dist/schemas/transcription-config.js +76 -15
- package/dist/schemas/transcription.d.ts +63 -11
- package/dist/schemas/transcription.js +56 -2
- package/dist/schemas/translation-config.d.ts +60 -10
- package/dist/schemas/vla.d.ts +87 -0
- package/dist/schemas/vla.js +108 -0
- package/dist/scripts/run-unit-tests.js +8 -2
- package/dist/server/bare/ops/transcribe.d.ts +2 -1
- package/dist/server/bare/ops/transcribe.js +59 -34
- package/dist/server/bare/plugins/ggml-classification/ops/classify.d.ts +6 -0
- package/dist/server/bare/plugins/ggml-classification/ops/classify.js +30 -0
- package/dist/server/bare/plugins/ggml-classification/plugin.d.ts +32 -0
- package/dist/server/bare/plugins/ggml-classification/plugin.js +55 -0
- package/dist/server/bare/plugins/ggml-vla/ops/vla-hparams.d.ts +3 -0
- package/dist/server/bare/plugins/ggml-vla/ops/vla-hparams.js +12 -0
- package/dist/server/bare/plugins/ggml-vla/ops/vla-run.d.ts +3 -0
- package/dist/server/bare/plugins/ggml-vla/ops/vla-run.js +75 -0
- package/dist/server/bare/plugins/ggml-vla/plugin.d.ts +54 -0
- package/dist/server/bare/plugins/ggml-vla/plugin.js +57 -0
- package/dist/server/bare/plugins/index.d.ts +3 -1
- package/dist/server/bare/plugins/index.js +3 -1
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +3 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/context-overflow.d.ts +29 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/context-overflow.js +65 -0
- package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +6 -0
- package/dist/server/bare/plugins/llamacpp-completion/plugin.js +26 -0
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +25 -0
- package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +5 -0
- package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +67 -246
- package/dist/server/bare/plugins/parakeet-transcription/plugin.js +66 -97
- package/dist/server/bare/plugins/sdcpp-generation/ops/video.d.ts +3 -0
- package/dist/server/bare/plugins/sdcpp-generation/ops/video.js +199 -0
- package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +200 -2
- package/dist/server/bare/plugins/sdcpp-generation/plugin.js +61 -8
- package/dist/server/bare/plugins/{onnx-tts ā tts-ggml}/ops/text-to-speech-stream.js +2 -15
- package/dist/server/bare/plugins/{onnx-tts ā tts-ggml}/ops/text-to-speech.js +2 -1
- package/dist/server/bare/plugins/tts-ggml/plugin.d.ts +75 -0
- package/dist/server/bare/plugins/tts-ggml/plugin.js +189 -0
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +29 -4
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +12 -1
- package/dist/server/bare/registry/model-config-utils.js +7 -0
- package/dist/server/bare/runtime/request-context.d.ts +1 -1
- package/dist/server/bare/runtime-lifecycle.js +9 -9
- package/dist/server/bare/types/addon-responses.d.ts +1 -0
- package/dist/server/env.js +4 -1
- package/dist/server/plugins/index.d.ts +1 -1
- package/dist/server/plugins/index.js +1 -1
- package/dist/server/plugins/registry.d.ts +1 -0
- package/dist/server/plugins/registry.js +5 -0
- package/dist/server/rpc/handler-registry.js +4 -0
- package/dist/server/rpc/handler-selection.d.ts +9 -0
- package/dist/server/rpc/handler-selection.js +14 -0
- package/dist/server/rpc/handler-utils.js +5 -13
- package/dist/server/rpc/handlers/classify.d.ts +3 -0
- package/dist/server/rpc/handlers/classify.js +4 -0
- package/dist/server/rpc/handlers/index.d.ts +2 -0
- package/dist/server/rpc/handlers/index.js +2 -0
- package/dist/server/rpc/handlers/load-model/handler.js +4 -1
- package/dist/server/rpc/handlers/load-model/resolve.js +9 -0
- package/dist/server/rpc/handlers/video-stream.d.ts +3 -0
- package/dist/server/rpc/handlers/video-stream.js +4 -0
- package/dist/server/rpc/profiling/operation-metrics.js +23 -0
- package/dist/server/utils/config-transform.js +1 -1
- package/dist/server/utils/tools/parsers/qwen35.js +7 -3
- package/dist/server/worker.js +14 -11
- package/dist/utils/errors-client.d.ts +23 -0
- package/dist/utils/errors-client.js +40 -0
- package/dist/utils/errors-server.d.ts +30 -2
- package/dist/utils/errors-server.js +51 -3
- package/package.json +66 -80
- package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +0 -490
- package/dist/server/bare/plugins/onnx-tts/plugin.js +0 -272
- package/dist/server/bare/plugins/onnx-tts/wav-helper.d.ts +0 -14
- package/dist/server/bare/plugins/onnx-tts/wav-helper.js +0 -107
- /package/dist/server/bare/plugins/{onnx-tts ā tts-ggml}/ops/text-to-speech-stream.d.ts +0 -0
- /package/dist/server/bare/plugins/{onnx-tts ā tts-ggml}/ops/text-to-speech.d.ts +0 -0
|
@@ -10,7 +10,7 @@ import { fileURLToPath } from "node:url";
|
|
|
10
10
|
import { initializeConfig } from "../init-hooks.js";
|
|
11
11
|
import { resolveConfig } from "../config-loader/resolve-config.node.js";
|
|
12
12
|
import { getClientLogger } from "../../logging/index.js";
|
|
13
|
-
import { RPCInitTimeoutError } from "../../utils/errors-client.js";
|
|
13
|
+
import { RPCInitTimeoutError, WorkerCrashedError, WorkerShutdownError, } from "../../utils/errors-client.js";
|
|
14
14
|
const RPC_INIT_TIMEOUT_MS = 30_000;
|
|
15
15
|
const logger = getClientLogger();
|
|
16
16
|
let rpcInstance = null;
|
|
@@ -19,6 +19,11 @@ let bareWorkerProc = null;
|
|
|
19
19
|
let ipcServer = null;
|
|
20
20
|
let currentSocketPath = null;
|
|
21
21
|
let closePromise = null;
|
|
22
|
+
// Aborted when the worker dies (crash) or close() runs (planned). Used
|
|
23
|
+
// to unblock in-flight `req.reply()` callers ā bare-rpc's `_onerror`
|
|
24
|
+
// does not iterate `_outgoingRequests`, so without this signal they
|
|
25
|
+
// would hang on the dead socket.
|
|
26
|
+
let workerLifeController = null;
|
|
22
27
|
/**
|
|
23
28
|
* Find project root by looking for package.json (sync version)
|
|
24
29
|
* Safe to call at module load time
|
|
@@ -148,9 +153,42 @@ function snapshotAndResetState() {
|
|
|
148
153
|
bareWorkerProc = null;
|
|
149
154
|
ipcServer = null;
|
|
150
155
|
currentSocketPath = null;
|
|
156
|
+
workerLifeController = null;
|
|
151
157
|
return { workerToClose, serverToClose, socketPathToClose };
|
|
152
158
|
}
|
|
159
|
+
export function getWorkerLifeSignal() {
|
|
160
|
+
return workerLifeController?.signal ?? null;
|
|
161
|
+
}
|
|
162
|
+
// `spawn` carries this listener's own captured resources. Module-level
|
|
163
|
+
// state may belong to a newer ensureRPC() by the time we fire.
|
|
164
|
+
function handlePostHandshakeExit(code, exitSignal, spawn) {
|
|
165
|
+
if (spawn.controller.signal.aborted) {
|
|
166
|
+
// close() already handled teardown.
|
|
167
|
+
logger.debug(`Bare worker exited after planned shutdown (code=${code}, signal=${exitSignal})`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
logger.info(`šŖ¦ Bare worker exited post-handshake (code=${code}, signal=${exitSignal})`);
|
|
171
|
+
// Only clear module state if we're still the active spawn.
|
|
172
|
+
if (workerLifeController === spawn.controller) {
|
|
173
|
+
rpcInstance = null;
|
|
174
|
+
rpcPromise = null;
|
|
175
|
+
bareWorkerProc = null;
|
|
176
|
+
ipcServer = null;
|
|
177
|
+
currentSocketPath = null;
|
|
178
|
+
workerLifeController = null;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
spawn.server.close();
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
logger.debug("Failed to close IPC server after worker crash", { err });
|
|
185
|
+
}
|
|
186
|
+
spawn.controller.abort(new WorkerCrashedError(code, exitSignal));
|
|
187
|
+
bestEffortUnlinkSocket(spawn.socketPath);
|
|
188
|
+
}
|
|
153
189
|
function closeSyncForExit() {
|
|
190
|
+
// Abort before kill so the exit handler sees planned intent.
|
|
191
|
+
workerLifeController?.abort(new WorkerShutdownError());
|
|
154
192
|
const { workerToClose, serverToClose, socketPathToClose } = snapshotAndResetState();
|
|
155
193
|
if (workerToClose) {
|
|
156
194
|
try {
|
|
@@ -180,6 +218,9 @@ async function ensureRPC() {
|
|
|
180
218
|
}
|
|
181
219
|
const socketPath = createSocketPath();
|
|
182
220
|
currentSocketPath = socketPath;
|
|
221
|
+
// Allocated here so the init race and exit handler share one controller.
|
|
222
|
+
const spawnController = new AbortController();
|
|
223
|
+
workerLifeController = spawnController;
|
|
183
224
|
rpcPromise = new Promise((resolve, reject) => {
|
|
184
225
|
let settled = false;
|
|
185
226
|
const timer = setTimeout(() => {
|
|
@@ -191,6 +232,7 @@ async function ensureRPC() {
|
|
|
191
232
|
bareWorkerProc = null;
|
|
192
233
|
ipcServer = null;
|
|
193
234
|
currentSocketPath = null;
|
|
235
|
+
workerLifeController = null;
|
|
194
236
|
reject(new RPCInitTimeoutError(RPC_INIT_TIMEOUT_MS));
|
|
195
237
|
}, RPC_INIT_TIMEOUT_MS);
|
|
196
238
|
ipcServer = createServer((socket) => {
|
|
@@ -211,23 +253,35 @@ async function ensureRPC() {
|
|
|
211
253
|
bareWorkerProc = null;
|
|
212
254
|
ipcServer = null;
|
|
213
255
|
currentSocketPath = null;
|
|
256
|
+
workerLifeController = null;
|
|
214
257
|
reject(error);
|
|
215
258
|
});
|
|
216
259
|
ipcServer.listen(socketPath, () => {
|
|
260
|
+
const spawnResources = {
|
|
261
|
+
controller: spawnController,
|
|
262
|
+
server: ipcServer,
|
|
263
|
+
socketPath,
|
|
264
|
+
};
|
|
217
265
|
bareWorkerProc = spawn("bare", {
|
|
218
266
|
args: [
|
|
219
267
|
WORKER_PATH,
|
|
220
268
|
JSON.stringify({
|
|
221
269
|
QVAC_IPC_SOCKET_PATH: socketPath,
|
|
222
|
-
|
|
270
|
+
// Snap's HOME can be revision-scoped; SNAP_USER_COMMON is stable.
|
|
271
|
+
HOME_DIR: process.env["SNAP_USER_COMMON"]
|
|
272
|
+
? String(process.env["SNAP_USER_COMMON"])
|
|
273
|
+
: os.homedir(),
|
|
223
274
|
}),
|
|
224
275
|
],
|
|
225
276
|
stdio: ["inherit", "inherit", "inherit"],
|
|
226
277
|
});
|
|
227
278
|
if (bareWorkerProc) {
|
|
228
|
-
bareWorkerProc.on("exit", (code) => {
|
|
229
|
-
if (settled)
|
|
279
|
+
bareWorkerProc.on("exit", (code, exitSignal) => {
|
|
280
|
+
if (settled) {
|
|
281
|
+
handlePostHandshakeExit(code, exitSignal, spawnResources);
|
|
230
282
|
return;
|
|
283
|
+
}
|
|
284
|
+
// Worker died before handshake ā reject the init promise.
|
|
231
285
|
settled = true;
|
|
232
286
|
clearTimeout(timer);
|
|
233
287
|
rpcPromise = null;
|
|
@@ -235,6 +289,7 @@ async function ensureRPC() {
|
|
|
235
289
|
bareWorkerProc = null;
|
|
236
290
|
ipcServer = null;
|
|
237
291
|
currentSocketPath = null;
|
|
292
|
+
workerLifeController = null;
|
|
238
293
|
reject(new RPCInitTimeoutError(RPC_INIT_TIMEOUT_MS, new Error(`Worker process exited with code ${code} before IPC connection was established`)));
|
|
239
294
|
});
|
|
240
295
|
}
|
|
@@ -245,9 +300,25 @@ async function ensureRPC() {
|
|
|
245
300
|
runtime: "node",
|
|
246
301
|
platform: process.platform,
|
|
247
302
|
};
|
|
248
|
-
|
|
303
|
+
// init-hooks calls bare-rpc directly, bypassing rpc-client's race.
|
|
304
|
+
await Promise.race([
|
|
305
|
+
initializeConfig(rpc, resolveConfig, runtimeContext),
|
|
306
|
+
rejectOnAbort(spawnController.signal),
|
|
307
|
+
]);
|
|
249
308
|
return rpc;
|
|
250
309
|
}
|
|
310
|
+
function rejectOnAbort(signal) {
|
|
311
|
+
return new Promise((_, reject) => {
|
|
312
|
+
const fail = () => reject(signal.reason instanceof Error
|
|
313
|
+
? signal.reason
|
|
314
|
+
: new WorkerCrashedError(null, null));
|
|
315
|
+
if (signal.aborted) {
|
|
316
|
+
fail();
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
signal.addEventListener("abort", fail, { once: true });
|
|
320
|
+
});
|
|
321
|
+
}
|
|
251
322
|
export async function getRPC() {
|
|
252
323
|
return ensureRPC();
|
|
253
324
|
}
|
|
@@ -257,6 +328,24 @@ export async function createDuplexSession(payload, commandId) {
|
|
|
257
328
|
const requestStream = req.createRequestStream();
|
|
258
329
|
const responseStream = req.createResponseStream({ encoding: "utf-8" });
|
|
259
330
|
requestStream.write(payload, "utf-8");
|
|
331
|
+
// Destroy on worker death so consumers' for-await throws instead of hangs.
|
|
332
|
+
const lifeSignal = workerLifeController?.signal;
|
|
333
|
+
if (lifeSignal && !lifeSignal.aborted) {
|
|
334
|
+
const onAbort = () => {
|
|
335
|
+
const err = lifeSignal.reason instanceof Error
|
|
336
|
+
? lifeSignal.reason
|
|
337
|
+
: new WorkerCrashedError(null, null);
|
|
338
|
+
try {
|
|
339
|
+
requestStream.destroy?.(err);
|
|
340
|
+
}
|
|
341
|
+
catch { }
|
|
342
|
+
try {
|
|
343
|
+
responseStream.destroy?.(err);
|
|
344
|
+
}
|
|
345
|
+
catch { }
|
|
346
|
+
};
|
|
347
|
+
lifeSignal.addEventListener("abort", onAbort, { once: true });
|
|
348
|
+
}
|
|
260
349
|
return { requestStream, responseStream };
|
|
261
350
|
}
|
|
262
351
|
export async function close() {
|
|
@@ -267,6 +356,9 @@ export async function close() {
|
|
|
267
356
|
if (!rpcInstance && !rpcPromise && !bareWorkerProc && !ipcServer)
|
|
268
357
|
return;
|
|
269
358
|
logger.info("š§¹ Closing RPC client");
|
|
359
|
+
// Abort before kill: exit handler sees planned intent; any in-flight
|
|
360
|
+
// caller (contract violators) rejects with WorkerShutdownError.
|
|
361
|
+
workerLifeController?.abort(new WorkerShutdownError());
|
|
270
362
|
const { workerToClose, serverToClose, socketPathToClose } = snapshotAndResetState();
|
|
271
363
|
closePromise = (async () => {
|
|
272
364
|
if (workerToClose) {
|
|
@@ -2,7 +2,8 @@ import { requestSchema, responseSchema, PROFILING_TRAILER_KEY, } from "../../sch
|
|
|
2
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
|
-
import { getRPC, close as closeRPC, createDuplexSession } from "#rpc";
|
|
5
|
+
import { getRPC, close as closeRPC, createDuplexSession, getWorkerLifeSignal, } from "#rpc";
|
|
6
|
+
import { WorkerCrashedError } from "../../utils/errors-client.js";
|
|
6
7
|
import { nowMs, shouldProfile, shouldIncludeServerBreakdown, generateId as createProfileId, createProfilingMeta, createProfilingDisabledMeta, injectProfilingMetaIntoObject, extractProfilingMeta, stripProfilingMeta, recordFailure, } from "../../profiling/index.js";
|
|
7
8
|
import { createClientTimings, createClientStreamTimings, recordClientEvents, recordClientStreamEvents, cacheConnectionTime, flushConnectionTime, resetConnectionTracking, } from "./profiling/index.js";
|
|
8
9
|
const logger = getClientLogger();
|
|
@@ -13,6 +14,98 @@ function getNextCommandId() {
|
|
|
13
14
|
commandCounter = (commandCounter + 1) % Number.MAX_SAFE_INTEGER;
|
|
14
15
|
return commandCounter;
|
|
15
16
|
}
|
|
17
|
+
// Race in-flight reply/stream pulls against the worker-life signal ā
|
|
18
|
+
// bare-rpc's `_onerror` does not iterate `_outgoingRequests`, so without
|
|
19
|
+
// this they hang on a dead socket.
|
|
20
|
+
function lifeSignalAbortError(signal) {
|
|
21
|
+
if (signal.reason instanceof Error)
|
|
22
|
+
return signal.reason;
|
|
23
|
+
return new WorkerCrashedError(null, null);
|
|
24
|
+
}
|
|
25
|
+
async function awaitWithLifeSignal(p) {
|
|
26
|
+
// Null on Bare-direct / Expo and pre-spawn.
|
|
27
|
+
const lifeSignal = getWorkerLifeSignal();
|
|
28
|
+
if (!lifeSignal)
|
|
29
|
+
return p;
|
|
30
|
+
if (lifeSignal.aborted)
|
|
31
|
+
throw lifeSignalAbortError(lifeSignal);
|
|
32
|
+
return await new Promise((resolve, reject) => {
|
|
33
|
+
let settled = false;
|
|
34
|
+
const onAbort = () => {
|
|
35
|
+
if (settled)
|
|
36
|
+
return;
|
|
37
|
+
settled = true;
|
|
38
|
+
lifeSignal.removeEventListener("abort", onAbort);
|
|
39
|
+
reject(lifeSignalAbortError(lifeSignal));
|
|
40
|
+
};
|
|
41
|
+
lifeSignal.addEventListener("abort", onAbort, { once: true });
|
|
42
|
+
p.then((value) => {
|
|
43
|
+
if (settled)
|
|
44
|
+
return;
|
|
45
|
+
settled = true;
|
|
46
|
+
lifeSignal.removeEventListener("abort", onAbort);
|
|
47
|
+
resolve(value);
|
|
48
|
+
}, (err) => {
|
|
49
|
+
if (settled)
|
|
50
|
+
return;
|
|
51
|
+
settled = true;
|
|
52
|
+
lifeSignal.removeEventListener("abort", onAbort);
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors -- forwarding upstream rejection as-is
|
|
54
|
+
reject(err);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async function* iterateWithLifeSignal(source) {
|
|
59
|
+
// Null on Bare-direct / Expo and pre-spawn.
|
|
60
|
+
const lifeSignal = getWorkerLifeSignal();
|
|
61
|
+
if (!lifeSignal) {
|
|
62
|
+
yield* source;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (lifeSignal.aborted)
|
|
66
|
+
throw lifeSignalAbortError(lifeSignal);
|
|
67
|
+
try {
|
|
68
|
+
while (true) {
|
|
69
|
+
const nextP = source.next();
|
|
70
|
+
const result = await new Promise((resolve, reject) => {
|
|
71
|
+
let settled = false;
|
|
72
|
+
const onAbort = () => {
|
|
73
|
+
if (settled)
|
|
74
|
+
return;
|
|
75
|
+
settled = true;
|
|
76
|
+
lifeSignal.removeEventListener("abort", onAbort);
|
|
77
|
+
reject(lifeSignalAbortError(lifeSignal));
|
|
78
|
+
};
|
|
79
|
+
lifeSignal.addEventListener("abort", onAbort, { once: true });
|
|
80
|
+
nextP.then((value) => {
|
|
81
|
+
if (settled)
|
|
82
|
+
return;
|
|
83
|
+
settled = true;
|
|
84
|
+
lifeSignal.removeEventListener("abort", onAbort);
|
|
85
|
+
resolve(value);
|
|
86
|
+
}, (err) => {
|
|
87
|
+
if (settled)
|
|
88
|
+
return;
|
|
89
|
+
settled = true;
|
|
90
|
+
lifeSignal.removeEventListener("abort", onAbort);
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors -- forwarding upstream rejection as-is
|
|
92
|
+
reject(err);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
if (result.done)
|
|
96
|
+
return;
|
|
97
|
+
yield result.value;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
// Fire-and-forget cleanup. If the source is suspended on a never-
|
|
102
|
+
// settling read (worker dead), `.return()` propagates an abrupt
|
|
103
|
+
// completion through the for-await chain so inner streams get
|
|
104
|
+
// destroyed. The `.catch` swallows the case where the underlying
|
|
105
|
+
// stream cannot honour `.return()` cleanly.
|
|
106
|
+
void source.return?.(undefined).catch(() => { });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
16
109
|
function checkAndThrowError(response) {
|
|
17
110
|
if (response.type === "error") {
|
|
18
111
|
// Use the typed-error reconstructor map in `rpc-error.ts` so the
|
|
@@ -23,12 +116,28 @@ function checkAndThrowError(response) {
|
|
|
23
116
|
throw reconstructError(response);
|
|
24
117
|
}
|
|
25
118
|
}
|
|
119
|
+
function invalidateRPCInstance() {
|
|
120
|
+
rpcInstance = null;
|
|
121
|
+
firstConnectionPending = true;
|
|
122
|
+
resetConnectionTracking();
|
|
123
|
+
}
|
|
26
124
|
async function getRPCInstance() {
|
|
27
125
|
if (rpcInstance)
|
|
28
126
|
return { rpc: await rpcInstance };
|
|
29
127
|
const connectionStart = firstConnectionPending ? nowMs() : null;
|
|
30
128
|
rpcInstance = getRPC();
|
|
31
129
|
const rpc = await rpcInstance;
|
|
130
|
+
// Drop the cache on worker death so the next call respawns and
|
|
131
|
+
// records connectionMs again as a "first" connection.
|
|
132
|
+
const lifeSignal = getWorkerLifeSignal();
|
|
133
|
+
if (lifeSignal?.aborted) {
|
|
134
|
+
invalidateRPCInstance();
|
|
135
|
+
}
|
|
136
|
+
else if (lifeSignal) {
|
|
137
|
+
lifeSignal.addEventListener("abort", invalidateRPCInstance, {
|
|
138
|
+
once: true,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
32
141
|
if (connectionStart !== null && firstConnectionPending) {
|
|
33
142
|
firstConnectionPending = false;
|
|
34
143
|
return { rpc, connectionMs: nowMs() - connectionStart };
|
|
@@ -63,7 +172,7 @@ async function sendBase(request, rpc, options, signalDisable = false) {
|
|
|
63
172
|
: parsedRequest;
|
|
64
173
|
const payload = JSON.stringify(payloadObj);
|
|
65
174
|
req.send(payload, "utf-8");
|
|
66
|
-
const response = await withTimeout(req.reply("utf-8"), options?.timeout);
|
|
175
|
+
const response = await awaitWithLifeSignal(withTimeout(req.reply("utf-8"), options?.timeout));
|
|
67
176
|
const resPayload = responseSchema.parse(JSON.parse(response?.toString() || "{}"));
|
|
68
177
|
logger.debug("ResPayload", { type: resPayload.type });
|
|
69
178
|
checkAndThrowError(resPayload);
|
|
@@ -87,7 +196,7 @@ async function sendProfiled(request, rpc, options) {
|
|
|
87
196
|
timings.requestStringifyMs = nowMs() - stringifyStart;
|
|
88
197
|
timings.sendStart = nowMs();
|
|
89
198
|
req.send(payload, "utf-8");
|
|
90
|
-
const response = await withTimeout(req.reply("utf-8"), options?.timeout);
|
|
199
|
+
const response = await awaitWithLifeSignal(withTimeout(req.reply("utf-8"), options?.timeout));
|
|
91
200
|
timings.firstResponseAt = nowMs();
|
|
92
201
|
const parseStart = nowMs();
|
|
93
202
|
const rawParsed = JSON.parse(response?.toString() || "{}");
|
|
@@ -140,7 +249,7 @@ async function* streamBase(request, rpc, options = {}, signalDisable = false) {
|
|
|
140
249
|
}
|
|
141
250
|
}
|
|
142
251
|
const streamWithTimeout = withTimeoutStream(processStream(), options?.timeout);
|
|
143
|
-
for await (const chunk of streamWithTimeout) {
|
|
252
|
+
for await (const chunk of iterateWithLifeSignal(streamWithTimeout)) {
|
|
144
253
|
buffer += chunk.toString();
|
|
145
254
|
// Process complete lines (newline-delimited JSON)
|
|
146
255
|
const lines = buffer.split("\n");
|
|
@@ -181,7 +290,7 @@ async function* streamProfiled(request, rpc, options = {}) {
|
|
|
181
290
|
}
|
|
182
291
|
}
|
|
183
292
|
const streamWithTimeout = withTimeoutStream(processStream(), options?.timeout);
|
|
184
|
-
for await (const chunk of streamWithTimeout) {
|
|
293
|
+
for await (const chunk of iterateWithLifeSignal(streamWithTimeout)) {
|
|
185
294
|
const chunkTime = nowMs();
|
|
186
295
|
if (timings.firstChunkAt === undefined) {
|
|
187
296
|
timings.firstChunkAt = chunkTime;
|
|
@@ -361,8 +470,6 @@ async function duplexProfiled(request, options = {}) {
|
|
|
361
470
|
export async function close() {
|
|
362
471
|
if (!rpcInstance)
|
|
363
472
|
return;
|
|
364
|
-
|
|
365
|
-
firstConnectionPending = true;
|
|
366
|
-
resetConnectionTracking();
|
|
473
|
+
invalidateRPCInstance();
|
|
367
474
|
await closeRPC();
|
|
368
475
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RequestIdConflictError, RequestNotFoundError, RequestRejectedByPolicyError, } from "../../utils/errors-server.js";
|
|
1
|
+
import { ContextOverflowError, RequestIdConflictError, RequestNotFoundError, RequestRejectedByPolicyError, } from "../../utils/errors-server.js";
|
|
2
2
|
export class RPCError extends Error {
|
|
3
3
|
timestamp;
|
|
4
4
|
remoteStack;
|
|
@@ -59,6 +59,16 @@ function readStringField(fields, key, fallback) {
|
|
|
59
59
|
const value = fields?.[key];
|
|
60
60
|
return typeof value === "string" ? value : fallback;
|
|
61
61
|
}
|
|
62
|
+
/** Read a number field from the `typedFields` envelope, returning `undefined` if missing or non-number. */
|
|
63
|
+
function readOptionalNumberField(fields, key) {
|
|
64
|
+
const value = fields?.[key];
|
|
65
|
+
return typeof value === "number" ? value : undefined;
|
|
66
|
+
}
|
|
67
|
+
/** Read a string field, returning `undefined` (not a fallback string) if missing. */
|
|
68
|
+
function readOptionalStringField(fields, key) {
|
|
69
|
+
const value = fields?.[key];
|
|
70
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
71
|
+
}
|
|
62
72
|
/**
|
|
63
73
|
* Map of server-thrown `QvacErrorBase` subclasses that need to survive
|
|
64
74
|
* the RPC boundary as their original class ā `err instanceof
|
|
@@ -98,6 +108,9 @@ const RECONSTRUCTORS = {
|
|
|
98
108
|
REQUEST_REJECTED_BY_POLICY: (response) => {
|
|
99
109
|
return new RequestRejectedByPolicyError(readStringField(response.typedFields, "requestId", ""), readStringField(response.typedFields, "kind", ""), readStringField(response.typedFields, "modelId", ""), readStringField(response.typedFields, "reason", response.message), response.cause);
|
|
100
110
|
},
|
|
111
|
+
CONTEXT_OVERFLOW: (response) => {
|
|
112
|
+
return new ContextOverflowError(readOptionalNumberField(response.typedFields, "promptTokens"), readOptionalNumberField(response.typedFields, "ctxSize"), readOptionalStringField(response.typedFields, "modelId"), response.cause);
|
|
113
|
+
},
|
|
101
114
|
};
|
|
102
115
|
/**
|
|
103
116
|
* Rebuild the original server-thrown typed error from its serialised
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Logger } from "../../logging/types";
|
|
2
|
+
interface RunBarePackOptions {
|
|
3
|
+
entryPath: string;
|
|
4
|
+
outputPath: string;
|
|
5
|
+
hosts: string[];
|
|
6
|
+
importsMapPath: string;
|
|
7
|
+
deferModules: string[];
|
|
8
|
+
quiet: boolean;
|
|
9
|
+
logger: Logger;
|
|
10
|
+
}
|
|
11
|
+
export declare function runBarePack(options: RunBarePackOptions): Promise<void>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=bare-pack.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { platform, execPath } from "node:process";
|
|
6
|
+
import { BarePackNotInstalledError, BarePackError, } from "../../utils/errors-client.js";
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
function resolveBarePackBin() {
|
|
9
|
+
try {
|
|
10
|
+
const barePackPkgPath = require.resolve("bare-pack/package");
|
|
11
|
+
const barePackDir = path.dirname(barePackPkgPath);
|
|
12
|
+
return path.join(barePackDir, "bin.js");
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function runBarePack(options) {
|
|
19
|
+
const { entryPath, outputPath, hosts, importsMapPath, deferModules, quiet, logger, } = options;
|
|
20
|
+
const barePackBin = resolveBarePackBin();
|
|
21
|
+
if (!barePackBin || !fs.existsSync(barePackBin)) {
|
|
22
|
+
throw new BarePackNotInstalledError();
|
|
23
|
+
}
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
const hostArgs = hosts.flatMap((h) => ["--host", h]);
|
|
26
|
+
const deferArgs = deferModules.flatMap((m) => ["--defer", m]);
|
|
27
|
+
const args = [
|
|
28
|
+
...hostArgs,
|
|
29
|
+
"--linked",
|
|
30
|
+
"--imports",
|
|
31
|
+
importsMapPath,
|
|
32
|
+
...deferArgs,
|
|
33
|
+
"--out",
|
|
34
|
+
outputPath,
|
|
35
|
+
entryPath,
|
|
36
|
+
];
|
|
37
|
+
const isWindows = platform === "win32";
|
|
38
|
+
const command = isWindows ? execPath : barePackBin;
|
|
39
|
+
const spawnArgs = isWindows ? [barePackBin, ...args] : args;
|
|
40
|
+
logger.debug(`\nš¦ Running: ${command} ${spawnArgs.join(" ")}`);
|
|
41
|
+
const proc = spawn(command, spawnArgs, {
|
|
42
|
+
stdio: quiet ? "ignore" : "inherit",
|
|
43
|
+
});
|
|
44
|
+
proc.on("close", (code) => {
|
|
45
|
+
if (code === 0) {
|
|
46
|
+
resolve();
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
reject(new BarePackError(code ?? 1, entryPath, outputPath));
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
proc.on("error", reject);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const BUILTIN_PLUGINS: Record<string, {
|
|
2
|
+
exportName: string;
|
|
3
|
+
}>;
|
|
4
|
+
export declare const BUILTIN_SUFFIXES: string[];
|
|
5
|
+
export declare const DEFAULT_HOSTS: string[];
|
|
6
|
+
export declare const DEFAULT_SDK_NAME = "@qvac/sdk";
|
|
7
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const BUILTIN_PLUGINS = {
|
|
2
|
+
"llamacpp-completion": { exportName: "llmPlugin" },
|
|
3
|
+
"llamacpp-embedding": { exportName: "embeddingsPlugin" },
|
|
4
|
+
"whispercpp-transcription": { exportName: "whisperPlugin" },
|
|
5
|
+
"parakeet-transcription": { exportName: "parakeetPlugin" },
|
|
6
|
+
"nmtcpp-translation": { exportName: "nmtPlugin" },
|
|
7
|
+
"tts-ggml": { exportName: "ttsPlugin" },
|
|
8
|
+
"onnx-ocr": { exportName: "ocrPlugin" },
|
|
9
|
+
"sdcpp-generation": { exportName: "diffusionPlugin" },
|
|
10
|
+
};
|
|
11
|
+
export const BUILTIN_SUFFIXES = Object.keys(BUILTIN_PLUGINS);
|
|
12
|
+
export const DEFAULT_HOSTS = [
|
|
13
|
+
"darwin-arm64",
|
|
14
|
+
"darwin-x64",
|
|
15
|
+
"linux-arm64",
|
|
16
|
+
"linux-x64",
|
|
17
|
+
"win32-x64",
|
|
18
|
+
"android-arm64",
|
|
19
|
+
"ios-arm64",
|
|
20
|
+
"ios-arm64-simulator",
|
|
21
|
+
"ios-x64-simulator",
|
|
22
|
+
];
|
|
23
|
+
export const DEFAULT_SDK_NAME = "@qvac/sdk";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { parseBuiltinSpecifier } from "./plugins.js";
|
|
2
|
+
export function generateWorkerEntry(pluginSpecifiers, sdkName) {
|
|
3
|
+
const imports = [];
|
|
4
|
+
const registrations = [];
|
|
5
|
+
let varIndex = 0;
|
|
6
|
+
for (const specifier of pluginSpecifiers) {
|
|
7
|
+
const builtin = parseBuiltinSpecifier(specifier, sdkName);
|
|
8
|
+
if (builtin) {
|
|
9
|
+
imports.push(`import { ${builtin.exportName} } from "${sdkName}/${builtin.suffix}/plugin";`);
|
|
10
|
+
registrations.push(`registerPlugin(${builtin.exportName});`);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
const varName = `customPlugin${varIndex++}`;
|
|
14
|
+
imports.push(`import ${varName} from "${specifier}";`);
|
|
15
|
+
registrations.push(`registerPlugin(${varName});`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const importsStr = imports.join("\n");
|
|
19
|
+
const registrationsStr = registrations.join("\n");
|
|
20
|
+
const pluginsList = pluginSpecifiers.map((p) => `* - ${p}`).join("\n");
|
|
21
|
+
return `/**
|
|
22
|
+
* QVAC SDK Worker Entry (auto-generated)
|
|
23
|
+
* Generated by: @qvac/sdk/commands bundleSdk
|
|
24
|
+
* Plugins: ${pluginSpecifiers.length}
|
|
25
|
+
*
|
|
26
|
+
${pluginsList}
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { initializeWorkerCore, ensureRPCSetup } from "${sdkName}/worker-core";
|
|
30
|
+
import { registerPlugin } from "${sdkName}/plugins";
|
|
31
|
+
import { getServerLogger } from "${sdkName}/logging";
|
|
32
|
+
|
|
33
|
+
${importsStr}
|
|
34
|
+
|
|
35
|
+
const { hasRPCConfig } = initializeWorkerCore();
|
|
36
|
+
|
|
37
|
+
const logger = getServerLogger();
|
|
38
|
+
logger.info("š» QVAC Worker (custom bundle)");
|
|
39
|
+
logger.info("š¦ Plugins: ${pluginSpecifiers.length}");
|
|
40
|
+
|
|
41
|
+
${registrationsStr}
|
|
42
|
+
|
|
43
|
+
// Auto-setup RPC if config present
|
|
44
|
+
if (hasRPCConfig) {
|
|
45
|
+
ensureRPCSetup();
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface BundleSdkOptions {
|
|
2
|
+
projectRoot?: string | undefined;
|
|
3
|
+
configPath?: string | undefined;
|
|
4
|
+
sdkPath?: string | undefined;
|
|
5
|
+
hosts?: string[] | undefined;
|
|
6
|
+
defer?: string[] | undefined;
|
|
7
|
+
quiet?: boolean | undefined;
|
|
8
|
+
verbose?: boolean | undefined;
|
|
9
|
+
}
|
|
10
|
+
export interface BundleSdkResult {
|
|
11
|
+
bundlePath: string;
|
|
12
|
+
plugins: string[];
|
|
13
|
+
addons: string[];
|
|
14
|
+
entryPaths: {
|
|
15
|
+
worker: string;
|
|
16
|
+
};
|
|
17
|
+
manifestPath: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function bundleSdk(options?: BundleSdkOptions): Promise<BundleSdkResult>;
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|