@qvac/sdk 0.8.2 → 0.9.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 (219) hide show
  1. package/NOTICE +267 -63
  2. package/README.md +2 -0
  3. package/dist/client/api/diffusion.d.ts +32 -0
  4. package/dist/client/api/diffusion.js +103 -0
  5. package/dist/client/api/embed.d.ts +9 -3
  6. package/dist/client/api/embed.js +4 -1
  7. package/dist/client/api/finetune.d.ts +69 -0
  8. package/dist/client/api/finetune.js +139 -0
  9. package/dist/client/api/get-model-info.d.ts +1 -1
  10. package/dist/client/api/index.d.ts +4 -0
  11. package/dist/client/api/index.js +4 -0
  12. package/dist/client/api/rag.d.ts +1 -1
  13. package/dist/client/api/rag.js +1 -1
  14. package/dist/client/api/resume.d.ts +14 -0
  15. package/dist/client/api/resume.js +20 -0
  16. package/dist/client/api/suspend.d.ts +13 -0
  17. package/dist/client/api/suspend.js +19 -0
  18. package/dist/client/api/transcribe.d.ts +25 -17
  19. package/dist/client/api/transcribe.js +115 -35
  20. package/dist/client/rpc/bare-client.d.ts +5 -1
  21. package/dist/client/rpc/bare-client.js +70 -57
  22. package/dist/client/rpc/expo-rpc-client.d.ts +4 -0
  23. package/dist/client/rpc/expo-rpc-client.js +8 -0
  24. package/dist/client/rpc/node-rpc-client.d.ts +4 -0
  25. package/dist/client/rpc/node-rpc-client.js +8 -0
  26. package/dist/client/rpc/rpc-client.d.ts +13 -0
  27. package/dist/client/rpc/rpc-client.js +131 -1
  28. package/dist/examples/delegated-inference/composite.d.ts +2 -0
  29. package/dist/examples/delegated-inference/composite.js +99 -0
  30. package/dist/examples/diffusion-flux2-klein.d.ts +2 -0
  31. package/dist/examples/diffusion-flux2-klein.js +46 -0
  32. package/dist/examples/diffusion-simple.d.ts +2 -0
  33. package/dist/examples/diffusion-simple.js +17 -0
  34. package/dist/examples/diffusion-txt2img.d.ts +2 -0
  35. package/dist/examples/diffusion-txt2img.js +40 -0
  36. package/dist/examples/embed-p2p.js +2 -2
  37. package/dist/examples/finetune/llamacpp-finetune.d.ts +2 -0
  38. package/dist/examples/finetune/llamacpp-finetune.js +78 -0
  39. package/dist/examples/logging-streaming.js +1 -1
  40. package/dist/examples/mcp-websearch.js +2 -0
  41. package/dist/examples/ocr-fasttext.js +11 -0
  42. package/dist/examples/profiling/per-call.js +3 -3
  43. package/dist/examples/rag/rag-chromadb.js +2 -2
  44. package/dist/examples/rag/rag-hyperdb/pipeline.js +1 -1
  45. package/dist/examples/rag/rag-lancedb.js +2 -2
  46. package/dist/examples/rag/rag-sqlite.js +2 -2
  47. package/dist/examples/suspend-resume.d.ts +2 -0
  48. package/dist/examples/suspend-resume.js +52 -0
  49. package/dist/examples/transcription/parakeet-ctc-filesystem.js +3 -5
  50. package/dist/examples/transcription/parakeet-microphone-record.js +42 -33
  51. package/dist/examples/transcription/parakeet-sortformer.js +13 -3
  52. package/dist/examples/transcription/parakeet-tdt-filesystem.js +5 -7
  53. package/dist/examples/transcription/whispercpp-filesystem-streaming.d.ts +2 -0
  54. package/dist/examples/transcription/whispercpp-filesystem-streaming.js +99 -0
  55. package/dist/examples/transcription/whispercpp-microphone-record.js +78 -299
  56. package/dist/examples/transcription/whispercpp-prompt.js +11 -0
  57. package/dist/examples/translation/translation-llm.js +2 -2
  58. package/dist/examples/tts/supertonic-multilingual.d.ts +2 -0
  59. package/dist/examples/tts/supertonic-multilingual.js +56 -0
  60. package/dist/examples/tts/supertonic.js +12 -7
  61. package/dist/index.d.ts +3 -2
  62. package/dist/index.js +3 -2
  63. package/dist/logging/utils.js +1 -1
  64. package/dist/models/registry/models.d.ts +15176 -4264
  65. package/dist/models/registry/models.js +18657 -7404
  66. package/dist/models/update-models/codegen.js +2 -2
  67. package/dist/models/update-models/naming.js +26 -0
  68. package/dist/pear/pre.js +2 -0
  69. package/dist/polyfills/bare-globals.d.ts +2 -0
  70. package/dist/polyfills/bare-globals.js +4 -0
  71. package/dist/polyfills/bare-globals.noop.d.ts +2 -0
  72. package/dist/polyfills/bare-globals.noop.js +1 -0
  73. package/dist/profiling/exporters.js +16 -11
  74. package/dist/profiling/model-execution.d.ts +16 -0
  75. package/dist/profiling/model-execution.js +33 -0
  76. package/dist/schemas/common.d.ts +967 -198
  77. package/dist/schemas/common.js +16 -1
  78. package/dist/schemas/completion-stream.d.ts +14 -6
  79. package/dist/schemas/completion-stream.js +4 -3
  80. package/dist/schemas/download-asset.d.ts +7 -3
  81. package/dist/schemas/embed.d.ts +19 -0
  82. package/dist/schemas/embed.js +7 -0
  83. package/dist/schemas/engine-addon-map.js +4 -1
  84. package/dist/schemas/finetune.d.ts +620 -0
  85. package/dist/schemas/finetune.js +161 -0
  86. package/dist/schemas/get-model-info.d.ts +2 -0
  87. package/dist/schemas/get-model-info.js +1 -0
  88. package/dist/schemas/index.d.ts +5 -1
  89. package/dist/schemas/index.js +5 -1
  90. package/dist/schemas/llamacpp-config.d.ts +26 -6
  91. package/dist/schemas/llamacpp-config.js +12 -0
  92. package/dist/schemas/load-model.d.ts +4005 -1400
  93. package/dist/schemas/load-model.js +34 -1
  94. package/dist/schemas/model-src-utils.d.ts +10 -2
  95. package/dist/schemas/model-types.d.ts +19 -1
  96. package/dist/schemas/model-types.js +11 -0
  97. package/dist/schemas/ocr.d.ts +2 -0
  98. package/dist/schemas/plugin.d.ts +24 -3
  99. package/dist/schemas/plugin.js +14 -0
  100. package/dist/schemas/profiling.d.ts +4 -0
  101. package/dist/schemas/profiling.js +4 -0
  102. package/dist/schemas/registry.d.ts +18 -0
  103. package/dist/schemas/registry.js +2 -0
  104. package/dist/schemas/reload-config.d.ts +9 -1
  105. package/dist/schemas/resume.d.ts +10 -0
  106. package/dist/schemas/resume.js +7 -0
  107. package/dist/schemas/sdcpp-config.d.ts +327 -0
  108. package/dist/schemas/sdcpp-config.js +102 -0
  109. package/dist/schemas/sdk-config.d.ts +1225 -235
  110. package/dist/schemas/sdk-config.js +4 -0
  111. package/dist/schemas/sdk-errors-server.d.ts +2 -0
  112. package/dist/schemas/sdk-errors-server.js +12 -0
  113. package/dist/schemas/suspend.d.ts +10 -0
  114. package/dist/schemas/suspend.js +7 -0
  115. package/dist/schemas/text-to-speech.d.ts +187 -10
  116. package/dist/schemas/text-to-speech.js +12 -4
  117. package/dist/schemas/transcription-config.d.ts +16 -56
  118. package/dist/schemas/transcription-config.js +0 -2
  119. package/dist/schemas/transcription.d.ts +66 -6
  120. package/dist/schemas/transcription.js +29 -7
  121. package/dist/schemas/translate.d.ts +14 -4
  122. package/dist/schemas/translate.js +10 -2
  123. package/dist/schemas/translation-config.d.ts +30 -10
  124. package/dist/server/bare/delegate-rpc-client.js +3 -3
  125. package/dist/server/bare/hyperswarm.js +15 -3
  126. package/dist/server/bare/ops/embed.d.ts +8 -4
  127. package/dist/server/bare/ops/embed.js +15 -4
  128. package/dist/server/bare/ops/transcribe.d.ts +6 -2
  129. package/dist/server/bare/ops/transcribe.js +53 -1
  130. package/dist/server/bare/ops/translate.d.ts +4 -1
  131. package/dist/server/bare/ops/translate.js +28 -65
  132. package/dist/server/bare/ops/unload-model.js +2 -0
  133. package/dist/server/bare/plugins/index.d.ts +1 -0
  134. package/dist/server/bare/plugins/index.js +1 -0
  135. package/dist/server/bare/plugins/llamacpp-completion/ops/cache-logger.d.ts +1 -0
  136. package/dist/server/bare/plugins/llamacpp-completion/ops/cache-logger.js +3 -0
  137. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +3 -1
  138. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +57 -11
  139. package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.d.ts +11 -0
  140. package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +112 -0
  141. package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +175 -5
  142. package/dist/server/bare/plugins/llamacpp-completion/plugin.js +45 -35
  143. package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +11 -1
  144. package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +9 -4
  145. package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +17 -2
  146. package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +16 -15
  147. package/dist/server/bare/plugins/onnx-ocr/ops/ocr-stream.d.ts +5 -7
  148. package/dist/server/bare/plugins/onnx-ocr/ops/ocr-stream.js +11 -3
  149. package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +2 -0
  150. package/dist/server/bare/plugins/onnx-ocr/plugin.js +24 -19
  151. package/dist/server/bare/plugins/onnx-tts/ops/text-to-speech.d.ts +7 -2
  152. package/dist/server/bare/plugins/onnx-tts/ops/text-to-speech.js +22 -97
  153. package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +98 -9
  154. package/dist/server/bare/plugins/onnx-tts/plugin.js +85 -46
  155. package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +30 -60
  156. package/dist/server/bare/plugins/parakeet-transcription/plugin.js +49 -37
  157. package/dist/server/bare/plugins/sdcpp-generation/ops/diffusion.d.ts +3 -0
  158. package/dist/server/bare/plugins/sdcpp-generation/ops/diffusion.js +51 -0
  159. package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +278 -0
  160. package/dist/server/bare/plugins/sdcpp-generation/plugin.js +70 -0
  161. package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +38 -2
  162. package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +43 -22
  163. package/dist/server/bare/rag-hyperdb/handlers/ingest.js +2 -1
  164. package/dist/server/bare/rag-hyperdb/handlers/search.js +2 -1
  165. package/dist/server/bare/rag-hyperdb/rag-workspace-manager.js +7 -0
  166. package/dist/server/bare/registry/model-config-utils.js +3 -0
  167. package/dist/server/bare/registry/model-registry.d.ts +2 -2
  168. package/dist/server/bare/registry/model-registry.js +1 -1
  169. package/dist/server/bare/registry/registry-client.js +21 -0
  170. package/dist/server/bare/runtime-lifecycle.d.ts +28 -0
  171. package/dist/server/bare/runtime-lifecycle.js +132 -0
  172. package/dist/server/bare/types/addon-responses.d.ts +62 -0
  173. package/dist/server/bare/types/addon-responses.js +1 -0
  174. package/dist/server/env.js +3 -1
  175. package/dist/server/rpc/delegate-transport.js +1 -1
  176. package/dist/server/rpc/handle-request.js +50 -4
  177. package/dist/server/rpc/handler-registry.js +20 -1
  178. package/dist/server/rpc/handler-utils.d.ts +5 -3
  179. package/dist/server/rpc/handler-utils.js +52 -50
  180. package/dist/server/rpc/handlers/delete-cache.js +3 -0
  181. package/dist/server/rpc/handlers/diffusion-stream.d.ts +3 -0
  182. package/dist/server/rpc/handlers/diffusion-stream.js +4 -0
  183. package/dist/server/rpc/handlers/finetune.d.ts +3 -0
  184. package/dist/server/rpc/handlers/finetune.js +16 -0
  185. package/dist/server/rpc/handlers/index.d.ts +10 -0
  186. package/dist/server/rpc/handlers/index.js +10 -0
  187. package/dist/server/rpc/handlers/load-model/hyperdrive.js +24 -9
  188. package/dist/server/rpc/handlers/load-model/registry.d.ts +11 -0
  189. package/dist/server/rpc/handlers/load-model/registry.js +31 -4
  190. package/dist/server/rpc/handlers/plugin-dispatch.d.ts +1 -13
  191. package/dist/server/rpc/handlers/plugin-dispatch.js +21 -21
  192. package/dist/server/rpc/handlers/resume.d.ts +3 -0
  193. package/dist/server/rpc/handlers/resume.js +11 -0
  194. package/dist/server/rpc/handlers/suspend.d.ts +3 -0
  195. package/dist/server/rpc/handlers/suspend.js +11 -0
  196. package/dist/server/rpc/handlers/transcribe-stream.d.ts +1 -1
  197. package/dist/server/rpc/handlers/transcribe-stream.js +2 -2
  198. package/dist/server/rpc/handlers/transcribe.d.ts +3 -0
  199. package/dist/server/rpc/handlers/transcribe.js +4 -0
  200. package/dist/server/rpc/profiling/operation-metrics.js +107 -16
  201. package/dist/server/rpc/profiling/operation-wrappers.d.ts +1 -1
  202. package/dist/server/rpc/profiling/operation-wrappers.js +51 -19
  203. package/dist/server/rpc/progress-throttle.d.ts +8 -0
  204. package/dist/server/rpc/progress-throttle.js +47 -0
  205. package/dist/server/rpc/rpc-utils.d.ts +5 -0
  206. package/dist/server/rpc/rpc-utils.js +11 -0
  207. package/dist/server/utils/path-security.js +1 -1
  208. package/dist/server/utils/tool-integration.js +10 -0
  209. package/dist/server/utils/tool-parser.js +9 -5
  210. package/dist/server/worker-core.d.ts +2 -0
  211. package/dist/server/worker-core.js +26 -23
  212. package/dist/server/worker.js +2 -1
  213. package/dist/utils/encoding.d.ts +9 -0
  214. package/dist/utils/encoding.js +14 -0
  215. package/dist/utils/errors-server.d.ts +6 -0
  216. package/dist/utils/errors-server.js +11 -0
  217. package/package.json +81 -25
  218. package/dist/server/rpc/delegate-utils.d.ts +0 -2
  219. package/dist/server/rpc/delegate-utils.js +0 -5
@@ -1,54 +1,134 @@
1
- import { transcribeStreamResponseSchema, } from "../../schemas/index.js";
2
- import { stream } from "../rpc/rpc-client.js";
3
- /**
4
- * This function streams audio transcription results in real-time, yielding
5
- * text chunks as they become available from the model.
6
- *
7
- * @param params - The arguments for the transcription
8
- * @param params.modelId - The identifier of the transcription model to use
9
- * @param params.audioChunk - Audio input as either a file path (string) or audio buffer
10
- * @param params.prompt - Optional initial prompt to guide the transcription
11
- * @param options - Optional RPC options including per-call profiling
12
- * @yields {string} Text chunks as they are transcribed
13
- * @throws {QvacErrorBase} When transcription fails with an error message
14
- */
15
- export async function* transcribeStream(params, options) {
16
- const request = {
17
- type: "transcribeStream",
1
+ import { transcribeResponseSchema, transcribeStreamResponseSchema, } from "../../schemas/index.js";
2
+ import { stream, duplex } from "../rpc/rpc-client.js";
3
+ import { getClientLogger } from "../../logging/index.js";
4
+ import { TranscriptionFailedError } from "../../utils/errors-client.js";
5
+ const logger = getClientLogger();
6
+ function buildTranscribeRequest(params) {
7
+ return {
8
+ type: "transcribe",
18
9
  modelId: params.modelId,
19
10
  audioChunk: typeof params.audioChunk === "string"
20
11
  ? { type: "filePath", value: params.audioChunk }
21
12
  : { type: "base64", value: params.audioChunk.toString("base64") },
22
13
  ...(params.prompt && { prompt: params.prompt }),
23
14
  };
24
- for await (const response of stream(request, options)) {
25
- if (response.type === "transcribeStream") {
26
- const streamResponse = transcribeStreamResponseSchema.parse(response);
27
- if (streamResponse.text) {
28
- yield streamResponse.text;
29
- }
30
- if (streamResponse.done) {
31
- break;
32
- }
33
- }
34
- }
35
15
  }
36
16
  /**
37
- * This function provides a simple interface for transcribing audio by
38
- * collecting all streaming results into a single string response.
17
+ * Transcribe audio and return the complete text. Accepts either a file
18
+ * path or an audio buffer.
39
19
  *
40
- * @param params - The arguments for the transcription
41
20
  * @param params.modelId - The identifier of the transcription model to use
42
21
  * @param params.audioChunk - Audio input as either a file path (string) or audio buffer
43
22
  * @param params.prompt - Optional initial prompt to guide the transcription
44
23
  * @param options - Optional RPC options including per-call profiling
45
- * @returns {Promise<string>} The complete transcribed text
46
- * @throws {QvacErrorBase} When transcription fails (propagated from transcribeStream)
24
+ * @returns The complete transcribed text
47
25
  */
48
26
  export async function transcribe(params, options) {
27
+ const request = buildTranscribeRequest(params);
49
28
  let fullText = "";
50
- for await (const textChunk of transcribeStream(params, options)) {
51
- fullText += textChunk;
29
+ for await (const response of stream(request, options)) {
30
+ if (response.type === "transcribe") {
31
+ const parsed = transcribeResponseSchema.parse(response);
32
+ if (parsed.text) {
33
+ fullText += parsed.text;
34
+ }
35
+ if (parsed.done) {
36
+ break;
37
+ }
38
+ }
52
39
  }
53
40
  return fullText;
54
41
  }
42
+ export function transcribeStream(params, options) {
43
+ if ("audioChunk" in params && params.audioChunk !== undefined) {
44
+ logger.warn("transcribeStream() with audioChunk is deprecated — use transcribe() instead.");
45
+ return transcribeStreamWithAudio(params, options);
46
+ }
47
+ return transcribeStreamDuplex(params, options);
48
+ }
49
+ async function* transcribeStreamWithAudio(params, options) {
50
+ const request = buildTranscribeRequest(params);
51
+ for await (const response of stream(request, options)) {
52
+ if (response.type === "transcribe") {
53
+ const parsed = transcribeResponseSchema.parse(response);
54
+ if (parsed.text)
55
+ yield parsed.text;
56
+ if (parsed.done)
57
+ break;
58
+ }
59
+ }
60
+ }
61
+ async function transcribeStreamDuplex(params, options) {
62
+ const request = {
63
+ type: "transcribeStream",
64
+ modelId: params.modelId,
65
+ ...(params.prompt && { prompt: params.prompt }),
66
+ };
67
+ const { requestStream, responseStream } = await duplex(request, options);
68
+ const responses = parseResponseLines(responseStream);
69
+ let consumed = false;
70
+ return {
71
+ write(audioChunk) {
72
+ requestStream.write(audioChunk);
73
+ },
74
+ end() {
75
+ requestStream.end();
76
+ },
77
+ destroy() {
78
+ requestStream.destroy();
79
+ responseStream.destroy();
80
+ },
81
+ [Symbol.asyncIterator]() {
82
+ if (consumed) {
83
+ throw new TranscriptionFailedError("TranscribeStreamSession can only be iterated once");
84
+ }
85
+ consumed = true;
86
+ return responses;
87
+ },
88
+ };
89
+ }
90
+ async function* parseResponseLines(responseStream) {
91
+ let buf = "";
92
+ for await (const chunk of responseStream) {
93
+ buf += chunk.toString();
94
+ const lines = buf.split("\n");
95
+ buf = lines.pop() || "";
96
+ for (const line of lines) {
97
+ const result = processLine(line);
98
+ if (result === null)
99
+ return;
100
+ if (result !== undefined)
101
+ yield result;
102
+ }
103
+ }
104
+ // Process any residual data after stream ends
105
+ if (buf.trim()) {
106
+ const result = processLine(buf);
107
+ if (result !== null && result !== undefined)
108
+ yield result;
109
+ }
110
+ }
111
+ function processLine(line) {
112
+ if (!line.trim())
113
+ return undefined;
114
+ let parsed;
115
+ try {
116
+ parsed = JSON.parse(line);
117
+ }
118
+ catch {
119
+ logger.warn("transcribeStream: malformed JSON from server:", line);
120
+ return undefined;
121
+ }
122
+ const obj = parsed;
123
+ if (obj["type"] === "error") {
124
+ throw new TranscriptionFailedError(obj["message"] ?? "Unknown server error");
125
+ }
126
+ const response = transcribeStreamResponseSchema.parse(parsed);
127
+ if (response.error)
128
+ throw new TranscriptionFailedError(response.error);
129
+ if (response.done)
130
+ return null;
131
+ if (response.text?.trim())
132
+ return response.text;
133
+ return undefined;
134
+ }
@@ -7,5 +7,9 @@ export declare function getRPC(): Promise<{
7
7
  createResponseStream(): AsyncGenerator<Buffer<ArrayBuffer>, void, unknown>;
8
8
  };
9
9
  }>;
10
- export declare function close(): void;
10
+ export declare function close(): Promise<void>;
11
+ export declare function createDuplexSession(payload: string, _commandId: number): Promise<{
12
+ requestStream: import("bare-stream").Transform<import("bare-stream").TransformEvents>;
13
+ responseStream: import("bare-stream").Transform<import("bare-stream").TransformEvents>;
14
+ }>;
11
15
  //# sourceMappingURL=bare-client.d.ts.map
@@ -1,6 +1,8 @@
1
1
  import { normalizeModelType } from "../../schemas/index.js";
2
2
  import os from "bare-os";
3
3
  import { handlers } from "../../server/rpc/handlers/index.js";
4
+ import { registry } from "../../server/rpc/handler-registry.js";
5
+ import { createErrorResponse } from "../../schemas/index.js";
4
6
  import { PearWorkerEntryRequiredError, RPCNoHandlerError, RPCRequestNotSentError, } from "../../utils/errors-client.js";
5
7
  import { initializeConfig } from "../init-hooks.js";
6
8
  import { setSDKConfig } from "../../server/bare/registry/config-registry.js";
@@ -9,7 +11,7 @@ import { resolveModelConfig } from "../../server/bare/registry/model-config-regi
9
11
  import { resolveConfig } from "../config-loader/resolve-config.bare.js";
10
12
  import { getClientLogger } from "../../logging/index.js";
11
13
  import { getAllPlugins } from "../../server/plugins/index.js";
12
- import { initializeWorkerCore } from "../../server/worker-core.js";
14
+ import { initializeWorkerCore, shutdownBareDirectWorker, } from "../../server/worker-core.js";
13
15
  const logger = getClientLogger();
14
16
  /**
15
17
  * Load worker entry to register plugins before any SDK calls.
@@ -67,6 +69,37 @@ function applyDeviceDefaultsToLoadModel(request) {
67
69
  const configWithDefaults = resolveModelConfig(canonicalType, rawConfig);
68
70
  return { ...request, modelConfig: configWithDefaults };
69
71
  }
72
+ function supportsProgressStreaming(request) {
73
+ return ("withProgress" in request &&
74
+ request.withProgress &&
75
+ ["loadModel", "downloadAsset", "rag", "finetune"].includes(request.type));
76
+ }
77
+ async function* streamWithProgress(request, handler) {
78
+ const queue = [];
79
+ const errors = [];
80
+ let done = false;
81
+ handler(request, (update) => queue.push(update))
82
+ .then((final) => {
83
+ queue.push(final);
84
+ done = true;
85
+ })
86
+ .catch((error) => {
87
+ errors.push(error);
88
+ done = true;
89
+ });
90
+ while (!done || queue.length > 0) {
91
+ if (queue.length > 0) {
92
+ yield queue.shift();
93
+ }
94
+ else {
95
+ await new Promise((resolve) => setTimeout(resolve, 10));
96
+ }
97
+ }
98
+ const handlerError = errors[0];
99
+ if (handlerError) {
100
+ throw handlerError;
101
+ }
102
+ }
70
103
  export async function send(request) {
71
104
  const handler = getHandler(request.type);
72
105
  if (!handler)
@@ -78,61 +111,9 @@ async function* stream(request) {
78
111
  const handler = getHandler(request.type);
79
112
  if (!handler)
80
113
  throw new RPCNoHandlerError(request.type);
81
- // Special handling for loadModel with progress
82
- if (request.type === "loadModel" &&
83
- "withProgress" in request &&
84
- request.withProgress) {
114
+ if (supportsProgressStreaming(request)) {
85
115
  const processedRequest = applyDeviceDefaultsToLoadModel(request);
86
- async function* streamWithProgress() {
87
- const queue = [];
88
- let done = false;
89
- const loadModelHandler = handler;
90
- loadModelHandler(processedRequest, (update) => queue.push(update))
91
- .then((final) => {
92
- queue.push(final);
93
- done = true;
94
- })
95
- .catch((error) => {
96
- done = true;
97
- throw error;
98
- });
99
- while (!done || queue.length > 0) {
100
- if (queue.length > 0) {
101
- yield queue.shift();
102
- }
103
- else {
104
- await new Promise((resolve) => setTimeout(resolve, 10));
105
- }
106
- }
107
- }
108
- yield* streamWithProgress();
109
- }
110
- else if (request.type === "downloadAsset" &&
111
- "withProgress" in request &&
112
- request.withProgress) {
113
- async function* streamWithProgress() {
114
- const queue = [];
115
- let done = false;
116
- const downloadAssetHandler = handler;
117
- downloadAssetHandler(request, (update) => queue.push(update))
118
- .then((final) => {
119
- queue.push(final);
120
- done = true;
121
- })
122
- .catch((error) => {
123
- done = true;
124
- throw error;
125
- });
126
- while (!done || queue.length > 0) {
127
- if (queue.length > 0) {
128
- yield queue.shift();
129
- }
130
- else {
131
- await new Promise((resolve) => setTimeout(resolve, 10));
132
- }
133
- }
134
- }
135
- yield* streamWithProgress();
116
+ yield* streamWithProgress(processedRequest, handler);
136
117
  }
137
118
  else {
138
119
  const result = handler(request);
@@ -214,6 +195,38 @@ export async function getRPC() {
214
195
  }
215
196
  return mockRPC;
216
197
  }
217
- export function close() {
218
- // noop
198
+ export async function close() {
199
+ await shutdownBareDirectWorker("rpc-close");
200
+ }
201
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
202
+ export async function createDuplexSession(payload, _commandId) {
203
+ await getRPC();
204
+ const { PassThrough } = await import("bare-stream");
205
+ const request = JSON.parse(payload);
206
+ const entry = registry[request.type];
207
+ if (!entry || entry.type !== "duplex") {
208
+ throw new RPCNoHandlerError(request.type);
209
+ }
210
+ const inputStream = new PassThrough();
211
+ const outputStream = new PassThrough();
212
+ const duplexHandler = entry.handler;
213
+ void (async () => {
214
+ try {
215
+ for await (const response of duplexHandler(request, inputStream)) {
216
+ outputStream.write(JSON.stringify(response) + "\n", "utf-8");
217
+ }
218
+ }
219
+ catch (error) {
220
+ inputStream.destroy();
221
+ const errorResponse = createErrorResponse(error);
222
+ outputStream.write(JSON.stringify(errorResponse) + "\n", "utf-8");
223
+ }
224
+ finally {
225
+ outputStream.end();
226
+ }
227
+ })();
228
+ return {
229
+ requestStream: inputStream,
230
+ responseStream: outputStream,
231
+ };
219
232
  }
@@ -1,4 +1,8 @@
1
1
  import RPC from "bare-rpc";
2
2
  export declare function getRPC(): Promise<RPC>;
3
3
  export declare function close(): void;
4
+ export declare function createDuplexSession(payload: string, commandId: number): Promise<{
5
+ requestStream: RPC.OutgoingStream;
6
+ responseStream: RPC.IncomingStream;
7
+ }>;
4
8
  //# sourceMappingURL=expo-rpc-client.d.ts.map
@@ -98,3 +98,11 @@ export function close() {
98
98
  rpcInstance = null;
99
99
  rpcPromise = null;
100
100
  }
101
+ export async function createDuplexSession(payload, commandId) {
102
+ const rpc = await getRPC();
103
+ const req = rpc.request(commandId);
104
+ const requestStream = req.createRequestStream();
105
+ const responseStream = req.createResponseStream({ encoding: "utf-8" });
106
+ requestStream.write(payload, "utf-8");
107
+ return { requestStream, responseStream };
108
+ }
@@ -1,4 +1,8 @@
1
1
  import RPC from "bare-rpc";
2
2
  export declare function getRPC(): Promise<RPC>;
3
+ export declare function createDuplexSession(payload: string, commandId: number): Promise<{
4
+ requestStream: RPC.OutgoingStream;
5
+ responseStream: RPC.IncomingStream;
6
+ }>;
3
7
  export declare function close(): Promise<void>;
4
8
  //# sourceMappingURL=node-rpc-client.d.ts.map
@@ -204,6 +204,14 @@ async function ensureRPC() {
204
204
  export async function getRPC() {
205
205
  return ensureRPC();
206
206
  }
207
+ export async function createDuplexSession(payload, commandId) {
208
+ const rpc = await ensureRPC();
209
+ const req = rpc.request(commandId);
210
+ const requestStream = req.createRequestStream();
211
+ const responseStream = req.createResponseStream({ encoding: "utf-8" });
212
+ requestStream.write(payload, "utf-8");
213
+ return { requestStream, responseStream };
214
+ }
207
215
  export async function close() {
208
216
  if (closePromise) {
209
217
  await closePromise;
@@ -2,5 +2,18 @@ import type RPC from "bare-rpc";
2
2
  import { type Request, type Response, type RPCOptions } from "../../schemas/index";
3
3
  export declare function send<T extends Request>(request: T, options?: RPCOptions, rpc?: RPC): Promise<Response>;
4
4
  export declare function stream<T extends Request>(request: T, options?: RPCOptions, rpc?: RPC): AsyncGenerator<Response>;
5
+ export interface DuplexWritable {
6
+ write(chunk: Buffer): void;
7
+ end(): void;
8
+ destroy(): void;
9
+ }
10
+ export interface DuplexReadable extends AsyncIterable<Buffer> {
11
+ destroy(): void;
12
+ }
13
+ export interface DuplexSession {
14
+ requestStream: DuplexWritable;
15
+ responseStream: DuplexReadable;
16
+ }
17
+ export declare function duplex<T extends Request>(request: T, options?: RPCOptions): Promise<DuplexSession>;
5
18
  export declare function close(): Promise<void>;
6
19
  //# sourceMappingURL=rpc-client.d.ts.map
@@ -2,7 +2,7 @@ import { requestSchema, responseSchema, PROFILING_TRAILER_KEY, } from "../../sch
2
2
  import { RPCError } from "./rpc-error.js";
3
3
  import { withTimeout, withTimeoutStream } from "../../utils/withTimeout.js";
4
4
  import { getClientLogger, summarizeRequest } from "../../logging/index.js";
5
- import { getRPC, close as closeRPC } from "#rpc";
5
+ import { getRPC, close as closeRPC, createDuplexSession } from "#rpc";
6
6
  import { nowMs, shouldProfile, shouldIncludeServerBreakdown, generateId as createProfileId, createProfilingMeta, createProfilingDisabledMeta, injectProfilingMetaIntoObject, extractProfilingMeta, stripProfilingMeta, recordFailure, } from "../../profiling/index.js";
7
7
  import { createClientTimings, createClientStreamTimings, recordClientEvents, recordClientStreamEvents, cacheConnectionTime, flushConnectionTime, resetConnectionTracking, } from "./profiling/index.js";
8
8
  const logger = getClientLogger();
@@ -222,6 +222,136 @@ async function* streamProfiled(request, rpc, options = {}) {
222
222
  }
223
223
  }
224
224
  }
225
+ export async function duplex(request, options) {
226
+ const ctx = await prepareRPCContext(request.type, options?.profiling);
227
+ if (!ctx.profilingEnabled) {
228
+ return duplexBase(request, ctx.signalDisable, options?.timeout);
229
+ }
230
+ return duplexProfiled(request, options);
231
+ }
232
+ async function duplexBase(request, signalDisable, timeout) {
233
+ const parsedRequest = requestSchema.parse(request);
234
+ logger.debug("RPC Client duplex:", summarizeRequest(request));
235
+ const payloadObj = signalDisable
236
+ ? injectProfilingMetaIntoObject(parsedRequest, createProfilingDisabledMeta())
237
+ : parsedRequest;
238
+ const payload = JSON.stringify(payloadObj);
239
+ const sessionPromise = createDuplexSession(payload, getNextCommandId());
240
+ const session = await withTimeout(sessionPromise, timeout);
241
+ return {
242
+ requestStream: session.requestStream,
243
+ responseStream: session.responseStream,
244
+ };
245
+ }
246
+ async function duplexProfiled(request, options = {}) {
247
+ const requestType = request.type;
248
+ const profileId = createProfileId();
249
+ const includeServer = shouldIncludeServerBreakdown(options?.profiling);
250
+ const timings = createClientStreamTimings(profileId, requestType);
251
+ let session;
252
+ try {
253
+ const zodStart = nowMs();
254
+ const parsedRequest = requestSchema.parse(request);
255
+ timings.requestZodValidationMs = nowMs() - zodStart;
256
+ logger.debug("RPC Client duplex:", summarizeRequest(request));
257
+ const requestMeta = createProfilingMeta(profileId, includeServer);
258
+ const requestWithMeta = injectProfilingMetaIntoObject(parsedRequest, requestMeta);
259
+ const stringifyStart = nowMs();
260
+ const payload = JSON.stringify(requestWithMeta);
261
+ timings.requestStringifyMs = nowMs() - stringifyStart;
262
+ timings.sendStart = nowMs();
263
+ const sessionPromise = createDuplexSession(payload, getNextCommandId());
264
+ session = await withTimeout(sessionPromise, options?.timeout);
265
+ }
266
+ catch (error) {
267
+ const base = {
268
+ ts: nowMs(),
269
+ op: timings.requestType,
270
+ kind: "rpc",
271
+ profileId: timings.profileId,
272
+ };
273
+ recordFailure(base, timings.requestStart, error);
274
+ throw error;
275
+ }
276
+ let profilingMeta = undefined;
277
+ const rawReadable = session.responseStream;
278
+ async function* profiledResponseStream() {
279
+ let lineBuffer = "";
280
+ try {
281
+ for await (const chunk of rawReadable) {
282
+ const chunkTime = nowMs();
283
+ if (timings.firstChunkAt === undefined) {
284
+ timings.firstChunkAt = chunkTime;
285
+ }
286
+ timings.lastChunkAt = chunkTime;
287
+ lineBuffer += chunk.toString();
288
+ const lines = lineBuffer.split("\n");
289
+ lineBuffer = lines.pop() || "";
290
+ const outputParts = [];
291
+ for (const line of lines) {
292
+ if (!line.trim())
293
+ continue;
294
+ let parsed;
295
+ try {
296
+ parsed = JSON.parse(line);
297
+ }
298
+ catch {
299
+ outputParts.push(line);
300
+ continue;
301
+ }
302
+ const chunkMeta = extractProfilingMeta(parsed);
303
+ if (chunkMeta) {
304
+ profilingMeta = chunkMeta;
305
+ }
306
+ if (parsed[PROFILING_TRAILER_KEY] === true) {
307
+ continue;
308
+ }
309
+ // Yield original line — consumer's Zod .parse() strips profiling keys
310
+ outputParts.push(line);
311
+ }
312
+ if (outputParts.length > 0) {
313
+ timings.chunkCount += outputParts.length;
314
+ yield Buffer.from(outputParts.join("\n") + "\n");
315
+ }
316
+ }
317
+ if (lineBuffer.trim()) {
318
+ timings.chunkCount++;
319
+ yield Buffer.from(lineBuffer + "\n");
320
+ }
321
+ }
322
+ catch (error) {
323
+ if (timings.requestEnd === undefined) {
324
+ const base = {
325
+ ts: nowMs(),
326
+ op: timings.requestType,
327
+ kind: "rpc",
328
+ profileId: timings.profileId,
329
+ };
330
+ recordFailure(base, timings.requestStart, error);
331
+ }
332
+ throw error;
333
+ }
334
+ finally {
335
+ if (timings.chunkCount > 0) {
336
+ timings.requestEnd = nowMs();
337
+ recordClientStreamEvents(timings, profilingMeta);
338
+ }
339
+ }
340
+ }
341
+ const generator = profiledResponseStream();
342
+ const wrappedResponseStream = {
343
+ [Symbol.asyncIterator]() {
344
+ return generator[Symbol.asyncIterator]();
345
+ },
346
+ destroy() {
347
+ rawReadable.destroy();
348
+ },
349
+ };
350
+ return {
351
+ requestStream: session.requestStream,
352
+ responseStream: wrappedResponseStream,
353
+ };
354
+ }
225
355
  export async function close() {
226
356
  if (!rpcInstance)
227
357
  return;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=composite.d.ts.map
@@ -0,0 +1,99 @@
1
+ import { spawn } from "child_process";
2
+ import { join, dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { completion, loadModel, close, LLAMA_3_2_1B_INST_Q4_0, } from "@qvac/sdk";
5
+ // ── Config ──────────────────────────────────────────────────────────
6
+ // Any 64-char hex string works as a topic — it's a shared secret that
7
+ // lets the consumer discover the provider on Hyperswarm.
8
+ const TOPIC = "66646f696865726f6569686a726530776a66646f696865726f6569686a726530";
9
+ const PROVIDER_STARTUP_TIMEOUT_MS = 30_000;
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ const providerScript = join(__dirname, "provider.ts");
12
+ // ── Provider lifecycle ──────────────────────────────────────────────
13
+ function spawnProviderProcess() {
14
+ const child = spawn("bun", ["run", providerScript, TOPIC], {
15
+ stdio: ["pipe", "pipe", "pipe"],
16
+ });
17
+ child.stderr?.on("data", (chunk) => {
18
+ process.stderr.write(chunk);
19
+ });
20
+ return child;
21
+ }
22
+ function terminateProvider(provider) {
23
+ if (!provider.killed) {
24
+ provider.kill("SIGTERM");
25
+ }
26
+ }
27
+ // The provider's Hyperswarm identity (and therefore its public key) is
28
+ // generated at startup — it can't be known ahead of time. We parse it
29
+ // from the provider's stdout where it prints:
30
+ // "🆔 Provider Public Key (unique): <hex>"
31
+ function waitForProviderPublicKey(provider) {
32
+ return new Promise((resolve, reject) => {
33
+ let output = "";
34
+ const timeout = setTimeout(() => {
35
+ reject(new Error("Provider did not emit its public key in time"));
36
+ }, PROVIDER_STARTUP_TIMEOUT_MS);
37
+ provider.stdout.on("data", (chunk) => {
38
+ const str = chunk.toString();
39
+ output += str;
40
+ process.stdout.write(str);
41
+ const match = output.match(/Provider Public Key \(unique\): ([a-f0-9]+)/i);
42
+ if (match) {
43
+ clearTimeout(timeout);
44
+ resolve(match[1]);
45
+ }
46
+ });
47
+ provider.on("error", (err) => {
48
+ clearTimeout(timeout);
49
+ reject(err);
50
+ });
51
+ provider.on("close", (code) => {
52
+ clearTimeout(timeout);
53
+ reject(new Error(`Provider exited unexpectedly (code ${String(code)})`));
54
+ });
55
+ });
56
+ }
57
+ // ── Consumer: delegated model load + completion ─────────────────────
58
+ async function runDelegatedCompletion(topic, providerPublicKey) {
59
+ console.log("→ Loading model via delegation...");
60
+ const modelId = await loadModel({
61
+ modelSrc: LLAMA_3_2_1B_INST_Q4_0,
62
+ modelType: "llm",
63
+ delegate: {
64
+ topic,
65
+ providerPublicKey,
66
+ timeout: 30_000,
67
+ },
68
+ onProgress: (progress) => {
69
+ console.log(` Download: ${progress.percentage.toFixed(1)}%`);
70
+ },
71
+ });
72
+ console.log(`✅ Model loaded: ${modelId}\n`);
73
+ console.log("→ Running delegated completion (streamed)...");
74
+ const response = completion({
75
+ modelId,
76
+ history: [{ role: "user", content: "Say hello in exactly 5 words." }],
77
+ stream: true,
78
+ });
79
+ process.stdout.write(" Response: ");
80
+ for await (const token of response.tokenStream) {
81
+ process.stdout.write(token);
82
+ }
83
+ const stats = await response.stats;
84
+ console.log(`\n📊 Stats: ${JSON.stringify(stats)}`);
85
+ }
86
+ // ── Main ────────────────────────────────────────────────────────────
87
+ const provider = spawnProviderProcess();
88
+ try {
89
+ console.log("🔧 Waiting for provider to start and announce its key...\n");
90
+ const publicKey = await waitForProviderPublicKey(provider);
91
+ console.log(`\n📡 Provider ready — key: ${publicKey}`);
92
+ console.log(`📡 Topic: ${TOPIC}\n`);
93
+ await runDelegatedCompletion(TOPIC, publicKey);
94
+ void close();
95
+ }
96
+ finally {
97
+ terminateProvider(provider);
98
+ }
99
+ process.exit(0);
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=diffusion-flux2-klein.d.ts.map