@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.
Files changed (154) hide show
  1. package/NOTICE +124 -64
  2. package/README.md +19 -0
  3. package/dist/client/api/audio-gen.d.ts +17 -0
  4. package/dist/client/api/audio-gen.js +17 -0
  5. package/dist/client/api/finetune.d.ts +2 -0
  6. package/dist/client/api/finetune.js +7 -1
  7. package/dist/client/api/index.d.ts +1 -1
  8. package/dist/client/api/index.js +1 -1
  9. package/dist/client/api/load-model.d.ts +8 -0
  10. package/dist/client/api/transcribe.d.ts +19 -2
  11. package/dist/client/api/transcribe.js +66 -27
  12. package/dist/client/api/translate.d.ts +1 -0
  13. package/dist/client/api/translate.js +40 -19
  14. package/dist/client/api/vla.d.ts +42 -1
  15. package/dist/client/api/vla.js +50 -1
  16. package/dist/commands/bundle/entry-gen.d.ts +4 -0
  17. package/dist/commands/bundle/entry-gen.js +6 -0
  18. package/dist/commands/bundle/index.js +19 -12
  19. package/dist/examples/asr/parakeet-indic-conformer-filesystem.d.ts +2 -0
  20. package/dist/examples/asr/parakeet-indic-conformer-filesystem.js +52 -0
  21. package/dist/examples/audiogen/generate-cover.d.ts +2 -0
  22. package/dist/examples/audiogen/generate-cover.js +100 -0
  23. package/dist/examples/llamacpp-fallback.d.ts +2 -0
  24. package/dist/examples/llamacpp-fallback.js +34 -0
  25. package/dist/examples/multi-job-completion.d.ts +28 -0
  26. package/dist/examples/multi-job-completion.js +128 -0
  27. package/dist/examples/profiling/basic.js +32 -1
  28. package/dist/examples/system-resources.d.ts +2 -0
  29. package/dist/examples/system-resources.js +86 -0
  30. package/dist/examples/tts/audio8.d.ts +2 -0
  31. package/dist/examples/tts/audio8.js +54 -0
  32. package/dist/examples/tts/cosyvoice3.d.ts +2 -0
  33. package/dist/examples/tts/cosyvoice3.js +52 -0
  34. package/dist/examples/vla-groot.js +92 -49
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.js +2 -2
  37. package/dist/models/registry/models.d.ts +782 -6
  38. package/dist/models/registry/models.js +2664 -1859
  39. package/dist/schemas/audio-gen.d.ts +103 -0
  40. package/dist/schemas/audio-gen.js +135 -4
  41. package/dist/schemas/common.d.ts +550 -11
  42. package/dist/schemas/constants-registry.d.ts +18 -4
  43. package/dist/schemas/constants-registry.js +16 -3
  44. package/dist/schemas/index.d.ts +1 -1
  45. package/dist/schemas/index.js +1 -1
  46. package/dist/schemas/llamacpp-config.d.ts +10 -10
  47. package/dist/schemas/llamacpp-config.js +12 -5
  48. package/dist/schemas/load-model.d.ts +3678 -323
  49. package/dist/schemas/load-model.js +21 -3
  50. package/dist/schemas/sdk-config.d.ts +60 -24
  51. package/dist/schemas/sdk-errors-server.d.ts +1 -0
  52. package/dist/schemas/sdk-errors-server.js +5 -0
  53. package/dist/schemas/text-to-speech.d.ts +1086 -110
  54. package/dist/schemas/text-to-speech.js +194 -4
  55. package/dist/schemas/tools.d.ts +0 -16
  56. package/dist/schemas/tools.js +0 -15
  57. package/dist/schemas/transcription-config.d.ts +2 -0
  58. package/dist/schemas/transcription-config.js +10 -4
  59. package/dist/schemas/transcription.d.ts +3 -0
  60. package/dist/schemas/vla.d.ts +83 -0
  61. package/dist/schemas/vla.js +105 -2
  62. package/dist/scripts/link-workspace-inference.d.ts +2 -0
  63. package/dist/scripts/link-workspace-inference.js +31 -0
  64. package/dist/server/bare/ops/cancel.d.ts +1 -1
  65. package/dist/server/bare/ops/cancel.js +1 -1
  66. package/dist/server/bare/ops/kv-cache-retention.js +11 -0
  67. package/dist/server/bare/ops/kv-cache-utils.d.ts +1 -1
  68. package/dist/server/bare/ops/kv-cache-utils.js +40 -12
  69. package/dist/server/bare/ops/transcribe.d.ts +2 -2
  70. package/dist/server/bare/ops/transcribe.js +54 -48
  71. package/dist/server/bare/ops/translate-stats.d.ts +5 -0
  72. package/dist/server/bare/ops/translate-stats.js +11 -0
  73. package/dist/server/bare/ops/translate.js +66 -38
  74. package/dist/server/bare/ops/unload-model.js +30 -24
  75. package/dist/server/bare/plugins/audiogen-ggml/ops/audio-gen-input.d.ts +20 -0
  76. package/dist/server/bare/plugins/audiogen-ggml/ops/audio-gen-input.js +153 -0
  77. package/dist/server/bare/plugins/audiogen-ggml/ops/audio-gen-stream.js +52 -1
  78. package/dist/server/bare/plugins/audiogen-ggml/plugin.d.ts +28 -0
  79. package/dist/server/bare/plugins/ggml-ocr/plugin.js +3 -1
  80. package/dist/server/bare/plugins/ggml-vla/ops/vla-set-embodiment.d.ts +3 -0
  81. package/dist/server/bare/plugins/ggml-vla/ops/vla-set-embodiment.js +9 -0
  82. package/dist/server/bare/plugins/ggml-vla/plugin.d.ts +37 -0
  83. package/dist/server/bare/plugins/ggml-vla/plugin.js +21 -4
  84. package/dist/server/bare/plugins/llamacpp-completion/ops/batch-completion-stream.js +16 -15
  85. package/dist/server/bare/plugins/llamacpp-completion/ops/cache-logger.d.ts +1 -1
  86. package/dist/server/bare/plugins/llamacpp-completion/ops/cache-logger.js +2 -2
  87. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stats.d.ts +6 -0
  88. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stats.js +10 -0
  89. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +1 -0
  90. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +131 -123
  91. package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +110 -20
  92. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.d.ts +49 -23
  93. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.js +308 -143
  94. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.d.ts +1 -0
  95. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.js +2 -2
  96. package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +6 -6
  97. package/dist/server/bare/plugins/llamacpp-completion/plugin.js +55 -36
  98. package/dist/server/bare/plugins/llamacpp-completion/transform.js +1 -7
  99. package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +2 -3
  100. package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +6 -5
  101. package/dist/server/bare/plugins/parakeet-transcription/plugin.js +27 -16
  102. package/dist/server/bare/plugins/tts-ggml/ops/parler-options.js +23 -3
  103. package/dist/server/bare/plugins/tts-ggml/plugin.d.ts +302 -4
  104. package/dist/server/bare/plugins/tts-ggml/plugin.js +131 -0
  105. package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +48 -39
  106. package/dist/server/bare/registry/model-registry.d.ts +1 -0
  107. package/dist/server/bare/runtime/request-registry-singleton.js +29 -15
  108. package/dist/server/bare/runtime/request-registry.d.ts +27 -0
  109. package/dist/server/bare/runtime/request-registry.js +246 -76
  110. package/dist/server/bare/types/addon-responses.d.ts +4 -12
  111. package/dist/server/rpc/handlers/load-model/handler.js +2 -2
  112. package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +1 -1
  113. package/dist/server/rpc/handlers/load-model/resolve-session.js +3 -3
  114. package/dist/server/rpc/handlers/load-model/resolve.d.ts +12 -2
  115. package/dist/server/rpc/handlers/load-model/resolve.js +90 -12
  116. package/dist/server/utils/audio/decoder.d.ts +37 -1
  117. package/dist/server/utils/audio/decoder.js +81 -51
  118. package/dist/server/utils/config-transform.d.ts +3 -0
  119. package/dist/server/utils/config-transform.js +6 -0
  120. package/dist/server/utils/index.d.ts +0 -1
  121. package/dist/server/utils/index.js +0 -1
  122. package/dist/server/utils/shard-utils.d.ts +1 -0
  123. package/dist/server/utils/shard-utils.js +14 -0
  124. package/dist/server/utils/tool-integration.d.ts +3 -10
  125. package/dist/server/utils/tool-integration.js +3 -12
  126. package/dist/utils/errors-server.d.ts +3 -0
  127. package/dist/utils/errors-server.js +5 -0
  128. package/package.json +12 -14
  129. package/dist/examples/llamacpp-dynamic-tools.d.ts +0 -2
  130. package/dist/examples/llamacpp-dynamic-tools.js +0 -152
  131. package/dist/models/update-models/codegen.d.ts +0 -3
  132. package/dist/models/update-models/codegen.js +0 -196
  133. package/dist/models/update-models/companions.d.ts +0 -30
  134. package/dist/models/update-models/companions.js +0 -296
  135. package/dist/models/update-models/history.d.ts +0 -24
  136. package/dist/models/update-models/history.js +0 -107
  137. package/dist/models/update-models/index.d.ts +0 -2
  138. package/dist/models/update-models/index.js +0 -123
  139. package/dist/models/update-models/naming.d.ts +0 -4
  140. package/dist/models/update-models/naming.js +0 -396
  141. package/dist/models/update-models/processing.d.ts +0 -8
  142. package/dist/models/update-models/processing.js +0 -65
  143. package/dist/models/update-models/registry.d.ts +0 -5
  144. package/dist/models/update-models/registry.js +0 -87
  145. package/dist/models/update-models/shards.d.ts +0 -4
  146. package/dist/models/update-models/shards.js +0 -57
  147. package/dist/models/update-models/types.d.ts +0 -88
  148. package/dist/models/update-models/types.js +0 -1
  149. package/dist/models/update-models/utils.d.ts +0 -3
  150. package/dist/models/update-models/utils.js +0 -21
  151. package/dist/server/bare/plugins/llamacpp-completion/ops/batch-cancelled.d.ts +0 -23
  152. package/dist/server/bare/plugins/llamacpp-completion/ops/batch-cancelled.js +0 -30
  153. package/dist/server/utils/expand-gguf-shards.d.ts +0 -3
  154. 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 duplex(request, options);
127
- const responses = parseLines(responseStream, process);
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
- for await (const chunk of responseStream) {
167
- buf += chunk.toString();
168
- const lines = buf.split('\n');
169
- buf = lines.pop() || '';
170
- for (const line of lines) {
171
- const result = process(line);
172
- if (result === null)
173
- return;
174
- if (result !== undefined)
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
- // Process any residual data after stream ends
179
- if (buf.trim()) {
180
- const result = process(buf);
181
- if (result !== null && result !== undefined)
182
- yield result;
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
- for await (const response of streamRpc(request, options)) {
61
- if (response.type === 'translate') {
62
- const streamResponse = translateResponseSchema.parse(response);
63
- if (!streamResponse.done) {
64
- yield streamResponse.token;
65
- }
66
- else {
67
- stats = streamResponse.stats;
68
- statsResolver(stats);
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
- for await (const response of streamRpc(request, options)) {
87
- if (response.type === 'translate') {
88
- const streamResponse = translateResponseSchema.parse(response);
89
- buffer += streamResponse.token;
90
- if (streamResponse.done) {
91
- stats = streamResponse.stats;
92
- statsResolver(stats);
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
  }
@@ -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
@@ -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 { generateWorkerEntry } from '../../commands/bundle/entry-gen.js';
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 workerEntry = generateWorkerEntry(pluginSpecifiers, sdkName, resolveSdkImport);
97
- await fsp.writeFile(entryPath, workerEntry, 'utf8');
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
- await runBarePack({
106
- entryPath,
107
- outputPath: bundlePath,
108
- hosts,
109
- importsMapPath,
110
- deferModules,
111
- quiet: options.quiet === true,
112
- logger
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parakeet-indic-conformer-filesystem.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=generate-cover.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=llamacpp-fallback.d.ts.map
@@ -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
+ }