@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.
Files changed (259) hide show
  1. package/README.md +48 -11
  2. package/bare-imports.json +35 -0
  3. package/dist/client/api/cancel.d.ts +12 -3
  4. package/dist/client/api/cancel.js +12 -3
  5. package/dist/client/api/get-model-info.d.ts +2 -2
  6. package/dist/client/api/get-model-info.js +2 -2
  7. package/dist/client/api/index.d.ts +2 -1
  8. package/dist/client/api/index.js +2 -1
  9. package/dist/client/api/load-model.js +6 -1
  10. package/dist/client/api/ocr.d.ts +31 -0
  11. package/dist/client/api/ocr.js +88 -0
  12. package/dist/client/api/rag.d.ts +215 -20
  13. package/dist/client/api/rag.js +382 -23
  14. package/dist/client/api/translate.js +10 -11
  15. package/dist/client/api/unload-model.js +1 -1
  16. package/dist/client/init-hooks.d.ts +4 -3
  17. package/dist/client/init-hooks.js +40 -31
  18. package/dist/client/rpc/bare-client.d.ts +1 -178
  19. package/dist/client/rpc/bare-client.js +42 -9
  20. package/dist/client/rpc/expo-rpc-client.js +57 -2
  21. package/dist/client/rpc/node-rpc-client.js +5 -1
  22. package/dist/client/rpc/rpc-client.d.ts +1 -1
  23. package/dist/client/rpc/rpc-client.js +11 -57
  24. package/dist/examples/config/blind-relay/blind-relay.config.d.ts +5 -0
  25. package/dist/examples/config/blind-relay/blind-relay.config.js +7 -0
  26. package/dist/examples/config/default/default.config.d.ts +8 -0
  27. package/dist/examples/config/default/default.config.js +6 -0
  28. package/dist/examples/default-config-usage.d.ts +17 -0
  29. package/dist/examples/default-config-usage.js +47 -0
  30. package/dist/examples/delegated-inference/consumer.js +1 -0
  31. package/dist/examples/download-with-blind-relays.js +4 -3
  32. package/dist/examples/embed-hyperdrive.js +4 -0
  33. package/dist/examples/kv-cache-custom-key.js +16 -1
  34. package/dist/examples/llamacpp-sharded.js +12 -3
  35. package/dist/examples/ocr-fasttext.d.ts +2 -0
  36. package/dist/examples/ocr-fasttext.js +52 -0
  37. package/dist/examples/quickstart.d.ts +2 -0
  38. package/dist/examples/quickstart.js +28 -0
  39. package/dist/examples/rag/rag-hyperdb/cancellation.d.ts +2 -0
  40. package/dist/examples/rag/rag-hyperdb/cancellation.js +75 -0
  41. package/dist/examples/rag/rag-hyperdb/ingest.d.ts +2 -0
  42. package/dist/examples/rag/{rag-hyperdb.js → rag-hyperdb/ingest.js} +10 -4
  43. package/dist/examples/rag/rag-hyperdb/pipeline.d.ts +2 -0
  44. package/dist/examples/rag/rag-hyperdb/pipeline.js +74 -0
  45. package/dist/examples/rag/rag-hyperdb/workspaces.d.ts +2 -0
  46. package/dist/examples/rag/rag-hyperdb/workspaces.js +178 -0
  47. package/dist/examples/translation/translation-bergamot-batch.d.ts +2 -0
  48. package/dist/examples/translation/translation-bergamot-batch.js +49 -0
  49. package/dist/examples/translation/translation-bergamot.d.ts +2 -0
  50. package/dist/examples/translation/translation-bergamot.js +39 -0
  51. package/dist/examples/translation/translation-indic.js +1 -0
  52. package/dist/examples/translation/translation-opus.js +1 -0
  53. package/dist/examples/translation/translation-stream.js +1 -0
  54. package/dist/expo/plugins/withMobileBundle.js +29 -10
  55. package/dist/index.d.ts +3 -2
  56. package/dist/index.js +3 -1
  57. package/dist/logging/addon.js +23 -7
  58. package/dist/logging/base-logger.js +2 -2
  59. package/dist/logging/client-logger.js +15 -4
  60. package/dist/logging/index.d.ts +1 -0
  61. package/dist/logging/index.js +1 -0
  62. package/dist/logging/logger.js +11 -3
  63. package/dist/logging/namespaces.d.ts +1 -0
  64. package/dist/logging/namespaces.js +1 -0
  65. package/dist/logging/registry.d.ts +5 -0
  66. package/dist/logging/registry.js +17 -5
  67. package/dist/logging/transport.js +2 -2
  68. package/dist/logging/utils.d.ts +2 -0
  69. package/dist/logging/utils.js +62 -0
  70. package/dist/models/hyperdrive/models.d.ts +1096 -178
  71. package/dist/models/hyperdrive/models.js +1476 -507
  72. package/dist/schemas/archive.d.ts +9 -0
  73. package/dist/schemas/archive.js +17 -0
  74. package/dist/schemas/cancel.d.ts +6 -0
  75. package/dist/schemas/cancel.js +5 -0
  76. package/dist/schemas/common.d.ts +582 -77
  77. package/dist/schemas/common.js +6 -1
  78. package/dist/schemas/delegate.d.ts +1 -0
  79. package/dist/schemas/delegate.js +1 -0
  80. package/dist/schemas/download-asset.d.ts +21 -9
  81. package/dist/schemas/get-model-info.d.ts +12 -10
  82. package/dist/schemas/get-model-info.js +9 -1
  83. package/dist/schemas/index.d.ts +6 -2
  84. package/dist/schemas/index.js +9 -2
  85. package/dist/schemas/llamacpp-config.d.ts +112 -8
  86. package/dist/schemas/llamacpp-config.js +40 -8
  87. package/dist/schemas/load-model.d.ts +2993 -525
  88. package/dist/schemas/load-model.js +88 -33
  89. package/dist/schemas/logging-stream.d.ts +2 -2
  90. package/dist/schemas/model-src-utils.d.ts +38 -16
  91. package/dist/schemas/model-src-utils.js +4 -8
  92. package/dist/schemas/model-types.d.ts +154 -0
  93. package/dist/schemas/model-types.js +140 -0
  94. package/dist/schemas/ocr.d.ts +88 -0
  95. package/dist/schemas/ocr.js +52 -0
  96. package/dist/schemas/rag.d.ts +183 -52
  97. package/dist/schemas/rag.js +148 -23
  98. package/dist/schemas/reload-config.d.ts +20 -8
  99. package/dist/schemas/reload-config.js +7 -5
  100. package/dist/schemas/runtime-context.d.ts +19 -0
  101. package/dist/schemas/runtime-context.js +7 -0
  102. package/dist/schemas/sdk-config.d.ts +697 -2
  103. package/dist/schemas/sdk-config.js +99 -1
  104. package/dist/schemas/sdk-errors-client.d.ts +1 -0
  105. package/dist/schemas/sdk-errors-client.js +5 -0
  106. package/dist/schemas/sdk-errors-server.d.ts +14 -0
  107. package/dist/schemas/sdk-errors-server.js +70 -0
  108. package/dist/schemas/shard.d.ts +21 -0
  109. package/dist/schemas/shard.js +17 -0
  110. package/dist/schemas/translate.d.ts +68 -23
  111. package/dist/schemas/translate.js +37 -11
  112. package/dist/schemas/translation-config.d.ts +272 -29
  113. package/dist/schemas/translation-config.js +63 -6
  114. package/dist/scripts/run-unit-tests.d.ts +2 -0
  115. package/dist/scripts/run-unit-tests.js +17 -0
  116. package/dist/server/bare/addons/llamacpp-completion/cache-logger.d.ts +6 -0
  117. package/dist/server/bare/addons/llamacpp-completion/cache-logger.js +34 -0
  118. package/dist/server/bare/addons/{llamacpp → llamacpp-completion}/create-model.d.ts +1 -5
  119. package/dist/server/bare/addons/{llamacpp → llamacpp-completion}/create-model.js +3 -26
  120. package/dist/server/bare/addons/{llamacpp → llamacpp-completion}/handlers/completion-stream.js +26 -7
  121. package/dist/server/bare/addons/llamacpp-completion/index.d.ts +4 -0
  122. package/dist/server/bare/addons/llamacpp-completion/index.js +3 -0
  123. package/dist/server/bare/addons/{llamacpp → llamacpp-completion}/kv-cache-utils.d.ts +4 -1
  124. package/dist/server/bare/addons/{llamacpp → llamacpp-completion}/kv-cache-utils.js +43 -10
  125. package/dist/server/bare/addons/llamacpp-embedding/create-model.d.ts +8 -0
  126. package/dist/server/bare/addons/llamacpp-embedding/create-model.js +38 -0
  127. package/dist/server/bare/addons/llamacpp-embedding/index.d.ts +3 -0
  128. package/dist/server/bare/addons/llamacpp-embedding/index.js +2 -0
  129. package/dist/server/bare/addons/load-model.js +28 -24
  130. package/dist/server/bare/addons/{translation → nmtcpp-translation}/create-model.js +23 -8
  131. package/dist/server/bare/addons/{translation → nmtcpp-translation}/handlers/translate.js +35 -11
  132. package/dist/server/bare/addons/onnx-ocr/create-model.d.ts +10 -0
  133. package/dist/server/bare/addons/onnx-ocr/create-model.js +39 -0
  134. package/dist/server/bare/addons/onnx-ocr/handlers/ocr-stream.d.ts +10 -0
  135. package/dist/server/bare/addons/onnx-ocr/handlers/ocr-stream.js +109 -0
  136. package/dist/server/bare/addons/onnx-ocr/index.d.ts +3 -0
  137. package/dist/server/bare/addons/onnx-ocr/index.js +2 -0
  138. package/dist/server/bare/addons/{whispercpp → whispercpp-transcription}/create-model.js +2 -1
  139. package/dist/server/bare/delegate-rpc-client.js +19 -1
  140. package/dist/server/bare/rag-hyperdb/handlers/chunk.d.ts +3 -0
  141. package/dist/server/bare/rag-hyperdb/handlers/chunk.js +7 -0
  142. package/dist/server/bare/rag-hyperdb/handlers/close-workspace.d.ts +3 -0
  143. package/dist/server/bare/rag-hyperdb/handlers/close-workspace.js +9 -0
  144. package/dist/server/bare/rag-hyperdb/handlers/delete-embeddings.d.ts +1 -1
  145. package/dist/server/bare/rag-hyperdb/handlers/delete-embeddings.js +9 -10
  146. package/dist/server/bare/rag-hyperdb/handlers/delete-workspace.d.ts +3 -0
  147. package/dist/server/bare/rag-hyperdb/handlers/delete-workspace.js +14 -0
  148. package/dist/server/bare/rag-hyperdb/handlers/ingest.d.ts +9 -0
  149. package/dist/server/bare/rag-hyperdb/handlers/ingest.js +18 -0
  150. package/dist/server/bare/rag-hyperdb/handlers/list-workspaces.d.ts +3 -0
  151. package/dist/server/bare/rag-hyperdb/handlers/list-workspaces.js +4 -0
  152. package/dist/server/bare/rag-hyperdb/handlers/reindex.d.ts +9 -0
  153. package/dist/server/bare/rag-hyperdb/handlers/reindex.js +16 -0
  154. package/dist/server/bare/rag-hyperdb/handlers/save-embeddings.d.ts +7 -4
  155. package/dist/server/bare/rag-hyperdb/handlers/save-embeddings.js +13 -14
  156. package/dist/server/bare/rag-hyperdb/handlers/search.js +10 -10
  157. package/dist/server/bare/rag-hyperdb/index.d.ts +8 -1
  158. package/dist/server/bare/rag-hyperdb/index.js +8 -1
  159. package/dist/server/bare/rag-hyperdb/rag-operation-manager.d.ts +7 -0
  160. package/dist/server/bare/rag-hyperdb/rag-operation-manager.js +39 -0
  161. package/dist/server/bare/rag-hyperdb/rag-workspace-manager.d.ts +15 -0
  162. package/dist/server/bare/rag-hyperdb/rag-workspace-manager.js +135 -0
  163. package/dist/server/bare/registry/config-registry.js +11 -0
  164. package/dist/server/bare/registry/logging-stream-registry.js +3 -3
  165. package/dist/server/bare/registry/model-config-registry.d.ts +4 -0
  166. package/dist/server/bare/registry/model-config-registry.js +15 -0
  167. package/dist/server/bare/registry/model-config-utils.d.ts +15 -0
  168. package/dist/server/bare/registry/model-config-utils.js +110 -0
  169. package/dist/server/bare/registry/model-registry.d.ts +6 -29
  170. package/dist/server/bare/registry/runtime-context-registry.d.ts +7 -0
  171. package/dist/server/bare/registry/runtime-context-registry.js +26 -0
  172. package/dist/server/bare/utils/loader-adapter.d.ts +7 -0
  173. package/dist/server/bare/utils/loader-adapter.js +9 -0
  174. package/dist/server/error-handlers.js +4 -2
  175. package/dist/server/rpc/handle-request.js +91 -9
  176. package/dist/server/rpc/handlers/cancelHandler.js +10 -1
  177. package/dist/server/rpc/handlers/completion-stream.js +1 -1
  178. package/dist/server/rpc/handlers/delete-cache.js +1 -1
  179. package/dist/server/rpc/handlers/embed.js +1 -1
  180. package/dist/server/rpc/handlers/get-model-info.d.ts +1 -1
  181. package/dist/server/rpc/handlers/get-model-info.js +22 -10
  182. package/dist/server/rpc/handlers/index.d.ts +4 -1
  183. package/dist/server/rpc/handlers/index.js +2 -0
  184. package/dist/server/rpc/handlers/load-model/download-manager.d.ts +11 -11
  185. package/dist/server/rpc/handlers/load-model/download-manager.js +36 -26
  186. package/dist/server/rpc/handlers/{load-model.d.ts → load-model/handler.d.ts} +2 -2
  187. package/dist/server/rpc/handlers/load-model/handler.js +179 -0
  188. package/dist/server/rpc/handlers/load-model/http.js +342 -33
  189. package/dist/server/rpc/handlers/load-model/hyperdrive.js +5 -3
  190. package/dist/server/rpc/handlers/load-model/index.d.ts +2 -2
  191. package/dist/server/rpc/handlers/load-model/index.js +2 -2
  192. package/dist/server/rpc/handlers/load-model/resolve.js +32 -3
  193. package/dist/server/rpc/handlers/load-model-delegated.js +6 -3
  194. package/dist/server/rpc/handlers/ocr-stream.d.ts +3 -0
  195. package/dist/server/rpc/handlers/ocr-stream.js +39 -0
  196. package/dist/server/rpc/handlers/rag.d.ts +2 -2
  197. package/dist/server/rpc/handlers/rag.js +107 -43
  198. package/dist/server/rpc/handlers/text-to-speech.js +1 -1
  199. package/dist/server/rpc/handlers/transcribe-stream.js +1 -1
  200. package/dist/server/rpc/handlers/translate.js +6 -15
  201. package/dist/server/utils/archive.d.ts +3 -0
  202. package/dist/server/utils/archive.js +171 -0
  203. package/dist/server/utils/audio/decoder.js +2 -2
  204. package/dist/server/utils/cache.d.ts +5 -5
  205. package/dist/server/utils/cache.js +6 -6
  206. package/dist/server/utils/config-transform.d.ts +2 -2
  207. package/dist/server/utils/config-transform.js +8 -6
  208. package/dist/server/utils/formatting.d.ts +13 -0
  209. package/dist/server/utils/formatting.js +26 -0
  210. package/dist/server/utils/gguf-tensor-extractor.d.ts +15 -0
  211. package/dist/server/utils/gguf-tensor-extractor.js +314 -0
  212. package/dist/server/utils/index.d.ts +3 -1
  213. package/dist/server/utils/index.js +3 -1
  214. package/dist/server/utils/shard-utils.d.ts +42 -16
  215. package/dist/server/utils/shard-utils.js +95 -7
  216. package/dist/server/worker.js +8 -3
  217. package/dist/utils/errors-base.d.ts +1 -1
  218. package/dist/utils/errors-base.js +2 -2
  219. package/dist/utils/errors-client.d.ts +12 -0
  220. package/dist/utils/errors-client.js +20 -0
  221. package/dist/utils/errors-server.d.ts +33 -0
  222. package/dist/utils/errors-server.js +55 -0
  223. package/package.json +42 -17
  224. package/dist/client/rpc/bare-rpc-client.d.ts +0 -4
  225. package/dist/client/rpc/bare-rpc-client.js +0 -94
  226. package/dist/examples/config/config-path.d.ts +0 -8
  227. package/dist/examples/config/config-path.js +0 -13
  228. package/dist/examples/rag/rag-hyperdb-workspaces.d.ts +0 -2
  229. package/dist/examples/rag/rag-hyperdb-workspaces.js +0 -106
  230. package/dist/examples/rag/rag-hyperdb.d.ts +0 -2
  231. package/dist/server/bare/addons/llamacpp/index.d.ts +0 -4
  232. package/dist/server/bare/addons/llamacpp/index.js +0 -3
  233. package/dist/server/bare/rag-hyperdb/rag-manager.d.ts +0 -5
  234. package/dist/server/bare/rag-hyperdb/rag-manager.js +0 -66
  235. package/dist/server/rpc/handlers/load-model.js +0 -111
  236. package/dist/server/utils/hash.d.ts +0 -5
  237. package/dist/server/utils/hash.js +0 -11
  238. package/dist/utils/getProcessSafe.d.ts +0 -7
  239. package/dist/utils/getProcessSafe.js +0 -17
  240. package/dist/utils/runtime.d.ts +0 -30
  241. package/dist/utils/runtime.js +0 -48
  242. /package/dist/server/bare/addons/{llamacpp → llamacpp-completion}/handlers/completion-stream.d.ts +0 -0
  243. /package/dist/server/bare/addons/{llamacpp → llamacpp-embedding}/handlers/embed.d.ts +0 -0
  244. /package/dist/server/bare/addons/{llamacpp → llamacpp-embedding}/handlers/embed.js +0 -0
  245. /package/dist/server/bare/addons/{translation → nmtcpp-translation}/create-model.d.ts +0 -0
  246. /package/dist/server/bare/addons/{translation → nmtcpp-translation}/handlers/translate.d.ts +0 -0
  247. /package/dist/server/bare/addons/{translation → nmtcpp-translation}/index.d.ts +0 -0
  248. /package/dist/server/bare/addons/{translation → nmtcpp-translation}/index.js +0 -0
  249. /package/dist/server/bare/addons/{tts → onnx-tts}/create-model.d.ts +0 -0
  250. /package/dist/server/bare/addons/{tts → onnx-tts}/create-model.js +0 -0
  251. /package/dist/server/bare/addons/{tts → onnx-tts}/handlers/text-to-speech.d.ts +0 -0
  252. /package/dist/server/bare/addons/{tts → onnx-tts}/handlers/text-to-speech.js +0 -0
  253. /package/dist/server/bare/addons/{tts → onnx-tts}/index.d.ts +0 -0
  254. /package/dist/server/bare/addons/{tts → onnx-tts}/index.js +0 -0
  255. /package/dist/server/bare/addons/{whispercpp → whispercpp-transcription}/create-model.d.ts +0 -0
  256. /package/dist/server/bare/addons/{whispercpp → whispercpp-transcription}/handlers/transcribe-stream.d.ts +0 -0
  257. /package/dist/server/bare/addons/{whispercpp → whispercpp-transcription}/handlers/transcribe-stream.js +0 -0
  258. /package/dist/server/bare/addons/{whispercpp → whispercpp-transcription}/index.d.ts +0 -0
  259. /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 { getModelsCacheDir, generateShortHash } from "../../../utils/index.js";
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
- // Get expected size from HEAD request
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 (error) {
33
- logger.warn(`⚠️ Could not get expected file size from HEAD request, will re-download: ${String(error)}`);
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
- // File doesn't exist
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
- // Make the fetch request
73
- const response = await fetch(url, {
74
- method: "GET",
75
- headers,
76
- ...(signal && { signal }),
77
- // bare-fetch automatically follows redirects
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
- const freshResponse = await fetch(url, {
135
+ response = await fetch(url, {
102
136
  method: "GET",
103
137
  headers: {
104
- "User-Agent": "qvac-sdk/1.0",
138
+ "User-Agent": "qvac-sdk",
105
139
  },
106
140
  ...(signal && { signal }),
107
141
  });
108
- if (!freshResponse.ok) {
109
- throw new HTTPError(freshResponse.status, freshResponse.statusText);
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: totalBytes > 0 ? (downloadedBytes / totalBytes) * 100 : 0,
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: totalBytes > 0 ? (downloadedBytes / totalBytes) * 100 : 0,
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: totalBytes > 0 ? (downloadedBytes / totalBytes) * 100 : 0,
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
- // Ignore stat errors, file exists but might not be readable
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
- // Ignore stat errors
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
- // Ignore cleanup errors
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: totalBytes > 0 ? (cappedDownloaded / totalBytes) * 100 : 0,
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: overallTotal > 0 ? (overallDownloaded / overallTotal) * 100 : 0,
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 "../load-model";
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 "../load-model.js";
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
- actualPath = actualModelSrc;
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, { timeout });
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,3 @@
1
+ import type { OCRStreamRequest, OCRStreamResponse } from "../../../schemas/index";
2
+ export declare function handleOCRStream(request: OCRStreamRequest): AsyncGenerator<OCRStreamResponse>;
3
+ //# sourceMappingURL=ocr-stream.d.ts.map
@@ -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
+ }