@qvac/sdk 0.10.1 → 0.11.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 (201) hide show
  1. package/NOTICE +92 -54
  2. package/dist/client/api/cancel.d.ts +24 -32
  3. package/dist/client/api/cancel.js +58 -31
  4. package/dist/client/api/client-request-id.d.ts +17 -0
  5. package/dist/client/api/client-request-id.js +27 -0
  6. package/dist/client/api/completion-stream.d.ts +1 -1
  7. package/dist/client/api/completion-stream.js +34 -3
  8. package/dist/client/api/diffusion.d.ts +46 -0
  9. package/dist/client/api/diffusion.js +50 -1
  10. package/dist/client/api/download-asset.d.ts +12 -2
  11. package/dist/client/api/download-asset.js +21 -3
  12. package/dist/client/api/embed.d.ts +8 -4
  13. package/dist/client/api/embed.js +14 -1
  14. package/dist/client/api/get-model-info.d.ts +1 -1
  15. package/dist/client/api/index.d.ts +1 -0
  16. package/dist/client/api/index.js +1 -0
  17. package/dist/client/api/load-model.d.ts +12 -4
  18. package/dist/client/api/load-model.js +25 -1
  19. package/dist/client/api/rag.d.ts +9 -3
  20. package/dist/client/api/rag.js +23 -3
  21. package/dist/client/api/transcribe.d.ts +20 -8
  22. package/dist/client/api/transcribe.js +58 -4
  23. package/dist/client/api/unload-model.d.ts +5 -2
  24. package/dist/client/api/unload-model.js +9 -4
  25. package/dist/client/api/upscale.d.ts +45 -0
  26. package/dist/client/api/upscale.js +99 -0
  27. package/dist/client/rpc/expo-rpc-client.js +2 -1
  28. package/dist/client/rpc/node-rpc-client.js +18 -7
  29. package/dist/client/rpc/rpc-client.d.ts +2 -2
  30. package/dist/client/rpc/rpc-client.js +10 -4
  31. package/dist/client/rpc/rpc-error.d.ts +9 -0
  32. package/dist/client/rpc/rpc-error.js +92 -0
  33. package/dist/examples/cancel-by-request-id.d.ts +38 -0
  34. package/dist/examples/cancel-by-request-id.js +103 -0
  35. package/dist/examples/diffusion-esrgan-upscale.d.ts +2 -0
  36. package/dist/examples/diffusion-esrgan-upscale.js +78 -0
  37. package/dist/examples/download-with-cancel.js +10 -10
  38. package/dist/examples/embed-multi-gpu.d.ts +2 -0
  39. package/dist/examples/embed-multi-gpu.js +46 -0
  40. package/dist/examples/llamacpp-multi-gpu.d.ts +2 -0
  41. package/dist/examples/llamacpp-multi-gpu.js +57 -0
  42. package/dist/examples/rag/rag-hyperdb/cancellation.js +20 -17
  43. package/dist/examples/tools/llamacpp-tools-gemma4.d.ts +2 -0
  44. package/dist/examples/tools/llamacpp-tools-gemma4.js +76 -0
  45. package/dist/examples/tools/llamacpp-tools-qwen35.d.ts +2 -0
  46. package/dist/examples/tools/llamacpp-tools-qwen35.js +70 -0
  47. package/dist/examples/transcription/whispercpp-microphone-conversation.d.ts +2 -0
  48. package/dist/examples/transcription/whispercpp-microphone-conversation.js +102 -0
  49. package/dist/examples/tts/supertonic.js +1 -1
  50. package/dist/expo/plugins/withMobileBundle.d.ts +20 -4
  51. package/dist/expo/plugins/withMobileBundle.js +44 -6
  52. package/dist/index.d.ts +5 -2
  53. package/dist/index.js +21 -1
  54. package/dist/models/registry/models.d.ts +186 -318
  55. package/dist/models/registry/models.js +1522 -1667
  56. package/dist/pear/pre.d.ts +3 -0
  57. package/dist/pear/pre.js +3 -0
  58. package/dist/schemas/cancel.d.ts +167 -28
  59. package/dist/schemas/cancel.js +119 -19
  60. package/dist/schemas/common.d.ts +427 -208
  61. package/dist/schemas/common.js +3 -1
  62. package/dist/schemas/completion-event.d.ts +14 -0
  63. package/dist/schemas/completion-event.js +11 -1
  64. package/dist/schemas/completion-stream.d.ts +15 -1
  65. package/dist/schemas/completion-stream.js +25 -7
  66. package/dist/schemas/download-asset.d.ts +15 -9
  67. package/dist/schemas/download-asset.js +17 -6
  68. package/dist/schemas/embed.d.ts +1 -0
  69. package/dist/schemas/embed.js +5 -0
  70. package/dist/schemas/error.d.ts +31 -0
  71. package/dist/schemas/error.js +29 -1
  72. package/dist/schemas/finetune.d.ts +2 -0
  73. package/dist/schemas/finetune.js +5 -0
  74. package/dist/schemas/get-model-info.d.ts +6 -6
  75. package/dist/schemas/llamacpp-config.d.ts +54 -8
  76. package/dist/schemas/llamacpp-config.js +12 -0
  77. package/dist/schemas/load-model.d.ts +1911 -1260
  78. package/dist/schemas/load-model.js +15 -0
  79. package/dist/schemas/model-src-utils.d.ts +14 -14
  80. package/dist/schemas/model-types.d.ts +10 -10
  81. package/dist/schemas/ocr.d.ts +3 -3
  82. package/dist/schemas/plugin.d.ts +40 -1
  83. package/dist/schemas/plugin.js +10 -1
  84. package/dist/schemas/rag.d.ts +9 -0
  85. package/dist/schemas/rag.js +17 -9
  86. package/dist/schemas/registry.d.ts +30 -30
  87. package/dist/schemas/reload-config.d.ts +14 -14
  88. package/dist/schemas/sdcpp-config.d.ts +131 -17
  89. package/dist/schemas/sdcpp-config.js +163 -1
  90. package/dist/schemas/sdk-config.d.ts +649 -271
  91. package/dist/schemas/sdk-errors-client.d.ts +1 -0
  92. package/dist/schemas/sdk-errors-client.js +5 -0
  93. package/dist/schemas/sdk-errors-server.d.ts +5 -0
  94. package/dist/schemas/sdk-errors-server.js +25 -0
  95. package/dist/schemas/text-to-speech.d.ts +78 -78
  96. package/dist/schemas/transcription-config.d.ts +24 -24
  97. package/dist/schemas/transcription.d.ts +56 -2
  98. package/dist/schemas/transcription.js +27 -0
  99. package/dist/schemas/translate.d.ts +2 -0
  100. package/dist/schemas/translate.js +13 -2
  101. package/dist/schemas/translation-config.d.ts +40 -40
  102. package/dist/schemas/unload-model.d.ts +2 -1
  103. package/dist/schemas/unload-model.js +4 -1
  104. package/dist/scripts/run-unit-tests.js +15 -2
  105. package/dist/server/bare/delegate-rpc-client.js +10 -8
  106. package/dist/server/bare/ops/cancel.d.ts +27 -1
  107. package/dist/server/bare/ops/cancel.js +36 -10
  108. package/dist/server/bare/ops/embed.d.ts +3 -3
  109. package/dist/server/bare/ops/embed.js +144 -28
  110. package/dist/server/bare/ops/kv-cache-utils.d.ts +0 -7
  111. package/dist/server/bare/ops/kv-cache-utils.js +8 -47
  112. package/dist/server/bare/ops/transcribe.d.ts +10 -5
  113. package/dist/server/bare/ops/transcribe.js +196 -41
  114. package/dist/server/bare/ops/translate.d.ts +1 -1
  115. package/dist/server/bare/ops/translate.js +197 -88
  116. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +12 -3
  117. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +170 -172
  118. package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +125 -21
  119. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.d.ts +154 -0
  120. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.js +448 -0
  121. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.d.ts +21 -73
  122. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.js +18 -101
  123. package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +30 -3
  124. package/dist/server/bare/plugins/llamacpp-completion/plugin.js +157 -85
  125. package/dist/server/bare/plugins/llamacpp-completion/transform.d.ts +10 -0
  126. package/dist/server/bare/plugins/llamacpp-completion/transform.js +34 -0
  127. package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +7 -0
  128. package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +10 -1
  129. package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +17 -15
  130. package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +5 -1
  131. package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +3 -3
  132. package/dist/server/bare/plugins/onnx-ocr/plugin.js +3 -0
  133. package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +39 -39
  134. package/dist/server/bare/plugins/onnx-tts/plugin.js +6 -0
  135. package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +29 -21
  136. package/dist/server/bare/plugins/parakeet-transcription/plugin.js +4 -1
  137. package/dist/server/bare/plugins/sdcpp-generation/ops/diffusion.js +8 -0
  138. package/dist/server/bare/plugins/sdcpp-generation/ops/upscale.d.ts +3 -0
  139. package/dist/server/bare/plugins/sdcpp-generation/ops/upscale.js +40 -0
  140. package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +91 -16
  141. package/dist/server/bare/plugins/sdcpp-generation/plugin.js +97 -7
  142. package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +22 -3
  143. package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +50 -14
  144. package/dist/server/bare/rag-hyperdb/index.d.ts +1 -1
  145. package/dist/server/bare/rag-hyperdb/index.js +1 -1
  146. package/dist/server/bare/rag-hyperdb/rag-operation-manager.d.ts +26 -4
  147. package/dist/server/bare/rag-hyperdb/rag-operation-manager.js +65 -28
  148. package/dist/server/bare/runtime/disposable-scope.d.ts +36 -0
  149. package/dist/server/bare/runtime/disposable-scope.js +69 -0
  150. package/dist/server/bare/runtime/index.d.ts +8 -0
  151. package/dist/server/bare/runtime/index.js +3 -0
  152. package/dist/server/bare/runtime/request-context.d.ts +49 -0
  153. package/dist/server/bare/runtime/request-context.js +1 -0
  154. package/dist/server/bare/runtime/request-id.d.ts +12 -0
  155. package/dist/server/bare/runtime/request-id.js +14 -0
  156. package/dist/server/bare/runtime/request-registry-singleton.d.ts +4 -0
  157. package/dist/server/bare/runtime/request-registry-singleton.js +27 -0
  158. package/dist/server/bare/runtime/request-registry.d.ts +136 -0
  159. package/dist/server/bare/runtime/request-registry.js +297 -0
  160. package/dist/server/bare/runtime/with-request-context.d.ts +22 -0
  161. package/dist/server/bare/runtime/with-request-context.js +53 -0
  162. package/dist/server/bare/types/addon-responses.d.ts +11 -1
  163. package/dist/server/rpc/handler-registry.js +26 -7
  164. package/dist/server/rpc/handlers/cancel-delegated.js +28 -31
  165. package/dist/server/rpc/handlers/cancelHandler.d.ts +30 -1
  166. package/dist/server/rpc/handlers/cancelHandler.js +54 -22
  167. package/dist/server/rpc/handlers/delete-cache.d.ts +9 -0
  168. package/dist/server/rpc/handlers/delete-cache.js +13 -15
  169. package/dist/server/rpc/handlers/download-asset.js +133 -40
  170. package/dist/server/rpc/handlers/index.d.ts +2 -0
  171. package/dist/server/rpc/handlers/index.js +2 -0
  172. package/dist/server/rpc/handlers/load-model/download-manager.d.ts +60 -2
  173. package/dist/server/rpc/handlers/load-model/download-manager.js +141 -8
  174. package/dist/server/rpc/handlers/load-model/handler.js +213 -98
  175. package/dist/server/rpc/handlers/load-model/http.js +3 -3
  176. package/dist/server/rpc/handlers/load-model/hyperdrive.js +1 -1
  177. package/dist/server/rpc/handlers/load-model/registry.js +1 -1
  178. package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +20 -1
  179. package/dist/server/rpc/handlers/load-model/resolve-session.js +6 -5
  180. package/dist/server/rpc/handlers/load-model/resolve.d.ts +3 -2
  181. package/dist/server/rpc/handlers/load-model/resolve.js +9 -6
  182. package/dist/server/rpc/handlers/load-model/types.d.ts +24 -0
  183. package/dist/server/rpc/handlers/rag.js +133 -15
  184. package/dist/server/rpc/handlers/upscale-stream.d.ts +3 -0
  185. package/dist/server/rpc/handlers/upscale-stream.js +4 -0
  186. package/dist/server/utils/completion-normalizer.js +8 -0
  187. package/dist/server/utils/tools/dialect.js +4 -0
  188. package/dist/server/utils/tools/parser.js +16 -1
  189. package/dist/server/utils/tools/parsers/gemma4native.d.ts +4 -0
  190. package/dist/server/utils/tools/parsers/gemma4native.js +60 -0
  191. package/dist/server/utils/tools/parsers/qwen35.d.ts +4 -0
  192. package/dist/server/utils/tools/parsers/qwen35.js +120 -0
  193. package/dist/utils/aggregate-events.d.ts +9 -0
  194. package/dist/utils/aggregate-events.js +18 -3
  195. package/dist/utils/decorate-promise.d.ts +34 -0
  196. package/dist/utils/decorate-promise.js +35 -0
  197. package/dist/utils/errors-client.d.ts +3 -0
  198. package/dist/utils/errors-client.js +5 -0
  199. package/dist/utils/errors-server.d.ts +85 -0
  200. package/dist/utils/errors-server.js +97 -0
  201. package/package.json +20 -15
@@ -1,3 +1,55 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
1
53
  import { getModel, getModelConfig, getModelEntry, } from "../registry/model-registry.js";
2
54
  import { ModelType, } from "../../../schemas/index.js";
3
55
  import { createAudioStream } from "../utils/audio-input.js";
@@ -6,8 +58,9 @@ import { TranscriptionFailedError } from "../../../utils/errors-server.js";
6
58
  import { nowMs } from "../../../profiling/index.js";
7
59
  import { buildStreamResult } from "../../../profiling/model-execution.js";
8
60
  import { assertMetadataSupported, toTranscribeSegment, } from "../utils/transcribe-metadata.js";
61
+ import { getRequestRegistry, withRequestContext, } from "../runtime/index.js";
62
+ import { generateServerRequestId } from "../runtime/request-id.js";
9
63
  export { assertMetadataSupported, toTranscribeSegment, };
10
- const logger = getServerLogger();
11
64
  function hasRunStreaming(model) {
12
65
  return "runStreaming" in model && typeof model.runStreaming === "function";
13
66
  }
@@ -56,22 +109,59 @@ async function restorePrompt(modelId, originalConfig) {
56
109
  ...(miscConfig && { miscConfig }),
57
110
  });
58
111
  }
59
- export async function* transcribe(params) {
60
- const { modelId, metadata } = params;
61
- const engineType = getEngineModelType(modelId);
62
- assertMetadataSupported(modelId, engineType, metadata);
63
- const silenceMarker = SILENCE_MARKERS[engineType] ?? "";
64
- const audioFormat = getAudioFormat(modelId, engineType);
65
- const originalConfig = await applyPrompt(modelId, params.prompt, engineType);
66
- let modelExecutionMs = 0;
67
- let response;
112
+ export async function* transcribe(params, requestId) {
113
+ const env_1 = { stack: [], error: void 0, hasError: false };
68
114
  try {
115
+ const { modelId, metadata } = params;
116
+ // Open a request-scoped lifecycle. The registry routes
117
+ // `cancel({ requestId })` and `cancel({ modelId, kind: "transcribe" })`
118
+ // through this context. Falls back to a server-generated id if the
119
+ // client didn't send one.
120
+ const ctx = __addDisposableResource(env_1, getRequestRegistry().begin({
121
+ requestId: requestId ?? generateServerRequestId(),
122
+ kind: "transcribe",
123
+ modelId,
124
+ }), true);
125
+ const requestLogger = withRequestContext(getServerLogger(), ctx);
126
+ const engineType = getEngineModelType(modelId);
127
+ assertMetadataSupported(modelId, engineType, metadata);
128
+ const silenceMarker = SILENCE_MARKERS[engineType] ?? "";
129
+ const audioFormat = getAudioFormat(modelId, engineType);
130
+ const originalConfig = await applyPrompt(modelId, params.prompt, engineType);
131
+ if (originalConfig) {
132
+ // `restorePrompt` runs on every exit path — happy, throw, cancel —
133
+ // via the scope. LIFO unwinding pairs with the addon-cancel detach
134
+ // below.
135
+ ctx.scope.defer(() => restorePrompt(modelId, originalConfig));
136
+ }
69
137
  const model = getModel(modelId);
138
+ // Hard-cancel wiring: whisper.cpp / parakeet expose model-wide
139
+ // `addon.cancel()`. The listener forwards an abort so the
140
+ // currently-running transcription stops decoding ASAP. The
141
+ // `if (ctx.signal.aborted) break` guard inside the iterate loop is
142
+ // the soft-cancel safety net for the case where the abort fires
143
+ // between the addon flag flipping and the iterator's next pull.
144
+ const onAbort = () => {
145
+ const addon = model.addon;
146
+ if (addon?.cancel) {
147
+ addon.cancel.call(addon).catch((err) => {
148
+ requestLogger.warn(`[cancel] addon.cancel() rejected during abort for modelId=${modelId}: ${err instanceof Error ? err.message : String(err)}`);
149
+ });
150
+ }
151
+ };
152
+ ctx.signal.addEventListener("abort", onAbort, { once: true });
153
+ if (ctx.signal.aborted)
154
+ onAbort();
155
+ ctx.scope.defer(() => {
156
+ ctx.signal.removeEventListener("abort", onAbort);
157
+ });
70
158
  const audioStream = await createAudioStream(params.audioChunk, audioFormat);
71
159
  const modelStart = nowMs();
72
- response = (await model.run(audioStream));
160
+ const response = (await model.run(audioStream));
73
161
  for await (const output of response.iterate()) {
74
- logger.debug("Streaming Transcription Update:", output);
162
+ if (ctx.signal.aborted)
163
+ break;
164
+ requestLogger.debug("Streaming Transcription Update:", output);
75
165
  const chunks = output;
76
166
  if (metadata) {
77
167
  for (const chunk of chunks) {
@@ -91,41 +181,102 @@ export async function* transcribe(params) {
91
181
  yield text;
92
182
  }
93
183
  }
94
- modelExecutionMs = nowMs() - modelStart;
184
+ const modelExecutionMs = nowMs() - modelStart;
185
+ const stats = {
186
+ ...(response.stats?.audioDurationMs !== undefined && { audioDuration: response.stats.audioDurationMs }),
187
+ ...(response.stats?.realTimeFactor !== undefined && { realTimeFactor: response.stats.realTimeFactor }),
188
+ ...(response.stats?.tokensPerSecond !== undefined && { tokensPerSecond: response.stats.tokensPerSecond }),
189
+ ...(response.stats?.totalTokens !== undefined && { totalTokens: response.stats.totalTokens }),
190
+ ...(response.stats?.totalSegments !== undefined && { totalSegments: response.stats.totalSegments }),
191
+ ...(response.stats?.whisperEncodeMs !== undefined && { whisperEncodeTime: response.stats.whisperEncodeMs }),
192
+ ...(response.stats?.whisperDecodeMs !== undefined && { whisperDecodeTime: response.stats.whisperDecodeMs }),
193
+ ...(response.stats?.encoderMs !== undefined && { encoderTime: response.stats.encoderMs }),
194
+ ...(response.stats?.decoderMs !== undefined && { decoderTime: response.stats.decoderMs }),
195
+ ...(response.stats?.melSpecMs !== undefined && { melSpecTime: response.stats.melSpecMs }),
196
+ };
197
+ return buildStreamResult(modelExecutionMs, stats);
198
+ }
199
+ catch (e_1) {
200
+ env_1.error = e_1;
201
+ env_1.hasError = true;
95
202
  }
96
203
  finally {
97
- if (originalConfig) {
98
- await restorePrompt(modelId, originalConfig);
99
- }
204
+ const result_1 = __disposeResources(env_1);
205
+ if (result_1)
206
+ await result_1;
100
207
  }
101
- const stats = {
102
- ...(response?.stats?.audioDurationMs !== undefined && { audioDuration: response.stats.audioDurationMs }),
103
- ...(response?.stats?.realTimeFactor !== undefined && { realTimeFactor: response.stats.realTimeFactor }),
104
- ...(response?.stats?.tokensPerSecond !== undefined && { tokensPerSecond: response.stats.tokensPerSecond }),
105
- ...(response?.stats?.totalTokens !== undefined && { totalTokens: response.stats.totalTokens }),
106
- ...(response?.stats?.totalSegments !== undefined && { totalSegments: response.stats.totalSegments }),
107
- ...(response?.stats?.whisperEncodeMs !== undefined && { whisperEncodeTime: response.stats.whisperEncodeMs }),
108
- ...(response?.stats?.whisperDecodeMs !== undefined && { whisperDecodeTime: response.stats.whisperDecodeMs }),
109
- ...(response?.stats?.encoderMs !== undefined && { encoderTime: response.stats.encoderMs }),
110
- ...(response?.stats?.decoderMs !== undefined && { decoderTime: response.stats.decoderMs }),
111
- ...(response?.stats?.melSpecMs !== undefined && { melSpecTime: response.stats.melSpecMs }),
112
- };
113
- return buildStreamResult(modelExecutionMs, stats);
114
208
  }
115
- export async function* transcribeStream(modelId, audioInputStream, prompt, metadata) {
116
- const engineType = getEngineModelType(modelId);
117
- assertMetadataSupported(modelId, engineType, metadata);
118
- const silenceMarker = SILENCE_MARKERS[engineType] ?? "";
119
- const originalConfig = await applyPrompt(modelId, prompt, engineType);
209
+ export async function* transcribeStream(modelId, audioInputStream, prompt, metadata, opts, requestId) {
210
+ const env_2 = { stack: [], error: void 0, hasError: false };
120
211
  try {
212
+ // Same `kind: "transcribe"` as the unary variant — the registry
213
+ // doesn't distinguish streaming vs non-streaming variants of the same
214
+ // operation, so `cancel({ modelId, kind: "transcribe" })` cancels
215
+ // either shape.
216
+ const ctx = __addDisposableResource(env_2, getRequestRegistry().begin({
217
+ requestId: requestId ?? generateServerRequestId(),
218
+ kind: "transcribe",
219
+ modelId,
220
+ }), true);
221
+ const requestLogger = withRequestContext(getServerLogger(), ctx);
222
+ const engineType = getEngineModelType(modelId);
223
+ assertMetadataSupported(modelId, engineType, metadata);
224
+ const silenceMarker = SILENCE_MARKERS[engineType] ?? "";
225
+ const originalConfig = await applyPrompt(modelId, prompt, engineType);
226
+ if (originalConfig) {
227
+ ctx.scope.defer(() => restorePrompt(modelId, originalConfig));
228
+ }
121
229
  const model = getModel(modelId);
122
230
  if (!hasRunStreaming(model)) {
123
231
  throw new TranscriptionFailedError(`Model ${modelId} does not support streaming transcription`);
124
232
  }
125
- const response = await model.runStreaming(audioInputStream);
126
- for await (const segments of response.iterate()) {
127
- logger.debug("Live Transcription Update:", segments);
128
- for (const segment of segments) {
233
+ const onAbort = () => {
234
+ const addon = model.addon;
235
+ if (addon?.cancel) {
236
+ addon.cancel.call(addon).catch((err) => {
237
+ requestLogger.warn(`[cancel] addon.cancel() rejected during abort for modelId=${modelId}: ${err instanceof Error ? err.message : String(err)}`);
238
+ });
239
+ }
240
+ };
241
+ ctx.signal.addEventListener("abort", onAbort, { once: true });
242
+ if (ctx.signal.aborted)
243
+ onAbort();
244
+ ctx.scope.defer(() => {
245
+ ctx.signal.removeEventListener("abort", onAbort);
246
+ });
247
+ const runOpts = {};
248
+ if (opts?.emitVadEvents)
249
+ runOpts.emitVadEvents = true;
250
+ if (opts?.endOfTurnSilenceMs !== undefined) {
251
+ runOpts.endOfTurnSilenceMs = opts.endOfTurnSilenceMs;
252
+ }
253
+ if (opts?.vadRunIntervalMs !== undefined) {
254
+ runOpts.vadRunIntervalMs = opts.vadRunIntervalMs;
255
+ }
256
+ const response = await model.runStreaming(audioInputStream, runOpts);
257
+ for await (const output of response.iterate()) {
258
+ if (ctx.signal.aborted)
259
+ break;
260
+ requestLogger.debug("Live Transcription Update:", output);
261
+ if (!Array.isArray(output)) {
262
+ if (output.type === "vad") {
263
+ yield {
264
+ type: "vad",
265
+ speaking: output.speaking,
266
+ probability: output.probability,
267
+ };
268
+ continue;
269
+ }
270
+ if (output.type === "endOfTurn") {
271
+ yield {
272
+ type: "endOfTurn",
273
+ silenceDurationMs: output.silenceDurationMs,
274
+ };
275
+ continue;
276
+ }
277
+ continue;
278
+ }
279
+ for (const segment of output) {
129
280
  if (!segment.text)
130
281
  continue;
131
282
  if (silenceMarker && segment.text.includes(silenceMarker))
@@ -140,9 +291,13 @@ export async function* transcribeStream(modelId, audioInputStream, prompt, metad
140
291
  }
141
292
  }
142
293
  }
294
+ catch (e_2) {
295
+ env_2.error = e_2;
296
+ env_2.hasError = true;
297
+ }
143
298
  finally {
144
- if (originalConfig) {
145
- await restorePrompt(modelId, originalConfig);
146
- }
299
+ const result_2 = __disposeResources(env_2);
300
+ if (result_2)
301
+ await result_2;
147
302
  }
148
303
  }
@@ -1,7 +1,7 @@
1
1
  import { type TranslateParams, type TranslationStats } from "../../../schemas/index";
2
2
  export declare function getLanguage(code: string | undefined): string;
3
3
  export declare function isAfrican(code: string | undefined): boolean;
4
- export declare function translate(params: TranslateParams): AsyncGenerator<string, {
4
+ export declare function translate(params: TranslateParams, requestId?: string): AsyncGenerator<string, {
5
5
  modelExecutionMs: number;
6
6
  stats?: TranslationStats;
7
7
  }, unknown>;
@@ -1,9 +1,64 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
1
53
  import { getModelEntry } from "../registry/model-registry.js";
2
54
  import { translateServerParamsSchema, normalizeModelType, ModelType, AFRICAN_LANGUAGES_MAP, } from "../../../schemas/index.js";
3
55
  import { getLangName } from "@qvac/langdetect-text";
4
56
  import { nowMs } from "../../../profiling/index.js";
5
57
  import { buildStreamResult } from "../../../profiling/model-execution.js";
6
58
  import { ModelIsDelegatedError, ModelNotFoundError, ModelTypeMismatchError, } from "../../../utils/errors-server.js";
59
+ import { getRequestRegistry, withRequestContext, } from "../runtime/index.js";
60
+ import { generateServerRequestId } from "../runtime/request-id.js";
61
+ import { getServerLogger } from "../../../logging/index.js";
7
62
  export function getLanguage(code) {
8
63
  if (!code)
9
64
  return "";
@@ -26,104 +81,158 @@ const LLM_TRANSLATE_GENERATION_PARAMS = {
26
81
  function shouldSkipPerCallSampling(modelName) {
27
82
  return !!modelName && modelName.startsWith("AFRICAN_");
28
83
  }
29
- export async function* translate(params) {
30
- const { modelId, text, modelType: inputModelType } = params;
31
- const entry = getModelEntry(modelId);
32
- if (!entry) {
33
- throw new ModelNotFoundError(modelId);
34
- }
35
- if (entry.isDelegated) {
36
- throw new ModelIsDelegatedError(modelId);
37
- }
38
- const canonicalModelType = entry.local.modelType;
39
- const model = entry.local.model;
40
- if (inputModelType !== undefined) {
41
- const requestedCanonical = normalizeModelType(inputModelType);
42
- if (requestedCanonical !== canonicalModelType) {
43
- throw new ModelTypeMismatchError(canonicalModelType, requestedCanonical);
84
+ export async function* translate(params, requestId) {
85
+ const env_1 = { stack: [], error: void 0, hasError: false };
86
+ try {
87
+ const { modelId, text, modelType: inputModelType } = params;
88
+ const entry = getModelEntry(modelId);
89
+ if (!entry) {
90
+ throw new ModelNotFoundError(modelId);
44
91
  }
45
- }
46
- const isLlm = canonicalModelType === ModelType.llamacppCompletion;
47
- const from = isLlm ? params.from : undefined;
48
- const to = isLlm ? params.to : undefined;
49
- const context = isLlm ? params.context : undefined;
50
- const afriquePrompt = isLlm && (isAfrican(from) || isAfrican(to));
51
- translateServerParamsSchema.parse(params);
52
- const fromLanguage = getLanguage(from);
53
- const toLanguage = getLanguage(to);
54
- // Check if input is an array and model type is NMT
55
- if (Array.isArray(text) &&
56
- canonicalModelType === ModelType.nmtcppTranslation) {
57
- // Use runBatch for batch processing
92
+ if (entry.isDelegated) {
93
+ throw new ModelIsDelegatedError(modelId);
94
+ }
95
+ const canonicalModelType = entry.local.modelType;
96
+ const model = entry.local.model;
97
+ if (inputModelType !== undefined) {
98
+ const requestedCanonical = normalizeModelType(inputModelType);
99
+ if (requestedCanonical !== canonicalModelType) {
100
+ throw new ModelTypeMismatchError(canonicalModelType, requestedCanonical);
101
+ }
102
+ }
103
+ const isLlm = canonicalModelType === ModelType.llamacppCompletion;
104
+ const from = isLlm ? params.from : undefined;
105
+ const to = isLlm ? params.to : undefined;
106
+ const context = isLlm ? params.context : undefined;
107
+ const afriquePrompt = isLlm && (isAfrican(from) || isAfrican(to));
108
+ translateServerParamsSchema.parse(params);
109
+ const fromLanguage = getLanguage(from);
110
+ const toLanguage = getLanguage(to);
111
+ // Open a request-scoped lifecycle for both engine branches. LLM-
112
+ // translate inherits llamacpp-completion's `{ scope: "model",
113
+ // hard: true }` cancel surface; NMT-translate inherits nmtcpp's
114
+ // `{ scope: "none" }` — so the addon-cancel wiring below only
115
+ // engages on the LLM path. NMT cancel is purely soft: the loop
116
+ // exits on `signal.aborted`, scope unwinds, and the addon may run
117
+ // to completion in the background — acceptable because the result
118
+ // is dropped either way.
119
+ const ctx = __addDisposableResource(env_1, getRequestRegistry().begin({
120
+ requestId: requestId ?? generateServerRequestId(),
121
+ kind: "translate",
122
+ modelId,
123
+ }), true);
124
+ const requestLogger = withRequestContext(getServerLogger(), ctx);
125
+ if (isLlm) {
126
+ const onAbort = () => {
127
+ const addon = model.addon;
128
+ if (addon?.cancel) {
129
+ addon.cancel.call(addon).catch((err) => {
130
+ requestLogger.warn(`[cancel] addon.cancel() rejected during abort for modelId=${modelId}: ${err instanceof Error ? err.message : String(err)}`);
131
+ });
132
+ }
133
+ };
134
+ ctx.signal.addEventListener("abort", onAbort, { once: true });
135
+ if (ctx.signal.aborted)
136
+ onAbort();
137
+ ctx.scope.defer(() => {
138
+ ctx.signal.removeEventListener("abort", onAbort);
139
+ });
140
+ }
141
+ // Check if input is an array and model type is NMT
142
+ if (Array.isArray(text) &&
143
+ canonicalModelType === ModelType.nmtcppTranslation) {
144
+ // Use runBatch for batch processing
145
+ const modelStart = nowMs();
146
+ const translations = await model.runBatch(text);
147
+ const modelExecutionMs = nowMs() - modelStart;
148
+ // Soft-cancel boundary: if `cancel({ requestId })` landed while
149
+ // the addon was running the batch, bail before yielding anything.
150
+ // The addon may have completed its work; the result is dropped.
151
+ if (ctx.signal.aborted) {
152
+ return { modelExecutionMs };
153
+ }
154
+ // Yield each translation with a newline separator
155
+ for (let i = 0; i < translations.length; i++) {
156
+ if (ctx.signal.aborted)
157
+ break;
158
+ const translation = translations[i];
159
+ yield translation;
160
+ if (i < translations.length - 1) {
161
+ yield "\n";
162
+ }
163
+ }
164
+ return { modelExecutionMs };
165
+ }
166
+ // Single text processing (for NMT or LLM)
167
+ const singleText = Array.isArray(text) ? text[0] : text;
168
+ // Prepare input based on model type
169
+ const input = canonicalModelType === ModelType.nmtcppTranslation
170
+ ? singleText
171
+ : [
172
+ {
173
+ role: afriquePrompt ? "user" : "system",
174
+ content: afriquePrompt
175
+ ? `Translate ${fromLanguage} to ${toLanguage}.\n${fromLanguage}: ${singleText}\n${toLanguage}:`
176
+ : `${context ? `${context}. ` : ""}Translate the following text from ${fromLanguage} into ${toLanguage}. Only output the translation, nothing else.\n\n${fromLanguage}: ${singleText}\n${toLanguage}:`,
177
+ },
178
+ ];
58
179
  const modelStart = nowMs();
59
- const translations = await model.runBatch(text);
60
- const modelExecutionMs = nowMs() - modelStart;
61
- // Yield each translation with a newline separator
62
- for (let i = 0; i < translations.length; i++) {
63
- const translation = translations[i];
64
- yield translation;
65
- if (i < translations.length - 1) {
66
- yield "\n";
180
+ let response;
181
+ if (canonicalModelType === ModelType.llamacppCompletion &&
182
+ !shouldSkipPerCallSampling(entry.local.name)) {
183
+ // AnyModel.run is intentionally erased to a single-arg signature in the
184
+ // registry layer (Omit<BaseInference, "addon">). Re-narrow to the engine
185
+ // shape so we get the same typing as @qvac/llm-llamacpp.run().
186
+ const llmRun = model.run.bind(model);
187
+ response = await llmRun(input, {
188
+ generationParams: LLM_TRANSLATE_GENERATION_PARAMS,
189
+ });
190
+ }
191
+ else {
192
+ response = await model.run(input);
193
+ }
194
+ // Check if the response has an iterate method (like LLM models)
195
+ if (canonicalModelType === ModelType.llamacppCompletion &&
196
+ typeof response.iterate === "function") {
197
+ const llmResponse = response;
198
+ for await (const token of llmResponse.iterate()) {
199
+ if (ctx.signal.aborted)
200
+ break;
201
+ yield token;
67
202
  }
203
+ const modelExecutionMs = nowMs() - modelStart;
204
+ const stats = {
205
+ ...(llmResponse.stats?.TPS !== undefined && { tokensPerSecond: llmResponse.stats.TPS }),
206
+ ...(llmResponse.stats?.TTFT !== undefined && { timeToFirstToken: llmResponse.stats.TTFT }),
207
+ ...(llmResponse.stats?.CacheTokens !== undefined && { cacheTokens: llmResponse.stats.CacheTokens }),
208
+ ...(llmResponse.stats?.generatedTokens !== undefined && { totalTokens: llmResponse.stats.generatedTokens }),
209
+ };
210
+ return buildStreamResult(modelExecutionMs, stats);
68
211
  }
69
- return { modelExecutionMs };
70
- }
71
- // Single text processing (for NMT or LLM)
72
- const singleText = Array.isArray(text) ? text[0] : text;
73
- // Prepare input based on model type
74
- const input = canonicalModelType === ModelType.nmtcppTranslation
75
- ? singleText
76
- : [
77
- {
78
- role: afriquePrompt ? "user" : "system",
79
- content: afriquePrompt
80
- ? `Translate ${fromLanguage} to ${toLanguage}.\n${fromLanguage}: ${singleText}\n${toLanguage}:`
81
- : `${context ? `${context}. ` : ""}Translate the following text from ${fromLanguage} into ${toLanguage}. Only output the translation, nothing else.\n\n${fromLanguage}: ${singleText}\n${toLanguage}:`,
82
- },
83
- ];
84
- const modelStart = nowMs();
85
- let response;
86
- if (canonicalModelType === ModelType.llamacppCompletion &&
87
- !shouldSkipPerCallSampling(entry.local.name)) {
88
- // AnyModel.run is intentionally erased to a single-arg signature in the
89
- // registry layer (Omit<BaseInference, "addon">). Re-narrow to the engine
90
- // shape so we get the same typing as @qvac/llm-llamacpp.run().
91
- const llmRun = model.run.bind(model);
92
- response = await llmRun(input, {
93
- generationParams: LLM_TRANSLATE_GENERATION_PARAMS,
94
- });
95
- }
96
- else {
97
- response = await model.run(input);
98
- }
99
- // Check if the response has an iterate method (like LLM models)
100
- if (canonicalModelType === ModelType.llamacppCompletion &&
101
- typeof response.iterate === "function") {
102
- const llmResponse = response;
103
- for await (const token of llmResponse.iterate()) {
212
+ const nmtResponse = response;
213
+ for await (const token of nmtResponse.iterate()) {
214
+ if (ctx.signal.aborted)
215
+ break;
104
216
  yield token;
105
217
  }
106
218
  const modelExecutionMs = nowMs() - modelStart;
107
219
  const stats = {
108
- ...(llmResponse.stats?.TPS !== undefined && { tokensPerSecond: llmResponse.stats.TPS }),
109
- ...(llmResponse.stats?.TTFT !== undefined && { timeToFirstToken: llmResponse.stats.TTFT }),
110
- ...(llmResponse.stats?.CacheTokens !== undefined && { cacheTokens: llmResponse.stats.CacheTokens }),
111
- ...(llmResponse.stats?.generatedTokens !== undefined && { totalTokens: llmResponse.stats.generatedTokens }),
220
+ ...(nmtResponse.stats?.totalTime !== undefined && { totalTime: nmtResponse.stats.totalTime }),
221
+ ...(nmtResponse.stats?.totalTokens !== undefined && { totalTokens: nmtResponse.stats.totalTokens }),
222
+ ...(nmtResponse.stats?.decodeTime !== undefined && { decodeTime: nmtResponse.stats.decodeTime }),
223
+ ...(nmtResponse.stats?.encodeTime !== undefined && { encodeTime: nmtResponse.stats.encodeTime }),
224
+ ...(nmtResponse.stats?.TPS !== undefined && { tokensPerSecond: nmtResponse.stats.TPS }),
225
+ ...(nmtResponse.stats?.TTFT !== undefined && { timeToFirstToken: nmtResponse.stats.TTFT }),
112
226
  };
113
227
  return buildStreamResult(modelExecutionMs, stats);
114
228
  }
115
- const nmtResponse = response;
116
- for await (const token of nmtResponse.iterate()) {
117
- yield token;
229
+ catch (e_1) {
230
+ env_1.error = e_1;
231
+ env_1.hasError = true;
232
+ }
233
+ finally {
234
+ const result_1 = __disposeResources(env_1);
235
+ if (result_1)
236
+ await result_1;
118
237
  }
119
- const modelExecutionMs = nowMs() - modelStart;
120
- const stats = {
121
- ...(nmtResponse.stats?.totalTime !== undefined && { totalTime: nmtResponse.stats.totalTime }),
122
- ...(nmtResponse.stats?.totalTokens !== undefined && { totalTokens: nmtResponse.stats.totalTokens }),
123
- ...(nmtResponse.stats?.decodeTime !== undefined && { decodeTime: nmtResponse.stats.decodeTime }),
124
- ...(nmtResponse.stats?.encodeTime !== undefined && { encodeTime: nmtResponse.stats.encodeTime }),
125
- ...(nmtResponse.stats?.TPS !== undefined && { tokensPerSecond: nmtResponse.stats.TPS }),
126
- ...(nmtResponse.stats?.TTFT !== undefined && { timeToFirstToken: nmtResponse.stats.TTFT }),
127
- };
128
- return buildStreamResult(modelExecutionMs, stats);
129
238
  }
@@ -1,17 +1,26 @@
1
+ import type { AbortSignal } from "bare-abort-controller";
1
2
  import type { CompletionParams, CompletionStats, GenerationParams, ResponseFormat, Tool, ToolCall, ToolDialect } from "../../../../../schemas/index";
2
- import { noteCancelRequested as noteCancelRequestedFromState } from "./kv-cache-state";
3
+ import type { DisposableScope } from "../../../runtime/disposable-scope";
4
+ import type { Logger } from "../../../../../logging/types";
3
5
  interface CompletionResult {
4
6
  modelExecutionMs: number;
5
7
  stats?: CompletionStats;
6
8
  toolCalls: ToolCall[];
7
9
  }
8
- export declare function clearCachedMessageCounts(prefix?: string): void;
9
- export declare const noteCancelRequested: typeof noteCancelRequestedFromState;
10
10
  export declare function completion(params: CompletionParams & {
11
11
  tools?: Tool[];
12
12
  generationParams?: GenerationParams;
13
13
  toolDialect?: ToolDialect;
14
14
  responseFormat?: ResponseFormat;
15
+ }, opts: {
16
+ signal: AbortSignal;
17
+ scope: DisposableScope;
18
+ /**
19
+ * Request-scoped logger forwarded to `createKvCacheSession` so
20
+ * kv-cache lines share the request's lifecycle prefix. Falls
21
+ * back to the module-level server logger when omitted.
22
+ */
23
+ logger?: Logger;
15
24
  }): AsyncGenerator<{
16
25
  token: string;
17
26
  }, CompletionResult, unknown>;