@qvac/sdk 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -24
- package/NOTICE +632 -0
- package/README.md +2 -1
- package/dist/client/api/embed.js +1 -4
- package/dist/client/api/get-model-info.d.ts +10 -3
- package/dist/client/api/index.d.ts +2 -0
- package/dist/client/api/index.js +2 -0
- package/dist/client/api/invoke-plugin.d.ts +14 -0
- package/dist/client/api/invoke-plugin.js +38 -0
- package/dist/client/api/registry.d.ts +14 -0
- package/dist/client/api/registry.js +37 -0
- package/dist/client/api/transcribe.js +0 -4
- package/dist/client/api/unload-model.js +1 -1
- package/dist/client/config-loader/resolve-config.node.d.ts +3 -2
- package/dist/client/config-loader/resolve-config.node.js +31 -4
- package/dist/client/init-hooks.js +2 -1
- package/dist/client/rpc/bare-client.d.ts +2 -0
- package/dist/client/rpc/bare-client.js +48 -7
- package/dist/client/rpc/expo-device-info.d.ts +6 -0
- package/dist/client/rpc/expo-device-info.js +32 -0
- package/dist/client/rpc/expo-rpc-client.js +9 -32
- package/dist/client/rpc/node-rpc-client.d.ts +1 -1
- package/dist/client/rpc/node-rpc-client.js +201 -28
- package/dist/client/rpc/rpc-client.d.ts +1 -1
- package/dist/client/rpc/rpc-client.js +2 -2
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/index.js +1 -0
- package/dist/constants/registry.d.ts +2 -0
- package/dist/constants/registry.js +1 -0
- package/dist/examples/cache-management.js +2 -2
- package/dist/examples/config/plugins/plugins.config.d.ts +5 -0
- package/dist/examples/config/plugins/plugins.config.js +4 -0
- package/dist/examples/download-with-blind-relays.js +1 -1
- package/dist/examples/llamacpp-multimodal.js +3 -3
- package/dist/examples/llamacpp-native-tools.js +2 -2
- package/dist/examples/mcp-websearch.js +2 -2
- package/dist/examples/multi-model-demo.js +2 -2
- package/dist/examples/ocr-fasttext.js +4 -4
- package/dist/examples/parallel-download.js +2 -2
- package/dist/examples/plugins.d.ts +14 -0
- package/dist/examples/plugins.js +129 -0
- package/dist/examples/registry-query.d.ts +2 -0
- package/dist/examples/registry-query.js +87 -0
- package/dist/examples/translation/translation-bergamot-batch.js +2 -2
- package/dist/examples/translation/translation-bergamot.js +4 -4
- package/dist/examples/translation/translation-opus.js +2 -2
- package/dist/examples/translation/translation-stream.js +2 -2
- package/dist/examples/tts/chatterbox.d.ts +2 -0
- package/dist/examples/tts/chatterbox.js +59 -0
- package/dist/examples/tts/supertonic.d.ts +2 -0
- package/dist/examples/tts/supertonic.js +51 -0
- package/dist/examples/tts/utils.d.ts +17 -0
- package/dist/examples/tts/utils.js +93 -0
- package/dist/examples/whispercpp-filesystem.js +2 -3
- package/dist/examples/whispercpp-microphone-record.js +1 -2
- package/dist/examples/whispercpp-prompt.js +2 -3
- package/dist/expo/plugins/index.d.ts +3 -0
- package/dist/expo/plugins/index.js +3 -0
- package/dist/expo/plugins/resolve-sdk-package-dir.d.ts +15 -0
- package/dist/expo/plugins/resolve-sdk-package-dir.js +31 -0
- package/dist/expo/plugins/withAndroidArchitecture.d.ts +8 -0
- package/dist/expo/plugins/withAndroidArchitecture.js +52 -0
- package/dist/expo/plugins/withAndroidNdkVersion.d.ts +8 -0
- package/dist/expo/plugins/withAndroidNdkVersion.js +31 -0
- package/dist/expo/plugins/withDeviceInfo.d.ts +10 -0
- package/dist/expo/plugins/withDeviceInfo.js +48 -0
- package/dist/expo/plugins/withMobileBundle.d.ts +5 -2
- package/dist/expo/plugins/withMobileBundle.js +121 -65
- package/dist/expo/plugins/withQvacSDK.d.ts +4 -0
- package/dist/expo/plugins/withQvacSDK.js +26 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +5 -5
- package/dist/models/{hyperdrive → registry}/index.js +1 -1
- package/dist/models/registry/models.d.ts +9056 -0
- package/dist/models/registry/models.js +9351 -0
- package/dist/models/update-models/codegen.d.ts +3 -0
- package/dist/models/update-models/codegen.js +135 -0
- package/dist/models/update-models/history.d.ts +13 -0
- package/dist/models/update-models/history.js +88 -0
- package/dist/models/update-models/index.d.ts +2 -0
- package/dist/models/update-models/index.js +124 -0
- package/dist/models/update-models/naming.d.ts +4 -0
- package/dist/models/update-models/naming.js +314 -0
- package/dist/models/update-models/processing.d.ts +8 -0
- package/dist/models/update-models/processing.js +67 -0
- package/dist/models/update-models/registry.d.ts +4 -0
- package/dist/models/update-models/registry.js +67 -0
- package/dist/models/update-models/shards.d.ts +4 -0
- package/dist/models/update-models/shards.js +57 -0
- package/dist/models/update-models/types.d.ts +60 -0
- package/dist/models/update-models/types.js +1 -0
- package/dist/models/update-models/utils.d.ts +3 -0
- package/dist/models/update-models/utils.js +21 -0
- package/dist/pear/pre.d.ts +18 -0
- package/dist/pear/pre.js +433 -0
- package/dist/schemas/common.d.ts +286 -32
- package/dist/schemas/common.js +12 -0
- package/dist/schemas/download-asset.d.ts +35 -11
- package/dist/schemas/download-asset.js +5 -1
- package/dist/schemas/engine-addon-map.d.ts +5 -0
- package/dist/schemas/engine-addon-map.js +49 -0
- package/dist/schemas/get-model-info.d.ts +32 -16
- package/dist/schemas/get-model-info.js +17 -8
- package/dist/schemas/index.d.ts +4 -1
- package/dist/schemas/index.js +4 -4
- package/dist/schemas/load-model.d.ts +2224 -900
- package/dist/schemas/load-model.js +130 -25
- package/dist/schemas/model-src-utils.d.ts +30 -12
- package/dist/schemas/model-src-utils.js +5 -2
- package/dist/schemas/model-types.d.ts +15 -4
- package/dist/schemas/model-types.js +21 -4
- package/dist/schemas/path-validation.d.ts +10 -0
- package/dist/schemas/path-validation.js +22 -0
- package/dist/schemas/plugin.d.ts +180 -0
- package/dist/schemas/plugin.js +132 -0
- package/dist/schemas/rag.d.ts +2 -2
- package/dist/schemas/rag.js +7 -3
- package/dist/schemas/registry.d.ts +338 -0
- package/dist/schemas/registry.js +91 -0
- package/dist/schemas/sdk-errors-client.d.ts +6 -6
- package/dist/schemas/sdk-errors-client.js +15 -15
- package/dist/schemas/sdk-errors-registry.d.ts +6 -0
- package/dist/schemas/sdk-errors-registry.js +8 -0
- package/dist/schemas/sdk-errors-server.d.ts +16 -2
- package/dist/schemas/sdk-errors-server.js +90 -10
- package/dist/schemas/text-to-speech.d.ts +670 -1
- package/dist/schemas/text-to-speech.js +51 -2
- package/dist/schemas/translate.d.ts +2 -2
- package/dist/scripts/run-security-tests-bare.d.ts +2 -0
- package/dist/scripts/run-security-tests-bare.js +78 -0
- package/dist/scripts/run-security-tests.d.ts +2 -0
- package/dist/scripts/run-security-tests.js +22 -0
- package/dist/server/bare/delegate-rpc-client.d.ts +6 -0
- package/dist/server/bare/delegate-rpc-client.js +136 -56
- package/dist/server/bare/{addons/llamacpp-embedding/handlers → ops}/embed.d.ts +1 -1
- package/dist/server/bare/{addons/llamacpp-embedding/handlers → ops}/embed.js +3 -3
- package/dist/server/bare/{addons/llamacpp-completion → ops}/kv-cache-utils.js +6 -6
- package/dist/server/bare/{addons → ops}/load-model.js +32 -42
- package/dist/server/bare/{addons/nmtcpp-translation/handlers → ops}/translate.d.ts +1 -1
- package/dist/server/bare/{addons/nmtcpp-translation/handlers → ops}/translate.js +5 -4
- package/dist/server/bare/plugins/index.d.ts +7 -0
- package/dist/server/bare/plugins/index.js +6 -0
- package/dist/server/bare/{addons/llamacpp-completion → plugins/llamacpp-completion/ops}/cache-logger.js +1 -1
- package/dist/server/bare/{addons/llamacpp-completion/handlers → plugins/llamacpp-completion/ops}/completion-stream.d.ts +1 -1
- package/dist/server/bare/{addons/llamacpp-completion/handlers → plugins/llamacpp-completion/ops}/completion-stream.js +2 -3
- package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +115 -0
- package/dist/server/bare/plugins/llamacpp-completion/plugin.js +142 -0
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +24 -0
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +73 -0
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +45 -0
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +97 -0
- package/dist/server/bare/{addons/onnx-ocr/handlers → plugins/onnx-ocr/ops}/ocr-stream.js +2 -1
- package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +38 -0
- package/dist/server/bare/{addons/onnx-ocr/create-model.js → plugins/onnx-ocr/plugin.js} +39 -2
- package/dist/server/bare/{addons/onnx-tts/handlers → plugins/onnx-tts/ops}/text-to-speech.js +23 -1
- package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +26 -0
- package/dist/server/bare/plugins/onnx-tts/plugin.js +162 -0
- package/dist/server/bare/plugins/onnx-tts/wav-helper.d.ts +14 -0
- package/dist/server/bare/plugins/onnx-tts/wav-helper.js +107 -0
- package/dist/server/bare/plugins/whispercpp-transcription/ops/transcribe-stream.d.ts +3 -0
- package/dist/server/bare/{addons/whispercpp-transcription/handlers → plugins/whispercpp-transcription/ops}/transcribe-stream.js +1 -2
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +31 -0
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +74 -0
- package/dist/server/bare/rag-hyperdb/handlers/ingest.js +1 -1
- package/dist/server/bare/rag-hyperdb/handlers/search.js +1 -1
- package/dist/server/bare/rag-hyperdb/rag-workspace-manager.js +2 -1
- package/dist/server/bare/registry/config-registry.js +1 -1
- package/dist/server/bare/registry/registry-client.d.ts +5 -0
- package/dist/server/bare/registry/registry-client.js +37 -0
- package/dist/server/env.d.ts +22 -0
- package/dist/server/env.js +57 -0
- package/dist/server/plugins/index.d.ts +2 -0
- package/dist/server/plugins/index.js +1 -0
- package/dist/server/plugins/registry.d.ts +9 -0
- package/dist/server/plugins/registry.js +75 -0
- package/dist/server/rpc/delegate-transport.d.ts +11 -0
- package/dist/server/rpc/delegate-transport.js +60 -0
- package/dist/server/rpc/handle-request.d.ts +0 -9
- package/dist/server/rpc/handle-request.js +13 -321
- package/dist/server/rpc/handler-registry.d.ts +3 -0
- package/dist/server/rpc/handler-registry.js +84 -0
- package/dist/server/rpc/handler-utils.d.ts +22 -0
- package/dist/server/rpc/handler-utils.js +87 -0
- package/dist/server/rpc/handlers/cancelHandler.js +1 -1
- package/dist/server/rpc/handlers/completion-stream-delegated.js +1 -1
- package/dist/server/rpc/handlers/completion-stream.js +2 -43
- package/dist/server/rpc/handlers/delete-cache.js +1 -1
- package/dist/server/rpc/handlers/embed.js +2 -21
- package/dist/server/rpc/handlers/get-model-info.js +19 -15
- package/dist/server/rpc/handlers/index.d.ts +9 -0
- package/dist/server/rpc/handlers/index.js +9 -0
- package/dist/server/rpc/handlers/load-model/download-manager.d.ts +1 -0
- package/dist/server/rpc/handlers/load-model/download-manager.js +3 -0
- package/dist/server/rpc/handlers/load-model/handler.js +47 -24
- package/dist/server/rpc/handlers/load-model/http.js +3 -2
- package/dist/server/rpc/handlers/load-model/hyperdrive.js +14 -10
- package/dist/server/rpc/handlers/load-model/registry.d.ts +10 -0
- package/dist/server/rpc/handlers/load-model/registry.js +571 -0
- package/dist/server/rpc/handlers/load-model/resolve.js +18 -5
- package/dist/server/rpc/handlers/load-model-delegated.js +4 -2
- package/dist/server/rpc/handlers/ocr-stream.js +2 -37
- package/dist/server/rpc/handlers/ping.d.ts +3 -0
- package/dist/server/rpc/handlers/ping.js +3 -0
- package/dist/server/rpc/handlers/plugin-dispatch.d.ts +15 -0
- package/dist/server/rpc/handlers/plugin-dispatch.js +54 -0
- package/dist/server/rpc/handlers/plugin-invoke.d.ts +4 -0
- package/dist/server/rpc/handlers/plugin-invoke.js +87 -0
- package/dist/server/rpc/handlers/registry.d.ts +5 -0
- package/dist/server/rpc/handlers/registry.js +157 -0
- package/dist/server/rpc/handlers/text-to-speech.js +2 -5
- package/dist/server/rpc/handlers/transcribe-stream.js +2 -30
- package/dist/server/rpc/handlers/translate.js +2 -42
- package/dist/server/rpc/handlers/unload-model.js +1 -1
- package/dist/server/rpc/index.d.ts +1 -1
- package/dist/server/utils/archive.d.ts +1 -0
- package/dist/server/utils/archive.js +10 -2
- package/dist/server/utils/cache.d.ts +5 -0
- package/dist/server/utils/cache.js +26 -2
- package/dist/server/utils/gguf-tensor-extractor.js +3 -3
- package/dist/server/utils/index.d.ts +1 -1
- package/dist/server/utils/index.js +1 -1
- package/dist/server/utils/path-security.d.ts +12 -0
- package/dist/server/utils/path-security.js +25 -0
- package/dist/server/utils/shard-utils.js +3 -3
- package/dist/server/worker-core.d.ts +6 -0
- package/dist/server/worker-core.js +88 -0
- package/dist/server/worker.d.ts +4 -5
- package/dist/server/worker.js +18 -116
- package/dist/utils/errors-client.d.ts +9 -6
- package/dist/utils/errors-client.js +19 -11
- package/dist/utils/errors-server.d.ts +44 -5
- package/dist/utils/errors-server.js +82 -9
- package/dist/utils/path-sanitize.d.ts +17 -0
- package/dist/utils/path-sanitize.js +54 -0
- package/expo/plugins/patches/android-link.mjs +63 -0
- package/expo/plugins/patches/ios-link.mjs +63 -0
- package/package.json +72 -10
- package/dist/examples/text-to-speech.d.ts +0 -3
- package/dist/examples/text-to-speech.js +0 -137
- package/dist/models/hyperdrive/models.d.ts +0 -4003
- package/dist/models/hyperdrive/models.js +0 -4282
- package/dist/server/bare/addons/index.d.ts +0 -4
- package/dist/server/bare/addons/index.js +0 -3
- package/dist/server/bare/addons/llamacpp-completion/create-model.d.ts +0 -8
- package/dist/server/bare/addons/llamacpp-completion/create-model.js +0 -43
- package/dist/server/bare/addons/llamacpp-completion/index.d.ts +0 -4
- package/dist/server/bare/addons/llamacpp-completion/index.js +0 -3
- package/dist/server/bare/addons/llamacpp-embedding/create-model.d.ts +0 -8
- package/dist/server/bare/addons/llamacpp-embedding/create-model.js +0 -38
- package/dist/server/bare/addons/llamacpp-embedding/index.d.ts +0 -3
- package/dist/server/bare/addons/llamacpp-embedding/index.js +0 -2
- package/dist/server/bare/addons/nmtcpp-translation/create-model.d.ts +0 -8
- package/dist/server/bare/addons/nmtcpp-translation/create-model.js +0 -52
- package/dist/server/bare/addons/nmtcpp-translation/index.d.ts +0 -3
- package/dist/server/bare/addons/nmtcpp-translation/index.js +0 -2
- package/dist/server/bare/addons/onnx-ocr/create-model.d.ts +0 -10
- package/dist/server/bare/addons/onnx-ocr/index.d.ts +0 -3
- package/dist/server/bare/addons/onnx-ocr/index.js +0 -2
- package/dist/server/bare/addons/onnx-tts/create-model.d.ts +0 -10
- package/dist/server/bare/addons/onnx-tts/create-model.js +0 -25
- package/dist/server/bare/addons/onnx-tts/index.d.ts +0 -3
- package/dist/server/bare/addons/onnx-tts/index.js +0 -2
- package/dist/server/bare/addons/whispercpp-transcription/create-model.d.ts +0 -10
- package/dist/server/bare/addons/whispercpp-transcription/create-model.js +0 -37
- package/dist/server/bare/addons/whispercpp-transcription/handlers/transcribe-stream.d.ts +0 -3
- package/dist/server/bare/addons/whispercpp-transcription/index.d.ts +0 -3
- package/dist/server/bare/addons/whispercpp-transcription/index.js +0 -2
- /package/dist/models/{hyperdrive → registry}/index.d.ts +0 -0
- /package/dist/server/bare/{addons → ops}/cancel.d.ts +0 -0
- /package/dist/server/bare/{addons → ops}/cancel.js +0 -0
- /package/dist/server/bare/{addons/llamacpp-completion → ops}/kv-cache-utils.d.ts +0 -0
- /package/dist/server/bare/{addons → ops}/load-model.d.ts +0 -0
- /package/dist/server/bare/{addons → ops}/unload-model.d.ts +0 -0
- /package/dist/server/bare/{addons → ops}/unload-model.js +0 -0
- /package/dist/server/bare/{addons/llamacpp-completion → plugins/llamacpp-completion/ops}/cache-logger.d.ts +0 -0
- /package/dist/server/bare/{addons/onnx-ocr/handlers → plugins/onnx-ocr/ops}/ocr-stream.d.ts +0 -0
- /package/dist/server/bare/{addons/onnx-tts/handlers → plugins/onnx-tts/ops}/text-to-speech.d.ts +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatches a request to a plugin handler and returns a Promise.
|
|
3
|
+
* Use for non-streaming (reply) handlers.
|
|
4
|
+
*
|
|
5
|
+
* @throws PluginHandlerTypeMismatchError if the handler is streaming
|
|
6
|
+
*/
|
|
7
|
+
export declare function dispatchPluginReply<TRequest, TResponse>(modelId: string, handlerName: string, request: TRequest): Promise<TResponse>;
|
|
8
|
+
/**
|
|
9
|
+
* Dispatches a request to a plugin handler and returns an AsyncGenerator.
|
|
10
|
+
* Use for streaming handlers.
|
|
11
|
+
*
|
|
12
|
+
* @throws PluginHandlerTypeMismatchError if the handler is not streaming
|
|
13
|
+
*/
|
|
14
|
+
export declare function dispatchPluginStream<TRequest, TResponse>(modelId: string, handlerName: string, request: TRequest): AsyncGenerator<TResponse>;
|
|
15
|
+
//# sourceMappingURL=plugin-dispatch.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { getModelEntry } from "../../bare/registry/model-registry.js";
|
|
2
|
+
import { getPlugin } from "../../plugins/index.js";
|
|
3
|
+
import { ModelNotFoundError, ModelIsDelegatedError, PluginNotFoundError, PluginHandlerNotFoundError, PluginHandlerTypeMismatchError, } from "../../../utils/errors-server.js";
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the plugin handler and returns both the result and streaming flag.
|
|
6
|
+
*/
|
|
7
|
+
function resolvePluginHandler(modelId, handlerName, request) {
|
|
8
|
+
const entry = getModelEntry(modelId);
|
|
9
|
+
if (!entry) {
|
|
10
|
+
throw new ModelNotFoundError(modelId);
|
|
11
|
+
}
|
|
12
|
+
if (entry.isDelegated || !entry.local) {
|
|
13
|
+
throw new ModelIsDelegatedError(modelId);
|
|
14
|
+
}
|
|
15
|
+
const plugin = getPlugin(entry.local.modelType);
|
|
16
|
+
if (!plugin) {
|
|
17
|
+
throw new PluginNotFoundError(entry.local.modelType);
|
|
18
|
+
}
|
|
19
|
+
const handlerDef = plugin.handlers[handlerName];
|
|
20
|
+
if (!handlerDef) {
|
|
21
|
+
const availableHandlers = Object.keys(plugin.handlers);
|
|
22
|
+
throw new PluginHandlerNotFoundError(entry.local.modelType, handlerName, availableHandlers);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
result: handlerDef.handler(request),
|
|
26
|
+
streaming: handlerDef.streaming,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Dispatches a request to a plugin handler and returns a Promise.
|
|
31
|
+
* Use for non-streaming (reply) handlers.
|
|
32
|
+
*
|
|
33
|
+
* @throws PluginHandlerTypeMismatchError if the handler is streaming
|
|
34
|
+
*/
|
|
35
|
+
export async function dispatchPluginReply(modelId, handlerName, request) {
|
|
36
|
+
const { result, streaming } = resolvePluginHandler(modelId, handlerName, request);
|
|
37
|
+
if (streaming) {
|
|
38
|
+
throw new PluginHandlerTypeMismatchError(handlerName, "reply", "streaming");
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Dispatches a request to a plugin handler and returns an AsyncGenerator.
|
|
44
|
+
* Use for streaming handlers.
|
|
45
|
+
*
|
|
46
|
+
* @throws PluginHandlerTypeMismatchError if the handler is not streaming
|
|
47
|
+
*/
|
|
48
|
+
export async function* dispatchPluginStream(modelId, handlerName, request) {
|
|
49
|
+
const { result, streaming } = resolvePluginHandler(modelId, handlerName, request);
|
|
50
|
+
if (!streaming) {
|
|
51
|
+
throw new PluginHandlerTypeMismatchError(handlerName, "streaming", "reply");
|
|
52
|
+
}
|
|
53
|
+
yield* result;
|
|
54
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PluginInvokeRequest, PluginInvokeResponse, PluginInvokeStreamRequest, PluginInvokeStreamResponse } from "../../../schemas/plugin";
|
|
2
|
+
export declare function handlePluginInvoke(request: PluginInvokeRequest): Promise<PluginInvokeResponse>;
|
|
3
|
+
export declare function handlePluginInvokeStream(request: PluginInvokeStreamRequest): AsyncGenerator<PluginInvokeStreamResponse>;
|
|
4
|
+
//# sourceMappingURL=plugin-invoke.d.ts.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { getModelEntry } from "../../bare/registry/model-registry.js";
|
|
2
|
+
import { getPlugin, getPluginHandler } from "../../plugins/index.js";
|
|
3
|
+
import { PluginNotFoundError, PluginHandlerNotFoundError, PluginHandlerTypeMismatchError, PluginRequestValidationFailedError, PluginResponseValidationFailedError, ModelIsDelegatedError, ModelNotFoundError, } from "../../../utils/errors-server.js";
|
|
4
|
+
import { getServerLogger } from "../../../logging/index.js";
|
|
5
|
+
const logger = getServerLogger();
|
|
6
|
+
function resolvePluginHandler(modelId, handlerName) {
|
|
7
|
+
const modelEntry = getModelEntry(modelId);
|
|
8
|
+
if (!modelEntry) {
|
|
9
|
+
throw new ModelNotFoundError(modelId);
|
|
10
|
+
}
|
|
11
|
+
if (modelEntry.isDelegated || !modelEntry.local) {
|
|
12
|
+
throw new ModelIsDelegatedError(modelId);
|
|
13
|
+
}
|
|
14
|
+
const modelType = modelEntry.local.modelType;
|
|
15
|
+
const plugin = getPlugin(modelType);
|
|
16
|
+
if (!plugin) {
|
|
17
|
+
throw new PluginNotFoundError(modelType);
|
|
18
|
+
}
|
|
19
|
+
const handlerDef = getPluginHandler(modelType, handlerName);
|
|
20
|
+
if (!handlerDef) {
|
|
21
|
+
const availableHandlers = Object.keys(plugin.handlers);
|
|
22
|
+
throw new PluginHandlerNotFoundError(modelType, handlerName, availableHandlers);
|
|
23
|
+
}
|
|
24
|
+
return { modelType, plugin, handlerDef };
|
|
25
|
+
}
|
|
26
|
+
export async function handlePluginInvoke(request) {
|
|
27
|
+
const { modelId, handler: handlerName, params } = request;
|
|
28
|
+
logger.debug(`[pluginInvoke] modelId=${modelId} handler=${handlerName}`);
|
|
29
|
+
const { handlerDef } = resolvePluginHandler(modelId, handlerName);
|
|
30
|
+
if (handlerDef.streaming) {
|
|
31
|
+
throw new PluginHandlerTypeMismatchError(handlerName, "reply", "streaming");
|
|
32
|
+
}
|
|
33
|
+
const parseResult = handlerDef.requestSchema.safeParse(params);
|
|
34
|
+
if (!parseResult.success) {
|
|
35
|
+
const details = parseResult.error.issues
|
|
36
|
+
.map((i) => `${String(i.path.join("."))}: ${i.message}`)
|
|
37
|
+
.join(", ");
|
|
38
|
+
throw new PluginRequestValidationFailedError(handlerName, details);
|
|
39
|
+
}
|
|
40
|
+
const result = await handlerDef.handler(parseResult.data);
|
|
41
|
+
const responseParseResult = handlerDef.responseSchema.safeParse(result);
|
|
42
|
+
if (!responseParseResult.success) {
|
|
43
|
+
const details = responseParseResult.error.issues
|
|
44
|
+
.map((i) => `${String(i.path.join("."))}: ${i.message}`)
|
|
45
|
+
.join(", ");
|
|
46
|
+
throw new PluginResponseValidationFailedError(handlerName, details);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
type: "pluginInvoke",
|
|
50
|
+
result: responseParseResult.data,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export async function* handlePluginInvokeStream(request) {
|
|
54
|
+
const { modelId, handler: handlerName, params } = request;
|
|
55
|
+
logger.debug(`[pluginInvokeStream] modelId=${modelId} handler=${handlerName}`);
|
|
56
|
+
const { handlerDef } = resolvePluginHandler(modelId, handlerName);
|
|
57
|
+
if (!handlerDef.streaming) {
|
|
58
|
+
throw new PluginHandlerTypeMismatchError(handlerName, "streaming", "reply");
|
|
59
|
+
}
|
|
60
|
+
const parseResult = handlerDef.requestSchema.safeParse(params);
|
|
61
|
+
if (!parseResult.success) {
|
|
62
|
+
const details = parseResult.error.issues
|
|
63
|
+
.map((i) => `${String(i.path.join("."))}: ${i.message}`)
|
|
64
|
+
.join(", ");
|
|
65
|
+
throw new PluginRequestValidationFailedError(handlerName, details);
|
|
66
|
+
}
|
|
67
|
+
const generator = handlerDef.handler(parseResult.data);
|
|
68
|
+
for await (const chunk of generator) {
|
|
69
|
+
const responseParseResult = handlerDef.responseSchema.safeParse(chunk);
|
|
70
|
+
if (!responseParseResult.success) {
|
|
71
|
+
const details = responseParseResult.error.issues
|
|
72
|
+
.map((i) => `${String(i.path.join("."))}: ${i.message}`)
|
|
73
|
+
.join(", ");
|
|
74
|
+
throw new PluginResponseValidationFailedError(handlerName, details);
|
|
75
|
+
}
|
|
76
|
+
yield {
|
|
77
|
+
type: "pluginInvokeStream",
|
|
78
|
+
result: responseParseResult.data,
|
|
79
|
+
done: false,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
yield {
|
|
83
|
+
type: "pluginInvokeStream",
|
|
84
|
+
result: null,
|
|
85
|
+
done: true,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ModelRegistryListResponse, ModelRegistrySearchRequest, ModelRegistrySearchResponse, ModelRegistryGetModelRequest, ModelRegistryGetModelResponse } from "../../../schemas/index";
|
|
2
|
+
export declare function handleModelRegistryList(): Promise<ModelRegistryListResponse>;
|
|
3
|
+
export declare function handleModelRegistrySearch(request: ModelRegistrySearchRequest): Promise<ModelRegistrySearchResponse>;
|
|
4
|
+
export declare function handleModelRegistryGetModel(request: ModelRegistryGetModelRequest): Promise<ModelRegistryGetModelResponse>;
|
|
5
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { REGISTRY_ERROR_CODES } from "../../../schemas/sdk-errors-registry.js";
|
|
2
|
+
import { getAddonFromEngine, resolveCanonicalEngine, } from "../../../schemas/engine-addon-map.js";
|
|
3
|
+
import { getRegistryClient } from "../../bare/registry/registry-client.js";
|
|
4
|
+
import { getServerLogger } from "../../../logging/index.js";
|
|
5
|
+
import { ModelRegistryQueryFailedError } from "../../../utils/errors-server.js";
|
|
6
|
+
const logger = getServerLogger();
|
|
7
|
+
function toHexString(value) {
|
|
8
|
+
if (!value)
|
|
9
|
+
return "";
|
|
10
|
+
if (Buffer.isBuffer(value))
|
|
11
|
+
return value.toString("hex");
|
|
12
|
+
if (typeof value === "string")
|
|
13
|
+
return value;
|
|
14
|
+
if (typeof value === "object" && "data" in value) {
|
|
15
|
+
return Buffer.from(value.data).toString("hex");
|
|
16
|
+
}
|
|
17
|
+
return "";
|
|
18
|
+
}
|
|
19
|
+
function processRegistryModel(model) {
|
|
20
|
+
const engine = resolveCanonicalEngine(model.engine || "");
|
|
21
|
+
if (!engine) {
|
|
22
|
+
logger.warn(`Skipping model with unknown engine "${model.engine}": ${model.path}`);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const filename = model.path.split("/").pop() || model.path;
|
|
26
|
+
const blobBinding = model.blobBinding;
|
|
27
|
+
const blobCoreKey = toHexString(blobBinding?.coreKey);
|
|
28
|
+
const blobBlockOffset = blobBinding?.blockOffset ?? 0;
|
|
29
|
+
const blobBlockLength = blobBinding?.blockLength ?? 0;
|
|
30
|
+
const blobByteOffset = blobBinding?.byteOffset ?? 0;
|
|
31
|
+
const expectedSize = blobBinding?.byteLength ?? 0;
|
|
32
|
+
// sha256 lives on blobBinding at runtime (per hyperschema), fall back to top-level
|
|
33
|
+
const sha256Checksum = blobBinding?.["sha256"] ||
|
|
34
|
+
model.sha256 ||
|
|
35
|
+
"";
|
|
36
|
+
const addon = getAddonFromEngine(engine);
|
|
37
|
+
// Use the filename without extension as the display name
|
|
38
|
+
const name = filename.replace(/\.\w+$/, "");
|
|
39
|
+
return {
|
|
40
|
+
name,
|
|
41
|
+
registryPath: model.path,
|
|
42
|
+
registrySource: model.source,
|
|
43
|
+
blobCoreKey,
|
|
44
|
+
blobBlockOffset,
|
|
45
|
+
blobBlockLength,
|
|
46
|
+
blobByteOffset,
|
|
47
|
+
modelId: filename,
|
|
48
|
+
addon,
|
|
49
|
+
expectedSize,
|
|
50
|
+
sha256Checksum,
|
|
51
|
+
engine,
|
|
52
|
+
quantization: model.quantization || "",
|
|
53
|
+
params: model.params || "",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export async function handleModelRegistryList() {
|
|
57
|
+
logger.debug("Handling QVAC model registry list request");
|
|
58
|
+
try {
|
|
59
|
+
const client = await getRegistryClient();
|
|
60
|
+
const registryModels = await client.findBy();
|
|
61
|
+
const models = registryModels
|
|
62
|
+
.map(processRegistryModel)
|
|
63
|
+
.filter((m) => m !== null);
|
|
64
|
+
logger.debug(`QVAC model registry list returned ${models.length} models`);
|
|
65
|
+
return {
|
|
66
|
+
type: "modelRegistryList",
|
|
67
|
+
success: true,
|
|
68
|
+
models,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
logger.error("QVAC model registry list failed:", error);
|
|
73
|
+
// Re-throw registry client errors directly (19001-19003)
|
|
74
|
+
const qvacError = error;
|
|
75
|
+
if (qvacError.code === REGISTRY_ERROR_CODES.FAILED_TO_CONNECT ||
|
|
76
|
+
qvacError.code === REGISTRY_ERROR_CODES.FAILED_TO_CLOSE ||
|
|
77
|
+
qvacError.code === REGISTRY_ERROR_CODES.MODEL_NOT_FOUND) {
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
// Wrap unknown errors in SDK error
|
|
81
|
+
throw new ModelRegistryQueryFailedError(error instanceof Error ? error.message : "Unknown error", error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export async function handleModelRegistrySearch(request) {
|
|
85
|
+
logger.debug("Handling QVAC model registry search request", request);
|
|
86
|
+
try {
|
|
87
|
+
const client = await getRegistryClient();
|
|
88
|
+
const registryModels = await client.findBy({
|
|
89
|
+
...(request.quantization && { quantization: request.quantization }),
|
|
90
|
+
});
|
|
91
|
+
let models = registryModels
|
|
92
|
+
.map(processRegistryModel)
|
|
93
|
+
.filter((m) => m !== null);
|
|
94
|
+
if (request.engine) {
|
|
95
|
+
const canonicalEngine = resolveCanonicalEngine(request.engine);
|
|
96
|
+
models = models.filter((m) => m.engine === (canonicalEngine ?? request.engine));
|
|
97
|
+
}
|
|
98
|
+
if (request.filter) {
|
|
99
|
+
const filterLower = request.filter.toLowerCase();
|
|
100
|
+
models = models.filter((m) => m.name.toLowerCase().includes(filterLower) ||
|
|
101
|
+
m.registryPath.toLowerCase().includes(filterLower) ||
|
|
102
|
+
m.addon.toLowerCase().includes(filterLower) ||
|
|
103
|
+
m.engine.toLowerCase().includes(filterLower));
|
|
104
|
+
}
|
|
105
|
+
if (request.addon) {
|
|
106
|
+
models = models.filter((m) => m.addon === request.addon);
|
|
107
|
+
}
|
|
108
|
+
logger.debug(`QVAC model registry search returned ${models.length} models`);
|
|
109
|
+
return {
|
|
110
|
+
type: "modelRegistrySearch",
|
|
111
|
+
success: true,
|
|
112
|
+
models,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
logger.error("QVAC model registry search failed:", error);
|
|
117
|
+
// Re-throw registry client errors directly (19001-19003)
|
|
118
|
+
const qvacError = error;
|
|
119
|
+
if (qvacError.code === REGISTRY_ERROR_CODES.FAILED_TO_CONNECT ||
|
|
120
|
+
qvacError.code === REGISTRY_ERROR_CODES.FAILED_TO_CLOSE ||
|
|
121
|
+
qvacError.code === REGISTRY_ERROR_CODES.MODEL_NOT_FOUND) {
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
throw new ModelRegistryQueryFailedError(error instanceof Error ? error.message : "Unknown error", error);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export async function handleModelRegistryGetModel(request) {
|
|
128
|
+
logger.debug("Handling QVAC model registry get model request", request);
|
|
129
|
+
try {
|
|
130
|
+
const client = await getRegistryClient();
|
|
131
|
+
const rawModel = await client.getModel(request.registryPath, request.registrySource);
|
|
132
|
+
if (!rawModel) {
|
|
133
|
+
throw new ModelRegistryQueryFailedError(`Model not found: ${request.registrySource}/${request.registryPath}`);
|
|
134
|
+
}
|
|
135
|
+
const model = processRegistryModel(rawModel);
|
|
136
|
+
if (!model) {
|
|
137
|
+
throw new ModelRegistryQueryFailedError(`Model has unknown engine "${rawModel.engine}": ${request.registryPath}`);
|
|
138
|
+
}
|
|
139
|
+
logger.debug("QVAC model registry get model found:", model.name);
|
|
140
|
+
return {
|
|
141
|
+
type: "modelRegistryGetModel",
|
|
142
|
+
success: true,
|
|
143
|
+
model,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
logger.error("QVAC model registry get model failed:", error);
|
|
148
|
+
// Re-throw registry client errors directly (19001-19003)
|
|
149
|
+
const qvacError = error;
|
|
150
|
+
if (qvacError.code === REGISTRY_ERROR_CODES.FAILED_TO_CONNECT ||
|
|
151
|
+
qvacError.code === REGISTRY_ERROR_CODES.FAILED_TO_CLOSE ||
|
|
152
|
+
qvacError.code === REGISTRY_ERROR_CODES.MODEL_NOT_FOUND) {
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
throw new ModelRegistryQueryFailedError(error instanceof Error ? error.message : "Unknown error", error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dispatchPluginStream } from "./plugin-dispatch.js";
|
|
2
2
|
export async function* handleTextToSpeech(request) {
|
|
3
|
-
|
|
4
|
-
for await (const response of stream) {
|
|
5
|
-
yield response;
|
|
6
|
-
}
|
|
3
|
+
yield* dispatchPluginStream(request.modelId, "textToSpeech", request);
|
|
7
4
|
}
|
|
@@ -1,32 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getServerLogger } from "../../../logging/index.js";
|
|
3
|
-
const logger = getServerLogger();
|
|
1
|
+
import { dispatchPluginStream } from "./plugin-dispatch.js";
|
|
4
2
|
export async function* handleTranscribeStream(request) {
|
|
5
|
-
|
|
6
|
-
try {
|
|
7
|
-
// Stream transcription results in real-time
|
|
8
|
-
for await (const textChunk of transcribeStream({
|
|
9
|
-
audioChunk,
|
|
10
|
-
modelId,
|
|
11
|
-
prompt,
|
|
12
|
-
})) {
|
|
13
|
-
yield {
|
|
14
|
-
type: "transcribeStream",
|
|
15
|
-
text: textChunk,
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
// Signal completion
|
|
19
|
-
yield {
|
|
20
|
-
type: "transcribeStream",
|
|
21
|
-
done: true,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
logger.error("Error during transcription:", error);
|
|
26
|
-
yield {
|
|
27
|
-
type: "transcribeStream",
|
|
28
|
-
text: "",
|
|
29
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
30
|
-
};
|
|
31
|
-
}
|
|
3
|
+
yield* dispatchPluginStream(request.modelId, "transcribeStream", request);
|
|
32
4
|
}
|
|
@@ -1,44 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getServerLogger } from "../../../logging/index.js";
|
|
3
|
-
const logger = getServerLogger();
|
|
1
|
+
import { dispatchPluginStream } from "./plugin-dispatch.js";
|
|
4
2
|
export async function* handleTranslate(request) {
|
|
5
|
-
|
|
6
|
-
// Remove the 'type' field from request and pass the rest to translate
|
|
7
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
8
|
-
const { type, ...translateParams } = request;
|
|
9
|
-
const generator = translate(translateParams);
|
|
10
|
-
let stats;
|
|
11
|
-
let done = false;
|
|
12
|
-
let buffer = "";
|
|
13
|
-
while (!done) {
|
|
14
|
-
const result = await generator.next();
|
|
15
|
-
if (result.done) {
|
|
16
|
-
stats = result.value;
|
|
17
|
-
done = true;
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
buffer += result.value;
|
|
21
|
-
if (translateParams.stream) {
|
|
22
|
-
yield {
|
|
23
|
-
type: "translate",
|
|
24
|
-
token: result.value,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
yield {
|
|
30
|
-
type: "translate",
|
|
31
|
-
token: buffer,
|
|
32
|
-
done: true,
|
|
33
|
-
stats,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
logger.error("Error during translation:", error);
|
|
38
|
-
yield {
|
|
39
|
-
type: "translate",
|
|
40
|
-
token: "",
|
|
41
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
42
|
-
};
|
|
43
|
-
}
|
|
3
|
+
yield* dispatchPluginStream(request.modelId, "translate", request);
|
|
44
4
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { unloadModel } from "../../bare/
|
|
1
|
+
import { unloadModel } from "../../bare/ops/unload-model.js";
|
|
2
2
|
import { getRegistryStats } from "../../bare/registry/model-registry.js";
|
|
3
3
|
import { hasActiveProviders } from "../../bare/hyperswarm.js";
|
|
4
4
|
import { getServerLogger } from "../../../logging/index.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export declare function isArchiveUrl(url: string): boolean;
|
|
2
|
+
export declare function extractTarStream(archivePath: string, extractDir: string, useGunzip: boolean, signal?: AbortSignal): Promise<void>;
|
|
2
3
|
export declare function extractAndValidateShardedArchive(archivePath: string, extractDir: string, signal?: AbortSignal): Promise<string>;
|
|
3
4
|
//# sourceMappingURL=archive.d.ts.map
|
|
@@ -5,6 +5,7 @@ import { createGunzip } from "bare-zlib";
|
|
|
5
5
|
import tarStream from "tar-stream";
|
|
6
6
|
import { detectShardedModel, generateShardFilenames, extractTensorsFromShards, validateShardedModelCache, } from "./shard-utils.js";
|
|
7
7
|
import { ModelLoadFailedError, ArchiveExtractionFailedError, ArchiveUnsupportedTypeError, ArchiveMissingShardsError, DownloadCancelledError, } from "../../utils/errors-server.js";
|
|
8
|
+
import { isPathWithinBase } from "./path-security.js";
|
|
8
9
|
import { getServerLogger } from "../../logging/index.js";
|
|
9
10
|
import { SUPPORTED_ARCHIVE_EXTENSIONS, filenameToArchiveTypeSchema, } from "../../schemas/archive.js";
|
|
10
11
|
const logger = getServerLogger();
|
|
@@ -63,7 +64,7 @@ async function extractArchive(archivePath, extractDir, signal) {
|
|
|
63
64
|
await fsPromises.mkdir(extractDir, { recursive: true });
|
|
64
65
|
await extractTarStream(archivePath, extractDir, isGzipped, signal);
|
|
65
66
|
}
|
|
66
|
-
async function extractTarStream(archivePath, extractDir, useGunzip, signal) {
|
|
67
|
+
export async function extractTarStream(archivePath, extractDir, useGunzip, signal) {
|
|
67
68
|
const extract = tarStream.extract();
|
|
68
69
|
const gunzip = useGunzip ? createGunzip() : null;
|
|
69
70
|
return new Promise((resolve, reject) => {
|
|
@@ -91,7 +92,14 @@ async function extractTarStream(archivePath, extractDir, useGunzip, signal) {
|
|
|
91
92
|
next();
|
|
92
93
|
return;
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
+
// Prevent zip-slip: reject entries that escape extractDir (CWE-22)
|
|
96
|
+
const filePath = path.resolve(path.join(extractDir, header.name));
|
|
97
|
+
if (!isPathWithinBase(extractDir, filePath)) {
|
|
98
|
+
logger.warn(`⚠️ Skipping archive entry with path traversal: ${header.name}`);
|
|
99
|
+
stream.resume();
|
|
100
|
+
next();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
95
103
|
if (header.type === "file") {
|
|
96
104
|
const dir = path.dirname(filePath);
|
|
97
105
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -7,6 +7,11 @@ export declare function getKVCacheDir(): string;
|
|
|
7
7
|
* Returns: cache/sharded/<hyperdriveKey>/
|
|
8
8
|
*/
|
|
9
9
|
export declare function getShardedModelCacheDir(hyperdriveKey: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Get full path to ONNX file in cache
|
|
12
|
+
* Returns: cache/onnx/<cacheKey>/<filename>
|
|
13
|
+
*/
|
|
14
|
+
export declare function getOnnxModelPath(cacheKey: string, filename: string): string;
|
|
10
15
|
/**
|
|
11
16
|
* Get full path to specific shard file
|
|
12
17
|
* Returns: cache/sharded/<hyperdriveKey>/<shardFilename>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import fs, { promises as fsPromises } from "bare-fs";
|
|
2
2
|
import path from "bare-path";
|
|
3
|
-
import { getEnv } from "../
|
|
3
|
+
import { getEnv } from "../env.js";
|
|
4
4
|
import { getConfiguredCacheDir } from "../bare/registry/config-registry.js";
|
|
5
5
|
import { calculateFileChecksum } from "./checksum.js";
|
|
6
|
+
import { validateAndJoinPath } from "./path-security.js";
|
|
6
7
|
import { getServerLogger } from "../../logging/index.js";
|
|
7
8
|
const logger = getServerLogger();
|
|
8
9
|
export function getCacheDir(subDir) {
|
|
@@ -44,13 +45,36 @@ export function getShardedModelCacheDir(hyperdriveKey) {
|
|
|
44
45
|
}
|
|
45
46
|
return shardDir;
|
|
46
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Get cache directory for ONNX model with external data
|
|
50
|
+
* Returns: cache/onnx/<cacheKey>/
|
|
51
|
+
*/
|
|
52
|
+
function getOnnxModelCacheDir(cacheKey) {
|
|
53
|
+
const baseCache = getModelsCacheDir();
|
|
54
|
+
const onnxDir = validateAndJoinPath(baseCache, "onnx", cacheKey);
|
|
55
|
+
try {
|
|
56
|
+
fs.mkdirSync(onnxDir, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
logger.error(`Error creating ONNX model cache directory:`, error instanceof Error ? error.message : String(error));
|
|
60
|
+
}
|
|
61
|
+
return onnxDir;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get full path to ONNX file in cache
|
|
65
|
+
* Returns: cache/onnx/<cacheKey>/<filename>
|
|
66
|
+
*/
|
|
67
|
+
export function getOnnxModelPath(cacheKey, filename) {
|
|
68
|
+
const onnxDir = getOnnxModelCacheDir(cacheKey);
|
|
69
|
+
return validateAndJoinPath(onnxDir, filename);
|
|
70
|
+
}
|
|
47
71
|
/**
|
|
48
72
|
* Get full path to specific shard file
|
|
49
73
|
* Returns: cache/sharded/<hyperdriveKey>/<shardFilename>
|
|
50
74
|
*/
|
|
51
75
|
export function getShardPath(hyperdriveKey, shardFilename) {
|
|
52
76
|
const shardDir = getShardedModelCacheDir(hyperdriveKey);
|
|
53
|
-
return
|
|
77
|
+
return validateAndJoinPath(shardDir, shardFilename);
|
|
54
78
|
}
|
|
55
79
|
/**
|
|
56
80
|
* Check if all shards exist and are valid (size + checksum check)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { promises as fsPromises, createReadStream } from "bare-fs";
|
|
2
|
-
import path from "bare-path";
|
|
3
2
|
import { getServerLogger } from "../../logging/index.js";
|
|
4
3
|
import { ModelLoadFailedError } from "../../utils/errors-server.js";
|
|
4
|
+
import { validateAndJoinPath } from "./path-security.js";
|
|
5
5
|
const logger = getServerLogger();
|
|
6
6
|
// GGUF value types
|
|
7
7
|
const GGUF_TYPE = {
|
|
@@ -253,7 +253,7 @@ async function extractTensorNamesFromHeader(filePath) {
|
|
|
253
253
|
* @throws ModelLoadFailedError if any shard fails extraction
|
|
254
254
|
*/
|
|
255
255
|
export async function extractAndWriteTensorsFile(shardDir, shardFilenames, baseFilename) {
|
|
256
|
-
const tensorsFilePath =
|
|
256
|
+
const tensorsFilePath = validateAndJoinPath(shardDir, `${baseFilename}.tensors.txt`);
|
|
257
257
|
try {
|
|
258
258
|
await fsPromises.access(tensorsFilePath);
|
|
259
259
|
logger.info(`Tensors file already exists: ${tensorsFilePath}`);
|
|
@@ -266,7 +266,7 @@ export async function extractAndWriteTensorsFile(shardDir, shardFilenames, baseF
|
|
|
266
266
|
const allTensorNames = new Set();
|
|
267
267
|
const failedShards = [];
|
|
268
268
|
for (const filename of shardFilenames) {
|
|
269
|
-
const shardPath =
|
|
269
|
+
const shardPath = validateAndJoinPath(shardDir, filename);
|
|
270
270
|
try {
|
|
271
271
|
logger.debug(`Extracting tensors from ${filename}...`);
|
|
272
272
|
const tensorNames = await extractTensorNamesFromHeader(shardPath);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export * from "./archive";
|
|
2
|
-
export * from "./audio/decoder";
|
|
3
2
|
export * from "./cache";
|
|
4
3
|
export * from "./checksum";
|
|
5
4
|
export * from "./config-transform";
|
|
6
5
|
export * from "./formatting";
|
|
7
6
|
export * from "./gguf-tensor-extractor";
|
|
8
7
|
export * from "./model-path";
|
|
8
|
+
export * from "./path-security";
|
|
9
9
|
export * from "./shard-utils";
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export * from "./archive.js";
|
|
2
|
-
export * from "./audio/decoder.js";
|
|
3
2
|
export * from "./cache.js";
|
|
4
3
|
export * from "./checksum.js";
|
|
5
4
|
export * from "./config-transform.js";
|
|
6
5
|
export * from "./formatting.js";
|
|
7
6
|
export * from "./gguf-tensor-extractor.js";
|
|
8
7
|
export * from "./model-path.js";
|
|
8
|
+
export * from "./path-security.js";
|
|
9
9
|
export * from "./shard-utils.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { sanitizePathComponent } from "../../utils/path-sanitize";
|
|
2
|
+
/**
|
|
3
|
+
* Check whether a target path is contained within a base directory.
|
|
4
|
+
* Both paths are resolved to absolute before comparison.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isPathWithinBase(basePath: string, targetPath: string): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Sanitize components, join them to a base path, and verify the result
|
|
9
|
+
* stays within the base directory. Throws PathTraversalError on escape.
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateAndJoinPath(basePath: string, ...components: string[]): string;
|
|
12
|
+
//# sourceMappingURL=path-security.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import path from "bare-path";
|
|
2
|
+
import { PathTraversalError } from "../../utils/errors-server.js";
|
|
3
|
+
import { sanitizePathComponent, checkPathWithinBase, } from "../../utils/path-sanitize.js";
|
|
4
|
+
// Re-export sanitizePathComponent unchanged
|
|
5
|
+
export { sanitizePathComponent } from "../../utils/path-sanitize.js";
|
|
6
|
+
/**
|
|
7
|
+
* Check whether a target path is contained within a base directory.
|
|
8
|
+
* Both paths are resolved to absolute before comparison.
|
|
9
|
+
*/
|
|
10
|
+
export function isPathWithinBase(basePath, targetPath) {
|
|
11
|
+
return checkPathWithinBase(basePath, targetPath, path.resolve, path.sep || "/");
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Sanitize components, join them to a base path, and verify the result
|
|
15
|
+
* stays within the base directory. Throws PathTraversalError on escape.
|
|
16
|
+
*/
|
|
17
|
+
export function validateAndJoinPath(basePath, ...components) {
|
|
18
|
+
const sanitized = components.map((c) => sanitizePathComponent(c));
|
|
19
|
+
const joined = path.join(basePath, ...sanitized);
|
|
20
|
+
const resolved = path.resolve(joined);
|
|
21
|
+
if (!isPathWithinBase(basePath, resolved)) {
|
|
22
|
+
throw new PathTraversalError(components.join("/"), basePath);
|
|
23
|
+
}
|
|
24
|
+
return resolved;
|
|
25
|
+
}
|