@qvac/sdk 0.17.1 → 0.18.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.
Files changed (156) 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 +168 -82
  48. package/dist/schemas/load-model.d.ts +3678 -323
  49. package/dist/schemas/load-model.js +21 -3
  50. package/dist/schemas/public.d.ts +3 -0
  51. package/dist/schemas/public.js +6 -0
  52. package/dist/schemas/sdk-config.d.ts +60 -24
  53. package/dist/schemas/sdk-errors-server.d.ts +1 -0
  54. package/dist/schemas/sdk-errors-server.js +5 -0
  55. package/dist/schemas/text-to-speech.d.ts +1086 -110
  56. package/dist/schemas/text-to-speech.js +194 -4
  57. package/dist/schemas/tools.d.ts +0 -16
  58. package/dist/schemas/tools.js +0 -15
  59. package/dist/schemas/transcription-config.d.ts +2 -0
  60. package/dist/schemas/transcription-config.js +10 -4
  61. package/dist/schemas/transcription.d.ts +3 -0
  62. package/dist/schemas/vla.d.ts +83 -0
  63. package/dist/schemas/vla.js +105 -2
  64. package/dist/scripts/link-workspace-inference.d.ts +2 -0
  65. package/dist/scripts/link-workspace-inference.js +31 -0
  66. package/dist/server/bare/ops/cancel.d.ts +1 -1
  67. package/dist/server/bare/ops/cancel.js +1 -1
  68. package/dist/server/bare/ops/kv-cache-retention.js +11 -0
  69. package/dist/server/bare/ops/kv-cache-utils.d.ts +1 -1
  70. package/dist/server/bare/ops/kv-cache-utils.js +40 -12
  71. package/dist/server/bare/ops/transcribe.d.ts +2 -2
  72. package/dist/server/bare/ops/transcribe.js +54 -48
  73. package/dist/server/bare/ops/translate-stats.d.ts +5 -0
  74. package/dist/server/bare/ops/translate-stats.js +11 -0
  75. package/dist/server/bare/ops/translate.js +66 -38
  76. package/dist/server/bare/ops/unload-model.js +30 -24
  77. package/dist/server/bare/plugins/audiogen-ggml/ops/audio-gen-input.d.ts +20 -0
  78. package/dist/server/bare/plugins/audiogen-ggml/ops/audio-gen-input.js +153 -0
  79. package/dist/server/bare/plugins/audiogen-ggml/ops/audio-gen-stream.js +52 -1
  80. package/dist/server/bare/plugins/audiogen-ggml/plugin.d.ts +28 -0
  81. package/dist/server/bare/plugins/ggml-ocr/plugin.js +3 -1
  82. package/dist/server/bare/plugins/ggml-vla/ops/vla-set-embodiment.d.ts +3 -0
  83. package/dist/server/bare/plugins/ggml-vla/ops/vla-set-embodiment.js +9 -0
  84. package/dist/server/bare/plugins/ggml-vla/plugin.d.ts +37 -0
  85. package/dist/server/bare/plugins/ggml-vla/plugin.js +21 -4
  86. package/dist/server/bare/plugins/llamacpp-completion/ops/batch-completion-stream.js +16 -15
  87. package/dist/server/bare/plugins/llamacpp-completion/ops/cache-logger.d.ts +1 -1
  88. package/dist/server/bare/plugins/llamacpp-completion/ops/cache-logger.js +2 -2
  89. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stats.d.ts +6 -0
  90. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stats.js +10 -0
  91. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +1 -0
  92. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +131 -123
  93. package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +110 -20
  94. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.d.ts +49 -23
  95. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.js +308 -143
  96. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.d.ts +1 -0
  97. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.js +2 -2
  98. package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +6 -6
  99. package/dist/server/bare/plugins/llamacpp-completion/plugin.js +55 -36
  100. package/dist/server/bare/plugins/llamacpp-completion/transform.js +1 -7
  101. package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +2 -3
  102. package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +6 -5
  103. package/dist/server/bare/plugins/parakeet-transcription/plugin.js +27 -16
  104. package/dist/server/bare/plugins/tts-ggml/ops/parler-options.js +23 -3
  105. package/dist/server/bare/plugins/tts-ggml/plugin.d.ts +302 -4
  106. package/dist/server/bare/plugins/tts-ggml/plugin.js +131 -0
  107. package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +48 -39
  108. package/dist/server/bare/registry/model-registry.d.ts +1 -0
  109. package/dist/server/bare/runtime/request-registry-singleton.js +29 -15
  110. package/dist/server/bare/runtime/request-registry.d.ts +27 -0
  111. package/dist/server/bare/runtime/request-registry.js +246 -76
  112. package/dist/server/bare/types/addon-responses.d.ts +4 -12
  113. package/dist/server/rpc/handlers/load-model/handler.js +2 -2
  114. package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +1 -1
  115. package/dist/server/rpc/handlers/load-model/resolve-session.js +3 -3
  116. package/dist/server/rpc/handlers/load-model/resolve.d.ts +12 -2
  117. package/dist/server/rpc/handlers/load-model/resolve.js +90 -12
  118. package/dist/server/utils/audio/decoder.d.ts +37 -1
  119. package/dist/server/utils/audio/decoder.js +81 -51
  120. package/dist/server/utils/config-transform.d.ts +3 -0
  121. package/dist/server/utils/config-transform.js +6 -0
  122. package/dist/server/utils/index.d.ts +0 -1
  123. package/dist/server/utils/index.js +0 -1
  124. package/dist/server/utils/shard-utils.d.ts +1 -0
  125. package/dist/server/utils/shard-utils.js +14 -0
  126. package/dist/server/utils/tool-integration.d.ts +3 -10
  127. package/dist/server/utils/tool-integration.js +3 -12
  128. package/dist/utils/errors-server.d.ts +3 -0
  129. package/dist/utils/errors-server.js +5 -0
  130. package/package.json +17 -14
  131. package/dist/examples/llamacpp-dynamic-tools.d.ts +0 -2
  132. package/dist/examples/llamacpp-dynamic-tools.js +0 -152
  133. package/dist/models/update-models/codegen.d.ts +0 -3
  134. package/dist/models/update-models/codegen.js +0 -196
  135. package/dist/models/update-models/companions.d.ts +0 -30
  136. package/dist/models/update-models/companions.js +0 -296
  137. package/dist/models/update-models/history.d.ts +0 -24
  138. package/dist/models/update-models/history.js +0 -107
  139. package/dist/models/update-models/index.d.ts +0 -2
  140. package/dist/models/update-models/index.js +0 -123
  141. package/dist/models/update-models/naming.d.ts +0 -4
  142. package/dist/models/update-models/naming.js +0 -396
  143. package/dist/models/update-models/processing.d.ts +0 -8
  144. package/dist/models/update-models/processing.js +0 -65
  145. package/dist/models/update-models/registry.d.ts +0 -5
  146. package/dist/models/update-models/registry.js +0 -87
  147. package/dist/models/update-models/shards.d.ts +0 -4
  148. package/dist/models/update-models/shards.js +0 -57
  149. package/dist/models/update-models/types.d.ts +0 -88
  150. package/dist/models/update-models/types.js +0 -1
  151. package/dist/models/update-models/utils.d.ts +0 -3
  152. package/dist/models/update-models/utils.js +0 -21
  153. package/dist/server/bare/plugins/llamacpp-completion/ops/batch-cancelled.d.ts +0 -23
  154. package/dist/server/bare/plugins/llamacpp-completion/ops/batch-cancelled.js +0 -30
  155. package/dist/server/utils/expand-gguf-shards.d.ts +0 -3
  156. package/dist/server/utils/expand-gguf-shards.js +0 -22
@@ -0,0 +1,153 @@
1
+ import fs from 'bare-fs';
2
+ import { AUDIOGEN_INPUT_CHANNELS, AUDIOGEN_INPUT_MAX_SECONDS, AUDIOGEN_INPUT_SAMPLE_RATE } from '../../../../../schemas/audio-gen.js';
3
+ import { decodeAudioToStream, needsDecoding } from '../../../../../server/utils/audio/decoder.js';
4
+ import { AudioFileNotFoundError, InvalidAudioInputError } from '../../../../../utils/errors-server.js';
5
+ const FLOAT32_BYTES = Float32Array.BYTES_PER_ELEMENT;
6
+ const STEREO_FRAME_BYTES = FLOAT32_BYTES * AUDIOGEN_INPUT_CHANNELS;
7
+ /** Upper bound for raw stereo input bytes (base64 payloads and raw PCM files). */
8
+ const MAX_STEREO_BYTES = AUDIOGEN_INPUT_MAX_SECONDS * AUDIOGEN_INPUT_SAMPLE_RATE * STEREO_FRAME_BYTES;
9
+ /** Upper bound for the mono float stream the FFmpeg decoder may emit. */
10
+ const MAX_DECODED_MONO_BYTES = AUDIOGEN_INPUT_MAX_SECONDS * AUDIOGEN_INPUT_SAMPLE_RATE * FLOAT32_BYTES;
11
+ /**
12
+ * Resolve a reference/source audio input into the interleaved stereo 48 kHz
13
+ * Float32 PCM the ACE-Step engine consumes.
14
+ *
15
+ * - `base64` inputs (client `Buffer`/`Uint8Array`) must already be raw
16
+ * interleaved stereo 48 kHz Float32 LE PCM; they are handed over as-is
17
+ * (a copy is made only when the bytes are not 4-byte aligned).
18
+ * - `filePath` inputs with a decodable extension (`.wav`, `.mp3`, `.m4a`,
19
+ * `.ogg`, `.flac`, `.aac`) are decoded through the SDK's FFmpeg decoder to
20
+ * 48 kHz mono float PCM and duplicated onto both channels. Any other
21
+ * extension is streamed in as raw interleaved stereo 48 kHz Float32 LE PCM.
22
+ *
23
+ * Every path is bounded by `AUDIOGEN_INPUT_MAX_SECONDS` and rejects
24
+ * non-finite samples, so malformed or oversized input fails with
25
+ * `InvalidAudioInputError` before the engine is invoked.
26
+ */
27
+ export async function resolveAudioGenPcm(input, name) {
28
+ switch (input.type) {
29
+ case 'base64':
30
+ return toStereoFloat32(Buffer.from(input.value, 'base64'), name);
31
+ case 'filePath': {
32
+ const filePath = input.value;
33
+ if (!needsDecoding(filePath)) {
34
+ return toStereoFloat32(await readRawPcmFile(filePath, name), name);
35
+ }
36
+ await assertDecodableFileSize(filePath, name);
37
+ return monoToStereo(await decodeMonoFloat32(filePath, name), name);
38
+ }
39
+ default:
40
+ throw new InvalidAudioInputError(`${name} must be a file path or raw PCM bytes`);
41
+ }
42
+ }
43
+ async function statInput(filePath) {
44
+ try {
45
+ return await fs.promises.stat(filePath);
46
+ }
47
+ catch (error) {
48
+ throw new AudioFileNotFoundError(filePath, error);
49
+ }
50
+ }
51
+ /**
52
+ * The FFmpeg decoder buffers the whole input file before emitting PCM, so the
53
+ * file itself is bounded up front: no encoded container for a clip within
54
+ * `AUDIOGEN_INPUT_MAX_SECONDS` exceeds the size of that clip as raw stereo
55
+ * Float32 PCM.
56
+ */
57
+ async function assertDecodableFileSize(filePath, name) {
58
+ const stats = await statInput(filePath);
59
+ if (stats.size <= MAX_STEREO_BYTES)
60
+ return;
61
+ throw new InvalidAudioInputError(`${name} (${filePath}) is ${Math.round(stats.size / 1_000_000)} MB; ` +
62
+ `files over ${Math.round(MAX_STEREO_BYTES / 1_000_000)} MB cannot fit the ` +
63
+ `${AUDIOGEN_INPUT_MAX_SECONDS} s limit and are rejected before decoding`);
64
+ }
65
+ /** Stream a raw PCM file into memory without blocking the event loop, capped by size. */
66
+ async function readRawPcmFile(filePath, name) {
67
+ const stats = await statInput(filePath);
68
+ assertWithinLimit(stats.size, MAX_STEREO_BYTES, STEREO_FRAME_BYTES, name, filePath);
69
+ const chunks = [];
70
+ let total = 0;
71
+ const stream = fs.createReadStream(filePath);
72
+ for await (const chunk of stream) {
73
+ total += chunk.byteLength;
74
+ assertWithinLimit(total, MAX_STEREO_BYTES, STEREO_FRAME_BYTES, name, filePath);
75
+ chunks.push(chunk);
76
+ }
77
+ return chunks.length === 1 ? chunks[0] : Buffer.concat(chunks, total);
78
+ }
79
+ async function decodeMonoFloat32(filePath, name) {
80
+ const chunks = [];
81
+ let total = 0;
82
+ try {
83
+ const stream = await decodeAudioToStream(filePath, 'f32le', {
84
+ sampleRate: AUDIOGEN_INPUT_SAMPLE_RATE
85
+ });
86
+ for await (const chunk of stream) {
87
+ total += chunk.byteLength;
88
+ if (total > MAX_DECODED_MONO_BYTES) {
89
+ ;
90
+ stream.destroy();
91
+ assertWithinLimit(total, MAX_DECODED_MONO_BYTES, FLOAT32_BYTES, name, filePath);
92
+ }
93
+ chunks.push(chunk);
94
+ }
95
+ }
96
+ catch (error) {
97
+ if (error instanceof InvalidAudioInputError)
98
+ throw error;
99
+ throw new InvalidAudioInputError(`${name} could not be decoded from ${filePath}`, error);
100
+ }
101
+ if (total === 0 || total % FLOAT32_BYTES !== 0) {
102
+ throw new InvalidAudioInputError(`${name} decoded to no usable audio from ${filePath}`);
103
+ }
104
+ return asFloat32(chunks.length === 1 ? chunks[0] : Buffer.concat(chunks, total));
105
+ }
106
+ function monoToStereo(mono, name) {
107
+ if (mono.length === 0) {
108
+ throw new InvalidAudioInputError(`${name} decoded to no usable audio`);
109
+ }
110
+ const stereo = new Float32Array(mono.length * AUDIOGEN_INPUT_CHANNELS);
111
+ for (let frame = 0; frame < mono.length; frame++) {
112
+ const sample = mono[frame];
113
+ if (!Number.isFinite(sample))
114
+ throw nonFiniteError(name);
115
+ stereo[frame * AUDIOGEN_INPUT_CHANNELS] = sample;
116
+ stereo[frame * AUDIOGEN_INPUT_CHANNELS + 1] = sample;
117
+ }
118
+ return stereo;
119
+ }
120
+ function toStereoFloat32(bytes, name) {
121
+ if (bytes.byteLength === 0 || bytes.byteLength % STEREO_FRAME_BYTES !== 0) {
122
+ throw new InvalidAudioInputError(`${name} must be non-empty interleaved stereo ${AUDIOGEN_INPUT_SAMPLE_RATE} Hz Float32 PCM ` +
123
+ `(byte length a multiple of ${STEREO_FRAME_BYTES}, got ${bytes.byteLength})`);
124
+ }
125
+ assertWithinLimit(bytes.byteLength, MAX_STEREO_BYTES, STEREO_FRAME_BYTES, name);
126
+ const pcm = asFloat32(bytes);
127
+ for (let index = 0; index < pcm.length; index++) {
128
+ if (!Number.isFinite(pcm[index]))
129
+ throw nonFiniteError(name);
130
+ }
131
+ return pcm;
132
+ }
133
+ /**
134
+ * View the bytes as Float32 without copying when they are 4-byte aligned
135
+ * (fresh Buffers from base64/file reads and `Buffer.concat` are); only a
136
+ * misaligned pooled slice needs to be copied out.
137
+ */
138
+ function asFloat32(bytes) {
139
+ if (bytes.byteOffset % FLOAT32_BYTES === 0) {
140
+ return new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / FLOAT32_BYTES);
141
+ }
142
+ return new Float32Array(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
143
+ }
144
+ function assertWithinLimit(bytes, limit, frameBytes, name, filePath) {
145
+ if (bytes <= limit)
146
+ return;
147
+ const seconds = Math.round(bytes / frameBytes / AUDIOGEN_INPUT_SAMPLE_RATE);
148
+ throw new InvalidAudioInputError(`${name}${filePath ? ` (${filePath})` : ''} is about ${seconds} s long; ` +
149
+ `the limit is ${AUDIOGEN_INPUT_MAX_SECONDS} s`);
150
+ }
151
+ function nonFiniteError(name) {
152
+ return new InvalidAudioInputError(`${name} must contain only finite samples`);
153
+ }
@@ -53,6 +53,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
53
53
  import { AudioGen } from '@qvac/audiogen-ggml';
54
54
  import { audioGenStatsSchema } from '../../../../../schemas/audio-gen.js';
55
55
  import { getServerLogger } from '../../../../../logging/index.js';
56
+ import { resolveAudioGenPcm } from '../../../../../server/bare/plugins/audiogen-ggml/ops/audio-gen-input.js';
56
57
  import { getModel } from '../../../../../server/bare/registry/model-registry.js';
57
58
  import { getRequestRegistry, withRequestContext } from '../../../../../server/bare/runtime/index.js';
58
59
  import { generateServerRequestId } from '../../../../../server/bare/runtime/request-id.js';
@@ -97,6 +98,14 @@ export async function* audioGenStream(request) {
97
98
  });
98
99
  let response;
99
100
  try {
101
+ // Reference/source audio is decoded before the run is admitted so the
102
+ // model slot is never held by a request that fails on input decoding.
103
+ // Decoding can take real time, so the abort state is re-checked after it:
104
+ // an abort that fired mid-decode found no active native job to cancel, and
105
+ // starting the run afterwards would leave a ghost generation on the slot.
106
+ const { referenceAudio, sourceAudio } = ctx.signal.aborted
107
+ ? {}
108
+ : await resolveAudioInputs(request, logger);
100
109
  if (!ctx.signal.aborted) {
101
110
  response = await model.run(request.caption, {
102
111
  ...(request.lyrics !== undefined && { lyrics: request.lyrics }),
@@ -105,7 +114,24 @@ export async function* audioGenStream(request) {
105
114
  ...(request.bpm !== undefined && { bpm: request.bpm }),
106
115
  ...(request.keyscale !== undefined && { keyscale: request.keyscale }),
107
116
  ...(request.timesignature !== undefined && { timesignature: request.timesignature }),
108
- ...(request.duration !== undefined && { duration: request.duration })
117
+ ...(request.duration !== undefined && { duration: request.duration }),
118
+ ...(request.lmTemperature !== undefined && { lmTemperature: request.lmTemperature }),
119
+ ...(request.lmTopP !== undefined && { lmTopP: request.lmTopP }),
120
+ ...(request.lmTopK !== undefined && { lmTopK: request.lmTopK }),
121
+ ...(request.lmCfgScale !== undefined && { lmCfgScale: request.lmCfgScale }),
122
+ ...(request.lmPhase1 !== undefined && { lmPhase1: request.lmPhase1 }),
123
+ ...(request.dcwEnabled !== undefined && { dcwEnabled: request.dcwEnabled }),
124
+ ...(request.dcwScaler !== undefined && { dcwScaler: request.dcwScaler }),
125
+ ...(request.dcwHighScaler !== undefined && { dcwHighScaler: request.dcwHighScaler }),
126
+ ...(request.taskType !== undefined && { taskType: request.taskType }),
127
+ ...(request.audioCoverStrength !== undefined && {
128
+ audioCoverStrength: request.audioCoverStrength
129
+ }),
130
+ ...(request.coverNoiseStrength !== undefined && {
131
+ coverNoiseStrength: request.coverNoiseStrength
132
+ }),
133
+ ...(referenceAudio && { referenceAudio }),
134
+ ...(sourceAudio && { sourceAudio })
109
135
  });
110
136
  for await (const chunk of response.iterate()) {
111
137
  if (ctx.signal.aborted)
@@ -160,3 +186,28 @@ export async function* audioGenStream(request) {
160
186
  await result_1;
161
187
  }
162
188
  }
189
+ /**
190
+ * Decode both optional audio inputs concurrently. When both fail, the first
191
+ * failure is thrown and the second is logged so neither diagnostic is lost.
192
+ */
193
+ async function resolveAudioInputs(request, logger) {
194
+ const [reference, source] = await Promise.allSettled([
195
+ request.referenceAudio
196
+ ? resolveAudioGenPcm(request.referenceAudio, 'referenceAudio')
197
+ : Promise.resolve(undefined),
198
+ request.sourceAudio
199
+ ? resolveAudioGenPcm(request.sourceAudio, 'sourceAudio')
200
+ : Promise.resolve(undefined)
201
+ ]);
202
+ const failures = [reference, source].filter((result) => result.status === 'rejected');
203
+ if (failures.length > 0) {
204
+ for (const extra of failures.slice(1)) {
205
+ logger.warn(`[audiogen] additional audio input failure for modelId=${request.modelId}: ${extra.reason instanceof Error ? extra.reason.message : String(extra.reason)}`);
206
+ }
207
+ throw failures[0].reason;
208
+ }
209
+ return {
210
+ referenceAudio: reference.status === 'fulfilled' ? reference.value : undefined,
211
+ sourceAudio: source.status === 'fulfilled' ? source.value : undefined
212
+ };
213
+ }
@@ -175,6 +175,20 @@ export declare const audioGenPlugin: {
175
175
  audioGenStream: import("../../../../schemas/index.js").PluginHandlerDefinition<import("zod").ZodObject<{
176
176
  type: import("zod").ZodLiteral<"audioGenStream">;
177
177
  requestId: import("zod").ZodOptional<import("zod").ZodString>;
178
+ referenceAudio: import("zod").ZodOptional<import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
179
+ type: import("zod").ZodLiteral<"base64">;
180
+ value: import("zod").ZodString;
181
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
182
+ type: import("zod").ZodLiteral<"filePath">;
183
+ value: import("zod").ZodString;
184
+ }, import("zod/v4/core").$strip>], "type">>;
185
+ sourceAudio: import("zod").ZodOptional<import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
186
+ type: import("zod").ZodLiteral<"base64">;
187
+ value: import("zod").ZodString;
188
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
189
+ type: import("zod").ZodLiteral<"filePath">;
190
+ value: import("zod").ZodString;
191
+ }, import("zod/v4/core").$strip>], "type">>;
178
192
  modelId: import("zod").ZodString;
179
193
  caption: import("zod").ZodString;
180
194
  lyrics: import("zod").ZodOptional<import("zod").ZodString>;
@@ -184,6 +198,20 @@ export declare const audioGenPlugin: {
184
198
  keyscale: import("zod").ZodOptional<import("zod").ZodString>;
185
199
  timesignature: import("zod").ZodOptional<import("zod").ZodString>;
186
200
  duration: import("zod").ZodOptional<import("zod").ZodNumber>;
201
+ lmTemperature: import("zod").ZodOptional<import("zod").ZodNumber>;
202
+ lmTopP: import("zod").ZodOptional<import("zod").ZodNumber>;
203
+ lmTopK: import("zod").ZodOptional<import("zod").ZodNumber>;
204
+ lmCfgScale: import("zod").ZodOptional<import("zod").ZodNumber>;
205
+ lmPhase1: import("zod").ZodOptional<import("zod").ZodBoolean>;
206
+ dcwEnabled: import("zod").ZodOptional<import("zod").ZodBoolean>;
207
+ dcwScaler: import("zod").ZodOptional<import("zod").ZodNumber>;
208
+ dcwHighScaler: import("zod").ZodOptional<import("zod").ZodNumber>;
209
+ taskType: import("zod").ZodOptional<import("zod").ZodEnum<{
210
+ text2music: "text2music";
211
+ "cover-nofsq": "cover-nofsq";
212
+ }>>;
213
+ audioCoverStrength: import("zod").ZodOptional<import("zod").ZodNumber>;
214
+ coverNoiseStrength: import("zod").ZodOptional<import("zod").ZodNumber>;
187
215
  }, import("zod/v4/core").$strict>, import("zod").ZodObject<{
188
216
  type: import("zod").ZodLiteral<"audioGenStream">;
189
217
  progress: import("zod").ZodOptional<import("zod").ZodObject<{
@@ -12,7 +12,9 @@ function createOCRModel(modelId, detectorPath, recognizerPath, ocrConfig) {
12
12
  const params = {
13
13
  pathDetector: detectorPath,
14
14
  pathRecognizer: recognizerPath,
15
- langList: ocrConfig.langList || ['en'],
15
+ ...(ocrConfig.langList !== undefined
16
+ ? { langList: ocrConfig.langList }
17
+ : ocrConfig.pipelineType !== 'doctr' && { langList: ['en'] }),
16
18
  ...(ocrConfig.pipelineType !== undefined && {
17
19
  pipelineType: ocrConfig.pipelineType
18
20
  }),
@@ -0,0 +1,3 @@
1
+ import { type VlaSetEmbodimentRequest, type VlaSetEmbodimentResponse } from '../../../../../schemas/index.js';
2
+ export declare function vlaSetEmbodiment(request: VlaSetEmbodimentRequest): Promise<VlaSetEmbodimentResponse>;
3
+ //# sourceMappingURL=vla-set-embodiment.d.ts.map
@@ -0,0 +1,9 @@
1
+ import { getModel } from '../../../../../server/bare/registry/model-registry.js';
2
+ import { vlaHparamsSchema } from '../../../../../schemas/index.js';
3
+ export async function vlaSetEmbodiment(request) {
4
+ const model = getModel(request.modelId);
5
+ const hparams = await model.setEmbodiment(request.embodiment);
6
+ // Validate the addon-reported hparams against our schema so the wire
7
+ // shape stays consistent even if the underlying addon changes.
8
+ return { hparams: vlaHparamsSchema.parse(hparams) };
9
+ }
@@ -9,6 +9,11 @@ export declare const vlaPlugin: {
9
9
  auto: "auto";
10
10
  }>>;
11
11
  verbosity: import("zod").ZodOptional<import("zod").ZodNumber>;
12
+ embodiment: import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodString, import("zod").ZodNumber, import("zod").ZodObject<{
13
+ tag: import("zod").ZodOptional<import("zod").ZodString>;
14
+ catId: import("zod").ZodOptional<import("zod").ZodNumber>;
15
+ numCameras: import("zod").ZodOptional<import("zod").ZodNumber>;
16
+ }, import("zod/v4/core").$strip>]>>;
12
17
  }, import("zod/v4/core").$strip>;
13
18
  createModel(params: CreateModelParams): PluginModelResult;
14
19
  handlers: {
@@ -58,9 +63,41 @@ export declare const vlaPlugin: {
58
63
  patches: "patches";
59
64
  }>>;
60
65
  imagePatchElems: import("zod").ZodOptional<import("zod").ZodNumber>;
66
+ selectedEmbodimentTag: import("zod").ZodOptional<import("zod").ZodString>;
67
+ selectedEmbodimentCatId: import("zod").ZodOptional<import("zod").ZodNumber>;
61
68
  }, import("zod/v4/core").$strip>;
62
69
  backendName: import("zod").ZodNullable<import("zod").ZodString>;
63
70
  }, import("zod/v4/core").$strip>>;
71
+ vlaSetEmbodiment: import("../../../../schemas/index.js").PluginHandlerDefinition<import("zod").ZodObject<{
72
+ type: import("zod").ZodLiteral<"vlaSetEmbodiment">;
73
+ modelId: import("zod").ZodString;
74
+ embodiment: import("zod").ZodUnion<readonly [import("zod").ZodString, import("zod").ZodNumber, import("zod").ZodObject<{
75
+ tag: import("zod").ZodOptional<import("zod").ZodString>;
76
+ catId: import("zod").ZodOptional<import("zod").ZodNumber>;
77
+ numCameras: import("zod").ZodOptional<import("zod").ZodNumber>;
78
+ }, import("zod/v4/core").$strip>]>;
79
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
80
+ hparams: import("zod").ZodObject<{
81
+ chunkSize: import("zod").ZodNumber;
82
+ actionDim: import("zod").ZodNumber;
83
+ maxActionDim: import("zod").ZodNumber;
84
+ maxStateDim: import("zod").ZodNumber;
85
+ tokenizerMaxLength: import("zod").ZodNumber;
86
+ visionImageSize: import("zod").ZodNumber;
87
+ numCameras: import("zod").ZodOptional<import("zod").ZodNumber>;
88
+ stateInputMode: import("zod").ZodOptional<import("zod").ZodEnum<{
89
+ discrete: "discrete";
90
+ continuous: "continuous";
91
+ }>>;
92
+ imageInputMode: import("zod").ZodOptional<import("zod").ZodEnum<{
93
+ pixels: "pixels";
94
+ patches: "patches";
95
+ }>>;
96
+ imagePatchElems: import("zod").ZodOptional<import("zod").ZodNumber>;
97
+ selectedEmbodimentTag: import("zod").ZodOptional<import("zod").ZodString>;
98
+ selectedEmbodimentCatId: import("zod").ZodOptional<import("zod").ZodNumber>;
99
+ }, import("zod/v4/core").$strip>;
100
+ }, import("zod/v4/core").$strip>>;
64
101
  };
65
102
  };
66
103
  //# sourceMappingURL=plugin.d.ts.map
@@ -1,8 +1,9 @@
1
1
  import { VlaModel } from '@qvac/vla-ggml';
2
- import { definePlugin, defineHandler, vlaConfigSchema, vlaRunRequestSchema, vlaRunResponseSchema, vlaHparamsRequestSchema, vlaHparamsResponseSchema, ModelType, ADDON_VLA } from '../../../../schemas/index.js';
2
+ import { definePlugin, defineHandler, vlaConfigSchema, vlaRunRequestSchema, vlaRunResponseSchema, vlaHparamsRequestSchema, vlaHparamsResponseSchema, vlaSetEmbodimentRequestSchema, vlaSetEmbodimentResponseSchema, ModelType, ADDON_VLA } from '../../../../schemas/index.js';
3
3
  import { createStreamLogger, registerAddonLogger } from '../../../../logging/index.js';
4
4
  import { vlaRun } from './ops/vla-run.js';
5
5
  import { vlaGetHparams } from './ops/vla-hparams.js';
6
+ import { vlaSetEmbodiment } from './ops/vla-set-embodiment.js';
6
7
  // The `@qvac/vla-ggml` VlaModel exposes `load({ backend })` rather than the
7
8
  // `load(force?)` signature `PluginModel` expects. Wrap it so the plugin
8
9
  // framework can call `load()` and have the configured backend flow through.
@@ -23,11 +24,18 @@ export const vlaPlugin = definePlugin({
23
24
  const config = (params.modelConfig ?? {});
24
25
  const logger = createStreamLogger(params.modelId, ModelType.ggmlVla);
25
26
  registerAddonLogger(params.modelId, ModelType.ggmlVla, logger);
27
+ const addonConfig = {
28
+ ...(config.verbosity !== undefined && { verbosity: config.verbosity }),
29
+ // The zod-inferred selector widens optional props with `| undefined`,
30
+ // which exactOptionalPropertyTypes rejects against the addon's exact
31
+ // shape — the runtime value is identical, so re-assert the addon type.
32
+ ...(config.embodiment !== undefined && {
33
+ embodiment: config.embodiment
34
+ })
35
+ };
26
36
  const inner = new VlaModel({
27
37
  files: { model: [params.modelPath] },
28
- ...(config.verbosity !== undefined && {
29
- config: { verbosity: config.verbosity }
30
- }),
38
+ ...(Object.keys(addonConfig).length > 0 && { config: addonConfig }),
31
39
  logger,
32
40
  opts: { stats: true }
33
41
  });
@@ -52,6 +60,15 @@ export const vlaPlugin = definePlugin({
52
60
  streaming: false,
53
61
  cancel: { scope: 'none' },
54
62
  handler: vlaGetHparams
63
+ }),
64
+ vlaSetEmbodiment: defineHandler({
65
+ requestSchema: vlaSetEmbodimentRequestSchema,
66
+ responseSchema: vlaSetEmbodimentResponseSchema,
67
+ streaming: false,
68
+ // The addon refuses to switch while an inference job is in flight
69
+ // (JOB_ALREADY_RUNNING), so there is nothing meaningful to cancel here.
70
+ cancel: { scope: 'none' },
71
+ handler: vlaSetEmbodiment
55
72
  })
56
73
  }
57
74
  });
@@ -1,4 +1,3 @@
1
- import { TOOLS_MODE } from '../../../../../schemas/tools.js';
2
1
  import { getModel, getModelConfig } from '../../../../../server/bare/registry/model-registry.js';
3
2
  import { getServerLogger } from '../../../../../logging/index.js';
4
3
  import { nowMs } from '../../../../../profiling/index.js';
@@ -6,7 +5,7 @@ import { buildStreamResult } from '../../../../../profiling/model-execution.js';
6
5
  import { getResponseFormatJsonSchema } from '../../../../../server/utils/response-format.js';
7
6
  import { transformMessages } from '../../../../../server/bare/plugins/llamacpp-completion/ops/completion-stream.js';
8
7
  import { normalizeCompletionStats } from '../../../../../server/bare/plugins/llamacpp-completion/ops/completion-stats.js';
9
- import { appendToolsToHistory, prependToolsToHistory } from '../../../../../server/utils/tool-integration.js';
8
+ import { prependToolsToHistory } from '../../../../../server/utils/tool-integration.js';
10
9
  const logger = getServerLogger();
11
10
  function runBatchModel(model, prompts) {
12
11
  const run = model.run.bind(model);
@@ -27,10 +26,7 @@ function renderPromptHistory(prompt, options) {
27
26
  const tools = options.toolsEnabled && prompt.tools && prompt.tools.length > 0 ? prompt.tools : undefined;
28
27
  let historyWithTools = prompt.history;
29
28
  if (tools) {
30
- historyWithTools =
31
- options.toolsMode === TOOLS_MODE.dynamic
32
- ? appendToolsToHistory(prompt.history, tools)
33
- : prependToolsToHistory(prompt.history, tools);
29
+ historyWithTools = prependToolsToHistory(prompt.history, tools);
34
30
  }
35
31
  // Uses the same attachment expansion as single completion: each SDK
36
32
  // attachment becomes an addon `type: "media"` message before the text turn.
@@ -61,26 +57,31 @@ export async function* batchCompletion(params, opts) {
61
57
  const model = getModel(modelId);
62
58
  const modelConfig = getModelConfig(modelId);
63
59
  const renderOptions = {
64
- toolsEnabled: modelConfig.tools === true,
65
- toolsMode: modelConfig.toolsMode
60
+ toolsEnabled: modelConfig.tools === true
66
61
  };
67
- const onAbort = () => {
68
- const addon = model.addon;
69
- if (addon?.cancel) {
70
- addon.cancel.call(addon).catch((err) => {
71
- requestLogger.warn(`[cancel] addon.cancel() rejected during batch abort for modelId=${modelId}: ${err instanceof Error ? err.message : String(err)}`);
72
- });
73
- }
62
+ // Cancel via the batch response, mirroring completion(): the addon routes
63
+ // response.cancel() to cancelJob for this batch group only, so a concurrent
64
+ // peer batch or completion on the same model keeps decoding.
65
+ let activeResponse = null;
66
+ const cancelActive = () => {
67
+ activeResponse?.cancel().catch((err) => {
68
+ requestLogger.warn(`[cancel] batch response.cancel() rejected during abort for modelId=${modelId}: ${err instanceof Error ? err.message : String(err)}`);
69
+ });
74
70
  };
71
+ const onAbort = () => cancelActive();
75
72
  signal.addEventListener('abort', onAbort, { once: true });
76
73
  if (signal.aborted)
77
74
  onAbort();
78
75
  scope.defer(() => {
79
76
  signal.removeEventListener('abort', onAbort);
77
+ activeResponse = null;
80
78
  });
81
79
  const addonPrompts = prompts.map((prompt) => buildBatchPrompt(prompt, renderOptions));
82
80
  const modelStart = nowMs();
83
81
  const response = await runBatchModel(model, addonPrompts);
82
+ activeResponse = response;
83
+ if (signal.aborted)
84
+ cancelActive();
84
85
  const ids = response.ids;
85
86
  yield { type: 'ids', ids };
86
87
  for await (const output of response.iterate()) {
@@ -1,5 +1,5 @@
1
1
  export declare function logCacheStatus(cacheKey: string, isReusing: boolean): void;
2
- export declare function logCacheInit(cacheKey: string, systemPrompt: string, toolCount: number): void;
2
+ export declare function logCacheInit(cacheKey: string, systemPrompt: string): void;
3
3
  export declare function logMessagesToAddon(messages: unknown[], phase?: 'CACHE_INIT' | 'PROMPT_SEND' | 'NO_CACHE'): void;
4
4
  export declare function logCacheDisabled(): void;
5
5
  export declare function logCacheSave(sessionPath: string): void;
@@ -17,9 +17,9 @@ export function logCacheStatus(cacheKey, isReusing) {
17
17
  const status = isReusing ? 'REUSING' : 'CREATING';
18
18
  logger.debug(`[kv-cache] [${cacheKey}] ${status} cache`);
19
19
  }
20
- export function logCacheInit(cacheKey, systemPrompt, toolCount) {
20
+ export function logCacheInit(cacheKey, systemPrompt) {
21
21
  const promptLen = systemPrompt.length;
22
- logger.debug(`[kv-cache] [${cacheKey}] Initializing cache (prompt: ${promptLen} chars, tools: ${toolCount})`);
22
+ logger.debug(`[kv-cache] [${cacheKey}] Initializing cache (prompt: ${promptLen} chars)`);
23
23
  }
24
24
  export function logMessagesToAddon(messages, phase = 'PROMPT_SEND') {
25
25
  const typedMessages = messages;
@@ -18,4 +18,10 @@ export declare function normalizeCompletionStats(stats: LlmStats | undefined): {
18
18
  * when nothing was streamed.
19
19
  */
20
20
  export declare function withEmittedTokens(stats: CompletionStats | undefined, emittedPieces: number): CompletionStats | undefined;
21
+ export declare function stoppedByLength({ cancelled, effectivePredict, generatedTokens, stoppedAtContextBoundary }: {
22
+ cancelled: boolean;
23
+ effectivePredict: number | undefined;
24
+ generatedTokens: number | undefined;
25
+ stoppedAtContextBoundary: boolean;
26
+ }): boolean;
21
27
  //# sourceMappingURL=completion-stats.d.ts.map
@@ -46,3 +46,13 @@ export function withEmittedTokens(stats, emittedPieces) {
46
46
  emittedTokens
47
47
  };
48
48
  }
49
+ export function stoppedByLength({ cancelled, effectivePredict, generatedTokens, stoppedAtContextBoundary }) {
50
+ if (cancelled)
51
+ return false;
52
+ if (stoppedAtContextBoundary)
53
+ return true;
54
+ return (effectivePredict !== undefined &&
55
+ effectivePredict > 0 &&
56
+ generatedTokens !== undefined &&
57
+ generatedTokens >= effectivePredict);
58
+ }
@@ -6,6 +6,7 @@ interface CompletionResult {
6
6
  modelExecutionMs: number;
7
7
  stats?: CompletionStats;
8
8
  toolCalls: ToolCall[];
9
+ stoppedAtContextBoundary: boolean;
9
10
  }
10
11
  interface ChatHistory {
11
12
  role?: string;