@qvac/sdk 0.20.1 → 0.21.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 (104) hide show
  1. package/NOTICE +76 -13
  2. package/README.md +39 -5
  3. package/dist/examples/advisory-model-fit.d.ts +17 -16
  4. package/dist/examples/advisory-model-fit.js +18 -18
  5. package/dist/examples/asr/parakeet-indic-conformer-filesystem.js +3 -3
  6. package/dist/examples/asr/parakeet-unified-coreml.d.ts +2 -0
  7. package/dist/examples/asr/parakeet-unified-coreml.js +113 -0
  8. package/dist/examples/assess-model-fit.js +9 -4
  9. package/dist/examples/model-fit-ui/server.js +5 -2
  10. package/dist/examples/rpc-inference.d.ts +2 -0
  11. package/dist/examples/rpc-inference.js +51 -0
  12. package/dist/examples/rpc-server.d.ts +2 -0
  13. package/dist/examples/rpc-server.js +32 -0
  14. package/dist/examples/tools/llamacpp-deferred-tools.d.ts +2 -0
  15. package/dist/examples/tools/llamacpp-deferred-tools.js +77 -0
  16. package/dist/examples/tts/audio8.js +4 -0
  17. package/dist/examples/tts/moss-dialogue.d.ts +2 -0
  18. package/dist/examples/tts/moss-dialogue.js +63 -0
  19. package/dist/examples/tts/moss.d.ts +2 -0
  20. package/dist/examples/tts/moss.js +71 -0
  21. package/dist/scripts/contract/build-contract.d.ts +2 -1
  22. package/dist/scripts/contract/build-contract.js +4 -1
  23. package/dist/scripts/contract/build-numeric-constants.d.ts +15 -0
  24. package/dist/scripts/contract/build-numeric-constants.js +13 -0
  25. package/dist/scripts/contract/method-shapes.d.ts +3 -0
  26. package/dist/scripts/contract/method-shapes.js +3 -0
  27. package/dist/scripts/enforce-inference-versions.d.ts +18 -0
  28. package/dist/scripts/enforce-inference-versions.js +158 -0
  29. package/dist/scripts/generate-bare-kit-runtimes.d.ts +2 -0
  30. package/dist/scripts/generate-bare-kit-runtimes.js +106 -0
  31. package/dist/src/client/api/assess-model-fit.d.ts +5 -4
  32. package/dist/src/client/api/assess-model-fit.js +24 -7
  33. package/dist/src/client/api/bci-transcribe.d.ts +4 -1
  34. package/dist/src/client/api/get-system-resources.d.ts +8 -0
  35. package/dist/src/client/api/get-system-resources.js +8 -0
  36. package/dist/src/client/api/index.d.ts +1 -0
  37. package/dist/src/client/api/index.js +1 -0
  38. package/dist/src/client/api/rpc-server.d.ts +20 -0
  39. package/dist/src/client/api/rpc-server.js +45 -0
  40. package/dist/src/client/api/text-to-speech.d.ts +9 -0
  41. package/dist/src/client/api/text-to-speech.js +9 -0
  42. package/dist/src/client/api/transcribe.d.ts +21 -15
  43. package/dist/src/client/api/transcribe.js +65 -20
  44. package/dist/src/client/rpc/rpc-error.js +11 -7
  45. package/dist/src/commands/bundle/entry-gen.d.ts +2 -2
  46. package/dist/src/commands/bundle/entry-gen.js +8 -4
  47. package/dist/src/commands/bundle/index.d.ts +20 -0
  48. package/dist/src/commands/bundle/index.js +88 -12
  49. package/dist/src/commands/bundle/plugins.js +1 -1
  50. package/dist/src/commands/command-logger.d.ts +6 -0
  51. package/dist/src/commands/command-logger.js +12 -0
  52. package/dist/src/commands/host-prebuilds/index.d.ts +71 -0
  53. package/dist/src/commands/host-prebuilds/index.js +227 -0
  54. package/dist/src/commands/host-prebuilds/package-manager.d.ts +54 -0
  55. package/dist/src/commands/host-prebuilds/package-manager.js +278 -0
  56. package/dist/src/commands/index.d.ts +10 -1
  57. package/dist/src/commands/index.js +6 -0
  58. package/dist/src/commands/verify/abi.d.ts +22 -2
  59. package/dist/src/commands/verify/abi.js +49 -27
  60. package/dist/src/commands/verify/addon-source.d.ts +22 -0
  61. package/dist/src/commands/verify/addon-source.js +69 -3
  62. package/dist/src/commands/verify/bare-kit-runtime.d.ts +38 -0
  63. package/dist/src/commands/verify/bare-kit-runtime.js +201 -0
  64. package/dist/src/commands/verify/bare-kit-runtimes.d.ts +12 -0
  65. package/dist/src/commands/verify/bare-kit-runtimes.js +108 -0
  66. package/dist/src/commands/verify/bundle-source.js +16 -15
  67. package/dist/src/commands/verify/engines-advice.d.ts +73 -0
  68. package/dist/src/commands/verify/engines-advice.js +271 -0
  69. package/dist/src/commands/verify/index.d.ts +22 -0
  70. package/dist/src/commands/verify/index.js +151 -13
  71. package/dist/src/commands/verify/node-modules-source.js +43 -39
  72. package/dist/src/commands/verify/prebuilds.d.ts +19 -0
  73. package/dist/src/commands/verify/prebuilds.js +52 -13
  74. package/dist/src/expo/plugins/index.d.ts +2 -0
  75. package/dist/src/expo/plugins/withMobileBundle.d.ts +15 -5
  76. package/dist/src/expo/plugins/withMobileBundle.js +68 -42
  77. package/dist/src/expo/plugins/withQvacSDK.d.ts +7 -1
  78. package/dist/src/expo/plugins/withQvacSDK.js +6 -3
  79. package/dist/src/index.d.ts +5 -2
  80. package/dist/src/index.js +4 -2
  81. package/dist/src/schemas/sdk-errors-client.d.ts +2 -0
  82. package/dist/src/schemas/sdk-errors-client.js +16 -0
  83. package/dist/src/schemas/sdk-errors-server.d.ts +1 -0
  84. package/dist/src/schemas/sdk-errors-server.js +5 -0
  85. package/dist/src/utils/errors-client.d.ts +15 -0
  86. package/dist/src/utils/errors-client.js +26 -0
  87. package/dist/src/utils/errors-server.d.ts +1 -0
  88. package/dist/src/utils/errors-server.js +1 -0
  89. package/dist/src/utils/mcp-adapter.js +7 -1
  90. package/dist/src/utils/tool-helpers.d.ts +4 -0
  91. package/dist/src/utils/tool-helpers.js +2 -0
  92. package/dist/src/worker/ggml-rpc-server-provider.d.ts +2 -0
  93. package/dist/src/worker/ggml-rpc-server-provider.js +1 -0
  94. package/dist/src/worker/rpc-server-provider.d.ts +3 -0
  95. package/dist/src/worker/rpc-server-provider.js +1 -0
  96. package/package.json +30 -25
  97. package/dist/scripts/enforce-inference-peer-dependencies.d.ts +0 -2
  98. package/dist/scripts/enforce-inference-peer-dependencies.js +0 -39
  99. package/dist/src/deprecate.d.ts +0 -2
  100. package/dist/src/deprecate.js +0 -9
  101. package/dist/src/worker/legacy-worker-entry.d.ts +0 -2
  102. package/dist/src/worker/legacy-worker-entry.js +0 -3
  103. package/dist/src/worker/worker-core.d.ts +0 -7
  104. package/dist/src/worker/worker-core.js +0 -8
@@ -0,0 +1,106 @@
1
+ import { promises as fsp } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import semver from 'semver';
5
+ import { fetchBareKitVersionFromGitHub, fetchBareVersionFromGitHub, fetchText, REACT_NATIVE_BARE_KIT } from '../src/commands/verify/bare-kit-runtime.js';
6
+ import { BARE_KIT_BARE_VERSIONS, REACT_NATIVE_BARE_KIT_RUNTIMES } from '../src/commands/verify/bare-kit-runtimes.js';
7
+ const CONCURRENCY = 8;
8
+ const OUTPUT_PATH = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'src', 'commands', 'verify', 'bare-kit-runtimes.ts');
9
+ async function listPublishedVersions() {
10
+ const metadata = JSON.parse(await fetchText(`https://registry.npmjs.org/${REACT_NATIVE_BARE_KIT}`));
11
+ return Object.keys(metadata.versions);
12
+ }
13
+ async function listBareKitTags() {
14
+ const tags = JSON.parse(await fetchText('https://api.github.com/repos/holepunchto/bare-kit/tags?per_page=100'));
15
+ return tags.map((tag) => tag.name.replace(/^v/, ''));
16
+ }
17
+ function latest(versions) {
18
+ const valid = versions.filter((version) => semver.valid(version) !== null);
19
+ return valid.length === 0 ? null : valid.sort(semver.rcompare)[0];
20
+ }
21
+ /**
22
+ * Releases newer than the newest one already in the table, oldest first.
23
+ * Everything at or below it was probed on an earlier run, including the old
24
+ * react-native-bare-kit releases that have no CMakeLists.txt to parse.
25
+ */
26
+ function newerThan(versions, lastKnown) {
27
+ return versions
28
+ .filter((version) => semver.valid(version) !== null)
29
+ .filter((version) => lastKnown === null || semver.gt(version, lastKnown))
30
+ .sort(semver.compare);
31
+ }
32
+ async function mapConcurrent(items, fn) {
33
+ const results = new Array(items.length);
34
+ let next = 0;
35
+ async function worker() {
36
+ while (next < items.length) {
37
+ const index = next++;
38
+ results[index] = await fn(items[index]);
39
+ }
40
+ }
41
+ await Promise.all(Array.from({ length: CONCURRENCY }, worker));
42
+ return results;
43
+ }
44
+ async function tryFetch(fn) {
45
+ try {
46
+ return await fn();
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ function sortedEntries(record) {
53
+ return Object.entries(record).sort(([a], [b]) => semver.compare(a, b));
54
+ }
55
+ function render(runtimes, bareByBareKit) {
56
+ const lines = [
57
+ '// Generated by scripts/generate-bare-kit-runtimes.ts. Do not edit by hand.',
58
+ '',
59
+ 'export interface BareKitRuntime {',
60
+ ' bareKit: string',
61
+ ' bare: string',
62
+ '}',
63
+ '',
64
+ '/**',
65
+ ' * react-native-bare-kit releases whose CMakeLists.txt names the bare-kit',
66
+ ' * prebuilds it ships. Older releases carry no such reference.',
67
+ ' */',
68
+ 'export const REACT_NATIVE_BARE_KIT_RUNTIMES: Record<string, BareKitRuntime> = {'
69
+ ];
70
+ lines.push(...entryLines(sortedEntries(runtimes).map(([version, runtime]) => `'${version}': { bareKit: '${runtime.bareKit}', bare: '${runtime.bare}' }`)));
71
+ lines.push('}', '', '/** Bare version each bare-kit release is built against. */');
72
+ lines.push('export const BARE_KIT_BARE_VERSIONS: Record<string, string> = {');
73
+ lines.push(...entryLines(sortedEntries(bareByBareKit).map(([bareKit, bare]) => `'${bareKit}': '${bare}'`)));
74
+ lines.push('}', '');
75
+ return lines.join('\n');
76
+ }
77
+ /** Object entries in the repo's Prettier style: no trailing comma. */
78
+ function entryLines(entries) {
79
+ return entries.map((entry, index) => ` ${entry}${index === entries.length - 1 ? '' : ','}`);
80
+ }
81
+ async function main() {
82
+ const full = process.argv.includes('--full');
83
+ const bareByBareKit = full ? {} : { ...BARE_KIT_BARE_VERSIONS };
84
+ const runtimes = full ? {} : { ...REACT_NATIVE_BARE_KIT_RUNTIMES };
85
+ const bareKitTags = newerThan(await listBareKitTags(), latest(Object.keys(bareByBareKit)));
86
+ console.log(`Resolving ${bareKitTags.length} new bare-kit releases...`);
87
+ const bareVersions = await mapConcurrent(bareKitTags, (bareKit) => tryFetch(() => fetchBareVersionFromGitHub(bareKit)));
88
+ bareKitTags.forEach((bareKit, index) => {
89
+ const bare = bareVersions[index];
90
+ if (bare)
91
+ bareByBareKit[bareKit] = bare;
92
+ });
93
+ const versions = newerThan(await listPublishedVersions(), latest(Object.keys(runtimes)));
94
+ console.log(`Resolving ${versions.length} new ${REACT_NATIVE_BARE_KIT} releases...`);
95
+ const bareKitByVersion = await mapConcurrent(versions, (version) => tryFetch(() => fetchBareKitVersionFromGitHub(version)));
96
+ versions.forEach((version, index) => {
97
+ const bareKit = bareKitByVersion[index];
98
+ const bare = bareKit ? bareByBareKit[bareKit] : undefined;
99
+ if (bareKit && bare)
100
+ runtimes[version] = { bareKit, bare };
101
+ });
102
+ await fsp.writeFile(OUTPUT_PATH, render(runtimes, bareByBareKit));
103
+ console.log(`Wrote ${Object.keys(runtimes).length} ${REACT_NATIVE_BARE_KIT} and ` +
104
+ `${Object.keys(bareByBareKit).length} bare-kit entries to ${OUTPUT_PATH}`);
105
+ }
106
+ await main();
@@ -9,11 +9,12 @@ import { type AssessModelFitInput, type AssessModelFitResult } from '@qvac/infer
9
9
  * should treat it as "cannot say", not "no".
10
10
  *
11
11
  * For a single candidate it fetches the registry's weightless description of
12
- * the artifact — tens of KB, never the weights — and runs the engine's own
13
- * fitter against it, reported as `native-fit` evidence. Where that is
14
- * unavailable, and for a set of candidates, the calibrated estimate stands.
12
+ * every source that load names — tens of KB each, never the weights — and runs
13
+ * the engine's own fitter against them, reported as `native-fit` evidence.
14
+ * Where that is unavailable, and for a set of candidates, the calibrated
15
+ * estimate stands.
15
16
  *
16
- * @param input - Candidates with their intended workloads, the declared
17
+ * @param input - Loads to assess in `loadModel`'s own parameters, the declared
17
18
  * execution mode, and the headroom policy.
18
19
  * @returns Per-model and combined verdicts, with the budget and bounds they came
19
20
  * from, plus every assumption that was made.
@@ -1,6 +1,19 @@
1
- import { assessModelFitInputSchema } from '@qvac/inference/surface';
1
+ import { assessModelFitInputSchema, inferModelTypeFromModelSrc } from '@qvac/inference/surface';
2
2
  import { send } from '../rpc/rpc-client.js';
3
- import { InvalidResponseError } from '../../utils/errors-client.js';
3
+ import { InvalidResponseError, ModelTypeRequiredError } from '../../utils/errors-client.js';
4
+ /**
5
+ * Names the engine a candidate omitted, from the source it carries.
6
+ *
7
+ * @throws {ModelTypeRequiredError} When the source names no engine either.
8
+ */
9
+ function withModelType(candidate) {
10
+ if (candidate.modelType !== undefined)
11
+ return candidate;
12
+ const inferred = inferModelTypeFromModelSrc(candidate.modelSrc);
13
+ if (inferred === undefined)
14
+ throw new ModelTypeRequiredError();
15
+ return { ...candidate, modelType: inferred };
16
+ }
4
17
  /**
5
18
  * Assesses, before anything is downloaded, whether the given models are likely
6
19
  * to fit in this device's memory.
@@ -11,17 +24,21 @@ import { InvalidResponseError } from '../../utils/errors-client.js';
11
24
  * should treat it as "cannot say", not "no".
12
25
  *
13
26
  * For a single candidate it fetches the registry's weightless description of
14
- * the artifact — tens of KB, never the weights — and runs the engine's own
15
- * fitter against it, reported as `native-fit` evidence. Where that is
16
- * unavailable, and for a set of candidates, the calibrated estimate stands.
27
+ * every source that load names — tens of KB each, never the weights — and runs
28
+ * the engine's own fitter against them, reported as `native-fit` evidence.
29
+ * Where that is unavailable, and for a set of candidates, the calibrated
30
+ * estimate stands.
17
31
  *
18
- * @param input - Candidates with their intended workloads, the declared
32
+ * @param input - Loads to assess in `loadModel`'s own parameters, the declared
19
33
  * execution mode, and the headroom policy.
20
34
  * @returns Per-model and combined verdicts, with the budget and bounds they came
21
35
  * from, plus every assumption that was made.
22
36
  */
23
37
  export async function assessModelFit(input) {
24
- const parsed = assessModelFitInputSchema.parse(input);
38
+ const parsed = assessModelFitInputSchema.parse({
39
+ ...input,
40
+ models: input.models.map(withModelType)
41
+ });
25
42
  const request = { type: 'assessModelFit', ...parsed };
26
43
  const response = await send(request);
27
44
  if (response.type !== 'assessModelFit') {
@@ -37,7 +37,10 @@ export declare function bciTranscribe(params: BciTranscribeClientParams, options
37
37
  * @param params.modelId - The loaded BCI model to use.
38
38
  * @param params.metadata - When true, the session yields transcript segment
39
39
  * objects (`{ text, startMs, endMs, append, id }`)
40
- * instead of plain text.
40
+ * instead of plain text. With `emit: 'delta'`, each
41
+ * segment also carries `windowStartTimestep`: its
42
+ * timestamps are window-local, and this maps them
43
+ * onto the stream timeline.
41
44
  * @param params.windowTimesteps - Decode window size in timesteps.
42
45
  * @param params.hopTimesteps - How far the window advances between decodes.
43
46
  * @param params.emit - `'delta'` (default) yields only the newly-discovered
@@ -1,3 +1,11 @@
1
1
  import { type GetSystemResourcesInput, type SystemResources } from '@qvac/inference/surface';
2
+ /**
3
+ * Read the worker's system capabilities and optional resource sample.
4
+ *
5
+ * @param input - Optional settings; set `sample` to request a current resource sample.
6
+ * @returns System capabilities with availability information and a sample when provided by the worker.
7
+ * @throws {InvalidResponseError} If the worker returns a different response type.
8
+ * @throws {Error} If the RPC request fails.
9
+ */
2
10
  export declare function getSystemResources(input?: GetSystemResourcesInput): Promise<SystemResources>;
3
11
  //# sourceMappingURL=get-system-resources.d.ts.map
@@ -1,6 +1,14 @@
1
1
  import {} from '@qvac/inference/surface';
2
2
  import { send } from '../rpc/rpc-client.js';
3
3
  import { InvalidResponseError } from '../../utils/errors-client.js';
4
+ /**
5
+ * Read the worker's system capabilities and optional resource sample.
6
+ *
7
+ * @param input - Optional settings; set `sample` to request a current resource sample.
8
+ * @returns System capabilities with availability information and a sample when provided by the worker.
9
+ * @throws {InvalidResponseError} If the worker returns a different response type.
10
+ * @throws {Error} If the RPC request fails.
11
+ */
4
12
  export async function getSystemResources(input) {
5
13
  const request = {
6
14
  type: 'getSystemResources',
@@ -37,4 +37,5 @@ export { vlaPreprocessImage, vlaPadState, VLA_DEFAULT_IMAGE_SIZE } from './vla-h
37
37
  export { audioGen } from './audio-gen.js';
38
38
  export { audioEdit } from './audio-edit.js';
39
39
  export { audioUnderstand } from './audio-understand.js';
40
+ export { startRpcServer, stopRpcServer, discoverRpcServers } from './rpc-server.js';
40
41
  //# sourceMappingURL=index.d.ts.map
@@ -37,3 +37,4 @@ export { vlaPreprocessImage, vlaPadState, VLA_DEFAULT_IMAGE_SIZE } from './vla-h
37
37
  export { audioGen } from './audio-gen.js';
38
38
  export { audioEdit } from './audio-edit.js';
39
39
  export { audioUnderstand } from './audio-understand.js';
40
+ export { startRpcServer, stopRpcServer, discoverRpcServers } from './rpc-server.js';
@@ -0,0 +1,20 @@
1
+ import { type StartRpcServerOptions, type StopRpcServerOptions, type DiscoverRpcServersOptions, type RpcServerInfo, type RpcServerCandidate } from '@qvac/inference/surface';
2
+ /**
3
+ * Start a worker-owned native RPC server. Non-loopback binding and advertising require explicit opt-in.
4
+ * Cancel a pending start with its promise.requestId.
5
+ */
6
+ export declare function startRpcServer(options?: StartRpcServerOptions): Promise<RpcServerInfo> & {
7
+ requestId: string;
8
+ };
9
+ /** Withdraw the announcement and await native stop. Bare stop has no deadline. */
10
+ export declare function stopRpcServer(options: StopRpcServerOptions): Promise<void>;
11
+ /**
12
+ * Find private-network candidates with device inventories and server RDMA availability.
13
+ * RDMA availability is a server-side offer, not a guarantee of the negotiated transport.
14
+ * Use getRpcDeviceMap with the chosen endpoint registration order before loadModel.
15
+ * Cancel a pending search with its promise.requestId.
16
+ */
17
+ export declare function discoverRpcServers(options: DiscoverRpcServersOptions): Promise<RpcServerCandidate[]> & {
18
+ requestId: string;
19
+ };
20
+ //# sourceMappingURL=rpc-server.d.ts.map
@@ -0,0 +1,45 @@
1
+ import {} from '@qvac/inference/surface';
2
+ import { send } from '../rpc/rpc-client.js';
3
+ import { InvalidResponseError } from '../../utils/errors-client.js';
4
+ import { decoratePromise } from '../../utils/decorate-promise.js';
5
+ import { generateClientRequestId } from './client-request-id.js';
6
+ /**
7
+ * Start a worker-owned native RPC server. Non-loopback binding and advertising require explicit opt-in.
8
+ * Cancel a pending start with its promise.requestId.
9
+ */
10
+ export function startRpcServer(options = {}) {
11
+ const requestId = generateClientRequestId();
12
+ return decoratePromise(runStartRpcServer(options, requestId), { requestId });
13
+ }
14
+ async function runStartRpcServer(options, requestId) {
15
+ const response = await send({ ...options, type: 'startRpcServer', requestId });
16
+ if (response.type !== 'startRpcServer')
17
+ throw new InvalidResponseError('startRpcServer');
18
+ return {
19
+ serverId: response.serverId,
20
+ url: response.url,
21
+ rdmaCapable: response.rdmaCapable
22
+ };
23
+ }
24
+ /** Withdraw the announcement and await native stop. Bare stop has no deadline. */
25
+ export async function stopRpcServer(options) {
26
+ const response = await send({ ...options, type: 'stopRpcServer' });
27
+ if (response.type !== 'stopRpcServer')
28
+ throw new InvalidResponseError('stopRpcServer');
29
+ }
30
+ /**
31
+ * Find private-network candidates with device inventories and server RDMA availability.
32
+ * RDMA availability is a server-side offer, not a guarantee of the negotiated transport.
33
+ * Use getRpcDeviceMap with the chosen endpoint registration order before loadModel.
34
+ * Cancel a pending search with its promise.requestId.
35
+ */
36
+ export function discoverRpcServers(options) {
37
+ const requestId = generateClientRequestId();
38
+ return decoratePromise(runDiscoverRpcServers(options, requestId), { requestId });
39
+ }
40
+ async function runDiscoverRpcServers(options, requestId) {
41
+ const response = await send({ ...options, type: 'discoverRpcServers', requestId });
42
+ if (response.type !== 'discoverRpcServers')
43
+ throw new InvalidResponseError('discoverRpcServers');
44
+ return response.servers;
45
+ }
@@ -43,6 +43,15 @@ export declare class TtsMulticast {
43
43
  * metadata with the audio. Multiple consumers can iterate the response
44
44
  * independently via the underlying `TtsMulticast`.
45
45
  *
46
+ * MOSS models stream natively rather than by sentence: under `stream: true`
47
+ * the whole text is one synthesis whose audio arrives every
48
+ * `streamChunkTokens` codec frames (in one piece when that is unset), so
49
+ * `durationTokens` and a MOSS-TTSD dialogue's reference transcripts apply to
50
+ * the text as a whole. `sentenceStream: true` still splits a single-speaker
51
+ * text; a dialogue rejects it. `sentenceStreamLocale` and
52
+ * `sentenceStreamMaxChunkScalars` configure that splitting, so a MOSS request
53
+ * rejects them unless it sets `sentenceStream: true`.
54
+ *
46
55
  * `result.done` resolves to `true` when synthesis completes cleanly, `false`
47
56
  * if the consumer breaks out before the terminal frame, or rejects on a
48
57
  * pipeline error. Awaiting `done` is safe even when no stream is iterated.
@@ -202,6 +202,15 @@ function buildTextToSpeechStreamRequest(params, requestId) {
202
202
  * metadata with the audio. Multiple consumers can iterate the response
203
203
  * independently via the underlying `TtsMulticast`.
204
204
  *
205
+ * MOSS models stream natively rather than by sentence: under `stream: true`
206
+ * the whole text is one synthesis whose audio arrives every
207
+ * `streamChunkTokens` codec frames (in one piece when that is unset), so
208
+ * `durationTokens` and a MOSS-TTSD dialogue's reference transcripts apply to
209
+ * the text as a whole. `sentenceStream: true` still splits a single-speaker
210
+ * text; a dialogue rejects it. `sentenceStreamLocale` and
211
+ * `sentenceStreamMaxChunkScalars` configure that splitting, so a MOSS request
212
+ * rejects them unless it sets `sentenceStream: true`.
213
+ *
205
214
  * `result.done` resolves to `true` when synthesis completes cleanly, `false`
206
215
  * if the consumer breaks out before the terminal frame, or rejects on a
207
216
  * pipeline error. Awaiting `done` is safe even when no stream is iterated.
@@ -1,5 +1,5 @@
1
- import { type TranscribeClientParams, type RPCOptions, type TranscribeSegment, type TranscribeStreamRequest, type TranscribeStreamClientParams, type TranscribeStreamSession, type TranscribeStreamMetadataSession, type TranscribeStreamConversationSession, type TranscribeStats } from '@qvac/inference/surface';
2
- import { duplex } from '../rpc/rpc-client.js';
1
+ import { type TranscribeClientParams, type RPCOptions, type TranscribeSegment, type TranscribeStreamRequest, type TranscribeStreamClientParams, type TranscribeStreamSession, type TranscribeStreamMetadataSession, type TranscribeStreamConversationSession, type TranscribeStreamEvent, type TranscribeStats, type InferenceBackendDiagnostics } from '@qvac/inference/surface';
2
+ import { stream, duplex } from '../rpc/rpc-client.js';
3
3
  type TranscribeDuplexFactory = (request: TranscribeStreamRequest, options?: RPCOptions) => ReturnType<typeof duplex>;
4
4
  /**
5
5
  * Transcribe audio and return the complete text. Accepts either a file
@@ -10,24 +10,27 @@ type TranscribeDuplexFactory = (request: TranscribeStreamRequest, options?: RPCO
10
10
  * @param params.audioChunk - Audio input as either a file path (string) or audio buffer
11
11
  * @param params.prompt - Optional initial prompt to guide the transcription
12
12
  * @param params.metadata - When true, resolves to an array of transcript
13
- * segments (`{ text, startMs, endMs, append, id }`)
14
- * instead of joined text. Whisper engine only.
13
+ * segments (`{ text, startMs, endMs, append, id }`,
14
+ * plus `isEndOfTurn` and `startsWord` on parakeet)
15
+ * instead of joined text.
15
16
  * @param options - Optional RPC options including per-call profiling
16
17
  * @returns A promise (decorated with `requestId`) resolving to the
17
18
  * complete transcribed text, or — when `metadata` is true —
18
19
  * the list of transcript segments in emission order. The
19
20
  * `requestId` is reachable synchronously so callers can target
20
21
  * this in-flight transcription with `cancel({ requestId })`
21
- * before `await` resolves.
22
+ * before `await` resolves. `stats` resolves to the terminal engine
23
+ * statistics, including Parakeet's per-run Core ML usage.
22
24
  */
23
- export declare function transcribe(params: TranscribeClientParams & {
24
- metadata: true;
25
- }, options?: RPCOptions): Promise<TranscribeSegment[]> & {
26
- requestId: string;
27
- };
28
- export declare function transcribe(params: TranscribeClientParams, options?: RPCOptions): Promise<string> & {
25
+ type TranscribeCall<T> = Promise<T> & {
29
26
  requestId: string;
27
+ stats: Promise<TranscribeStats | undefined>;
30
28
  };
29
+ export declare function transcribe(params: TranscribeClientParams & {
30
+ metadata: true;
31
+ }, options?: RPCOptions): TranscribeCall<TranscribeSegment[]>;
32
+ export declare function transcribe(params: TranscribeClientParams, options?: RPCOptions): TranscribeCall<string>;
33
+ export declare function createTranscribeCall(params: TranscribeClientParams, options?: RPCOptions, responseStream?: typeof stream): TranscribeCall<string | TranscribeSegment[]>;
31
34
  /**
32
35
  * @deprecated Pass audio via `transcribe()` instead. This overload will be
33
36
  * removed in the next major version.
@@ -57,8 +60,9 @@ export declare function transcribeStream(params: TranscribeClientParams, options
57
60
  * @param params.modelId - The loaded transcription model to use
58
61
  * @param params.prompt - Optional initial prompt to guide transcription
59
62
  * @param params.metadata - When true, the session yields transcript segment
60
- * objects (`{ text, startMs, endMs, append, id }`)
61
- * instead of plain text. Whisper engine only.
63
+ * objects (`{ text, startMs, endMs, append, id }`,
64
+ * plus `isEndOfTurn` and `startsWord` on parakeet)
65
+ * instead of plain text.
62
66
  * @param options - Optional RPC options including per-call profiling.
63
67
  * @returns A session object: call `write(audioChunk)` with a `Uint8Array`
64
68
  * (Node `Buffer` is a `Uint8Array` subtype) to feed audio,
@@ -81,13 +85,15 @@ export declare function transcribeStream(params: TranscribeStreamClientParams, o
81
85
  * to surface strings or segments; `sessionName` is only used to label the
82
86
  * "already iterated" error so callers see the correct session type.
83
87
  */
84
- export declare function createTranscribeStreamSession<T>(params: TranscribeStreamClientParams, options: RPCOptions | undefined, process: (line: string, onStats: (stats: TranscribeStats | undefined) => void) => T | undefined | null, sessionName: string, duplexFactory?: TranscribeDuplexFactory): Promise<{
88
+ export declare function createTranscribeStreamSession<T>(params: TranscribeStreamClientParams, options: RPCOptions | undefined, process: (line: string, onTerminal: (stats: TranscribeStats | undefined, diagnostics: InferenceBackendDiagnostics | undefined) => void) => T | undefined | null, sessionName: string, duplexFactory?: TranscribeDuplexFactory): Promise<{
85
89
  stats: Promise<TranscribeStats | undefined>;
90
+ diagnostics: Promise<InferenceBackendDiagnostics | undefined>;
86
91
  write(audioChunk: Uint8Array): void;
87
92
  end(): void;
88
93
  destroy(): void;
89
94
  [Symbol.asyncIterator](): AsyncIterator<T>;
90
95
  }>;
91
- export declare function processLine(line: string, onStats: (stats: TranscribeStats | undefined) => void): string | undefined | null;
96
+ export declare function processLine(line: string, onTerminal: (stats: TranscribeStats | undefined, diagnostics: InferenceBackendDiagnostics | undefined) => void): string | undefined | null;
97
+ export declare function processLineConversation(line: string, wantsMetadata: boolean, onTerminal: (stats: TranscribeStats | undefined, diagnostics: InferenceBackendDiagnostics | undefined) => void): TranscribeStreamEvent | undefined | null;
92
98
  export {};
93
99
  //# sourceMappingURL=transcribe.d.ts.map
@@ -18,26 +18,38 @@ function buildTranscribeRequest(params, requestId) {
18
18
  };
19
19
  }
20
20
  export function transcribe(params, options) {
21
+ return createTranscribeCall(params, options);
22
+ }
23
+ export function createTranscribeCall(params, options, responseStream = stream) {
21
24
  // Client-generated id surfaced synchronously on the returned promise
22
25
  // — same shape as `loadModel` / `downloadAsset` / `completion`. The
23
26
  // CLI cancel bridge in `qvac serve` binds `req.on('close')` to
24
27
  // `cancel({ requestId })` immediately after the call returns so a
25
28
  // client disconnect aborts the in-flight transcription.
26
29
  const requestId = generateClientRequestId();
27
- const inner = runTranscribe(params, requestId, options);
28
- return decoratePromise(inner, { requestId });
30
+ let resolveStats = () => { };
31
+ let rejectStats = () => { };
32
+ const stats = new Promise((resolve, reject) => {
33
+ resolveStats = resolve;
34
+ rejectStats = reject;
35
+ });
36
+ stats.catch(() => { });
37
+ const inner = runTranscribe(params, requestId, options, resolveStats, responseStream);
38
+ inner.then(() => resolveStats(undefined), rejectStats);
39
+ return decoratePromise(inner, { requestId, stats });
29
40
  }
30
- async function runTranscribe(params, requestId, options) {
41
+ async function runTranscribe(params, requestId, options, onStats, responseStream) {
31
42
  const request = buildTranscribeRequest(params, requestId);
32
43
  if (params.metadata === true) {
33
44
  const segments = [];
34
- for await (const response of stream(request, options)) {
45
+ for await (const response of responseStream(request, options)) {
35
46
  if (response.type === 'transcribe') {
36
47
  const parsed = transcribeResponseSchema.parse(response);
37
48
  if (parsed.segment) {
38
49
  segments.push(parsed.segment);
39
50
  }
40
51
  if (parsed.done) {
52
+ onStats(parsed.stats);
41
53
  break;
42
54
  }
43
55
  }
@@ -45,13 +57,14 @@ async function runTranscribe(params, requestId, options) {
45
57
  return segments;
46
58
  }
47
59
  let fullText = '';
48
- for await (const response of stream(request, options)) {
60
+ for await (const response of responseStream(request, options)) {
49
61
  if (response.type === 'transcribe') {
50
62
  const parsed = transcribeResponseSchema.parse(response);
51
63
  if (parsed.text) {
52
64
  fullText += parsed.text;
53
65
  }
54
66
  if (parsed.done) {
67
+ onStats(parsed.stats);
55
68
  break;
56
69
  }
57
70
  }
@@ -144,16 +157,47 @@ export async function createTranscribeStreamSession(params, options, process, se
144
157
  statsSettled = true;
145
158
  rejectStatsPromise(error);
146
159
  }
147
- function resolveEmptyStats() {
148
- resolveStats(undefined);
160
+ // Settled from the same terminal frame as `stats`, and on the same paths:
161
+ // resolved empty when the stream closes without one, rejected on error.
162
+ let diagnosticsSettled = false;
163
+ let resolveDiagnosticsPromise = () => { };
164
+ let rejectDiagnosticsPromise = () => { };
165
+ const diagnostics = new Promise((resolve, reject) => {
166
+ resolveDiagnosticsPromise = resolve;
167
+ rejectDiagnosticsPromise = reject;
168
+ });
169
+ diagnostics.catch(() => { });
170
+ function resolveDiagnostics(value) {
171
+ if (diagnosticsSettled)
172
+ return;
173
+ diagnosticsSettled = true;
174
+ resolveDiagnosticsPromise(value);
175
+ }
176
+ function rejectDiagnostics(error) {
177
+ if (diagnosticsSettled)
178
+ return;
179
+ diagnosticsSettled = true;
180
+ rejectDiagnosticsPromise(error);
181
+ }
182
+ function resolveTerminal(statsValue, diagnosticsValue) {
183
+ resolveStats(statsValue);
184
+ resolveDiagnostics(diagnosticsValue);
185
+ }
186
+ function resolveEmptyTerminal() {
187
+ resolveTerminal(undefined, undefined);
188
+ }
189
+ function rejectTerminal(error) {
190
+ rejectStats(error);
191
+ rejectDiagnostics(error);
149
192
  }
150
193
  function processResponse(line) {
151
- return process(line, resolveStats);
194
+ return process(line, resolveTerminal);
152
195
  }
153
- const responses = parseLines(responseStream, processResponse, resolveEmptyStats, rejectStats);
196
+ const responses = parseLines(responseStream, processResponse, resolveEmptyTerminal, rejectTerminal);
154
197
  let consumed = false;
155
198
  return {
156
199
  stats,
200
+ diagnostics,
157
201
  write(audioChunk) {
158
202
  requestStream.write(audioChunk);
159
203
  },
@@ -161,7 +205,7 @@ export async function createTranscribeStreamSession(params, options, process, se
161
205
  requestStream.end();
162
206
  },
163
207
  destroy() {
164
- resolveStats(undefined);
208
+ resolveEmptyTerminal();
165
209
  requestStream.destroy();
166
210
  responseStream.destroy();
167
211
  },
@@ -182,7 +226,7 @@ function transcribeStreamDuplexMetadata(params, options) {
182
226
  }
183
227
  function transcribeStreamDuplexConversation(params, options) {
184
228
  const wantsMetadata = params.metadata === true;
185
- return createTranscribeStreamSession(params, options, (line, onStats) => processLineConversation(line, wantsMetadata, onStats), 'TranscribeStreamConversationSession');
229
+ return createTranscribeStreamSession(params, options, (line, onTerminal) => processLineConversation(line, wantsMetadata, onTerminal), 'TranscribeStreamConversationSession');
186
230
  }
187
231
  /**
188
232
  * Line-delimited parser: reads newline-separated frames from a duplex
@@ -241,31 +285,32 @@ function parseResponseLine(line) {
241
285
  * `undefined` for frames the caller should skip, or the value extracted from
242
286
  * the frame.
243
287
  */
244
- function processWith(line, extract, onStats) {
288
+ function processWith(line, extract, onTerminal) {
245
289
  const response = parseResponseLine(line);
246
290
  if (response === null)
247
291
  return undefined;
248
292
  if (response.error)
249
293
  throw new TranscriptionFailedError(response.error);
250
294
  if (response.done) {
251
- onStats(response.stats);
295
+ onTerminal(response.stats, response.diagnostics);
252
296
  return null;
253
297
  }
254
298
  return extract(response);
255
299
  }
256
- export function processLine(line, onStats) {
257
- return processWith(line, (response) => (response.text?.trim() ? response.text : undefined), onStats);
300
+ export function processLine(line, onTerminal) {
301
+ return processWith(line, (response) => (response.text?.trim() ? response.text : undefined), onTerminal);
258
302
  }
259
- function processLineMetadata(line, onStats) {
260
- return processWith(line, (response) => response.segment, onStats);
303
+ function processLineMetadata(line, onTerminal) {
304
+ return processWith(line, (response) => response.segment, onTerminal);
261
305
  }
262
- function processLineConversation(line, wantsMetadata, onStats) {
306
+ export function processLineConversation(line, wantsMetadata, onTerminal) {
263
307
  return processWith(line, (response) => {
264
308
  if (response.vad) {
265
309
  return {
266
310
  type: 'vad',
267
311
  speaking: response.vad.speaking,
268
- probability: response.vad.probability
312
+ probability: response.vad.probability,
313
+ ...(response.vad.source && { source: response.vad.source })
269
314
  };
270
315
  }
271
316
  if (response.endOfTurn) {
@@ -291,5 +336,5 @@ function processLineConversation(line, wantsMetadata, onStats) {
291
336
  return { type: 'text', text: response.text };
292
337
  }
293
338
  return undefined;
294
- }, onStats);
339
+ }, onTerminal);
295
340
  }
@@ -1,4 +1,4 @@
1
- import { ContextOverflowError, RequestIdConflictError, RequestNotFoundError, RequestRejectedByPolicyError, TranslationFailedError } from '../../utils/errors-server.js';
1
+ import { RpcServerOperationError, InferenceCancelledError, ContextOverflowError, RequestIdConflictError, RequestNotFoundError, RequestRejectedByPolicyError, TranslationFailedError } from '../../utils/errors-server.js';
2
2
  export class RPCError extends Error {
3
3
  timestamp;
4
4
  remoteStack;
@@ -93,14 +93,18 @@ function readOptionalStringField(fields, key) {
93
93
  * be re-exported from `@qvac/sdk` so consumers can `import` it.
94
94
  * Forgetting either side means `instanceof` regresses for that class.
95
95
  *
96
- * Client-constructed typed errors (e.g. `InferenceCancelledError`
97
- * built in `client/api/completion-stream.ts` from the aggregated
98
- * partial state) are NOT registered here — they never round-trip the
99
- * envelope, and adding a reconstructor for one would create a
100
- * parallel construction path that fires whenever the server happens
101
- * to throw the same class name.
96
+ * Completion aggregators construct cancellation errors locally with partial
97
+ * output. RPC control operations also throw them in the worker, so cancellation
98
+ * errors support both paths.
102
99
  */
103
100
  const RECONSTRUCTORS = {
101
+ INFERENCE_CANCELLED: (response) => {
102
+ const partial = response.typedFields?.['partial'];
103
+ return new InferenceCancelledError(readStringField(response.typedFields, 'requestId', ''), partial && typeof partial === 'object' && !Array.isArray(partial)
104
+ ? partial
105
+ : {}, response.cause);
106
+ },
107
+ RPC_SERVER_OPERATION_FAILED: (response) => new RpcServerOperationError(readStringField(response.typedFields, 'operation', 'rpcServer'), readStringField(response.typedFields, 'details', response.message), response.cause),
104
108
  REQUEST_ID_CONFLICT: (response) => {
105
109
  return new RequestIdConflictError(readStringField(response.typedFields, 'requestId', ''), response.cause);
106
110
  },
@@ -1,5 +1,5 @@
1
- export declare function generateWorkerEntry(pluginSpecifiers: string[], sdkName: string, resolveImport?: (specifier: string) => string): string;
2
- export declare function generateWorkerEntries(pluginSpecifiers: string[], sdkName: string, resolveBundleImport: (specifier: string) => string): {
1
+ export declare function generateWorkerEntry(pluginSpecifiers: string[], sdkName: string, resolveImport?: (specifier: string) => string, rpcServerProvider?: string): string;
2
+ export declare function generateWorkerEntries(pluginSpecifiers: string[], sdkName: string, resolveBundleImport: (specifier: string) => string, rpcServerProvider?: string): {
3
3
  runtimeEntry: string;
4
4
  bundleEntry: string;
5
5
  };