@qvac/sdk 0.5.0 → 0.6.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/README.md +48 -11
- package/bare-imports.json +35 -0
- package/dist/client/api/cancel.d.ts +12 -3
- package/dist/client/api/cancel.js +12 -3
- package/dist/client/api/get-model-info.d.ts +2 -2
- package/dist/client/api/get-model-info.js +2 -2
- package/dist/client/api/index.d.ts +2 -1
- package/dist/client/api/index.js +2 -1
- package/dist/client/api/load-model.js +6 -1
- package/dist/client/api/ocr.d.ts +31 -0
- package/dist/client/api/ocr.js +88 -0
- package/dist/client/api/rag.d.ts +215 -20
- package/dist/client/api/rag.js +382 -23
- package/dist/client/api/translate.js +10 -11
- package/dist/client/api/unload-model.js +1 -1
- package/dist/client/init-hooks.d.ts +4 -3
- package/dist/client/init-hooks.js +40 -31
- package/dist/client/rpc/bare-client.d.ts +1 -178
- package/dist/client/rpc/bare-client.js +42 -9
- package/dist/client/rpc/expo-rpc-client.js +57 -2
- package/dist/client/rpc/node-rpc-client.js +5 -1
- package/dist/client/rpc/rpc-client.d.ts +1 -1
- package/dist/client/rpc/rpc-client.js +11 -57
- package/dist/examples/config/blind-relay/blind-relay.config.d.ts +5 -0
- package/dist/examples/config/blind-relay/blind-relay.config.js +7 -0
- package/dist/examples/config/default/default.config.d.ts +8 -0
- package/dist/examples/config/default/default.config.js +6 -0
- package/dist/examples/default-config-usage.d.ts +17 -0
- package/dist/examples/default-config-usage.js +47 -0
- package/dist/examples/delegated-inference/consumer.js +1 -0
- package/dist/examples/download-with-blind-relays.js +4 -3
- package/dist/examples/embed-hyperdrive.js +4 -0
- package/dist/examples/kv-cache-custom-key.js +16 -1
- package/dist/examples/llamacpp-sharded.js +12 -3
- package/dist/examples/ocr-fasttext.d.ts +2 -0
- package/dist/examples/ocr-fasttext.js +52 -0
- package/dist/examples/quickstart.d.ts +2 -0
- package/dist/examples/quickstart.js +28 -0
- package/dist/examples/rag/rag-hyperdb/cancellation.d.ts +2 -0
- package/dist/examples/rag/rag-hyperdb/cancellation.js +75 -0
- package/dist/examples/rag/rag-hyperdb/ingest.d.ts +2 -0
- package/dist/examples/rag/{rag-hyperdb.js → rag-hyperdb/ingest.js} +10 -4
- package/dist/examples/rag/rag-hyperdb/pipeline.d.ts +2 -0
- package/dist/examples/rag/rag-hyperdb/pipeline.js +74 -0
- package/dist/examples/rag/rag-hyperdb/workspaces.d.ts +2 -0
- package/dist/examples/rag/rag-hyperdb/workspaces.js +178 -0
- package/dist/examples/translation/translation-bergamot-batch.d.ts +2 -0
- package/dist/examples/translation/translation-bergamot-batch.js +49 -0
- package/dist/examples/translation/translation-bergamot.d.ts +2 -0
- package/dist/examples/translation/translation-bergamot.js +39 -0
- package/dist/examples/translation/translation-indic.js +1 -0
- package/dist/examples/translation/translation-opus.js +1 -0
- package/dist/examples/translation/translation-stream.js +1 -0
- package/dist/expo/plugins/withMobileBundle.js +29 -10
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -1
- package/dist/logging/addon.js +23 -7
- package/dist/logging/base-logger.js +2 -2
- package/dist/logging/client-logger.js +15 -4
- package/dist/logging/index.d.ts +1 -0
- package/dist/logging/index.js +1 -0
- package/dist/logging/logger.js +11 -3
- package/dist/logging/namespaces.d.ts +1 -0
- package/dist/logging/namespaces.js +1 -0
- package/dist/logging/registry.d.ts +5 -0
- package/dist/logging/registry.js +17 -5
- package/dist/logging/transport.js +2 -2
- package/dist/logging/utils.d.ts +2 -0
- package/dist/logging/utils.js +62 -0
- package/dist/models/hyperdrive/models.d.ts +1096 -178
- package/dist/models/hyperdrive/models.js +1476 -507
- package/dist/schemas/archive.d.ts +9 -0
- package/dist/schemas/archive.js +17 -0
- package/dist/schemas/cancel.d.ts +6 -0
- package/dist/schemas/cancel.js +5 -0
- package/dist/schemas/common.d.ts +582 -77
- package/dist/schemas/common.js +6 -1
- package/dist/schemas/delegate.d.ts +1 -0
- package/dist/schemas/delegate.js +1 -0
- package/dist/schemas/download-asset.d.ts +21 -9
- package/dist/schemas/get-model-info.d.ts +12 -10
- package/dist/schemas/get-model-info.js +9 -1
- package/dist/schemas/index.d.ts +6 -2
- package/dist/schemas/index.js +9 -2
- package/dist/schemas/llamacpp-config.d.ts +112 -8
- package/dist/schemas/llamacpp-config.js +40 -8
- package/dist/schemas/load-model.d.ts +2993 -525
- package/dist/schemas/load-model.js +88 -33
- package/dist/schemas/logging-stream.d.ts +2 -2
- package/dist/schemas/model-src-utils.d.ts +38 -16
- package/dist/schemas/model-src-utils.js +4 -8
- package/dist/schemas/model-types.d.ts +154 -0
- package/dist/schemas/model-types.js +140 -0
- package/dist/schemas/ocr.d.ts +88 -0
- package/dist/schemas/ocr.js +52 -0
- package/dist/schemas/rag.d.ts +183 -52
- package/dist/schemas/rag.js +148 -23
- package/dist/schemas/reload-config.d.ts +20 -8
- package/dist/schemas/reload-config.js +7 -5
- package/dist/schemas/runtime-context.d.ts +19 -0
- package/dist/schemas/runtime-context.js +7 -0
- package/dist/schemas/sdk-config.d.ts +697 -2
- package/dist/schemas/sdk-config.js +99 -1
- package/dist/schemas/sdk-errors-client.d.ts +1 -0
- package/dist/schemas/sdk-errors-client.js +5 -0
- package/dist/schemas/sdk-errors-server.d.ts +14 -0
- package/dist/schemas/sdk-errors-server.js +70 -0
- package/dist/schemas/shard.d.ts +21 -0
- package/dist/schemas/shard.js +17 -0
- package/dist/schemas/translate.d.ts +68 -23
- package/dist/schemas/translate.js +37 -11
- package/dist/schemas/translation-config.d.ts +272 -29
- package/dist/schemas/translation-config.js +63 -6
- package/dist/scripts/run-unit-tests.d.ts +2 -0
- package/dist/scripts/run-unit-tests.js +17 -0
- package/dist/server/bare/addons/llamacpp-completion/cache-logger.d.ts +6 -0
- package/dist/server/bare/addons/llamacpp-completion/cache-logger.js +34 -0
- package/dist/server/bare/addons/{llamacpp → llamacpp-completion}/create-model.d.ts +1 -5
- package/dist/server/bare/addons/{llamacpp → llamacpp-completion}/create-model.js +3 -26
- package/dist/server/bare/addons/{llamacpp → llamacpp-completion}/handlers/completion-stream.js +26 -7
- package/dist/server/bare/addons/llamacpp-completion/index.d.ts +4 -0
- package/dist/server/bare/addons/llamacpp-completion/index.js +3 -0
- package/dist/server/bare/addons/{llamacpp → llamacpp-completion}/kv-cache-utils.d.ts +4 -1
- package/dist/server/bare/addons/{llamacpp → llamacpp-completion}/kv-cache-utils.js +43 -10
- package/dist/server/bare/addons/llamacpp-embedding/create-model.d.ts +8 -0
- package/dist/server/bare/addons/llamacpp-embedding/create-model.js +38 -0
- package/dist/server/bare/addons/llamacpp-embedding/index.d.ts +3 -0
- package/dist/server/bare/addons/llamacpp-embedding/index.js +2 -0
- package/dist/server/bare/addons/load-model.js +28 -24
- package/dist/server/bare/addons/{translation → nmtcpp-translation}/create-model.js +23 -8
- package/dist/server/bare/addons/{translation → nmtcpp-translation}/handlers/translate.js +35 -11
- package/dist/server/bare/addons/onnx-ocr/create-model.d.ts +10 -0
- package/dist/server/bare/addons/onnx-ocr/create-model.js +39 -0
- package/dist/server/bare/addons/onnx-ocr/handlers/ocr-stream.d.ts +10 -0
- package/dist/server/bare/addons/onnx-ocr/handlers/ocr-stream.js +109 -0
- package/dist/server/bare/addons/onnx-ocr/index.d.ts +3 -0
- package/dist/server/bare/addons/onnx-ocr/index.js +2 -0
- package/dist/server/bare/addons/{whispercpp → whispercpp-transcription}/create-model.js +2 -1
- package/dist/server/bare/delegate-rpc-client.js +19 -1
- package/dist/server/bare/rag-hyperdb/handlers/chunk.d.ts +3 -0
- package/dist/server/bare/rag-hyperdb/handlers/chunk.js +7 -0
- package/dist/server/bare/rag-hyperdb/handlers/close-workspace.d.ts +3 -0
- package/dist/server/bare/rag-hyperdb/handlers/close-workspace.js +9 -0
- package/dist/server/bare/rag-hyperdb/handlers/delete-embeddings.d.ts +1 -1
- package/dist/server/bare/rag-hyperdb/handlers/delete-embeddings.js +9 -10
- package/dist/server/bare/rag-hyperdb/handlers/delete-workspace.d.ts +3 -0
- package/dist/server/bare/rag-hyperdb/handlers/delete-workspace.js +14 -0
- package/dist/server/bare/rag-hyperdb/handlers/ingest.d.ts +9 -0
- package/dist/server/bare/rag-hyperdb/handlers/ingest.js +18 -0
- package/dist/server/bare/rag-hyperdb/handlers/list-workspaces.d.ts +3 -0
- package/dist/server/bare/rag-hyperdb/handlers/list-workspaces.js +4 -0
- package/dist/server/bare/rag-hyperdb/handlers/reindex.d.ts +9 -0
- package/dist/server/bare/rag-hyperdb/handlers/reindex.js +16 -0
- package/dist/server/bare/rag-hyperdb/handlers/save-embeddings.d.ts +7 -4
- package/dist/server/bare/rag-hyperdb/handlers/save-embeddings.js +13 -14
- package/dist/server/bare/rag-hyperdb/handlers/search.js +10 -10
- package/dist/server/bare/rag-hyperdb/index.d.ts +8 -1
- package/dist/server/bare/rag-hyperdb/index.js +8 -1
- package/dist/server/bare/rag-hyperdb/rag-operation-manager.d.ts +7 -0
- package/dist/server/bare/rag-hyperdb/rag-operation-manager.js +39 -0
- package/dist/server/bare/rag-hyperdb/rag-workspace-manager.d.ts +15 -0
- package/dist/server/bare/rag-hyperdb/rag-workspace-manager.js +135 -0
- package/dist/server/bare/registry/config-registry.js +11 -0
- package/dist/server/bare/registry/logging-stream-registry.js +3 -3
- package/dist/server/bare/registry/model-config-registry.d.ts +4 -0
- package/dist/server/bare/registry/model-config-registry.js +15 -0
- package/dist/server/bare/registry/model-config-utils.d.ts +15 -0
- package/dist/server/bare/registry/model-config-utils.js +110 -0
- package/dist/server/bare/registry/model-registry.d.ts +6 -29
- package/dist/server/bare/registry/runtime-context-registry.d.ts +7 -0
- package/dist/server/bare/registry/runtime-context-registry.js +26 -0
- package/dist/server/bare/utils/loader-adapter.d.ts +7 -0
- package/dist/server/bare/utils/loader-adapter.js +9 -0
- package/dist/server/error-handlers.js +4 -2
- package/dist/server/rpc/handle-request.js +91 -9
- package/dist/server/rpc/handlers/cancelHandler.js +10 -1
- package/dist/server/rpc/handlers/completion-stream.js +1 -1
- package/dist/server/rpc/handlers/delete-cache.js +1 -1
- package/dist/server/rpc/handlers/embed.js +1 -1
- package/dist/server/rpc/handlers/get-model-info.d.ts +1 -1
- package/dist/server/rpc/handlers/get-model-info.js +22 -10
- package/dist/server/rpc/handlers/index.d.ts +4 -1
- package/dist/server/rpc/handlers/index.js +2 -0
- package/dist/server/rpc/handlers/load-model/download-manager.d.ts +11 -11
- package/dist/server/rpc/handlers/load-model/download-manager.js +36 -26
- package/dist/server/rpc/handlers/{load-model.d.ts → load-model/handler.d.ts} +2 -2
- package/dist/server/rpc/handlers/load-model/handler.js +179 -0
- package/dist/server/rpc/handlers/load-model/http.js +342 -33
- package/dist/server/rpc/handlers/load-model/hyperdrive.js +5 -3
- package/dist/server/rpc/handlers/load-model/index.d.ts +2 -2
- package/dist/server/rpc/handlers/load-model/index.js +2 -2
- package/dist/server/rpc/handlers/load-model/resolve.js +32 -3
- package/dist/server/rpc/handlers/load-model-delegated.js +6 -3
- package/dist/server/rpc/handlers/ocr-stream.d.ts +3 -0
- package/dist/server/rpc/handlers/ocr-stream.js +39 -0
- package/dist/server/rpc/handlers/rag.d.ts +2 -2
- package/dist/server/rpc/handlers/rag.js +107 -43
- package/dist/server/rpc/handlers/text-to-speech.js +1 -1
- package/dist/server/rpc/handlers/transcribe-stream.js +1 -1
- package/dist/server/rpc/handlers/translate.js +6 -15
- package/dist/server/utils/archive.d.ts +3 -0
- package/dist/server/utils/archive.js +171 -0
- package/dist/server/utils/audio/decoder.js +2 -2
- package/dist/server/utils/cache.d.ts +5 -5
- package/dist/server/utils/cache.js +6 -6
- package/dist/server/utils/config-transform.d.ts +2 -2
- package/dist/server/utils/config-transform.js +8 -6
- package/dist/server/utils/formatting.d.ts +13 -0
- package/dist/server/utils/formatting.js +26 -0
- package/dist/server/utils/gguf-tensor-extractor.d.ts +15 -0
- package/dist/server/utils/gguf-tensor-extractor.js +314 -0
- package/dist/server/utils/index.d.ts +3 -1
- package/dist/server/utils/index.js +3 -1
- package/dist/server/utils/shard-utils.d.ts +42 -16
- package/dist/server/utils/shard-utils.js +95 -7
- package/dist/server/worker.js +8 -3
- package/dist/utils/errors-base.d.ts +1 -1
- package/dist/utils/errors-base.js +2 -2
- package/dist/utils/errors-client.d.ts +12 -0
- package/dist/utils/errors-client.js +20 -0
- package/dist/utils/errors-server.d.ts +33 -0
- package/dist/utils/errors-server.js +55 -0
- package/package.json +42 -17
- package/dist/client/rpc/bare-rpc-client.d.ts +0 -4
- package/dist/client/rpc/bare-rpc-client.js +0 -94
- package/dist/examples/config/config-path.d.ts +0 -8
- package/dist/examples/config/config-path.js +0 -13
- package/dist/examples/rag/rag-hyperdb-workspaces.d.ts +0 -2
- package/dist/examples/rag/rag-hyperdb-workspaces.js +0 -106
- package/dist/examples/rag/rag-hyperdb.d.ts +0 -2
- package/dist/server/bare/addons/llamacpp/index.d.ts +0 -4
- package/dist/server/bare/addons/llamacpp/index.js +0 -3
- package/dist/server/bare/rag-hyperdb/rag-manager.d.ts +0 -5
- package/dist/server/bare/rag-hyperdb/rag-manager.js +0 -66
- package/dist/server/rpc/handlers/load-model.js +0 -111
- package/dist/server/utils/hash.d.ts +0 -5
- package/dist/server/utils/hash.js +0 -11
- package/dist/utils/getProcessSafe.d.ts +0 -7
- package/dist/utils/getProcessSafe.js +0 -17
- package/dist/utils/runtime.d.ts +0 -30
- package/dist/utils/runtime.js +0 -48
- /package/dist/server/bare/addons/{llamacpp → llamacpp-completion}/handlers/completion-stream.d.ts +0 -0
- /package/dist/server/bare/addons/{llamacpp → llamacpp-embedding}/handlers/embed.d.ts +0 -0
- /package/dist/server/bare/addons/{llamacpp → llamacpp-embedding}/handlers/embed.js +0 -0
- /package/dist/server/bare/addons/{translation → nmtcpp-translation}/create-model.d.ts +0 -0
- /package/dist/server/bare/addons/{translation → nmtcpp-translation}/handlers/translate.d.ts +0 -0
- /package/dist/server/bare/addons/{translation → nmtcpp-translation}/index.d.ts +0 -0
- /package/dist/server/bare/addons/{translation → nmtcpp-translation}/index.js +0 -0
- /package/dist/server/bare/addons/{tts → onnx-tts}/create-model.d.ts +0 -0
- /package/dist/server/bare/addons/{tts → onnx-tts}/create-model.js +0 -0
- /package/dist/server/bare/addons/{tts → onnx-tts}/handlers/text-to-speech.d.ts +0 -0
- /package/dist/server/bare/addons/{tts → onnx-tts}/handlers/text-to-speech.js +0 -0
- /package/dist/server/bare/addons/{tts → onnx-tts}/index.d.ts +0 -0
- /package/dist/server/bare/addons/{tts → onnx-tts}/index.js +0 -0
- /package/dist/server/bare/addons/{whispercpp → whispercpp-transcription}/create-model.d.ts +0 -0
- /package/dist/server/bare/addons/{whispercpp → whispercpp-transcription}/handlers/transcribe-stream.d.ts +0 -0
- /package/dist/server/bare/addons/{whispercpp → whispercpp-transcription}/handlers/transcribe-stream.js +0 -0
- /package/dist/server/bare/addons/{whispercpp → whispercpp-transcription}/index.d.ts +0 -0
- /package/dist/server/bare/addons/{whispercpp → whispercpp-transcription}/index.js +0 -0
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import fs, { promises as fsPromises } from "bare-fs";
|
|
2
|
+
import path from "bare-path";
|
|
2
3
|
import { Readable } from "bare-stream";
|
|
3
4
|
import fetch from "bare-fetch";
|
|
4
5
|
import { AbortController } from "bare-abort-controller";
|
|
5
|
-
import {
|
|
6
|
+
import { withTimeout } from "../../../../utils/withTimeout.js";
|
|
7
|
+
import { getModelsCacheDir, getShardedModelCacheDir, generateShortHash, detectShardedModel, parsePatternBasedShardUrl, extractTensorsFromShards, calculatePercentage, isArchiveUrl, extractAndValidateShardedArchive, validateShardedModelCache, checkAllShardsExist, generateShardFilenames, hasValidGGUFHeader, } from "../../../utils/index.js";
|
|
8
|
+
import { getSDKConfig } from "../../../bare/registry/config-registry.js";
|
|
6
9
|
import { getActiveDownload, registerDownload, unregisterDownload, createHttpDownloadKey, shouldClearCache, clearClearCacheFlag, } from "./download-manager.js";
|
|
7
|
-
import { DownloadCancelledError, HTTPError, NoResponseBodyError, ResponseBodyNotReadableError, } from "../../../../utils/errors-server.js";
|
|
10
|
+
import { DownloadCancelledError, HTTPError, NoResponseBodyError, PartialDownloadOfflineError, ResponseBodyNotReadableError, } from "../../../../utils/errors-server.js";
|
|
8
11
|
import { getServerLogger } from "../../../../logging/index.js";
|
|
9
12
|
const logger = getServerLogger();
|
|
13
|
+
const DEFAULT_CONCURRENCY = 3;
|
|
14
|
+
const DEFAULT_HTTP_CONNECTION_TIMEOUT_MS = 10_000;
|
|
10
15
|
function extractFilenameFromUrl(url) {
|
|
11
16
|
// Parse URL to get the filename from the path
|
|
12
17
|
const urlParts = url.split("/");
|
|
@@ -20,17 +25,29 @@ async function validateCachedFile(modelPath, url, signal) {
|
|
|
20
25
|
await fsPromises.access(modelPath);
|
|
21
26
|
const localStats = await fsPromises.stat(modelPath);
|
|
22
27
|
const localSize = localStats.size;
|
|
23
|
-
|
|
28
|
+
const config = getSDKConfig();
|
|
29
|
+
const connectionTimeout = config.httpConnectionTimeoutMs ?? DEFAULT_HTTP_CONNECTION_TIMEOUT_MS;
|
|
24
30
|
let expectedSize = 0;
|
|
25
31
|
try {
|
|
26
|
-
const response = await fetch(url, {
|
|
32
|
+
const response = await withTimeout(fetch(url, {
|
|
27
33
|
method: "HEAD",
|
|
28
34
|
...(signal && { signal }),
|
|
29
|
-
});
|
|
35
|
+
}), connectionTimeout);
|
|
30
36
|
expectedSize = parseInt(response.headers.get("content-length") || "0");
|
|
31
37
|
}
|
|
32
|
-
catch (
|
|
33
|
-
logger.warn(`⚠️
|
|
38
|
+
catch (headError) {
|
|
39
|
+
logger.warn(`⚠️ HEAD request failed: ${headError instanceof Error ? headError.message : String(headError)}`);
|
|
40
|
+
logger.info(`📴 Falling back to GGUF header validation...`);
|
|
41
|
+
const hasValidHeader = await hasValidGGUFHeader(modelPath);
|
|
42
|
+
if (hasValidHeader) {
|
|
43
|
+
logger.info(`✅ Offline - GGUF header valid, using cached file: ${modelPath}`);
|
|
44
|
+
return modelPath;
|
|
45
|
+
}
|
|
46
|
+
if (localSize > 0) {
|
|
47
|
+
logger.error(`❌ Offline with partial download (${localSize} bytes). Cannot resume without network.`);
|
|
48
|
+
throw new PartialDownloadOfflineError(url, localSize);
|
|
49
|
+
}
|
|
50
|
+
logger.warn(`⚠️ Offline and GGUF validation failed - file may be incomplete`);
|
|
34
51
|
return null;
|
|
35
52
|
}
|
|
36
53
|
if (localSize !== expectedSize) {
|
|
@@ -40,8 +57,12 @@ async function validateCachedFile(modelPath, url, signal) {
|
|
|
40
57
|
logger.info(`✅ Using cached HTTP model: ${modelPath}`);
|
|
41
58
|
return modelPath;
|
|
42
59
|
}
|
|
43
|
-
catch {
|
|
44
|
-
//
|
|
60
|
+
catch (error) {
|
|
61
|
+
// Re-throw PartialDownloadOfflineError
|
|
62
|
+
if (error instanceof PartialDownloadOfflineError) {
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
// File doesn't exist or other access error
|
|
45
66
|
return null;
|
|
46
67
|
}
|
|
47
68
|
}
|
|
@@ -69,13 +90,26 @@ async function performHttpDownload(url, modelPath, downloadKey, progressCallback
|
|
|
69
90
|
if (startOffset > 0) {
|
|
70
91
|
headers["Range"] = `bytes=${startOffset}-`;
|
|
71
92
|
}
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
93
|
+
const config = getSDKConfig();
|
|
94
|
+
const connectionTimeout = config.httpConnectionTimeoutMs ?? DEFAULT_HTTP_CONNECTION_TIMEOUT_MS;
|
|
95
|
+
let response;
|
|
96
|
+
try {
|
|
97
|
+
response = await withTimeout(fetch(url, {
|
|
98
|
+
method: "GET",
|
|
99
|
+
headers,
|
|
100
|
+
...(signal && { signal }),
|
|
101
|
+
}), connectionTimeout);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
// Check if it was parent abort
|
|
105
|
+
if (signal?.aborted) {
|
|
106
|
+
throw new DownloadCancelledError();
|
|
107
|
+
}
|
|
108
|
+
// Connection timeout or network error
|
|
109
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
110
|
+
logger.error(`❌ Connection failed: ${errorMsg}. URL: ${url}`);
|
|
111
|
+
throw new HTTPError(0, `Connection failed: ${errorMsg}`, error);
|
|
112
|
+
}
|
|
79
113
|
if (!response.ok) {
|
|
80
114
|
// Check if it's a 416 (Range Not Satisfiable) - file already complete
|
|
81
115
|
if (response.status === 416 && startOffset > 0) {
|
|
@@ -98,18 +132,16 @@ async function performHttpDownload(url, modelPath, downloadKey, progressCallback
|
|
|
98
132
|
startOffset = 0;
|
|
99
133
|
downloadedBytes = 0;
|
|
100
134
|
// Retry without Range header
|
|
101
|
-
|
|
135
|
+
response = await fetch(url, {
|
|
102
136
|
method: "GET",
|
|
103
137
|
headers: {
|
|
104
|
-
"User-Agent": "qvac-sdk
|
|
138
|
+
"User-Agent": "qvac-sdk",
|
|
105
139
|
},
|
|
106
140
|
...(signal && { signal }),
|
|
107
141
|
});
|
|
108
|
-
if (!
|
|
109
|
-
throw new HTTPError(
|
|
142
|
+
if (!response.ok) {
|
|
143
|
+
throw new HTTPError(response.status, response.statusText);
|
|
110
144
|
}
|
|
111
|
-
// Use the fresh response
|
|
112
|
-
Object.assign(response, freshResponse);
|
|
113
145
|
}
|
|
114
146
|
else if (response.status !== 206) {
|
|
115
147
|
// 206 is Partial Content (successful resume)
|
|
@@ -156,7 +188,7 @@ async function performHttpDownload(url, modelPath, downloadKey, progressCallback
|
|
|
156
188
|
type: "modelProgress",
|
|
157
189
|
downloaded: downloadedBytes,
|
|
158
190
|
total: totalBytes,
|
|
159
|
-
percentage:
|
|
191
|
+
percentage: calculatePercentage(downloadedBytes, totalBytes),
|
|
160
192
|
downloadKey,
|
|
161
193
|
});
|
|
162
194
|
}
|
|
@@ -201,7 +233,7 @@ async function performHttpDownload(url, modelPath, downloadKey, progressCallback
|
|
|
201
233
|
type: "modelProgress",
|
|
202
234
|
downloaded: downloadedBytes,
|
|
203
235
|
total: totalBytes,
|
|
204
|
-
percentage:
|
|
236
|
+
percentage: calculatePercentage(downloadedBytes, totalBytes),
|
|
205
237
|
downloadKey,
|
|
206
238
|
});
|
|
207
239
|
}
|
|
@@ -243,7 +275,7 @@ async function performHttpDownload(url, modelPath, downloadKey, progressCallback
|
|
|
243
275
|
type: "modelProgress",
|
|
244
276
|
downloaded: downloadedBytes,
|
|
245
277
|
total: totalBytes,
|
|
246
|
-
percentage:
|
|
278
|
+
percentage: calculatePercentage(downloadedBytes, totalBytes),
|
|
247
279
|
downloadKey,
|
|
248
280
|
});
|
|
249
281
|
}
|
|
@@ -282,8 +314,15 @@ async function performHttpDownload(url, modelPath, downloadKey, progressCallback
|
|
|
282
314
|
}
|
|
283
315
|
}
|
|
284
316
|
export async function downloadModelFromHttp(url, progressCallback) {
|
|
317
|
+
const filename = extractFilenameFromUrl(url);
|
|
318
|
+
if (isArchiveUrl(url)) {
|
|
319
|
+
return downloadShardedModelFromArchive(url, progressCallback);
|
|
320
|
+
}
|
|
321
|
+
const shardInfo = detectShardedModel(filename);
|
|
322
|
+
if (shardInfo.isSharded && shardInfo.totalShards) {
|
|
323
|
+
return downloadShardedModelFromHttp(url, progressCallback);
|
|
324
|
+
}
|
|
285
325
|
const downloadKey = createHttpDownloadKey(url);
|
|
286
|
-
// Check if already downloading
|
|
287
326
|
const existing = getActiveDownload(downloadKey);
|
|
288
327
|
if (existing) {
|
|
289
328
|
logger.info(`📥 Reusing existing download for: ${downloadKey}`);
|
|
@@ -291,7 +330,6 @@ export async function downloadModelFromHttp(url, progressCallback) {
|
|
|
291
330
|
}
|
|
292
331
|
const cacheDir = getModelsCacheDir();
|
|
293
332
|
const sourceHash = generateShortHash(url);
|
|
294
|
-
const filename = extractFilenameFromUrl(url);
|
|
295
333
|
const modelPath = `${cacheDir}/${sourceHash}_${filename}`;
|
|
296
334
|
// Create managed download with AbortController
|
|
297
335
|
const abortController = new AbortController();
|
|
@@ -311,8 +349,11 @@ export async function downloadModelFromHttp(url, progressCallback) {
|
|
|
311
349
|
downloadKey,
|
|
312
350
|
});
|
|
313
351
|
}
|
|
314
|
-
catch {
|
|
315
|
-
|
|
352
|
+
catch (error) {
|
|
353
|
+
logger.debug("Failed to get file stats for progress callback", {
|
|
354
|
+
path: cachedPath,
|
|
355
|
+
error,
|
|
356
|
+
});
|
|
316
357
|
}
|
|
317
358
|
}
|
|
318
359
|
return cachedPath;
|
|
@@ -331,8 +372,11 @@ export async function downloadModelFromHttp(url, progressCallback) {
|
|
|
331
372
|
downloadKey,
|
|
332
373
|
});
|
|
333
374
|
}
|
|
334
|
-
catch {
|
|
335
|
-
|
|
375
|
+
catch (error) {
|
|
376
|
+
logger.debug("Failed to get file stats for final progress update", {
|
|
377
|
+
path: modelPath,
|
|
378
|
+
error,
|
|
379
|
+
});
|
|
336
380
|
}
|
|
337
381
|
}
|
|
338
382
|
return modelPath;
|
|
@@ -347,8 +391,11 @@ export async function downloadModelFromHttp(url, progressCallback) {
|
|
|
347
391
|
await fsPromises.unlink(modelPath);
|
|
348
392
|
logger.info(`✅ Deleted partial file: ${modelPath}`);
|
|
349
393
|
}
|
|
350
|
-
catch {
|
|
351
|
-
|
|
394
|
+
catch (error) {
|
|
395
|
+
logger.debug("Failed to delete partial file during cleanup", {
|
|
396
|
+
path: modelPath,
|
|
397
|
+
error,
|
|
398
|
+
});
|
|
352
399
|
}
|
|
353
400
|
}
|
|
354
401
|
else {
|
|
@@ -378,3 +425,265 @@ export async function downloadModelFromHttp(url, progressCallback) {
|
|
|
378
425
|
registerDownload(downloadKey, downloadEntry);
|
|
379
426
|
return downloadPromise;
|
|
380
427
|
}
|
|
428
|
+
async function downloadShardedModelFromHttp(shardUrl, progressCallback) {
|
|
429
|
+
const config = getSDKConfig();
|
|
430
|
+
const concurrency = config.httpDownloadConcurrency ?? DEFAULT_CONCURRENCY;
|
|
431
|
+
const { shardUrls: shardInfos, cacheKey } = parsePatternBasedShardUrl(shardUrl);
|
|
432
|
+
const downloadKey = `http-sharded:${cacheKey}`;
|
|
433
|
+
logger.info(`📥 HTTP sharded download: ${shardInfos.length} shards detected from ${shardUrl}`);
|
|
434
|
+
const existing = getActiveDownload(downloadKey);
|
|
435
|
+
if (existing) {
|
|
436
|
+
logger.info(`📥 Reusing existing sharded download for: ${downloadKey}`);
|
|
437
|
+
return existing.promise;
|
|
438
|
+
}
|
|
439
|
+
const abortController = new AbortController();
|
|
440
|
+
const shardDir = getShardedModelCacheDir(cacheKey);
|
|
441
|
+
const downloadPromise = (async () => {
|
|
442
|
+
try {
|
|
443
|
+
const shardStates = await Promise.all(shardInfos.map(async (shard, index) => {
|
|
444
|
+
const shardPath = path.join(shardDir, shard.filename);
|
|
445
|
+
let expectedSize = 0;
|
|
446
|
+
try {
|
|
447
|
+
const response = await fetch(shard.url, {
|
|
448
|
+
method: "HEAD",
|
|
449
|
+
signal: abortController.signal,
|
|
450
|
+
});
|
|
451
|
+
expectedSize = parseInt(response.headers.get("content-length") || "0");
|
|
452
|
+
}
|
|
453
|
+
catch (error) {
|
|
454
|
+
logger.warn("Failed to get shard size via HEAD request", {
|
|
455
|
+
url: shard.url,
|
|
456
|
+
error,
|
|
457
|
+
});
|
|
458
|
+
// expectedSize remains 0, progress percentage will be 0
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
index,
|
|
462
|
+
shard,
|
|
463
|
+
shardPath,
|
|
464
|
+
expectedSize,
|
|
465
|
+
downloadedBytes: 0,
|
|
466
|
+
isComplete: false,
|
|
467
|
+
};
|
|
468
|
+
}));
|
|
469
|
+
const overallTotal = shardStates.reduce((sum, s) => sum + s.expectedSize, 0);
|
|
470
|
+
logger.info(`📏 Total size: ${overallTotal} bytes (${(overallTotal / 1024 / 1024).toFixed(2)} MB)`);
|
|
471
|
+
const cacheChecks = await Promise.all(shardStates.map(async (state) => {
|
|
472
|
+
const cached = await validateCachedFile(state.shardPath, state.shard.url, abortController.signal);
|
|
473
|
+
return { state, isCached: cached !== null };
|
|
474
|
+
}));
|
|
475
|
+
const shardsToDownload = cacheChecks
|
|
476
|
+
.filter((c) => !c.isCached)
|
|
477
|
+
.map((c) => c.state);
|
|
478
|
+
for (const check of cacheChecks) {
|
|
479
|
+
if (check.isCached) {
|
|
480
|
+
check.state.isComplete = true;
|
|
481
|
+
check.state.downloadedBytes = check.state.expectedSize;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
logger.info(`📥 ${shardsToDownload.length} of ${shardInfos.length} shards need downloading`);
|
|
485
|
+
await downloadShardsWithConcurrency(shardsToDownload, shardStates, concurrency, abortController.signal, downloadKey, overallTotal, progressCallback);
|
|
486
|
+
logger.info(`✅ All ${shardInfos.length} shards downloaded successfully`);
|
|
487
|
+
await extractTensorsFromShards(shardDir, shardInfos[0].filename);
|
|
488
|
+
return path.join(shardDir, shardInfos[0].filename);
|
|
489
|
+
}
|
|
490
|
+
catch (error) {
|
|
491
|
+
logger.error("❌ Error during sharded download:", error instanceof Error ? error.message : String(error));
|
|
492
|
+
if (error instanceof Error && error.message === "Download cancelled") {
|
|
493
|
+
if (shouldClearCache(downloadKey)) {
|
|
494
|
+
logger.info("🗑️ Clearing cache - deleting partial shard files");
|
|
495
|
+
try {
|
|
496
|
+
await fsPromises.rm(shardDir, { recursive: true, force: true });
|
|
497
|
+
logger.info(`✅ Deleted shard directory: ${shardDir}`);
|
|
498
|
+
}
|
|
499
|
+
catch (cleanupError) {
|
|
500
|
+
logger.debug("Failed to delete shard directory during cleanup", {
|
|
501
|
+
path: shardDir,
|
|
502
|
+
error: cleanupError,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
throw error;
|
|
508
|
+
}
|
|
509
|
+
finally {
|
|
510
|
+
unregisterDownload(downloadKey);
|
|
511
|
+
clearClearCacheFlag(downloadKey);
|
|
512
|
+
}
|
|
513
|
+
})();
|
|
514
|
+
registerDownload(downloadKey, {
|
|
515
|
+
key: downloadKey,
|
|
516
|
+
promise: downloadPromise,
|
|
517
|
+
abortController,
|
|
518
|
+
startTime: Date.now(),
|
|
519
|
+
type: "http",
|
|
520
|
+
url: shardUrl,
|
|
521
|
+
modelPath: shardDir,
|
|
522
|
+
});
|
|
523
|
+
return downloadPromise;
|
|
524
|
+
}
|
|
525
|
+
async function downloadShardedModelFromArchive(archiveUrl, progressCallback) {
|
|
526
|
+
const filename = extractFilenameFromUrl(archiveUrl);
|
|
527
|
+
const sourceHash = generateShortHash(archiveUrl);
|
|
528
|
+
const downloadKey = `http-archive:${sourceHash}`;
|
|
529
|
+
logger.info(`📦 HTTP archive download: ${filename}`);
|
|
530
|
+
const existing = getActiveDownload(downloadKey);
|
|
531
|
+
if (existing) {
|
|
532
|
+
logger.info(`📥 Reusing existing archive download for: ${downloadKey}`);
|
|
533
|
+
return existing.promise;
|
|
534
|
+
}
|
|
535
|
+
const abortController = new AbortController();
|
|
536
|
+
const extractDir = getShardedModelCacheDir(sourceHash);
|
|
537
|
+
const archivePath = path.join(extractDir, `${sourceHash}_${filename}`);
|
|
538
|
+
const downloadPromise = (async () => {
|
|
539
|
+
try {
|
|
540
|
+
await fsPromises.mkdir(extractDir, { recursive: true });
|
|
541
|
+
const files = await fsPromises.readdir(extractDir);
|
|
542
|
+
const shardedFile = files.find((f) => detectShardedModel(String(f)).isSharded);
|
|
543
|
+
if (!shardedFile) {
|
|
544
|
+
return downloadAndExtractArchive();
|
|
545
|
+
}
|
|
546
|
+
const shardFilename = String(shardedFile);
|
|
547
|
+
const allShardsExist = await checkAllShardsExist(extractDir, shardFilename);
|
|
548
|
+
if (!allShardsExist) {
|
|
549
|
+
logger.warn(`⚠️ Incomplete shards found, re-downloading archive`);
|
|
550
|
+
return downloadAndExtractArchive();
|
|
551
|
+
}
|
|
552
|
+
const shardFilenames = generateShardFilenames(shardFilename);
|
|
553
|
+
const firstShard = path.join(extractDir, shardFilenames[0]);
|
|
554
|
+
const isComplete = await validateShardedModelCache(extractDir, shardFilename);
|
|
555
|
+
if (isComplete) {
|
|
556
|
+
logger.info(`✅ Archive already extracted: ${extractDir}`);
|
|
557
|
+
if (progressCallback) {
|
|
558
|
+
progressCallback({
|
|
559
|
+
type: "modelProgress",
|
|
560
|
+
downloaded: 1,
|
|
561
|
+
total: 1,
|
|
562
|
+
percentage: 100,
|
|
563
|
+
downloadKey,
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
return firstShard;
|
|
567
|
+
}
|
|
568
|
+
logger.info(`📝 All shards present but tensors.txt missing, extracting tensors...`);
|
|
569
|
+
try {
|
|
570
|
+
await extractTensorsFromShards(extractDir, shardFilename);
|
|
571
|
+
logger.info(`✅ Tensors extracted successfully`);
|
|
572
|
+
if (progressCallback) {
|
|
573
|
+
progressCallback({
|
|
574
|
+
type: "modelProgress",
|
|
575
|
+
downloaded: 1,
|
|
576
|
+
total: 1,
|
|
577
|
+
percentage: 100,
|
|
578
|
+
downloadKey,
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
return firstShard;
|
|
582
|
+
}
|
|
583
|
+
catch (error) {
|
|
584
|
+
logger.warn(`Failed to extract tensors, will re-download archive`, {
|
|
585
|
+
error,
|
|
586
|
+
});
|
|
587
|
+
return downloadAndExtractArchive();
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
catch (error) {
|
|
591
|
+
logger.error("❌ Error downloading/extracting archive:", error);
|
|
592
|
+
throw error;
|
|
593
|
+
}
|
|
594
|
+
finally {
|
|
595
|
+
unregisterDownload(downloadKey);
|
|
596
|
+
}
|
|
597
|
+
async function downloadAndExtractArchive() {
|
|
598
|
+
await performHttpDownload(archiveUrl, archivePath, downloadKey, progressCallback, abortController.signal);
|
|
599
|
+
logger.info(`✅ Archive downloaded, extracting to: ${extractDir}`);
|
|
600
|
+
const firstShardPath = await extractAndValidateShardedArchive(archivePath, extractDir, abortController.signal);
|
|
601
|
+
try {
|
|
602
|
+
await fsPromises.unlink(archivePath);
|
|
603
|
+
logger.info(`🗑️ Cleaned up archive file: ${archivePath}`);
|
|
604
|
+
}
|
|
605
|
+
catch (cleanupError) {
|
|
606
|
+
logger.debug("Failed to delete archive file during cleanup", {
|
|
607
|
+
path: archivePath,
|
|
608
|
+
error: cleanupError,
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
return firstShardPath;
|
|
612
|
+
}
|
|
613
|
+
})();
|
|
614
|
+
registerDownload(downloadKey, {
|
|
615
|
+
key: downloadKey,
|
|
616
|
+
promise: downloadPromise,
|
|
617
|
+
abortController,
|
|
618
|
+
startTime: Date.now(),
|
|
619
|
+
type: "http",
|
|
620
|
+
url: archiveUrl,
|
|
621
|
+
modelPath: archivePath,
|
|
622
|
+
});
|
|
623
|
+
return downloadPromise;
|
|
624
|
+
}
|
|
625
|
+
async function downloadShardsWithConcurrency(shardsToDownload, allShards, concurrency, signal, downloadKey, overallTotal, progressCallback) {
|
|
626
|
+
const queue = [...shardsToDownload];
|
|
627
|
+
const inFlight = new Set();
|
|
628
|
+
while (queue.length > 0 || inFlight.size > 0) {
|
|
629
|
+
if (signal.aborted) {
|
|
630
|
+
throw new DownloadCancelledError();
|
|
631
|
+
}
|
|
632
|
+
while (queue.length > 0 && inFlight.size < concurrency) {
|
|
633
|
+
const state = queue.shift();
|
|
634
|
+
const downloadPromise = (async () => {
|
|
635
|
+
logger.info(`📥 Downloading shard ${state.index + 1}: ${state.shard.filename}`);
|
|
636
|
+
await performHttpDownload(state.shard.url, state.shardPath, downloadKey, (progress) => {
|
|
637
|
+
state.downloadedBytes = progress.downloaded;
|
|
638
|
+
if (progressCallback) {
|
|
639
|
+
const overallDownloaded = allShards.reduce((sum, s) => sum + s.downloadedBytes, 0);
|
|
640
|
+
progressCallback({
|
|
641
|
+
type: "modelProgress",
|
|
642
|
+
downloaded: state.downloadedBytes,
|
|
643
|
+
total: state.expectedSize,
|
|
644
|
+
percentage: calculatePercentage(state.downloadedBytes, state.expectedSize),
|
|
645
|
+
downloadKey,
|
|
646
|
+
shardInfo: {
|
|
647
|
+
currentShard: state.index + 1,
|
|
648
|
+
totalShards: allShards.length,
|
|
649
|
+
shardName: state.shard.filename,
|
|
650
|
+
overallDownloaded,
|
|
651
|
+
overallTotal,
|
|
652
|
+
overallPercentage: calculatePercentage(overallDownloaded, overallTotal),
|
|
653
|
+
},
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
}, signal);
|
|
657
|
+
logger.info(`✅ Shard ${state.index + 1} complete: ${state.shard.filename}`);
|
|
658
|
+
})().finally(() => {
|
|
659
|
+
inFlight.delete(downloadPromise);
|
|
660
|
+
});
|
|
661
|
+
inFlight.add(downloadPromise);
|
|
662
|
+
}
|
|
663
|
+
if (inFlight.size > 0) {
|
|
664
|
+
await Promise.race(inFlight);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
// Mark all downloaded shards as complete
|
|
668
|
+
for (const state of shardsToDownload) {
|
|
669
|
+
state.isComplete = true;
|
|
670
|
+
state.downloadedBytes = state.expectedSize;
|
|
671
|
+
}
|
|
672
|
+
if (progressCallback) {
|
|
673
|
+
progressCallback({
|
|
674
|
+
type: "modelProgress",
|
|
675
|
+
downloaded: overallTotal,
|
|
676
|
+
total: overallTotal,
|
|
677
|
+
percentage: 100,
|
|
678
|
+
downloadKey,
|
|
679
|
+
shardInfo: {
|
|
680
|
+
currentShard: allShards.length,
|
|
681
|
+
totalShards: allShards.length,
|
|
682
|
+
shardName: allShards[allShards.length - 1].shard.filename,
|
|
683
|
+
overallDownloaded: overallTotal,
|
|
684
|
+
overallTotal,
|
|
685
|
+
overallPercentage: 100,
|
|
686
|
+
},
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
}
|
|
@@ -6,7 +6,7 @@ import Hyperdrive from "hyperdrive";
|
|
|
6
6
|
import {} from "bare-stream";
|
|
7
7
|
import { AbortController } from "bare-abort-controller";
|
|
8
8
|
import { getEnv } from "../../../worker.js";
|
|
9
|
-
import { getModelsCacheDir, generateShortHash, detectShardedModel, getShardedModelCacheDir, getShardPath, checkShardCompleteness, calculateFileChecksum, } from "../../../utils/index.js";
|
|
9
|
+
import { getModelsCacheDir, generateShortHash, detectShardedModel, getShardedModelCacheDir, getShardPath, checkShardCompleteness, calculateFileChecksum, extractTensorsFromShards, calculatePercentage, } from "../../../utils/index.js";
|
|
10
10
|
import { getModelBySrc } from "../../../../models/hyperdrive/index.js";
|
|
11
11
|
import { getActiveDownload, registerDownload, unregisterDownload, createHyperdriveDownloadKey, shouldClearCache, clearClearCacheFlag, } from "./download-manager.js";
|
|
12
12
|
import { getSDKConfig } from "../../../bare/registry/config-registry.js";
|
|
@@ -265,7 +265,7 @@ async function downloadAndValidateFile(drive, sourceFileName, targetFilePath, ex
|
|
|
265
265
|
type: "modelProgress",
|
|
266
266
|
downloaded: cappedDownloaded,
|
|
267
267
|
total: totalBytes,
|
|
268
|
-
percentage:
|
|
268
|
+
percentage: calculatePercentage(cappedDownloaded, totalBytes),
|
|
269
269
|
downloadKey: progressContext.downloadKey,
|
|
270
270
|
};
|
|
271
271
|
if (progressContext.shardInfo) {
|
|
@@ -394,6 +394,7 @@ async function downloadShardedFilesToFilesystem(hyperdriveKey, firstShardFileNam
|
|
|
394
394
|
},
|
|
395
395
|
});
|
|
396
396
|
}
|
|
397
|
+
await extractTensorsFromShards(shardDir, firstShardFileName);
|
|
397
398
|
return getShardPath(hyperdriveKey, allFiles[0]);
|
|
398
399
|
}
|
|
399
400
|
logger.info(`📥 Need to download ${invalidIndices.length} of ${allFiles.length} files`);
|
|
@@ -438,7 +439,7 @@ async function downloadShardedFilesToFilesystem(hyperdriveKey, firstShardFileNam
|
|
|
438
439
|
shardName: file,
|
|
439
440
|
overallDownloaded,
|
|
440
441
|
overallTotal,
|
|
441
|
-
overallPercentage:
|
|
442
|
+
overallPercentage: calculatePercentage(overallDownloaded, overallTotal),
|
|
442
443
|
},
|
|
443
444
|
});
|
|
444
445
|
}
|
|
@@ -483,6 +484,7 @@ async function downloadShardedFilesToFilesystem(hyperdriveKey, firstShardFileNam
|
|
|
483
484
|
},
|
|
484
485
|
});
|
|
485
486
|
}
|
|
487
|
+
await extractTensorsFromShards(shardDir, firstShardFileName);
|
|
486
488
|
// Delete corestore directory after successful download (only when not seeding)
|
|
487
489
|
if (!seed) {
|
|
488
490
|
await deleteCorestoreDirectory(corestoreDir);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { handleLoadModel } from "
|
|
2
|
-
export { downloadModelFromHyperdrive } from "./hyperdrive";
|
|
1
|
+
export { handleLoadModel } from "./handler";
|
|
3
2
|
export { resolveModelPath } from "./resolve";
|
|
3
|
+
export { createCancelFunction } from "./download-manager";
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { handleLoadModel } from "
|
|
2
|
-
export { downloadModelFromHyperdrive } from "./hyperdrive.js";
|
|
1
|
+
export { handleLoadModel } from "./handler.js";
|
|
3
2
|
export { resolveModelPath } from "./resolve.js";
|
|
3
|
+
export { createCancelFunction } from "./download-manager.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { models } from "../../../../models/hyperdrive/models.js";
|
|
2
|
-
import { hyperdriveUrlSchema } from "../../../../schemas/index.js";
|
|
3
|
-
import { getModelsCacheDir } from "../../../utils/index.js";
|
|
2
|
+
import { hyperdriveUrlSchema, SUPPORTED_ARCHIVE_EXTENSIONS, } from "../../../../schemas/index.js";
|
|
3
|
+
import { getModelsCacheDir, getShardedModelCacheDir, generateShortHash, extractAndValidateShardedArchive, } from "../../../utils/index.js";
|
|
4
4
|
import { promises as fsPromises } from "bare-fs";
|
|
5
5
|
import path from "bare-path";
|
|
6
6
|
import { downloadModelFromHttp } from "./http.js";
|
|
@@ -8,6 +8,10 @@ import { downloadModelFromHyperdrive } from "./hyperdrive.js";
|
|
|
8
8
|
import { ModelLoadFailedError, ModelNotFoundError, SeedingNotSupportedError, } from "../../../../utils/errors-server.js";
|
|
9
9
|
import { getServerLogger } from "../../../../logging/index.js";
|
|
10
10
|
const logger = getServerLogger();
|
|
11
|
+
function isArchivePath(filePath) {
|
|
12
|
+
const filename = path.basename(filePath).toLowerCase();
|
|
13
|
+
return SUPPORTED_ARCHIVE_EXTENSIONS.some((ext) => filename.endsWith(ext));
|
|
14
|
+
}
|
|
11
15
|
/**
|
|
12
16
|
* Resolves a local file path or cached file
|
|
13
17
|
*/
|
|
@@ -23,6 +27,14 @@ async function resolveLocalOrCachedFile(modelIdOrPath) {
|
|
|
23
27
|
try {
|
|
24
28
|
await fsPromises.access(cachedPath);
|
|
25
29
|
logger.info(`Loading cached model: ${cachedPath}`);
|
|
30
|
+
// Check if cached file is an archive
|
|
31
|
+
if (isArchivePath(cachedPath)) {
|
|
32
|
+
logger.info(`Extracting cached archive: ${cachedPath}`);
|
|
33
|
+
const archiveHash = generateShortHash(cachedPath);
|
|
34
|
+
const extractDir = getShardedModelCacheDir(archiveHash);
|
|
35
|
+
const extractedPath = await extractAndValidateShardedArchive(cachedPath, extractDir);
|
|
36
|
+
return extractedPath;
|
|
37
|
+
}
|
|
26
38
|
return cachedPath;
|
|
27
39
|
}
|
|
28
40
|
catch {
|
|
@@ -30,6 +42,14 @@ async function resolveLocalOrCachedFile(modelIdOrPath) {
|
|
|
30
42
|
try {
|
|
31
43
|
await fsPromises.access(modelIdOrPath);
|
|
32
44
|
logger.info(`Loading local file: ${modelIdOrPath}`);
|
|
45
|
+
// Check if local file is an archive
|
|
46
|
+
if (isArchivePath(modelIdOrPath)) {
|
|
47
|
+
logger.info(`Extracting local archive: ${modelIdOrPath}`);
|
|
48
|
+
const archiveHash = generateShortHash(modelIdOrPath);
|
|
49
|
+
const extractDir = getShardedModelCacheDir(archiveHash);
|
|
50
|
+
const extractedPath = await extractAndValidateShardedArchive(modelIdOrPath, extractDir);
|
|
51
|
+
return extractedPath;
|
|
52
|
+
}
|
|
33
53
|
return modelIdOrPath;
|
|
34
54
|
}
|
|
35
55
|
catch {
|
|
@@ -70,7 +90,16 @@ export async function resolveModelPath(modelSrc, progressCallback, seed) {
|
|
|
70
90
|
}
|
|
71
91
|
else if (actualModelSrc.includes("/") || actualModelSrc.includes("\\")) {
|
|
72
92
|
// Handle file paths (absolute or relative with slashes)
|
|
73
|
-
|
|
93
|
+
// Check if it's a local archive file
|
|
94
|
+
if (isArchivePath(actualModelSrc)) {
|
|
95
|
+
logger.info(`Extracting local archive: ${actualModelSrc}`);
|
|
96
|
+
const archiveHash = generateShortHash(actualModelSrc);
|
|
97
|
+
const extractDir = getShardedModelCacheDir(archiveHash);
|
|
98
|
+
actualPath = await extractAndValidateShardedArchive(actualModelSrc, extractDir);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
actualPath = actualModelSrc;
|
|
102
|
+
}
|
|
74
103
|
}
|
|
75
104
|
else {
|
|
76
105
|
// Handle local files and cached files
|
|
@@ -11,11 +11,14 @@ export async function handleLoadModelDelegated(request, progressCallback) {
|
|
|
11
11
|
throw new ModelLoadFailedError("Delegate information is required for delegated load model");
|
|
12
12
|
}
|
|
13
13
|
const { delegate } = request;
|
|
14
|
-
const { topic, providerPublicKey, timeout, fallbackToLocal } = delegate;
|
|
14
|
+
const { topic, providerPublicKey, timeout, fallbackToLocal, forceNewConnection, } = delegate;
|
|
15
15
|
try {
|
|
16
|
-
logger.info(`📤 Sending delegated loadModel request to provider: ${providerPublicKey}${timeout ? `, timeout: ${timeout}ms` : ""}`);
|
|
16
|
+
logger.info(`📤 Sending delegated loadModel request to provider: ${providerPublicKey}${timeout ? `, timeout: ${timeout}ms` : ""}${forceNewConnection ? " (forcing new connection)" : ""}`);
|
|
17
17
|
// Create RPC instance for this HyperSwarm peer
|
|
18
|
-
const rpc = await getRPC(topic, providerPublicKey, {
|
|
18
|
+
const rpc = await getRPC(topic, providerPublicKey, {
|
|
19
|
+
timeout,
|
|
20
|
+
forceNewConnection,
|
|
21
|
+
});
|
|
19
22
|
// Strip out the delegate field to avoid infinite delegation
|
|
20
23
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
21
24
|
const { delegate: _, ...providerRequest } = request;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ocr } from "../../bare/addons/onnx-ocr/index.js";
|
|
2
|
+
import { getServerLogger } from "../../../logging/index.js";
|
|
3
|
+
const logger = getServerLogger();
|
|
4
|
+
export async function* handleOCRStream(request) {
|
|
5
|
+
const { modelId, image, options } = request;
|
|
6
|
+
try {
|
|
7
|
+
// Stream OCR results in real-time
|
|
8
|
+
for await (const result of ocr({
|
|
9
|
+
image,
|
|
10
|
+
modelId,
|
|
11
|
+
options,
|
|
12
|
+
})) {
|
|
13
|
+
if (result.blocks && result.blocks.length > 0) {
|
|
14
|
+
yield {
|
|
15
|
+
type: "ocrStream",
|
|
16
|
+
blocks: result.blocks,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (result.stats) {
|
|
20
|
+
yield {
|
|
21
|
+
type: "ocrStream",
|
|
22
|
+
stats: result.stats,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Signal completion
|
|
27
|
+
yield {
|
|
28
|
+
type: "ocrStream",
|
|
29
|
+
done: true,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
logger.error("Error during OCR:", error);
|
|
34
|
+
yield {
|
|
35
|
+
type: "ocrStream",
|
|
36
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|