@qvac/sdk 0.9.2 โ 0.10.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 +42 -95
- 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-structured-output.d.ts +2 -0
- package/dist/examples/llamacpp-structured-output.js +106 -0
- package/dist/examples/plugins.js +4 -4
- package/dist/examples/suspend-resume.js +21 -4
- package/dist/examples/{llamacpp-native-tools.js โ tools/llamacpp-native-tools.js} +6 -55
- package/dist/examples/tools/llamacpp-tools-harmony-multiturn.d.ts +2 -0
- package/dist/examples/tools/llamacpp-tools-harmony-multiturn.js +81 -0
- package/dist/examples/tools/llamacpp-tools-pythonic.d.ts +2 -0
- package/dist/examples/tools/llamacpp-tools-pythonic.js +50 -0
- package/dist/examples/tools/shared.d.ts +21 -0
- package/dist/examples/tools/shared.js +42 -0
- 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 +6973 -7457
- package/dist/models/registry/models.js +6889 -6855
- 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.d.ts +1 -0
- package/dist/models/update-models/registry.js +23 -3
- 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 +354 -271
- 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 +126 -31
- package/dist/schemas/completion-stream.js +138 -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/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 +80 -22
- 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 +54 -116
- package/dist/server/bare/plugins/nmtcpp-translation/resolve-vocab.d.ts +32 -0
- package/dist/server/bare/plugins/nmtcpp-translation/resolve-vocab.js +117 -0
- 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 +413 -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 +14 -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 +46 -0
- package/dist/server/utils/tools/parsers/harmony.d.ts +4 -0
- package/dist/server/utils/tools/parsers/harmony.js +59 -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 +14 -18
- 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
- /package/dist/examples/{llamacpp-native-tools.d.ts โ tools/llamacpp-native-tools.d.ts} +0 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { generateStableToolCallId, validateToolArguments, } from "../shared.js";
|
|
2
|
+
// Pythonic `[name(k=v), ...]` arrays. LFM 2.x is auto-routed; Llama-style
|
|
3
|
+
// tokens are kept for callers that opt in via `completion({ toolDialect:
|
|
4
|
+
// "pythonic" })` for a model known to emit Pythonic. Pure string parsing โ
|
|
5
|
+
// no eval/vm/Function (untrusted model output).
|
|
6
|
+
const PYTHONIC_LFM_OPEN = "<|tool_call_start|>";
|
|
7
|
+
const PYTHONIC_LFM_CLOSE = "<|tool_call_end|>";
|
|
8
|
+
const PYTHONIC_LLAMA_OPEN = "<|start_header_id|>tool_call<|end_header_id|>";
|
|
9
|
+
const PYTHONIC_LLAMA_CLOSE = "<|eot_id|>";
|
|
10
|
+
function locatePythonicCallList(text) {
|
|
11
|
+
const lfmStart = text.indexOf(PYTHONIC_LFM_OPEN);
|
|
12
|
+
if (lfmStart !== -1) {
|
|
13
|
+
const after = text.slice(lfmStart + PYTHONIC_LFM_OPEN.length);
|
|
14
|
+
const end = after.indexOf(PYTHONIC_LFM_CLOSE);
|
|
15
|
+
return end === -1 ? after : after.slice(0, end);
|
|
16
|
+
}
|
|
17
|
+
const llamaStart = text.indexOf(PYTHONIC_LLAMA_OPEN);
|
|
18
|
+
if (llamaStart !== -1) {
|
|
19
|
+
const after = text.slice(llamaStart + PYTHONIC_LLAMA_OPEN.length);
|
|
20
|
+
const end = after.indexOf(PYTHONIC_LLAMA_CLOSE);
|
|
21
|
+
return end === -1 ? after : after.slice(0, end);
|
|
22
|
+
}
|
|
23
|
+
// Identifier+`(` requirement rejects JSON arrays and plain bracketed lists.
|
|
24
|
+
const bareMatch = /\[\s*[A-Za-z_]\w*\s*\(/.exec(text);
|
|
25
|
+
if (bareMatch)
|
|
26
|
+
return text.slice(bareMatch.index);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function pyPeek(s, offset = 0) {
|
|
30
|
+
return s.text[s.pos + offset] ?? "";
|
|
31
|
+
}
|
|
32
|
+
function pyConsume(s) {
|
|
33
|
+
const c = s.text[s.pos] ?? "";
|
|
34
|
+
s.pos++;
|
|
35
|
+
return c;
|
|
36
|
+
}
|
|
37
|
+
function pySkipWs(s) {
|
|
38
|
+
while (/\s/.test(pyPeek(s)))
|
|
39
|
+
s.pos++;
|
|
40
|
+
}
|
|
41
|
+
function pyParseString(s) {
|
|
42
|
+
const quote = pyConsume(s);
|
|
43
|
+
let out = "";
|
|
44
|
+
while (s.pos < s.text.length && pyPeek(s) !== quote) {
|
|
45
|
+
if (pyPeek(s) === "\\") {
|
|
46
|
+
s.pos++;
|
|
47
|
+
const esc = pyConsume(s);
|
|
48
|
+
switch (esc) {
|
|
49
|
+
case "n":
|
|
50
|
+
out += "\n";
|
|
51
|
+
break;
|
|
52
|
+
case "t":
|
|
53
|
+
out += "\t";
|
|
54
|
+
break;
|
|
55
|
+
case "r":
|
|
56
|
+
out += "\r";
|
|
57
|
+
break;
|
|
58
|
+
case "\\":
|
|
59
|
+
out += "\\";
|
|
60
|
+
break;
|
|
61
|
+
case "'":
|
|
62
|
+
out += "'";
|
|
63
|
+
break;
|
|
64
|
+
case '"':
|
|
65
|
+
out += '"';
|
|
66
|
+
break;
|
|
67
|
+
case "0":
|
|
68
|
+
out += "\0";
|
|
69
|
+
break;
|
|
70
|
+
default:
|
|
71
|
+
out += esc;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
out += pyConsume(s);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (s.pos >= s.text.length) {
|
|
80
|
+
throw new Error("Unterminated string literal");
|
|
81
|
+
}
|
|
82
|
+
pyConsume(s);
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
function pyParseNumber(s) {
|
|
86
|
+
const start = s.pos;
|
|
87
|
+
if (pyPeek(s) === "-" || pyPeek(s) === "+")
|
|
88
|
+
s.pos++;
|
|
89
|
+
while (/\d/.test(pyPeek(s)))
|
|
90
|
+
s.pos++;
|
|
91
|
+
if (pyPeek(s) === ".") {
|
|
92
|
+
s.pos++;
|
|
93
|
+
while (/\d/.test(pyPeek(s)))
|
|
94
|
+
s.pos++;
|
|
95
|
+
}
|
|
96
|
+
if (pyPeek(s) === "e" || pyPeek(s) === "E") {
|
|
97
|
+
s.pos++;
|
|
98
|
+
if (pyPeek(s) === "-" || pyPeek(s) === "+")
|
|
99
|
+
s.pos++;
|
|
100
|
+
while (/\d/.test(pyPeek(s)))
|
|
101
|
+
s.pos++;
|
|
102
|
+
}
|
|
103
|
+
const literal = s.text.slice(start, s.pos);
|
|
104
|
+
const n = Number(literal);
|
|
105
|
+
if (Number.isNaN(n) || literal.length === 0) {
|
|
106
|
+
throw new Error(`Invalid number literal: ${JSON.stringify(literal)}`);
|
|
107
|
+
}
|
|
108
|
+
return n;
|
|
109
|
+
}
|
|
110
|
+
function pyParseIdentifier(s) {
|
|
111
|
+
if (!/[A-Za-z_]/.test(pyPeek(s))) {
|
|
112
|
+
throw new Error(`Expected identifier, got ${JSON.stringify(pyPeek(s))}`);
|
|
113
|
+
}
|
|
114
|
+
const start = s.pos;
|
|
115
|
+
while (/[\w]/.test(pyPeek(s)))
|
|
116
|
+
s.pos++;
|
|
117
|
+
return s.text.slice(start, s.pos);
|
|
118
|
+
}
|
|
119
|
+
// Identifier-as-string fallback: any bare word that isn't `True`/`False`/`None`
|
|
120
|
+
// is returned as a string, so `[get_weather(city=Paris)]` still produces
|
|
121
|
+
// `{ city: "Paris" }`. Side-effect: lowercase `null` / `true` / `false` from
|
|
122
|
+
// JS-trained models also coerce to strings โ validate against your tool schema.
|
|
123
|
+
function pyParseValue(s) {
|
|
124
|
+
pySkipWs(s);
|
|
125
|
+
const c = pyPeek(s);
|
|
126
|
+
if (c === '"' || c === "'")
|
|
127
|
+
return pyParseString(s);
|
|
128
|
+
if (c === "[")
|
|
129
|
+
return pyParseList(s);
|
|
130
|
+
if (c === "{")
|
|
131
|
+
return pyParseDict(s);
|
|
132
|
+
if (c === "-" || c === "+" || /\d/.test(c))
|
|
133
|
+
return pyParseNumber(s);
|
|
134
|
+
if (/[A-Za-z_]/.test(c)) {
|
|
135
|
+
const id = pyParseIdentifier(s);
|
|
136
|
+
if (id === "True")
|
|
137
|
+
return true;
|
|
138
|
+
if (id === "False")
|
|
139
|
+
return false;
|
|
140
|
+
if (id === "None")
|
|
141
|
+
return null;
|
|
142
|
+
return id;
|
|
143
|
+
}
|
|
144
|
+
throw new Error(`Unexpected character: ${JSON.stringify(c)}`);
|
|
145
|
+
}
|
|
146
|
+
// Drives `parseItem, sep, parseItem, sep, ..., close` loops with trailing-
|
|
147
|
+
// comma tolerance. Assumes the open delimiter was already consumed.
|
|
148
|
+
function pyParseDelimited(s, close, context, parseItem) {
|
|
149
|
+
pySkipWs(s);
|
|
150
|
+
if (pyPeek(s) === close) {
|
|
151
|
+
pyConsume(s);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
for (;;) {
|
|
155
|
+
pySkipWs(s);
|
|
156
|
+
parseItem();
|
|
157
|
+
pySkipWs(s);
|
|
158
|
+
const sep = pyPeek(s);
|
|
159
|
+
if (sep === ",") {
|
|
160
|
+
pyConsume(s);
|
|
161
|
+
pySkipWs(s);
|
|
162
|
+
if (pyPeek(s) === close) {
|
|
163
|
+
pyConsume(s);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else if (sep === close) {
|
|
168
|
+
pyConsume(s);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
throw new Error(`Expected , or ${close} in ${context}, got ${JSON.stringify(sep)}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function pyParseList(s) {
|
|
177
|
+
pyConsume(s);
|
|
178
|
+
const items = [];
|
|
179
|
+
pyParseDelimited(s, "]", "list", () => {
|
|
180
|
+
items.push(pyParseValue(s));
|
|
181
|
+
});
|
|
182
|
+
return items;
|
|
183
|
+
}
|
|
184
|
+
function pyParseDict(s) {
|
|
185
|
+
pyConsume(s);
|
|
186
|
+
const obj = {};
|
|
187
|
+
pyParseDelimited(s, "}", "dict", () => {
|
|
188
|
+
const c = pyPeek(s);
|
|
189
|
+
let key;
|
|
190
|
+
if (c === '"' || c === "'")
|
|
191
|
+
key = pyParseString(s);
|
|
192
|
+
else if (/[A-Za-z_]/.test(c))
|
|
193
|
+
key = pyParseIdentifier(s);
|
|
194
|
+
else
|
|
195
|
+
throw new Error(`Expected dict key, got ${JSON.stringify(c)}`);
|
|
196
|
+
pySkipWs(s);
|
|
197
|
+
if (pyConsume(s) !== ":") {
|
|
198
|
+
throw new Error(`Expected : after dict key "${key}"`);
|
|
199
|
+
}
|
|
200
|
+
obj[key] = pyParseValue(s);
|
|
201
|
+
});
|
|
202
|
+
return obj;
|
|
203
|
+
}
|
|
204
|
+
function pyParseArgList(s) {
|
|
205
|
+
const args = {};
|
|
206
|
+
pyParseDelimited(s, ")", "args", () => {
|
|
207
|
+
const key = pyParseIdentifier(s);
|
|
208
|
+
pySkipWs(s);
|
|
209
|
+
if (pyConsume(s) !== "=") {
|
|
210
|
+
throw new Error(`Expected = after arg name "${key}"`);
|
|
211
|
+
}
|
|
212
|
+
args[key] = pyParseValue(s);
|
|
213
|
+
});
|
|
214
|
+
return args;
|
|
215
|
+
}
|
|
216
|
+
function pyParseCallList(text) {
|
|
217
|
+
const s = { text, pos: 0 };
|
|
218
|
+
pySkipWs(s);
|
|
219
|
+
if (pyConsume(s) !== "[") {
|
|
220
|
+
throw new Error("Expected [ at start of call list");
|
|
221
|
+
}
|
|
222
|
+
const calls = [];
|
|
223
|
+
pyParseDelimited(s, "]", "call list", () => {
|
|
224
|
+
const name = pyParseIdentifier(s);
|
|
225
|
+
pySkipWs(s);
|
|
226
|
+
if (pyConsume(s) !== "(") {
|
|
227
|
+
throw new Error(`Expected ( after call name "${name}"`);
|
|
228
|
+
}
|
|
229
|
+
calls.push({ name, arguments: pyParseArgList(s) });
|
|
230
|
+
});
|
|
231
|
+
return calls;
|
|
232
|
+
}
|
|
233
|
+
export function parsePythonicFormat(text, tools) {
|
|
234
|
+
const toolCalls = [];
|
|
235
|
+
const errors = [];
|
|
236
|
+
const slice = locatePythonicCallList(text);
|
|
237
|
+
if (slice === null) {
|
|
238
|
+
return { matched: false, toolCalls, errors };
|
|
239
|
+
}
|
|
240
|
+
let calls;
|
|
241
|
+
try {
|
|
242
|
+
calls = pyParseCallList(slice);
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
errors.push({
|
|
246
|
+
code: "PARSE_ERROR",
|
|
247
|
+
message: `Failed to parse Pythonic tool call list: ${error instanceof Error ? error.message : String(error)}`,
|
|
248
|
+
raw: slice,
|
|
249
|
+
});
|
|
250
|
+
return { matched: true, toolCalls, errors };
|
|
251
|
+
}
|
|
252
|
+
for (const call of calls) {
|
|
253
|
+
const raw = `${call.name}(${JSON.stringify(call.arguments)})`;
|
|
254
|
+
const validation = validateToolArguments(call.name, call.arguments, tools);
|
|
255
|
+
if (!validation.isValid && validation.error) {
|
|
256
|
+
errors.push({ ...validation.error, raw });
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
toolCalls.push({
|
|
260
|
+
id: generateStableToolCallId(call.name, call.arguments),
|
|
261
|
+
name: call.name,
|
|
262
|
+
arguments: call.arguments,
|
|
263
|
+
raw,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return { matched: true, toolCalls, errors };
|
|
267
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Tool, ToolCall, ToolCallError } from "../../../schemas/index";
|
|
2
|
+
export type ParserResult = {
|
|
3
|
+
matched: boolean;
|
|
4
|
+
toolCalls: ToolCall[];
|
|
5
|
+
errors: ToolCallError[];
|
|
6
|
+
};
|
|
7
|
+
export declare function stripThinkingBlocks(text: string): string;
|
|
8
|
+
export declare function generateStableToolCallId(name: string, args: Record<string, unknown>): string;
|
|
9
|
+
export declare function isValidToolCall(obj: unknown): obj is {
|
|
10
|
+
name: string;
|
|
11
|
+
arguments: Record<string, unknown>;
|
|
12
|
+
id?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function validateToolArguments(toolName: string, args: Record<string, unknown>, tools: Tool[]): {
|
|
15
|
+
isValid: boolean;
|
|
16
|
+
error?: ToolCallError;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export function stripThinkingBlocks(text) {
|
|
2
|
+
return text.replace(/<think>[\s\S]*?<\/think>/gi, "");
|
|
3
|
+
}
|
|
4
|
+
let toolCallSequence = 0;
|
|
5
|
+
export function generateStableToolCallId(name, args) {
|
|
6
|
+
const content = `${name}:${JSON.stringify(args)}`;
|
|
7
|
+
let hash = 0;
|
|
8
|
+
for (let i = 0; i < content.length; i++) {
|
|
9
|
+
const char = content.charCodeAt(i);
|
|
10
|
+
hash = (hash << 5) - hash + char;
|
|
11
|
+
hash = hash & hash;
|
|
12
|
+
}
|
|
13
|
+
const sequence = toolCallSequence++;
|
|
14
|
+
return `call_${Math.abs(hash).toString(36)}_${sequence}`;
|
|
15
|
+
}
|
|
16
|
+
export function isValidToolCall(obj) {
|
|
17
|
+
if (!obj || typeof obj !== "object") {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (!("name" in obj) || typeof obj.name !== "string") {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (!("arguments" in obj) ||
|
|
24
|
+
typeof obj.arguments !== "object" ||
|
|
25
|
+
obj.arguments === null) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
export function validateToolArguments(toolName, args, tools) {
|
|
31
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
32
|
+
if (!tool) {
|
|
33
|
+
return {
|
|
34
|
+
isValid: false,
|
|
35
|
+
error: {
|
|
36
|
+
code: "UNKNOWN_TOOL",
|
|
37
|
+
message: `Tool "${toolName}" not found in available tools`,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const required = tool.parameters.required || [];
|
|
42
|
+
for (const requiredParam of required) {
|
|
43
|
+
if (!(requiredParam in args)) {
|
|
44
|
+
return {
|
|
45
|
+
isValid: false,
|
|
46
|
+
error: {
|
|
47
|
+
code: "VALIDATION_ERROR",
|
|
48
|
+
message: `Missing required parameter "${requiredParam}" for tool "${toolName}"`,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { isValid: true };
|
|
54
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from "bare-fs";
|
|
2
|
+
import path from "bare-path";
|
|
3
|
+
import process from "bare-process";
|
|
4
|
+
import { getServerLogger } from "../../logging/index.js";
|
|
5
|
+
import { getQvacPath } from "./qvac-paths.js";
|
|
6
|
+
const logger = getServerLogger();
|
|
7
|
+
const LOCK_FILENAME = ".worker.lock";
|
|
8
|
+
function getLockFilePath() {
|
|
9
|
+
return getQvacPath(LOCK_FILENAME);
|
|
10
|
+
}
|
|
11
|
+
function isProcessAlive(pid) {
|
|
12
|
+
try {
|
|
13
|
+
process.kill(pid, 0);
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function readLockFile(lockPath) {
|
|
21
|
+
try {
|
|
22
|
+
if (!fs.existsSync(lockPath))
|
|
23
|
+
return null;
|
|
24
|
+
const raw = fs.readFileSync(lockPath, "utf-8");
|
|
25
|
+
return JSON.parse(raw);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function acquireWorkerLock() {
|
|
32
|
+
const lockPath = getLockFilePath();
|
|
33
|
+
const existing = readLockFile(lockPath);
|
|
34
|
+
if (existing) {
|
|
35
|
+
if (isProcessAlive(existing.pid)) {
|
|
36
|
+
logger.warn(`Another worker (PID ${existing.pid}) is still running โ lock file exists at ${lockPath}`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
logger.warn(`Stale lock file detected (PID ${existing.pid} is dead, started ${existing.startedAt}). Removing.`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const content = {
|
|
43
|
+
pid: process.pid,
|
|
44
|
+
startedAt: new Date().toISOString(),
|
|
45
|
+
};
|
|
46
|
+
try {
|
|
47
|
+
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
|
|
48
|
+
fs.writeFileSync(lockPath, JSON.stringify(content));
|
|
49
|
+
logger.debug(`Worker lock acquired (PID ${process.pid})`);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
logger.error("Failed to write worker lock file:", error instanceof Error ? error.message : String(error));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function releaseWorkerLock() {
|
|
56
|
+
const lockPath = getLockFilePath();
|
|
57
|
+
try {
|
|
58
|
+
const existing = readLockFile(lockPath);
|
|
59
|
+
if (existing && existing.pid === process.pid) {
|
|
60
|
+
fs.unlinkSync(lockPath);
|
|
61
|
+
logger.debug("Worker lock released");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Best-effort โ may already be gone
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -3,6 +3,21 @@ export declare function initializeWorkerCore(): {
|
|
|
3
3
|
};
|
|
4
4
|
export declare function ensureRPCSetup(): void;
|
|
5
5
|
export declare function isCoreInitialized(): boolean;
|
|
6
|
-
export type BareDirectShutdownReason = "signal" | "rpc-close";
|
|
6
|
+
export type BareDirectShutdownReason = "signal" | "rpc-close" | "uncaught-exception" | "unhandled-rejection" | "ipc-disconnect";
|
|
7
|
+
/**
|
|
8
|
+
* Pre-terminate cleanup, callable while the worker is still alive.
|
|
9
|
+
*
|
|
10
|
+
* On platforms where the worker lives in the same OS process as the JS host
|
|
11
|
+
* (i.e. mobile via react-native-bare-kit Worklet), `process.exit()` would
|
|
12
|
+
* kill the entire app. This path runs the same registry/model cleanup as
|
|
13
|
+
* `shutdownBareDirectWorker` but skips the lock release + exit, leaving the
|
|
14
|
+
* caller (typically the SDK client about to call `worklet.terminate()`)
|
|
15
|
+
* responsible for tearing the worker down.
|
|
16
|
+
*
|
|
17
|
+
* Critical for clean termination: addons hold static state with js_ref_t
|
|
18
|
+
* handles into the current V8 isolate; without this cleanup, those refs
|
|
19
|
+
* survive into the next worklet's isolate and crash on first access.
|
|
20
|
+
*/
|
|
21
|
+
export declare function cleanupForTerminate(): Promise<void>;
|
|
7
22
|
export declare function shutdownBareDirectWorker(reason: BareDirectShutdownReason): Promise<void>;
|
|
8
23
|
//# sourceMappingURL=worker-core.d.ts.map
|
|
@@ -9,10 +9,43 @@ import { closeRegistryClient } from "./bare/registry/registry-client.js";
|
|
|
9
9
|
import { clearAllLoggingStreams, startLogBuffering, } from "./bare/registry/logging-stream-registry.js";
|
|
10
10
|
import { clearAllAddonLoggers, getServerLogger, SDK_LOG_ID } from "../logging/index.js";
|
|
11
11
|
import { clearPlugins } from "./plugins/index.js";
|
|
12
|
+
import { acquireWorkerLock, releaseWorkerLock, } from "./utils/worker-lock.js";
|
|
12
13
|
let coreInitialized = false;
|
|
13
14
|
let rpcInitialized = false;
|
|
15
|
+
// Set true when the cleanup body has run at least once. Lets both
|
|
16
|
+
// cleanupForTerminate (pre-terminate path) and shutdownBareDirectWorker
|
|
17
|
+
// (signal/exit path) call runCleanup() without doing duplicate work.
|
|
18
|
+
let cleanupRan = false;
|
|
19
|
+
// Set true when shutdownBareDirectWorker is in flight. Distinct from
|
|
20
|
+
// cleanupRan: cleanupForTerminate must NOT set this, otherwise a later
|
|
21
|
+
// SIGTERM/SIGINT/uncaught-exception would early-return at the guard
|
|
22
|
+
// in shutdownBareDirectWorker and skip releaseWorkerLock + process.exit.
|
|
14
23
|
let isShuttingDown = false;
|
|
15
24
|
const logger = getServerLogger();
|
|
25
|
+
// Defense-in-depth grace period for the SIGKILL safety net armed before
|
|
26
|
+
// process.exit() in shutdownBareDirectWorker. If process.exit cannot
|
|
27
|
+
// terminate the worker within this window โ typically because some path
|
|
28
|
+
// holds a non-cancellable native handle (e.g. a libuv worker thread
|
|
29
|
+
// blocked on flock; see QVAC-18197) โ we force-kill the OS process to
|
|
30
|
+
// guarantee bounded shutdown time.
|
|
31
|
+
const FORCE_EXIT_GRACE_MS = 3_000;
|
|
32
|
+
function scheduleForceExit() {
|
|
33
|
+
const timer = setTimeout(() => {
|
|
34
|
+
logger.error(`process.exit did not terminate the worker within ${FORCE_EXIT_GRACE_MS}ms โ ` +
|
|
35
|
+
`force-killing self (likely blocked native handle)`);
|
|
36
|
+
try {
|
|
37
|
+
process.kill(process.pid, "SIGKILL");
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// best-effort โ if SIGKILL itself fails, there's nothing more to do
|
|
41
|
+
}
|
|
42
|
+
}, FORCE_EXIT_GRACE_MS);
|
|
43
|
+
// Don't let the safety-net timer keep the process alive on the happy
|
|
44
|
+
// path. Bare returns an object (not a number) from setTimeout.
|
|
45
|
+
if (timer && typeof timer === "object" && "unref" in timer) {
|
|
46
|
+
timer.unref();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
16
49
|
export function initializeWorkerCore() {
|
|
17
50
|
if (coreInitialized) {
|
|
18
51
|
const validatedEnv = getValidatedEnv();
|
|
@@ -20,6 +53,7 @@ export function initializeWorkerCore() {
|
|
|
20
53
|
}
|
|
21
54
|
startLogBuffering(SDK_LOG_ID);
|
|
22
55
|
const { hasRPCConfig } = initEnv();
|
|
56
|
+
acquireWorkerLock();
|
|
23
57
|
setupShutdownHandlers();
|
|
24
58
|
coreInitialized = true;
|
|
25
59
|
logger.debug("Worker core initialized");
|
|
@@ -37,7 +71,9 @@ export function ensureRPCSetup() {
|
|
|
37
71
|
const ipcSocketPath = validatedEnv.QVAC_IPC_SOCKET_PATH;
|
|
38
72
|
if (ipcSocketPath) {
|
|
39
73
|
logger.info(`Running in desktop mode, connecting to IPC socket: ${ipcSocketPath}`);
|
|
40
|
-
const rpc = createIPCClient(ipcSocketPath
|
|
74
|
+
const rpc = createIPCClient(ipcSocketPath, {
|
|
75
|
+
onDisconnect: () => void shutdownBareDirectWorker("ipc-disconnect"),
|
|
76
|
+
});
|
|
41
77
|
logger.debug("Desktop IPC client created?", !!rpc);
|
|
42
78
|
}
|
|
43
79
|
else {
|
|
@@ -61,31 +97,95 @@ function clearRegistries() {
|
|
|
61
97
|
clearAllAddonLoggers();
|
|
62
98
|
clearPlugins();
|
|
63
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Run the cleanup body shared by terminal and graceful-shutdown paths.
|
|
102
|
+
* Clears plugin registries (which calls each addon's `releaseLogger` โ
|
|
103
|
+
* frees env-bound js_ref_t state), unloads all loaded models (which calls
|
|
104
|
+
* each addon's `destroyInstance`), and closes infra (swarm, rag, downloads,
|
|
105
|
+
* registry client). Does NOT touch the worker lock or call `process.exit`.
|
|
106
|
+
*
|
|
107
|
+
* Idempotent: subsequent calls are no-ops via the `cleanupRan` flag. The
|
|
108
|
+
* underlying clearPlugins / unloadAllModels / closers are also idempotent
|
|
109
|
+
* on empty registries, but the flag avoids the redundant log noise and
|
|
110
|
+
* allocator churn.
|
|
111
|
+
*/
|
|
112
|
+
async function runCleanup() {
|
|
113
|
+
if (cleanupRan)
|
|
114
|
+
return;
|
|
115
|
+
cleanupRan = true;
|
|
116
|
+
clearRegistries();
|
|
117
|
+
await Promise.allSettled([
|
|
118
|
+
destroySwarm(),
|
|
119
|
+
closeAllRagInstances(),
|
|
120
|
+
cleanupDownloads(),
|
|
121
|
+
unloadAllModels(),
|
|
122
|
+
closeRegistryClient(),
|
|
123
|
+
]);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Pre-terminate cleanup, callable while the worker is still alive.
|
|
127
|
+
*
|
|
128
|
+
* On platforms where the worker lives in the same OS process as the JS host
|
|
129
|
+
* (i.e. mobile via react-native-bare-kit Worklet), `process.exit()` would
|
|
130
|
+
* kill the entire app. This path runs the same registry/model cleanup as
|
|
131
|
+
* `shutdownBareDirectWorker` but skips the lock release + exit, leaving the
|
|
132
|
+
* caller (typically the SDK client about to call `worklet.terminate()`)
|
|
133
|
+
* responsible for tearing the worker down.
|
|
134
|
+
*
|
|
135
|
+
* Critical for clean termination: addons hold static state with js_ref_t
|
|
136
|
+
* handles into the current V8 isolate; without this cleanup, those refs
|
|
137
|
+
* survive into the next worklet's isolate and crash on first access.
|
|
138
|
+
*/
|
|
139
|
+
export async function cleanupForTerminate() {
|
|
140
|
+
// Intentionally does NOT set isShuttingDown โ that flag is reserved for
|
|
141
|
+
// shutdownBareDirectWorker so a later SIGTERM/SIGINT still gets to run
|
|
142
|
+
// the lock release + process.exit path. runCleanup is idempotent on its
|
|
143
|
+
// own, so a follow-up shutdownBareDirectWorker call won't redo the body.
|
|
144
|
+
if (cleanupRan)
|
|
145
|
+
return;
|
|
146
|
+
logger.info("๐งน Pre-terminate cleanup starting...");
|
|
147
|
+
try {
|
|
148
|
+
await runCleanup();
|
|
149
|
+
logger.info("โ
Pre-terminate cleanup completed");
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
logger.error("โ Error during pre-terminate cleanup:", error);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
64
155
|
export async function shutdownBareDirectWorker(reason) {
|
|
65
156
|
if (isShuttingDown)
|
|
66
157
|
return;
|
|
67
158
|
isShuttingDown = true;
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
: "๐งน Bare direct mode RPC closed, cleaning up..."
|
|
71
|
-
|
|
159
|
+
const messages = {
|
|
160
|
+
signal: "๐ป Bare worker shutdown signal received, cleaning up...",
|
|
161
|
+
"rpc-close": "๐งน Bare direct mode RPC closed, cleaning up...",
|
|
162
|
+
"uncaught-exception": "๐ฅ Uncaught exception, cleaning up...",
|
|
163
|
+
"unhandled-rejection": "๐ฅ Unhandled rejection, cleaning up...",
|
|
164
|
+
"ipc-disconnect": "๐ Parent IPC disconnected, cleaning up...",
|
|
165
|
+
};
|
|
166
|
+
logger.info(messages[reason]);
|
|
72
167
|
try {
|
|
73
|
-
|
|
74
|
-
await
|
|
75
|
-
destroySwarm(),
|
|
76
|
-
closeAllRagInstances(),
|
|
77
|
-
cleanupDownloads(),
|
|
78
|
-
unloadAllModels(),
|
|
79
|
-
closeRegistryClient(),
|
|
80
|
-
]);
|
|
168
|
+
// Idempotent: if cleanupForTerminate already ran, this is a no-op.
|
|
169
|
+
await runCleanup();
|
|
81
170
|
logger.info("โ
Cleanup completed successfully");
|
|
82
171
|
}
|
|
83
172
|
catch (error) {
|
|
84
173
|
logger.error("โ Error during shutdown cleanup:", error);
|
|
85
174
|
}
|
|
86
|
-
|
|
175
|
+
releaseWorkerLock();
|
|
176
|
+
scheduleForceExit();
|
|
177
|
+
const isGraceful = reason === "signal" || reason === "rpc-close";
|
|
178
|
+
process.exit(isGraceful ? 0 : 1);
|
|
87
179
|
}
|
|
88
180
|
function setupShutdownHandlers() {
|
|
89
181
|
process.once("SIGTERM", () => void shutdownBareDirectWorker("signal"));
|
|
90
182
|
process.once("SIGINT", () => void shutdownBareDirectWorker("signal"));
|
|
183
|
+
process.on("uncaughtException", (err) => {
|
|
184
|
+
logger.error("Uncaught exception in worker:", err);
|
|
185
|
+
void shutdownBareDirectWorker("uncaught-exception");
|
|
186
|
+
});
|
|
187
|
+
process.on("unhandledRejection", (reason) => {
|
|
188
|
+
logger.error("Unhandled rejection in worker:", reason);
|
|
189
|
+
void shutdownBareDirectWorker("unhandled-rejection");
|
|
190
|
+
});
|
|
91
191
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CompletionError, CompletionEvent, CompletionFinal, CompletionStats, ToolCall } from "../schemas/index";
|
|
2
|
+
import { type ToolHandlerMap } from "./tool-helpers";
|
|
3
|
+
export type AggregatedEvents = {
|
|
4
|
+
contentText: string;
|
|
5
|
+
thinkingText: string;
|
|
6
|
+
stats: CompletionStats | undefined;
|
|
7
|
+
toolCalls: ToolCall[];
|
|
8
|
+
rawFullText: string | undefined;
|
|
9
|
+
error: CompletionError | undefined;
|
|
10
|
+
};
|
|
11
|
+
export declare function aggregateEvents(events: CompletionEvent[]): AggregatedEvents;
|
|
12
|
+
export declare function buildFinalFromEvents(events: CompletionEvent[], handlers: ToolHandlerMap): {
|
|
13
|
+
final: CompletionFinal;
|
|
14
|
+
error: CompletionError | undefined;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=aggregate-events.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { normalizeAssistantCacheContent } from "./cache-normalize.js";
|
|
2
|
+
import { attachHandlersToToolCalls, } from "./tool-helpers.js";
|
|
3
|
+
export function aggregateEvents(events) {
|
|
4
|
+
let contentText = "";
|
|
5
|
+
let thinkingText = "";
|
|
6
|
+
let stats;
|
|
7
|
+
let rawFullText;
|
|
8
|
+
let error;
|
|
9
|
+
const toolCalls = [];
|
|
10
|
+
for (const event of events) {
|
|
11
|
+
if (event.type === "contentDelta") {
|
|
12
|
+
contentText += event.text;
|
|
13
|
+
}
|
|
14
|
+
else if (event.type === "thinkingDelta") {
|
|
15
|
+
thinkingText += event.text;
|
|
16
|
+
}
|
|
17
|
+
else if (event.type === "completionStats") {
|
|
18
|
+
stats = event.stats;
|
|
19
|
+
}
|
|
20
|
+
else if (event.type === "toolCall") {
|
|
21
|
+
toolCalls.push(event.call);
|
|
22
|
+
}
|
|
23
|
+
else if (event.type === "completionDone") {
|
|
24
|
+
if ("raw" in event && event.raw) {
|
|
25
|
+
rawFullText = event.raw.fullText;
|
|
26
|
+
}
|
|
27
|
+
if (event.stopReason === "error" && "error" in event) {
|
|
28
|
+
error = event.error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { contentText, thinkingText, stats, toolCalls, rawFullText, error };
|
|
33
|
+
}
|
|
34
|
+
export function buildFinalFromEvents(events, handlers) {
|
|
35
|
+
const { contentText, thinkingText, stats, toolCalls, rawFullText, error } = aggregateEvents(events);
|
|
36
|
+
const attachedToolCalls = attachHandlersToToolCalls(toolCalls, handlers);
|
|
37
|
+
const fullText = rawFullText ?? contentText;
|
|
38
|
+
const cacheableAssistantContent = attachedToolCalls.length === 0
|
|
39
|
+
? normalizeAssistantCacheContent(fullText)
|
|
40
|
+
: undefined;
|
|
41
|
+
const final = {
|
|
42
|
+
contentText,
|
|
43
|
+
...(thinkingText && { thinkingText }),
|
|
44
|
+
toolCalls: attachedToolCalls,
|
|
45
|
+
...(stats && { stats }),
|
|
46
|
+
raw: { fullText },
|
|
47
|
+
...(cacheableAssistantContent !== undefined && {
|
|
48
|
+
cacheableAssistantContent,
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
return { final, error };
|
|
52
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonicalize assistant message text for the auto-cache key.
|
|
3
|
+
*
|
|
4
|
+
* The server saves raw stream text (with think tags when `captureThinking` is
|
|
5
|
+
* on), while `final.contentText` only aggregates `contentDelta` events.
|
|
6
|
+
* Stripping think blocks (paired and unclosed-trailing) and trimming lets
|
|
7
|
+
* callers push back either shape and still hit the cache.
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeAssistantCacheContent(content: string): string;
|
|
10
|
+
//# sourceMappingURL=cache-normalize.d.ts.map
|