@qvac/sdk 0.9.1 → 0.10.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.
- package/NOTICE +15 -68
- package/dist/client/api/cancel.d.ts +1 -1
- package/dist/client/api/cancel.js +1 -1
- package/dist/client/api/completion-stream.d.ts +51 -12
- package/dist/client/api/completion-stream.js +129 -56
- package/dist/client/api/delete-cache.d.ts +1 -0
- package/dist/client/api/delete-cache.js +1 -0
- package/dist/client/api/diffusion.d.ts +17 -2
- package/dist/client/api/diffusion.js +21 -4
- package/dist/client/api/embed.d.ts +4 -0
- package/dist/client/api/finetune.d.ts +21 -0
- package/dist/client/api/get-loaded-model-info.d.ts +25 -0
- package/dist/client/api/get-loaded-model-info.js +36 -0
- package/dist/client/api/get-model-info.d.ts +9 -1
- package/dist/client/api/get-model-info.js +8 -0
- package/dist/client/api/heartbeat.d.ts +2 -2
- package/dist/client/api/heartbeat.js +2 -2
- package/dist/client/api/index.d.ts +3 -1
- package/dist/client/api/index.js +3 -1
- package/dist/client/api/invoke-plugin.d.ts +16 -0
- package/dist/client/api/invoke-plugin.js +16 -0
- package/dist/client/api/load-model.d.ts +36 -3
- package/dist/client/api/load-model.js +31 -10
- package/dist/client/api/provide.d.ts +11 -5
- package/dist/client/api/provide.js +11 -6
- package/dist/client/api/rag.d.ts +9 -0
- package/dist/client/api/rag.js +9 -0
- package/dist/client/api/registry.d.ts +26 -0
- package/dist/client/api/registry.js +26 -0
- package/dist/client/api/resume.d.ts +20 -4
- package/dist/client/api/resume.js +20 -4
- package/dist/client/api/state.d.ts +23 -0
- package/dist/client/api/state.js +29 -0
- package/dist/client/api/stop-provider.d.ts +11 -5
- package/dist/client/api/stop-provider.js +11 -5
- package/dist/client/api/suspend.d.ts +21 -3
- package/dist/client/api/suspend.js +21 -3
- package/dist/client/api/text-to-speech.d.ts +64 -6
- package/dist/client/api/text-to-speech.js +423 -47
- package/dist/client/api/transcribe.d.ts +29 -4
- package/dist/client/api/transcribe.js +86 -28
- package/dist/client/api/translate.d.ts +1 -0
- package/dist/client/api/translate.js +1 -0
- package/dist/client/rpc/bare-client.js +22 -1
- package/dist/client/rpc/expo-rpc-client.d.ts +1 -1
- package/dist/client/rpc/expo-rpc-client.js +96 -4
- package/dist/client/rpc/node-rpc-client.js +36 -0
- package/dist/examples/audio/mic-input.d.ts +9 -0
- package/dist/examples/audio/mic-input.js +111 -0
- package/dist/examples/completion-events.d.ts +18 -0
- package/dist/examples/completion-events.js +79 -0
- package/dist/examples/delegated-inference/composite.js +12 -18
- package/dist/examples/delegated-inference/consumer-profiled.js +4 -11
- package/dist/examples/delegated-inference/consumer.js +8 -13
- package/dist/examples/delegated-inference/provider.js +11 -16
- package/dist/examples/diffusion-flux2-klein-img2img.d.ts +2 -0
- package/dist/examples/diffusion-flux2-klein-img2img.js +60 -0
- package/dist/examples/diffusion-flux2-klein.js +1 -0
- package/dist/examples/diffusion-img2img.d.ts +2 -0
- package/dist/examples/diffusion-img2img.js +49 -0
- package/dist/examples/diffusion-txt2img.js +2 -1
- package/dist/examples/kv-cache-example.js +11 -12
- package/dist/examples/llamacpp-cache.d.ts +2 -0
- package/dist/examples/llamacpp-cache.js +107 -0
- package/dist/examples/llamacpp-dynamic-tools.d.ts +2 -0
- package/dist/examples/llamacpp-dynamic-tools.js +144 -0
- package/dist/examples/llamacpp-native-tools.js +2 -8
- package/dist/examples/llamacpp-structured-output.d.ts +2 -0
- package/dist/examples/llamacpp-structured-output.js +106 -0
- package/dist/examples/llamacpp-tools-pythonic.d.ts +2 -0
- package/dist/examples/llamacpp-tools-pythonic.js +69 -0
- package/dist/examples/plugins.js +4 -4
- package/dist/examples/suspend-resume.js +21 -4
- package/dist/examples/transcription/parakeet-microphone-record.js +6 -33
- package/dist/examples/transcription/whispercpp-filesystem-streaming.js +7 -5
- package/dist/examples/transcription/whispercpp-filesystem.js +12 -3
- package/dist/examples/transcription/whispercpp-microphone-record.js +6 -29
- package/dist/examples/translation/translation-stream.js +5 -5
- package/dist/examples/tts/llm-to-tts-streaming.d.ts +16 -0
- package/dist/examples/tts/llm-to-tts-streaming.js +143 -0
- package/dist/examples/tts/supertonic-multilingual.js +3 -3
- package/dist/examples/tts/supertonic.js +3 -3
- package/dist/examples/tts/utils.d.ts +11 -1
- package/dist/examples/tts/utils.js +70 -17
- package/dist/examples/voice-assistant/voice-assistant.d.ts +2 -0
- package/dist/examples/voice-assistant/voice-assistant.js +207 -0
- package/dist/expo/plugins/withMobileBundle.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/logging/logger.d.ts +11 -0
- package/dist/logging/logger.js +11 -0
- package/dist/logging/types.d.ts +18 -0
- package/dist/models/registry/models.d.ts +6620 -6185
- package/dist/models/registry/models.js +6296 -5318
- package/dist/models/update-models/codegen.js +84 -23
- package/dist/models/update-models/companions.d.ts +18 -0
- package/dist/models/update-models/companions.js +175 -0
- package/dist/models/update-models/naming.js +0 -25
- package/dist/models/update-models/registry.js +4 -2
- package/dist/models/update-models/types.d.ts +37 -9
- package/dist/profiling/index.d.ts +49 -0
- package/dist/profiling/index.js +49 -0
- package/dist/schemas/cancel.d.ts +6 -2
- package/dist/schemas/cancel.js +14 -7
- package/dist/schemas/common.d.ts +361 -279
- package/dist/schemas/common.js +26 -5
- package/dist/schemas/completion-event.d.ts +225 -0
- package/dist/schemas/completion-event.js +79 -0
- package/dist/schemas/completion-stream.d.ts +122 -31
- package/dist/schemas/completion-stream.js +137 -36
- package/dist/schemas/delegate.d.ts +0 -2
- package/dist/schemas/delegate.js +25 -6
- package/dist/schemas/download-asset.d.ts +5 -5
- package/dist/schemas/embed.js +21 -9
- package/dist/schemas/engine-addon-map.d.ts +11 -1
- package/dist/schemas/engine-addon-map.js +2 -2
- package/dist/schemas/finetune.d.ts +16 -16
- package/dist/schemas/finetune.js +8 -8
- package/dist/schemas/get-loaded-model-info.d.ts +52 -0
- package/dist/schemas/get-loaded-model-info.js +44 -0
- package/dist/schemas/get-model-info.d.ts +2 -2
- package/dist/schemas/get-model-info.js +103 -32
- package/dist/schemas/heartbeat.d.ts +0 -1
- package/dist/schemas/index.d.ts +4 -1
- package/dist/schemas/index.js +4 -1
- package/dist/schemas/llamacpp-config.d.ts +14 -4
- package/dist/schemas/llamacpp-config.js +5 -0
- package/dist/schemas/load-model.d.ts +3233 -1260
- package/dist/schemas/load-model.js +23 -16
- package/dist/schemas/model-src-utils.d.ts +7 -6
- package/dist/schemas/model-src-utils.js +27 -1
- package/dist/schemas/model-types.d.ts +4 -4
- package/dist/schemas/ocr.d.ts +1 -1
- package/dist/schemas/plugin.d.ts +27 -3
- package/dist/schemas/plugin.js +27 -2
- package/dist/schemas/profiling.js +11 -3
- package/dist/schemas/provide.d.ts +1 -3
- package/dist/schemas/provide.js +0 -3
- package/dist/schemas/rag.d.ts +4 -4
- package/dist/schemas/registry.d.ts +10 -10
- package/dist/schemas/registry.js +36 -14
- package/dist/schemas/reload-config.d.ts +73 -7
- package/dist/schemas/sdcpp-config.d.ts +16 -8
- package/dist/schemas/sdcpp-config.js +118 -25
- package/dist/schemas/sdk-config.d.ts +116 -90
- package/dist/schemas/sdk-config.js +16 -0
- package/dist/schemas/sdk-errors-client.d.ts +3 -0
- package/dist/schemas/sdk-errors-client.js +16 -1
- package/dist/schemas/sdk-errors-server.d.ts +3 -0
- package/dist/schemas/sdk-errors-server.js +23 -0
- package/dist/schemas/state.d.ts +23 -0
- package/dist/schemas/state.js +14 -0
- package/dist/schemas/stop-provide.d.ts +0 -5
- package/dist/schemas/stop-provide.js +2 -6
- package/dist/schemas/text-to-speech.d.ts +93 -27
- package/dist/schemas/text-to-speech.js +27 -0
- package/dist/schemas/tools.d.ts +16 -0
- package/dist/schemas/tools.js +15 -0
- package/dist/schemas/transcription-config.d.ts +8 -8
- package/dist/schemas/transcription.d.ts +33 -0
- package/dist/schemas/transcription.js +9 -0
- package/dist/schemas/translate.js +59 -19
- package/dist/schemas/translation-config.d.ts +157 -133
- package/dist/schemas/translation-config.js +34 -11
- package/dist/server/bare/delegate-rpc-client.d.ts +1 -1
- package/dist/server/bare/delegate-rpc-client.js +131 -122
- package/dist/server/bare/hyperswarm.d.ts +2 -2
- package/dist/server/bare/hyperswarm.js +12 -7
- package/dist/server/bare/ops/cancel.js +7 -0
- package/dist/server/bare/ops/kv-cache-utils.d.ts +7 -10
- package/dist/server/bare/ops/kv-cache-utils.js +31 -40
- package/dist/server/bare/ops/load-model.js +0 -1
- package/dist/server/bare/ops/transcribe.d.ts +11 -4
- package/dist/server/bare/ops/transcribe.js +25 -4
- package/dist/server/bare/ops/translate.js +42 -4
- package/dist/server/bare/ops/unload-model.js +21 -19
- package/dist/server/bare/plugins/llamacpp-completion/ops/cache-logger.js +1 -2
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +13 -8
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +257 -85
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.d.ts +101 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.js +135 -0
- package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +83 -26
- package/dist/server/bare/plugins/llamacpp-completion/plugin.js +57 -32
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +10 -16
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +69 -44
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +113 -94
- package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +1 -1
- package/dist/server/bare/plugins/onnx-ocr/plugin.js +1 -2
- package/dist/server/bare/plugins/onnx-tts/ops/text-to-speech-stream.d.ts +7 -0
- package/dist/server/bare/plugins/onnx-tts/ops/text-to-speech-stream.js +116 -0
- package/dist/server/bare/plugins/onnx-tts/ops/text-to-speech.d.ts +2 -3
- package/dist/server/bare/plugins/onnx-tts/ops/text-to-speech.js +71 -9
- package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +41 -13
- package/dist/server/bare/plugins/onnx-tts/plugin.js +48 -3
- package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +15 -7
- package/dist/server/bare/plugins/parakeet-transcription/plugin.js +5 -6
- package/dist/server/bare/plugins/sdcpp-generation/ops/diffusion.js +6 -0
- package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +8 -5
- package/dist/server/bare/plugins/sdcpp-generation/plugin.js +13 -13
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +17 -1
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +40 -17
- package/dist/server/bare/registry/config-registry.js +14 -3
- package/dist/server/bare/registry/model-registry.d.ts +8 -10
- package/dist/server/bare/registry/model-registry.js +17 -12
- package/dist/server/bare/registry/registry-client.js +79 -19
- package/dist/server/bare/runtime-lifecycle.d.ts +2 -1
- package/dist/server/bare/runtime-lifecycle.js +13 -2
- package/dist/server/bare/types/addon-responses.d.ts +8 -3
- package/dist/server/bare/utils/transcribe-metadata.d.ts +21 -0
- package/dist/server/bare/utils/transcribe-metadata.js +28 -0
- package/dist/server/bare/utils/tts-stats.d.ts +22 -0
- package/dist/server/bare/utils/tts-stats.js +10 -0
- package/dist/server/rpc/create-server.d.ts +4 -1
- package/dist/server/rpc/create-server.js +5 -1
- package/dist/server/rpc/delegate-transport.js +36 -29
- package/dist/server/rpc/handle-request.js +28 -3
- package/dist/server/rpc/handler-registry.js +7 -1
- package/dist/server/rpc/handler-utils.d.ts +5 -0
- package/dist/server/rpc/handler-utils.js +22 -0
- package/dist/server/rpc/handlers/cancel-delegated.js +3 -5
- package/dist/server/rpc/handlers/cancelHandler.js +4 -5
- package/dist/server/rpc/handlers/completion-stream-delegated.js +14 -5
- package/dist/server/rpc/handlers/delete-cache.js +8 -3
- package/dist/server/rpc/handlers/download-asset.js +3 -24
- package/dist/server/rpc/handlers/get-loaded-model-info.d.ts +3 -0
- package/dist/server/rpc/handlers/get-loaded-model-info.js +39 -0
- package/dist/server/rpc/handlers/get-model-info.js +93 -84
- package/dist/server/rpc/handlers/heartbeat-delegated.js +2 -2
- package/dist/server/rpc/handlers/index.d.ts +6 -0
- package/dist/server/rpc/handlers/index.js +6 -0
- package/dist/server/rpc/handlers/load-model/download-manager.d.ts +35 -9
- package/dist/server/rpc/handlers/load-model/download-manager.js +142 -38
- package/dist/server/rpc/handlers/load-model/download-stats.d.ts +5 -4
- package/dist/server/rpc/handlers/load-model/download-stats.js +49 -8
- package/dist/server/rpc/handlers/load-model/handler.js +31 -50
- package/dist/server/rpc/handlers/load-model/http.d.ts +2 -2
- package/dist/server/rpc/handlers/load-model/http.js +97 -142
- package/dist/server/rpc/handlers/load-model/hyperdrive.d.ts +2 -2
- package/dist/server/rpc/handlers/load-model/hyperdrive.js +55 -74
- package/dist/server/rpc/handlers/load-model/index.d.ts +0 -1
- package/dist/server/rpc/handlers/load-model/index.js +0 -1
- package/dist/server/rpc/handlers/load-model/registry-client-options.d.ts +27 -0
- package/dist/server/rpc/handlers/load-model/registry-client-options.js +19 -0
- package/dist/server/rpc/handlers/load-model/registry-companion-set.d.ts +22 -0
- package/dist/server/rpc/handlers/load-model/registry-companion-set.js +208 -0
- package/dist/server/rpc/handlers/load-model/registry-download-utils.d.ts +24 -0
- package/dist/server/rpc/handlers/load-model/registry-download-utils.js +114 -0
- package/dist/server/rpc/handlers/load-model/registry.d.ts +2 -20
- package/dist/server/rpc/handlers/load-model/registry.js +109 -381
- package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +15 -0
- package/dist/server/rpc/handlers/load-model/resolve-session.js +61 -0
- package/dist/server/rpc/handlers/load-model/resolve.d.ts +3 -3
- package/dist/server/rpc/handlers/load-model/resolve.js +14 -23
- package/dist/server/rpc/handlers/load-model/types.d.ts +11 -4
- package/dist/server/rpc/handlers/load-model/types.js +28 -1
- package/dist/server/rpc/handlers/load-model-delegated.js +2 -4
- package/dist/server/rpc/handlers/plugin-dispatch.js +9 -5
- package/dist/server/rpc/handlers/plugin-invoke.js +1 -1
- package/dist/server/rpc/handlers/provideHandler/connection.js +13 -2
- package/dist/server/rpc/handlers/provideHandler/handler.js +42 -29
- package/dist/server/rpc/handlers/state.d.ts +3 -0
- package/dist/server/rpc/handlers/state.js +7 -0
- package/dist/server/rpc/handlers/stopProvideHandler.d.ts +2 -2
- package/dist/server/rpc/handlers/stopProvideHandler.js +12 -7
- package/dist/server/rpc/handlers/text-to-speech-stream.d.ts +3 -0
- package/dist/server/rpc/handlers/text-to-speech-stream.js +4 -0
- package/dist/server/rpc/handlers/unload-model-delegated.js +3 -3
- package/dist/server/rpc/profiling/operation-metrics.js +20 -0
- package/dist/server/rpc/rpc-utils.js +1 -1
- package/dist/server/rpc/transport-selector.d.ts +3 -0
- package/dist/server/rpc/transport-selector.js +18 -0
- package/dist/server/utils/cache/auto-history.d.ts +4 -0
- package/dist/server/utils/cache/auto-history.js +22 -0
- package/dist/server/utils/{cache.d.ts → cache/paths.d.ts} +22 -5
- package/dist/server/utils/{cache.js → cache/paths.js} +36 -20
- package/dist/server/utils/cache/types.d.ts +8 -0
- package/dist/server/utils/cache/types.js +1 -0
- package/dist/server/utils/checksum.d.ts +2 -2
- package/dist/server/utils/checksum.js +1 -1
- package/dist/server/utils/clear-storage.d.ts +5 -0
- package/dist/server/utils/clear-storage.js +17 -0
- package/dist/server/utils/completion-normalizer.d.ts +17 -0
- package/dist/server/utils/completion-normalizer.js +301 -0
- package/dist/server/utils/expand-gguf-shards.d.ts +3 -0
- package/dist/server/utils/expand-gguf-shards.js +22 -0
- package/dist/server/utils/formatting.js +1 -1
- package/dist/server/utils/index.d.ts +4 -2
- package/dist/server/utils/index.js +4 -2
- package/dist/server/utils/qvac-paths.d.ts +2 -0
- package/dist/server/utils/qvac-paths.js +5 -0
- package/dist/server/utils/response-format.d.ts +3 -0
- package/dist/server/utils/response-format.js +20 -0
- package/dist/server/utils/tool-integration.d.ts +15 -4
- package/dist/server/utils/tool-integration.js +21 -41
- package/dist/server/utils/tools/dialect.d.ts +8 -0
- package/dist/server/utils/tools/dialect.js +12 -0
- package/dist/server/utils/tools/index.d.ts +3 -0
- package/dist/server/utils/tools/index.js +2 -0
- package/dist/server/utils/tools/parser.d.ts +6 -0
- package/dist/server/utils/tools/parser.js +40 -0
- package/dist/server/utils/tools/parsers/hermes.d.ts +4 -0
- package/dist/server/utils/tools/parsers/hermes.js +100 -0
- package/dist/server/utils/tools/parsers/json.d.ts +6 -0
- package/dist/server/utils/tools/parsers/json.js +115 -0
- package/dist/server/utils/tools/parsers/pythonic.d.ts +4 -0
- package/dist/server/utils/tools/parsers/pythonic.js +267 -0
- package/dist/server/utils/tools/shared.d.ts +18 -0
- package/dist/server/utils/tools/shared.js +54 -0
- package/dist/server/utils/worker-lock.d.ts +3 -0
- package/dist/server/utils/worker-lock.js +67 -0
- package/dist/server/worker-core.d.ts +16 -1
- package/dist/server/worker-core.js +114 -14
- package/dist/utils/aggregate-events.d.ts +16 -0
- package/dist/utils/aggregate-events.js +52 -0
- package/dist/utils/cache-normalize.d.ts +10 -0
- package/dist/utils/cache-normalize.js +23 -0
- package/dist/utils/encoding.d.ts +8 -0
- package/dist/utils/encoding.js +13 -0
- package/dist/utils/errors-client.d.ts +12 -0
- package/dist/utils/errors-client.js +20 -0
- package/dist/utils/errors-server.d.ts +14 -0
- package/dist/utils/errors-server.js +31 -0
- package/dist/utils/load-model-validation.d.ts +7 -0
- package/dist/utils/load-model-validation.js +18 -0
- package/expo/plugins/patches/android-link.mjs +1 -1
- package/expo/plugins/patches/ios-link.mjs +1 -1
- package/package.json +15 -19
- package/dist/examples/translation/translation-opus.d.ts +0 -2
- package/dist/examples/translation/translation-opus.js +0 -33
- package/dist/server/bare/utils/loader-adapter.d.ts +0 -7
- package/dist/server/bare/utils/loader-adapter.js +0 -9
- package/dist/server/utils/model-path.d.ts +0 -5
- package/dist/server/utils/model-path.js +0 -7
- package/dist/server/utils/tool-parser.d.ts +0 -7
- package/dist/server/utils/tool-parser.js +0 -248
|
@@ -11,20 +11,26 @@ function buildTranscribeRequest(params) {
|
|
|
11
11
|
? { type: "filePath", value: params.audioChunk }
|
|
12
12
|
: { type: "base64", value: params.audioChunk.toString("base64") },
|
|
13
13
|
...(params.prompt && { prompt: params.prompt }),
|
|
14
|
+
...(params.metadata === true && { metadata: true }),
|
|
14
15
|
};
|
|
15
16
|
}
|
|
16
|
-
/**
|
|
17
|
-
* Transcribe audio and return the complete text. Accepts either a file
|
|
18
|
-
* path or an audio buffer.
|
|
19
|
-
*
|
|
20
|
-
* @param params.modelId - The identifier of the transcription model to use
|
|
21
|
-
* @param params.audioChunk - Audio input as either a file path (string) or audio buffer
|
|
22
|
-
* @param params.prompt - Optional initial prompt to guide the transcription
|
|
23
|
-
* @param options - Optional RPC options including per-call profiling
|
|
24
|
-
* @returns The complete transcribed text
|
|
25
|
-
*/
|
|
26
17
|
export async function transcribe(params, options) {
|
|
27
18
|
const request = buildTranscribeRequest(params);
|
|
19
|
+
if (params.metadata === true) {
|
|
20
|
+
const segments = [];
|
|
21
|
+
for await (const response of stream(request, options)) {
|
|
22
|
+
if (response.type === "transcribe") {
|
|
23
|
+
const parsed = transcribeResponseSchema.parse(response);
|
|
24
|
+
if (parsed.segment) {
|
|
25
|
+
segments.push(parsed.segment);
|
|
26
|
+
}
|
|
27
|
+
if (parsed.done) {
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return segments;
|
|
33
|
+
}
|
|
28
34
|
let fullText = "";
|
|
29
35
|
for await (const response of stream(request, options)) {
|
|
30
36
|
if (response.type === "transcribe") {
|
|
@@ -42,30 +48,57 @@ export async function transcribe(params, options) {
|
|
|
42
48
|
export function transcribeStream(params, options) {
|
|
43
49
|
if ("audioChunk" in params && params.audioChunk !== undefined) {
|
|
44
50
|
logger.warn("transcribeStream() with audioChunk is deprecated — use transcribe() instead.");
|
|
51
|
+
if (params.metadata === true) {
|
|
52
|
+
return transcribeStreamWithAudioMetadata(params, options);
|
|
53
|
+
}
|
|
45
54
|
return transcribeStreamWithAudio(params, options);
|
|
46
55
|
}
|
|
47
|
-
|
|
56
|
+
const streamParams = params;
|
|
57
|
+
if (streamParams.metadata === true) {
|
|
58
|
+
return transcribeStreamDuplexMetadata(streamParams, options);
|
|
59
|
+
}
|
|
60
|
+
return transcribeStreamDuplex(streamParams, options);
|
|
48
61
|
}
|
|
49
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Streams `transcribe` wire responses for an upfront-audio request and yields
|
|
64
|
+
* the value extracted from each response frame until `done` is seen.
|
|
65
|
+
*/
|
|
66
|
+
async function* streamTranscribeValues(params, options, extract) {
|
|
50
67
|
const request = buildTranscribeRequest(params);
|
|
51
68
|
for await (const response of stream(request, options)) {
|
|
52
69
|
if (response.type === "transcribe") {
|
|
53
70
|
const parsed = transcribeResponseSchema.parse(response);
|
|
54
|
-
|
|
55
|
-
|
|
71
|
+
const value = extract(parsed);
|
|
72
|
+
if (value !== undefined)
|
|
73
|
+
yield value;
|
|
56
74
|
if (parsed.done)
|
|
57
75
|
break;
|
|
58
76
|
}
|
|
59
77
|
}
|
|
60
78
|
}
|
|
61
|
-
|
|
62
|
-
|
|
79
|
+
function transcribeStreamWithAudio(params, options) {
|
|
80
|
+
return streamTranscribeValues(params, options, (parsed) => parsed.text ? parsed.text : undefined);
|
|
81
|
+
}
|
|
82
|
+
function transcribeStreamWithAudioMetadata(params, options) {
|
|
83
|
+
return streamTranscribeValues(params, options, (parsed) => parsed.segment);
|
|
84
|
+
}
|
|
85
|
+
function buildTranscribeStreamRequest(params) {
|
|
86
|
+
return {
|
|
63
87
|
type: "transcribeStream",
|
|
64
88
|
modelId: params.modelId,
|
|
65
89
|
...(params.prompt && { prompt: params.prompt }),
|
|
90
|
+
...(params.metadata === true && { metadata: true }),
|
|
66
91
|
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Shared duplex session factory. The per-call line processor decides whether
|
|
95
|
+
* to surface strings or segments; `sessionName` is only used to label the
|
|
96
|
+
* "already iterated" error so callers see the correct session type.
|
|
97
|
+
*/
|
|
98
|
+
async function createTranscribeStreamSession(params, options, process, sessionName) {
|
|
99
|
+
const request = buildTranscribeStreamRequest(params);
|
|
67
100
|
const { requestStream, responseStream } = await duplex(request, options);
|
|
68
|
-
const responses =
|
|
101
|
+
const responses = parseLines(responseStream, process);
|
|
69
102
|
let consumed = false;
|
|
70
103
|
return {
|
|
71
104
|
write(audioChunk) {
|
|
@@ -80,21 +113,32 @@ async function transcribeStreamDuplex(params, options) {
|
|
|
80
113
|
},
|
|
81
114
|
[Symbol.asyncIterator]() {
|
|
82
115
|
if (consumed) {
|
|
83
|
-
throw new TranscriptionFailedError(
|
|
116
|
+
throw new TranscriptionFailedError(`${sessionName} can only be iterated once`);
|
|
84
117
|
}
|
|
85
118
|
consumed = true;
|
|
86
119
|
return responses;
|
|
87
120
|
},
|
|
88
121
|
};
|
|
89
122
|
}
|
|
90
|
-
|
|
123
|
+
function transcribeStreamDuplex(params, options) {
|
|
124
|
+
return createTranscribeStreamSession(params, options, processLine, "TranscribeStreamSession");
|
|
125
|
+
}
|
|
126
|
+
function transcribeStreamDuplexMetadata(params, options) {
|
|
127
|
+
return createTranscribeStreamSession(params, options, processLineMetadata, "TranscribeStreamMetadataSession");
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Line-delimited parser: reads newline-separated frames from a duplex
|
|
131
|
+
* response stream, passes each non-empty line through `process`, and yields
|
|
132
|
+
* whatever values it returns. `null` from `process` terminates the stream.
|
|
133
|
+
*/
|
|
134
|
+
async function* parseLines(responseStream, process) {
|
|
91
135
|
let buf = "";
|
|
92
136
|
for await (const chunk of responseStream) {
|
|
93
137
|
buf += chunk.toString();
|
|
94
138
|
const lines = buf.split("\n");
|
|
95
139
|
buf = lines.pop() || "";
|
|
96
140
|
for (const line of lines) {
|
|
97
|
-
const result =
|
|
141
|
+
const result = process(line);
|
|
98
142
|
if (result === null)
|
|
99
143
|
return;
|
|
100
144
|
if (result !== undefined)
|
|
@@ -103,32 +147,46 @@ async function* parseResponseLines(responseStream) {
|
|
|
103
147
|
}
|
|
104
148
|
// Process any residual data after stream ends
|
|
105
149
|
if (buf.trim()) {
|
|
106
|
-
const result =
|
|
150
|
+
const result = process(buf);
|
|
107
151
|
if (result !== null && result !== undefined)
|
|
108
152
|
yield result;
|
|
109
153
|
}
|
|
110
154
|
}
|
|
111
|
-
function
|
|
155
|
+
function parseResponseLine(line) {
|
|
112
156
|
if (!line.trim())
|
|
113
|
-
return
|
|
157
|
+
return null;
|
|
114
158
|
let parsed;
|
|
115
159
|
try {
|
|
116
160
|
parsed = JSON.parse(line);
|
|
117
161
|
}
|
|
118
162
|
catch {
|
|
119
163
|
logger.warn("transcribeStream: malformed JSON from server:", line);
|
|
120
|
-
return
|
|
164
|
+
return null;
|
|
121
165
|
}
|
|
122
166
|
const obj = parsed;
|
|
123
167
|
if (obj["type"] === "error") {
|
|
124
168
|
throw new TranscriptionFailedError(obj["message"] ?? "Unknown server error");
|
|
125
169
|
}
|
|
126
|
-
|
|
170
|
+
return transcribeStreamResponseSchema.parse(parsed);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Shared wire-frame decoder. Returns `null` for the terminal `done` frame,
|
|
174
|
+
* `undefined` for frames the caller should skip, or the value extracted from
|
|
175
|
+
* the frame.
|
|
176
|
+
*/
|
|
177
|
+
function processWith(line, extract) {
|
|
178
|
+
const response = parseResponseLine(line);
|
|
179
|
+
if (response === null)
|
|
180
|
+
return undefined;
|
|
127
181
|
if (response.error)
|
|
128
182
|
throw new TranscriptionFailedError(response.error);
|
|
129
183
|
if (response.done)
|
|
130
184
|
return null;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
185
|
+
return extract(response);
|
|
186
|
+
}
|
|
187
|
+
function processLine(line) {
|
|
188
|
+
return processWith(line, (response) => response.text?.trim() ? response.text : undefined);
|
|
189
|
+
}
|
|
190
|
+
function processLineMetadata(line) {
|
|
191
|
+
return processWith(line, (response) => response.segment);
|
|
134
192
|
}
|
|
@@ -9,6 +9,7 @@ import { type TranslateClientParams, type TranslationStats, type RPCOptions } fr
|
|
|
9
9
|
* @param params.from - Source language code (optional)
|
|
10
10
|
* @param params.to - Target language code
|
|
11
11
|
* @param params.stream - Whether to stream tokens (true) or return complete response (false). Defaults to true.
|
|
12
|
+
* @param options - Optional RPC options (timeout, profiling, force new connection, etc.).
|
|
12
13
|
* @returns Object with tokenStream generator and text/stats properties
|
|
13
14
|
* @throws {QvacErrorBase} When translation fails with an error message or when language detection fails
|
|
14
15
|
* @example
|
|
@@ -12,6 +12,7 @@ import { TranslationFailedError } from "../../utils/errors-client.js";
|
|
|
12
12
|
* @param params.from - Source language code (optional)
|
|
13
13
|
* @param params.to - Target language code
|
|
14
14
|
* @param params.stream - Whether to stream tokens (true) or return complete response (false). Defaults to true.
|
|
15
|
+
* @param options - Optional RPC options (timeout, profiling, force new connection, etc.).
|
|
15
16
|
* @returns Object with tokenStream generator and text/stats properties
|
|
16
17
|
* @throws {QvacErrorBase} When translation fails with an error message or when language detection fails
|
|
17
18
|
* @example
|
|
@@ -11,7 +11,8 @@ import { resolveModelConfig } from "../../server/bare/registry/model-config-regi
|
|
|
11
11
|
import { resolveConfig } from "../config-loader/resolve-config.bare.js";
|
|
12
12
|
import { getClientLogger } from "../../logging/index.js";
|
|
13
13
|
import { getAllPlugins } from "../../server/plugins/index.js";
|
|
14
|
-
import { initializeWorkerCore, shutdownBareDirectWorker, } from "../../server/worker-core.js";
|
|
14
|
+
import { initializeWorkerCore, shutdownBareDirectWorker, cleanupForTerminate, } from "../../server/worker-core.js";
|
|
15
|
+
import { assertLifecycleAllowed } from "../../server/bare/runtime-lifecycle.js";
|
|
15
16
|
const logger = getClientLogger();
|
|
16
17
|
/**
|
|
17
18
|
* Load worker entry to register plugins before any SDK calls.
|
|
@@ -101,6 +102,7 @@ async function* streamWithProgress(request, handler) {
|
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
export async function send(request) {
|
|
105
|
+
assertLifecycleAllowed(request);
|
|
104
106
|
const handler = getHandler(request.type);
|
|
105
107
|
if (!handler)
|
|
106
108
|
throw new RPCNoHandlerError(request.type);
|
|
@@ -108,6 +110,7 @@ export async function send(request) {
|
|
|
108
110
|
return (await handler(processedRequest));
|
|
109
111
|
}
|
|
110
112
|
async function* stream(request) {
|
|
113
|
+
assertLifecycleAllowed(request);
|
|
111
114
|
const handler = getHandler(request.type);
|
|
112
115
|
if (!handler)
|
|
113
116
|
throw new RPCNoHandlerError(request.type);
|
|
@@ -160,6 +163,23 @@ function createMockRPCRequest() {
|
|
|
160
163
|
}));
|
|
161
164
|
}
|
|
162
165
|
}
|
|
166
|
+
// Handle special pre-terminate cleanup signal. In direct mode the
|
|
167
|
+
// bare runtime is the host JS context, so we run cleanup but never
|
|
168
|
+
// exit the process here.
|
|
169
|
+
if (typeof requestData === "object" &&
|
|
170
|
+
"type" in requestData &&
|
|
171
|
+
requestData.type === "__shutdown__") {
|
|
172
|
+
try {
|
|
173
|
+
await cleanupForTerminate();
|
|
174
|
+
return Buffer.from(JSON.stringify({ success: true }));
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
return Buffer.from(JSON.stringify({
|
|
178
|
+
success: false,
|
|
179
|
+
error: error instanceof Error ? error.message : String(error),
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
163
183
|
const response = await send(requestData);
|
|
164
184
|
return Buffer.from(JSON.stringify(response));
|
|
165
185
|
},
|
|
@@ -203,6 +223,7 @@ export async function createDuplexSession(payload, _commandId) {
|
|
|
203
223
|
await getRPC();
|
|
204
224
|
const { PassThrough } = await import("bare-stream");
|
|
205
225
|
const request = JSON.parse(payload);
|
|
226
|
+
assertLifecycleAllowed(request);
|
|
206
227
|
const entry = registry[request.type];
|
|
207
228
|
if (!entry || entry.type !== "duplex") {
|
|
208
229
|
throw new RPCNoHandlerError(request.type);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import RPC from "bare-rpc";
|
|
2
2
|
export declare function getRPC(): Promise<RPC>;
|
|
3
|
-
export declare function close(): void
|
|
3
|
+
export declare function close(): Promise<void>;
|
|
4
4
|
export declare function createDuplexSession(payload: string, commandId: number): Promise<{
|
|
5
5
|
requestStream: RPC.OutgoingStream;
|
|
6
6
|
responseStream: RPC.IncomingStream;
|
|
@@ -10,6 +10,10 @@ let rpcPromise = null;
|
|
|
10
10
|
let workletInstance = null;
|
|
11
11
|
let workletInitialized = false;
|
|
12
12
|
let cachedRuntimeContext;
|
|
13
|
+
// Set while close() is in flight. Concurrent callers share the same
|
|
14
|
+
// promise instead of double-sending __shutdown__ or re-entering the
|
|
15
|
+
// terminate block on already-null state.
|
|
16
|
+
let closingPromise = null;
|
|
13
17
|
logger.debug("EXPO RPC Client bundle");
|
|
14
18
|
async function getRuntimeContext() {
|
|
15
19
|
if (cachedRuntimeContext) {
|
|
@@ -93,10 +97,98 @@ export async function getRPC() {
|
|
|
93
97
|
rpcPromise = null;
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
const SHUTDOWN_RPC_TIMEOUT_MS = 10_000;
|
|
101
|
+
/**
|
|
102
|
+
* Pre-terminate cleanup roundtrip. Sends an internal `__shutdown__` message
|
|
103
|
+
* to the worker so it can clear addon plugin registries (calls each addon's
|
|
104
|
+
* `releaseLogger` → frees env-bound js_ref_t state) and unload all model
|
|
105
|
+
* instances BEFORE we kill the worklet.
|
|
106
|
+
*
|
|
107
|
+
* Without this, the worklet's V8 isolate dies while addon static state still
|
|
108
|
+
* holds js_ref_t handles into it; the next worklet's first `setLogger` call
|
|
109
|
+
* trips a V8 GlobalHandle assertion (brk 0 / SIGTRAP) and the iOS app dies.
|
|
110
|
+
*
|
|
111
|
+
* Best-effort: never throws. Falls through to terminate even on timeout.
|
|
112
|
+
*/
|
|
113
|
+
async function sendShutdownMessage(rpc) {
|
|
114
|
+
let timer;
|
|
115
|
+
try {
|
|
116
|
+
await Promise.race([
|
|
117
|
+
(async () => {
|
|
118
|
+
const req = rpc.request(1);
|
|
119
|
+
req.send(JSON.stringify({ type: "__shutdown__" }), "utf8");
|
|
120
|
+
const response = await req.reply("utf8");
|
|
121
|
+
const parsed = JSON.parse(String(response));
|
|
122
|
+
if (!parsed.success) {
|
|
123
|
+
throw new Error(parsed.error ?? "Worker reported cleanup failure");
|
|
124
|
+
}
|
|
125
|
+
})(),
|
|
126
|
+
new Promise((_, reject) => {
|
|
127
|
+
timer = setTimeout(() => reject(new Error(`Worker did not ack __shutdown__ within ${SHUTDOWN_RPC_TIMEOUT_MS}ms`)), SHUTDOWN_RPC_TIMEOUT_MS);
|
|
128
|
+
}),
|
|
129
|
+
]);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
// Best-effort: log but don't block termination if cleanup fails.
|
|
133
|
+
logger.warn(`⚠️ Pre-terminate worker cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
if (timer)
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export async function close() {
|
|
141
|
+
// Concurrent callers (or a getRPC retry that overlaps with a manual
|
|
142
|
+
// close) share the in-flight close promise instead of each sending
|
|
143
|
+
// their own __shutdown__ and racing on the terminate block.
|
|
144
|
+
if (closingPromise)
|
|
145
|
+
return closingPromise;
|
|
146
|
+
closingPromise = (async () => {
|
|
147
|
+
logger.info("🧹 Closing RPC client (Expo)");
|
|
148
|
+
// terminate() crashes on Android (addon dlclose leaves pthread_key_t
|
|
149
|
+
// destructors dangling); iOS dyld no-ops dlclose so it's safe there.
|
|
150
|
+
// Non-iOS: drop refs only -- sending __shutdown__ without a follow-up
|
|
151
|
+
// terminate would clear the worker plugin registry.
|
|
152
|
+
let platform;
|
|
153
|
+
try {
|
|
154
|
+
platform = (await getRuntimeContext()).platform;
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
logger.debug("Failed to resolve runtime context for close()", { err });
|
|
158
|
+
}
|
|
159
|
+
if (platform !== "ios") {
|
|
160
|
+
rpcInstance = null;
|
|
161
|
+
rpcPromise = null;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// iOS: existing pre-terminate cleanup + terminate.
|
|
165
|
+
if (rpcInstance) {
|
|
166
|
+
logger.info("🧹 Requesting worker pre-terminate cleanup");
|
|
167
|
+
await sendShutdownMessage(rpcInstance);
|
|
168
|
+
}
|
|
169
|
+
rpcInstance = null;
|
|
170
|
+
rpcPromise = null;
|
|
171
|
+
if (workletInstance) {
|
|
172
|
+
logger.info("🐻🔫 Terminating bare worklet");
|
|
173
|
+
try {
|
|
174
|
+
workletInstance.terminate();
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
logger.debug("Failed to terminate worklet", { error });
|
|
178
|
+
}
|
|
179
|
+
workletInstance = null;
|
|
180
|
+
workletInitialized = false;
|
|
181
|
+
}
|
|
182
|
+
})();
|
|
183
|
+
try {
|
|
184
|
+
await closingPromise;
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
// Reset so a subsequent close() (e.g. after a fresh getRPC spawned
|
|
188
|
+
// a new worklet) can run again. Body guards on rpcInstance / workletInstance
|
|
189
|
+
// make a redundant call a no-op if state is already cleared.
|
|
190
|
+
closingPromise = null;
|
|
191
|
+
}
|
|
100
192
|
}
|
|
101
193
|
export async function createDuplexSession(payload, commandId) {
|
|
102
194
|
const rpc = await getRPC();
|
|
@@ -10,6 +10,8 @@ import { fileURLToPath } from "node:url";
|
|
|
10
10
|
import { initializeConfig } from "../init-hooks.js";
|
|
11
11
|
import { resolveConfig } from "../config-loader/resolve-config.node.js";
|
|
12
12
|
import { getClientLogger } from "../../logging/index.js";
|
|
13
|
+
import { RPCInitTimeoutError } from "../../utils/errors-client.js";
|
|
14
|
+
const RPC_INIT_TIMEOUT_MS = 30_000;
|
|
13
15
|
const logger = getClientLogger();
|
|
14
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
17
|
const __dirname = path.dirname(__filename);
|
|
@@ -168,11 +170,31 @@ async function ensureRPC() {
|
|
|
168
170
|
const socketPath = createSocketPath();
|
|
169
171
|
currentSocketPath = socketPath;
|
|
170
172
|
rpcPromise = new Promise((resolve, reject) => {
|
|
173
|
+
let settled = false;
|
|
174
|
+
const timer = setTimeout(() => {
|
|
175
|
+
if (settled)
|
|
176
|
+
return;
|
|
177
|
+
settled = true;
|
|
178
|
+
rpcPromise = null;
|
|
179
|
+
rpcInstance = null;
|
|
180
|
+
bareWorkerProc = null;
|
|
181
|
+
ipcServer = null;
|
|
182
|
+
currentSocketPath = null;
|
|
183
|
+
reject(new RPCInitTimeoutError(RPC_INIT_TIMEOUT_MS));
|
|
184
|
+
}, RPC_INIT_TIMEOUT_MS);
|
|
171
185
|
ipcServer = createServer((socket) => {
|
|
186
|
+
if (settled)
|
|
187
|
+
return;
|
|
188
|
+
settled = true;
|
|
189
|
+
clearTimeout(timer);
|
|
172
190
|
rpcInstance = new RPC(socket, () => { });
|
|
173
191
|
resolve(rpcInstance);
|
|
174
192
|
});
|
|
175
193
|
ipcServer.on("error", (error) => {
|
|
194
|
+
if (settled)
|
|
195
|
+
return;
|
|
196
|
+
settled = true;
|
|
197
|
+
clearTimeout(timer);
|
|
176
198
|
rpcPromise = null;
|
|
177
199
|
rpcInstance = null;
|
|
178
200
|
bareWorkerProc = null;
|
|
@@ -191,6 +213,20 @@ async function ensureRPC() {
|
|
|
191
213
|
],
|
|
192
214
|
stdio: ["inherit", "inherit", "inherit"],
|
|
193
215
|
});
|
|
216
|
+
if (bareWorkerProc) {
|
|
217
|
+
bareWorkerProc.on("exit", (code) => {
|
|
218
|
+
if (settled)
|
|
219
|
+
return;
|
|
220
|
+
settled = true;
|
|
221
|
+
clearTimeout(timer);
|
|
222
|
+
rpcPromise = null;
|
|
223
|
+
rpcInstance = null;
|
|
224
|
+
bareWorkerProc = null;
|
|
225
|
+
ipcServer = null;
|
|
226
|
+
currentSocketPath = null;
|
|
227
|
+
reject(new RPCInitTimeoutError(RPC_INIT_TIMEOUT_MS, new Error(`Worker process exited with code ${code} before IPC connection was established`)));
|
|
228
|
+
});
|
|
229
|
+
}
|
|
194
230
|
});
|
|
195
231
|
});
|
|
196
232
|
const rpc = await rpcPromise;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type MicFormat = "f32le" | "s16le";
|
|
2
|
+
export declare function listWindowsAudioDevices(): string[];
|
|
3
|
+
export declare function getAudioInputArgs(): string[];
|
|
4
|
+
export interface StartMicrophoneOptions {
|
|
5
|
+
sampleRate: number;
|
|
6
|
+
format: MicFormat;
|
|
7
|
+
}
|
|
8
|
+
export declare function startMicrophone(options: StartMicrophoneOptions): import("child_process").ChildProcessByStdio<null, import("stream").Readable, null>;
|
|
9
|
+
//# sourceMappingURL=mic-input.d.ts.map
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared microphone-capture helpers for the transcription and
|
|
3
|
+
* voice-assistant examples. Wraps ffmpeg so the examples don't each
|
|
4
|
+
* re-implement device selection.
|
|
5
|
+
*
|
|
6
|
+
* The default device is the system default mic. Override on any OS
|
|
7
|
+
* with the MIC_DEVICE environment variable. On Windows, if no override
|
|
8
|
+
* is set, the first DirectShow audio device reported by ffmpeg is used.
|
|
9
|
+
*/
|
|
10
|
+
import { spawn, spawnSync } from "child_process";
|
|
11
|
+
import { platform } from "os";
|
|
12
|
+
// Parses the DirectShow audio device list printed by:
|
|
13
|
+
// ffmpeg -hide_banner -f dshow -list_devices true -i dummy
|
|
14
|
+
// Output goes to stderr; ffmpeg always exits non-zero because `-i dummy`
|
|
15
|
+
// isn't a real device — that's expected.
|
|
16
|
+
//
|
|
17
|
+
// Handles both ffmpeg output formats:
|
|
18
|
+
//
|
|
19
|
+
// Old (ffmpeg ≤ 4.3):
|
|
20
|
+
// [dshow @ ...] DirectShow audio devices
|
|
21
|
+
// [dshow @ ...] "Microphone (Realtek Audio)"
|
|
22
|
+
// [dshow @ ...] Alternative name "@device_cm_..."
|
|
23
|
+
//
|
|
24
|
+
// New (ffmpeg ≥ 4.4):
|
|
25
|
+
// [dshow @ ...] "Integrated Camera" (video)
|
|
26
|
+
// [dshow @ ...] Alternative name "@device_pnp_..."
|
|
27
|
+
// [dshow @ ...] "Microphone (Realtek Audio)" (audio)
|
|
28
|
+
// [dshow @ ...] Alternative name "@device_cm_..."
|
|
29
|
+
//
|
|
30
|
+
// The newer format dropped the "DirectShow audio devices" header and tags
|
|
31
|
+
// each device line with `(audio)` or `(video)` instead.
|
|
32
|
+
export function listWindowsAudioDevices() {
|
|
33
|
+
const result = spawnSync("ffmpeg", ["-hide_banner", "-f", "dshow", "-list_devices", "true", "-i", "dummy"], { encoding: "utf8" });
|
|
34
|
+
const stderr = result.stderr ?? "";
|
|
35
|
+
const devices = [];
|
|
36
|
+
let section = null;
|
|
37
|
+
for (const line of stderr.split(/\r?\n/)) {
|
|
38
|
+
// Old-format section headers.
|
|
39
|
+
if (line.includes("DirectShow audio devices")) {
|
|
40
|
+
section = "audio";
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (line.includes("DirectShow video devices")) {
|
|
44
|
+
section = "video";
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
// New-format per-line tag wins over the section state.
|
|
48
|
+
const hasAudioTag = /\(audio\)/.test(line);
|
|
49
|
+
const hasVideoTag = /\(video\)/.test(line);
|
|
50
|
+
const lineSection = hasAudioTag ? "audio" : hasVideoTag ? "video" : section;
|
|
51
|
+
if (lineSection !== "audio")
|
|
52
|
+
continue;
|
|
53
|
+
// First quoted token is the device name. Alternative-name lines start
|
|
54
|
+
// with `@device_...` and are filtered out.
|
|
55
|
+
const match = line.match(/"([^"]+)"/);
|
|
56
|
+
if (!match)
|
|
57
|
+
continue;
|
|
58
|
+
const name = match[1];
|
|
59
|
+
if (!name || name.startsWith("@device"))
|
|
60
|
+
continue;
|
|
61
|
+
devices.push(name);
|
|
62
|
+
}
|
|
63
|
+
return devices;
|
|
64
|
+
}
|
|
65
|
+
export function getAudioInputArgs() {
|
|
66
|
+
const env = process.env;
|
|
67
|
+
const override = env["MIC_DEVICE"];
|
|
68
|
+
switch (platform()) {
|
|
69
|
+
case "darwin":
|
|
70
|
+
// AVFoundation device spec is `<video>:<audio>`. `:0` = default audio.
|
|
71
|
+
// Override with e.g. MIC_DEVICE=":1" to pick another device, or run
|
|
72
|
+
// `ffmpeg -f avfoundation -list_devices true -i ""` to enumerate.
|
|
73
|
+
return ["-f", "avfoundation", "-i", override ?? ":0"];
|
|
74
|
+
case "linux":
|
|
75
|
+
// PulseAudio source name. `default` follows the system default mic.
|
|
76
|
+
// Override with MIC_DEVICE="alsa_input.pci-..." etc.; list with
|
|
77
|
+
// `pactl list short sources`.
|
|
78
|
+
return ["-f", "pulse", "-i", override ?? "default"];
|
|
79
|
+
case "win32": {
|
|
80
|
+
if (override)
|
|
81
|
+
return ["-f", "dshow", "-i", `audio=${override}`];
|
|
82
|
+
const devices = listWindowsAudioDevices();
|
|
83
|
+
const deviceName = devices[0];
|
|
84
|
+
if (!deviceName) {
|
|
85
|
+
throw new Error("No Windows audio input device found. List devices with:\n" +
|
|
86
|
+
" ffmpeg -hide_banner -f dshow -list_devices true -i dummy\n" +
|
|
87
|
+
'Then set MIC_DEVICE="Your Device Name" and retry.');
|
|
88
|
+
}
|
|
89
|
+
return ["-f", "dshow", "-i", `audio=${deviceName}`];
|
|
90
|
+
}
|
|
91
|
+
default:
|
|
92
|
+
throw new Error(`Unsupported platform: ${platform()}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export function startMicrophone(options) {
|
|
96
|
+
const formatArgs = options.format === "f32le"
|
|
97
|
+
? ["-sample_fmt", "flt", "-f", "f32le"]
|
|
98
|
+
: ["-sample_fmt", "s16", "-f", "s16le"];
|
|
99
|
+
const ffmpeg = spawn("ffmpeg", [
|
|
100
|
+
...getAudioInputArgs(),
|
|
101
|
+
"-ar",
|
|
102
|
+
String(options.sampleRate),
|
|
103
|
+
"-ac",
|
|
104
|
+
"1",
|
|
105
|
+
...formatArgs,
|
|
106
|
+
"pipe:1",
|
|
107
|
+
], { stdio: ["ignore", "pipe", "ignore"] });
|
|
108
|
+
if (!ffmpeg.stdout)
|
|
109
|
+
throw new Error("Failed to open microphone");
|
|
110
|
+
return ffmpeg;
|
|
111
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event-driven completion — demonstrates the unified `CompletionEvent` stream.
|
|
3
|
+
*
|
|
4
|
+
* `completion()` returns a `CompletionRun` with two primary surfaces:
|
|
5
|
+
*
|
|
6
|
+
* - `events` — an `AsyncIterable<CompletionEvent>` of ordered, typed events
|
|
7
|
+
* (`contentDelta`, `thinkingDelta`, `toolCall`, `toolError`,
|
|
8
|
+
* `completionStats`, `completionDone`, `rawDelta`).
|
|
9
|
+
* - `final` — a `Promise<CompletionFinal>` that resolves once the stream
|
|
10
|
+
* ends, providing aggregated `contentText`, `thinkingText`,
|
|
11
|
+
* `toolCalls`, `stats`, and `raw.fullText`.
|
|
12
|
+
*
|
|
13
|
+
* Set `captureThinking: true` to attempt best-effort `<think>` block parsing
|
|
14
|
+
* into dedicated `thinkingDelta` events. `final.raw.fullText` keeps the exact
|
|
15
|
+
* model output.
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=completion-events.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event-driven completion — demonstrates the unified `CompletionEvent` stream.
|
|
3
|
+
*
|
|
4
|
+
* `completion()` returns a `CompletionRun` with two primary surfaces:
|
|
5
|
+
*
|
|
6
|
+
* - `events` — an `AsyncIterable<CompletionEvent>` of ordered, typed events
|
|
7
|
+
* (`contentDelta`, `thinkingDelta`, `toolCall`, `toolError`,
|
|
8
|
+
* `completionStats`, `completionDone`, `rawDelta`).
|
|
9
|
+
* - `final` — a `Promise<CompletionFinal>` that resolves once the stream
|
|
10
|
+
* ends, providing aggregated `contentText`, `thinkingText`,
|
|
11
|
+
* `toolCalls`, `stats`, and `raw.fullText`.
|
|
12
|
+
*
|
|
13
|
+
* Set `captureThinking: true` to attempt best-effort `<think>` block parsing
|
|
14
|
+
* into dedicated `thinkingDelta` events. `final.raw.fullText` keeps the exact
|
|
15
|
+
* model output.
|
|
16
|
+
*/
|
|
17
|
+
import { completion, loadModel, unloadModel, QWEN3_600M_INST_Q4, } from "@qvac/sdk";
|
|
18
|
+
try {
|
|
19
|
+
const modelId = await loadModel({
|
|
20
|
+
modelSrc: QWEN3_600M_INST_Q4,
|
|
21
|
+
modelType: "llm",
|
|
22
|
+
modelConfig: { ctx_size: 4096 },
|
|
23
|
+
onProgress: (p) => console.log(`Loading: ${p.percentage.toFixed(1)}%`),
|
|
24
|
+
});
|
|
25
|
+
console.log(`✅ Model loaded: ${modelId}\n`);
|
|
26
|
+
const result = completion({
|
|
27
|
+
modelId,
|
|
28
|
+
history: [{ role: "user", content: "Explain quantum computing in 2 sentences" }],
|
|
29
|
+
stream: true,
|
|
30
|
+
captureThinking: true,
|
|
31
|
+
});
|
|
32
|
+
for await (const event of result.events) {
|
|
33
|
+
handleEvent(event);
|
|
34
|
+
}
|
|
35
|
+
const final = await result.final;
|
|
36
|
+
console.log("\n\n--- Final Result ---");
|
|
37
|
+
console.log(`Content: ${final.contentText}\n`);
|
|
38
|
+
if (final.thinkingText) {
|
|
39
|
+
console.log(`Thinking: ${final.thinkingText}\n`);
|
|
40
|
+
}
|
|
41
|
+
if (final.stats) {
|
|
42
|
+
console.log(`Stats: ${final.stats.tokensPerSecond?.toFixed(1)} tok/s`);
|
|
43
|
+
}
|
|
44
|
+
if (final.toolCalls.length > 0) {
|
|
45
|
+
console.log(`Tool calls: ${final.toolCalls.map((c) => c.name).join(", ")}`);
|
|
46
|
+
}
|
|
47
|
+
console.log(`Raw output length: ${final.raw.fullText.length} chars`);
|
|
48
|
+
await unloadModel({ modelId, clearStorage: false });
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error("❌ Error:", error);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
function handleEvent(event) {
|
|
55
|
+
switch (event.type) {
|
|
56
|
+
case "contentDelta":
|
|
57
|
+
process.stdout.write(event.text);
|
|
58
|
+
break;
|
|
59
|
+
case "thinkingDelta":
|
|
60
|
+
process.stdout.write(`\x1b[2m${event.text}\x1b[0m`);
|
|
61
|
+
break;
|
|
62
|
+
case "toolCall":
|
|
63
|
+
console.log(`\n→ Tool: ${event.call.name}(${JSON.stringify(event.call.arguments)})`);
|
|
64
|
+
break;
|
|
65
|
+
case "toolError":
|
|
66
|
+
console.warn(`\n⚠ Tool error [${event.error.code}]: ${event.error.message}`);
|
|
67
|
+
break;
|
|
68
|
+
case "completionStats":
|
|
69
|
+
console.log(`\n📊 ${event.stats.tokensPerSecond?.toFixed(1)} tok/s`);
|
|
70
|
+
break;
|
|
71
|
+
case "completionDone":
|
|
72
|
+
if (event.stopReason === "error" && "error" in event) {
|
|
73
|
+
console.error(`\n❌ ${event.error.message}`);
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
case "rawDelta":
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|