@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
@@ -37,6 +37,7 @@ interface CacheCommitContext {
37
37
  producedTokens: boolean;
38
38
  generatedTokens?: number | undefined;
39
39
  predict?: number | undefined;
40
+ stoppedAtContextBoundary: boolean;
40
41
  }
41
42
  export declare function shouldCommitCachedTurn(context: CacheCommitContext): boolean;
42
43
  /**
@@ -15,12 +15,12 @@
15
15
  * slice-decision helper remains here.
16
16
  */
17
17
  export function shouldCommitCachedTurn(context) {
18
- const { aborted, producedTokens, generatedTokens, predict } = context;
18
+ const { aborted, producedTokens, generatedTokens, predict, stoppedAtContextBoundary } = context;
19
19
  const stoppedByBudget = predict !== undefined &&
20
20
  predict > 0 &&
21
21
  generatedTokens !== undefined &&
22
22
  generatedTokens >= predict;
23
- return !aborted && producedTokens && !stoppedByBudget;
23
+ return !aborted && producedTokens && !stoppedByBudget && !stoppedAtContextBoundary;
24
24
  }
25
25
  /**
26
26
  * Pure slice decision for `prepareMessagesForCache`.
@@ -28,10 +28,6 @@ export declare const llmPlugin: {
28
28
  n_discarded: import("zod").ZodOptional<import("zod").ZodNumber>;
29
29
  parallel: import("zod").ZodOptional<import("zod").ZodNumber>;
30
30
  tools: import("zod").ZodOptional<import("zod").ZodBoolean>;
31
- toolsMode: import("zod").ZodOptional<import("zod").ZodEnum<{
32
- static: "static";
33
- dynamic: "dynamic";
34
- }>>;
35
31
  'cache-type-k': import("zod").ZodOptional<import("zod").ZodString>;
36
32
  'cache-type-v': import("zod").ZodOptional<import("zod").ZodString>;
37
33
  'main-gpu': import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodNumber, import("zod").ZodEnum<{
@@ -90,6 +86,10 @@ export declare const llmPlugin: {
90
86
  batched: "batched";
91
87
  sequential: "sequential";
92
88
  }>>;
89
+ image_no_upscale: import("zod").ZodOptional<import("zod").ZodEnum<{
90
+ on: "on";
91
+ off: "off";
92
+ }>>;
93
93
  'mmproj-use-gpu': import("zod").ZodOptional<import("zod").ZodBoolean>;
94
94
  }, import("zod/v4/core").$strip>;
95
95
  resolveConfig(cfg: LlmConfigInput, ctx: ResolveContext): Promise<{
@@ -113,7 +113,6 @@ export declare const llmPlugin: {
113
113
  n_discarded?: number | undefined;
114
114
  parallel?: number | undefined;
115
115
  tools?: boolean | undefined;
116
- toolsMode?: "static" | "dynamic" | undefined;
117
116
  'cache-type-k'?: string | undefined;
118
117
  'cache-type-v'?: string | undefined;
119
118
  'main-gpu'?: number | "integrated" | "dedicated" | undefined;
@@ -122,6 +121,7 @@ export declare const llmPlugin: {
122
121
  openclCacheDir?: string | undefined;
123
122
  reasoning_budget?: number | undefined;
124
123
  image_tile_mode?: "disabled" | "batched" | "sequential" | undefined;
124
+ image_no_upscale?: "on" | "off" | undefined;
125
125
  'mmproj-use-gpu'?: boolean | undefined;
126
126
  };
127
127
  artifacts?: never;
@@ -146,7 +146,6 @@ export declare const llmPlugin: {
146
146
  n_discarded?: number | undefined;
147
147
  parallel?: number | undefined;
148
148
  tools?: boolean | undefined;
149
- toolsMode?: "static" | "dynamic" | undefined;
150
149
  'cache-type-k'?: string | undefined;
151
150
  'cache-type-v'?: string | undefined;
152
151
  'main-gpu'?: number | "integrated" | "dedicated" | undefined;
@@ -155,6 +154,7 @@ export declare const llmPlugin: {
155
154
  openclCacheDir?: string | undefined;
156
155
  reasoning_budget?: number | undefined;
157
156
  image_tile_mode?: "disabled" | "batched" | "sequential" | undefined;
157
+ image_no_upscale?: "on" | "off" | undefined;
158
158
  'mmproj-use-gpu'?: boolean | undefined;
159
159
  };
160
160
  artifacts: {
@@ -54,7 +54,7 @@ import LlmLlamacpp from '@qvac/llm-llamacpp';
54
54
  import llmAddonLogging from '@qvac/llm-llamacpp/addonLogging';
55
55
  import { definePlugin, defineHandler, finetuneRequestSchema, batchCompletionStreamRequestSchema, batchCompletionStreamResponseSchema, completionStreamRequestSchema, completionStreamResponseSchema, finetuneResponseSchema, translateRequestSchema, translateResponseSchema, ModelType, llmConfigBaseSchema, ADDON_LLM } from '../../../../schemas/index.js';
56
56
  import { createStreamLogger, registerAddonLogger } from '../../../../logging/index.js';
57
- import { expandGGUFIntoShards } from '../../../../server/utils/index.js';
57
+ import { getFirstShardPath, getModelParallel } from '../../../../server/utils/index.js';
58
58
  import { completion } from '../../../../server/bare/plugins/llamacpp-completion/ops/completion-stream.js';
59
59
  import { batchCompletion } from '../../../../server/bare/plugins/llamacpp-completion/ops/batch-completion-stream.js';
60
60
  import { finetune } from '../../../../server/bare/plugins/llamacpp-completion/ops/finetune.js';
@@ -69,7 +69,7 @@ import { generateServerRequestId } from '../../../../server/bare/runtime/request
69
69
  import { getServerLogger } from '../../../../logging/index.js';
70
70
  import { ContextOverflowError } from '../../../../utils/errors-server.js';
71
71
  import { isAddonContextOverflowError, parseContextOverflowMessage } from '../../../../server/bare/plugins/llamacpp-completion/ops/context-overflow.js';
72
- import { isAddonCancelledError } from '../../../../server/bare/plugins/llamacpp-completion/ops/batch-cancelled.js';
72
+ import { stoppedByLength } from '../../../../server/bare/plugins/llamacpp-completion/ops/completion-stats.js';
73
73
  import { isMobile } from '../../../../server/bare/registry/runtime-context-registry.js';
74
74
  import { stripMultiGpuKeys } from '../../../../server/utils/multi-gpu-mobile.js';
75
75
  function createLlmModel(modelId, modelPath, llmConfig, projectionModelPath) {
@@ -82,15 +82,17 @@ function createLlmModel(modelId, modelPath, llmConfig, projectionModelPath) {
82
82
  getServerLogger().warn(`[${ModelType.llamacppCompletion}:${modelId}] Multi-GPU parameters (${stripped.join(', ')}) are not supported on mobile (single-GPU device) — removing from config; model will load with single-GPU defaults`);
83
83
  }
84
84
  }
85
- const modelFiles = expandGGUFIntoShards(modelPath);
85
+ // Addon overflow flag: at a single slot the addon throws when driven over
86
+ // capacity, at parallel>1 it queues the surplus. Not on the SDK surface.
87
+ const rejectWhenBusy = getModelParallel(llmConfig) <= 1;
86
88
  const model = new LlmLlamacpp({
87
89
  files: {
88
- model: modelFiles,
90
+ model: [getFirstShardPath(modelPath)],
89
91
  ...(projectionModelPath && { projectionModel: projectionModelPath })
90
92
  },
91
93
  config: llmConfigStrings,
92
94
  logger,
93
- opts: { stats: true }
95
+ opts: { stats: true, rejectWhenBusy }
94
96
  });
95
97
  return { model };
96
98
  }
@@ -136,7 +138,8 @@ export const llmPlugin = definePlugin({
136
138
  requestSchema: batchCompletionStreamRequestSchema,
137
139
  responseSchema: batchCompletionStreamResponseSchema,
138
140
  streaming: true,
139
- cancel: { scope: 'model', hard: true },
141
+ // Request-scope: a model-wide cancel would kill concurrent batching peers.
142
+ cancel: { scope: 'request', hard: true },
140
143
  handler: async function* (request) {
141
144
  const env_1 = { stack: [], error: void 0, hasError: false };
142
145
  try {
@@ -162,10 +165,14 @@ export const llmPlugin = definePlugin({
162
165
  }
163
166
  return normalizer;
164
167
  }
168
+ // Batches share the completion lane at the `parallel` cap. The cap counts
169
+ // requests, not the prompts inside a batch (the addon schedules those).
170
+ const parallel = getModelParallel(modelCfg);
165
171
  const ctx = __addDisposableResource(env_1, await getRequestRegistry().begin({
166
172
  requestId: request.requestId ?? generateServerRequestId(),
167
173
  kind: 'batchCompletion',
168
- modelId: request.modelId
174
+ modelId: request.modelId,
175
+ maxConcurrentPerModel: parallel
169
176
  }), true);
170
177
  const requestLogger = withRequestContext(getServerLogger(), ctx);
171
178
  if (Boolean(ctx.signal.aborted)) {
@@ -238,14 +245,11 @@ export const llmPlugin = definePlugin({
238
245
  const { promptTokens, ctxSize } = parseContextOverflowMessage(err instanceof Error ? err.message : '');
239
246
  throw new ContextOverflowError(promptTokens, ctxSize, request.modelId, err);
240
247
  }
241
- // Cancelling a batch that still has prompts queued behind the
242
- // `parallel` slot limit fails the whole native batch group with
243
- // a `Cancelled` error (the addon rejects `run()` rather than
244
- // resolving with partial outputs — see continuous-batching docs).
245
- // Ride the same graceful "done" path the non-overflow cancel
246
- // uses: emit cancelled terminals per id so the client surfaces
247
- // `InferenceCancelledError`, not a generic `CompletionFailedError`.
248
- if (ctx.signal.aborted && isAddonCancelledError(err)) {
248
+ // Once the registry accepts cancellation, the request signal owns the
249
+ // terminal outcome. The addon may reject with different shapes
250
+ // depending on whether native sequences were active or queued; those
251
+ // transport details must not change the SDK cancellation contract.
252
+ if (ctx.signal.aborted) {
249
253
  const cancelledIds = ids.length > 0
250
254
  ? ids
251
255
  : request.prompts.map((prompt, index) => prompt.id ?? String(index));
@@ -281,7 +285,8 @@ export const llmPlugin = definePlugin({
281
285
  requestSchema: completionStreamRequestSchema,
282
286
  responseSchema: completionStreamResponseSchema,
283
287
  streaming: true,
284
- cancel: { scope: 'model', hard: true },
288
+ // Request-scope: a model-wide cancel would kill concurrent batching peers.
289
+ cancel: { scope: 'request', hard: true },
285
290
  handler: async function* (request) {
286
291
  const env_2 = { stack: [], error: void 0, hasError: false };
287
292
  try {
@@ -312,10 +317,14 @@ export const llmPlugin = definePlugin({
312
317
  // client can target this run with `cancel({ requestId })`.
313
318
  // Falls back to a server-generated id if the client (e.g. an
314
319
  // older release) didn't send one.
320
+ // Admit up to `parallel` jobs. Disk-KV-cache turns share this lane too;
321
+ // same-file writes serialise per cache path inside the KV-cache session.
322
+ const parallel = getModelParallel(modelCfg);
315
323
  const ctx = __addDisposableResource(env_2, await getRequestRegistry().begin({
316
324
  requestId: request.requestId ?? generateServerRequestId(),
317
325
  kind: 'completion',
318
- modelId: request.modelId
326
+ modelId: request.modelId,
327
+ maxConcurrentPerModel: parallel
319
328
  }), true);
320
329
  const requestLogger = withRequestContext(getServerLogger(), ctx);
321
330
  // begin() can return already-aborted when the client cancels while
@@ -361,26 +370,26 @@ export const llmPlugin = definePlugin({
361
370
  }
362
371
  result = await stream.next();
363
372
  }
364
- const { modelExecutionMs, stats, toolCalls } = result.value;
373
+ const { modelExecutionMs, stats, toolCalls, stoppedAtContextBoundary } = result.value;
365
374
  // Cancellation rides the done path: observable via the last event's
366
375
  // stopReason; client aggregates reject with InferenceCancelledError.
367
376
  const cancelled = ctx.signal.aborted;
368
377
  // EOS tokens are not decoded by llama_decode, so n_eval (and
369
378
  // therefore stats.generatedTokens) counts only real decode calls.
370
- // When generatedTokens >= effectivePredict the run exhausted its
371
- // token budget without hitting EOS — emit stopReason "length".
372
- // -1 (unlimited) and -2 (context fill) must never trigger this.
379
+ // Positive prediction-budget exhaustion and a full context window
380
+ // both map to stopReason "length"; cancellation takes precedence.
373
381
  const effectivePredict = request.generationParams?.predict ?? modelCfg.predict;
374
- const stoppedByBudget = !cancelled &&
375
- effectivePredict !== undefined &&
376
- effectivePredict > 0 &&
377
- stats?.generatedTokens !== undefined &&
378
- stats.generatedTokens >= effectivePredict;
382
+ const lengthStop = stoppedByLength({
383
+ cancelled,
384
+ effectivePredict,
385
+ generatedTokens: stats?.generatedTokens,
386
+ stoppedAtContextBoundary
387
+ });
379
388
  const terminalEvents = normalizer.finish({
380
389
  ...(stats && { stats }),
381
390
  ...(toolCalls.length > 0 && { toolCalls }),
382
391
  ...(cancelled && { stopReason: 'cancelled' }),
383
- ...(stoppedByBudget && { stopReason: 'length' })
392
+ ...(lengthStop && { stopReason: 'length' })
384
393
  });
385
394
  if (!request.stream) {
386
395
  batchedEvents.push(...terminalEvents);
@@ -393,18 +402,27 @@ export const llmPlugin = definePlugin({
393
402
  }, modelExecutionMs);
394
403
  }
395
404
  catch (err) {
396
- // The llama.cpp addon emits a structured `ContextOverflow` status
397
- // (LlmErrors.hpp::ContextOverflow = 14) when the prompt exceeds
398
- // the model's `ctx_size`. Bare's `js_throw_error(env, code, msg)`
399
- // surfaces it as a JS Error with `.code = "[ <addonId> :: ContextOverflow ]"`
400
- // and `.message` carrying the C++-formatted detail. Rethrow as
401
- // a typed `ContextOverflowError` so consumers can switch on the
402
- // class (and `err.code === SDK_SERVER_ERROR_CODES.CONTEXT_OVERFLOW`)
403
- // instead of substring-matching on the raw addon message.
405
+ // Classify a structured ContextOverflow first, even under an aborted
406
+ // signal, so a real overflow racing a cancel surfaces as a typed
407
+ // ContextOverflowError.
404
408
  if (isAddonContextOverflowError(err)) {
405
409
  const { promptTokens, ctxSize } = parseContextOverflowMessage(err instanceof Error ? err.message : '');
406
410
  throw new ContextOverflowError(promptTokens, ctxSize, request.modelId, err);
407
411
  }
412
+ // Context overflow is classified above. For every other error after
413
+ // accepted cancellation, the request signal owns the terminal outcome
414
+ // regardless of the addon's active/queued error shape.
415
+ if (ctx.signal.aborted) {
416
+ yield attachModelExecutionMs({
417
+ type: 'completionStream',
418
+ done: true,
419
+ events: normalizer.finish({ stopReason: 'cancelled' })
420
+ },
421
+ // The error path has no model execution time to report; omit it
422
+ // rather than claim 0.
423
+ undefined);
424
+ return;
425
+ }
408
426
  throw err;
409
427
  }
410
428
  finally {
@@ -442,7 +460,8 @@ export const llmPlugin = definePlugin({
442
460
  requestSchema: translateRequestSchema,
443
461
  responseSchema: translateResponseSchema,
444
462
  streaming: true,
445
- cancel: { scope: 'model', hard: true },
463
+ // LLM translate cancels its own run (request-scoped), like completion.
464
+ cancel: { scope: 'request', hard: true },
446
465
  handler: async function* (request) {
447
466
  const stream = translate(request, request.requestId);
448
467
  try {
@@ -1,4 +1,4 @@
1
- import { TOOLS_MODE } from '../../../../schemas/index.js';
1
+ import {} from '../../../../schemas/index.js';
2
2
  /**
3
3
  * Converts an LlmConfig into the flat string-keyed map the C++ addon expects.
4
4
  *
@@ -24,11 +24,5 @@ export function transformLlmConfig(llmConfig) {
24
24
  transformed['openclCacheDir'] = transformed['opencl_cache_dir'];
25
25
  delete transformed['opencl_cache_dir'];
26
26
  }
27
- if ('tools_mode' in transformed) {
28
- if (transformed['tools_mode'] === TOOLS_MODE.dynamic) {
29
- transformed['tools_compact'] = 'true';
30
- }
31
- delete transformed['tools_mode'];
32
- }
33
27
  return transformed;
34
28
  }
@@ -2,7 +2,7 @@ import EmbedLlamacpp, {} from '@qvac/embed-llamacpp';
2
2
  import embedAddonLogging from '@qvac/embed-llamacpp/addonLogging';
3
3
  import { definePlugin, defineHandler, embedRequestSchema, embedResponseSchema, ModelType, embedConfigBaseSchema, ADDON_EMBEDDING } from '../../../../schemas/index.js';
4
4
  import { createStreamLogger, registerAddonLogger, getServerLogger } from '../../../../logging/index.js';
5
- import { expandGGUFIntoShards } from '../../../../server/utils/index.js';
5
+ import { getFirstShardPath } from '../../../../server/utils/index.js';
6
6
  import { embed } from '../../../../server/bare/ops/embed.js';
7
7
  import { forwardModelExecution } from '../../../../profiling/model-execution.js';
8
8
  import { isMobile } from '../../../../server/bare/registry/runtime-context-registry.js';
@@ -53,9 +53,8 @@ function createEmbeddingsModel(modelId, modelPath, embedConfig) {
53
53
  getServerLogger().warn(`[${ModelType.llamacppEmbedding}:${modelId}] Multi-GPU parameters (${stripped.join(', ')}) are not supported on mobile (single-GPU device) — removing from config; model will load with single-GPU defaults`);
54
54
  }
55
55
  }
56
- const modelFiles = expandGGUFIntoShards(modelPath);
57
56
  const model = new EmbedLlamacpp({
58
- files: { model: modelFiles },
57
+ files: { model: [getFirstShardPath(modelPath)] },
59
58
  config,
60
59
  logger,
61
60
  opts: { stats: true }
@@ -31,7 +31,7 @@ function deriveColocatedBergamotVocabPaths(modelPath) {
31
31
  function createNmtModel(modelId, modelPath, nmtConfig, srcVocabPath, dstVocabPath, pivotModelPath, pivotSrcVocabPath, pivotDstVocabPath) {
32
32
  const logger = createStreamLogger(modelId, ModelType.nmtcppTranslation);
33
33
  registerAddonLogger(modelId, ModelType.nmtcppTranslation, logger);
34
- const { mode, from, to, engine, beamsize, lengthpenalty, maxlength, repetitionpenalty, norepeatngramsize, temperature, topk, topp } = nmtConfig;
34
+ const { from, to, engine, beamsize, lengthpenalty, maxlength, repetitionpenalty, norepeatngramsize, temperature, topk, topp } = nmtConfig;
35
35
  const files = {
36
36
  model: modelPath,
37
37
  ...(srcVocabPath && { srcVocab: srcVocabPath }),
@@ -69,7 +69,7 @@ function createNmtModel(modelId, modelPath, nmtConfig, srcVocabPath, dstVocabPat
69
69
  };
70
70
  const model = new TranslationNmtcpp({
71
71
  files,
72
- params: { mode, srcLang: from, dstLang: to },
72
+ params: { srcLang: from, dstLang: to },
73
73
  config,
74
74
  logger,
75
75
  opts: { stats: true }
@@ -107,9 +107,10 @@ export const nmtPlugin = definePlugin({
107
107
  requestSchema: translateRequestSchema,
108
108
  responseSchema: translateResponseSchema,
109
109
  streaming: true,
110
- // nmtcpp does not expose a cancel surface today — SDK falls
111
- // back to soft-cancel (stop yielding, drop result, skip
112
- // post-processing; the C++ work runs to completion).
110
+ // nmtcpp 0.7.0 exposes response.cancel(), but the translate op is not
111
+ // wired for addon-level cancellation yet — SDK keeps soft-cancel (stop
112
+ // yielding, drop result, skip post-processing; the C++ work may run to
113
+ // completion).
113
114
  cancel: { scope: 'none' },
114
115
  handler: async function* (request) {
115
116
  const stream = translate(request, request.requestId);
@@ -100,26 +100,37 @@ export const parakeetPlugin = definePlugin({
100
100
  })
101
101
  };
102
102
  const iterator = transcribeStream(request.modelId, inputStream, undefined, false, streamOpts, request.requestId);
103
- for await (const value of iterator) {
104
- if (typeof value === 'object' && value !== null && 'type' in value) {
105
- if (value.type === 'endOfTurn') {
106
- yield {
107
- type: 'transcribeStream',
108
- endOfTurn: { source: 'parakeet' }
109
- };
103
+ try {
104
+ let result = await iterator.next();
105
+ while (!result.done) {
106
+ const value = result.value;
107
+ if (typeof value === 'object' && value !== null && 'type' in value) {
108
+ if (value.type === 'endOfTurn') {
109
+ yield {
110
+ type: 'transcribeStream',
111
+ endOfTurn: { source: 'parakeet' }
112
+ };
113
+ }
114
+ result = await iterator.next();
115
+ continue;
110
116
  }
111
- continue;
117
+ yield {
118
+ type: 'transcribeStream',
119
+ text: value
120
+ };
121
+ result = await iterator.next();
112
122
  }
113
- yield {
123
+ const { modelExecutionMs, stats } = result.value;
124
+ yield attachModelExecutionMs({
114
125
  type: 'transcribeStream',
115
- text: value
116
- };
126
+ text: '',
127
+ done: true,
128
+ ...(stats && { stats })
129
+ }, modelExecutionMs);
130
+ }
131
+ finally {
132
+ await iterator.return?.(undefined);
117
133
  }
118
- yield {
119
- type: 'transcribeStream',
120
- text: '',
121
- done: true
122
- };
123
134
  }
124
135
  })
125
136
  },
@@ -21,10 +21,30 @@ export function getParlerJobOptions(request) {
21
21
  ...(request.quality !== undefined ? { quality: request.quality } : {})
22
22
  };
23
23
  }
24
+ // Per-call conditioning keys shared by the engines that support them.
25
+ // CosyVoice3 accepts the cross-engine emotion/pace pair; everything else in
26
+ // the description/template surface remains Parler-only.
27
+ const CROSS_ENGINE_CONDITIONING_KEYS = ['emotion', 'pace'];
24
28
  export function assertParlerJobOptionsSupported(model, options, handlerName) {
25
- if (Object.keys(options).length === 0)
29
+ const setKeys = Object.keys(options);
30
+ if (setKeys.length === 0)
26
31
  return;
27
- if (hasEngineType(model) && model.getEngineType() === 'parler')
32
+ const engine = hasEngineType(model) ? model.getEngineType() : undefined;
33
+ if (engine === 'parler')
28
34
  return;
29
- throw new PluginRequestValidationFailedError(handlerName, 'description, voiceDescription, voice, emotion, pitch, pace, expressivity, noise, reverb, and quality are only supported by Parler TTS models');
35
+ if (engine === 'cosyvoice3') {
36
+ const parlerOnlyKeys = setKeys.filter((key) => !CROSS_ENGINE_CONDITIONING_KEYS.includes(key));
37
+ if (parlerOnlyKeys.length > 0) {
38
+ throw new PluginRequestValidationFailedError(handlerName, `${parlerOnlyKeys.join(', ')} ${parlerOnlyKeys.length === 1 ? 'is' : 'are'} only supported by Parler TTS models; CosyVoice3 supports emotion and pace`);
39
+ }
40
+ // CosyVoice3 is trained on one instruction per synthesis; a request that
41
+ // engages both channels always conflicts regardless of the loaded config.
42
+ if (options.emotion !== undefined &&
43
+ options.pace !== undefined &&
44
+ options.pace !== 'moderate') {
45
+ throw new PluginRequestValidationFailedError(handlerName, 'CosyVoice3 accepts one conditioning control per synthesis; set either emotion or a non-moderate pace, not both');
46
+ }
47
+ return;
48
+ }
49
+ throw new PluginRequestValidationFailedError(handlerName, 'description, voiceDescription, voice, emotion, pitch, pace, expressivity, noise, reverb, and quality are only supported by Parler TTS and CosyVoice3 models (CosyVoice3: emotion and pace only)');
30
50
  }