@qvac/sdk 0.17.0 → 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 +83 -18
  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 +3 -2
  13. package/dist/client/api/translate.js +42 -21
  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
@@ -12,14 +12,14 @@ export function createResolveSession(options) {
12
12
  },
13
13
  ...(requestBinding !== undefined && { requestBinding })
14
14
  };
15
- async function resolvePrimaryModelPath(modelSrc) {
15
+ async function resolvePrimaryModelPath(modelSrc, fallbackSrc) {
16
16
  if (profilingEnabled) {
17
- const result = await resolveModelPathWithStats(modelSrc, progressCallback, seed, signal, downloadHooks);
17
+ const result = await resolveModelPathWithStats(modelSrc, progressCallback, seed, signal, downloadHooks, fallbackSrc);
18
18
  primaryResult = result;
19
19
  resolveResults.push(result);
20
20
  return result.path;
21
21
  }
22
- return resolveModelPath(modelSrc, progressCallback, seed, signal, downloadHooks);
22
+ return resolveModelPath(modelSrc, progressCallback, seed, signal, downloadHooks, fallbackSrc);
23
23
  }
24
24
  async function resolveForPlugin(src) {
25
25
  if (profilingEnabled) {
@@ -1,6 +1,16 @@
1
1
  import { type ModelProgressUpdate } from '../../../../schemas/index.js';
2
2
  import type { ResolveResult, DownloadHooks } from './types.js';
3
3
  import type { AbortSignal } from 'bare-abort-controller';
4
- export declare function resolveModelPath(modelSrc: unknown, progressCallback?: (progress: ModelProgressUpdate) => void, seed?: boolean, signal?: AbortSignal, hooks?: DownloadHooks): Promise<string>;
5
- export declare function resolveModelPathWithStats(modelSrc: unknown, progressCallback?: (progress: ModelProgressUpdate) => void, seed?: boolean, signal?: AbortSignal, hooks?: DownloadHooks): Promise<ResolveResult>;
4
+ type ResolveMode = 'base' | 'stats';
5
+ export declare function resolveFallbackModel(fallbackSrc: string, expectedChecksum: string | undefined, progressCallback: ((progress: ModelProgressUpdate) => void) | undefined, mode: ResolveMode, signal: AbortSignal | undefined, hooks: DownloadHooks | undefined): Promise<ResolveResult>;
6
+ export declare function resolveRegistryModel(params: {
7
+ hasFallback: boolean;
8
+ isKnownModel: boolean;
9
+ downloadPrimary: () => Promise<ResolveResult>;
10
+ resolveFallback: () => Promise<ResolveResult>;
11
+ buildUnreachableError: (cause: unknown) => Error;
12
+ }): Promise<ResolveResult>;
13
+ export declare function resolveModelPath(modelSrc: unknown, progressCallback?: (progress: ModelProgressUpdate) => void, seed?: boolean, signal?: AbortSignal, hooks?: DownloadHooks, fallbackSrc?: string): Promise<string>;
14
+ export declare function resolveModelPathWithStats(modelSrc: unknown, progressCallback?: (progress: ModelProgressUpdate) => void, seed?: boolean, signal?: AbortSignal, hooks?: DownloadHooks, fallbackSrc?: string): Promise<ResolveResult>;
15
+ export {};
6
16
  //# sourceMappingURL=resolve.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import { models, getModelByPath } from '../../../../models/registry/models.js';
2
2
  import { hyperdriveUrlSchema, registryUrlSchema, SUPPORTED_ARCHIVE_EXTENSIONS } from '../../../../schemas/index.js';
3
- import { getModelsCacheDir, getShardedModelCacheDir, generateShortHash, extractAndValidateShardedArchive } from '../../../../server/utils/index.js';
3
+ import { getModelsCacheDir, getShardedModelCacheDir, generateShortHash, extractAndValidateShardedArchive, measureChecksum } from '../../../../server/utils/index.js';
4
4
  import { promises as fsPromises } from 'bare-fs';
5
5
  import path from 'bare-path';
6
6
  import { downloadModelFromHttp } from './http.js';
@@ -8,7 +8,7 @@ import { downloadModelFromHyperdrive } from './hyperdrive.js';
8
8
  import { downloadModelFromRegistry } from './registry.js';
9
9
  import { getExplicitRegistryMetadata } from './registry-metadata.js';
10
10
  import { downloadModelFromHttpWithStats, downloadModelFromHyperdriveWithStats, downloadModelFromRegistryWithStats } from './download-stats.js';
11
- import { InferenceCancelledError, ModelLoadFailedError, ModelNotFoundError, SeedingNotSupportedError } from '../../../../utils/errors-server.js';
11
+ import { ChecksumValidationFailedError, DownloadCancelledError, InferenceCancelledError, ModelFileNotFoundError, ModelLoadFailedError, ModelNotFoundError, SeedingNotSupportedError } from '../../../../utils/errors-server.js';
12
12
  import { validateAndJoinPath } from '../../../../server/utils/path-security.js';
13
13
  import { getServerLogger } from '../../../../logging/index.js';
14
14
  import { modelInputToSrcSchema } from '../../../../schemas/index.js';
@@ -75,11 +75,66 @@ function buildResult(pathOrResult, sourceType) {
75
75
  }
76
76
  return { path: pathOrResult, sourceType };
77
77
  }
78
- async function resolveModelPathCore(modelSrc, progressCallback, seed, mode, signal, hooks) {
78
+ // A fallback is a non-P2P source: only HTTP URLs and file paths are valid.
79
+ // registry:// and pear:// are rejected.
80
+ export async function resolveFallbackModel(fallbackSrc, expectedChecksum, progressCallback, mode, signal, hooks) {
81
+ if (fallbackSrc.startsWith('registry://') || fallbackSrc.startsWith('pear://')) {
82
+ throw new ModelLoadFailedError('fallbackSrc must be an HTTP URL or a local file path.');
83
+ }
84
+ // seed applies only to hyperdrive sources, which a fallback can never be.
85
+ const result = await resolveModelPathCore(fallbackSrc, progressCallback, false, mode, signal, hooks);
86
+ // Report a missing fallback file as a not-found error before checksumming.
87
+ try {
88
+ await fsPromises.access(result.path);
89
+ }
90
+ catch {
91
+ throw new ModelFileNotFoundError(result.path);
92
+ }
93
+ if (expectedChecksum && expectedChecksum.length === 64) {
94
+ const actualChecksum = await measureChecksum(result.path, hooks);
95
+ if (actualChecksum !== expectedChecksum) {
96
+ // http downloads are cached under the models dir; drop a corrupt one.
97
+ if (result.sourceType === 'http') {
98
+ await fsPromises.unlink(result.path).catch(() => { });
99
+ }
100
+ throw new ChecksumValidationFailedError(fallbackSrc);
101
+ }
102
+ }
103
+ return result;
104
+ }
105
+ // Registry download with optional fallback. A user-initiated cancel always
106
+ // propagates. On any other primary failure: a fallback is taken when present;
107
+ // otherwise a known model surfaces a neutral error and an unknown one rethrows.
108
+ export async function resolveRegistryModel(params) {
109
+ try {
110
+ return await params.downloadPrimary();
111
+ }
112
+ catch (error) {
113
+ if (error instanceof InferenceCancelledError || error instanceof DownloadCancelledError) {
114
+ throw error;
115
+ }
116
+ if (params.hasFallback) {
117
+ return params.resolveFallback();
118
+ }
119
+ const isCorrectnessError = error instanceof ChecksumValidationFailedError ||
120
+ error instanceof ModelNotFoundError ||
121
+ error instanceof ModelFileNotFoundError;
122
+ if (params.isKnownModel && !isCorrectnessError) {
123
+ throw params.buildUnreachableError(error);
124
+ }
125
+ throw error;
126
+ }
127
+ }
128
+ async function resolveModelPathCore(modelSrc, progressCallback, seed, mode, signal, hooks, fallbackSrc) {
79
129
  if (signal?.aborted) {
80
130
  throw new InferenceCancelledError(hooks?.requestBinding?.requestId ?? 'unknown');
81
131
  }
82
132
  const srcString = modelInputToSrcSchema.parse(modelSrc);
133
+ // A fallback is validated against the catalog checksum, which exists only for
134
+ // built-in registry models. Other sources have no checksum to validate against.
135
+ if (fallbackSrc !== undefined && !srcString.startsWith('registry://')) {
136
+ throw new ModelLoadFailedError('fallbackSrc is only supported when modelSrc is a built-in registry model.');
137
+ }
83
138
  // Empty modelSrc is reserved for plugins that ship bundled weights
84
139
  // (e.g. `@qvac/classification-ggml`). The handler skips this resolver
85
140
  // for them when `skipPrimaryModelPathValidation` is set, but if it
@@ -105,11 +160,34 @@ async function resolveModelPathCore(modelSrc, progressCallback, seed, mode, sign
105
160
  const modelMetadata = getModelByPath(registryPath);
106
161
  const explicitMetadata = getExplicitRegistryMetadata(modelSrc);
107
162
  const expectedChecksum = modelMetadata?.sha256Checksum ?? explicitMetadata?.sha256Checksum;
108
- const result = mode === 'stats'
109
- ? await downloadModelFromRegistryWithStats(registryPath, registrySource, progressCallback, expectedChecksum, hooks, explicitMetadata)
110
- : await downloadModelFromRegistry(registryPath, registrySource, progressCallback, expectedChecksum, hooks, explicitMetadata);
111
- logger.info(`Loaded Model to ${isDownloadResult(result) ? result.path : result}`);
112
- return buildResult(result, 'registry');
163
+ // An uncatalogued registry path carries no checksum to validate a fallback against.
164
+ if (fallbackSrc !== undefined && !modelMetadata) {
165
+ throw new ModelLoadFailedError('fallbackSrc is only supported when modelSrc is a built-in registry model.');
166
+ }
167
+ // A single-file fallback cannot satisfy a sharded or companion-set model.
168
+ if (fallbackSrc !== undefined &&
169
+ (modelMetadata?.shardMetadata || modelMetadata?.companionSet)) {
170
+ throw new ModelLoadFailedError('fallbackSrc is not supported for sharded or multi-file models.');
171
+ }
172
+ return resolveRegistryModel({
173
+ hasFallback: fallbackSrc !== undefined,
174
+ isKnownModel: modelMetadata !== undefined,
175
+ downloadPrimary: async () => {
176
+ const result = mode === 'stats'
177
+ ? await downloadModelFromRegistryWithStats(registryPath, registrySource, progressCallback, expectedChecksum, hooks, explicitMetadata)
178
+ : await downloadModelFromRegistry(registryPath, registrySource, progressCallback, expectedChecksum, hooks, explicitMetadata);
179
+ logger.info(`Loaded Model to ${isDownloadResult(result) ? result.path : result}`);
180
+ return buildResult(result, 'registry');
181
+ },
182
+ resolveFallback: () => {
183
+ logger.info(`Registry download failed for ${registryPath}; trying fallbackSrc: ${fallbackSrc}`);
184
+ return resolveFallbackModel(fallbackSrc, expectedChecksum, progressCallback, mode, signal, hooks);
185
+ },
186
+ buildUnreachableError: (cause) => new ModelLoadFailedError(`Could not download "${modelMetadata?.modelId}" from the registry on this network ` +
187
+ `(${cause instanceof Error ? cause.message : String(cause)}). ` +
188
+ 'If the registry is not reliably reachable here, pass a fallbackSrc ' +
189
+ '(an HTTP URL or local file path) to load this model from an alternate source.', cause)
190
+ });
113
191
  }
114
192
  // Validate seeding is only used with hyperdrive models
115
193
  if (seed && !hyperdriveKey) {
@@ -150,10 +228,10 @@ async function resolveModelPathCore(modelSrc, progressCallback, seed, mode, sign
150
228
  }
151
229
  return { path: resolvedPath, sourceType: 'filesystem' };
152
230
  }
153
- export async function resolveModelPath(modelSrc, progressCallback, seed, signal, hooks) {
154
- const result = await resolveModelPathCore(modelSrc, progressCallback, seed, 'base', signal, hooks);
231
+ export async function resolveModelPath(modelSrc, progressCallback, seed, signal, hooks, fallbackSrc) {
232
+ const result = await resolveModelPathCore(modelSrc, progressCallback, seed, 'base', signal, hooks, fallbackSrc);
155
233
  return result.path;
156
234
  }
157
- export async function resolveModelPathWithStats(modelSrc, progressCallback, seed, signal, hooks) {
158
- return resolveModelPathCore(modelSrc, progressCallback, seed, 'stats', signal, hooks);
235
+ export async function resolveModelPathWithStats(modelSrc, progressCallback, seed, signal, hooks, fallbackSrc) {
236
+ return resolveModelPathCore(modelSrc, progressCallback, seed, 'stats', signal, hooks, fallbackSrc);
159
237
  }
@@ -1,5 +1,41 @@
1
+ import { type DecoderOutput } from '@qvac/decoder-audio';
1
2
  import { Readable } from 'bare-stream';
2
3
  import { type AudioFormat } from '../../../schemas/index.js';
3
4
  export declare function needsDecoding(filePath: string): boolean;
4
- export declare function decodeAudioToStream(inputPath: string, audioFormat?: AudioFormat): Promise<Readable>;
5
+ /**
6
+ * Longest silence the decoder may go without emitting a PCM chunk before the
7
+ * decode is abandoned. Measured between outputs, not against the whole file, so
8
+ * long inputs decode fine as long as FFmpeg keeps producing data.
9
+ */
10
+ export declare const DECODER_INACTIVITY_TIMEOUT_MS = 10000;
11
+ export interface DecodeAudioOptions {
12
+ /** Output sample rate; the decoder's default is 16 kHz (what every transcription engine expects). */
13
+ sampleRate?: number;
14
+ /** Inactivity timeout between decoded chunks (default `DECODER_INACTIVITY_TIMEOUT_MS`). */
15
+ inactivityTimeoutMs?: number;
16
+ }
17
+ /** The subset of the decoder's `QvacResponse` that the stream bridge consumes. */
18
+ export interface DecoderResponseLike {
19
+ onUpdate(callback: (output: DecoderOutput) => void): DecoderResponseLike;
20
+ onFinish(callback: () => void): DecoderResponseLike;
21
+ onError(callback: (error: Error) => void): DecoderResponseLike;
22
+ await(): Promise<unknown>;
23
+ }
24
+ export interface DecoderStreamOptions {
25
+ /** Used in error messages only. */
26
+ inputPath: string;
27
+ inactivityTimeoutMs?: number;
28
+ /** Runs once, on whichever terminal path (finish, error, timeout) settles the stream. */
29
+ onSettled?: () => void;
30
+ }
31
+ /**
32
+ * Decode an audio file into a raw mono PCM stream.
33
+ */
34
+ export declare function decodeAudioToStream(inputPath: string, audioFormat?: AudioFormat, options?: DecodeAudioOptions): Promise<Readable>;
35
+ /**
36
+ * Bridge a decoder response into a `Readable` of PCM chunks. The stream fails
37
+ * if the decoder emits nothing for `inactivityTimeoutMs`; the timer is re-armed
38
+ * on every chunk, so it bounds inactivity, not the total decode time.
39
+ */
40
+ export declare function decoderResponseToStream(response: DecoderResponseLike, options: DecoderStreamOptions): Readable;
5
41
  //# sourceMappingURL=decoder.d.ts.map
@@ -10,65 +10,30 @@ export function needsDecoding(filePath) {
10
10
  const ext = path.extname(filePath).toLowerCase();
11
11
  return FORMATS_NEEDING_DECODE.includes(ext);
12
12
  }
13
- const DECODER_TIMEOUT_MS = 10000;
14
- export async function decodeAudioToStream(inputPath, audioFormat = 's16le') {
13
+ /**
14
+ * Longest silence the decoder may go without emitting a PCM chunk before the
15
+ * decode is abandoned. Measured between outputs, not against the whole file, so
16
+ * long inputs decode fine as long as FFmpeg keeps producing data.
17
+ */
18
+ export const DECODER_INACTIVITY_TIMEOUT_MS = 10000;
19
+ /**
20
+ * Decode an audio file into a raw mono PCM stream.
21
+ */
22
+ export async function decodeAudioToStream(inputPath, audioFormat = 's16le', options = {}) {
23
+ const { sampleRate, inactivityTimeoutMs = DECODER_INACTIVITY_TIMEOUT_MS } = options;
15
24
  const decoder = new FFmpegDecoder({
16
- config: { audioFormat },
25
+ config: { audioFormat, ...(sampleRate !== undefined && { sampleRate }) },
17
26
  logger
18
27
  });
19
28
  try {
20
29
  await decoder.load();
21
30
  const audioStream = fs.createReadStream(inputPath);
22
31
  const response = decoder.run(audioStream);
23
- const outputStream = new Readable({
24
- read() { }
32
+ return decoderResponseToStream(response, {
33
+ inputPath,
34
+ inactivityTimeoutMs,
35
+ onSettled: () => setImmediate(() => void decoder.unload())
25
36
  });
26
- let hasReceivedData = false;
27
- let hasEnded = false;
28
- // Fallback timeout in case the decoder hangs without emitting any events
29
- const timeoutId = setTimeout(() => {
30
- if (!hasEnded) {
31
- hasEnded = true;
32
- const timeoutError = new Error(`Audio decoding timed out after ${DECODER_TIMEOUT_MS}ms for file: ${inputPath}`);
33
- outputStream.destroy(timeoutError);
34
- }
35
- }, DECODER_TIMEOUT_MS);
36
- response
37
- .onUpdate((output) => {
38
- hasReceivedData = true;
39
- const bytes = new Uint8Array(output.outputArray);
40
- outputStream.push(Buffer.from(bytes));
41
- })
42
- .onFinish(() => {
43
- if (!hasEnded) {
44
- hasEnded = true;
45
- clearTimeout(timeoutId);
46
- setImmediate(() => void decoder.unload());
47
- if (!hasReceivedData) {
48
- outputStream.destroy(new Error(`No audio data decoded from file: ${inputPath}`));
49
- }
50
- else {
51
- outputStream.push(null);
52
- }
53
- }
54
- })
55
- .onError((error) => {
56
- if (!hasEnded) {
57
- hasEnded = true;
58
- clearTimeout(timeoutId);
59
- setImmediate(() => void decoder.unload());
60
- outputStream.destroy(error);
61
- }
62
- });
63
- response.await().catch((error) => {
64
- if (!hasEnded) {
65
- hasEnded = true;
66
- clearTimeout(timeoutId);
67
- setImmediate(() => void decoder.unload());
68
- outputStream.destroy(error instanceof Error ? error : new Error(String(error)));
69
- }
70
- });
71
- return outputStream;
72
37
  }
73
38
  catch (error) {
74
39
  await decoder.unload();
@@ -76,3 +41,68 @@ export async function decodeAudioToStream(inputPath, audioFormat = 's16le') {
76
41
  throw error;
77
42
  }
78
43
  }
44
+ /**
45
+ * Bridge a decoder response into a `Readable` of PCM chunks. The stream fails
46
+ * if the decoder emits nothing for `inactivityTimeoutMs`; the timer is re-armed
47
+ * on every chunk, so it bounds inactivity, not the total decode time.
48
+ */
49
+ export function decoderResponseToStream(response, options) {
50
+ const { inputPath, inactivityTimeoutMs = DECODER_INACTIVITY_TIMEOUT_MS, onSettled } = options;
51
+ const outputStream = new Readable({
52
+ read() { }
53
+ });
54
+ let hasReceivedData = false;
55
+ let hasEnded = false;
56
+ let timeoutId;
57
+ const settle = () => {
58
+ if (hasEnded)
59
+ return false;
60
+ hasEnded = true;
61
+ if (timeoutId !== undefined)
62
+ clearTimeout(timeoutId);
63
+ onSettled?.();
64
+ return true;
65
+ };
66
+ const armTimeout = () => {
67
+ if (timeoutId !== undefined)
68
+ clearTimeout(timeoutId);
69
+ timeoutId = setTimeout(() => {
70
+ if (!settle())
71
+ return;
72
+ outputStream.destroy(new Error(`Audio decoding produced no output for ${inactivityTimeoutMs}ms for file: ${inputPath}`));
73
+ }, inactivityTimeoutMs);
74
+ };
75
+ armTimeout();
76
+ outputStream.on('close', () => {
77
+ settle();
78
+ });
79
+ response
80
+ .onUpdate((output) => {
81
+ hasReceivedData = true;
82
+ if (!hasEnded)
83
+ armTimeout();
84
+ const bytes = new Uint8Array(output.outputArray);
85
+ outputStream.push(Buffer.from(bytes));
86
+ })
87
+ .onFinish(() => {
88
+ if (!settle())
89
+ return;
90
+ if (!hasReceivedData) {
91
+ outputStream.destroy(new Error(`No audio data decoded from file: ${inputPath}`));
92
+ }
93
+ else {
94
+ outputStream.push(null);
95
+ }
96
+ })
97
+ .onError((error) => {
98
+ if (!settle())
99
+ return;
100
+ outputStream.destroy(error);
101
+ });
102
+ response.await().catch((error) => {
103
+ if (!settle())
104
+ return;
105
+ outputStream.destroy(error instanceof Error ? error : new Error(String(error)));
106
+ });
107
+ return outputStream;
108
+ }
@@ -1,3 +1,6 @@
1
1
  import { type ModelTypeInput } from '../../schemas/index.js';
2
+ export declare function getModelParallel(config: {
3
+ parallel?: number | undefined;
4
+ }): number;
2
5
  export declare function transformConfigForReload(modelType: ModelTypeInput, config: unknown): unknown;
3
6
  //# sourceMappingURL=config-transform.d.ts.map
@@ -1,5 +1,11 @@
1
1
  import { normalizeModelType, ModelType } from '../../schemas/index.js';
2
2
  import { buildParakeetReloadConfig, buildWhisperReloadConfig } from '../../server/bare/plugins/asr-ggml/config.js';
3
+ // The model's concurrent sequence slots. Missing / 0 / NaN all mean single-slot;
4
+ // floor a fractional value so it can't over-admit.
5
+ export function getModelParallel(config) {
6
+ const n = Math.floor(Number(config.parallel));
7
+ return Number.isFinite(n) && n >= 1 ? n : 1;
8
+ }
3
9
  export function transformConfigForReload(modelType, config) {
4
10
  const canonicalType = normalizeModelType(modelType);
5
11
  switch (canonicalType) {
@@ -4,7 +4,6 @@ export * from './cache/auto-history.js';
4
4
  export * from './cache/paths.js';
5
5
  export * from './checksum.js';
6
6
  export * from './config-transform.js';
7
- export * from './expand-gguf-shards.js';
8
7
  export * from './formatting.js';
9
8
  export * from './gguf-tensor-extractor.js';
10
9
  export * from './path-security.js';
@@ -4,7 +4,6 @@ export * from './cache/auto-history.js';
4
4
  export * from './cache/paths.js';
5
5
  export * from './checksum.js';
6
6
  export * from './config-transform.js';
7
- export * from './expand-gguf-shards.js';
8
7
  export * from './formatting.js';
9
8
  export * from './gguf-tensor-extractor.js';
10
9
  export * from './path-security.js';
@@ -3,6 +3,7 @@ import type { ShardPatternInfo, ShardUrl } from '../../schemas/index.js';
3
3
  * Detect if model filename follows shard pattern (00001-of-0000x)
4
4
  */
5
5
  export declare function detectShardedModel(filename: string): ShardPatternInfo;
6
+ export declare function getFirstShardPath(modelPath: string): string;
6
7
  /**
7
8
  * Generate list of shard filenames for a sharded model
8
9
  * Accepts any shard in the group
@@ -20,6 +20,20 @@ export function detectShardedModel(filename) {
20
20
  }
21
21
  return { isSharded: false };
22
22
  }
23
+ export function getFirstShardPath(modelPath) {
24
+ const lastSeparator = Math.max(modelPath.lastIndexOf('/'), modelPath.lastIndexOf('\\'));
25
+ const directory = lastSeparator >= 0 ? modelPath.slice(0, lastSeparator + 1) : '';
26
+ const filename = lastSeparator >= 0 ? modelPath.slice(lastSeparator + 1) : modelPath;
27
+ const shardInfo = detectShardedModel(filename);
28
+ if (!shardInfo.isSharded ||
29
+ !shardInfo.baseFilename ||
30
+ !shardInfo.totalShards ||
31
+ !shardInfo.extension) {
32
+ return modelPath;
33
+ }
34
+ const totalShards = shardInfo.totalShards.toString().padStart(5, '0');
35
+ return `${directory}${shardInfo.baseFilename}-00001-of-${totalShards}${shardInfo.extension}`;
36
+ }
23
37
  /**
24
38
  * Generate list of shard filenames for a sharded model
25
39
  * Accepts any shard in the group
@@ -7,18 +7,11 @@ interface HistoryMessage {
7
7
  }[] | undefined;
8
8
  }
9
9
  /**
10
- * Static tools mode: prepend tools right after the system message (or at the
11
- * very start when no system message is present). The tool block stays in the
12
- * kv-cache for the whole chat session.
10
+ * Prepend tools right after the system message (or at the very start when no
11
+ * system message is present). The tool block stays in the kv-cache for the
12
+ * whole chat session.
13
13
  */
14
14
  export declare function prependToolsToHistory(history: HistoryMessage[], tools: Tool[]): Array<HistoryMessage | Tool>;
15
- /**
16
- * Dynamic tools mode: append tools after the last history message. The
17
- * addon's compact-tools mode anchors the block after the last user message
18
- * and trims it from the kv-cache once the tool-call chain resolves, so a
19
- * subsequent turn can ship a different tool set without poisoning the cache.
20
- */
21
- export declare function appendToolsToHistory(history: HistoryMessage[], tools: Tool[]): Array<HistoryMessage | Tool>;
22
15
  export declare function detectToolDialect(modelId: string): ToolDialect;
23
16
  export {};
24
17
  //# sourceMappingURL=tool-integration.d.ts.map
@@ -1,9 +1,9 @@
1
1
  import { detectToolDialectFromName } from '../../server/utils/tools/index.js';
2
2
  import { getModelInfo } from '../../server/bare/registry/model-registry.js';
3
3
  /**
4
- * Static tools mode: prepend tools right after the system message (or at the
5
- * very start when no system message is present). The tool block stays in the
6
- * kv-cache for the whole chat session.
4
+ * Prepend tools right after the system message (or at the very start when no
5
+ * system message is present). The tool block stays in the kv-cache for the
6
+ * whole chat session.
7
7
  */
8
8
  export function prependToolsToHistory(history, tools) {
9
9
  const systemMsgIndex = history.findIndex((msg) => msg.role === 'system');
@@ -12,15 +12,6 @@ export function prependToolsToHistory(history, tools) {
12
12
  }
13
13
  return [...tools, ...history];
14
14
  }
15
- /**
16
- * Dynamic tools mode: append tools after the last history message. The
17
- * addon's compact-tools mode anchors the block after the last user message
18
- * and trims it from the kv-cache once the tool-call chain resolves, so a
19
- * subsequent turn can ship a different tool set without poisoning the cache.
20
- */
21
- export function appendToolsToHistory(history, tools) {
22
- return [...history, ...tools];
23
- }
24
15
  export function detectToolDialect(modelId) {
25
16
  const info = getModelInfo(modelId);
26
17
  if (!info)
@@ -209,6 +209,9 @@ export declare class ImageFileNotFoundError extends QvacErrorBase {
209
209
  export declare class InvalidImageInputError extends QvacErrorBase {
210
210
  constructor(cause?: unknown);
211
211
  }
212
+ export declare class InvalidAudioInputError extends QvacErrorBase {
213
+ constructor(details?: string, cause?: unknown);
214
+ }
212
215
  export declare class RAGSaveFailedError extends QvacErrorBase {
213
216
  constructor(details?: string, cause?: unknown);
214
217
  }
@@ -318,6 +318,11 @@ export class InvalidImageInputError extends QvacErrorBase {
318
318
  super(createErrorOptions(SDK_SERVER_ERROR_CODES.INVALID_IMAGE_INPUT, undefined, cause));
319
319
  }
320
320
  }
321
+ export class InvalidAudioInputError extends QvacErrorBase {
322
+ constructor(details, cause) {
323
+ super(createErrorOptions(SDK_SERVER_ERROR_CODES.INVALID_AUDIO_INPUT, details ? [details] : undefined, cause));
324
+ }
325
+ }
321
326
  // ============== RAG Operation Errors ==============
322
327
  export class RAGSaveFailedError extends QvacErrorBase {
323
328
  constructor(details, cause) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qvac/sdk",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -192,6 +192,7 @@
192
192
  "postcompile:aliases": "tsc-alias -p tsconfig.alias.json",
193
193
  "build": "rm -rf dist && npm run lint && tsc --project tsconfig.json && npm run postcompile:aliases",
194
194
  "build:pack": "bun run build && bun pm pack --destination dist/",
195
+ "sdk-source:workspace": "bun run ./scripts/link-workspace-inference.ts",
195
196
  "watch": "nodemon --watch server --ext ts --exec 'bun run build'",
196
197
  "bare:ensureprebuilds:llamacpp": "cp -r node_modules/@qvac/llm-llamacpp/prebuilds/android-arm64 node_modules/@qvac/llm-llamacpp/prebuilds/android-ia32 && cp -r node_modules/@qvac/llm-llamacpp/prebuilds/android-arm64 node_modules/@qvac/llm-llamacpp/prebuilds/android-arm && cp -r node_modules/@qvac/llm-llamacpp/prebuilds/android-arm64 node_modules/@qvac/llm-llamacpp/prebuilds/android-x64",
197
198
  "bare:ensureprebuilds:asr": "cp -r node_modules/@qvac/asr-ggml/prebuilds/android-arm64 node_modules/@qvac/asr-ggml/prebuilds/android-ia32 && cp -r node_modules/@qvac/asr-ggml/prebuilds/android-arm64 node_modules/@qvac/asr-ggml/prebuilds/android-arm && cp -r node_modules/@qvac/asr-ggml/prebuilds/android-arm64 node_modules/@qvac/asr-ggml/prebuilds/android-x64",
@@ -202,29 +203,26 @@
202
203
  "contract:export": "tsx ./scripts/export-contract.ts",
203
204
  "contract:check": "tsx ./scripts/export-contract.ts --check",
204
205
  "check:resource-collectors": "tsx ./scripts/check-resource-collectors-packaging.ts",
205
- "check-models": "tsx ./models/update-models/index.ts --check",
206
- "check-models:hook": "tsx ./models/update-models/index.ts --check --non-blocking",
207
- "update-models": "tsx ./models/update-models/index.ts && bun run contract:export",
208
206
  "changelog:generate": "node ../../scripts/sdk/generate-changelog-sdk-pod.cjs --package=sdk && prettier --write changelog"
209
207
  },
210
208
  "dependencies": {
211
- "@qvac/audiogen-ggml": "^0.1.0",
212
- "@qvac/asr-ggml": "^0.1.0",
213
- "@qvac/bci-whispercpp": "^0.6.0",
214
- "@qvac/classification-ggml": "^0.15.1",
209
+ "@qvac/audiogen-ggml": "^0.2.1",
210
+ "@qvac/asr-ggml": "^0.3.0",
211
+ "@qvac/bci-whispercpp": "^0.7.1",
212
+ "@qvac/classification-ggml": "^0.20.0",
215
213
  "@qvac/decoder-audio": "^0.5.0",
216
214
  "@qvac/diffusion-cpp": "^0.17.0",
217
- "@qvac/embed-llamacpp": "^0.30.1",
215
+ "@qvac/embed-llamacpp": "^0.34.0",
218
216
  "@qvac/error": "^0.1.1",
219
217
  "@qvac/langdetect-text": "^0.1.2",
220
- "@qvac/llm-llamacpp": "^0.39.3",
218
+ "@qvac/llm-llamacpp": "^0.45.0",
221
219
  "@qvac/logging": "^0.1.0",
222
- "@qvac/ocr-ggml": "^0.13.1",
220
+ "@qvac/ocr-ggml": "^0.18.0",
223
221
  "@qvac/rag": "^0.6.4",
224
222
  "@qvac/registry-client": "^0.6.1",
225
- "@qvac/translation-nmtcpp": "^8.3.0",
226
- "@qvac/tts-ggml": "^0.6.0",
227
- "@qvac/vla-ggml": "^0.16.2",
223
+ "@qvac/translation-nmtcpp": "^0.10.0",
224
+ "@qvac/tts-ggml": "^0.7.4",
225
+ "@qvac/vla-ggml": "^0.21.1",
228
226
  "bare-abort-controller": "^1.0.0",
229
227
  "bare-cpu-info": "0.1.1",
230
228
  "bare-crypto": "^1.15.0",
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=llamacpp-dynamic-tools.d.ts.map