@qvac/sdk 0.17.1 → 0.18.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.
- package/NOTICE +124 -64
- package/README.md +19 -0
- package/dist/client/api/audio-gen.d.ts +17 -0
- package/dist/client/api/audio-gen.js +17 -0
- package/dist/client/api/finetune.d.ts +2 -0
- package/dist/client/api/finetune.js +7 -1
- package/dist/client/api/index.d.ts +1 -1
- package/dist/client/api/index.js +1 -1
- package/dist/client/api/load-model.d.ts +8 -0
- package/dist/client/api/transcribe.d.ts +19 -2
- package/dist/client/api/transcribe.js +66 -27
- package/dist/client/api/translate.d.ts +1 -0
- package/dist/client/api/translate.js +40 -19
- package/dist/client/api/vla.d.ts +42 -1
- package/dist/client/api/vla.js +50 -1
- package/dist/commands/bundle/entry-gen.d.ts +4 -0
- package/dist/commands/bundle/entry-gen.js +6 -0
- package/dist/commands/bundle/index.js +19 -12
- package/dist/examples/asr/parakeet-indic-conformer-filesystem.d.ts +2 -0
- package/dist/examples/asr/parakeet-indic-conformer-filesystem.js +52 -0
- package/dist/examples/audiogen/generate-cover.d.ts +2 -0
- package/dist/examples/audiogen/generate-cover.js +100 -0
- package/dist/examples/llamacpp-fallback.d.ts +2 -0
- package/dist/examples/llamacpp-fallback.js +34 -0
- package/dist/examples/multi-job-completion.d.ts +28 -0
- package/dist/examples/multi-job-completion.js +128 -0
- package/dist/examples/profiling/basic.js +32 -1
- package/dist/examples/system-resources.d.ts +2 -0
- package/dist/examples/system-resources.js +86 -0
- package/dist/examples/tts/audio8.d.ts +2 -0
- package/dist/examples/tts/audio8.js +54 -0
- package/dist/examples/tts/cosyvoice3.d.ts +2 -0
- package/dist/examples/tts/cosyvoice3.js +52 -0
- package/dist/examples/vla-groot.js +92 -49
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/models/registry/models.d.ts +782 -6
- package/dist/models/registry/models.js +2664 -1859
- package/dist/schemas/audio-gen.d.ts +103 -0
- package/dist/schemas/audio-gen.js +135 -4
- package/dist/schemas/common.d.ts +550 -11
- package/dist/schemas/constants-registry.d.ts +18 -4
- package/dist/schemas/constants-registry.js +16 -3
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/index.js +1 -1
- package/dist/schemas/llamacpp-config.d.ts +10 -10
- package/dist/schemas/llamacpp-config.js +12 -5
- package/dist/schemas/load-model.d.ts +3678 -323
- package/dist/schemas/load-model.js +21 -3
- package/dist/schemas/sdk-config.d.ts +60 -24
- package/dist/schemas/sdk-errors-server.d.ts +1 -0
- package/dist/schemas/sdk-errors-server.js +5 -0
- package/dist/schemas/text-to-speech.d.ts +1086 -110
- package/dist/schemas/text-to-speech.js +194 -4
- package/dist/schemas/tools.d.ts +0 -16
- package/dist/schemas/tools.js +0 -15
- package/dist/schemas/transcription-config.d.ts +2 -0
- package/dist/schemas/transcription-config.js +10 -4
- package/dist/schemas/transcription.d.ts +3 -0
- package/dist/schemas/vla.d.ts +83 -0
- package/dist/schemas/vla.js +105 -2
- package/dist/scripts/link-workspace-inference.d.ts +2 -0
- package/dist/scripts/link-workspace-inference.js +31 -0
- package/dist/server/bare/ops/cancel.d.ts +1 -1
- package/dist/server/bare/ops/cancel.js +1 -1
- package/dist/server/bare/ops/kv-cache-retention.js +11 -0
- package/dist/server/bare/ops/kv-cache-utils.d.ts +1 -1
- package/dist/server/bare/ops/kv-cache-utils.js +40 -12
- package/dist/server/bare/ops/transcribe.d.ts +2 -2
- package/dist/server/bare/ops/transcribe.js +54 -48
- package/dist/server/bare/ops/translate-stats.d.ts +5 -0
- package/dist/server/bare/ops/translate-stats.js +11 -0
- package/dist/server/bare/ops/translate.js +66 -38
- package/dist/server/bare/ops/unload-model.js +30 -24
- package/dist/server/bare/plugins/audiogen-ggml/ops/audio-gen-input.d.ts +20 -0
- package/dist/server/bare/plugins/audiogen-ggml/ops/audio-gen-input.js +153 -0
- package/dist/server/bare/plugins/audiogen-ggml/ops/audio-gen-stream.js +52 -1
- package/dist/server/bare/plugins/audiogen-ggml/plugin.d.ts +28 -0
- package/dist/server/bare/plugins/ggml-ocr/plugin.js +3 -1
- package/dist/server/bare/plugins/ggml-vla/ops/vla-set-embodiment.d.ts +3 -0
- package/dist/server/bare/plugins/ggml-vla/ops/vla-set-embodiment.js +9 -0
- package/dist/server/bare/plugins/ggml-vla/plugin.d.ts +37 -0
- package/dist/server/bare/plugins/ggml-vla/plugin.js +21 -4
- package/dist/server/bare/plugins/llamacpp-completion/ops/batch-completion-stream.js +16 -15
- package/dist/server/bare/plugins/llamacpp-completion/ops/cache-logger.d.ts +1 -1
- package/dist/server/bare/plugins/llamacpp-completion/ops/cache-logger.js +2 -2
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stats.d.ts +6 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stats.js +10 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +1 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +131 -123
- package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +110 -20
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.d.ts +49 -23
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.js +308 -143
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.d.ts +1 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.js +2 -2
- package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +6 -6
- package/dist/server/bare/plugins/llamacpp-completion/plugin.js +55 -36
- package/dist/server/bare/plugins/llamacpp-completion/transform.js +1 -7
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +2 -3
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +6 -5
- package/dist/server/bare/plugins/parakeet-transcription/plugin.js +27 -16
- package/dist/server/bare/plugins/tts-ggml/ops/parler-options.js +23 -3
- package/dist/server/bare/plugins/tts-ggml/plugin.d.ts +302 -4
- package/dist/server/bare/plugins/tts-ggml/plugin.js +131 -0
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +48 -39
- package/dist/server/bare/registry/model-registry.d.ts +1 -0
- package/dist/server/bare/runtime/request-registry-singleton.js +29 -15
- package/dist/server/bare/runtime/request-registry.d.ts +27 -0
- package/dist/server/bare/runtime/request-registry.js +246 -76
- package/dist/server/bare/types/addon-responses.d.ts +4 -12
- package/dist/server/rpc/handlers/load-model/handler.js +2 -2
- package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +1 -1
- package/dist/server/rpc/handlers/load-model/resolve-session.js +3 -3
- package/dist/server/rpc/handlers/load-model/resolve.d.ts +12 -2
- package/dist/server/rpc/handlers/load-model/resolve.js +90 -12
- package/dist/server/utils/audio/decoder.d.ts +37 -1
- package/dist/server/utils/audio/decoder.js +81 -51
- package/dist/server/utils/config-transform.d.ts +3 -0
- package/dist/server/utils/config-transform.js +6 -0
- package/dist/server/utils/index.d.ts +0 -1
- package/dist/server/utils/index.js +0 -1
- package/dist/server/utils/shard-utils.d.ts +1 -0
- package/dist/server/utils/shard-utils.js +14 -0
- package/dist/server/utils/tool-integration.d.ts +3 -10
- package/dist/server/utils/tool-integration.js +3 -12
- package/dist/utils/errors-server.d.ts +3 -0
- package/dist/utils/errors-server.js +5 -0
- package/package.json +12 -14
- package/dist/examples/llamacpp-dynamic-tools.d.ts +0 -2
- package/dist/examples/llamacpp-dynamic-tools.js +0 -152
- package/dist/models/update-models/codegen.d.ts +0 -3
- package/dist/models/update-models/codegen.js +0 -196
- package/dist/models/update-models/companions.d.ts +0 -30
- package/dist/models/update-models/companions.js +0 -296
- package/dist/models/update-models/history.d.ts +0 -24
- package/dist/models/update-models/history.js +0 -107
- package/dist/models/update-models/index.d.ts +0 -2
- package/dist/models/update-models/index.js +0 -123
- package/dist/models/update-models/naming.d.ts +0 -4
- package/dist/models/update-models/naming.js +0 -396
- package/dist/models/update-models/processing.d.ts +0 -8
- package/dist/models/update-models/processing.js +0 -65
- package/dist/models/update-models/registry.d.ts +0 -5
- package/dist/models/update-models/registry.js +0 -87
- package/dist/models/update-models/shards.d.ts +0 -4
- package/dist/models/update-models/shards.js +0 -57
- package/dist/models/update-models/types.d.ts +0 -88
- package/dist/models/update-models/types.js +0 -1
- package/dist/models/update-models/utils.d.ts +0 -3
- package/dist/models/update-models/utils.js +0 -21
- package/dist/server/bare/plugins/llamacpp-completion/ops/batch-cancelled.d.ts +0 -23
- package/dist/server/bare/plugins/llamacpp-completion/ops/batch-cancelled.js +0 -30
- package/dist/server/utils/expand-gguf-shards.d.ts +0 -3
- package/dist/server/utils/expand-gguf-shards.js +0 -22
|
@@ -121,12 +121,39 @@ function buildTranscribeStreamRequest(params) {
|
|
|
121
121
|
* to surface strings or segments; `sessionName` is only used to label the
|
|
122
122
|
* "already iterated" error so callers see the correct session type.
|
|
123
123
|
*/
|
|
124
|
-
async function createTranscribeStreamSession(params, options, process, sessionName) {
|
|
124
|
+
export async function createTranscribeStreamSession(params, options, process, sessionName, duplexFactory = duplex) {
|
|
125
125
|
const request = buildTranscribeStreamRequest(params);
|
|
126
|
-
const { requestStream, responseStream } = await
|
|
127
|
-
|
|
126
|
+
const { requestStream, responseStream } = await duplexFactory(request, options);
|
|
127
|
+
let statsSettled = false;
|
|
128
|
+
let resolveStatsPromise = () => { };
|
|
129
|
+
let rejectStatsPromise = () => { };
|
|
130
|
+
const stats = new Promise((resolve, reject) => {
|
|
131
|
+
resolveStatsPromise = resolve;
|
|
132
|
+
rejectStatsPromise = reject;
|
|
133
|
+
});
|
|
134
|
+
stats.catch(() => { });
|
|
135
|
+
function resolveStats(value) {
|
|
136
|
+
if (statsSettled)
|
|
137
|
+
return;
|
|
138
|
+
statsSettled = true;
|
|
139
|
+
resolveStatsPromise(value);
|
|
140
|
+
}
|
|
141
|
+
function rejectStats(error) {
|
|
142
|
+
if (statsSettled)
|
|
143
|
+
return;
|
|
144
|
+
statsSettled = true;
|
|
145
|
+
rejectStatsPromise(error);
|
|
146
|
+
}
|
|
147
|
+
function resolveEmptyStats() {
|
|
148
|
+
resolveStats(undefined);
|
|
149
|
+
}
|
|
150
|
+
function processResponse(line) {
|
|
151
|
+
return process(line, resolveStats);
|
|
152
|
+
}
|
|
153
|
+
const responses = parseLines(responseStream, processResponse, resolveEmptyStats, rejectStats);
|
|
128
154
|
let consumed = false;
|
|
129
155
|
return {
|
|
156
|
+
stats,
|
|
130
157
|
write(audioChunk) {
|
|
131
158
|
requestStream.write(audioChunk);
|
|
132
159
|
},
|
|
@@ -134,6 +161,7 @@ async function createTranscribeStreamSession(params, options, process, sessionNa
|
|
|
134
161
|
requestStream.end();
|
|
135
162
|
},
|
|
136
163
|
destroy() {
|
|
164
|
+
resolveStats(undefined);
|
|
137
165
|
requestStream.destroy();
|
|
138
166
|
responseStream.destroy();
|
|
139
167
|
},
|
|
@@ -154,32 +182,41 @@ function transcribeStreamDuplexMetadata(params, options) {
|
|
|
154
182
|
}
|
|
155
183
|
function transcribeStreamDuplexConversation(params, options) {
|
|
156
184
|
const wantsMetadata = params.metadata === true;
|
|
157
|
-
return createTranscribeStreamSession(params, options, (line) => processLineConversation(line, wantsMetadata), 'TranscribeStreamConversationSession');
|
|
185
|
+
return createTranscribeStreamSession(params, options, (line, onStats) => processLineConversation(line, wantsMetadata, onStats), 'TranscribeStreamConversationSession');
|
|
158
186
|
}
|
|
159
187
|
/**
|
|
160
188
|
* Line-delimited parser: reads newline-separated frames from a duplex
|
|
161
189
|
* response stream, passes each non-empty line through `process`, and yields
|
|
162
190
|
* whatever values it returns. `null` from `process` terminates the stream.
|
|
163
191
|
*/
|
|
164
|
-
async function* parseLines(responseStream, process) {
|
|
192
|
+
async function* parseLines(responseStream, process, onClose, onError) {
|
|
165
193
|
let buf = '';
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
194
|
+
try {
|
|
195
|
+
for await (const chunk of responseStream) {
|
|
196
|
+
buf += chunk.toString();
|
|
197
|
+
const lines = buf.split('\n');
|
|
198
|
+
buf = lines.pop() || '';
|
|
199
|
+
for (const line of lines) {
|
|
200
|
+
const result = process(line);
|
|
201
|
+
if (result === null)
|
|
202
|
+
return;
|
|
203
|
+
if (result !== undefined)
|
|
204
|
+
yield result;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Process any residual data after stream ends
|
|
208
|
+
if (buf.trim()) {
|
|
209
|
+
const result = process(buf);
|
|
210
|
+
if (result !== null && result !== undefined)
|
|
175
211
|
yield result;
|
|
176
212
|
}
|
|
177
213
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
214
|
+
catch (error) {
|
|
215
|
+
onError(error);
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
onClose();
|
|
183
220
|
}
|
|
184
221
|
}
|
|
185
222
|
function parseResponseLine(line) {
|
|
@@ -204,23 +241,25 @@ function parseResponseLine(line) {
|
|
|
204
241
|
* `undefined` for frames the caller should skip, or the value extracted from
|
|
205
242
|
* the frame.
|
|
206
243
|
*/
|
|
207
|
-
function processWith(line, extract) {
|
|
244
|
+
function processWith(line, extract, onStats) {
|
|
208
245
|
const response = parseResponseLine(line);
|
|
209
246
|
if (response === null)
|
|
210
247
|
return undefined;
|
|
211
248
|
if (response.error)
|
|
212
249
|
throw new TranscriptionFailedError(response.error);
|
|
213
|
-
if (response.done)
|
|
250
|
+
if (response.done) {
|
|
251
|
+
onStats(response.stats);
|
|
214
252
|
return null;
|
|
253
|
+
}
|
|
215
254
|
return extract(response);
|
|
216
255
|
}
|
|
217
|
-
function processLine(line) {
|
|
218
|
-
return processWith(line, (response) => (response.text?.trim() ? response.text : undefined));
|
|
256
|
+
export function processLine(line, onStats) {
|
|
257
|
+
return processWith(line, (response) => (response.text?.trim() ? response.text : undefined), onStats);
|
|
219
258
|
}
|
|
220
|
-
function processLineMetadata(line) {
|
|
221
|
-
return processWith(line, (response) => response.segment);
|
|
259
|
+
function processLineMetadata(line, onStats) {
|
|
260
|
+
return processWith(line, (response) => response.segment, onStats);
|
|
222
261
|
}
|
|
223
|
-
function processLineConversation(line, wantsMetadata) {
|
|
262
|
+
function processLineConversation(line, wantsMetadata, onStats) {
|
|
224
263
|
return processWith(line, (response) => {
|
|
225
264
|
if (response.vad) {
|
|
226
265
|
return {
|
|
@@ -252,5 +291,5 @@ function processLineConversation(line, wantsMetadata) {
|
|
|
252
291
|
return { type: 'text', text: response.text };
|
|
253
292
|
}
|
|
254
293
|
return undefined;
|
|
255
|
-
});
|
|
294
|
+
}, onStats);
|
|
256
295
|
}
|
|
@@ -44,5 +44,6 @@ export declare function translate(params: TranslateClientParams, options?: RPCOp
|
|
|
44
44
|
tokenStream: AsyncGenerator<string>;
|
|
45
45
|
stats: Promise<TranslationStats | undefined>;
|
|
46
46
|
text: Promise<string>;
|
|
47
|
+
requestId: string;
|
|
47
48
|
};
|
|
48
49
|
//# sourceMappingURL=translate.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { stream as streamRpc } from '../../client/rpc/rpc-client.js';
|
|
2
|
+
import { generateClientRequestId } from '../../client/api/client-request-id.js';
|
|
2
3
|
import { translateResponseSchema } from '../../schemas/index.js';
|
|
3
4
|
/**
|
|
4
5
|
* Translates text from one language to another using a specified translation model.
|
|
@@ -42,13 +43,17 @@ import { translateResponseSchema } from '../../schemas/index.js';
|
|
|
42
43
|
* ```
|
|
43
44
|
*/
|
|
44
45
|
export function translate(params, options) {
|
|
46
|
+
// Stable identity generated client-side so `cancel({ requestId })` works the
|
|
47
|
+
// moment we return, before the first round-trip. Surfaced on the handle.
|
|
48
|
+
const requestId = generateClientRequestId();
|
|
45
49
|
// Source-language auto-detection lives in the worker now
|
|
46
50
|
// (server/bare/ops/translate.ts): `from` is passed through when present and
|
|
47
51
|
// resolved server-side when absent, so every language binding shares one
|
|
48
52
|
// detector instead of each shipping its own.
|
|
49
53
|
const request = {
|
|
50
54
|
type: 'translate',
|
|
51
|
-
...params
|
|
55
|
+
...params,
|
|
56
|
+
requestId
|
|
52
57
|
};
|
|
53
58
|
let stats;
|
|
54
59
|
let statsResolver = () => { };
|
|
@@ -57,24 +62,32 @@ export function translate(params, options) {
|
|
|
57
62
|
});
|
|
58
63
|
if (params.stream) {
|
|
59
64
|
const tokenStream = (async function* () {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
try {
|
|
66
|
+
for await (const response of streamRpc(request, options)) {
|
|
67
|
+
if (response.type === 'translate') {
|
|
68
|
+
const streamResponse = translateResponseSchema.parse(response);
|
|
69
|
+
if (!streamResponse.done) {
|
|
70
|
+
yield streamResponse.token;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
stats = streamResponse.stats;
|
|
74
|
+
statsResolver(stats);
|
|
75
|
+
}
|
|
69
76
|
}
|
|
70
77
|
}
|
|
71
78
|
}
|
|
79
|
+
finally {
|
|
80
|
+
// Settle stats even if the stream ended early (cancel) or errored, so
|
|
81
|
+
// `await handle.stats` can't hang. Idempotent if already resolved.
|
|
82
|
+
statsResolver(stats);
|
|
83
|
+
}
|
|
72
84
|
})();
|
|
73
85
|
const textPromise = Promise.resolve('');
|
|
74
86
|
return {
|
|
75
87
|
tokenStream,
|
|
76
88
|
text: textPromise,
|
|
77
|
-
stats: statsPromise
|
|
89
|
+
stats: statsPromise,
|
|
90
|
+
requestId
|
|
78
91
|
};
|
|
79
92
|
}
|
|
80
93
|
else {
|
|
@@ -83,22 +96,30 @@ export function translate(params, options) {
|
|
|
83
96
|
})();
|
|
84
97
|
const textPromise = (async () => {
|
|
85
98
|
let buffer = '';
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
try {
|
|
100
|
+
for await (const response of streamRpc(request, options)) {
|
|
101
|
+
if (response.type === 'translate') {
|
|
102
|
+
const streamResponse = translateResponseSchema.parse(response);
|
|
103
|
+
buffer += streamResponse.token;
|
|
104
|
+
if (streamResponse.done) {
|
|
105
|
+
stats = streamResponse.stats;
|
|
106
|
+
statsResolver(stats);
|
|
107
|
+
}
|
|
93
108
|
}
|
|
94
109
|
}
|
|
95
110
|
}
|
|
111
|
+
finally {
|
|
112
|
+
// Settle stats even if the stream ended early (cancel) or errored, so
|
|
113
|
+
// `await handle.stats` can't hang. Idempotent if already resolved.
|
|
114
|
+
statsResolver(stats);
|
|
115
|
+
}
|
|
96
116
|
return buffer;
|
|
97
117
|
})();
|
|
98
118
|
return {
|
|
99
119
|
tokenStream,
|
|
100
120
|
text: textPromise,
|
|
101
|
-
stats: statsPromise
|
|
121
|
+
stats: statsPromise,
|
|
122
|
+
requestId
|
|
102
123
|
};
|
|
103
124
|
}
|
|
104
125
|
}
|
package/dist/client/api/vla.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type VlaClientRunParams, type VlaClientRunResult, type VlaHparams } from '../../schemas/index.js';
|
|
1
|
+
import { type VlaClientRunParams, type VlaClientRunResult, type VlaEmbodimentSelection, type VlaHparams } from '../../schemas/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Run VLA inference on a loaded model (SmolVLA, π₀.₅, or GR00T) and return the
|
|
4
4
|
* produced action chunk plus per-stage timings.
|
|
@@ -70,4 +70,45 @@ export declare function vlaHparams(params: {
|
|
|
70
70
|
hparams: VlaHparams;
|
|
71
71
|
backendName: string | null;
|
|
72
72
|
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Switch a loaded multi-embodiment GR00T model to another embodiment shipped
|
|
75
|
+
* in the same GGUF — no reload, only that embodiment's weight rows are re-read.
|
|
76
|
+
*
|
|
77
|
+
* The returned hparams follow the newly active embodiment: re-read
|
|
78
|
+
* `numCameras` (and re-size buffers if needed) before the next `vla()` call.
|
|
79
|
+
* `selectedEmbodimentTag` / `selectedEmbodimentCatId` report what was
|
|
80
|
+
* resolved. Selecting by `catId` reports that id's canonical tag, which may
|
|
81
|
+
* differ from the alias used to select — the id is the stable identity.
|
|
82
|
+
*
|
|
83
|
+
* Rejects (leaving the current embodiment active) for a non-GR00T or
|
|
84
|
+
* single-embodiment model, an unknown tag or `cat_id`, one not stored in the
|
|
85
|
+
* GGUF, an embodiment with no known camera count and no `numCameras`
|
|
86
|
+
* override, or while an inference response is still pending — await the
|
|
87
|
+
* in-flight `vla()` call first.
|
|
88
|
+
*
|
|
89
|
+
* @param params.modelId - Identifier of the loaded VLA model.
|
|
90
|
+
* @param params.embodiment - The embodiment to activate: a tag string, a
|
|
91
|
+
* numeric `cat_id` (0..31), or `{ tag | catId, numCameras }` where
|
|
92
|
+
* `numCameras` overrides the camera count stored in the GGUF.
|
|
93
|
+
* @returns The refreshed model hparams after the switch.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* import { loadModel, vlaSetEmbodiment, vlaHparams } from "@qvac/sdk";
|
|
98
|
+
*
|
|
99
|
+
* const modelId = await loadModel({
|
|
100
|
+
* modelSrc: "/path/to/groot-multi.gguf",
|
|
101
|
+
* modelType: "vla",
|
|
102
|
+
* modelConfig: { embodiment: "libero_sim" }, // initial selection at load
|
|
103
|
+
* });
|
|
104
|
+
* const { hparams } = await vlaSetEmbodiment({ modelId, embodiment: "real_droid" });
|
|
105
|
+
* console.log(hparams.selectedEmbodimentTag, hparams.numCameras);
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export declare function vlaSetEmbodiment(params: {
|
|
109
|
+
modelId: string;
|
|
110
|
+
embodiment: VlaEmbodimentSelection;
|
|
111
|
+
}): Promise<{
|
|
112
|
+
hparams: VlaHparams;
|
|
113
|
+
}>;
|
|
73
114
|
//# sourceMappingURL=vla.d.ts.map
|
package/dist/client/api/vla.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { vlaHparamsResponseSchema, vlaRunResponseSchema } from '../../schemas/index.js';
|
|
1
|
+
import { vlaHparamsResponseSchema, vlaRunResponseSchema, vlaSetEmbodimentResponseSchema } from '../../schemas/index.js';
|
|
2
2
|
import { decodeBase64, encodeBase64 } from '../../utils/encoding.js';
|
|
3
3
|
import { invokePlugin } from './invoke-plugin.js';
|
|
4
4
|
const VLA_RUN_HANDLER = 'vlaRun';
|
|
5
5
|
const VLA_HPARAMS_HANDLER = 'vlaHparams';
|
|
6
|
+
const VLA_SET_EMBODIMENT_HANDLER = 'vlaSetEmbodiment';
|
|
6
7
|
function bytesOf(arr) {
|
|
7
8
|
return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
8
9
|
}
|
|
@@ -115,3 +116,51 @@ export async function vlaHparams(params) {
|
|
|
115
116
|
});
|
|
116
117
|
return vlaHparamsResponseSchema.parse(result);
|
|
117
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Switch a loaded multi-embodiment GR00T model to another embodiment shipped
|
|
121
|
+
* in the same GGUF — no reload, only that embodiment's weight rows are re-read.
|
|
122
|
+
*
|
|
123
|
+
* The returned hparams follow the newly active embodiment: re-read
|
|
124
|
+
* `numCameras` (and re-size buffers if needed) before the next `vla()` call.
|
|
125
|
+
* `selectedEmbodimentTag` / `selectedEmbodimentCatId` report what was
|
|
126
|
+
* resolved. Selecting by `catId` reports that id's canonical tag, which may
|
|
127
|
+
* differ from the alias used to select — the id is the stable identity.
|
|
128
|
+
*
|
|
129
|
+
* Rejects (leaving the current embodiment active) for a non-GR00T or
|
|
130
|
+
* single-embodiment model, an unknown tag or `cat_id`, one not stored in the
|
|
131
|
+
* GGUF, an embodiment with no known camera count and no `numCameras`
|
|
132
|
+
* override, or while an inference response is still pending — await the
|
|
133
|
+
* in-flight `vla()` call first.
|
|
134
|
+
*
|
|
135
|
+
* @param params.modelId - Identifier of the loaded VLA model.
|
|
136
|
+
* @param params.embodiment - The embodiment to activate: a tag string, a
|
|
137
|
+
* numeric `cat_id` (0..31), or `{ tag | catId, numCameras }` where
|
|
138
|
+
* `numCameras` overrides the camera count stored in the GGUF.
|
|
139
|
+
* @returns The refreshed model hparams after the switch.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* import { loadModel, vlaSetEmbodiment, vlaHparams } from "@qvac/sdk";
|
|
144
|
+
*
|
|
145
|
+
* const modelId = await loadModel({
|
|
146
|
+
* modelSrc: "/path/to/groot-multi.gguf",
|
|
147
|
+
* modelType: "vla",
|
|
148
|
+
* modelConfig: { embodiment: "libero_sim" }, // initial selection at load
|
|
149
|
+
* });
|
|
150
|
+
* const { hparams } = await vlaSetEmbodiment({ modelId, embodiment: "real_droid" });
|
|
151
|
+
* console.log(hparams.selectedEmbodimentTag, hparams.numCameras);
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export async function vlaSetEmbodiment(params) {
|
|
155
|
+
const wireRequest = {
|
|
156
|
+
type: 'vlaSetEmbodiment',
|
|
157
|
+
modelId: params.modelId,
|
|
158
|
+
embodiment: params.embodiment
|
|
159
|
+
};
|
|
160
|
+
const result = await invokePlugin({
|
|
161
|
+
modelId: params.modelId,
|
|
162
|
+
handler: VLA_SET_EMBODIMENT_HANDLER,
|
|
163
|
+
params: wireRequest
|
|
164
|
+
});
|
|
165
|
+
return vlaSetEmbodimentResponseSchema.parse(result);
|
|
166
|
+
}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
export declare function generateWorkerEntry(pluginSpecifiers: string[], sdkName: string, resolveImport?: (specifier: string) => string): string;
|
|
2
|
+
export declare function generateWorkerEntries(pluginSpecifiers: string[], sdkName: string, resolveBundleImport: (specifier: string) => string): {
|
|
3
|
+
runtimeEntry: string;
|
|
4
|
+
bundleEntry: string;
|
|
5
|
+
};
|
|
2
6
|
//# sourceMappingURL=entry-gen.d.ts.map
|
|
@@ -46,3 +46,9 @@ if (hasRPCConfig) {
|
|
|
46
46
|
}
|
|
47
47
|
`;
|
|
48
48
|
}
|
|
49
|
+
export function generateWorkerEntries(pluginSpecifiers, sdkName, resolveBundleImport) {
|
|
50
|
+
return {
|
|
51
|
+
runtimeEntry: generateWorkerEntry(pluginSpecifiers, sdkName),
|
|
52
|
+
bundleEntry: generateWorkerEntry(pluginSpecifiers, sdkName, resolveBundleImport)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -6,7 +6,7 @@ import { CONFIG_CANDIDATES, resolveConfigForProject } from '../../client/config-
|
|
|
6
6
|
import { getClientLogger } from '../../logging/index.js';
|
|
7
7
|
import { BareImportsMapNotFoundError } from '../../utils/errors-client.js';
|
|
8
8
|
import { resolvePluginSpecifiers, parseBuiltinSpecifier } from '../../commands/bundle/plugins.js';
|
|
9
|
-
import {
|
|
9
|
+
import { generateWorkerEntries } from '../../commands/bundle/entry-gen.js';
|
|
10
10
|
import { runBarePack } from '../../commands/bundle/bare-pack.js';
|
|
11
11
|
import { generateAddonsManifest } from '../../commands/bundle/manifest.js';
|
|
12
12
|
import { createSdkImportResolver } from '../../commands/bundle/resolve-sdk-import.js';
|
|
@@ -63,6 +63,7 @@ export async function bundleSdk(options = {}) {
|
|
|
63
63
|
const projectRoot = options.projectRoot ?? process.cwd();
|
|
64
64
|
const outputDir = path.join(projectRoot, 'qvac');
|
|
65
65
|
const entryPath = path.join(outputDir, 'worker.entry.mjs');
|
|
66
|
+
const bundleEntryPath = path.join(outputDir, 'worker.bundle.entry.mjs');
|
|
66
67
|
const bundlePath = path.join(outputDir, 'worker.bundle.js');
|
|
67
68
|
const logger = createCommandLogger(options);
|
|
68
69
|
logger.info('🔧 QVAC SDK Worker Bundler\n');
|
|
@@ -93,8 +94,8 @@ export async function bundleSdk(options = {}) {
|
|
|
93
94
|
await fsp.mkdir(outputDir, { recursive: true });
|
|
94
95
|
logger.info('\n📝 Generating worker entry...');
|
|
95
96
|
const resolveSdkImport = createSdkImportResolver(sdkPath, sdkName);
|
|
96
|
-
const
|
|
97
|
-
await fsp.writeFile(entryPath,
|
|
97
|
+
const { runtimeEntry, bundleEntry } = generateWorkerEntries(pluginSpecifiers, sdkName, resolveSdkImport);
|
|
98
|
+
await fsp.writeFile(entryPath, runtimeEntry, 'utf8');
|
|
98
99
|
logger.info(` Created: ${path.relative(projectRoot, entryPath)}`);
|
|
99
100
|
logger.info(` Using: ${path.relative(projectRoot, importsMapPath)}`);
|
|
100
101
|
logger.info('\n🔨 Bundling with bare-pack...');
|
|
@@ -102,15 +103,21 @@ export async function bundleSdk(options = {}) {
|
|
|
102
103
|
if (deferModules.length > 0) {
|
|
103
104
|
logger.debug(` Deferred: ${deferModules.join(', ')}`);
|
|
104
105
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
try {
|
|
107
|
+
await fsp.writeFile(bundleEntryPath, bundleEntry, 'utf8');
|
|
108
|
+
await runBarePack({
|
|
109
|
+
entryPath: bundleEntryPath,
|
|
110
|
+
outputPath: bundlePath,
|
|
111
|
+
hosts,
|
|
112
|
+
importsMapPath,
|
|
113
|
+
deferModules,
|
|
114
|
+
quiet: options.quiet === true,
|
|
115
|
+
logger
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
await fsp.rm(bundleEntryPath, { force: true });
|
|
120
|
+
}
|
|
114
121
|
const stats = await fsp.stat(bundlePath);
|
|
115
122
|
const sizeKB = (stats.size / 1024).toFixed(1);
|
|
116
123
|
logger.info(`\n✅ Bundle created: ${path.relative(projectRoot, bundlePath)}`);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Indic Conformer CTC transcription from a WAV file.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* bun run examples/asr/parakeet-indic-conformer-filesystem.ts <wav-file> <language> [gguf]
|
|
6
|
+
*
|
|
7
|
+
* Loads a single GGUF checkpoint (`PARAKEET_INDIC_CONFORMER_CTC_Q8_0` by
|
|
8
|
+
* default) and transcribes with the batch `transcribe` API. `language` is
|
|
9
|
+
* required (e.g. `hi`, `ta`) because Indic Conformer CTC masks the vocab
|
|
10
|
+
* with `parakeet.ctc.lang_*` ranges. English Parakeet CTC ignores this field.
|
|
11
|
+
*
|
|
12
|
+
* Audio should be 16 kHz mono PCM in a WAV container.
|
|
13
|
+
*/
|
|
14
|
+
import { loadModel, unloadModel, transcribe, PARAKEET_INDIC_CONFORMER_CTC_Q8_0 } from '@qvac/sdk';
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
if (!args[0] || !args[1]) {
|
|
17
|
+
console.error('Usage: bun run examples/asr/parakeet-indic-conformer-filesystem.ts ' +
|
|
18
|
+
'<wav-file> <language> [indic-conformer-gguf]');
|
|
19
|
+
console.error('\nExample: ... filesystem.ts speech.wav hi');
|
|
20
|
+
console.error('If the model path is omitted, defaults to the registry model.');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const audioFilePath = args[0];
|
|
24
|
+
const language = args[1];
|
|
25
|
+
const parakeetModelSrc = args[2] ?? PARAKEET_INDIC_CONFORMER_CTC_Q8_0;
|
|
26
|
+
try {
|
|
27
|
+
console.log('▸ Loading Indic Conformer CTC model...');
|
|
28
|
+
const modelId = await loadModel({
|
|
29
|
+
modelSrc: parakeetModelSrc,
|
|
30
|
+
modelType: 'parakeet-transcription',
|
|
31
|
+
modelConfig: { language },
|
|
32
|
+
onProgress: (p) => {
|
|
33
|
+
const mb = (n) => (n / 1e6).toFixed(1);
|
|
34
|
+
const line = `▸ Downloading ${p.percentage.toFixed(0)}% (${mb(p.downloaded)}/${mb(p.total)} MB)`;
|
|
35
|
+
process.stderr.write(process.stderr.isTTY ? `\r${line}` : `${line}\n`);
|
|
36
|
+
if (p.percentage >= 100)
|
|
37
|
+
process.stderr.write('\n');
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
console.log(`▸ Indic Conformer CTC model loaded with ID: ${modelId}`);
|
|
41
|
+
console.log(`▸ Language: ${language}`);
|
|
42
|
+
console.log('▸ Transcribing audio...');
|
|
43
|
+
const text = await transcribe({ modelId, audioChunk: audioFilePath });
|
|
44
|
+
console.log(text);
|
|
45
|
+
console.log('▸ Unloading model...');
|
|
46
|
+
await unloadModel({ modelId });
|
|
47
|
+
console.log('▸ Done');
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.error('✖', error);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs';
|
|
2
|
+
import { AUDIOGEN_ACESTEP_5HZ_LM_0_6B_Q8_0, AUDIOGEN_ACESTEP_V15_TURBO_Q4_K_M, AUDIOGEN_QWEN3_EMBEDDING_0_6B_Q8_0, AUDIOGEN_VAE_BF16, audioGen, loadModel, unloadModel } from '@qvac/sdk';
|
|
3
|
+
// Re-render an existing song with a new caption (ACE-Step "cover-nofsq"),
|
|
4
|
+
// optionally conditioning the timbre on a second reference clip.
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// bun examples/audiogen/generate-cover.ts <source.wav|mp3|...> "orchestral arrangement" [reference.wav] [output.wav]
|
|
8
|
+
//
|
|
9
|
+
// Both audio inputs are file paths: the SDK decodes them (any FFmpeg-decodable
|
|
10
|
+
// format) to the 48 kHz stereo float PCM the engine expects. Pass raw
|
|
11
|
+
// interleaved stereo 48 kHz Float32 LE PCM as a Buffer instead when the audio
|
|
12
|
+
// is already in memory.
|
|
13
|
+
const sourcePath = process.argv[2];
|
|
14
|
+
const caption = process.argv[3] ?? 'Orchestral arrangement with dramatic strings';
|
|
15
|
+
const referencePath = process.argv[4];
|
|
16
|
+
const outputPath = process.argv[5] ?? 'audiogen-cover.wav';
|
|
17
|
+
if (!sourcePath) {
|
|
18
|
+
console.error('Usage: bun examples/audiogen/generate-cover.ts <source-audio> "<caption>" [reference-audio] [output.wav]');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
let modelId;
|
|
22
|
+
try {
|
|
23
|
+
console.log('▸ Loading ACE-Step AudioGen models...');
|
|
24
|
+
modelId = await loadModel({
|
|
25
|
+
modelType: 'audiogen',
|
|
26
|
+
modelConfig: {
|
|
27
|
+
textEncModelSrc: AUDIOGEN_QWEN3_EMBEDDING_0_6B_Q8_0,
|
|
28
|
+
lmModelSrc: AUDIOGEN_ACESTEP_5HZ_LM_0_6B_Q8_0,
|
|
29
|
+
ditModelSrc: AUDIOGEN_ACESTEP_V15_TURBO_Q4_K_M,
|
|
30
|
+
vaeModelSrc: AUDIOGEN_VAE_BF16,
|
|
31
|
+
useGPU: true
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
console.log(`▸ Model loaded: ${modelId}`);
|
|
35
|
+
console.log(`▸ Covering ${sourcePath} as: ${caption}`);
|
|
36
|
+
const run = audioGen({
|
|
37
|
+
modelId,
|
|
38
|
+
caption,
|
|
39
|
+
lyrics: '[Instrumental]',
|
|
40
|
+
taskType: 'cover-nofsq',
|
|
41
|
+
sourceAudio: sourcePath,
|
|
42
|
+
...(referencePath !== undefined && { referenceAudio: referencePath }),
|
|
43
|
+
// cover-nofsq keeps the full source context; coverNoiseStrength blends the
|
|
44
|
+
// initial noise toward the source latent (0 = pure noise, 1 ≈ source).
|
|
45
|
+
audioCoverStrength: 1,
|
|
46
|
+
coverNoiseStrength: 0.75,
|
|
47
|
+
seed: 22886
|
|
48
|
+
});
|
|
49
|
+
console.log(`▸ requestId: ${run.requestId}`);
|
|
50
|
+
for await (const progress of run.progressStream) {
|
|
51
|
+
console.log(`▸ ${progress.stage}: ${progress.step}/${progress.total}`);
|
|
52
|
+
}
|
|
53
|
+
const [audio, stats] = await Promise.all([run.audio, run.stats]);
|
|
54
|
+
writeFileSync(outputPath, createWav(audio.pcm, audio.sampleRate, audio.channels, audio.bitsPerSample));
|
|
55
|
+
if (stats)
|
|
56
|
+
console.log(`▸ Stats: ${JSON.stringify(stats)}`);
|
|
57
|
+
console.log(`▸ Saved ${outputPath}`);
|
|
58
|
+
await unloadModel({ modelId });
|
|
59
|
+
modelId = undefined;
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (modelId !== undefined) {
|
|
64
|
+
try {
|
|
65
|
+
await unloadModel({ modelId });
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Preserve the generation error as the primary failure.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
console.error('✖', error);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
function createWav(pcm, sampleRate, channels, bitsPerSample) {
|
|
75
|
+
const header = new ArrayBuffer(44);
|
|
76
|
+
const view = new DataView(header);
|
|
77
|
+
const blockAlign = channels * (bitsPerSample / 8);
|
|
78
|
+
writeAscii(view, 0, 'RIFF');
|
|
79
|
+
view.setUint32(4, 36 + pcm.byteLength, true);
|
|
80
|
+
writeAscii(view, 8, 'WAVE');
|
|
81
|
+
writeAscii(view, 12, 'fmt ');
|
|
82
|
+
view.setUint32(16, 16, true);
|
|
83
|
+
view.setUint16(20, 1, true);
|
|
84
|
+
view.setUint16(22, channels, true);
|
|
85
|
+
view.setUint32(24, sampleRate, true);
|
|
86
|
+
view.setUint32(28, sampleRate * blockAlign, true);
|
|
87
|
+
view.setUint16(32, blockAlign, true);
|
|
88
|
+
view.setUint16(34, bitsPerSample, true);
|
|
89
|
+
writeAscii(view, 36, 'data');
|
|
90
|
+
view.setUint32(40, pcm.byteLength, true);
|
|
91
|
+
const wav = new Uint8Array(44 + pcm.byteLength);
|
|
92
|
+
wav.set(new Uint8Array(header));
|
|
93
|
+
wav.set(pcm, 44);
|
|
94
|
+
return wav;
|
|
95
|
+
}
|
|
96
|
+
function writeAscii(view, offset, value) {
|
|
97
|
+
for (let index = 0; index < value.length; index++) {
|
|
98
|
+
view.setUint8(offset + index, value.charCodeAt(index));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { completion, loadModel, unloadModel, LLAMA_3_2_1B_INST_Q4_0 } from '@qvac/sdk';
|
|
2
|
+
// A catalog model loads from the registry by default. On networks where the
|
|
3
|
+
// registry is unreachable, pass `fallbackSrc` — an HTTP URL or a local file
|
|
4
|
+
// path — to load the same model from an alternate source. The bytes are
|
|
5
|
+
// validated against the catalog model's checksum before use.
|
|
6
|
+
const fallbackUrl = process.argv[2] ||
|
|
7
|
+
'https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_0.gguf';
|
|
8
|
+
console.log(`▸ Loading ${LLAMA_3_2_1B_INST_Q4_0.name} (fallback: ${fallbackUrl})`);
|
|
9
|
+
try {
|
|
10
|
+
const modelId = await loadModel({
|
|
11
|
+
modelSrc: LLAMA_3_2_1B_INST_Q4_0,
|
|
12
|
+
fallbackSrc: fallbackUrl,
|
|
13
|
+
onProgress: (p) => {
|
|
14
|
+
const mb = (n) => (n / 1e6).toFixed(1);
|
|
15
|
+
const line = `▸ Downloading ${p.percentage.toFixed(0)}% (${mb(p.downloaded)}/${mb(p.total)} MB)`;
|
|
16
|
+
process.stderr.write(process.stderr.isTTY ? `\r${line}` : `${line}\n`);
|
|
17
|
+
if (p.percentage >= 100)
|
|
18
|
+
process.stderr.write('\n');
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
console.log(`▸ Model loaded successfully! Model ID: ${modelId}`);
|
|
22
|
+
const history = [{ role: 'user', content: 'Explain quantum computing in 3 key points' }];
|
|
23
|
+
console.log('\n▸ AI Response:');
|
|
24
|
+
const result = completion({ modelId, history, stream: true });
|
|
25
|
+
for await (const token of result.tokenStream) {
|
|
26
|
+
process.stdout.write(token);
|
|
27
|
+
}
|
|
28
|
+
console.log('\n\n▸ Completed!');
|
|
29
|
+
await unloadModel({ modelId, clearStorage: false });
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error('✖', error);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|