@qvac/sdk 0.9.2 → 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 +353 -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 +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/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 +79 -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 +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 +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
|
@@ -1,19 +1,46 @@
|
|
|
1
|
+
import { TOOLS_MODE } from "../../../../../schemas/tools.js";
|
|
1
2
|
import { logCacheDisabled, logCacheInit, logCacheSave, logCacheSaveError, logCacheStatus, logMessagesToAddon, } from "./cache-logger.js";
|
|
2
|
-
import { customCacheExists, extractSystemPrompt, findMatchingCache, generateConfigHash, getCacheFilePath, getCurrentCacheInfo, markCacheInitialized, renameCacheFile, } from "../../../ops/kv-cache-utils.js";
|
|
3
|
+
import { clearCacheRegistry, customCacheExists, extractSystemPrompt, findMatchingCache, generateConfigHash, getCacheFilePath, getCurrentCacheInfo, markCacheInitialized, renameCacheFile, } from "../../../ops/kv-cache-utils.js";
|
|
3
4
|
import { getModel, getModelConfig, } from "../../../registry/model-registry.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { cachedMessageCounts, clearCachedMessageCounts as clearCachedMessageCountsFromState, decideCachedHistorySlice, noteCancelRequested as noteCancelRequestedFromState, shouldRecordSavedCount, snapshotCancelCount, } from "./kv-cache-state.js";
|
|
6
|
+
import { appendToolsToHistory, detectToolDialect, prependToolsToHistory, } from "../../../../utils/tool-integration.js";
|
|
7
|
+
import { parseToolCalls } from "../../../../utils/tools/index.js";
|
|
8
|
+
import { getResponseFormatJsonSchema } from "../../../../utils/response-format.js";
|
|
9
|
+
import { buildAutoCacheSaveHistory } from "../../../../utils/index.js";
|
|
10
|
+
import { getServerLogger } from "../../../../../logging/index.js";
|
|
6
11
|
import { AttachmentNotFoundError } from "../../../../../utils/errors-server.js";
|
|
7
12
|
import { nowMs } from "../../../../../profiling/index.js";
|
|
8
13
|
import { buildStreamResult, hasDefinedValues, } from "../../../../../profiling/model-execution.js";
|
|
9
|
-
import fs from "bare-fs";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
import fs, { promises as fsPromises } from "bare-fs";
|
|
15
|
+
import path from "bare-path";
|
|
16
|
+
const logger = getServerLogger();
|
|
17
|
+
// Re-export so existing callers keep their import surface intact. The pure
|
|
18
|
+
// state module has no `bare-*` imports, so we inject the platform path
|
|
19
|
+
// separator here — without this, prefix-based clears would miss entries
|
|
20
|
+
// under directory keys on Windows.
|
|
21
|
+
export function clearCachedMessageCounts(prefix) {
|
|
22
|
+
clearCachedMessageCountsFromState(prefix, path.sep);
|
|
23
|
+
}
|
|
24
|
+
export const noteCancelRequested = noteCancelRequestedFromState;
|
|
25
|
+
// Verify the addon actually persisted the cache file before recording its
|
|
26
|
+
// message count. The addon currently swallows write errors silently, so a
|
|
27
|
+
// missing file means the next turn must resend the full history rather than
|
|
28
|
+
// slicing against a stale `savedCount`.
|
|
29
|
+
//
|
|
30
|
+
// TODO: once the addon surfaces save failures (e.g. throws
|
|
31
|
+
// `UnableToSaveSessionFile` when `llama_state_save_file` returns false),
|
|
32
|
+
// drop the `access()` probe and wrap the `model.run()` call in a real
|
|
33
|
+
// try/catch that forwards the error to `logCacheSaveError`.
|
|
34
|
+
async function recordCacheSaveCount(cachePath, messageCount) {
|
|
35
|
+
try {
|
|
36
|
+
await fsPromises.access(cachePath);
|
|
37
|
+
cachedMessageCounts.set(cachePath, messageCount);
|
|
38
|
+
return true;
|
|
14
39
|
}
|
|
15
|
-
|
|
16
|
-
cachedMessageCounts.
|
|
40
|
+
catch (err) {
|
|
41
|
+
cachedMessageCounts.delete(cachePath);
|
|
42
|
+
logCacheSaveError(cachePath, err);
|
|
43
|
+
return false;
|
|
17
44
|
}
|
|
18
45
|
}
|
|
19
46
|
function transformMessage(message) {
|
|
@@ -47,6 +74,10 @@ function transformMessage(message) {
|
|
|
47
74
|
});
|
|
48
75
|
return transformed;
|
|
49
76
|
}
|
|
77
|
+
function runModel(model, prompt, opts) {
|
|
78
|
+
const run = model.run.bind(model);
|
|
79
|
+
return run(prompt, opts);
|
|
80
|
+
}
|
|
50
81
|
function transformMessages(messages) {
|
|
51
82
|
const transformed = [];
|
|
52
83
|
for (const message of messages) {
|
|
@@ -56,7 +87,6 @@ function transformMessages(messages) {
|
|
|
56
87
|
}
|
|
57
88
|
async function initSystemPromptCache(model, cachePathToUse, systemPromptToUse, cacheKey, tools) {
|
|
58
89
|
const primeMessages = [
|
|
59
|
-
{ role: "session", content: cachePathToUse },
|
|
60
90
|
{ role: "system", content: systemPromptToUse },
|
|
61
91
|
];
|
|
62
92
|
let toolCount = 0;
|
|
@@ -67,76 +97,115 @@ async function initSystemPromptCache(model, cachePathToUse, systemPromptToUse, c
|
|
|
67
97
|
}
|
|
68
98
|
logCacheInit(cacheKey, systemPromptToUse, toolCount);
|
|
69
99
|
logMessagesToAddon(primeMessages, "CACHE_INIT");
|
|
70
|
-
const primeResponse = await model
|
|
100
|
+
const primeResponse = await runModel(model, primeMessages, {
|
|
101
|
+
cacheKey: cachePathToUse,
|
|
102
|
+
saveCacheToDisk: true,
|
|
103
|
+
});
|
|
71
104
|
primeResponse.once("output", () => {
|
|
72
105
|
void primeResponse.cancel();
|
|
73
106
|
});
|
|
74
107
|
await primeResponse.await();
|
|
75
108
|
}
|
|
76
|
-
|
|
77
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Pick the messages that need to reach the model for the next turn.
|
|
111
|
+
*
|
|
112
|
+
* Static mode (no `tools` argument):
|
|
113
|
+
* - Cache miss: send the whole history minus the system message (which
|
|
114
|
+
* was primed during cache init).
|
|
115
|
+
* - Cache hit with a recorded `savedCount`: send only the unsaved tail
|
|
116
|
+
* (`history.slice(savedCount)`), so a multi-message turn (e.g. a
|
|
117
|
+
* consumer pushing both an assistant transcript and a follow-up user
|
|
118
|
+
* message between completions) all reaches the model.
|
|
119
|
+
* - Cache hit with a stale/missing `savedCount`: fall back to the full
|
|
120
|
+
* non-system history.
|
|
121
|
+
*
|
|
122
|
+
* Dynamic mode (`tools` argument set):
|
|
123
|
+
* - The addon anchors the tool block after the last user message and
|
|
124
|
+
* trims tools + the assistant's tool-call output from the cache once
|
|
125
|
+
* the chain resolves. After that trim, the cache only holds messages
|
|
126
|
+
* up to the last user turn, so the SDK has to ship the right slice
|
|
127
|
+
* plus the (possibly new) tool set:
|
|
128
|
+
* * tool-chain continuation (last role is "tool"): send the trailing
|
|
129
|
+
* consecutive tool messages, no tool block — tools are still
|
|
130
|
+
* anchored in the cache from the previous round.
|
|
131
|
+
* * new user turn after a chain (prev role is "assistant"): send
|
|
132
|
+
* [assistant, user] so the model sees its own final reply before
|
|
133
|
+
* the new prompt, then re-anchor the tool block.
|
|
134
|
+
* * otherwise: send just the last message + tool block.
|
|
135
|
+
*/
|
|
136
|
+
function prepareMessagesForCache(cachePathToUse, cacheExists, history, tools) {
|
|
137
|
+
const addTools = tools?.length ? transformMessages(tools) : [];
|
|
138
|
+
const dynamic = addTools.length > 0;
|
|
139
|
+
if (!(cacheExists && history.length > 0)) {
|
|
140
|
+
const historyWithoutSystem = history.filter((msg) => msg.role !== "system");
|
|
141
|
+
return [...transformMessages(historyWithoutSystem), ...addTools];
|
|
142
|
+
}
|
|
143
|
+
if (!dynamic) {
|
|
144
|
+
// Static path — slice from the recorded `savedCount` so callers can
|
|
145
|
+
// stage multiple messages between completions. `decideCachedHistorySlice`
|
|
146
|
+
// also guards against the QVAC-17780 stale-count regression: if the
|
|
147
|
+
// saved boundary would slice the history down to an empty payload
|
|
148
|
+
// (e.g. after a cancelled mid-decode), it falls back to the full
|
|
149
|
+
// non-system history and signals the caller to drop the bad entry.
|
|
78
150
|
const savedCount = cachedMessageCounts.get(cachePathToUse) ?? 0;
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
? history.slice(savedCount)
|
|
82
|
-
: history.filter((msg) => msg.role !== "system");
|
|
83
|
-
if (!canSlice && savedCount > 0) {
|
|
151
|
+
const { messages, clearStaleCount } = decideCachedHistorySlice(savedCount, cacheExists, history);
|
|
152
|
+
if (clearStaleCount) {
|
|
84
153
|
cachedMessageCounts.delete(cachePathToUse);
|
|
85
154
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
155
|
+
return transformMessages(messages);
|
|
156
|
+
}
|
|
157
|
+
// Dynamic path. The addon trimmed tools after the previous round, so the
|
|
158
|
+
// cache no longer holds the saved-count we'd rely on for slicing — pick
|
|
159
|
+
// the right fragment based on the role of the last history message.
|
|
160
|
+
const lastMsg = history[history.length - 1];
|
|
161
|
+
if (lastMsg.role === "tool") {
|
|
162
|
+
const trailingTools = [];
|
|
163
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
164
|
+
const msg = history[i];
|
|
165
|
+
if (msg.role !== "tool")
|
|
166
|
+
break;
|
|
167
|
+
trailingTools.unshift(msg);
|
|
168
|
+
}
|
|
169
|
+
return transformMessages(trailingTools);
|
|
170
|
+
}
|
|
171
|
+
if (lastMsg.role === "user") {
|
|
172
|
+
const prevMsg = history[history.length - 2];
|
|
173
|
+
const tail = prevMsg?.role === "assistant" ? [prevMsg, lastMsg] : [lastMsg];
|
|
174
|
+
return [...transformMessages(tail), ...addTools];
|
|
175
|
+
}
|
|
176
|
+
return [...transformMessages([lastMsg]), ...addTools];
|
|
98
177
|
}
|
|
99
|
-
async function* processModelResponse(model, messagesToSend,
|
|
100
|
-
const
|
|
101
|
-
|
|
178
|
+
async function* processModelResponse(model, messagesToSend, tools, generationParams, cacheOptions, dialect) {
|
|
179
|
+
const runOptions = {
|
|
180
|
+
...(generationParams && { generationParams }),
|
|
181
|
+
...(cacheOptions?.cacheKey !== undefined && {
|
|
182
|
+
cacheKey: cacheOptions.cacheKey,
|
|
183
|
+
}),
|
|
184
|
+
...(cacheOptions?.saveCacheToDisk !== undefined && {
|
|
185
|
+
saveCacheToDisk: cacheOptions.saveCacheToDisk,
|
|
186
|
+
}),
|
|
187
|
+
};
|
|
188
|
+
const hasRunOptions = Object.keys(runOptions).length > 0;
|
|
102
189
|
const modelStart = nowMs();
|
|
103
|
-
const response = await
|
|
190
|
+
const response = await runModel(model, messagesToSend, hasRunOptions ? runOptions : undefined);
|
|
104
191
|
let accumulatedText = "";
|
|
105
|
-
|
|
192
|
+
let producedTokens = false;
|
|
106
193
|
let toolCallsResult = [];
|
|
107
194
|
for await (const token of response.iterate()) {
|
|
108
195
|
const tokenStr = token;
|
|
196
|
+
if (tokenStr.length > 0)
|
|
197
|
+
producedTokens = true;
|
|
109
198
|
accumulatedText += tokenStr;
|
|
110
199
|
yield { token: tokenStr };
|
|
111
|
-
if (tools && tools.length > 0) {
|
|
112
|
-
const toolEvents = checkForToolEvents(accumulatedText, tokenStr, tools, emittedToolCallPositions);
|
|
113
|
-
for (const toolEvent of toolEvents) {
|
|
114
|
-
yield { token: "", toolCallEvent: toolEvent };
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
200
|
}
|
|
118
201
|
const modelExecutionMs = nowMs() - modelStart;
|
|
202
|
+
if (cacheOptions?.saveCacheToDisk && cacheOptions.cacheKey) {
|
|
203
|
+
logCacheSave(cacheOptions.cacheKey);
|
|
204
|
+
}
|
|
119
205
|
if (tools && tools.length > 0) {
|
|
120
|
-
const { toolCalls } = parseToolCalls(accumulatedText, tools);
|
|
206
|
+
const { toolCalls } = parseToolCalls(accumulatedText, tools, dialect);
|
|
121
207
|
toolCallsResult = toolCalls;
|
|
122
208
|
}
|
|
123
|
-
if (shouldSaveCache) {
|
|
124
|
-
const sessionMsg = messagesToSend.find((m) => m.role === "session");
|
|
125
|
-
if (sessionMsg?.content) {
|
|
126
|
-
logCacheSave(sessionMsg.content);
|
|
127
|
-
const cachePath = sessionMsg.content;
|
|
128
|
-
const saveResp = await model.run([
|
|
129
|
-
{ role: "session", content: cachePath },
|
|
130
|
-
{ role: "session", content: "save" },
|
|
131
|
-
]);
|
|
132
|
-
try {
|
|
133
|
-
await saveResp.await();
|
|
134
|
-
}
|
|
135
|
-
catch (err) {
|
|
136
|
-
logCacheSaveError(cachePath, err);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
209
|
const responseWithStats = response;
|
|
141
210
|
const stats = {
|
|
142
211
|
...(responseWithStats.stats?.TTFT !== undefined && {
|
|
@@ -148,6 +217,9 @@ async function* processModelResponse(model, messagesToSend, shouldSaveCache, too
|
|
|
148
217
|
...(responseWithStats.stats?.CacheTokens !== undefined && {
|
|
149
218
|
cacheTokens: responseWithStats.stats.CacheTokens,
|
|
150
219
|
}),
|
|
220
|
+
...(responseWithStats.stats?.generatedTokens !== undefined && {
|
|
221
|
+
generatedTokens: responseWithStats.stats.generatedTokens,
|
|
222
|
+
}),
|
|
151
223
|
...(responseWithStats.stats?.backendDevice !== undefined && {
|
|
152
224
|
backendDevice: responseWithStats.stats.backendDevice,
|
|
153
225
|
}),
|
|
@@ -155,23 +227,44 @@ async function* processModelResponse(model, messagesToSend, shouldSaveCache, too
|
|
|
155
227
|
return {
|
|
156
228
|
...buildStreamResult(modelExecutionMs, hasDefinedValues(stats) ? stats : undefined),
|
|
157
229
|
toolCalls: toolCallsResult,
|
|
230
|
+
responseText: accumulatedText,
|
|
231
|
+
producedTokens,
|
|
158
232
|
};
|
|
159
233
|
}
|
|
160
234
|
export async function* completion(params) {
|
|
161
|
-
const { history, modelId, kvCache, tools, generationParams } = params;
|
|
235
|
+
const { history, modelId, kvCache, tools, generationParams, responseFormat } = params;
|
|
162
236
|
const modelConfig = getModelConfig(modelId);
|
|
163
237
|
const toolsEnabled = modelConfig.tools === true;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
238
|
+
const toolsMode = modelConfig.toolsMode;
|
|
239
|
+
const dynamicTools = !!tools?.length && toolsEnabled && toolsMode === TOOLS_MODE.dynamic;
|
|
240
|
+
const staticTools = !!tools?.length && toolsEnabled && !dynamicTools;
|
|
241
|
+
const dialect = tools && tools.length > 0
|
|
242
|
+
? (params.toolDialect ?? detectToolDialect(modelId))
|
|
243
|
+
: undefined;
|
|
244
|
+
// `responseFormat` is forwarded to the addon as a per-request
|
|
245
|
+
// `generationParams.json_schema`, which the addon converts to GBNF and
|
|
246
|
+
// applies for the duration of the request only. This avoids mutating
|
|
247
|
+
// the shared `modelConfig` and is therefore safe under concurrent
|
|
248
|
+
// completions on the same model. `tools` still constrain output through
|
|
249
|
+
// their parameter schema and the dialect-specific parser chain (mutually
|
|
250
|
+
// exclusive with a non-text `responseFormat` at the schema layer).
|
|
251
|
+
let mergedGenerationParams = generationParams;
|
|
252
|
+
if (responseFormat && !(tools && tools.length > 0)) {
|
|
253
|
+
const jsonSchema = getResponseFormatJsonSchema(responseFormat);
|
|
254
|
+
if (jsonSchema !== undefined) {
|
|
255
|
+
mergedGenerationParams = {
|
|
256
|
+
...(generationParams ?? {}),
|
|
257
|
+
json_schema: jsonSchema,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
168
260
|
}
|
|
169
|
-
const transformedHistory = transformMessages(historyWithTools);
|
|
170
261
|
const model = getModel(modelId);
|
|
171
262
|
if (kvCache) {
|
|
172
|
-
const modelConfig = getModelConfig(modelId);
|
|
173
263
|
const systemPromptFromHistory = extractSystemPrompt(history);
|
|
174
|
-
|
|
264
|
+
// Dynamic mode lets each turn carry its own tool set, so the cache
|
|
265
|
+
// hash must not depend on the tool list — otherwise a tool change
|
|
266
|
+
// would force a fresh cache file and defeat the whole optimisation.
|
|
267
|
+
const configHash = generateConfigHash(systemPromptFromHistory, dynamicTools ? undefined : tools);
|
|
175
268
|
const systemPromptToUse = systemPromptFromHistory ||
|
|
176
269
|
modelConfig.system_prompt ||
|
|
177
270
|
"You are a helpful assistant.";
|
|
@@ -181,14 +274,43 @@ export async function* completion(params) {
|
|
|
181
274
|
let cacheExists = await customCacheExists(modelId, configHash, kvCache);
|
|
182
275
|
logCacheStatus(kvCache, cacheExists);
|
|
183
276
|
if (!cacheExists) {
|
|
184
|
-
await initSystemPromptCache(model, cachePathToUse, systemPromptToUse, kvCache,
|
|
277
|
+
await initSystemPromptCache(model, cachePathToUse, systemPromptToUse, kvCache,
|
|
278
|
+
// Static-mode tools are baked into the system-prompt cache so
|
|
279
|
+
// they're shared across the session. Dynamic-mode tools belong
|
|
280
|
+
// to a per-turn anchor and must not enter the system cache.
|
|
281
|
+
staticTools ? tools : undefined);
|
|
185
282
|
markCacheInitialized(modelId, configHash, kvCache);
|
|
186
283
|
cacheExists = true;
|
|
187
284
|
}
|
|
188
|
-
const messagesToSend = prepareMessagesForCache(cachePathToUse, cacheExists, history);
|
|
285
|
+
const messagesToSend = prepareMessagesForCache(cachePathToUse, cacheExists, history, dynamicTools ? tools : undefined);
|
|
189
286
|
logMessagesToAddon(messagesToSend, "PROMPT_SEND");
|
|
190
|
-
const
|
|
191
|
-
|
|
287
|
+
const cancelCountBefore = snapshotCancelCount(modelId);
|
|
288
|
+
const result = yield* processModelResponse(model, messagesToSend, tools, mergedGenerationParams, { cacheKey: cachePathToUse, saveCacheToDisk: true }, dialect);
|
|
289
|
+
const wasCancelled = snapshotCancelCount(modelId) > cancelCountBefore;
|
|
290
|
+
if (shouldRecordSavedCount(wasCancelled, result.producedTokens)) {
|
|
291
|
+
// Turn ran to completion and produced content — record the new
|
|
292
|
+
// boundary so the next turn can slice its history.
|
|
293
|
+
await recordCacheSaveCount(cachePathToUse, history.length + 1);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
// The addon writes the cache file unconditionally on
|
|
297
|
+
// `saveCacheToDisk` turns, including cancellations and zero-token
|
|
298
|
+
// exits, so what's left on disk holds partial decode state that
|
|
299
|
+
// does not correspond to a clean turn boundary. Mirror the
|
|
300
|
+
// auto-key handling: drop the file, clear the in-memory init
|
|
301
|
+
// flag (otherwise `customCacheExists` would still report true),
|
|
302
|
+
// and forget the saved count. Next turn re-primes the system
|
|
303
|
+
// prompt cleanly — a one-turn perf hit, but no risk of the
|
|
304
|
+
// addon loading the stale KV state.
|
|
305
|
+
try {
|
|
306
|
+
await fsPromises.unlink(cachePathToUse);
|
|
307
|
+
}
|
|
308
|
+
catch (unlinkError) {
|
|
309
|
+
logger.warn(`[kv-cache] Failed to remove cache file after cancelled or empty custom-key turn; next turn may load stale KV state. path=${cachePathToUse} error=${unlinkError instanceof Error ? unlinkError.message : String(unlinkError)}`);
|
|
310
|
+
}
|
|
311
|
+
clearCacheRegistry({ cacheKey: kvCache, modelId });
|
|
312
|
+
cachedMessageCounts.delete(cachePathToUse);
|
|
313
|
+
}
|
|
192
314
|
return result;
|
|
193
315
|
}
|
|
194
316
|
else {
|
|
@@ -199,34 +321,84 @@ export async function* completion(params) {
|
|
|
199
321
|
attachments: msg.attachments ?? undefined,
|
|
200
322
|
}));
|
|
201
323
|
const existingCache = await findMatchingCache(modelId, configHash, cacheMessages);
|
|
202
|
-
const
|
|
324
|
+
const preResponseCacheInfo = await getCurrentCacheInfo(modelId, configHash, cacheMessages);
|
|
203
325
|
cachePathToUse =
|
|
204
326
|
existingCache !== null
|
|
205
327
|
? existingCache.cachePath
|
|
206
|
-
:
|
|
328
|
+
: preResponseCacheInfo.cachePath;
|
|
207
329
|
let cacheExists = existingCache !== null;
|
|
208
330
|
logCacheStatus("auto", cacheExists);
|
|
209
331
|
if (!cacheExists) {
|
|
210
|
-
await initSystemPromptCache(model, cachePathToUse, systemPromptToUse, "auto",
|
|
211
|
-
markCacheInitialized(modelId, configHash,
|
|
332
|
+
await initSystemPromptCache(model, cachePathToUse, systemPromptToUse, "auto", staticTools ? tools : undefined);
|
|
333
|
+
markCacheInitialized(modelId, configHash, preResponseCacheInfo.cacheKey);
|
|
212
334
|
cacheExists = true;
|
|
213
335
|
}
|
|
214
|
-
const messagesToSend = prepareMessagesForCache(cachePathToUse, cacheExists, history);
|
|
336
|
+
const messagesToSend = prepareMessagesForCache(cachePathToUse, cacheExists, history, dynamicTools ? tools : undefined);
|
|
215
337
|
logMessagesToAddon(messagesToSend, "PROMPT_SEND");
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
338
|
+
const cancelCountBefore = snapshotCancelCount(modelId);
|
|
339
|
+
const result = yield* processModelResponse(model, messagesToSend, tools, mergedGenerationParams, { cacheKey: cachePathToUse, saveCacheToDisk: true }, dialect);
|
|
340
|
+
const wasCancelled = snapshotCancelCount(modelId) > cancelCountBefore;
|
|
341
|
+
// TODO: support auto-cache for tool-call turns by keying off the
|
|
342
|
+
// structured assistant/tool messages callers push into history,
|
|
343
|
+
// not result.responseText (which is raw tool-call markup here).
|
|
344
|
+
// Until then, remove any cache file the addon wrote so it doesn't
|
|
345
|
+
// leak on disk (the next turn would compute a different key and
|
|
346
|
+
// never reach it).
|
|
347
|
+
if (result.toolCalls.length > 0) {
|
|
348
|
+
logger.warn(`[kv-cache] Auto cache tool-call turn; removing orphaned cache to avoid disk leak. path=${cachePathToUse}`);
|
|
349
|
+
try {
|
|
350
|
+
await fsPromises.unlink(cachePathToUse);
|
|
351
|
+
}
|
|
352
|
+
catch (unlinkError) {
|
|
353
|
+
logger.warn(`[kv-cache] Failed to remove orphaned tool-turn cache file; disk leak likely. path=${cachePathToUse} error=${unlinkError instanceof Error ? unlinkError.message : String(unlinkError)}`);
|
|
354
|
+
}
|
|
355
|
+
cachedMessageCounts.delete(cachePathToUse);
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
// A cancelled or zero-token turn cannot be promoted to a post-response
|
|
359
|
+
// cache: the post-response key is derived from `result.responseText`,
|
|
360
|
+
// which is empty/partial in those cases, and the on-disk cache the
|
|
361
|
+
// addon wrote is not aligned with the current-history hash. Treat it
|
|
362
|
+
// like the tool-call branch — drop the cache file and clear the count.
|
|
363
|
+
if (!shouldRecordSavedCount(wasCancelled, result.producedTokens)) {
|
|
364
|
+
try {
|
|
365
|
+
await fsPromises.unlink(cachePathToUse);
|
|
366
|
+
}
|
|
367
|
+
catch (unlinkError) {
|
|
368
|
+
logger.warn(`[kv-cache] Failed to remove cache file after cancelled or empty turn; disk leak possible. path=${cachePathToUse} error=${unlinkError instanceof Error ? unlinkError.message : String(unlinkError)}`);
|
|
369
|
+
}
|
|
370
|
+
cachedMessageCounts.delete(cachePathToUse);
|
|
371
|
+
return result;
|
|
372
|
+
}
|
|
373
|
+
const savedHistory = buildAutoCacheSaveHistory(cacheMessages, result.responseText);
|
|
374
|
+
const postResponseCacheInfo = await getCurrentCacheInfo(modelId, configHash, savedHistory);
|
|
375
|
+
if (!(await renameCacheFile(cachePathToUse, postResponseCacheInfo.cachePath))) {
|
|
376
|
+
logger.warn(`[kv-cache] Auto cache rename failed; removing stale cache to avoid disk leak. from=${cachePathToUse} to=${postResponseCacheInfo.cachePath}`);
|
|
377
|
+
try {
|
|
378
|
+
await fsPromises.unlink(cachePathToUse);
|
|
379
|
+
}
|
|
380
|
+
catch (unlinkError) {
|
|
381
|
+
logger.warn(`[kv-cache] Failed to remove stale cache file; disk leak likely. path=${cachePathToUse} error=${unlinkError instanceof Error ? unlinkError.message : String(unlinkError)}`);
|
|
382
|
+
}
|
|
383
|
+
cachedMessageCounts.delete(cachePathToUse);
|
|
384
|
+
return result;
|
|
223
385
|
}
|
|
386
|
+
cachedMessageCounts.delete(cachePathToUse);
|
|
387
|
+
await recordCacheSaveCount(postResponseCacheInfo.cachePath, savedHistory.length);
|
|
224
388
|
return result;
|
|
225
389
|
}
|
|
226
390
|
}
|
|
227
391
|
else {
|
|
392
|
+
let historyWithTools = history;
|
|
393
|
+
if (staticTools && tools) {
|
|
394
|
+
historyWithTools = prependToolsToHistory(history, tools);
|
|
395
|
+
}
|
|
396
|
+
else if (dynamicTools && tools) {
|
|
397
|
+
historyWithTools = appendToolsToHistory(history, tools);
|
|
398
|
+
}
|
|
399
|
+
const transformedHistory = transformMessages(historyWithTools);
|
|
228
400
|
logCacheDisabled();
|
|
229
401
|
logMessagesToAddon(transformedHistory, "NO_CACHE");
|
|
230
|
-
return yield* processModelResponse(model, transformedHistory,
|
|
402
|
+
return yield* processModelResponse(model, transformedHistory, tools, mergedGenerationParams, undefined, dialect);
|
|
231
403
|
}
|
|
232
404
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure state + decision helpers for kv-cache bookkeeping used by
|
|
3
|
+
* `completion-stream.ts`.
|
|
4
|
+
*
|
|
5
|
+
* This module intentionally has **no** `bare-*` imports so it can be
|
|
6
|
+
* exercised directly from unit tests running under `bun` without pulling
|
|
7
|
+
* in the Bare runtime (which is not available in that environment).
|
|
8
|
+
* The file-system-dependent pieces (e.g. `recordCacheSaveCount`) live in
|
|
9
|
+
* `completion-stream.ts` and consume the state exported here.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Number of chat messages the kv-cache file on disk is known to cover, keyed
|
|
13
|
+
* by cache path. Written after a successful completion records a save, read
|
|
14
|
+
* by `prepareMessagesForCache` to slice the history on the next turn so a
|
|
15
|
+
* consumer can stage multiple messages between completions (e.g. an
|
|
16
|
+
* `[assistant, user]` recovery sequence) without resending the whole history.
|
|
17
|
+
*
|
|
18
|
+
* INVARIANT: an entry is only present if the corresponding kv-cache file is
|
|
19
|
+
* considered trustworthy. On any turn where the SDK cannot prove the saved
|
|
20
|
+
* count reflects the on-disk state (cancellation mid-decode, zero-token
|
|
21
|
+
* reply, cache file missing after a save attempt), the entry MUST be
|
|
22
|
+
* deleted; a stale entry causes the next turn to slice its history down to
|
|
23
|
+
* an empty payload and the model returns zero tokens.
|
|
24
|
+
*
|
|
25
|
+
* Mode notes:
|
|
26
|
+
* - Static-mode turns are the only readers of this map. `clearStaleCount`
|
|
27
|
+
* in `decideCachedHistorySlice` exists so a stale-but-non-zero entry
|
|
28
|
+
* can be detected and dropped on the next read.
|
|
29
|
+
* - Dynamic-mode turns do NOT consume this map — the addon trims tools
|
|
30
|
+
* and the chain output from the kv-cache after each round, so the
|
|
31
|
+
* SDK falls back to role-based dispatch in `prepareMessagesForCache`.
|
|
32
|
+
* Writes still happen on dynamic-mode turns, but the recorded count
|
|
33
|
+
* reflects the messages the SDK shipped, not the (possibly trimmed)
|
|
34
|
+
* on-disk cache shape. The map is internally consistent within a
|
|
35
|
+
* single mode; a `kvCache` key should not be reused across modes.
|
|
36
|
+
*/
|
|
37
|
+
export declare const cachedMessageCounts: Map<string, number>;
|
|
38
|
+
/**
|
|
39
|
+
* Clear bookkeeping entries. With no argument, clears the whole map. With a
|
|
40
|
+
* `prefix`, removes any entry whose path is equal to it OR sits beneath it
|
|
41
|
+
* as a directory (i.e. `key.startsWith(prefix + sep)`).
|
|
42
|
+
*
|
|
43
|
+
* Runtime callers MUST pass the platform path separator (e.g. `path.sep`
|
|
44
|
+
* from `bare-path`) so directory-prefix matches are correct on every
|
|
45
|
+
* target. The "/" default exists only for unit tests under `bun`, where
|
|
46
|
+
* cache paths are POSIX-shaped and importing `bare-path` would pull in
|
|
47
|
+
* the Bare runtime. The exported wrapper in `completion-stream.ts`
|
|
48
|
+
* injects the real separator for in-process use.
|
|
49
|
+
*/
|
|
50
|
+
export declare function clearCachedMessageCounts(prefix?: string, sep?: string): void;
|
|
51
|
+
/**
|
|
52
|
+
* Called from `server/bare/ops/cancel.ts` right before `addon.cancel()` so
|
|
53
|
+
* that the in-flight `completion()` for this model can detect that its
|
|
54
|
+
* current turn is being cancelled and skip poisoning `cachedMessageCounts`
|
|
55
|
+
* with a `history.length + 1` entry that does not correspond to a real
|
|
56
|
+
* assistant reply.
|
|
57
|
+
*/
|
|
58
|
+
export declare function noteCancelRequested(modelId: string): void;
|
|
59
|
+
export declare function snapshotCancelCount(modelId: string): number;
|
|
60
|
+
/** Test-only. */
|
|
61
|
+
export declare function _resetCancelCountersForTest(): void;
|
|
62
|
+
/**
|
|
63
|
+
* A completion's `savedCount` should only be recorded when the turn ran to
|
|
64
|
+
* completion AND produced at least one token. Any other outcome —
|
|
65
|
+
* cancelled mid-decode, legitimate zero-token reply, or an early EOS — must
|
|
66
|
+
* clear the entry instead, because there is no guarantee that the kv-cache
|
|
67
|
+
* file on disk matches the `history.length + 1` boundary the SDK would
|
|
68
|
+
* otherwise record.
|
|
69
|
+
*/
|
|
70
|
+
export declare function shouldRecordSavedCount(wasCancelled: boolean, producedTokens: boolean): boolean;
|
|
71
|
+
export interface HistoryMessage {
|
|
72
|
+
role: string;
|
|
73
|
+
content: string;
|
|
74
|
+
attachments?: {
|
|
75
|
+
path: string;
|
|
76
|
+
}[] | undefined;
|
|
77
|
+
}
|
|
78
|
+
export interface HistorySliceDecision {
|
|
79
|
+
/** Messages to send to the model on the next turn. */
|
|
80
|
+
messages: HistoryMessage[];
|
|
81
|
+
/**
|
|
82
|
+
* True when the decision path proves the current `savedCount` is stale
|
|
83
|
+
* and the caller should `cachedMessageCounts.delete(cachePath)` to avoid
|
|
84
|
+
* propagating the bad count to the next turn.
|
|
85
|
+
*/
|
|
86
|
+
clearStaleCount: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Pure slice decision for `prepareMessagesForCache`.
|
|
90
|
+
*
|
|
91
|
+
* Mirrors the shape of the logic in `completion-stream.ts` but without
|
|
92
|
+
* calling `transformMessages` (which depends on `bare-fs` for attachment
|
|
93
|
+
* probing). Kept here so the decision can be unit-tested in isolation.
|
|
94
|
+
*
|
|
95
|
+
* The key regression guard: when a non-zero `savedCount` would slice the
|
|
96
|
+
* history down to an empty array, it is treated as stale — the caller
|
|
97
|
+
* falls back to sending the system-stripped full history rather than
|
|
98
|
+
* handing the model an empty payload.
|
|
99
|
+
*/
|
|
100
|
+
export declare function decideCachedHistorySlice(savedCount: number, cacheExists: boolean, history: HistoryMessage[]): HistorySliceDecision;
|
|
101
|
+
//# sourceMappingURL=kv-cache-state.d.ts.map
|