@qvac/sdk 0.10.2 → 0.12.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 +1455 -29
- package/README.md +2 -0
- package/dist/client/api/cancel.d.ts +24 -32
- package/dist/client/api/cancel.js +58 -31
- package/dist/client/api/classify.d.ts +23 -0
- package/dist/client/api/classify.js +43 -0
- package/dist/client/api/client-request-id.d.ts +17 -0
- package/dist/client/api/client-request-id.js +27 -0
- package/dist/client/api/completion-stream.d.ts +1 -1
- package/dist/client/api/completion-stream.js +34 -3
- package/dist/client/api/diffusion.d.ts +46 -0
- package/dist/client/api/diffusion.js +50 -1
- package/dist/client/api/download-asset.d.ts +12 -2
- package/dist/client/api/download-asset.js +21 -3
- package/dist/client/api/embed.d.ts +8 -4
- package/dist/client/api/embed.js +14 -1
- package/dist/client/api/get-model-info.d.ts +1 -1
- package/dist/client/api/index.d.ts +5 -0
- package/dist/client/api/index.js +5 -0
- package/dist/client/api/load-model.d.ts +12 -4
- package/dist/client/api/load-model.js +49 -2
- package/dist/client/api/rag.d.ts +9 -3
- package/dist/client/api/rag.js +23 -3
- package/dist/client/api/transcribe.d.ts +23 -8
- package/dist/client/api/transcribe.js +69 -4
- package/dist/client/api/unload-model.d.ts +5 -2
- package/dist/client/api/unload-model.js +9 -4
- package/dist/client/api/upscale.d.ts +45 -0
- package/dist/client/api/upscale.js +99 -0
- package/dist/client/api/video.d.ts +64 -0
- package/dist/client/api/video.js +147 -0
- package/dist/client/api/vla-helpers.d.ts +45 -0
- package/dist/client/api/vla-helpers.js +130 -0
- package/dist/client/api/vla.d.ts +65 -0
- package/dist/client/api/vla.js +109 -0
- package/dist/client/config-loader/resolve-config.node.d.ts +16 -0
- package/dist/client/config-loader/resolve-config.node.js +77 -23
- package/dist/client/plugins-factory.d.ts +8 -0
- package/dist/client/plugins-factory.js +10 -0
- package/dist/client/rpc/bare-client.js +62 -66
- package/dist/client/rpc/ensure-worker-ready.d.ts +9 -0
- package/dist/client/rpc/ensure-worker-ready.js +17 -0
- package/dist/client/rpc/expo-rpc-client.js +2 -1
- package/dist/client/rpc/node-rpc-client.js +22 -8
- package/dist/client/rpc/rpc-client.d.ts +2 -2
- package/dist/client/rpc/rpc-client.js +10 -4
- package/dist/client/rpc/rpc-error.d.ts +9 -0
- package/dist/client/rpc/rpc-error.js +105 -0
- package/dist/commands/bundle/bare-pack.d.ts +13 -0
- package/dist/commands/bundle/bare-pack.js +54 -0
- package/dist/commands/bundle/constants.d.ts +7 -0
- package/dist/commands/bundle/constants.js +23 -0
- package/dist/commands/bundle/entry-gen.d.ts +2 -0
- package/dist/commands/bundle/entry-gen.js +48 -0
- package/dist/commands/bundle/index.d.ts +20 -0
- package/dist/commands/bundle/index.js +141 -0
- package/dist/commands/bundle/manifest.d.ts +21 -0
- package/dist/commands/bundle/manifest.js +193 -0
- package/dist/commands/bundle/plugins.d.ts +9 -0
- package/dist/commands/bundle/plugins.js +51 -0
- package/dist/commands/index.d.ts +5 -0
- package/dist/commands/index.js +2 -0
- package/dist/commands/verify/abi.d.ts +52 -0
- package/dist/commands/verify/abi.js +125 -0
- package/dist/commands/verify/addon-source.d.ts +43 -0
- package/dist/commands/verify/addon-source.js +75 -0
- package/dist/commands/verify/bundle-source.d.ts +12 -0
- package/dist/commands/verify/bundle-source.js +53 -0
- package/dist/commands/verify/index.d.ts +59 -0
- package/dist/commands/verify/index.js +371 -0
- package/dist/commands/verify/node-modules-source.d.ts +11 -0
- package/dist/commands/verify/node-modules-source.js +76 -0
- package/dist/commands/verify/prebuilds.d.ts +15 -0
- package/dist/commands/verify/prebuilds.js +39 -0
- package/dist/examples/cancel-by-request-id.d.ts +38 -0
- package/dist/examples/cancel-by-request-id.js +103 -0
- package/dist/examples/classification/classify-image.d.ts +2 -0
- package/dist/examples/classification/classify-image.js +23 -0
- package/dist/examples/diffusion-esrgan-upscale.d.ts +2 -0
- package/dist/examples/diffusion-esrgan-upscale.js +78 -0
- package/dist/examples/diffusion-txt2vid.d.ts +2 -0
- package/dist/examples/diffusion-txt2vid.js +81 -0
- package/dist/examples/download-with-cancel.js +10 -10
- package/dist/examples/embed-multi-gpu.d.ts +2 -0
- package/dist/examples/embed-multi-gpu.js +46 -0
- package/dist/examples/llamacpp-multi-gpu.d.ts +2 -0
- package/dist/examples/llamacpp-multi-gpu.js +57 -0
- package/dist/examples/plugins.js +3 -7
- package/dist/examples/rag/rag-hyperdb/cancellation.js +20 -17
- package/dist/examples/tools/llamacpp-tools-gemma4.d.ts +2 -0
- package/dist/examples/tools/llamacpp-tools-gemma4.js +73 -0
- package/dist/examples/tools/llamacpp-tools-qwen35.d.ts +2 -0
- package/dist/examples/tools/llamacpp-tools-qwen35.js +69 -0
- package/dist/examples/transcription/parakeet-ctc-filesystem.js +18 -12
- package/dist/examples/transcription/parakeet-microphone-record.js +7 -12
- package/dist/examples/transcription/parakeet-microphone-stream.d.ts +2 -0
- package/dist/examples/transcription/parakeet-microphone-stream.js +79 -0
- package/dist/examples/transcription/parakeet-sortformer-streaming.d.ts +2 -0
- package/dist/examples/transcription/parakeet-sortformer-streaming.js +121 -0
- package/dist/examples/transcription/parakeet-sortformer.js +58 -55
- package/dist/examples/transcription/parakeet-tdt-filesystem.js +18 -15
- package/dist/examples/transcription/whispercpp-microphone-conversation.d.ts +2 -0
- package/dist/examples/transcription/whispercpp-microphone-conversation.js +107 -0
- package/dist/examples/tts/chatterbox.js +7 -16
- package/dist/examples/tts/llm-to-tts-streaming.js +3 -10
- package/dist/examples/tts/supertonic-multilingual.js +4 -12
- package/dist/examples/tts/supertonic.js +5 -12
- package/dist/examples/vla-smolvla.d.ts +2 -0
- package/dist/examples/vla-smolvla.js +75 -0
- package/dist/examples/voice-assistant/voice-assistant.js +3 -10
- package/dist/expo/plugins/find-in-ancestor-node-modules.d.ts +14 -0
- package/dist/expo/plugins/find-in-ancestor-node-modules.js +24 -0
- package/dist/expo/plugins/get-project-root.d.ts +22 -0
- package/dist/expo/plugins/get-project-root.js +23 -0
- package/dist/expo/plugins/index.d.ts +4 -0
- package/dist/expo/plugins/index.js +4 -0
- package/dist/expo/plugins/resolve-sdk-package-dir.d.ts +9 -2
- package/dist/expo/plugins/resolve-sdk-package-dir.js +38 -11
- package/dist/expo/plugins/withDeviceInfo.js +2 -1
- package/dist/expo/plugins/withMobileBundle.d.ts +5 -4
- package/dist/expo/plugins/withMobileBundle.js +42 -58
- package/dist/index.d.ts +6 -2
- package/dist/index.js +23 -2
- package/dist/models/registry/models.d.ts +3725 -514
- package/dist/models/registry/models.js +7269 -3910
- package/dist/models/update-models/naming.js +22 -8
- package/dist/pear/pre.d.ts +3 -0
- package/dist/pear/pre.js +9 -2
- package/dist/schemas/cancel.d.ts +172 -28
- package/dist/schemas/cancel.js +120 -19
- package/dist/schemas/classification.d.ts +53 -0
- package/dist/schemas/classification.js +33 -0
- package/dist/schemas/common.d.ts +860 -1379
- package/dist/schemas/common.js +8 -1
- package/dist/schemas/completion-event.d.ts +17 -0
- package/dist/schemas/completion-event.js +12 -1
- package/dist/schemas/completion-stream.d.ts +16 -1
- package/dist/schemas/completion-stream.js +25 -7
- package/dist/schemas/download-asset.d.ts +25 -9
- package/dist/schemas/download-asset.js +17 -6
- package/dist/schemas/embed.d.ts +1 -0
- package/dist/schemas/embed.js +5 -0
- package/dist/schemas/engine-addon-map.d.ts +3 -0
- package/dist/schemas/engine-addon-map.js +18 -5
- package/dist/schemas/error.d.ts +31 -0
- package/dist/schemas/error.js +29 -1
- package/dist/schemas/finetune.d.ts +2 -0
- package/dist/schemas/finetune.js +5 -0
- package/dist/schemas/get-model-info.d.ts +10 -6
- package/dist/schemas/get-model-info.js +2 -0
- package/dist/schemas/index.d.ts +3 -0
- package/dist/schemas/index.js +3 -0
- package/dist/schemas/llamacpp-config.d.ts +64 -8
- package/dist/schemas/llamacpp-config.js +12 -0
- package/dist/schemas/load-model.d.ts +4644 -8605
- package/dist/schemas/load-model.js +90 -7
- package/dist/schemas/model-src-utils.d.ts +34 -14
- package/dist/schemas/model-types.d.ts +54 -14
- package/dist/schemas/model-types.js +28 -5
- package/dist/schemas/ocr.d.ts +8 -3
- package/dist/schemas/plugin.d.ts +60 -6
- package/dist/schemas/plugin.js +31 -5
- package/dist/schemas/rag.d.ts +9 -0
- package/dist/schemas/rag.js +17 -9
- package/dist/schemas/registry.d.ts +74 -30
- package/dist/schemas/registry.js +5 -0
- package/dist/schemas/reload-config.d.ts +34 -14
- package/dist/schemas/sdcpp-config.d.ts +445 -16
- package/dist/schemas/sdcpp-config.js +387 -26
- package/dist/schemas/sdk-config.d.ts +2431 -2723
- package/dist/schemas/sdk-config.js +16 -1
- package/dist/schemas/sdk-errors-client.d.ts +6 -0
- package/dist/schemas/sdk-errors-client.js +31 -1
- package/dist/schemas/sdk-errors-server.d.ts +8 -1
- package/dist/schemas/sdk-errors-server.js +45 -5
- package/dist/schemas/text-to-speech.d.ts +81 -698
- package/dist/schemas/text-to-speech.js +46 -19
- package/dist/schemas/transcription-config.d.ts +40 -229
- package/dist/schemas/transcription-config.js +76 -15
- package/dist/schemas/transcription.d.ts +108 -2
- package/dist/schemas/transcription.js +81 -0
- package/dist/schemas/translate.d.ts +2 -0
- package/dist/schemas/translate.js +13 -2
- package/dist/schemas/translation-config.d.ts +90 -40
- package/dist/schemas/unload-model.d.ts +2 -1
- package/dist/schemas/unload-model.js +4 -1
- package/dist/schemas/vla.d.ts +87 -0
- package/dist/schemas/vla.js +108 -0
- package/dist/scripts/run-unit-tests.js +22 -3
- package/dist/server/bare/ops/cancel.d.ts +27 -1
- package/dist/server/bare/ops/cancel.js +36 -10
- package/dist/server/bare/ops/embed.d.ts +3 -3
- package/dist/server/bare/ops/embed.js +144 -28
- package/dist/server/bare/ops/kv-cache-utils.d.ts +0 -7
- package/dist/server/bare/ops/kv-cache-utils.js +8 -47
- package/dist/server/bare/ops/transcribe.d.ts +11 -5
- package/dist/server/bare/ops/transcribe.js +230 -50
- package/dist/server/bare/ops/translate.d.ts +1 -1
- package/dist/server/bare/ops/translate.js +197 -88
- package/dist/server/bare/plugins/ggml-classification/ops/classify.d.ts +6 -0
- package/dist/server/bare/plugins/ggml-classification/ops/classify.js +30 -0
- package/dist/server/bare/plugins/ggml-classification/plugin.d.ts +32 -0
- package/dist/server/bare/plugins/ggml-classification/plugin.js +55 -0
- package/dist/server/bare/plugins/ggml-vla/ops/vla-hparams.d.ts +3 -0
- package/dist/server/bare/plugins/ggml-vla/ops/vla-hparams.js +12 -0
- package/dist/server/bare/plugins/ggml-vla/ops/vla-run.d.ts +3 -0
- package/dist/server/bare/plugins/ggml-vla/ops/vla-run.js +75 -0
- package/dist/server/bare/plugins/ggml-vla/plugin.d.ts +54 -0
- package/dist/server/bare/plugins/ggml-vla/plugin.js +57 -0
- package/dist/server/bare/plugins/index.d.ts +3 -1
- package/dist/server/bare/plugins/index.js +3 -1
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +12 -3
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +173 -172
- package/dist/server/bare/plugins/llamacpp-completion/ops/context-overflow.d.ts +29 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/context-overflow.js +65 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +125 -21
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.d.ts +154 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.js +448 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.d.ts +21 -73
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.js +18 -101
- package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +36 -3
- package/dist/server/bare/plugins/llamacpp-completion/plugin.js +183 -85
- package/dist/server/bare/plugins/llamacpp-completion/transform.d.ts +10 -0
- package/dist/server/bare/plugins/llamacpp-completion/transform.js +34 -0
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +7 -0
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +10 -1
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +42 -15
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +5 -1
- package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +8 -3
- package/dist/server/bare/plugins/onnx-ocr/plugin.js +3 -0
- package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +73 -244
- package/dist/server/bare/plugins/parakeet-transcription/plugin.js +68 -96
- package/dist/server/bare/plugins/sdcpp-generation/ops/diffusion.js +8 -0
- package/dist/server/bare/plugins/sdcpp-generation/ops/upscale.d.ts +3 -0
- package/dist/server/bare/plugins/sdcpp-generation/ops/upscale.js +40 -0
- package/dist/server/bare/plugins/sdcpp-generation/ops/video.d.ts +3 -0
- package/dist/server/bare/plugins/sdcpp-generation/ops/video.js +199 -0
- package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +288 -15
- package/dist/server/bare/plugins/sdcpp-generation/plugin.js +150 -7
- package/dist/server/bare/plugins/{onnx-tts → tts-ggml}/ops/text-to-speech-stream.js +2 -15
- package/dist/server/bare/plugins/{onnx-tts → tts-ggml}/ops/text-to-speech.js +2 -1
- package/dist/server/bare/plugins/tts-ggml/plugin.d.ts +75 -0
- package/dist/server/bare/plugins/tts-ggml/plugin.js +189 -0
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +47 -3
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +61 -14
- package/dist/server/bare/rag-hyperdb/index.d.ts +1 -1
- package/dist/server/bare/rag-hyperdb/index.js +1 -1
- package/dist/server/bare/rag-hyperdb/rag-operation-manager.d.ts +26 -4
- package/dist/server/bare/rag-hyperdb/rag-operation-manager.js +65 -28
- package/dist/server/bare/registry/model-config-utils.js +7 -0
- package/dist/server/bare/runtime/disposable-scope.d.ts +36 -0
- package/dist/server/bare/runtime/disposable-scope.js +69 -0
- package/dist/server/bare/runtime/index.d.ts +8 -0
- package/dist/server/bare/runtime/index.js +3 -0
- package/dist/server/bare/runtime/request-context.d.ts +49 -0
- package/dist/server/bare/runtime/request-context.js +1 -0
- package/dist/server/bare/runtime/request-id.d.ts +12 -0
- package/dist/server/bare/runtime/request-id.js +14 -0
- package/dist/server/bare/runtime/request-registry-singleton.d.ts +4 -0
- package/dist/server/bare/runtime/request-registry-singleton.js +27 -0
- package/dist/server/bare/runtime/request-registry.d.ts +136 -0
- package/dist/server/bare/runtime/request-registry.js +297 -0
- package/dist/server/bare/runtime/with-request-context.d.ts +22 -0
- package/dist/server/bare/runtime/with-request-context.js +53 -0
- package/dist/server/bare/runtime-lifecycle.js +9 -9
- package/dist/server/bare/types/addon-responses.d.ts +12 -1
- package/dist/server/env.js +4 -1
- package/dist/server/plugins/index.d.ts +1 -1
- package/dist/server/plugins/index.js +1 -1
- package/dist/server/plugins/registry.d.ts +1 -0
- package/dist/server/plugins/registry.js +5 -0
- package/dist/server/rpc/handler-registry.js +30 -7
- package/dist/server/rpc/handler-selection.d.ts +9 -0
- package/dist/server/rpc/handler-selection.js +14 -0
- package/dist/server/rpc/handler-utils.js +5 -13
- package/dist/server/rpc/handlers/cancel-delegated.js +28 -31
- package/dist/server/rpc/handlers/cancelHandler.d.ts +30 -1
- package/dist/server/rpc/handlers/cancelHandler.js +54 -22
- package/dist/server/rpc/handlers/classify.d.ts +3 -0
- package/dist/server/rpc/handlers/classify.js +4 -0
- package/dist/server/rpc/handlers/delete-cache.d.ts +9 -0
- package/dist/server/rpc/handlers/delete-cache.js +13 -15
- package/dist/server/rpc/handlers/download-asset.js +133 -40
- package/dist/server/rpc/handlers/index.d.ts +4 -0
- package/dist/server/rpc/handlers/index.js +4 -0
- package/dist/server/rpc/handlers/load-model/download-manager.d.ts +60 -2
- package/dist/server/rpc/handlers/load-model/download-manager.js +141 -8
- package/dist/server/rpc/handlers/load-model/handler.js +216 -98
- package/dist/server/rpc/handlers/load-model/http.js +3 -3
- package/dist/server/rpc/handlers/load-model/hyperdrive.js +1 -1
- package/dist/server/rpc/handlers/load-model/registry.js +1 -1
- package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +20 -1
- package/dist/server/rpc/handlers/load-model/resolve-session.js +6 -5
- package/dist/server/rpc/handlers/load-model/resolve.d.ts +3 -2
- package/dist/server/rpc/handlers/load-model/resolve.js +18 -6
- package/dist/server/rpc/handlers/load-model/types.d.ts +24 -0
- package/dist/server/rpc/handlers/rag.js +133 -15
- package/dist/server/rpc/handlers/upscale-stream.d.ts +3 -0
- package/dist/server/rpc/handlers/upscale-stream.js +4 -0
- package/dist/server/rpc/handlers/video-stream.d.ts +3 -0
- package/dist/server/rpc/handlers/video-stream.js +4 -0
- package/dist/server/rpc/profiling/operation-metrics.js +23 -0
- package/dist/server/utils/completion-normalizer.js +8 -0
- package/dist/server/utils/config-transform.js +1 -1
- package/dist/server/utils/tools/dialect.js +4 -0
- package/dist/server/utils/tools/parser.js +16 -1
- package/dist/server/utils/tools/parsers/gemma4native.d.ts +4 -0
- package/dist/server/utils/tools/parsers/gemma4native.js +60 -0
- package/dist/server/utils/tools/parsers/qwen35.d.ts +4 -0
- package/dist/server/utils/tools/parsers/qwen35.js +120 -0
- package/dist/server/worker.js +14 -11
- package/dist/utils/aggregate-events.d.ts +9 -0
- package/dist/utils/aggregate-events.js +18 -3
- package/dist/utils/decorate-promise.d.ts +34 -0
- package/dist/utils/decorate-promise.js +35 -0
- package/dist/utils/errors-client.d.ts +18 -0
- package/dist/utils/errors-client.js +31 -0
- package/dist/utils/errors-server.d.ts +115 -2
- package/dist/utils/errors-server.js +148 -3
- package/package.json +68 -77
- package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +0 -490
- package/dist/server/bare/plugins/onnx-tts/plugin.js +0 -266
- package/dist/server/bare/plugins/onnx-tts/wav-helper.d.ts +0 -14
- package/dist/server/bare/plugins/onnx-tts/wav-helper.js +0 -107
- /package/dist/server/bare/plugins/{onnx-tts → tts-ggml}/ops/text-to-speech-stream.d.ts +0 -0
- /package/dist/server/bare/plugins/{onnx-tts → tts-ggml}/ops/text-to-speech.d.ts +0 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import { promises as fsPromises } from "bare-fs";
|
|
2
|
+
import path from "bare-path";
|
|
3
|
+
import { findMatchingCache, generateConfigHash, getCacheFilePath, getCurrentCacheInfo, renameCacheFile, deleteCache as deleteCacheUtil, } from "../../../ops/kv-cache-utils.js";
|
|
4
|
+
import { logCacheSaveError, logCacheStatus, } from "./cache-logger.js";
|
|
5
|
+
import { getServerLogger } from "../../../../../logging/index.js";
|
|
6
|
+
// Used by cross-model paths that have no `RequestContext` (e.g.
|
|
7
|
+
// `deleteKvCacheState`). Per-session call sites receive a logger from
|
|
8
|
+
// the caller — typically `withRequestContext(...)`.
|
|
9
|
+
const moduleLogger = getServerLogger();
|
|
10
|
+
/**
|
|
11
|
+
* Single owner of the three KV-cache bookkeeping layers.
|
|
12
|
+
*
|
|
13
|
+
* The llama.cpp completion handler has three independent layers it must
|
|
14
|
+
* keep consistent across every cancel/error branch:
|
|
15
|
+
*
|
|
16
|
+
* 1. `cachedMessageCounts: Map<path, count>` — the "n messages
|
|
17
|
+
* currently on disk" tracker.
|
|
18
|
+
* 2. `initializedCaches: Set<key>` — the "addon defers disk writes;
|
|
19
|
+
* we know this cache is primed" tracker.
|
|
20
|
+
* 3. On-disk `.bin` files written by the addon.
|
|
21
|
+
*
|
|
22
|
+
* Without a single owner, every cancel / zero-token / rename-failed /
|
|
23
|
+
* tool-call exit would need to touch all three; any branch that forgets
|
|
24
|
+
* a layer produces three-layer drift bugs.
|
|
25
|
+
*
|
|
26
|
+
* `KvCacheSession` collapses the three layers behind one object with
|
|
27
|
+
* three operations:
|
|
28
|
+
*
|
|
29
|
+
* - `beginTurn` — resolves the cache file path, primes the system
|
|
30
|
+
* prompt cache if missing (delegated to a caller-supplied closure
|
|
31
|
+
* so the session doesn't depend on the model addon), marks the
|
|
32
|
+
* cache initialized, and returns a `TurnHandle` carrying the
|
|
33
|
+
* resolved path + the snapshot of the on-disk saved count.
|
|
34
|
+
* - `commitTurn` — records the new saved count (for custom-key
|
|
35
|
+
* turns) or renames the addon's pre-response file to the
|
|
36
|
+
* post-response path and records the count there (for auto-cache
|
|
37
|
+
* turns). Flips the turn's internal `committed` flag so the
|
|
38
|
+
* deferred `rollback` becomes a no-op on the happy path.
|
|
39
|
+
* - `rollback` — atomically deletes the on-disk file, clears the
|
|
40
|
+
* in-memory init entry, and forgets the saved count. **All three
|
|
41
|
+
* layers, always, in one place.** Handlers call it once via
|
|
42
|
+
* `ctx.scope.defer(() => session.rollback(turn))`; `commitTurn`
|
|
43
|
+
* short-circuits it on success.
|
|
44
|
+
*
|
|
45
|
+
* The module-level `deleteKvCacheState(...)` function (below) provides
|
|
46
|
+
* an administrative cross-model delete API for the
|
|
47
|
+
* `handleDeleteCache` RPC handler.
|
|
48
|
+
*
|
|
49
|
+
* The module-scoped `cachedMessageCounts` and `initializedCaches` maps
|
|
50
|
+
* are *private* to this file — no other module reaches into them.
|
|
51
|
+
* Callers that need cache-status info do so through the session API.
|
|
52
|
+
*/
|
|
53
|
+
// ----- module-scoped state. The session is the single mutation point
|
|
54
|
+
// for the in-memory KV-cache bookkeeping. -----
|
|
55
|
+
/**
|
|
56
|
+
* Number of chat messages the kv-cache file on disk is known to cover,
|
|
57
|
+
* keyed by cache path. Written by `commitTurn`, read by `getSavedCount`,
|
|
58
|
+
* deleted by `rollback` / `delete` / `dropStaleSavedCount`. The same
|
|
59
|
+
* INVARIANT that existed in `kv-cache-state.ts` still holds: an entry is
|
|
60
|
+
* present only when the corresponding `.bin` file is considered
|
|
61
|
+
* trustworthy. Cancelled or zero-token turns must remove the entry so
|
|
62
|
+
* the next-turn slice doesn't read a stale boundary.
|
|
63
|
+
*/
|
|
64
|
+
const cachedMessageCounts = new Map();
|
|
65
|
+
/**
|
|
66
|
+
* In-memory registry of caches initialized this session. The addon
|
|
67
|
+
* defers disk writes, so the absence of a `.bin` file on disk isn't
|
|
68
|
+
* proof that the cache hasn't been primed in this worker process. Keyed
|
|
69
|
+
* by `${modelId}:${configHash}:${cacheKey}`, so on-disk caches from
|
|
70
|
+
* older worker runs still hit the lazy-load path in `beginTurn`.
|
|
71
|
+
*/
|
|
72
|
+
const initializedCaches = new Set();
|
|
73
|
+
function initRegistryKey(modelId, configHash, cacheKey) {
|
|
74
|
+
return `${modelId}:${configHash}:${cacheKey}`;
|
|
75
|
+
}
|
|
76
|
+
// ----- factory -----
|
|
77
|
+
/**
|
|
78
|
+
* Construct a session bound to one `(modelId, turn-owning request)`
|
|
79
|
+
* scope. `options.logger` is the per-instance logger the session emits
|
|
80
|
+
* through (typically `withRequestContext(getServerLogger(), ctx)`);
|
|
81
|
+
* falls back to the module-scoped logger when omitted.
|
|
82
|
+
*/
|
|
83
|
+
export function createKvCacheSession(modelId, options) {
|
|
84
|
+
const logger = options?.logger ?? moduleLogger;
|
|
85
|
+
// Per-session map: each `TurnHandle` carries an opaque entry here. A
|
|
86
|
+
// WeakMap so handles drop their state once the handler scope releases
|
|
87
|
+
// the reference; the module-scoped maps above survive.
|
|
88
|
+
const turnState = new WeakMap();
|
|
89
|
+
function makeHandle(cachePath, registryKey) {
|
|
90
|
+
const handle = {
|
|
91
|
+
cachePath,
|
|
92
|
+
savedCount: cachedMessageCounts.get(cachePath) ?? 0,
|
|
93
|
+
};
|
|
94
|
+
turnState.set(handle, {
|
|
95
|
+
cachePath,
|
|
96
|
+
registryKey,
|
|
97
|
+
committed: false,
|
|
98
|
+
rolledBack: false,
|
|
99
|
+
});
|
|
100
|
+
return handle;
|
|
101
|
+
}
|
|
102
|
+
async function beginCustom(input) {
|
|
103
|
+
const cachePath = await getCacheFilePath(modelId, input.configHash, input.customKey);
|
|
104
|
+
const registryKey = initRegistryKey(modelId, input.configHash, input.customKey);
|
|
105
|
+
// In-memory registry check first — the addon defers disk writes, so
|
|
106
|
+
// a freshly-primed cache may not yet exist on disk. If the
|
|
107
|
+
// in-memory flag isn't set, fall back to a filesystem probe so
|
|
108
|
+
// caches surviving across worker restarts still hit the reuse path.
|
|
109
|
+
let exists = initializedCaches.has(registryKey);
|
|
110
|
+
if (!exists) {
|
|
111
|
+
try {
|
|
112
|
+
await fsPromises.access(cachePath);
|
|
113
|
+
exists = true;
|
|
114
|
+
initializedCaches.add(registryKey);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
exists = false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
logCacheStatus(input.customKey, exists);
|
|
121
|
+
if (!exists) {
|
|
122
|
+
await input.primeIfMissing(cachePath);
|
|
123
|
+
await verifyPrimedFile(cachePath, logger);
|
|
124
|
+
initializedCaches.add(registryKey);
|
|
125
|
+
}
|
|
126
|
+
return makeHandle(cachePath, registryKey);
|
|
127
|
+
}
|
|
128
|
+
async function beginAuto(input) {
|
|
129
|
+
// The pre-response cache key is derived from
|
|
130
|
+
// `history.slice(0, -1)` — `findMatchingCache` does that
|
|
131
|
+
// internally. The post-response key (used after a successful turn)
|
|
132
|
+
// is computed by the caller and passed to `commitTurn` as
|
|
133
|
+
// `targetCachePath`.
|
|
134
|
+
const existingCache = await findMatchingCache(modelId, input.configHash, input.history);
|
|
135
|
+
const preResponseCacheInfo = await getCurrentCacheInfo(modelId, input.configHash, input.history);
|
|
136
|
+
const cachePath = existingCache !== null
|
|
137
|
+
? existingCache.cachePath
|
|
138
|
+
: preResponseCacheInfo.cachePath;
|
|
139
|
+
const cacheKeyForRegistry = existingCache !== null
|
|
140
|
+
? existingCache.cacheKey
|
|
141
|
+
: preResponseCacheInfo.cacheKey;
|
|
142
|
+
const registryKey = initRegistryKey(modelId, input.configHash, cacheKeyForRegistry);
|
|
143
|
+
const cacheExists = existingCache !== null;
|
|
144
|
+
logCacheStatus("auto", cacheExists);
|
|
145
|
+
if (!cacheExists) {
|
|
146
|
+
await input.primeIfMissing(cachePath);
|
|
147
|
+
await verifyPrimedFile(cachePath, logger);
|
|
148
|
+
initializedCaches.add(registryKey);
|
|
149
|
+
}
|
|
150
|
+
return makeHandle(cachePath, registryKey);
|
|
151
|
+
}
|
|
152
|
+
async function beginTurn(input) {
|
|
153
|
+
if (input.kind === "custom")
|
|
154
|
+
return beginCustom(input);
|
|
155
|
+
return beginAuto(input);
|
|
156
|
+
}
|
|
157
|
+
async function commitTurn(turn, result) {
|
|
158
|
+
const state = turnState.get(turn);
|
|
159
|
+
if (!state) {
|
|
160
|
+
// Handle from a different session or already torn down. Treat as
|
|
161
|
+
// no-op — caller shouldn't be reaching into another session's
|
|
162
|
+
// state, but failing loudly here punishes the rollback-after-end
|
|
163
|
+
// path more than it helps.
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (state.committed || state.rolledBack)
|
|
167
|
+
return;
|
|
168
|
+
if (result.kind === "static") {
|
|
169
|
+
// Custom-key path: the addon wrote the new cache state inline
|
|
170
|
+
// at the same path. Verify the file persisted (the addon
|
|
171
|
+
// currently swallows save errors — see TODO in
|
|
172
|
+
// `verifySaveAndRecord`) and record the new boundary.
|
|
173
|
+
const ok = await verifySaveAndRecord(state.cachePath, result.messageCount);
|
|
174
|
+
if (!ok) {
|
|
175
|
+
// The expected save didn't land — treat the turn as a rollback
|
|
176
|
+
// so the next turn re-primes cleanly.
|
|
177
|
+
await runRollback(state);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
state.committed = true;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Auto-rename path: the pre-response file is now stale (its key
|
|
184
|
+
// refers to history minus the last user turn). Rename it to the
|
|
185
|
+
// post-response key and record the new count there.
|
|
186
|
+
if (!(await renameCacheFile(state.cachePath, result.targetCachePath))) {
|
|
187
|
+
logger.warn(`[kv-cache] Auto cache rename failed; rolling back. from=${state.cachePath} to=${result.targetCachePath}`);
|
|
188
|
+
await runRollback(state);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
// The source path's entry is gone (the file moved). Drop it and
|
|
192
|
+
// record the new count at the rename target.
|
|
193
|
+
cachedMessageCounts.delete(state.cachePath);
|
|
194
|
+
const ok = await verifySaveAndRecord(result.targetCachePath, result.messageCount);
|
|
195
|
+
if (!ok) {
|
|
196
|
+
// Rename succeeded but the file isn't where we expected. Roll
|
|
197
|
+
// back via the target path instead of the (now-empty) source.
|
|
198
|
+
state.cachePath = result.targetCachePath;
|
|
199
|
+
await runRollback(state);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
// Successful auto-rename. The handle's `cachePath` field still
|
|
203
|
+
// points at the (now-gone) source path — that's fine, the handle
|
|
204
|
+
// is committed and won't roll back. Future turns compute fresh
|
|
205
|
+
// paths.
|
|
206
|
+
state.committed = true;
|
|
207
|
+
}
|
|
208
|
+
async function rollback(turn) {
|
|
209
|
+
const state = turnState.get(turn);
|
|
210
|
+
if (!state)
|
|
211
|
+
return;
|
|
212
|
+
if (state.committed || state.rolledBack)
|
|
213
|
+
return;
|
|
214
|
+
await runRollback(state);
|
|
215
|
+
}
|
|
216
|
+
async function runRollback(state) {
|
|
217
|
+
// Order matters only weakly: unlink first so a partial disk-state
|
|
218
|
+
// can't be re-loaded by a sibling turn between the file delete and
|
|
219
|
+
// the in-memory clear. In practice handlers serialise per model;
|
|
220
|
+
// the order is belt-and-suspenders.
|
|
221
|
+
try {
|
|
222
|
+
await fsPromises.unlink(state.cachePath);
|
|
223
|
+
}
|
|
224
|
+
catch (unlinkError) {
|
|
225
|
+
logger.warn(`[kv-cache] Failed to remove cache file during rollback; next turn may load stale KV state. path=${state.cachePath} error=${unlinkError instanceof Error ? unlinkError.message : String(unlinkError)}`);
|
|
226
|
+
}
|
|
227
|
+
initializedCaches.delete(state.registryKey);
|
|
228
|
+
cachedMessageCounts.delete(state.cachePath);
|
|
229
|
+
state.rolledBack = true;
|
|
230
|
+
}
|
|
231
|
+
function dropStaleSavedCount(turn) {
|
|
232
|
+
const state = turnState.get(turn);
|
|
233
|
+
if (!state)
|
|
234
|
+
return;
|
|
235
|
+
cachedMessageCounts.delete(state.cachePath);
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
beginTurn,
|
|
239
|
+
commitTurn,
|
|
240
|
+
rollback,
|
|
241
|
+
dropStaleSavedCount,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
// ----- module-level administrative API -----
|
|
245
|
+
/**
|
|
246
|
+
* Atomically delete every layer of KV-cache state for a
|
|
247
|
+
* `(kvCacheKey, modelId)` pair, or wipe everything. Single entry point
|
|
248
|
+
* — the only mutation point for cross-model state outside of
|
|
249
|
+
* turn-scoped `commitTurn`/`rollback`.
|
|
250
|
+
*
|
|
251
|
+
* Why this isn't a method on `KvCacheSession`: deletes are
|
|
252
|
+
* cross-model (`all: true` has no model; the keyed form has
|
|
253
|
+
* `modelId` optional on the wire). A session, by contrast, is
|
|
254
|
+
* created with a *fixed* `modelId` for the duration of a turn. Making
|
|
255
|
+
* delete a method would force callers to materialise an irrelevant
|
|
256
|
+
* session for cross-model administrative cleanups.
|
|
257
|
+
*
|
|
258
|
+
* Layers cleared, in order:
|
|
259
|
+
* 1. On-disk: `deleteCache(...)` removes the matching directory
|
|
260
|
+
* tree (or wipes and recreates the root for `all: true`).
|
|
261
|
+
* 2. `cachedMessageCounts`: prefix-cleanup by the removed directory
|
|
262
|
+
* so any per-cache count under the deleted tree is forgotten.
|
|
263
|
+
* 3. `initializedCaches`: scope clear by `(kvCacheKey[, modelId])`,
|
|
264
|
+
* matching the on-disk scope.
|
|
265
|
+
*
|
|
266
|
+
* Concurrency with in-flight turns: this delete is wire-async with
|
|
267
|
+
* respect to any turn currently holding a `TurnHandle` for the same
|
|
268
|
+
* cache key. Worst case the on-disk `.bin` is removed while a turn is
|
|
269
|
+
* mid-write; the turn's eventual `commitTurn(...)` then fails the
|
|
270
|
+
* `verifySaveAndRecord` probe (file gone) and rolls back idempotently.
|
|
271
|
+
* No coordination primitive is needed because every layer's mutation
|
|
272
|
+
* is idempotent (`unlink` no-ops if missing, `Map.delete` / `Set.delete`
|
|
273
|
+
* no-op on absent keys).
|
|
274
|
+
*/
|
|
275
|
+
export async function deleteKvCacheState(target) {
|
|
276
|
+
if ("all" in target) {
|
|
277
|
+
const removed = await deleteCacheUtil({ all: true });
|
|
278
|
+
cachedMessageCounts.clear();
|
|
279
|
+
initializedCaches.clear();
|
|
280
|
+
// `removed` is the kv-cache root dir; surfaces it for ops
|
|
281
|
+
// visibility but isn't part of the contract.
|
|
282
|
+
moduleLogger.debug(`[kv-cache] Cleared all caches under ${removed}`);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const removedPath = await deleteCacheUtil({
|
|
286
|
+
kvCacheKey: target.kvCacheKey,
|
|
287
|
+
...(target.modelId !== undefined && { modelId: target.modelId }),
|
|
288
|
+
});
|
|
289
|
+
// Prefix-cleanup the in-memory counts. The on-disk directory tree
|
|
290
|
+
// is `{kvCacheRoot}/{kvCacheKey}[/{modelId}]/`, so every entry in
|
|
291
|
+
// `cachedMessageCounts` whose key is the removed directory itself
|
|
292
|
+
// or sits beneath it must go.
|
|
293
|
+
clearCachedMessageCountsByPrefix(removedPath, path.sep);
|
|
294
|
+
// The in-memory init-set keys are
|
|
295
|
+
// `${modelId}:${configHash}:${kvCacheKey}` — clear by the user-
|
|
296
|
+
// facing kvCacheKey (and optionally narrow by modelId).
|
|
297
|
+
clearInitializedCachesByScope({
|
|
298
|
+
cacheKey: target.kvCacheKey,
|
|
299
|
+
...(target.modelId !== undefined && { modelId: target.modelId }),
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
// ----- private helpers -----
|
|
303
|
+
/**
|
|
304
|
+
* Verify that the addon actually persisted a usable cache file after a
|
|
305
|
+
* prime. Mirrors the `verifySaveAndRecord` access-probe used at commit
|
|
306
|
+
* time, applied at prime time so the session doesn't mark a cache
|
|
307
|
+
* `initializedCaches.add(...)` against a path that's missing or empty
|
|
308
|
+
* on disk.
|
|
309
|
+
*
|
|
310
|
+
* Failure modes this catches:
|
|
311
|
+
*
|
|
312
|
+
* - The addon's `model.run({ saveSessionPath })` was interrupted
|
|
313
|
+
* before the save call ran (e.g. signal abort during prefill); the
|
|
314
|
+
* prime closure resolves cleanly because addon save errors are not
|
|
315
|
+
* propagated, but no file is on disk.
|
|
316
|
+
* - The addon's `llama_state_save_file` was called but produced an
|
|
317
|
+
* empty file (out-of-space / fs error swallowed by the addon).
|
|
318
|
+
*
|
|
319
|
+
* Failure modes this does **NOT** catch:
|
|
320
|
+
*
|
|
321
|
+
* - A partial-but-nonzero file written by the addon (e.g. header +
|
|
322
|
+
* truncated KV state). Catching this requires either an
|
|
323
|
+
* addon-side change (have `CacheManager::writeCacheFile` check the
|
|
324
|
+
* return value of `llama_state_save_file` and throw on failure) or
|
|
325
|
+
* a structural hash check we can't currently compute from the
|
|
326
|
+
* SDK. Filed as a follow-up — see `cache-api.md` in the addon
|
|
327
|
+
* repo / tracking ticket.
|
|
328
|
+
*
|
|
329
|
+
* On failure we best-effort `unlink` an empty leftover file (so the
|
|
330
|
+
* next existence probe doesn't trust it) and throw — the handler in
|
|
331
|
+
* `completion-stream.ts` lets the error propagate up and no
|
|
332
|
+
* `initializedCaches` entry is recorded.
|
|
333
|
+
*/
|
|
334
|
+
async function verifyPrimedFile(cachePath, logger) {
|
|
335
|
+
let stats;
|
|
336
|
+
try {
|
|
337
|
+
stats = await fsPromises.stat(cachePath);
|
|
338
|
+
}
|
|
339
|
+
catch (statError) {
|
|
340
|
+
// ENOENT is the common case here — addon prime returned without
|
|
341
|
+
// calling save (most often: signal abort during prefill).
|
|
342
|
+
throw new Error(`[kv-cache] prime closure resolved but no cache file was written. path=${cachePath} cause=${statError instanceof Error ? statError.message : String(statError)}`);
|
|
343
|
+
}
|
|
344
|
+
if (stats.size === 0) {
|
|
345
|
+
// Best-effort cleanup so a future probe doesn't trust the empty
|
|
346
|
+
// file. Unlink failure is non-fatal — we still throw on the
|
|
347
|
+
// primary "prime didn't persist" condition.
|
|
348
|
+
try {
|
|
349
|
+
await fsPromises.unlink(cachePath);
|
|
350
|
+
}
|
|
351
|
+
catch (unlinkError) {
|
|
352
|
+
logger.warn(`[kv-cache] Failed to remove empty primed cache file. path=${cachePath} error=${unlinkError instanceof Error ? unlinkError.message : String(unlinkError)}`);
|
|
353
|
+
}
|
|
354
|
+
throw new Error(`[kv-cache] prime closure resolved but cache file is empty. path=${cachePath}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Verify the addon actually persisted the cache file before recording
|
|
359
|
+
* its message count. The addon currently swallows write errors
|
|
360
|
+
* silently, so a missing file means the next turn must resend the full
|
|
361
|
+
* history rather than slicing against a stale `savedCount`.
|
|
362
|
+
*
|
|
363
|
+
* TODO: once the addon surfaces save failures (e.g. throws
|
|
364
|
+
* `UnableToSaveSessionFile` when `llama_state_save_file` returns
|
|
365
|
+
* false), drop the `access()` probe and wrap the `model.run()` call in
|
|
366
|
+
* a real try/catch that forwards the error.
|
|
367
|
+
*/
|
|
368
|
+
async function verifySaveAndRecord(cachePath, messageCount) {
|
|
369
|
+
try {
|
|
370
|
+
await fsPromises.access(cachePath);
|
|
371
|
+
cachedMessageCounts.set(cachePath, messageCount);
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
catch (err) {
|
|
375
|
+
cachedMessageCounts.delete(cachePath);
|
|
376
|
+
logCacheSaveError(cachePath, err);
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function clearCachedMessageCountsByPrefix(prefix, sep) {
|
|
381
|
+
if (!prefix) {
|
|
382
|
+
cachedMessageCounts.clear();
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
for (const key of cachedMessageCounts.keys()) {
|
|
386
|
+
if (key === prefix) {
|
|
387
|
+
cachedMessageCounts.delete(key);
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
if (!key.startsWith(prefix + sep))
|
|
391
|
+
continue;
|
|
392
|
+
cachedMessageCounts.delete(key);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function clearInitializedCachesByScope(scope) {
|
|
396
|
+
if (scope.cacheKey === undefined && scope.modelId === undefined) {
|
|
397
|
+
initializedCaches.clear();
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
for (const key of initializedCaches) {
|
|
401
|
+
const firstSep = key.indexOf(":");
|
|
402
|
+
const secondSep = key.indexOf(":", firstSep + 1);
|
|
403
|
+
if (firstSep === -1 || secondSep === -1)
|
|
404
|
+
continue;
|
|
405
|
+
const entryModelId = key.slice(0, firstSep);
|
|
406
|
+
const entryCacheKey = key.slice(secondSep + 1);
|
|
407
|
+
if (scope.cacheKey !== undefined && entryCacheKey !== scope.cacheKey) {
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
if (scope.modelId !== undefined && entryModelId !== scope.modelId) {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
initializedCaches.delete(key);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
// ----- test-only escape hatches -----
|
|
417
|
+
/**
|
|
418
|
+
* Test-only access to the module-scoped state. Production code reaches
|
|
419
|
+
* for cache state exclusively through the session API; the unit suite
|
|
420
|
+
* for `kv-cache-session.test.ts` needs to seed and inspect raw state
|
|
421
|
+
* to assert the rollback / commit invariants. Not part of the public
|
|
422
|
+
* SDK surface.
|
|
423
|
+
*
|
|
424
|
+
* @internal
|
|
425
|
+
*/
|
|
426
|
+
export const __kvCacheSessionTestHooks = {
|
|
427
|
+
getSavedCount(cachePath) {
|
|
428
|
+
return cachedMessageCounts.get(cachePath);
|
|
429
|
+
},
|
|
430
|
+
setSavedCountForTest(cachePath, count) {
|
|
431
|
+
cachedMessageCounts.set(cachePath, count);
|
|
432
|
+
},
|
|
433
|
+
hasInitializedKey(modelId, configHash, cacheKey) {
|
|
434
|
+
return initializedCaches.has(initRegistryKey(modelId, configHash, cacheKey));
|
|
435
|
+
},
|
|
436
|
+
markInitializedForTest(modelId, configHash, cacheKey) {
|
|
437
|
+
initializedCaches.add(initRegistryKey(modelId, configHash, cacheKey));
|
|
438
|
+
},
|
|
439
|
+
resetForTest() {
|
|
440
|
+
cachedMessageCounts.clear();
|
|
441
|
+
initializedCaches.clear();
|
|
442
|
+
},
|
|
443
|
+
};
|
|
444
|
+
// Re-export `generateConfigHash` from the path utilities so callers of
|
|
445
|
+
// the session can compute the hash without separately importing
|
|
446
|
+
// `kv-cache-utils`. The function itself stays in `kv-cache-utils.ts`
|
|
447
|
+
// (pure, no state) — only the re-export lives here.
|
|
448
|
+
export { generateConfigHash };
|
|
@@ -1,73 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pure
|
|
3
|
-
* `completion-stream.ts
|
|
2
|
+
* Pure decision helper for kv-cache history slicing used by
|
|
3
|
+
* `completion-stream.ts` (via the slice computed from
|
|
4
|
+
* `TurnHandle.savedCount`).
|
|
4
5
|
*
|
|
5
6
|
* This module intentionally has **no** `bare-*` imports so it can be
|
|
6
|
-
* exercised directly from unit tests running under `bun` without
|
|
7
|
-
* in the Bare runtime (which is not available in that
|
|
8
|
-
*
|
|
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)`).
|
|
7
|
+
* exercised directly from unit tests running under `bun` without
|
|
8
|
+
* pulling in the Bare runtime (which is not available in that
|
|
9
|
+
* environment).
|
|
42
10
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
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.
|
|
11
|
+
* Cancel detection flows through the per-request `AbortSignal` from
|
|
12
|
+
* `RequestRegistry`; the `cachedMessageCounts` map lives in
|
|
13
|
+
* `kv-cache-session.ts`, which owns all three KV-cache bookkeeping
|
|
14
|
+
* layers (saved counts, init flags, on-disk files). Only the pure
|
|
15
|
+
* slice-decision helper remains here.
|
|
69
16
|
*/
|
|
70
|
-
export declare function shouldRecordSavedCount(wasCancelled: boolean, producedTokens: boolean): boolean;
|
|
71
17
|
export interface HistoryMessage {
|
|
72
18
|
role: string;
|
|
73
19
|
content: string;
|
|
@@ -80,8 +26,9 @@ export interface HistorySliceDecision {
|
|
|
80
26
|
messages: HistoryMessage[];
|
|
81
27
|
/**
|
|
82
28
|
* True when the decision path proves the current `savedCount` is stale
|
|
83
|
-
* and the caller should
|
|
84
|
-
*
|
|
29
|
+
* and the caller should drop the cached entry (via
|
|
30
|
+
* `KvCacheSession.dropStaleSavedCount(turn)`) to avoid propagating the
|
|
31
|
+
* bad count to the next turn.
|
|
85
32
|
*/
|
|
86
33
|
clearStaleCount: boolean;
|
|
87
34
|
}
|
|
@@ -89,13 +36,14 @@ export interface HistorySliceDecision {
|
|
|
89
36
|
* Pure slice decision for `prepareMessagesForCache`.
|
|
90
37
|
*
|
|
91
38
|
* Mirrors the shape of the logic in `completion-stream.ts` but without
|
|
92
|
-
* calling `transformMessages` (which depends on `bare-fs` for
|
|
93
|
-
* probing). Kept here so the decision can be unit-tested in
|
|
39
|
+
* calling `transformMessages` (which depends on `bare-fs` for
|
|
40
|
+
* attachment probing). Kept here so the decision can be unit-tested in
|
|
41
|
+
* isolation.
|
|
94
42
|
*
|
|
95
|
-
* The key regression guard: when a non-zero `savedCount` would slice
|
|
96
|
-
* history down to an empty array, it is treated as stale — the
|
|
97
|
-
* falls back to sending the system-stripped full history rather
|
|
98
|
-
* handing the model an empty payload.
|
|
43
|
+
* The key regression guard: when a non-zero `savedCount` would slice
|
|
44
|
+
* the history down to an empty array, it is treated as stale — the
|
|
45
|
+
* caller falls back to sending the system-stripped full history rather
|
|
46
|
+
* than handing the model an empty payload.
|
|
99
47
|
*/
|
|
100
48
|
export declare function decideCachedHistorySlice(savedCount: number, cacheExists: boolean, history: HistoryMessage[]): HistorySliceDecision;
|
|
101
49
|
//# sourceMappingURL=kv-cache-state.d.ts.map
|