@qvac/sdk 0.8.3 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +267 -63
- package/README.md +1 -1
- package/dist/client/api/diffusion.d.ts +32 -0
- package/dist/client/api/diffusion.js +103 -0
- package/dist/client/api/embed.d.ts +9 -3
- package/dist/client/api/embed.js +4 -1
- package/dist/client/api/finetune.d.ts +69 -0
- package/dist/client/api/finetune.js +139 -0
- package/dist/client/api/get-model-info.d.ts +1 -1
- package/dist/client/api/index.d.ts +4 -0
- package/dist/client/api/index.js +4 -0
- package/dist/client/api/rag.d.ts +1 -1
- package/dist/client/api/rag.js +1 -1
- package/dist/client/api/resume.d.ts +14 -0
- package/dist/client/api/resume.js +20 -0
- package/dist/client/api/suspend.d.ts +13 -0
- package/dist/client/api/suspend.js +19 -0
- package/dist/client/api/transcribe.d.ts +25 -17
- package/dist/client/api/transcribe.js +115 -35
- package/dist/client/rpc/bare-client.d.ts +5 -1
- package/dist/client/rpc/bare-client.js +70 -57
- package/dist/client/rpc/expo-rpc-client.d.ts +4 -0
- package/dist/client/rpc/expo-rpc-client.js +8 -0
- package/dist/client/rpc/node-rpc-client.d.ts +4 -0
- package/dist/client/rpc/node-rpc-client.js +8 -0
- package/dist/client/rpc/rpc-client.d.ts +13 -0
- package/dist/client/rpc/rpc-client.js +131 -1
- package/dist/examples/delegated-inference/composite.d.ts +2 -0
- package/dist/examples/delegated-inference/composite.js +99 -0
- package/dist/examples/diffusion-flux2-klein.d.ts +2 -0
- package/dist/examples/diffusion-flux2-klein.js +46 -0
- package/dist/examples/diffusion-simple.d.ts +2 -0
- package/dist/examples/diffusion-simple.js +17 -0
- package/dist/examples/diffusion-txt2img.d.ts +2 -0
- package/dist/examples/diffusion-txt2img.js +40 -0
- package/dist/examples/embed-p2p.js +2 -2
- package/dist/examples/finetune/llamacpp-finetune.d.ts +2 -0
- package/dist/examples/finetune/llamacpp-finetune.js +78 -0
- package/dist/examples/logging-streaming.js +1 -1
- package/dist/examples/mcp-websearch.js +2 -0
- package/dist/examples/ocr-fasttext.js +11 -0
- package/dist/examples/profiling/per-call.js +3 -3
- package/dist/examples/rag/rag-chromadb.js +2 -2
- package/dist/examples/rag/rag-hyperdb/pipeline.js +1 -1
- package/dist/examples/rag/rag-lancedb.js +2 -2
- package/dist/examples/rag/rag-sqlite.js +2 -2
- package/dist/examples/suspend-resume.d.ts +2 -0
- package/dist/examples/suspend-resume.js +52 -0
- package/dist/examples/transcription/parakeet-ctc-filesystem.js +3 -5
- package/dist/examples/transcription/parakeet-microphone-record.js +42 -33
- package/dist/examples/transcription/parakeet-sortformer.js +13 -3
- package/dist/examples/transcription/parakeet-tdt-filesystem.js +5 -7
- package/dist/examples/transcription/whispercpp-filesystem-streaming.d.ts +2 -0
- package/dist/examples/transcription/whispercpp-filesystem-streaming.js +99 -0
- package/dist/examples/transcription/whispercpp-microphone-record.js +78 -299
- package/dist/examples/transcription/whispercpp-prompt.js +11 -0
- package/dist/examples/translation/translation-llm.js +2 -2
- package/dist/examples/tts/supertonic-multilingual.d.ts +2 -0
- package/dist/examples/tts/supertonic-multilingual.js +56 -0
- package/dist/examples/tts/supertonic.js +12 -7
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/logging/utils.js +1 -1
- package/dist/models/registry/models.d.ts +15176 -4264
- package/dist/models/registry/models.js +18657 -7404
- package/dist/models/update-models/codegen.js +2 -2
- package/dist/models/update-models/naming.js +26 -0
- package/dist/pear/pre.js +2 -0
- package/dist/polyfills/bare-globals.d.ts +2 -0
- package/dist/polyfills/bare-globals.js +4 -0
- package/dist/polyfills/bare-globals.noop.d.ts +2 -0
- package/dist/polyfills/bare-globals.noop.js +1 -0
- package/dist/profiling/exporters.js +16 -11
- package/dist/profiling/model-execution.d.ts +16 -0
- package/dist/profiling/model-execution.js +33 -0
- package/dist/schemas/common.d.ts +967 -198
- package/dist/schemas/common.js +16 -1
- package/dist/schemas/completion-stream.d.ts +14 -6
- package/dist/schemas/completion-stream.js +4 -3
- package/dist/schemas/download-asset.d.ts +7 -3
- package/dist/schemas/embed.d.ts +19 -0
- package/dist/schemas/embed.js +7 -0
- package/dist/schemas/engine-addon-map.js +4 -1
- package/dist/schemas/finetune.d.ts +620 -0
- package/dist/schemas/finetune.js +161 -0
- package/dist/schemas/get-model-info.d.ts +2 -0
- package/dist/schemas/get-model-info.js +1 -0
- package/dist/schemas/index.d.ts +5 -1
- package/dist/schemas/index.js +5 -1
- package/dist/schemas/llamacpp-config.d.ts +26 -6
- package/dist/schemas/llamacpp-config.js +12 -0
- package/dist/schemas/load-model.d.ts +4005 -1400
- package/dist/schemas/load-model.js +34 -1
- package/dist/schemas/model-src-utils.d.ts +10 -2
- package/dist/schemas/model-types.d.ts +19 -1
- package/dist/schemas/model-types.js +11 -0
- package/dist/schemas/ocr.d.ts +2 -0
- package/dist/schemas/plugin.d.ts +24 -3
- package/dist/schemas/plugin.js +14 -0
- package/dist/schemas/profiling.d.ts +4 -0
- package/dist/schemas/profiling.js +4 -0
- package/dist/schemas/registry.d.ts +18 -0
- package/dist/schemas/registry.js +2 -0
- package/dist/schemas/reload-config.d.ts +9 -1
- package/dist/schemas/resume.d.ts +10 -0
- package/dist/schemas/resume.js +7 -0
- package/dist/schemas/sdcpp-config.d.ts +327 -0
- package/dist/schemas/sdcpp-config.js +102 -0
- package/dist/schemas/sdk-config.d.ts +1225 -235
- package/dist/schemas/sdk-config.js +4 -0
- package/dist/schemas/sdk-errors-server.d.ts +2 -0
- package/dist/schemas/sdk-errors-server.js +12 -0
- package/dist/schemas/suspend.d.ts +10 -0
- package/dist/schemas/suspend.js +7 -0
- package/dist/schemas/text-to-speech.d.ts +187 -10
- package/dist/schemas/text-to-speech.js +12 -4
- package/dist/schemas/transcription-config.d.ts +16 -56
- package/dist/schemas/transcription-config.js +0 -2
- package/dist/schemas/transcription.d.ts +66 -6
- package/dist/schemas/transcription.js +29 -7
- package/dist/schemas/translate.d.ts +14 -4
- package/dist/schemas/translate.js +10 -2
- package/dist/schemas/translation-config.d.ts +30 -10
- package/dist/server/bare/delegate-rpc-client.js +3 -3
- package/dist/server/bare/hyperswarm.js +15 -3
- package/dist/server/bare/ops/embed.d.ts +8 -4
- package/dist/server/bare/ops/embed.js +15 -4
- package/dist/server/bare/ops/transcribe.d.ts +6 -2
- package/dist/server/bare/ops/transcribe.js +53 -1
- package/dist/server/bare/ops/translate.d.ts +4 -1
- package/dist/server/bare/ops/translate.js +28 -65
- package/dist/server/bare/ops/unload-model.js +2 -0
- package/dist/server/bare/plugins/index.d.ts +1 -0
- package/dist/server/bare/plugins/index.js +1 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +3 -1
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +45 -9
- package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.d.ts +11 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +112 -0
- package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +175 -5
- package/dist/server/bare/plugins/llamacpp-completion/plugin.js +45 -35
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +11 -1
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +9 -4
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +17 -2
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +16 -15
- package/dist/server/bare/plugins/onnx-ocr/ops/ocr-stream.d.ts +5 -7
- package/dist/server/bare/plugins/onnx-ocr/ops/ocr-stream.js +11 -3
- package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +2 -0
- package/dist/server/bare/plugins/onnx-ocr/plugin.js +24 -19
- package/dist/server/bare/plugins/onnx-tts/ops/text-to-speech.d.ts +7 -2
- package/dist/server/bare/plugins/onnx-tts/ops/text-to-speech.js +22 -97
- package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +98 -9
- package/dist/server/bare/plugins/onnx-tts/plugin.js +85 -46
- package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +30 -60
- package/dist/server/bare/plugins/parakeet-transcription/plugin.js +49 -37
- package/dist/server/bare/plugins/sdcpp-generation/ops/diffusion.d.ts +3 -0
- package/dist/server/bare/plugins/sdcpp-generation/ops/diffusion.js +51 -0
- package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +278 -0
- package/dist/server/bare/plugins/sdcpp-generation/plugin.js +70 -0
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +38 -2
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +43 -22
- package/dist/server/bare/rag-hyperdb/handlers/ingest.js +2 -1
- package/dist/server/bare/rag-hyperdb/handlers/search.js +2 -1
- package/dist/server/bare/rag-hyperdb/rag-workspace-manager.js +7 -0
- package/dist/server/bare/registry/model-config-utils.js +3 -0
- package/dist/server/bare/registry/model-registry.d.ts +2 -2
- package/dist/server/bare/registry/model-registry.js +1 -1
- package/dist/server/bare/registry/registry-client.js +21 -0
- package/dist/server/bare/runtime-lifecycle.d.ts +28 -0
- package/dist/server/bare/runtime-lifecycle.js +132 -0
- package/dist/server/bare/types/addon-responses.d.ts +62 -0
- package/dist/server/bare/types/addon-responses.js +1 -0
- package/dist/server/env.js +3 -1
- package/dist/server/rpc/delegate-transport.js +1 -1
- package/dist/server/rpc/handle-request.js +50 -4
- package/dist/server/rpc/handler-registry.js +20 -1
- package/dist/server/rpc/handler-utils.d.ts +5 -3
- package/dist/server/rpc/handler-utils.js +52 -50
- package/dist/server/rpc/handlers/delete-cache.js +3 -0
- package/dist/server/rpc/handlers/diffusion-stream.d.ts +3 -0
- package/dist/server/rpc/handlers/diffusion-stream.js +4 -0
- package/dist/server/rpc/handlers/finetune.d.ts +3 -0
- package/dist/server/rpc/handlers/finetune.js +16 -0
- package/dist/server/rpc/handlers/index.d.ts +10 -0
- package/dist/server/rpc/handlers/index.js +10 -0
- package/dist/server/rpc/handlers/load-model/hyperdrive.js +24 -9
- package/dist/server/rpc/handlers/load-model/registry.d.ts +11 -0
- package/dist/server/rpc/handlers/load-model/registry.js +31 -4
- package/dist/server/rpc/handlers/plugin-dispatch.d.ts +1 -13
- package/dist/server/rpc/handlers/plugin-dispatch.js +21 -21
- package/dist/server/rpc/handlers/resume.d.ts +3 -0
- package/dist/server/rpc/handlers/resume.js +11 -0
- package/dist/server/rpc/handlers/suspend.d.ts +3 -0
- package/dist/server/rpc/handlers/suspend.js +11 -0
- package/dist/server/rpc/handlers/transcribe-stream.d.ts +1 -1
- package/dist/server/rpc/handlers/transcribe-stream.js +2 -2
- package/dist/server/rpc/handlers/transcribe.d.ts +3 -0
- package/dist/server/rpc/handlers/transcribe.js +4 -0
- package/dist/server/rpc/profiling/operation-metrics.js +107 -16
- package/dist/server/rpc/profiling/operation-wrappers.d.ts +1 -1
- package/dist/server/rpc/profiling/operation-wrappers.js +51 -19
- package/dist/server/rpc/progress-throttle.d.ts +8 -0
- package/dist/server/rpc/progress-throttle.js +47 -0
- package/dist/server/rpc/rpc-utils.d.ts +5 -0
- package/dist/server/rpc/rpc-utils.js +11 -0
- package/dist/server/utils/path-security.js +1 -1
- package/dist/server/utils/tool-integration.js +10 -0
- package/dist/server/utils/tool-parser.js +9 -5
- package/dist/server/worker-core.d.ts +2 -0
- package/dist/server/worker-core.js +26 -23
- package/dist/server/worker.js +2 -1
- package/dist/utils/encoding.d.ts +9 -0
- package/dist/utils/encoding.js +14 -0
- package/dist/utils/errors-server.d.ts +6 -0
- package/dist/utils/errors-server.js +11 -0
- package/package.json +36 -25
- package/dist/server/rpc/delegate-utils.d.ts +0 -2
- 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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
*
|
|
38
|
-
*
|
|
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
|
|
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
|
|
51
|
-
|
|
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
|
-
|
|
82
|
-
if (request.type === "loadModel" &&
|
|
83
|
-
"withProgress" in request &&
|
|
84
|
-
request.withProgress) {
|
|
114
|
+
if (supportsProgressStreaming(request)) {
|
|
85
115
|
const processedRequest = applyDeviceDefaultsToLoadModel(request);
|
|
86
|
-
|
|
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
|
-
|
|
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,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);
|