@qvac/sdk 0.7.1 → 0.8.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 +73 -10
- package/README.md +4 -4
- package/bare-imports.json +9 -0
- package/dist/client/api/cancel.d.ts +9 -0
- package/dist/client/api/cancel.js +9 -0
- package/dist/client/api/completion-stream.d.ts +2 -1
- package/dist/client/api/completion-stream.js +2 -1
- package/dist/client/api/download-asset.d.ts +3 -2
- package/dist/client/api/download-asset.js +4 -3
- package/dist/client/api/embed.d.ts +5 -2
- package/dist/client/api/embed.js +2 -2
- package/dist/client/api/get-model-info.d.ts +1 -1
- package/dist/client/api/heartbeat.d.ts +30 -0
- package/dist/client/api/heartbeat.js +38 -0
- package/dist/client/api/index.d.ts +1 -1
- package/dist/client/api/index.js +1 -1
- package/dist/client/api/invoke-plugin.d.ts +3 -2
- package/dist/client/api/invoke-plugin.js +4 -4
- package/dist/client/api/load-model.d.ts +12 -10
- package/dist/client/api/load-model.js +3 -3
- package/dist/client/api/rag.d.ts +10 -10
- package/dist/client/api/rag.js +21 -21
- package/dist/client/api/text-to-speech.d.ts +2 -2
- package/dist/client/api/text-to-speech.js +3 -3
- package/dist/client/api/transcribe.d.ts +5 -3
- package/dist/client/api/transcribe.js +6 -4
- package/dist/client/api/translate.d.ts +2 -2
- package/dist/client/api/translate.js +3 -3
- package/dist/client/rpc/node-rpc-client.d.ts +2 -7
- package/dist/client/rpc/node-rpc-client.js +2 -39
- package/dist/client/rpc/profiling/index.d.ts +2 -0
- package/dist/client/rpc/profiling/index.js +1 -0
- package/dist/client/rpc/profiling/profiler.d.ts +27 -0
- package/dist/client/rpc/profiling/profiler.js +139 -0
- package/dist/client/rpc/rpc-client.d.ts +2 -2
- package/dist/client/rpc/rpc-client.js +174 -12
- package/dist/examples/delegated-inference/consumer-profiled.d.ts +2 -0
- package/dist/examples/delegated-inference/consumer-profiled.js +67 -0
- package/dist/examples/embed-p2p.d.ts +2 -0
- package/dist/examples/llamacpp-multimodal.js +1 -1
- package/dist/examples/llamacpp-p2p.d.ts +2 -0
- package/dist/examples/parallel-download.js +45 -13
- package/dist/examples/profiling/basic.d.ts +2 -0
- package/dist/examples/profiling/basic.js +71 -0
- package/dist/examples/profiling/per-call.d.ts +2 -0
- package/dist/examples/profiling/per-call.js +30 -0
- package/dist/examples/seed-p2p.d.ts +2 -0
- package/dist/examples/transcription/parakeet-ctc-filesystem.d.ts +2 -0
- package/dist/examples/transcription/parakeet-ctc-filesystem.js +40 -0
- package/dist/examples/transcription/parakeet-microphone-record.d.ts +2 -0
- package/dist/examples/transcription/parakeet-microphone-record.js +95 -0
- package/dist/examples/transcription/parakeet-sortformer.d.ts +2 -0
- package/dist/examples/transcription/parakeet-sortformer.js +115 -0
- package/dist/examples/transcription/parakeet-tdt-filesystem.d.ts +2 -0
- package/dist/examples/transcription/parakeet-tdt-filesystem.js +44 -0
- package/dist/examples/translation/translation-bergamot-pivot.d.ts +2 -0
- package/dist/examples/translation/translation-bergamot-pivot.js +73 -0
- package/dist/examples/translation/translation-llm-afriquegemma.d.ts +2 -0
- package/dist/examples/translation/translation-llm-afriquegemma.js +39 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/logging/index.d.ts +1 -1
- package/dist/logging/index.js +1 -1
- package/dist/logging/namespaces.d.ts +3 -9
- package/dist/logging/namespaces.js +2 -8
- package/dist/logging/registry.js +15 -1
- package/dist/models/registry/models.d.ts +1671 -310
- package/dist/models/registry/models.js +2116 -710
- package/dist/models/update-models/codegen.js +2 -2
- package/dist/models/update-models/history.d.ts +11 -0
- package/dist/models/update-models/history.js +24 -5
- package/dist/models/update-models/index.js +17 -16
- package/dist/models/update-models/naming.js +56 -0
- package/dist/profiling/aggregator.d.ts +15 -0
- package/dist/profiling/aggregator.js +100 -0
- package/dist/profiling/clock.d.ts +12 -0
- package/dist/profiling/clock.js +51 -0
- package/dist/profiling/controller.d.ts +33 -0
- package/dist/profiling/controller.js +123 -0
- package/dist/profiling/envelope.d.ts +10 -0
- package/dist/profiling/envelope.js +36 -0
- package/dist/profiling/events.d.ts +20 -0
- package/dist/profiling/events.js +37 -0
- package/dist/profiling/exporters.d.ts +10 -0
- package/dist/profiling/exporters.js +236 -0
- package/dist/profiling/index.d.ts +36 -0
- package/dist/profiling/index.js +31 -0
- package/dist/profiling/ring-buffer.d.ts +19 -0
- package/dist/profiling/ring-buffer.js +83 -0
- package/dist/profiling/types.d.ts +47 -0
- package/dist/profiling/types.js +1 -0
- package/dist/schemas/cancel.d.ts +12 -0
- package/dist/schemas/cancel.js +2 -0
- package/dist/schemas/common.d.ts +1927 -135
- package/dist/schemas/common.js +6 -3
- package/dist/schemas/completion-stream.d.ts +31 -0
- package/dist/schemas/completion-stream.js +13 -0
- package/dist/schemas/delegate.d.ts +8 -0
- package/dist/schemas/delegate.js +5 -2
- package/dist/schemas/download-asset.d.ts +14 -3
- package/dist/schemas/download-asset.js +3 -1
- package/dist/schemas/engine-addon-map.js +9 -6
- package/dist/schemas/get-model-info.d.ts +2 -0
- package/dist/schemas/get-model-info.js +1 -0
- package/dist/schemas/heartbeat.d.ts +17 -0
- package/dist/schemas/heartbeat.js +10 -0
- package/dist/schemas/index.d.ts +3 -2
- package/dist/schemas/index.js +3 -2
- package/dist/schemas/llamacpp-config.d.ts +140 -14
- package/dist/schemas/llamacpp-config.js +11 -7
- package/dist/schemas/load-model.d.ts +9762 -2515
- package/dist/schemas/load-model.js +142 -182
- 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 +37 -0
- package/dist/schemas/ocr.js +5 -0
- package/dist/schemas/plugin.d.ts +51 -5
- package/dist/schemas/plugin.js +26 -0
- package/dist/schemas/profiling.d.ts +105 -0
- package/dist/schemas/profiling.js +66 -0
- package/dist/schemas/registry.d.ts +18 -0
- package/dist/schemas/registry.js +2 -0
- package/dist/schemas/reload-config.d.ts +126 -6
- package/dist/schemas/reload-config.js +1 -1
- package/dist/schemas/sdk-config.d.ts +2280 -24
- package/dist/schemas/sdk-config.js +6 -1
- package/dist/schemas/sdk-errors-client.d.ts +1 -0
- package/dist/schemas/sdk-errors-client.js +7 -0
- package/dist/schemas/sdk-errors-server.d.ts +3 -0
- package/dist/schemas/sdk-errors-server.js +15 -0
- package/dist/schemas/text-to-speech.d.ts +90 -68
- package/dist/schemas/text-to-speech.js +16 -33
- package/dist/schemas/transcription-config.d.ts +383 -0
- package/dist/schemas/{whispercpp-config.js → transcription-config.js} +29 -1
- package/dist/schemas/translation-config.d.ts +463 -8
- package/dist/schemas/translation-config.js +35 -2
- package/dist/server/bare/delegate-rpc-client.js +52 -11
- package/dist/server/bare/ops/load-model.d.ts +7 -1
- package/dist/server/bare/ops/load-model.js +41 -42
- package/dist/server/bare/ops/transcribe.d.ts +3 -0
- package/dist/server/bare/ops/transcribe.js +75 -0
- package/dist/server/bare/ops/translate.d.ts +2 -0
- package/dist/server/bare/ops/translate.js +12 -4
- 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 +2 -1
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +8 -6
- package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +108 -3
- package/dist/server/bare/plugins/llamacpp-completion/plugin.js +19 -6
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +32 -2
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +31 -17
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +325 -3
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +117 -13
- package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +50 -2
- package/dist/server/bare/plugins/onnx-ocr/plugin.js +46 -5
- package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +351 -4
- package/dist/server/bare/plugins/onnx-tts/plugin.js +81 -61
- package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +317 -0
- package/dist/server/bare/plugins/parakeet-transcription/plugin.js +154 -0
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +209 -3
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +21 -9
- package/dist/server/bare/rag-hyperdb/rag-workspace-manager.js +2 -2
- package/dist/server/bare/registry/model-config-utils.js +3 -1
- package/dist/server/bare/registry/model-registry.d.ts +2 -0
- package/dist/server/bare/registry/model-registry.js +2 -1
- package/dist/server/bare/utils/audio-input.d.ts +10 -0
- package/dist/server/bare/utils/audio-input.js +33 -0
- package/dist/server/error-handlers.d.ts +3 -2
- package/dist/server/error-handlers.js +11 -8
- package/dist/server/plugins/registry.js +5 -1
- package/dist/server/rpc/delegate-transport.d.ts +11 -3
- package/dist/server/rpc/delegate-transport.js +214 -38
- package/dist/server/rpc/delegate-utils.d.ts +2 -0
- package/dist/server/rpc/delegate-utils.js +5 -0
- package/dist/server/rpc/handle-request.js +43 -4
- package/dist/server/rpc/handler-registry.js +33 -4
- package/dist/server/rpc/handler-utils.d.ts +5 -4
- package/dist/server/rpc/handler-utils.js +114 -31
- package/dist/server/rpc/handlers/cancel-delegated.d.ts +4 -0
- package/dist/server/rpc/handlers/cancel-delegated.js +71 -0
- package/dist/server/rpc/handlers/completion-stream-delegated.d.ts +3 -1
- package/dist/server/rpc/handlers/completion-stream-delegated.js +13 -4
- package/dist/server/rpc/handlers/download-asset.js +55 -3
- package/dist/server/rpc/handlers/heartbeat-delegated.d.ts +4 -0
- package/dist/server/rpc/handlers/heartbeat-delegated.js +32 -0
- package/dist/server/rpc/handlers/heartbeat.d.ts +3 -0
- package/dist/server/rpc/handlers/heartbeat.js +3 -0
- package/dist/server/rpc/handlers/index.d.ts +2 -2
- package/dist/server/rpc/handlers/index.js +2 -2
- package/dist/server/rpc/handlers/load-model/download-stats.d.ts +6 -0
- package/dist/server/rpc/handlers/load-model/download-stats.js +87 -0
- package/dist/server/rpc/handlers/load-model/handler.js +108 -123
- package/dist/server/rpc/handlers/load-model/http.d.ts +2 -1
- package/dist/server/rpc/handlers/load-model/http.js +21 -5
- package/dist/server/rpc/handlers/load-model/hyperdrive.d.ts +2 -1
- package/dist/server/rpc/handlers/load-model/hyperdrive.js +19 -14
- package/dist/server/rpc/handlers/load-model/registry.d.ts +2 -1
- package/dist/server/rpc/handlers/load-model/registry.js +113 -136
- package/dist/server/rpc/handlers/load-model/resolve.d.ts +2 -0
- package/dist/server/rpc/handlers/load-model/resolve.js +55 -22
- package/dist/server/rpc/handlers/load-model/types.d.ts +29 -0
- package/dist/server/rpc/handlers/load-model/types.js +1 -0
- package/dist/server/rpc/handlers/load-model-delegated.d.ts +5 -1
- package/dist/server/rpc/handlers/load-model-delegated.js +41 -11
- package/dist/server/rpc/handlers/plugin-dispatch.js +15 -10
- package/dist/server/rpc/handlers/plugin-invoke.js +56 -51
- package/dist/server/rpc/handlers/rag.js +24 -0
- package/dist/server/rpc/handlers/unload-model-delegated.d.ts +3 -0
- package/dist/server/rpc/handlers/unload-model-delegated.js +32 -0
- package/dist/server/rpc/profiling/context.d.ts +18 -0
- package/dist/server/rpc/profiling/context.js +38 -0
- package/dist/server/rpc/profiling/delegation-profiler.d.ts +32 -0
- package/dist/server/rpc/profiling/delegation-profiler.js +154 -0
- package/dist/server/rpc/profiling/index.d.ts +5 -0
- package/dist/server/rpc/profiling/index.js +4 -0
- package/dist/server/rpc/profiling/operation-metrics.d.ts +25 -0
- package/dist/server/rpc/profiling/operation-metrics.js +199 -0
- package/dist/server/rpc/profiling/operation-wrappers.d.ts +18 -0
- package/dist/server/rpc/profiling/operation-wrappers.js +124 -0
- package/dist/server/rpc/profiling/profiler.d.ts +13 -0
- package/dist/server/rpc/profiling/profiler.js +97 -0
- package/dist/server/utils/cache.d.ts +2 -1
- package/dist/server/utils/cache.js +5 -1
- package/dist/server/utils/checksum.d.ts +2 -0
- package/dist/server/utils/checksum.js +7 -0
- package/dist/server/utils/formatting.d.ts +6 -0
- package/dist/server/utils/formatting.js +35 -0
- package/dist/server/worker.js +2 -1
- package/dist/utils/errors-client.d.ts +3 -0
- package/dist/utils/errors-client.js +6 -0
- package/dist/utils/errors-server.d.ts +9 -0
- package/dist/utils/errors-server.js +15 -0
- package/dist/utils/global-singleton.d.ts +2 -0
- package/dist/utils/global-singleton.js +10 -0
- package/package.json +22 -18
- package/dist/client/api/ping.d.ts +0 -11
- package/dist/client/api/ping.js +0 -19
- package/dist/examples/embed-hyperdrive.d.ts +0 -2
- package/dist/examples/llamacpp-hyperdrive.d.ts +0 -2
- package/dist/examples/seed-hyperdrive.d.ts +0 -2
- package/dist/schemas/ping.d.ts +0 -11
- package/dist/schemas/ping.js +0 -8
- package/dist/schemas/whispercpp-config.d.ts +0 -70
- package/dist/server/bare/plugins/whispercpp-transcription/ops/transcribe-stream.d.ts +0 -3
- package/dist/server/bare/plugins/whispercpp-transcription/ops/transcribe-stream.js +0 -81
- package/dist/server/rpc/handlers/ping.d.ts +0 -3
- package/dist/server/rpc/handlers/ping.js +0 -3
- /package/dist/examples/{embed-hyperdrive.js → embed-p2p.js} +0 -0
- /package/dist/examples/{llamacpp-hyperdrive.js → llamacpp-p2p.js} +0 -0
- /package/dist/examples/{seed-hyperdrive.js → seed-p2p.js} +0 -0
- /package/dist/examples/{whispercpp-filesystem.d.ts → transcription/whispercpp-filesystem.d.ts} +0 -0
- /package/dist/examples/{whispercpp-filesystem.js → transcription/whispercpp-filesystem.js} +0 -0
- /package/dist/examples/{whispercpp-microphone-record.d.ts → transcription/whispercpp-microphone-record.d.ts} +0 -0
- /package/dist/examples/{whispercpp-microphone-record.js → transcription/whispercpp-microphone-record.js} +0 -0
- /package/dist/examples/{whispercpp-prompt.d.ts → transcription/whispercpp-prompt.d.ts} +0 -0
- /package/dist/examples/{whispercpp-prompt.js → transcription/whispercpp-prompt.js} +0 -0
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { requestSchema, responseSchema, } from "../../schemas/index.js";
|
|
1
|
+
import { requestSchema, responseSchema, PROFILING_TRAILER_KEY, } from "../../schemas/index.js";
|
|
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
5
|
import { getRPC, close as closeRPC } from "#rpc";
|
|
6
|
+
import { nowMs, shouldProfile, shouldIncludeServerBreakdown, generateId as createProfileId, createProfilingMeta, createProfilingDisabledMeta, injectProfilingMetaIntoObject, extractProfilingMeta, stripProfilingMeta, recordFailure, } from "../../profiling/index.js";
|
|
7
|
+
import { createClientTimings, createClientStreamTimings, recordClientEvents, recordClientStreamEvents, cacheConnectionTime, flushConnectionTime, resetConnectionTracking, } from "./profiling/index.js";
|
|
6
8
|
const logger = getClientLogger();
|
|
7
9
|
let rpcInstance = null;
|
|
8
10
|
let commandCounter = 0;
|
|
11
|
+
let firstConnectionPending = true;
|
|
9
12
|
function getNextCommandId() {
|
|
10
13
|
commandCounter = (commandCounter + 1) % Number.MAX_SAFE_INTEGER;
|
|
11
14
|
return commandCounter;
|
|
@@ -17,16 +20,43 @@ function checkAndThrowError(response) {
|
|
|
17
20
|
}
|
|
18
21
|
async function getRPCInstance() {
|
|
19
22
|
if (rpcInstance)
|
|
20
|
-
return rpcInstance;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
return { rpc: await rpcInstance };
|
|
24
|
+
const connectionStart = firstConnectionPending ? nowMs() : null;
|
|
25
|
+
rpcInstance = getRPC();
|
|
26
|
+
const rpc = await rpcInstance;
|
|
27
|
+
if (connectionStart !== null && firstConnectionPending) {
|
|
28
|
+
firstConnectionPending = false;
|
|
29
|
+
return { rpc, connectionMs: nowMs() - connectionStart };
|
|
30
|
+
}
|
|
31
|
+
return { rpc };
|
|
32
|
+
}
|
|
33
|
+
async function prepareRPCContext(requestType, perCallProfiling, rpc) {
|
|
34
|
+
const rpcResult = rpc ? { rpc } : await getRPCInstance();
|
|
35
|
+
const profilingEnabled = shouldProfile(requestType, perCallProfiling);
|
|
36
|
+
const signalDisable = perCallProfiling?.enabled === false;
|
|
37
|
+
if (rpcResult.connectionMs !== undefined) {
|
|
38
|
+
cacheConnectionTime(rpcResult.connectionMs);
|
|
39
|
+
}
|
|
40
|
+
if (profilingEnabled) {
|
|
41
|
+
flushConnectionTime();
|
|
42
|
+
}
|
|
43
|
+
return { rpc: rpcResult.rpc, profilingEnabled, signalDisable };
|
|
44
|
+
}
|
|
45
|
+
export async function send(request, options, rpc) {
|
|
46
|
+
const ctx = await prepareRPCContext(request.type, options?.profiling, rpc);
|
|
47
|
+
if (!ctx.profilingEnabled) {
|
|
48
|
+
return sendBase(request, ctx.rpc, options, ctx.signalDisable);
|
|
49
|
+
}
|
|
50
|
+
return sendProfiled(request, ctx.rpc, options);
|
|
23
51
|
}
|
|
24
|
-
|
|
52
|
+
async function sendBase(request, rpc, options, signalDisable = false) {
|
|
25
53
|
const parsedRequest = requestSchema.parse(request);
|
|
26
|
-
const
|
|
27
|
-
const req = rpcInstance.request(getNextCommandId());
|
|
54
|
+
const req = rpc.request(getNextCommandId());
|
|
28
55
|
logger.debug("RPC Client sending:", summarizeRequest(request));
|
|
29
|
-
const
|
|
56
|
+
const payloadObj = signalDisable
|
|
57
|
+
? injectProfilingMetaIntoObject(parsedRequest, createProfilingDisabledMeta())
|
|
58
|
+
: parsedRequest;
|
|
59
|
+
const payload = JSON.stringify(payloadObj);
|
|
30
60
|
req.send(payload, "utf-8");
|
|
31
61
|
const response = await withTimeout(req.reply("utf-8"), options?.timeout);
|
|
32
62
|
const resPayload = responseSchema.parse(JSON.parse(response?.toString() || "{}"));
|
|
@@ -34,12 +64,69 @@ export async function send(request, rpc, options) {
|
|
|
34
64
|
checkAndThrowError(resPayload);
|
|
35
65
|
return resPayload;
|
|
36
66
|
}
|
|
37
|
-
|
|
67
|
+
async function sendProfiled(request, rpc, options) {
|
|
68
|
+
const requestType = request.type;
|
|
69
|
+
const profileId = createProfileId();
|
|
70
|
+
const includeServer = shouldIncludeServerBreakdown(options?.profiling);
|
|
71
|
+
const timings = createClientTimings(profileId, requestType);
|
|
72
|
+
try {
|
|
73
|
+
const zodStart = nowMs();
|
|
74
|
+
const parsedRequest = requestSchema.parse(request);
|
|
75
|
+
timings.requestZodValidationMs = nowMs() - zodStart;
|
|
76
|
+
const req = rpc.request(getNextCommandId());
|
|
77
|
+
logger.debug("RPC Client sending:", summarizeRequest(request));
|
|
78
|
+
const profilingMeta = createProfilingMeta(profileId, includeServer);
|
|
79
|
+
const requestWithMeta = injectProfilingMetaIntoObject(parsedRequest, profilingMeta);
|
|
80
|
+
const stringifyStart = nowMs();
|
|
81
|
+
const payload = JSON.stringify(requestWithMeta);
|
|
82
|
+
timings.requestStringifyMs = nowMs() - stringifyStart;
|
|
83
|
+
timings.sendStart = nowMs();
|
|
84
|
+
req.send(payload, "utf-8");
|
|
85
|
+
const response = await withTimeout(req.reply("utf-8"), options?.timeout);
|
|
86
|
+
timings.firstResponseAt = nowMs();
|
|
87
|
+
const parseStart = nowMs();
|
|
88
|
+
const rawParsed = JSON.parse(response?.toString() || "{}");
|
|
89
|
+
timings.responseJsonParseMs = nowMs() - parseStart;
|
|
90
|
+
const responseMeta = extractProfilingMeta(rawParsed);
|
|
91
|
+
const cleanPayload = stripProfilingMeta(rawParsed);
|
|
92
|
+
const zodValidateStart = nowMs();
|
|
93
|
+
const resPayload = responseSchema.parse(cleanPayload);
|
|
94
|
+
timings.responseZodValidationMs = nowMs() - zodValidateStart;
|
|
95
|
+
timings.requestEnd = nowMs();
|
|
96
|
+
logger.debug("ResPayload", { type: resPayload.type });
|
|
97
|
+
recordClientEvents(timings, responseMeta);
|
|
98
|
+
checkAndThrowError(resPayload);
|
|
99
|
+
return resPayload;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (timings.requestEnd === undefined) {
|
|
103
|
+
const base = {
|
|
104
|
+
ts: nowMs(),
|
|
105
|
+
op: timings.requestType,
|
|
106
|
+
kind: "rpc",
|
|
107
|
+
profileId: timings.profileId,
|
|
108
|
+
};
|
|
109
|
+
recordFailure(base, timings.requestStart, error);
|
|
110
|
+
}
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export async function* stream(request, options = {}, rpc) {
|
|
115
|
+
const ctx = await prepareRPCContext(request.type, options?.profiling, rpc);
|
|
116
|
+
if (!ctx.profilingEnabled) {
|
|
117
|
+
yield* streamBase(request, ctx.rpc, options, ctx.signalDisable);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
yield* streamProfiled(request, ctx.rpc, options);
|
|
121
|
+
}
|
|
122
|
+
async function* streamBase(request, rpc, options = {}, signalDisable = false) {
|
|
38
123
|
const parsedRequest = requestSchema.parse(request);
|
|
39
|
-
const
|
|
40
|
-
const req = rpcInstance.request(getNextCommandId());
|
|
124
|
+
const req = rpc.request(getNextCommandId());
|
|
41
125
|
logger.debug("RPC Client streaming:", summarizeRequest(request));
|
|
42
|
-
|
|
126
|
+
const payloadObj = signalDisable
|
|
127
|
+
? injectProfilingMetaIntoObject(parsedRequest, createProfilingDisabledMeta())
|
|
128
|
+
: parsedRequest;
|
|
129
|
+
req.send(JSON.stringify(payloadObj), "utf-8");
|
|
43
130
|
const responseStream = req.createResponseStream({ encoding: "utf-8" });
|
|
44
131
|
let buffer = "";
|
|
45
132
|
async function* processStream() {
|
|
@@ -62,9 +149,84 @@ export async function* stream(request, rpc, options = {}) {
|
|
|
62
149
|
}
|
|
63
150
|
}
|
|
64
151
|
}
|
|
152
|
+
async function* streamProfiled(request, rpc, options = {}) {
|
|
153
|
+
const requestType = request.type;
|
|
154
|
+
const profileId = createProfileId();
|
|
155
|
+
const includeServer = shouldIncludeServerBreakdown(options?.profiling);
|
|
156
|
+
const timings = createClientStreamTimings(profileId, requestType);
|
|
157
|
+
let profilingMeta = undefined;
|
|
158
|
+
try {
|
|
159
|
+
const zodStart = nowMs();
|
|
160
|
+
const parsedRequest = requestSchema.parse(request);
|
|
161
|
+
timings.requestZodValidationMs = nowMs() - zodStart;
|
|
162
|
+
const req = rpc.request(getNextCommandId());
|
|
163
|
+
logger.debug("RPC Client streaming:", summarizeRequest(request));
|
|
164
|
+
const requestMeta = createProfilingMeta(profileId, includeServer);
|
|
165
|
+
const requestWithMeta = injectProfilingMetaIntoObject(parsedRequest, requestMeta);
|
|
166
|
+
const stringifyStart = nowMs();
|
|
167
|
+
const payload = JSON.stringify(requestWithMeta);
|
|
168
|
+
timings.requestStringifyMs = nowMs() - stringifyStart;
|
|
169
|
+
timings.sendStart = nowMs();
|
|
170
|
+
req.send(payload, "utf-8");
|
|
171
|
+
const responseStream = req.createResponseStream({ encoding: "utf-8" });
|
|
172
|
+
let buffer = "";
|
|
173
|
+
async function* processStream() {
|
|
174
|
+
for await (const chunk of responseStream) {
|
|
175
|
+
yield chunk;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const streamWithTimeout = withTimeoutStream(processStream(), options?.timeout);
|
|
179
|
+
for await (const chunk of streamWithTimeout) {
|
|
180
|
+
const chunkTime = nowMs();
|
|
181
|
+
if (timings.firstChunkAt === undefined) {
|
|
182
|
+
timings.firstChunkAt = chunkTime;
|
|
183
|
+
}
|
|
184
|
+
timings.lastChunkAt = chunkTime;
|
|
185
|
+
buffer += chunk.toString();
|
|
186
|
+
const lines = buffer.split("\n");
|
|
187
|
+
buffer = lines.pop() || "";
|
|
188
|
+
for (const line of lines) {
|
|
189
|
+
if (line.trim()) {
|
|
190
|
+
const rawParsed = JSON.parse(line);
|
|
191
|
+
const chunkMeta = extractProfilingMeta(rawParsed);
|
|
192
|
+
if (chunkMeta) {
|
|
193
|
+
profilingMeta = chunkMeta;
|
|
194
|
+
}
|
|
195
|
+
if (rawParsed[PROFILING_TRAILER_KEY] === true)
|
|
196
|
+
continue;
|
|
197
|
+
const cleanPayload = stripProfilingMeta(rawParsed);
|
|
198
|
+
const response = responseSchema.parse(cleanPayload);
|
|
199
|
+
timings.chunkCount++;
|
|
200
|
+
checkAndThrowError(response);
|
|
201
|
+
yield response;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
if (timings.requestEnd === undefined) {
|
|
208
|
+
const base = {
|
|
209
|
+
ts: nowMs(),
|
|
210
|
+
op: timings.requestType,
|
|
211
|
+
kind: "rpc",
|
|
212
|
+
profileId: timings.profileId,
|
|
213
|
+
};
|
|
214
|
+
recordFailure(base, timings.requestStart, error);
|
|
215
|
+
}
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
if (timings.chunkCount > 0) {
|
|
220
|
+
timings.requestEnd = nowMs();
|
|
221
|
+
recordClientStreamEvents(timings, profilingMeta);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
65
225
|
export async function close() {
|
|
66
226
|
if (!rpcInstance)
|
|
67
227
|
return;
|
|
68
228
|
rpcInstance = null;
|
|
229
|
+
firstConnectionPending = true;
|
|
230
|
+
resetConnectionTracking();
|
|
69
231
|
await closeRPC();
|
|
70
232
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { close, completion, LLAMA_3_2_1B_INST_Q4_0, loadModel, profiler, } from "@qvac/sdk";
|
|
2
|
+
const topicHex = process.argv[2];
|
|
3
|
+
if (!topicHex) {
|
|
4
|
+
console.error("❌ Usage: bun run consumer-profiled.ts <topic> <publicKey>");
|
|
5
|
+
process.exit(1);
|
|
6
|
+
}
|
|
7
|
+
const providerPublicKey = process.argv[3];
|
|
8
|
+
if (!providerPublicKey) {
|
|
9
|
+
console.error("❌ Usage: bun run consumer-profiled.ts <topic> <publicKey>");
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
profiler.enable({ mode: "verbose", includeServerBreakdown: true });
|
|
14
|
+
console.log("✓ Profiler enabled");
|
|
15
|
+
console.log(`\n📡 Topic: ${topicHex}`);
|
|
16
|
+
console.log(`🔑 Provider: ${providerPublicKey}\n`);
|
|
17
|
+
console.log("→ Loading model (delegated, unary)...");
|
|
18
|
+
const modelId = await loadModel({
|
|
19
|
+
modelSrc: LLAMA_3_2_1B_INST_Q4_0,
|
|
20
|
+
modelType: "llm",
|
|
21
|
+
delegate: {
|
|
22
|
+
topic: topicHex,
|
|
23
|
+
providerPublicKey,
|
|
24
|
+
timeout: 30_000,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
console.log(`✓ Model loaded: ${modelId}\n`);
|
|
28
|
+
console.log("→ Running completion (delegated, streamed)...");
|
|
29
|
+
const response = completion({
|
|
30
|
+
modelId,
|
|
31
|
+
history: [{ role: "user", content: "Say hello in exactly 5 words." }],
|
|
32
|
+
stream: true,
|
|
33
|
+
});
|
|
34
|
+
process.stdout.write(" Response: ");
|
|
35
|
+
for await (const token of response.tokenStream) {
|
|
36
|
+
process.stdout.write(token);
|
|
37
|
+
}
|
|
38
|
+
await response.stats;
|
|
39
|
+
console.log("\n✓ Completion done\n");
|
|
40
|
+
console.log("=== Profiler Summary ===");
|
|
41
|
+
console.log(profiler.exportSummary());
|
|
42
|
+
console.log("=== Profiler Table ===");
|
|
43
|
+
console.log(profiler.exportTable());
|
|
44
|
+
// Look for delegation-specific metrics
|
|
45
|
+
const json = profiler.exportJSON();
|
|
46
|
+
const delegationMetrics = Object.keys(json.aggregates).filter((k) => k.includes("delegation") || k.includes("delegated"));
|
|
47
|
+
if (delegationMetrics.length > 0) {
|
|
48
|
+
console.log("=== Delegation Metrics ===");
|
|
49
|
+
for (const key of delegationMetrics) {
|
|
50
|
+
const agg = json.aggregates[key];
|
|
51
|
+
if (agg) {
|
|
52
|
+
console.log(` ${key}: ${agg.avg.toFixed(2)}ms (count: ${agg.count})`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
console.log("⚠️ No delegation-specific metrics found");
|
|
58
|
+
console.log(" (delegation profiling may need to be triggered)");
|
|
59
|
+
}
|
|
60
|
+
profiler.disable();
|
|
61
|
+
void close();
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error("❌ Error:", error);
|
|
65
|
+
profiler.disable();
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
@@ -10,9 +10,9 @@ try {
|
|
|
10
10
|
const modelId = await loadModel({
|
|
11
11
|
modelSrc: SMOLVLM2_500M_MULTIMODAL_Q8_0,
|
|
12
12
|
modelType: "llm",
|
|
13
|
-
projectionModelSrc: MMPROJ_SMOLVLM2_500M_MULTIMODAL_Q8_0,
|
|
14
13
|
modelConfig: {
|
|
15
14
|
ctx_size: 1024,
|
|
15
|
+
projectionModelSrc: MMPROJ_SMOLVLM2_500M_MULTIMODAL_Q8_0,
|
|
16
16
|
},
|
|
17
17
|
onProgress: (progress) => {
|
|
18
18
|
console.log(`Loading: ${progress.percentage.toFixed(1)}%`);
|
|
@@ -1,18 +1,50 @@
|
|
|
1
|
-
import { LLAMA_3_2_1B_INST_Q4_0,
|
|
1
|
+
import { LLAMA_3_2_1B_INST_Q4_0, WHISPER_TINY, VAD_SILERO_5_1_2, GTE_LARGE_FP16, downloadAsset, close, } from "@qvac/sdk";
|
|
2
|
+
function now() {
|
|
3
|
+
return Date.now();
|
|
4
|
+
}
|
|
5
|
+
const assets = [
|
|
6
|
+
{ name: "Llama 3.2 1B", src: LLAMA_3_2_1B_INST_Q4_0 },
|
|
7
|
+
{ name: "Whisper Tiny", src: WHISPER_TINY },
|
|
8
|
+
{ name: "VAD Silero", src: VAD_SILERO_5_1_2 },
|
|
9
|
+
{ name: "GTE Large FP16", src: GTE_LARGE_FP16 },
|
|
10
|
+
];
|
|
11
|
+
const timers = {};
|
|
12
|
+
console.log(`\n=== Parallel Download (${assets.length} assets) ===\n`);
|
|
13
|
+
const wallStart = now();
|
|
2
14
|
try {
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
const promises = assets.map((asset) => {
|
|
16
|
+
timers[asset.name] = { start: now() };
|
|
17
|
+
return downloadAsset({
|
|
18
|
+
assetSrc: asset.src,
|
|
19
|
+
onProgress: (progress) => {
|
|
20
|
+
const t = timers[asset.name];
|
|
21
|
+
if (t.firstProgress == null) {
|
|
22
|
+
t.firstProgress = now();
|
|
23
|
+
console.log(`[${asset.name}] first progress at ${((t.firstProgress - wallStart) / 1000).toFixed(1)}s — ${progress.percentage}%`);
|
|
24
|
+
}
|
|
25
|
+
if (progress.percentage === 100 && t.end == null) {
|
|
26
|
+
t.end = now();
|
|
27
|
+
console.log(`[${asset.name}] done at ${((t.end - wallStart) / 1000).toFixed(1)}s`);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
});
|
|
14
31
|
});
|
|
15
|
-
await Promise.
|
|
32
|
+
const results = await Promise.allSettled(promises);
|
|
33
|
+
const wallEnd = now();
|
|
34
|
+
console.log(`\n=== Results ===\n`);
|
|
35
|
+
for (let i = 0; i < assets.length; i++) {
|
|
36
|
+
const asset = assets[i];
|
|
37
|
+
const result = results[i];
|
|
38
|
+
const t = timers[asset.name];
|
|
39
|
+
const status = result.status === "fulfilled" ? "OK" : "FAILED";
|
|
40
|
+
const reason = result.status === "rejected" ? ` — ${result.reason}` : "";
|
|
41
|
+
const timeToFirst = t.firstProgress != null
|
|
42
|
+
? `${((t.firstProgress - t.start) / 1000).toFixed(1)}s`
|
|
43
|
+
: "N/A";
|
|
44
|
+
const total = t.end != null ? `${((t.end - t.start) / 1000).toFixed(1)}s` : "N/A";
|
|
45
|
+
console.log(`${status} ${asset.name}: first-progress=${timeToFirst}, total=${total}${reason}`);
|
|
46
|
+
}
|
|
47
|
+
console.log(`\nWall time: ${((wallEnd - wallStart) / 1000).toFixed(1)}s`);
|
|
16
48
|
void close();
|
|
17
49
|
}
|
|
18
50
|
catch (error) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { completion, loadModel, unloadModel, LLAMA_3_2_1B_INST_Q4_0, profiler, } from "@qvac/sdk";
|
|
2
|
+
try {
|
|
3
|
+
// Enable profiling globally
|
|
4
|
+
profiler.enable({
|
|
5
|
+
mode: "verbose",
|
|
6
|
+
includeServerBreakdown: true,
|
|
7
|
+
});
|
|
8
|
+
console.log("Profiler enabled:", profiler.isEnabled());
|
|
9
|
+
const modelId = await loadModel({
|
|
10
|
+
modelSrc: LLAMA_3_2_1B_INST_Q4_0,
|
|
11
|
+
modelType: "llm",
|
|
12
|
+
onProgress: (p) => console.log(` ${p.percentage.toFixed(1)}%`),
|
|
13
|
+
});
|
|
14
|
+
console.log("Model loaded:", modelId);
|
|
15
|
+
console.log("\n→ Running completion...");
|
|
16
|
+
const result = completion({
|
|
17
|
+
modelId,
|
|
18
|
+
history: [{ role: "user", content: "Say hello in one sentence." }],
|
|
19
|
+
stream: true,
|
|
20
|
+
});
|
|
21
|
+
for await (const token of result.tokenStream) {
|
|
22
|
+
process.stdout.write(token);
|
|
23
|
+
}
|
|
24
|
+
console.log();
|
|
25
|
+
await unloadModel({ modelId });
|
|
26
|
+
// Export profiling data
|
|
27
|
+
console.log("\n=== Profiler Summary ===");
|
|
28
|
+
console.log(profiler.exportSummary());
|
|
29
|
+
console.log("\n=== Profiler Table ===");
|
|
30
|
+
console.log(profiler.exportTable());
|
|
31
|
+
const json = profiler.exportJSON();
|
|
32
|
+
console.log("\n=== Load Model Metrics ===");
|
|
33
|
+
// Filter for operation-level event (kind: "handler"), not RPC phase events
|
|
34
|
+
const loadModelEvent = json.recentEvents?.find((e) => e.op === "loadModel" && e.kind === "handler");
|
|
35
|
+
if (loadModelEvent) {
|
|
36
|
+
const tags = loadModelEvent.tags ?? {};
|
|
37
|
+
const gauges = loadModelEvent.gauges ?? {};
|
|
38
|
+
console.log(" sourceType:", tags["sourceType"] ?? "(not set)");
|
|
39
|
+
console.log(" cacheHit:", tags["cacheHit"] ?? "(not set)");
|
|
40
|
+
console.log(" totalLoadTime:", gauges["totalLoadTime"], "ms");
|
|
41
|
+
console.log(" modelInitializationTime:", gauges["modelInitializationTime"], "ms");
|
|
42
|
+
if (tags["cacheHit"] !== "true") {
|
|
43
|
+
console.log(" downloadTime:", gauges["downloadTime"] ?? "(cached)", "ms");
|
|
44
|
+
console.log(" totalBytesDownloaded:", gauges["totalBytesDownloaded"] ?? "(cached)");
|
|
45
|
+
console.log(" downloadSpeedBps:", gauges["downloadSpeedBps"] ?? "(cached)");
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.log(" (download metrics omitted - cache hit)");
|
|
49
|
+
}
|
|
50
|
+
if (gauges["checksumValidationTime"] !== undefined) {
|
|
51
|
+
console.log(" checksumValidationTime:", gauges["checksumValidationTime"], "ms");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
console.log(" (no loadModel handler event captured)");
|
|
56
|
+
// Debug: show what ops are available
|
|
57
|
+
const ops = [...new Set(json.recentEvents?.map((e) => `${e.op}:${e.kind}`) ?? [])];
|
|
58
|
+
console.log(" Available ops:", ops.join(", "));
|
|
59
|
+
}
|
|
60
|
+
console.log("\n=== Profiler JSON (structure) ===");
|
|
61
|
+
console.log(" aggregates:", Object.keys(json.aggregates).length, "metrics");
|
|
62
|
+
console.log(" recentEvents:", json.recentEvents?.length ?? 0, "events");
|
|
63
|
+
console.log(" config:", json.config);
|
|
64
|
+
// Disable profiling
|
|
65
|
+
profiler.disable();
|
|
66
|
+
console.log("\nProfiler disabled:", !profiler.isEnabled());
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error("Error:", error);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { embed, loadModel, unloadModel, GTE_LARGE_FP16, profiler, } from "@qvac/sdk";
|
|
2
|
+
try {
|
|
3
|
+
profiler.disable();
|
|
4
|
+
console.log("Profiler globally enabled:", profiler.isEnabled());
|
|
5
|
+
const modelId = await loadModel({
|
|
6
|
+
modelSrc: GTE_LARGE_FP16,
|
|
7
|
+
modelType: "embeddings",
|
|
8
|
+
onProgress: (p) => console.log(` ${p.percentage.toFixed(1)}%`),
|
|
9
|
+
});
|
|
10
|
+
console.log("Model loaded:", modelId);
|
|
11
|
+
console.log("\n=== Embed with per-call profiling ===");
|
|
12
|
+
const embedding1 = await embed({ modelId, text: "Profile this specific call" }, { profiling: { enabled: true, includeServerBreakdown: true } });
|
|
13
|
+
console.log("Embedding dimensions:", embedding1.length);
|
|
14
|
+
console.log("\n=== Embed without profiling ===");
|
|
15
|
+
const embedding2 = await embed({
|
|
16
|
+
modelId,
|
|
17
|
+
text: "This call is not profiled",
|
|
18
|
+
});
|
|
19
|
+
console.log("Embedding dimensions:", embedding2.length);
|
|
20
|
+
console.log("\n=== Embed with profiling explicitly disabled ===");
|
|
21
|
+
const embedding3 = await embed({ modelId, text: "Profiling explicitly disabled for this call" }, { profiling: { enabled: false } });
|
|
22
|
+
console.log("Embedding dimensions:", embedding3.length);
|
|
23
|
+
await unloadModel({ modelId });
|
|
24
|
+
console.log("\n=== Profiler Summary (per-call data only) ===");
|
|
25
|
+
console.log(profiler.exportSummary());
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error("Error:", error);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { loadModel, unloadModel, transcribe, PARAKEET_CTC_FP32, PARAKEET_CTC_DATA_FP32, PARAKEET_CTC_TOKENIZER, } from "@qvac/sdk";
|
|
2
|
+
const args = process.argv.slice(2);
|
|
3
|
+
if (!args[0]) {
|
|
4
|
+
console.error("Usage: bun run examples/transcription/parakeet-ctc-filesystem.ts <wav-file> " +
|
|
5
|
+
"[model.onnx] [model.onnx_data] [tokenizer.json]");
|
|
6
|
+
console.error("\nIf model paths are omitted, defaults to registry models.");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
const audioFilePath = args[0];
|
|
10
|
+
const parakeetCtcModelSrc = args[1] ?? PARAKEET_CTC_FP32;
|
|
11
|
+
const parakeetCtcModelDataSrc = args[2] ?? PARAKEET_CTC_DATA_FP32;
|
|
12
|
+
const parakeetTokenizerSrc = args[3] ?? PARAKEET_CTC_TOKENIZER;
|
|
13
|
+
try {
|
|
14
|
+
console.log("Loading Parakeet CTC model...");
|
|
15
|
+
const modelId = await loadModel({
|
|
16
|
+
modelSrc: parakeetCtcModelSrc,
|
|
17
|
+
modelType: "parakeet",
|
|
18
|
+
modelConfig: {
|
|
19
|
+
modelType: "ctc",
|
|
20
|
+
parakeetCtcModelSrc,
|
|
21
|
+
parakeetCtcModelDataSrc,
|
|
22
|
+
parakeetTokenizerSrc,
|
|
23
|
+
},
|
|
24
|
+
onProgress: (progress) => {
|
|
25
|
+
console.log(`Download progress: ${progress.percentage.toFixed(1)}%`);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
console.log(`Parakeet CTC model loaded with ID: ${modelId}`);
|
|
29
|
+
console.log("Transcribing audio...");
|
|
30
|
+
const text = await transcribe({ modelId, audioChunk: audioFilePath });
|
|
31
|
+
console.log("Transcription result:");
|
|
32
|
+
console.log(text);
|
|
33
|
+
console.log("Unloading model...");
|
|
34
|
+
await unloadModel({ modelId });
|
|
35
|
+
console.log("Done");
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error("Error:", error);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { loadModel, unloadModel, transcribeStream, PARAKEET_TDT_ENCODER_FP32, PARAKEET_TDT_ENCODER_DATA_FP32, PARAKEET_TDT_DECODER_FP32, PARAKEET_TDT_VOCAB, PARAKEET_TDT_PREPROCESSOR_FP32, } from "@qvac/sdk";
|
|
2
|
+
import { spawn, spawnSync } from "child_process";
|
|
3
|
+
import { platform } from "os";
|
|
4
|
+
function checkFFmpeg() {
|
|
5
|
+
const result = spawnSync("ffmpeg", ["-version"], { stdio: "ignore" });
|
|
6
|
+
if (result.error || result.status !== 0) {
|
|
7
|
+
throw new Error("FFmpeg is required but not found in PATH.");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function getAudioDevice() {
|
|
11
|
+
switch (platform()) {
|
|
12
|
+
case "darwin":
|
|
13
|
+
return ":0";
|
|
14
|
+
case "linux":
|
|
15
|
+
return "default";
|
|
16
|
+
case "win32":
|
|
17
|
+
// Change as per your system
|
|
18
|
+
return "audio=@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\\wave_{58C07110-A4FD-4FF8-BA10-5A3C14389F71}";
|
|
19
|
+
default:
|
|
20
|
+
throw new Error(`Unsupported platform: ${platform()}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function getAudioInputArgs() {
|
|
24
|
+
switch (platform()) {
|
|
25
|
+
case "darwin":
|
|
26
|
+
return ["-f", "avfoundation", "-i", getAudioDevice()];
|
|
27
|
+
case "linux":
|
|
28
|
+
return ["-f", "pulse", "-i", getAudioDevice()];
|
|
29
|
+
case "win32":
|
|
30
|
+
return ["-f", "dshow", "-i", getAudioDevice()];
|
|
31
|
+
default:
|
|
32
|
+
throw new Error(`Unsupported platform: ${platform()}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
checkFFmpeg();
|
|
36
|
+
console.log("Loading Parakeet model...");
|
|
37
|
+
const modelId = await loadModel({
|
|
38
|
+
modelSrc: PARAKEET_TDT_ENCODER_FP32,
|
|
39
|
+
modelType: "parakeet",
|
|
40
|
+
modelConfig: {
|
|
41
|
+
parakeetEncoderSrc: PARAKEET_TDT_ENCODER_FP32,
|
|
42
|
+
parakeetEncoderDataSrc: PARAKEET_TDT_ENCODER_DATA_FP32,
|
|
43
|
+
parakeetDecoderSrc: PARAKEET_TDT_DECODER_FP32,
|
|
44
|
+
parakeetVocabSrc: PARAKEET_TDT_VOCAB,
|
|
45
|
+
parakeetPreprocessorSrc: PARAKEET_TDT_PREPROCESSOR_FP32,
|
|
46
|
+
},
|
|
47
|
+
onProgress: (p) => console.log(`Download: ${p.percentage.toFixed(1)}%`),
|
|
48
|
+
});
|
|
49
|
+
console.log("Model loaded. Speak into your microphone (Ctrl+C to stop):\n");
|
|
50
|
+
const ffmpeg = spawn("ffmpeg", [
|
|
51
|
+
...getAudioInputArgs(),
|
|
52
|
+
"-ar",
|
|
53
|
+
"16000",
|
|
54
|
+
"-ac",
|
|
55
|
+
"1",
|
|
56
|
+
"-sample_fmt",
|
|
57
|
+
"s16",
|
|
58
|
+
"-f",
|
|
59
|
+
"s16le",
|
|
60
|
+
"pipe:1",
|
|
61
|
+
], { stdio: ["ignore", "pipe", "ignore"] });
|
|
62
|
+
const CHUNK_SIZE = 96000; // ~3s of 16kHz 16-bit mono
|
|
63
|
+
let buffer = Buffer.alloc(0);
|
|
64
|
+
let processing = false;
|
|
65
|
+
ffmpeg.stdout.on("data", (chunk) => {
|
|
66
|
+
buffer = Buffer.concat([buffer, chunk]);
|
|
67
|
+
if (buffer.length >= CHUNK_SIZE && !processing) {
|
|
68
|
+
const audioChunk = buffer.subarray(0, CHUNK_SIZE);
|
|
69
|
+
buffer = buffer.subarray(CHUNK_SIZE);
|
|
70
|
+
processing = true;
|
|
71
|
+
void (async () => {
|
|
72
|
+
try {
|
|
73
|
+
for await (const text of transcribeStream({ modelId, audioChunk })) {
|
|
74
|
+
if (text.trim() && !text.includes("[No speech detected]")) {
|
|
75
|
+
process.stdout.write(text);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
console.error("Transcription error:", err instanceof Error ? err.message : err);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
processing = false;
|
|
84
|
+
}
|
|
85
|
+
})();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
async function cleanup() {
|
|
89
|
+
console.log("\n\nStopping...");
|
|
90
|
+
ffmpeg.kill();
|
|
91
|
+
await unloadModel({ modelId });
|
|
92
|
+
console.log("Done.");
|
|
93
|
+
}
|
|
94
|
+
process.on("SIGINT", () => void cleanup());
|
|
95
|
+
process.on("SIGTERM", () => void cleanup());
|