@qvac/sdk 0.12.2 → 0.13.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 +55 -1225
- package/README.md +12 -0
- package/dist/client/api/bci-transcribe.d.ts +57 -0
- package/dist/client/api/bci-transcribe.js +172 -0
- package/dist/client/api/get-model-info.d.ts +1 -1
- package/dist/client/api/index.d.ts +1 -0
- package/dist/client/api/index.js +1 -0
- package/dist/client/api/video.d.ts +26 -6
- package/dist/client/api/video.js +30 -7
- package/dist/client/api/vla.d.ts +11 -7
- package/dist/client/api/vla.js +11 -7
- package/dist/client/rpc/bare-client.js +2 -2
- package/dist/commands/bundle/constants.js +2 -0
- package/dist/examples/bci/bci-filesystem-streaming.d.ts +2 -0
- package/dist/examples/bci/bci-filesystem-streaming.js +77 -0
- package/dist/examples/bci/bci-filesystem.d.ts +2 -0
- package/dist/examples/bci/bci-filesystem.js +63 -0
- package/dist/examples/cache-management.js +0 -1
- package/dist/examples/cancel-by-request-id.js +0 -1
- package/dist/examples/classification/classify-image.js +1 -1
- package/dist/examples/completion-events.js +6 -2
- package/dist/examples/completion-stop-reason.d.ts +25 -0
- package/dist/examples/completion-stop-reason.js +65 -0
- package/dist/examples/concurrent-completion-collision.d.ts +2 -0
- package/dist/examples/concurrent-completion-collision.js +98 -0
- package/dist/examples/config-reload.js +1 -2
- package/dist/examples/default-config-usage.js +0 -1
- package/dist/examples/delegated-inference/composite.js +0 -1
- package/dist/examples/delegated-inference/consumer-profiled.js +0 -1
- package/dist/examples/delegated-inference/consumer.js +0 -1
- package/dist/examples/diffusion-esrgan-upscale.js +1 -3
- package/dist/examples/diffusion-flux2-klein-img2img.js +1 -1
- package/dist/examples/diffusion-flux2-klein.js +2 -2
- package/dist/examples/diffusion-img2img.js +1 -1
- package/dist/examples/diffusion-img2vid.d.ts +2 -0
- package/dist/examples/diffusion-img2vid.js +78 -0
- package/dist/examples/diffusion-simple.js +1 -1
- package/dist/examples/diffusion-txt2img.js +8 -3
- package/dist/examples/diffusion-txt2vid.js +2 -3
- package/dist/examples/download-with-blind-relays.js +0 -1
- package/dist/examples/embed-multi-gpu.js +1 -2
- package/dist/examples/embed-p2p.js +4 -2
- package/dist/examples/finetune/llamacpp-finetune.js +5 -3
- package/dist/examples/kv-cache-cleanup.js +0 -1
- package/dist/examples/kv-cache-custom-key.js +0 -1
- package/dist/examples/kv-cache-example.js +4 -2
- package/dist/examples/llamacpp-cache.js +0 -1
- package/dist/examples/llamacpp-dynamic-tools.js +4 -2
- package/dist/examples/llamacpp-filesystem.js +1 -1
- package/dist/examples/llamacpp-http.js +1 -1
- package/dist/examples/llamacpp-multi-gpu.js +2 -2
- package/dist/examples/llamacpp-multimodal.js +0 -1
- package/dist/examples/llamacpp-p2p.js +0 -1
- package/dist/examples/llamacpp-sharded.js +1 -1
- package/dist/examples/llamacpp-structured-output.js +0 -1
- package/dist/examples/logging-file-transport.js +0 -1
- package/dist/examples/logging-streaming.js +0 -2
- package/dist/examples/mcp-websearch.js +0 -1
- package/dist/examples/multi-model-demo.js +0 -2
- package/dist/examples/ocr-fasttext.js +0 -1
- package/dist/examples/plugins.js +1 -4
- package/dist/examples/profiling/basic.js +3 -2
- package/dist/examples/profiling/per-call.js +0 -1
- package/dist/examples/quickstart.js +0 -1
- package/dist/examples/rag/rag-chromadb.js +0 -1
- package/dist/examples/rag/rag-hyperdb/cancellation.js +0 -1
- package/dist/examples/rag/rag-hyperdb/ingest.js +0 -1
- package/dist/examples/rag/rag-hyperdb/pipeline.js +0 -1
- package/dist/examples/rag/rag-hyperdb/workspaces.js +0 -1
- package/dist/examples/rag/rag-lancedb.js +0 -1
- package/dist/examples/rag/rag-sqlite.js +0 -1
- package/dist/examples/suspend-resume.js +0 -1
- package/dist/examples/tools/llamacpp-native-tools.js +0 -1
- package/dist/examples/tools/llamacpp-tools-gemma4.js +1 -1
- package/dist/examples/tools/llamacpp-tools-pythonic.js +2 -2
- package/dist/examples/tools/llamacpp-tools-qwen35.js +1 -1
- package/dist/examples/transcription/parakeet-ctc-filesystem.js +1 -1
- package/dist/examples/transcription/parakeet-microphone-record.js +0 -1
- package/dist/examples/transcription/parakeet-microphone-stream.js +0 -1
- package/dist/examples/transcription/parakeet-sortformer.js +1 -2
- package/dist/examples/transcription/parakeet-tdt-filesystem.js +1 -1
- package/dist/examples/transcription/whispercpp-filesystem-streaming.js +10 -6
- package/dist/examples/transcription/whispercpp-filesystem.js +4 -2
- package/dist/examples/transcription/whispercpp-microphone-conversation.js +0 -1
- package/dist/examples/transcription/whispercpp-microphone-record.js +0 -1
- package/dist/examples/transcription/whispercpp-prompt.js +0 -1
- package/dist/examples/translation/translation-bergamot-batch.js +1 -2
- package/dist/examples/translation/translation-bergamot-pivot.js +3 -4
- package/dist/examples/translation/translation-bergamot.js +1 -2
- package/dist/examples/translation/translation-indic.js +1 -2
- package/dist/examples/translation/translation-llm-afriquegemma.js +3 -4
- package/dist/examples/translation/translation-llm-context.js +1 -2
- package/dist/examples/translation/translation-llm.js +2 -3
- package/dist/examples/translation/translation-stream.js +2 -3
- package/dist/examples/tts/chatterbox.js +1 -2
- package/dist/examples/tts/llm-to-tts-streaming.js +2 -4
- package/dist/examples/tts/supertonic-multilingual.js +1 -2
- package/dist/examples/tts/supertonic.js +1 -2
- package/dist/examples/vla-pi05.d.ts +2 -0
- package/dist/examples/vla-pi05.js +84 -0
- package/dist/examples/vla-smolvla.js +1 -1
- package/dist/examples/voice-assistant/voice-assistant.js +1 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +9 -7
- package/dist/models/registry/models.d.ts +869 -186
- package/dist/models/registry/models.js +3160 -2453
- package/dist/models/update-models/companions.d.ts +7 -0
- package/dist/models/update-models/companions.js +56 -0
- package/dist/models/update-models/naming.js +10 -0
- package/dist/pear/pre.js +2 -0
- package/dist/schemas/bci-config.d.ts +36 -0
- package/dist/schemas/bci-config.js +56 -0
- package/dist/schemas/bci.d.ts +177 -0
- package/dist/schemas/bci.js +95 -0
- package/dist/schemas/common.d.ts +395 -51
- package/dist/schemas/common.js +5 -0
- package/dist/schemas/completion-event.d.ts +1 -0
- package/dist/schemas/download-asset.d.ts +7 -3
- package/dist/schemas/embed.d.ts +2 -0
- package/dist/schemas/embed.js +1 -0
- package/dist/schemas/engine-addon-map.d.ts +1 -0
- package/dist/schemas/engine-addon-map.js +4 -1
- package/dist/schemas/get-model-info.d.ts +2 -0
- package/dist/schemas/get-model-info.js +1 -0
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/llamacpp-config.d.ts +6 -2
- package/dist/schemas/load-model.d.ts +1841 -267
- package/dist/schemas/load-model.js +34 -1
- package/dist/schemas/model-src-utils.d.ts +10 -2
- package/dist/schemas/model-types.d.ts +19 -1
- package/dist/schemas/model-types.js +11 -0
- package/dist/schemas/ocr.d.ts +2 -0
- package/dist/schemas/plugin.d.ts +13 -5
- package/dist/schemas/plugin.js +13 -4
- package/dist/schemas/registry.d.ts +18 -0
- package/dist/schemas/registry.js +2 -0
- package/dist/schemas/reload-config.d.ts +10 -2
- package/dist/schemas/sdcpp-config.d.ts +75 -8
- package/dist/schemas/sdcpp-config.js +56 -10
- package/dist/schemas/sdk-config.d.ts +342 -0
- package/dist/schemas/text-to-speech.d.ts +8 -0
- package/dist/schemas/transcription-config.d.ts +2 -0
- package/dist/schemas/transcription.d.ts +12 -0
- package/dist/schemas/transcription.js +8 -0
- package/dist/schemas/translation-config.d.ts +44 -16
- package/dist/schemas/translation-config.js +2 -0
- package/dist/schemas/vla.d.ts +14 -0
- package/dist/schemas/vla.js +26 -3
- package/dist/server/bare/delegate-connect-diagnostics.d.ts +9 -0
- package/dist/server/bare/delegate-connect-diagnostics.js +44 -0
- package/dist/server/bare/delegate-rpc-client.js +10 -12
- package/dist/server/bare/hyperswarm.js +1 -0
- package/dist/server/bare/ops/bci-transcribe.d.ts +13 -0
- package/dist/server/bare/ops/bci-transcribe.js +248 -0
- package/dist/server/bare/ops/embed.js +2 -1
- package/dist/server/bare/ops/kv-cache-utils.js +4 -3
- package/dist/server/bare/ops/transcribe.js +6 -2
- package/dist/server/bare/ops/translate.js +1 -1
- package/dist/server/bare/plugins/bci-whispercpp-transcription/plugin.d.ts +130 -0
- package/dist/server/bare/plugins/bci-whispercpp-transcription/plugin.js +116 -0
- package/dist/server/bare/plugins/ggml-vla/ops/vla-run.js +4 -0
- package/dist/server/bare/plugins/ggml-vla/plugin.d.ts +7 -0
- package/dist/server/bare/plugins/ggml-vla/plugin.js +1 -1
- package/dist/server/bare/plugins/index.d.ts +1 -0
- package/dist/server/bare/plugins/index.js +1 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +22 -29
- package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +2 -0
- package/dist/server/bare/plugins/llamacpp-completion/plugin.js +38 -7
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +1 -0
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +9 -1
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +14 -0
- package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +2 -0
- package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +8 -0
- package/dist/server/bare/plugins/sdcpp-generation/ops/video.js +7 -1
- package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +58 -4
- package/dist/server/bare/plugins/sdcpp-generation/plugin.js +9 -4
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +10 -0
- package/dist/server/bare/registry/model-config-utils.js +3 -0
- package/dist/server/bare/runtime/request-registry-singleton.js +11 -4
- package/dist/server/bare/runtime/request-registry.d.ts +60 -16
- package/dist/server/bare/runtime/request-registry.js +258 -15
- package/dist/server/bare/runtime/with-request-context.d.ts +1 -1
- package/dist/server/bare/runtime/with-request-context.js +1 -1
- package/dist/server/bare/types/addon-responses.d.ts +5 -0
- package/dist/server/rpc/handler-registry.js +4 -0
- package/dist/server/rpc/handlers/bci-transcribe-stream.d.ts +3 -0
- package/dist/server/rpc/handlers/bci-transcribe-stream.js +4 -0
- package/dist/server/rpc/handlers/bci-transcribe.d.ts +3 -0
- package/dist/server/rpc/handlers/bci-transcribe.js +4 -0
- package/dist/server/rpc/handlers/download-asset.js +1 -1
- package/dist/server/rpc/handlers/get-model-info.js +3 -3
- package/dist/server/rpc/handlers/index.d.ts +4 -0
- package/dist/server/rpc/handlers/index.js +4 -0
- package/dist/server/rpc/handlers/load-model/download-manager.d.ts +1 -1
- package/dist/server/rpc/handlers/load-model/handler.js +1 -1
- package/dist/server/rpc/handlers/load-model/http.js +6 -6
- package/dist/server/rpc/handlers/load-model/hyperdrive.js +1 -0
- package/dist/server/rpc/handlers/load-model/registry-companion-set.d.ts +1 -0
- package/dist/server/rpc/handlers/load-model/registry-companion-set.js +2 -2
- package/dist/server/rpc/handlers/load-model/registry.js +1 -0
- package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +1 -1
- package/dist/server/rpc/handlers/rag.js +5 -5
- package/dist/server/utils/audio/decoder.js +1 -1
- package/dist/server/utils/checksum.js +2 -1
- package/dist/server/utils/formatting.js +2 -1
- package/dist/server/utils/multi-gpu-mobile.d.ts +5 -0
- package/dist/server/utils/multi-gpu-mobile.js +10 -0
- package/dist/server/worker.js +2 -1
- package/dist/utils/aggregate-events.d.ts +2 -1
- package/dist/utils/aggregate-events.js +11 -3
- package/dist/utils/errors-server.d.ts +6 -4
- package/dist/utils/errors-server.js +6 -4
- package/electron-forge/index.cjs +820 -0
- package/package.json +70 -37
- package/dist/scripts/run-security-tests-bare.d.ts +0 -2
- package/dist/scripts/run-security-tests-bare.js +0 -78
|
@@ -24,10 +24,30 @@ import { getServerLogger } from "../../../logging/index.js";
|
|
|
24
24
|
*/
|
|
25
25
|
const CANCEL_BEFORE_BEGIN_MAX_ENTRIES = 128;
|
|
26
26
|
const CANCEL_BEFORE_BEGIN_TTL_MS = 30_000;
|
|
27
|
+
/**
|
|
28
|
+
* Default cap on waiters queued per `(kind, modelId)` when a policy uses
|
|
29
|
+
* `onOverflow: "queue"` without an explicit `maxQueueDepthPerModel`.
|
|
30
|
+
* Bounds memory so a misbehaving client firing a flood of same-model
|
|
31
|
+
* requests can't grow the queue without limit — the (depth + 1)th begin
|
|
32
|
+
* rejects with `RequestRejectedByPolicyError` instead of enqueuing.
|
|
33
|
+
*/
|
|
34
|
+
const DEFAULT_MAX_QUEUE_DEPTH_PER_MODEL = 64;
|
|
27
35
|
export function createRequestRegistry(options) {
|
|
28
36
|
const entries = new Map();
|
|
29
37
|
const policies = new Map();
|
|
30
38
|
const logger = options?.logger ?? getServerLogger();
|
|
39
|
+
/**
|
|
40
|
+
* Per-`(kind, modelId)` admission semaphores. Keyed by
|
|
41
|
+
* `slotKey(kind, modelId)`. Absent until the first gated `begin(...)`
|
|
42
|
+
* for that key; removed again the moment the key is idle.
|
|
43
|
+
*/
|
|
44
|
+
const keyStates = new Map();
|
|
45
|
+
/**
|
|
46
|
+
* Reverse index from a queued waiter's `requestId` to its slot so
|
|
47
|
+
* `cancel({ requestId })` can pull a still-queued request out of the
|
|
48
|
+
* FIFO in O(1) without scanning every `KeyState`.
|
|
49
|
+
*/
|
|
50
|
+
const waitersById = new Map();
|
|
31
51
|
function logLifecycle(event, ctx, durationMs) {
|
|
32
52
|
const modelId = ctx.modelId !== undefined ? ctx.modelId : "-";
|
|
33
53
|
const base = `[request-lifecycle] ${event} requestId=${ctx.requestId} kind=${ctx.kind} modelId=${modelId} state=${ctx.state}`;
|
|
@@ -99,6 +119,124 @@ export function createRequestRegistry(options) {
|
|
|
99
119
|
cancelledBeforeBegin.delete(requestId);
|
|
100
120
|
return entry;
|
|
101
121
|
}
|
|
122
|
+
function slotKey(kind, modelId) {
|
|
123
|
+
// NUL separator can't appear in a kind (closed union) or a modelId
|
|
124
|
+
// (sdkModelId is a generated handle), so the join is unambiguous.
|
|
125
|
+
return `${kind}\u0000${modelId}`;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Pull a waiter out of its queue and tidy up: clear its timeout, drop
|
|
129
|
+
* the reverse index, and delete the `KeyState` if that left it idle.
|
|
130
|
+
* Does NOT settle the waiter's promise — the caller decides whether the
|
|
131
|
+
* removal resolves (granted / graceful cancel) or rejects (timeout /
|
|
132
|
+
* teardown).
|
|
133
|
+
*/
|
|
134
|
+
function removeWaiter(key, waiter) {
|
|
135
|
+
if (waiter.timer !== undefined) {
|
|
136
|
+
clearTimeout(waiter.timer);
|
|
137
|
+
waiter.timer = undefined;
|
|
138
|
+
}
|
|
139
|
+
waitersById.delete(waiter.requestId);
|
|
140
|
+
const st = keyStates.get(key);
|
|
141
|
+
if (!st)
|
|
142
|
+
return;
|
|
143
|
+
const i = st.waiters.indexOf(waiter);
|
|
144
|
+
if (i >= 0)
|
|
145
|
+
st.waiters.splice(i, 1);
|
|
146
|
+
if (st.active <= 0 && st.waiters.length === 0)
|
|
147
|
+
keyStates.delete(key);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Admission gate. Resolves to the slot key the caller must release on
|
|
151
|
+
* dispose, or `undefined` when the request is ungated (no policy, no
|
|
152
|
+
* `modelId`, an unbounded limit, or an already-aborted parent — which we
|
|
153
|
+
* let through so the normal abort path produces a clean cancelled
|
|
154
|
+
* outcome rather than queuing a doomed request).
|
|
155
|
+
*
|
|
156
|
+
* When the key is at capacity it either rejects (`onOverflow: "reject"`,
|
|
157
|
+
* queue-depth cap, or `queueTimeoutMs`) or enqueues a FIFO waiter and
|
|
158
|
+
* awaits it. The releaser hands a freed slot to the oldest waiter
|
|
159
|
+
* (`resolve(true)`), so `active` is never decremented and re-incremented
|
|
160
|
+
* across a hand-off — that's what guarantees FIFO fairness with no
|
|
161
|
+
* acquire/release race in the single-threaded event loop.
|
|
162
|
+
*/
|
|
163
|
+
async function acquireSlot(opts) {
|
|
164
|
+
if (opts.modelId === undefined)
|
|
165
|
+
return { slotKey: undefined };
|
|
166
|
+
const policy = policies.get(opts.kind);
|
|
167
|
+
if (!policy || !Number.isFinite(policy.maxConcurrent)) {
|
|
168
|
+
return { slotKey: undefined };
|
|
169
|
+
}
|
|
170
|
+
// A parent (worker-shutdown) signal that's already aborted: don't
|
|
171
|
+
// queue behind live work that may never drain — let begin() proceed
|
|
172
|
+
// and abort immediately via the parentSignal path.
|
|
173
|
+
if (opts.parentSignal?.aborted)
|
|
174
|
+
return { slotKey: undefined };
|
|
175
|
+
const modelId = opts.modelId;
|
|
176
|
+
const key = slotKey(opts.kind, modelId);
|
|
177
|
+
let st = keyStates.get(key);
|
|
178
|
+
if (!st) {
|
|
179
|
+
st = { active: 0, waiters: [] };
|
|
180
|
+
keyStates.set(key, st);
|
|
181
|
+
}
|
|
182
|
+
if (st.active < policy.maxConcurrent) {
|
|
183
|
+
st.active++;
|
|
184
|
+
return { slotKey: key };
|
|
185
|
+
}
|
|
186
|
+
if (policy.onOverflow === "reject") {
|
|
187
|
+
throw new RequestRejectedByPolicyError(opts.requestId, opts.kind, modelId, `another ${opts.kind} request is already running on this model`);
|
|
188
|
+
}
|
|
189
|
+
if (st.waiters.length >= policy.maxQueueDepth) {
|
|
190
|
+
throw new RequestRejectedByPolicyError(opts.requestId, opts.kind, modelId, `request queue for this model is full (${st.waiters.length} waiting)`);
|
|
191
|
+
}
|
|
192
|
+
const queue = st;
|
|
193
|
+
const granted = await new Promise((resolve, reject) => {
|
|
194
|
+
const waiter = {
|
|
195
|
+
requestId: opts.requestId,
|
|
196
|
+
kind: opts.kind,
|
|
197
|
+
modelId,
|
|
198
|
+
enqueuedAt: Date.now(),
|
|
199
|
+
resolve,
|
|
200
|
+
reject,
|
|
201
|
+
timer: undefined,
|
|
202
|
+
};
|
|
203
|
+
if (policy.queueTimeoutMs !== undefined) {
|
|
204
|
+
const timeoutMs = policy.queueTimeoutMs;
|
|
205
|
+
waiter.timer = setTimeout(() => {
|
|
206
|
+
removeWaiter(key, waiter);
|
|
207
|
+
reject(new RequestRejectedByPolicyError(opts.requestId, opts.kind, modelId, `timed out waiting ${timeoutMs}ms for a slot on this model`));
|
|
208
|
+
}, timeoutMs);
|
|
209
|
+
}
|
|
210
|
+
queue.waiters.push(waiter);
|
|
211
|
+
waitersById.set(opts.requestId, { key, waiter });
|
|
212
|
+
});
|
|
213
|
+
return granted ? { slotKey: key } : { slotKey: undefined };
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Hand a freed slot to the next FIFO waiter, or free it outright when
|
|
217
|
+
* none are queued. Called from `disposeEntry` after the scope has fully
|
|
218
|
+
* unwound, so the native context is genuinely free before the next
|
|
219
|
+
* same-model request is admitted.
|
|
220
|
+
*/
|
|
221
|
+
function releaseSlot(key) {
|
|
222
|
+
const st = keyStates.get(key);
|
|
223
|
+
if (!st)
|
|
224
|
+
return;
|
|
225
|
+
const next = st.waiters.shift();
|
|
226
|
+
if (next) {
|
|
227
|
+
// Hand off: the waiter inherits the slot, so `active` is unchanged.
|
|
228
|
+
waitersById.delete(next.requestId);
|
|
229
|
+
if (next.timer !== undefined) {
|
|
230
|
+
clearTimeout(next.timer);
|
|
231
|
+
next.timer = undefined;
|
|
232
|
+
}
|
|
233
|
+
next.resolve(true);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
st.active--;
|
|
237
|
+
if (st.active <= 0 && st.waiters.length === 0)
|
|
238
|
+
keyStates.delete(key);
|
|
239
|
+
}
|
|
102
240
|
function cancelEntry(entry, reason) {
|
|
103
241
|
if (entry.controller.signal.aborted)
|
|
104
242
|
return false;
|
|
@@ -116,23 +254,43 @@ export function createRequestRegistry(options) {
|
|
|
116
254
|
// Pull the entry out before unwinding so observers (e.g. a `cancel(...)`
|
|
117
255
|
// racing with dispose) don't see a half-disposed context.
|
|
118
256
|
entries.delete(entry.ctx.requestId);
|
|
119
|
-
|
|
257
|
+
try {
|
|
258
|
+
await entry.scope[Symbol.asyncDispose]();
|
|
259
|
+
}
|
|
260
|
+
finally {
|
|
261
|
+
// Release the admission slot only after the scope has fully unwound:
|
|
262
|
+
// the shared llama.cpp context / addon job is torn down in scope
|
|
263
|
+
// cleanup, so the next queued same-model request must not be admitted
|
|
264
|
+
// (and start decoding) until this one has actually let go. `finally`
|
|
265
|
+
// guarantees the slot is freed even if a deferred cleanup throws,
|
|
266
|
+
// otherwise a throwing teardown would strand the queue forever.
|
|
267
|
+
if (entry.slotKey !== undefined)
|
|
268
|
+
releaseSlot(entry.slotKey);
|
|
269
|
+
}
|
|
120
270
|
}
|
|
121
|
-
function begin(opts) {
|
|
122
|
-
|
|
271
|
+
async function begin(opts) {
|
|
272
|
+
// A request id is reserved for its whole lifecycle, including the time
|
|
273
|
+
// it spends *queued* for an admission slot. `waitersById` holds the
|
|
274
|
+
// still-queued begins (they aren't in `entries` yet), so a duplicate id
|
|
275
|
+
// must be rejected against both maps — otherwise a second begin with the
|
|
276
|
+
// same id would enqueue behind the first and overwrite its `waitersById`
|
|
277
|
+
// index, leaving the original waiter unreachable by `cancel({ requestId })`.
|
|
278
|
+
if (entries.has(opts.requestId) || waitersById.has(opts.requestId)) {
|
|
123
279
|
throw new RequestIdConflictError(opts.requestId);
|
|
124
280
|
}
|
|
125
|
-
// Admission control runs before allocation so a rejected begin
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
281
|
+
// Admission control runs before allocation so a rejected begin leaves
|
|
282
|
+
// no controller / scope behind. This may await: a gated `(kind,
|
|
283
|
+
// modelId)` at capacity queues the begin FIFO and resolves once a slot
|
|
284
|
+
// frees (or rejects on overflow / queue-depth cap / timeout).
|
|
285
|
+
const { slotKey } = await acquireSlot(opts);
|
|
286
|
+
// The only interleaving point in this otherwise synchronous body is the
|
|
287
|
+
// await above. A duplicate id could (astronomically unlikely) have
|
|
288
|
+
// landed while we were queued — re-check and hand the just-acquired
|
|
289
|
+
// slot back rather than stranding it on the conflicting throw.
|
|
290
|
+
if (entries.has(opts.requestId)) {
|
|
291
|
+
if (slotKey !== undefined)
|
|
292
|
+
releaseSlot(slotKey);
|
|
293
|
+
throw new RequestIdConflictError(opts.requestId);
|
|
136
294
|
}
|
|
137
295
|
const controller = new AbortController();
|
|
138
296
|
const scope = createDisposableScope();
|
|
@@ -179,6 +337,7 @@ export function createRequestRegistry(options) {
|
|
|
179
337
|
scope,
|
|
180
338
|
detachParent,
|
|
181
339
|
startedAt: Date.now(),
|
|
340
|
+
slotKey,
|
|
182
341
|
};
|
|
183
342
|
entries.set(opts.requestId, entry);
|
|
184
343
|
logLifecycle("begin", ctx);
|
|
@@ -215,6 +374,18 @@ export function createRequestRegistry(options) {
|
|
|
215
374
|
function list() {
|
|
216
375
|
return Array.from(entries.values(), (e) => e.ctx);
|
|
217
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* Cancel a still-queued waiter the same way the Stop-button race is
|
|
379
|
+
* handled: record a cancel-before-begin so its `begin(...)` — which
|
|
380
|
+
* resumes the instant we `resolve(false)` (without a slot) — observes the
|
|
381
|
+
* cancel and returns an already-aborted context. The request thus ends in
|
|
382
|
+
* a clean `cancelled` state rather than throwing.
|
|
383
|
+
*/
|
|
384
|
+
function cancelQueuedWaiterGracefully(key, waiter, reason) {
|
|
385
|
+
recordCancelBeforeBegin(waiter.requestId, reason);
|
|
386
|
+
removeWaiter(key, waiter);
|
|
387
|
+
waiter.resolve(false);
|
|
388
|
+
}
|
|
218
389
|
function cancel(target) {
|
|
219
390
|
let cancelled = 0;
|
|
220
391
|
if ("requestId" in target) {
|
|
@@ -224,6 +395,17 @@ export function createRequestRegistry(options) {
|
|
|
224
395
|
cancelled++;
|
|
225
396
|
return cancelled;
|
|
226
397
|
}
|
|
398
|
+
// Queued (still waiting for an admission slot)? It has no controller
|
|
399
|
+
// yet, so cancel it through the cancel-before-begin tripwire: pull it
|
|
400
|
+
// from the FIFO and let its begin() resume into an aborted context.
|
|
401
|
+
// Counted as one — unlike the pure race below, this targets a known
|
|
402
|
+
// in-flight (queued) begin, so the Stop button feels responsive
|
|
403
|
+
// instead of waiting for the slot to free first.
|
|
404
|
+
const queued = waitersById.get(target.requestId);
|
|
405
|
+
if (queued) {
|
|
406
|
+
cancelQueuedWaiterGracefully(queued.key, queued.waiter, target.reason);
|
|
407
|
+
return cancelled + 1;
|
|
408
|
+
}
|
|
227
409
|
// Stop-button race: the client beat its own
|
|
228
410
|
// `begin(...)`. Record the cancel so the next matching `begin`
|
|
229
411
|
// aborts immediately. The return value stays 0 — no in-flight
|
|
@@ -240,12 +422,35 @@ export function createRequestRegistry(options) {
|
|
|
240
422
|
if (cancelEntry(entry, target.reason))
|
|
241
423
|
cancelled++;
|
|
242
424
|
}
|
|
425
|
+
// Queued waiters aren't in `entries`; drain the ones matching this
|
|
426
|
+
// model (and optional kind) so a broad `cancel({ modelId })` doesn't
|
|
427
|
+
// strand them behind a request that's being cancelled out from under
|
|
428
|
+
// them. Snapshot first — `removeWaiter` mutates `waitersById`.
|
|
429
|
+
for (const { key, waiter } of Array.from(waitersById.values())) {
|
|
430
|
+
if (waiter.modelId !== target.modelId)
|
|
431
|
+
continue;
|
|
432
|
+
if (target.kind && waiter.kind !== target.kind)
|
|
433
|
+
continue;
|
|
434
|
+
cancelQueuedWaiterGracefully(key, waiter, target.reason);
|
|
435
|
+
cancelled++;
|
|
436
|
+
}
|
|
243
437
|
return cancelled;
|
|
244
438
|
}
|
|
245
439
|
function cancelAll(reason) {
|
|
246
440
|
for (const entry of entries.values()) {
|
|
247
441
|
cancelEntry(entry, reason);
|
|
248
442
|
}
|
|
443
|
+
// Drain every queued waiter so a shutdown / model-unload sweep can't
|
|
444
|
+
// leave a `begin(...)` promise hung forever — which would in turn block
|
|
445
|
+
// the unload waiting on a request that never resolves. Unlike the
|
|
446
|
+
// targeted cancels above these *reject* rather than resolve-into-
|
|
447
|
+
// aborted: the model / worker they queued against is being torn down,
|
|
448
|
+
// so there is nothing left for them to run. Snapshot first —
|
|
449
|
+
// `removeWaiter` mutates `waitersById`.
|
|
450
|
+
for (const { key, waiter } of Array.from(waitersById.values())) {
|
|
451
|
+
removeWaiter(key, waiter);
|
|
452
|
+
waiter.reject(new RequestRejectedByPolicyError(waiter.requestId, waiter.kind, waiter.modelId, `queued request cancelled (${reason})`));
|
|
453
|
+
}
|
|
249
454
|
// The interface returns Promise<void> so we can later make this an
|
|
250
455
|
// async sweep that awaits per-handler scope unwinding (e.g. join on
|
|
251
456
|
// the disposers). Today every handler unwinds on its own dispose
|
|
@@ -259,7 +464,7 @@ export function createRequestRegistry(options) {
|
|
|
259
464
|
await disposeEntry(entry, outcome);
|
|
260
465
|
}
|
|
261
466
|
function policy(opts) {
|
|
262
|
-
policies.set(opts.kind, opts);
|
|
467
|
+
policies.set(opts.kind, normalizePolicy(opts));
|
|
263
468
|
}
|
|
264
469
|
return {
|
|
265
470
|
begin,
|
|
@@ -275,6 +480,43 @@ export function createRequestRegistry(options) {
|
|
|
275
480
|
// interface (typed via the augmented return type below) so handler
|
|
276
481
|
// code can't depend on it accidentally.
|
|
277
482
|
__cancelBeforeBeginSize: () => cancelledBeforeBegin.size,
|
|
483
|
+
// Test-only: the number of live per-`(kind, modelId)` admission
|
|
484
|
+
// states. Lets the queue tests assert the map empties on drain /
|
|
485
|
+
// dispose (no `KeyState` leak). Also off the public interface.
|
|
486
|
+
__keyStateSize: () => keyStates.size,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Resolve a `ConcurrencyPolicy` to concrete numbers, applying defaults and
|
|
491
|
+
* the deprecated `oneAtATimePerModel` alias. Done once at registration so
|
|
492
|
+
* the `begin(...)` hot path reads plain numbers.
|
|
493
|
+
*/
|
|
494
|
+
function normalizePolicy(opts) {
|
|
495
|
+
let maxConcurrent;
|
|
496
|
+
let onOverflow;
|
|
497
|
+
if (opts.maxConcurrentPerModel !== undefined) {
|
|
498
|
+
maxConcurrent = opts.maxConcurrentPerModel;
|
|
499
|
+
onOverflow = opts.onOverflow ?? "queue";
|
|
500
|
+
}
|
|
501
|
+
else if (opts.oneAtATimePerModel === true) {
|
|
502
|
+
// Legacy alias: at most one in-flight per model, reject the rest.
|
|
503
|
+
maxConcurrent = 1;
|
|
504
|
+
onOverflow = opts.onOverflow ?? "reject";
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
// Nothing configured (includes `oneAtATimePerModel: false`) ⇒ no gating.
|
|
508
|
+
maxConcurrent = Infinity;
|
|
509
|
+
onOverflow = opts.onOverflow ?? "queue";
|
|
510
|
+
}
|
|
511
|
+
// A finite limit below 1 would gate every request forever; clamp to the
|
|
512
|
+
// smallest sensible serial limit.
|
|
513
|
+
if (Number.isFinite(maxConcurrent) && maxConcurrent < 1)
|
|
514
|
+
maxConcurrent = 1;
|
|
515
|
+
return {
|
|
516
|
+
maxConcurrent,
|
|
517
|
+
onOverflow,
|
|
518
|
+
maxQueueDepth: opts.maxQueueDepthPerModel ?? DEFAULT_MAX_QUEUE_DEPTH_PER_MODEL,
|
|
519
|
+
queueTimeoutMs: opts.queueTimeoutMs,
|
|
278
520
|
};
|
|
279
521
|
}
|
|
280
522
|
/**
|
|
@@ -287,6 +529,7 @@ export function createRequestRegistry(options) {
|
|
|
287
529
|
export const __requestRegistryTestHooks = {
|
|
288
530
|
cancelBeforeBeginMaxEntries: CANCEL_BEFORE_BEGIN_MAX_ENTRIES,
|
|
289
531
|
cancelBeforeBeginTtlMs: CANCEL_BEFORE_BEGIN_TTL_MS,
|
|
532
|
+
defaultMaxQueueDepthPerModel: DEFAULT_MAX_QUEUE_DEPTH_PER_MODEL,
|
|
290
533
|
};
|
|
291
534
|
function derivedTerminalState(ctx) {
|
|
292
535
|
if (ctx.state === "failed")
|
|
@@ -13,7 +13,7 @@ export interface RequestLogContext {
|
|
|
13
13
|
* the prefixed message.
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
|
-
* await using ctx = registry.begin({ requestId, kind: "completion", modelId });
|
|
16
|
+
* await using ctx = await registry.begin({ requestId, kind: "completion", modelId });
|
|
17
17
|
* const log = withRequestContext(getServerLogger(), ctx);
|
|
18
18
|
* log.info("decoding token 7");
|
|
19
19
|
* // → "[request-lifecycle completion requestId=<id> modelId=<id>] decoding token 7"
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* the prefixed message.
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
|
-
* await using ctx = registry.begin({ requestId, kind: "completion", modelId });
|
|
10
|
+
* await using ctx = await registry.begin({ requestId, kind: "completion", modelId });
|
|
11
11
|
* const log = withRequestContext(getServerLogger(), ctx);
|
|
12
12
|
* log.info("decoding token 7");
|
|
13
13
|
* // → "[request-lifecycle completion requestId=<id> modelId=<id>] decoding token 7"
|
|
@@ -37,6 +37,7 @@ export interface EmbedStats {
|
|
|
37
37
|
tokens_per_second?: number;
|
|
38
38
|
total_tokens?: number;
|
|
39
39
|
backendDevice?: "cpu" | "gpu";
|
|
40
|
+
context_size?: number;
|
|
40
41
|
}
|
|
41
42
|
export interface EmbedResponse {
|
|
42
43
|
stats?: EmbedStats;
|
|
@@ -53,6 +54,10 @@ export interface TranscribeStats {
|
|
|
53
54
|
encoderMs?: number;
|
|
54
55
|
decoderMs?: number;
|
|
55
56
|
melSpecMs?: number;
|
|
57
|
+
backendDevice?: number;
|
|
58
|
+
backendId?: number;
|
|
59
|
+
gpuMemTotalMb?: number;
|
|
60
|
+
gpuMemFreeMb?: number;
|
|
56
61
|
}
|
|
57
62
|
export interface TranscribeAddonSegment {
|
|
58
63
|
text: string;
|
|
@@ -9,6 +9,8 @@ import { handleUnloadModel } from "./handlers/unload-model.js";
|
|
|
9
9
|
import { handleUnloadModelDelegated } from "./handlers/unload-model-delegated.js";
|
|
10
10
|
import { handleTranscribe } from "./handlers/transcribe.js";
|
|
11
11
|
import { handleTranscribeStream } from "./handlers/transcribe-stream.js";
|
|
12
|
+
import { handleBciTranscribe } from "./handlers/bci-transcribe.js";
|
|
13
|
+
import { handleBciTranscribeStream } from "./handlers/bci-transcribe-stream.js";
|
|
12
14
|
import { handleEmbed } from "./handlers/embed.js";
|
|
13
15
|
import { handleTranslate } from "./handlers/translate.js";
|
|
14
16
|
import { handleLoggingStream } from "./handlers/logging-stream.js";
|
|
@@ -118,6 +120,8 @@ export const registry = {
|
|
|
118
120
|
// Simple Stream handlers
|
|
119
121
|
transcribe: { type: "stream", handler: handleTranscribe },
|
|
120
122
|
transcribeStream: { type: "duplex", handler: handleTranscribeStream },
|
|
123
|
+
bciTranscribe: { type: "stream", handler: handleBciTranscribe },
|
|
124
|
+
bciTranscribeStream: { type: "duplex", handler: handleBciTranscribeStream },
|
|
121
125
|
loggingStream: { type: "stream", handler: handleLoggingStream },
|
|
122
126
|
translate: { type: "stream", handler: handleTranslate },
|
|
123
127
|
textToSpeech: { type: "stream", handler: handleTextToSpeech },
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { BciTranscribeStreamRequest, BciTranscribeStreamResponse } from "../../../schemas/index";
|
|
2
|
+
export declare function handleBciTranscribeStream(request: BciTranscribeStreamRequest, inputStream: AsyncIterable<Buffer>): AsyncGenerator<BciTranscribeStreamResponse>;
|
|
3
|
+
//# sourceMappingURL=bci-transcribe-stream.d.ts.map
|
|
@@ -70,7 +70,7 @@ export async function handleDownloadAsset(request, progressCallback) {
|
|
|
70
70
|
// `modelId` to register on the registry entry. Cancel by `requestId`
|
|
71
71
|
// is the primary path; `cancel({ modelId })` is intentionally a
|
|
72
72
|
// non-match for this kind.
|
|
73
|
-
const ctx = __addDisposableResource(env_1, getRequestRegistry().begin({
|
|
73
|
+
const ctx = __addDisposableResource(env_1, await getRequestRegistry().begin({
|
|
74
74
|
requestId,
|
|
75
75
|
kind: "downloadAsset",
|
|
76
76
|
}), true);
|
|
@@ -15,7 +15,7 @@ export async function handleGetModelInfo(request) {
|
|
|
15
15
|
const cacheStatus = catalogEntry.shardMetadata && catalogEntry.shardMetadata.length > 0
|
|
16
16
|
? await handleShardedModel(cacheKey, catalogEntry.shardMetadata)
|
|
17
17
|
: catalogEntry.companionSet
|
|
18
|
-
? await handleCompanionSetModel(catalogEntry.companionSet, catalogEntry.registryPath)
|
|
18
|
+
? await handleCompanionSetModel(catalogEntry.companionSet, catalogEntry.registryPath, catalogEntry.addon)
|
|
19
19
|
: await handleSingleFileModel(catalogEntry.registryPath, catalogEntry.expectedSize, catalogEntry.sha256Checksum);
|
|
20
20
|
const { cacheFiles, isCached, actualSize, cachedAt, primaryPath } = cacheStatus;
|
|
21
21
|
const loadedModelIds = getAllModelIds();
|
|
@@ -73,7 +73,7 @@ async function handleShardedModel(cacheKey, shardMetadata) {
|
|
|
73
73
|
const paths = shardMetadata.map((s) => getShardPath(cacheKey, s.filename));
|
|
74
74
|
return checkCacheStatus(fileEntries, paths);
|
|
75
75
|
}
|
|
76
|
-
async function handleCompanionSetModel(companionSet, primaryRegistryPath) {
|
|
76
|
+
async function handleCompanionSetModel(companionSet, primaryRegistryPath, addon) {
|
|
77
77
|
const { setKey, primaryKey, files } = companionSet;
|
|
78
78
|
const baseCache = getModelsCacheDir();
|
|
79
79
|
const fileEntries = files.map((f) => ({
|
|
@@ -94,7 +94,7 @@ async function handleCompanionSetModel(companionSet, primaryRegistryPath) {
|
|
|
94
94
|
if (legacyResult.isCached)
|
|
95
95
|
return legacyResult;
|
|
96
96
|
}
|
|
97
|
-
else {
|
|
97
|
+
else if (addon === "nmt") {
|
|
98
98
|
// Legacy flat-cache compatibility for Bergamot-style companion sets.
|
|
99
99
|
// This is valid only for families whose runtime still works with explicit
|
|
100
100
|
// per-file absolute paths; it is not a generic rule for all companion sets.
|
|
@@ -5,6 +5,8 @@ import { handleUnloadModel } from "./unload-model";
|
|
|
5
5
|
import { handleEmbed } from "./embed";
|
|
6
6
|
import { handleTranscribe } from "./transcribe";
|
|
7
7
|
import { handleTranscribeStream } from "./transcribe-stream";
|
|
8
|
+
import { handleBciTranscribe } from "./bci-transcribe";
|
|
9
|
+
import { handleBciTranscribeStream } from "./bci-transcribe-stream";
|
|
8
10
|
import { provideHandler } from "./provideHandler";
|
|
9
11
|
import { stopProvideHandler } from "./stopProvideHandler";
|
|
10
12
|
import { handleTranslate } from "./translate";
|
|
@@ -37,6 +39,8 @@ export declare const handlers: {
|
|
|
37
39
|
embed: typeof handleEmbed;
|
|
38
40
|
transcribe: typeof handleTranscribe;
|
|
39
41
|
transcribeStream: typeof handleTranscribeStream;
|
|
42
|
+
bciTranscribe: typeof handleBciTranscribe;
|
|
43
|
+
bciTranscribeStream: typeof handleBciTranscribeStream;
|
|
40
44
|
provide: typeof provideHandler;
|
|
41
45
|
stopProvide: typeof stopProvideHandler;
|
|
42
46
|
translate: typeof handleTranslate;
|
|
@@ -5,6 +5,8 @@ import { handleUnloadModel } from "./unload-model.js";
|
|
|
5
5
|
import { handleEmbed } from "./embed.js";
|
|
6
6
|
import { handleTranscribe } from "./transcribe.js";
|
|
7
7
|
import { handleTranscribeStream } from "./transcribe-stream.js";
|
|
8
|
+
import { handleBciTranscribe } from "./bci-transcribe.js";
|
|
9
|
+
import { handleBciTranscribeStream } from "./bci-transcribe-stream.js";
|
|
8
10
|
import { provideHandler } from "./provideHandler.js";
|
|
9
11
|
import { stopProvideHandler } from "./stopProvideHandler.js";
|
|
10
12
|
import { handleTranslate } from "./translate.js";
|
|
@@ -37,6 +39,8 @@ export const handlers = {
|
|
|
37
39
|
embed: handleEmbed,
|
|
38
40
|
transcribe: handleTranscribe,
|
|
39
41
|
transcribeStream: handleTranscribeStream,
|
|
42
|
+
bciTranscribe: handleBciTranscribe,
|
|
43
|
+
bciTranscribeStream: handleBciTranscribeStream,
|
|
40
44
|
provide: provideHandler,
|
|
41
45
|
stopProvide: stopProvideHandler,
|
|
42
46
|
translate: handleTranslate,
|
|
@@ -5,7 +5,7 @@ import type { DownloadHooks } from "./types";
|
|
|
5
5
|
/**
|
|
6
6
|
* Per-subscriber binding to a registry-tracked request.
|
|
7
7
|
* `startOrJoinDownload` is request-aware: each caller (the
|
|
8
|
-
* `await using ctx = registry.begin(...)` inside `handleLoadModel` /
|
|
8
|
+
* `await using ctx = await registry.begin(...)` inside `handleLoadModel` /
|
|
9
9
|
* `handleDownloadAsset`) registers a subscriber bound to its
|
|
10
10
|
* `requestId`. A `cancel({ requestId })` against the registry aborts
|
|
11
11
|
* the subscriber's `ctx.signal`, which:
|
|
@@ -82,7 +82,7 @@ export async function handleLoadModel(request, progressCallback) {
|
|
|
82
82
|
// is opened with `modelId: undefined` so a cancel-by-modelId fired
|
|
83
83
|
// before the load completes is a clean no-op (rather than matching a
|
|
84
84
|
// half-built entry). Cancel-by-`requestId` works from `begin(...)` on.
|
|
85
|
-
const ctx = __addDisposableResource(env_1, getRequestRegistry().begin({
|
|
85
|
+
const ctx = __addDisposableResource(env_1, await getRequestRegistry().begin({
|
|
86
86
|
requestId,
|
|
87
87
|
kind: "loadModel",
|
|
88
88
|
}), true);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs, { promises as fsPromises } from "bare-fs";
|
|
2
2
|
import path from "bare-path";
|
|
3
3
|
import { Readable } from "bare-stream";
|
|
4
|
-
import fetch from "bare-fetch";
|
|
4
|
+
import fetch, { Headers } from "bare-fetch";
|
|
5
5
|
import { withTimeout } from "../../../../utils/withTimeout.js";
|
|
6
6
|
import { getModelsCacheDir, getShardedModelCacheDir, generateShortHash, detectShardedModel, parsePatternBasedShardUrl, extractTensorsFromShards, calculatePercentage, isArchiveUrl, sanitizePathComponent, extractAndValidateShardedArchive, validateShardedModelCache, checkAllShardsExist, generateShardFilenames, hasValidGGUFHeader, } from "../../../utils/index.js";
|
|
7
7
|
import { getSDKConfig } from "../../../bare/registry/config-registry.js";
|
|
@@ -84,11 +84,11 @@ async function performHttpDownload(url, modelPath, downloadKey, progressCallback
|
|
|
84
84
|
logger.info(`📥 Starting fresh download`);
|
|
85
85
|
}
|
|
86
86
|
// Prepare headers for resume if needed
|
|
87
|
-
const headers = {
|
|
87
|
+
const headers = new Headers({
|
|
88
88
|
"User-Agent": "qvac-sdk",
|
|
89
|
-
};
|
|
89
|
+
});
|
|
90
90
|
if (startOffset > 0) {
|
|
91
|
-
headers
|
|
91
|
+
headers.append("Range", `bytes=${startOffset}-`);
|
|
92
92
|
}
|
|
93
93
|
const config = getSDKConfig();
|
|
94
94
|
const connectionTimeout = config.httpConnectionTimeoutMs ?? DEFAULT_HTTP_CONNECTION_TIMEOUT_MS;
|
|
@@ -134,9 +134,9 @@ async function performHttpDownload(url, modelPath, downloadKey, progressCallback
|
|
|
134
134
|
// Retry without Range header
|
|
135
135
|
response = await fetch(url, {
|
|
136
136
|
method: "GET",
|
|
137
|
-
headers: {
|
|
137
|
+
headers: new Headers({
|
|
138
138
|
"User-Agent": "qvac-sdk",
|
|
139
|
-
},
|
|
139
|
+
}),
|
|
140
140
|
...(signal && { signal }),
|
|
141
141
|
});
|
|
142
142
|
if (!response.ok) {
|
|
@@ -12,6 +12,7 @@ import { getSDKConfig } from "../../../bare/registry/config-registry.js";
|
|
|
12
12
|
import { FileNotFoundError, ChecksumValidationFailedError, DownloadCancelledError, HyperdriveDownloadFailedError, ModelLoadFailedError, NoBlobFoundError, } from "../../../../utils/errors-server.js";
|
|
13
13
|
import { getServerLogger } from "../../../../logging/index.js";
|
|
14
14
|
import { registerSwarm, unregisterSwarm, registerCorestore, unregisterCorestore, } from "../../../bare/runtime-lifecycle.js";
|
|
15
|
+
import { Buffer } from "bare-buffer";
|
|
15
16
|
const logger = getServerLogger();
|
|
16
17
|
function getCorestoreDir(hyperdriveKey) {
|
|
17
18
|
return getQvacPath("corestore", hyperdriveKey);
|
|
@@ -6,6 +6,7 @@ type CompanionSetMetadata = NonNullable<RegistryItem["companionSet"]>;
|
|
|
6
6
|
export interface DownloadCompanionSetOptions {
|
|
7
7
|
companionSet: CompanionSetMetadata;
|
|
8
8
|
downloadKey: string;
|
|
9
|
+
allowLegacyFlatCache?: boolean | undefined;
|
|
9
10
|
progressCallback?: ((progress: ModelProgressUpdate) => void) | undefined;
|
|
10
11
|
signal?: AbortSignal | undefined;
|
|
11
12
|
hooks?: DownloadHooks | undefined;
|
|
@@ -13,7 +13,7 @@ const logger = getServerLogger();
|
|
|
13
13
|
* Returns the path to the primary file.
|
|
14
14
|
*/
|
|
15
15
|
export async function downloadCompanionSetFromRegistry(options) {
|
|
16
|
-
const { companionSet, downloadKey, progressCallback, signal, hooks, shouldClearCache, } = options;
|
|
16
|
+
const { companionSet, downloadKey, allowLegacyFlatCache = false, progressCallback, signal, hooks, shouldClearCache, } = options;
|
|
17
17
|
const { files, setKey, primaryKey } = companionSet;
|
|
18
18
|
validateCompanionSetMetadata(files, primaryKey);
|
|
19
19
|
const primaryEntry = files.find((f) => f.key === primaryKey);
|
|
@@ -30,7 +30,7 @@ export async function downloadCompanionSetFromRegistry(options) {
|
|
|
30
30
|
// Legacy flat-cache compatibility for Bergamot-style companion sets.
|
|
31
31
|
// This is valid only for families whose runtime still works with explicit
|
|
32
32
|
// per-file absolute paths; it is not a generic rule for all companion sets.
|
|
33
|
-
if (!isLegacyOnnxSet(files)) {
|
|
33
|
+
if (allowLegacyFlatCache && !isLegacyOnnxSet(files)) {
|
|
34
34
|
const flatPath = await checkLegacyFlatCache(files, primaryKey, hooks);
|
|
35
35
|
if (flatPath) {
|
|
36
36
|
logger.info(`✅ Using legacy flat cache for companion set: ${flatPath}`);
|
|
@@ -251,6 +251,7 @@ export async function downloadModelFromRegistry(registryPath, registrySource, pr
|
|
|
251
251
|
return await downloadCompanionSetFromRegistry({
|
|
252
252
|
companionSet: modelMetadata.companionSet,
|
|
253
253
|
downloadKey,
|
|
254
|
+
allowLegacyFlatCache: modelMetadata.addon === "nmt",
|
|
254
255
|
progressCallback: ctx.broadcastProgress,
|
|
255
256
|
signal: ctx.signal,
|
|
256
257
|
hooks: companionHooks,
|
|
@@ -7,7 +7,7 @@ export interface ResolveSessionOptions {
|
|
|
7
7
|
profilingEnabled: boolean;
|
|
8
8
|
/**
|
|
9
9
|
* Optional cancel signal — typically `ctx.signal` from the surrounding
|
|
10
|
-
* `await using ctx = registry.begin(...)` block. When provided,
|
|
10
|
+
* `await using ctx = await registry.begin(...)` block. When provided,
|
|
11
11
|
* `resolveModelPath` short-circuits with `InferenceCancelledError` if the
|
|
12
12
|
* signal is already aborted on entry; the same signal also propagates
|
|
13
13
|
* to in-progress transfers via the request binding below so cancel
|