vectra 0.12.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +92 -100
  3. package/bin/vectra.js +3 -0
  4. package/lib/BrowserWebFetcher.d.ts +75 -0
  5. package/lib/BrowserWebFetcher.d.ts.map +1 -0
  6. package/lib/BrowserWebFetcher.js +290 -0
  7. package/lib/BrowserWebFetcher.js.map +1 -0
  8. package/lib/FileFetcher.d.ts +5 -0
  9. package/lib/FileFetcher.d.ts.map +1 -0
  10. package/lib/FileFetcher.js +89 -0
  11. package/lib/FileFetcher.js.map +1 -0
  12. package/lib/FileFetcher.spec.d.ts +2 -0
  13. package/lib/FileFetcher.spec.d.ts.map +1 -0
  14. package/lib/FileFetcher.spec.js +244 -0
  15. package/lib/FileFetcher.spec.js.map +1 -0
  16. package/lib/FolderWatcher.d.ts +91 -0
  17. package/lib/FolderWatcher.d.ts.map +1 -0
  18. package/lib/FolderWatcher.js +304 -0
  19. package/lib/FolderWatcher.js.map +1 -0
  20. package/lib/FolderWatcher.spec.d.ts +2 -0
  21. package/lib/FolderWatcher.spec.d.ts.map +1 -0
  22. package/lib/FolderWatcher.spec.js +308 -0
  23. package/lib/FolderWatcher.spec.js.map +1 -0
  24. package/lib/GPT3Tokenizer.d.ts +9 -0
  25. package/lib/GPT3Tokenizer.spec.d.ts +2 -0
  26. package/lib/GPT3Tokenizer.spec.d.ts.map +1 -0
  27. package/lib/GPT3Tokenizer.spec.js +45 -0
  28. package/lib/GPT3Tokenizer.spec.js.map +1 -0
  29. package/lib/ItemSelector.d.ts +41 -0
  30. package/lib/ItemSelector.d.ts.map +1 -0
  31. package/lib/ItemSelector.js +179 -0
  32. package/lib/ItemSelector.js.map +1 -0
  33. package/lib/ItemSelector.spec.d.ts +2 -0
  34. package/lib/ItemSelector.spec.d.ts.map +1 -0
  35. package/lib/ItemSelector.spec.js +204 -0
  36. package/lib/ItemSelector.spec.js.map +1 -0
  37. package/lib/LocalDocument.d.ts +54 -0
  38. package/lib/LocalDocument.d.ts.map +1 -1
  39. package/lib/LocalDocument.js +116 -0
  40. package/lib/LocalDocument.js.map +1 -0
  41. package/lib/LocalDocument.spec.d.ts +2 -0
  42. package/lib/LocalDocument.spec.d.ts.map +1 -0
  43. package/lib/LocalDocument.spec.js +214 -0
  44. package/lib/LocalDocument.spec.js.map +1 -0
  45. package/lib/LocalDocumentIndex.d.ts +152 -0
  46. package/lib/LocalDocumentIndex.d.ts.map +1 -1
  47. package/lib/LocalDocumentIndex.js +420 -0
  48. package/lib/LocalDocumentIndex.js.map +1 -0
  49. package/lib/LocalDocumentIndex.spec.d.ts +2 -0
  50. package/lib/LocalDocumentIndex.spec.d.ts.map +1 -0
  51. package/lib/LocalDocumentIndex.spec.js +494 -0
  52. package/lib/LocalDocumentIndex.spec.js.map +1 -0
  53. package/lib/LocalDocumentResult.d.ts +66 -0
  54. package/lib/LocalDocumentResult.d.ts.map +1 -1
  55. package/lib/LocalDocumentResult.js +376 -0
  56. package/lib/LocalDocumentResult.js.map +1 -0
  57. package/lib/LocalDocumentResult.spec.d.ts +2 -0
  58. package/lib/LocalDocumentResult.spec.d.ts.map +1 -0
  59. package/lib/LocalDocumentResult.spec.js +373 -0
  60. package/lib/LocalDocumentResult.spec.js.map +1 -0
  61. package/lib/LocalEmbeddings.d.ts +59 -0
  62. package/lib/LocalEmbeddings.d.ts.map +1 -0
  63. package/lib/LocalEmbeddings.js +101 -0
  64. package/lib/LocalEmbeddings.js.map +1 -0
  65. package/lib/LocalEmbeddings.spec.d.ts +2 -0
  66. package/lib/LocalEmbeddings.spec.d.ts.map +1 -0
  67. package/lib/LocalEmbeddings.spec.js +155 -0
  68. package/lib/LocalEmbeddings.spec.js.map +1 -0
  69. package/lib/LocalIndex.d.ts +159 -0
  70. package/lib/LocalIndex.d.ts.map +1 -1
  71. package/lib/LocalIndex.js +519 -0
  72. package/lib/LocalIndex.js.map +1 -0
  73. package/lib/LocalIndex.spec.d.ts +2 -0
  74. package/lib/LocalIndex.spec.js +611 -9
  75. package/lib/LocalIndex.spec.js.map +1 -1
  76. package/lib/OpenAIEmbeddings.d.ts +124 -0
  77. package/lib/OpenAIEmbeddings.d.ts.map +1 -0
  78. package/lib/OpenAIEmbeddings.js +166 -0
  79. package/lib/OpenAIEmbeddings.js.map +1 -0
  80. package/lib/OpenAIEmbeddings.spec.d.ts +2 -0
  81. package/lib/OpenAIEmbeddings.spec.d.ts.map +1 -0
  82. package/lib/OpenAIEmbeddings.spec.js +298 -0
  83. package/lib/OpenAIEmbeddings.spec.js.map +1 -0
  84. package/lib/TextSplitter.d.ts +21 -0
  85. package/lib/TextSplitter.d.ts.map +1 -1
  86. package/lib/TextSplitter.js +500 -0
  87. package/lib/TextSplitter.js.map +1 -0
  88. package/lib/TextSplitter.spec.d.ts +2 -0
  89. package/lib/TextSplitter.spec.d.ts.map +1 -0
  90. package/lib/TextSplitter.spec.js +337 -0
  91. package/lib/TextSplitter.spec.js.map +1 -0
  92. package/lib/TransformersEmbeddings.d.ts +121 -0
  93. package/lib/TransformersEmbeddings.d.ts.map +1 -0
  94. package/lib/TransformersEmbeddings.js +176 -0
  95. package/lib/TransformersEmbeddings.js.map +1 -0
  96. package/lib/TransformersEmbeddings.spec.d.ts +2 -0
  97. package/lib/TransformersEmbeddings.spec.d.ts.map +1 -0
  98. package/lib/TransformersEmbeddings.spec.js +198 -0
  99. package/lib/TransformersEmbeddings.spec.js.map +1 -0
  100. package/lib/TransformersTokenizer.d.ts +33 -0
  101. package/lib/TransformersTokenizer.d.ts.map +1 -0
  102. package/lib/TransformersTokenizer.js +44 -0
  103. package/lib/TransformersTokenizer.js.map +1 -0
  104. package/lib/TransformersTokenizer.spec.d.ts +2 -0
  105. package/lib/TransformersTokenizer.spec.d.ts.map +1 -0
  106. package/lib/TransformersTokenizer.spec.js +112 -0
  107. package/lib/TransformersTokenizer.spec.js.map +1 -0
  108. package/lib/WebFetcher.d.ts +14 -0
  109. package/lib/WebFetcher.d.ts.map +1 -0
  110. package/lib/WebFetcher.js +238 -0
  111. package/lib/WebFetcher.js.map +1 -0
  112. package/lib/WebFetcher.spec.d.ts +2 -0
  113. package/lib/WebFetcher.spec.d.ts.map +1 -0
  114. package/lib/WebFetcher.spec.js +263 -0
  115. package/lib/WebFetcher.spec.js.map +1 -0
  116. package/lib/browser.d.ts +30 -0
  117. package/lib/browser.d.ts.map +1 -0
  118. package/lib/browser.js +52 -0
  119. package/lib/browser.js.map +1 -0
  120. package/lib/codecs/IndexCodec.d.ts +37 -0
  121. package/lib/codecs/IndexCodec.d.ts.map +1 -0
  122. package/lib/codecs/IndexCodec.js +3 -0
  123. package/lib/codecs/IndexCodec.js.map +1 -0
  124. package/lib/codecs/JsonCodec.d.ts +19 -0
  125. package/lib/codecs/JsonCodec.d.ts.map +1 -0
  126. package/lib/codecs/JsonCodec.js +35 -0
  127. package/lib/codecs/JsonCodec.js.map +1 -0
  128. package/lib/codecs/JsonCodec.spec.d.ts +2 -0
  129. package/lib/codecs/JsonCodec.spec.d.ts.map +1 -0
  130. package/lib/codecs/JsonCodec.spec.js +66 -0
  131. package/lib/codecs/JsonCodec.spec.js.map +1 -0
  132. package/lib/codecs/LocalIndex.protobuf.spec.d.ts +2 -0
  133. package/lib/codecs/LocalIndex.protobuf.spec.d.ts.map +1 -0
  134. package/lib/codecs/LocalIndex.protobuf.spec.js +108 -0
  135. package/lib/codecs/LocalIndex.protobuf.spec.js.map +1 -0
  136. package/lib/codecs/ProtobufCodec.d.ts +20 -0
  137. package/lib/codecs/ProtobufCodec.d.ts.map +1 -0
  138. package/lib/codecs/ProtobufCodec.js +225 -0
  139. package/lib/codecs/ProtobufCodec.js.map +1 -0
  140. package/lib/codecs/ProtobufCodec.spec.d.ts +2 -0
  141. package/lib/codecs/ProtobufCodec.spec.d.ts.map +1 -0
  142. package/lib/codecs/ProtobufCodec.spec.js +155 -0
  143. package/lib/codecs/ProtobufCodec.spec.js.map +1 -0
  144. package/lib/codecs/index.d.ts +5 -0
  145. package/lib/codecs/index.d.ts.map +1 -0
  146. package/lib/codecs/index.js +21 -0
  147. package/lib/codecs/index.js.map +1 -0
  148. package/lib/codecs/migrateIndex.d.ts +24 -0
  149. package/lib/codecs/migrateIndex.d.ts.map +1 -0
  150. package/lib/codecs/migrateIndex.js +119 -0
  151. package/lib/codecs/migrateIndex.js.map +1 -0
  152. package/lib/codecs/migrateIndex.spec.d.ts +2 -0
  153. package/lib/codecs/migrateIndex.spec.d.ts.map +1 -0
  154. package/lib/codecs/migrateIndex.spec.js +151 -0
  155. package/lib/codecs/migrateIndex.spec.js.map +1 -0
  156. package/lib/codecs/schemas/index.proto +34 -0
  157. package/lib/index.d.ts +20 -0
  158. package/lib/index.d.ts.map +1 -1
  159. package/lib/index.js +36 -0
  160. package/lib/index.js.map +1 -0
  161. package/lib/internals/Colorize.d.ts +14 -0
  162. package/lib/internals/Colorize.d.ts.map +1 -0
  163. package/lib/internals/Colorize.js +69 -0
  164. package/lib/internals/Colorize.js.map +1 -0
  165. package/lib/internals/index.d.ts +3 -0
  166. package/lib/internals/index.d.ts.map +1 -0
  167. package/lib/internals/index.js +19 -0
  168. package/lib/internals/index.js.map +1 -0
  169. package/lib/internals/types.d.ts +43 -0
  170. package/lib/internals/types.d.ts.map +1 -0
  171. package/lib/internals/types.js +3 -0
  172. package/lib/internals/types.js.map +1 -0
  173. package/lib/server/IndexManager.d.ts +78 -0
  174. package/lib/server/IndexManager.d.ts.map +1 -0
  175. package/lib/server/IndexManager.js +259 -0
  176. package/lib/server/IndexManager.js.map +1 -0
  177. package/lib/server/VectraServer.d.ts +40 -0
  178. package/lib/server/VectraServer.d.ts.map +1 -0
  179. package/lib/server/VectraServer.js +151 -0
  180. package/lib/server/VectraServer.js.map +1 -0
  181. package/lib/server/VectraServer.spec.d.ts +2 -0
  182. package/lib/server/VectraServer.spec.d.ts.map +1 -0
  183. package/lib/server/VectraServer.spec.js +322 -0
  184. package/lib/server/VectraServer.spec.js.map +1 -0
  185. package/lib/server/handlers/documentHandlers.d.ts +15 -0
  186. package/lib/server/handlers/documentHandlers.d.ts.map +1 -0
  187. package/lib/server/handlers/documentHandlers.js +95 -0
  188. package/lib/server/handlers/documentHandlers.js.map +1 -0
  189. package/lib/server/handlers/helpers.d.ts +23 -0
  190. package/lib/server/handlers/helpers.d.ts.map +1 -0
  191. package/lib/server/handlers/helpers.js +138 -0
  192. package/lib/server/handlers/helpers.js.map +1 -0
  193. package/lib/server/handlers/index.d.ts +8 -0
  194. package/lib/server/handlers/index.d.ts.map +1 -0
  195. package/lib/server/handlers/index.js +22 -0
  196. package/lib/server/handlers/index.js.map +1 -0
  197. package/lib/server/handlers/indexHandlers.d.ts +14 -0
  198. package/lib/server/handlers/indexHandlers.d.ts.map +1 -0
  199. package/lib/server/handlers/indexHandlers.js +85 -0
  200. package/lib/server/handlers/indexHandlers.js.map +1 -0
  201. package/lib/server/handlers/itemHandlers.d.ts +34 -0
  202. package/lib/server/handlers/itemHandlers.d.ts.map +1 -0
  203. package/lib/server/handlers/itemHandlers.js +166 -0
  204. package/lib/server/handlers/itemHandlers.js.map +1 -0
  205. package/lib/server/handlers/lifecycleHandlers.d.ts +11 -0
  206. package/lib/server/handlers/lifecycleHandlers.d.ts.map +1 -0
  207. package/lib/server/handlers/lifecycleHandlers.js +31 -0
  208. package/lib/server/handlers/lifecycleHandlers.js.map +1 -0
  209. package/lib/server/handlers/queryHandlers.d.ts +27 -0
  210. package/lib/server/handlers/queryHandlers.d.ts.map +1 -0
  211. package/lib/server/handlers/queryHandlers.js +135 -0
  212. package/lib/server/handlers/queryHandlers.js.map +1 -0
  213. package/lib/server/handlers/statsHandlers.d.ts +17 -0
  214. package/lib/server/handlers/statsHandlers.d.ts.map +1 -0
  215. package/lib/server/handlers/statsHandlers.js +81 -0
  216. package/lib/server/handlers/statsHandlers.js.map +1 -0
  217. package/lib/server/index.d.ts +4 -0
  218. package/lib/server/index.d.ts.map +1 -0
  219. package/lib/server/index.js +23 -0
  220. package/lib/server/index.js.map +1 -0
  221. package/lib/storage/FileStorage.d.ts +92 -0
  222. package/lib/storage/FileStorage.d.ts.map +1 -0
  223. package/lib/storage/FileStorage.js +3 -0
  224. package/lib/storage/FileStorage.js.map +1 -0
  225. package/lib/storage/FileStorageUtilities.d.ts +36 -0
  226. package/lib/storage/FileStorageUtilities.d.ts.map +1 -0
  227. package/lib/storage/FileStorageUtilities.js +91 -0
  228. package/lib/storage/FileStorageUtilities.js.map +1 -0
  229. package/lib/storage/FileStorageUtilities.spec.d.ts +2 -0
  230. package/lib/storage/FileStorageUtilities.spec.d.ts.map +1 -0
  231. package/lib/storage/FileStorageUtilities.spec.js +98 -0
  232. package/lib/storage/FileStorageUtilities.spec.js.map +1 -0
  233. package/lib/storage/FileType.d.ts +29 -0
  234. package/lib/storage/FileType.d.ts.map +1 -0
  235. package/lib/storage/FileType.js +38 -0
  236. package/lib/storage/FileType.js.map +1 -0
  237. package/lib/storage/IndexedDBStorage.d.ts +47 -0
  238. package/lib/storage/IndexedDBStorage.d.ts.map +1 -0
  239. package/lib/storage/IndexedDBStorage.js +347 -0
  240. package/lib/storage/IndexedDBStorage.js.map +1 -0
  241. package/lib/storage/LocalFileStorage.browser.d.ts +19 -0
  242. package/lib/storage/LocalFileStorage.browser.d.ts.map +1 -0
  243. package/lib/storage/LocalFileStorage.browser.js +43 -0
  244. package/lib/storage/LocalFileStorage.browser.js.map +1 -0
  245. package/lib/storage/LocalFileStorage.d.ts +23 -0
  246. package/lib/storage/LocalFileStorage.d.ts.map +1 -0
  247. package/lib/storage/LocalFileStorage.js +152 -0
  248. package/lib/storage/LocalFileStorage.js.map +1 -0
  249. package/lib/storage/LocalFileStorage.spec.d.ts +2 -0
  250. package/lib/storage/LocalFileStorage.spec.d.ts.map +1 -0
  251. package/lib/storage/LocalFileStorage.spec.js +249 -0
  252. package/lib/storage/LocalFileStorage.spec.js.map +1 -0
  253. package/lib/storage/VirtualFileStorage.d.ts +18 -0
  254. package/lib/storage/VirtualFileStorage.d.ts.map +1 -0
  255. package/lib/storage/VirtualFileStorage.js +178 -0
  256. package/lib/storage/VirtualFileStorage.js.map +1 -0
  257. package/lib/storage/VirtualFileStorage.spec.d.ts +2 -0
  258. package/lib/storage/VirtualFileStorage.spec.d.ts.map +1 -0
  259. package/lib/storage/VirtualFileStorage.spec.js +302 -0
  260. package/lib/storage/VirtualFileStorage.spec.js.map +1 -0
  261. package/lib/storage/index.d.ts +6 -0
  262. package/lib/storage/index.d.ts.map +1 -0
  263. package/lib/storage/index.js +22 -0
  264. package/lib/storage/index.js.map +1 -0
  265. package/lib/templates/templates/csharp/README.md +48 -0
  266. package/lib/templates/templates/csharp/VectraClient.cs +234 -0
  267. package/lib/templates/templates/go/README.md +71 -0
  268. package/lib/templates/templates/go/vectra_client.go +322 -0
  269. package/lib/templates/templates/java/README.md +81 -0
  270. package/lib/templates/templates/java/VectraClient.java +232 -0
  271. package/lib/templates/templates/python/README.md +37 -0
  272. package/lib/templates/templates/python/vectra_client.py +279 -0
  273. package/lib/templates/templates/rust/Cargo.toml +14 -0
  274. package/lib/templates/templates/rust/README.md +39 -0
  275. package/lib/templates/templates/rust/build.rs +4 -0
  276. package/lib/templates/templates/rust/lib.rs +284 -0
  277. package/lib/templates/templates/typescript/README.md +96 -0
  278. package/lib/templates/templates/typescript/VectraClient.ts +374 -0
  279. package/lib/templates/typescript/VectraClient.d.ts +114 -0
  280. package/lib/templates/typescript/VectraClient.d.ts.map +1 -0
  281. package/lib/templates/typescript/VectraClient.js +328 -0
  282. package/lib/templates/typescript/VectraClient.js.map +1 -0
  283. package/lib/types.d.ts +153 -0
  284. package/lib/types.d.ts.map +1 -0
  285. package/lib/types.js +3 -0
  286. package/lib/types.js.map +1 -0
  287. package/lib/utils/index.d.ts +2 -0
  288. package/lib/utils/index.d.ts.map +1 -0
  289. package/lib/utils/index.js +18 -0
  290. package/lib/utils/index.js.map +1 -0
  291. package/lib/utils/pathUtils.d.ts +40 -0
  292. package/lib/utils/pathUtils.d.ts.map +1 -0
  293. package/lib/utils/pathUtils.js +98 -0
  294. package/lib/utils/pathUtils.js.map +1 -0
  295. package/lib/vectra-cli.d.ts +2 -0
  296. package/lib/vectra-cli.d.ts.map +1 -1
  297. package/lib/vectra-cli.generate.spec.d.ts +2 -0
  298. package/lib/vectra-cli.generate.spec.d.ts.map +1 -0
  299. package/lib/vectra-cli.generate.spec.js +112 -0
  300. package/lib/vectra-cli.generate.spec.js.map +1 -0
  301. package/lib/vectra-cli.js +760 -0
  302. package/lib/vectra-cli.js.map +1 -0
  303. package/lib/vectra-cli.spec.d.ts +1 -0
  304. package/lib/vectra-cli.spec.d.ts.map +1 -0
  305. package/lib/vectra-cli.spec.js +2 -0
  306. package/lib/vectra-cli.spec.js.map +1 -0
  307. package/package.json +91 -16
  308. package/proto/vectra_service.proto +276 -0
  309. package/src/BrowserWebFetcher.ts +345 -0
  310. package/src/FileFetcher.spec.ts +234 -0
  311. package/src/FileFetcher.ts +37 -25
  312. package/src/FolderWatcher.spec.ts +288 -0
  313. package/src/FolderWatcher.ts +304 -0
  314. package/src/GPT3Tokenizer.spec.ts +50 -0
  315. package/src/ItemSelector.spec.ts +252 -0
  316. package/src/ItemSelector.ts +163 -150
  317. package/src/LocalDocument.spec.ts +211 -0
  318. package/src/LocalDocument.ts +88 -94
  319. package/src/LocalDocumentIndex.spec.ts +481 -0
  320. package/src/LocalDocumentIndex.ts +39 -40
  321. package/src/LocalDocumentResult.spec.ts +373 -0
  322. package/src/LocalDocumentResult.ts +489 -319
  323. package/src/LocalEmbeddings.spec.ts +138 -0
  324. package/src/LocalEmbeddings.ts +120 -0
  325. package/src/LocalIndex.spec.ts +808 -66
  326. package/src/LocalIndex.ts +479 -429
  327. package/src/OpenAIEmbeddings.spec.ts +354 -0
  328. package/src/OpenAIEmbeddings.ts +26 -27
  329. package/src/TextSplitter.spec.ts +342 -0
  330. package/src/TextSplitter.ts +517 -532
  331. package/src/TransformersEmbeddings.spec.ts +188 -0
  332. package/src/TransformersEmbeddings.ts +232 -0
  333. package/src/TransformersTokenizer.spec.ts +143 -0
  334. package/src/TransformersTokenizer.ts +45 -0
  335. package/src/WebFetcher.spec.ts +288 -0
  336. package/src/WebFetcher.ts +184 -186
  337. package/src/browser.ts +69 -0
  338. package/src/codecs/IndexCodec.ts +40 -0
  339. package/src/codecs/JsonCodec.spec.ts +70 -0
  340. package/src/codecs/JsonCodec.ts +37 -0
  341. package/src/codecs/LocalIndex.protobuf.spec.ts +115 -0
  342. package/src/codecs/ProtobufCodec.spec.ts +166 -0
  343. package/src/codecs/ProtobufCodec.ts +193 -0
  344. package/src/codecs/index.ts +4 -0
  345. package/src/codecs/migrateIndex.spec.ts +176 -0
  346. package/src/codecs/migrateIndex.ts +125 -0
  347. package/src/codecs/schemas/index.proto +34 -0
  348. package/src/index.ts +9 -1
  349. package/src/internals/Colorize.ts +19 -16
  350. package/src/server/IndexManager.ts +243 -0
  351. package/src/server/VectraServer.spec.ts +303 -0
  352. package/src/server/VectraServer.ts +156 -0
  353. package/src/server/handlers/documentHandlers.ts +59 -0
  354. package/src/server/handlers/helpers.ts +93 -0
  355. package/src/server/handlers/index.ts +7 -0
  356. package/src/server/handlers/indexHandlers.ts +44 -0
  357. package/src/server/handlers/itemHandlers.ts +140 -0
  358. package/src/server/handlers/lifecycleHandlers.ts +26 -0
  359. package/src/server/handlers/queryHandlers.ts +96 -0
  360. package/src/server/handlers/statsHandlers.ts +38 -0
  361. package/src/server/index.ts +3 -0
  362. package/src/storage/FileStorage.ts +105 -0
  363. package/src/storage/FileStorageUtilities.spec.ts +106 -0
  364. package/src/storage/FileStorageUtilities.ts +77 -0
  365. package/src/storage/FileType.ts +61 -0
  366. package/src/storage/IndexedDBStorage.ts +365 -0
  367. package/src/storage/LocalFileStorage.browser.ts +52 -0
  368. package/src/storage/LocalFileStorage.spec.ts +292 -0
  369. package/src/storage/LocalFileStorage.ts +98 -0
  370. package/src/storage/VirtualFileStorage.spec.ts +307 -0
  371. package/src/storage/VirtualFileStorage.ts +169 -0
  372. package/src/storage/index.ts +5 -0
  373. package/src/templates/csharp/README.md +48 -0
  374. package/src/templates/csharp/VectraClient.cs +234 -0
  375. package/src/templates/go/README.md +71 -0
  376. package/src/templates/go/vectra_client.go +322 -0
  377. package/src/templates/java/README.md +81 -0
  378. package/src/templates/java/VectraClient.java +232 -0
  379. package/src/templates/python/README.md +37 -0
  380. package/src/templates/python/vectra_client.py +279 -0
  381. package/src/templates/rust/Cargo.toml +14 -0
  382. package/src/templates/rust/README.md +39 -0
  383. package/src/templates/rust/build.rs +4 -0
  384. package/src/templates/rust/lib.rs +284 -0
  385. package/src/templates/typescript/README.md +96 -0
  386. package/src/templates/typescript/VectraClient.ts +374 -0
  387. package/src/types.ts +131 -123
  388. package/src/utils/index.ts +1 -0
  389. package/src/utils/pathUtils.ts +106 -0
  390. package/src/vectra-cli.generate.spec.ts +72 -0
  391. package/src/vectra-cli.spec.ts +0 -0
  392. package/src/vectra-cli.ts +687 -246
@@ -1,358 +1,528 @@
1
1
  import { LocalDocument } from "./LocalDocument";
2
2
  import { LocalDocumentIndex } from "./LocalDocumentIndex";
3
- import { QueryResult, DocumentChunkMetadata, Tokenizer, DocumentTextSection } from "./types";
3
+ import {
4
+ QueryResult,
5
+ DocumentChunkMetadata,
6
+ Tokenizer,
7
+ DocumentTextSection,
8
+ } from "./types";
4
9
 
5
10
  /**
6
11
  * Represents a search result for a document stored on disk.
7
12
  */
8
13
  export class LocalDocumentResult extends LocalDocument {
9
- private readonly _chunks: QueryResult<DocumentChunkMetadata>[];
10
- private readonly _tokenizer: Tokenizer;
11
- private readonly _score: number;
14
+ private readonly _chunks: QueryResult<DocumentChunkMetadata>[];
15
+ private readonly _tokenizer: Tokenizer;
16
+ private readonly _score: number;
17
+
18
+ public static readonly CONNECTOR = "\n\n...\n\n";
19
+
20
+ /**
21
+ * @private
22
+ * Internal constructor for `LocalDocumentResult` instances.
23
+ */
24
+ public constructor(
25
+ index: LocalDocumentIndex,
26
+ id: string,
27
+ uri: string,
28
+ chunks: QueryResult<DocumentChunkMetadata>[],
29
+ tokenizer: Tokenizer
30
+ ) {
31
+ super(index, id, uri);
32
+ this._chunks = chunks;
33
+ this._tokenizer = tokenizer;
34
+
35
+ // Average score across chunks
36
+ let score = 0;
37
+ this._chunks.forEach((chunk) => (score += chunk.score));
38
+ this._score = this._chunks.length > 0 ? score / this._chunks.length : 0;
39
+ }
40
+
41
+ /**
42
+ * Returns the chunks of the document that matched the query.
43
+ */
44
+ public get chunks(): QueryResult<DocumentChunkMetadata>[] {
45
+ return this._chunks;
46
+ }
47
+
48
+ /**
49
+ * Returns the average score of the document result.
50
+ */
51
+ public get score(): number {
52
+ return this._score;
53
+ }
54
+
55
+ /**
56
+ * Helper: robust check for BM25-marked chunks.
57
+ */
58
+ protected isBm25Chunk(chunk: QueryResult<DocumentChunkMetadata>): boolean {
59
+ const val = chunk.item.metadata?.isBm25 as any;
60
+ return val === true || val === "true" || val === 1 || val === "1";
61
+ }
62
+
63
+ /**
64
+ * A small, testable packer that mimics the old `renderAllSections()` behavior
65
+ * but exposes the internal flush logic so all branches are coverable.
66
+ */
67
+ protected createAllSectionsPacker() {
68
+ const sections: DocumentTextSection[] = [];
69
+
70
+ let currentTokens: number[] = [];
71
+ let currentScores: number[] = [];
72
+ let currentIsBm25AllTrue = true;
73
+
74
+ const flush = () => {
75
+ // Branch 1: empty flush
76
+ if (currentTokens.length === 0) return;
77
+
78
+ // Branch 2: avgScore fallback when scores are missing
79
+ const avgScore =
80
+ currentScores.length > 0
81
+ ? currentScores.reduce((a, b) => a + b, 0) / currentScores.length
82
+ : 0;
83
+
84
+ // Branch 3: isBm25 depends on "all bm25" and "has scores"
85
+ const isBm25 =
86
+ currentIsBm25AllTrue && currentScores.length > 0 ? true : false;
87
+
88
+ sections.push({
89
+ text: this._tokenizer.decode(currentTokens),
90
+ tokenCount: currentTokens.length,
91
+ score: avgScore,
92
+ isBm25,
93
+ });
94
+
95
+ currentTokens = [];
96
+ currentScores = [];
97
+ currentIsBm25AllTrue = true;
98
+ };
99
+
100
+ const pushChunkTokens = (
101
+ tokens: number[],
102
+ score: number,
103
+ isBm25Chunk: boolean
104
+ ) => {
105
+ currentTokens.push(...tokens);
106
+ currentScores.push(score);
107
+ currentIsBm25AllTrue = currentIsBm25AllTrue && isBm25Chunk;
108
+ };
12
109
 
13
110
  /**
14
- * @private
15
- * Internal constructor for `LocalDocumentResult` instances.
111
+ * Test-only escape hatch: force internal state to cover otherwise-invariant branches.
112
+ * Not used by production code.
16
113
  */
17
- public constructor(index: LocalDocumentIndex, id: string, uri: string, chunks: QueryResult<DocumentChunkMetadata>[], tokenizer: Tokenizer) {
18
- super(index, id, uri);
19
- this._chunks = chunks;
20
- this._tokenizer = tokenizer;
21
-
22
- // Compute average score
23
- let score = 0;
24
- this._chunks.forEach(chunk => score += chunk.score);
25
- this._score = score / this._chunks.length;
114
+ const __testSetState = (state: {
115
+ currentTokens?: number[];
116
+ currentScores?: number[];
117
+ currentIsBm25AllTrue?: boolean;
118
+ }) => {
119
+ if (state.currentTokens) currentTokens = state.currentTokens.slice();
120
+ if (state.currentScores) currentScores = state.currentScores.slice();
121
+ if (typeof state.currentIsBm25AllTrue === "boolean") {
122
+ currentIsBm25AllTrue = state.currentIsBm25AllTrue;
123
+ }
124
+ };
125
+
126
+ const getSections = () => sections;
127
+
128
+ return { flush, pushChunkTokens, getSections, __testSetState };
129
+ }
130
+
131
+ /**
132
+ * Renders all of the results chunks as spans of text (sections.)
133
+ * @remarks
134
+ * - Chunks are sorted by document order.
135
+ * - Multiple small chunks are packed into a single section up to maxTokens.
136
+ * - Oversized chunks are split into multiple sections, each carrying the chunk's score.
137
+ * - When multiple chunks are packed, section score is the arithmetic mean of packed chunks' scores.
138
+ */
139
+ public async renderAllSections(
140
+ maxTokens: number
141
+ ): Promise<DocumentTextSection[]> {
142
+ const docText = await this.loadText();
143
+
144
+ // Sort by document order
145
+ const sorted = this._chunks
146
+ .slice()
147
+ .sort(
148
+ (a, b) =>
149
+ Number(a.item.metadata.startPos) - Number(b.item.metadata.startPos)
150
+ );
151
+
152
+ const packer = this.createAllSectionsPacker();
153
+
154
+ // We'll keep a local "current length" mirror, to avoid re-encoding just to compute lengths.
155
+ let currentLen = 0;
156
+
157
+ const flushAndReset = () => {
158
+ packer.flush();
159
+ currentLen = 0;
160
+ };
161
+
162
+ for (const chunk of sorted) {
163
+ const startPos = Number(chunk.item.metadata.startPos);
164
+ const endPos = Number(chunk.item.metadata.endPos);
165
+ const chunkText = docText.substring(startPos, endPos + 1);
166
+ const tokens = this._tokenizer.encode(chunkText);
167
+
168
+ // Oversized chunk: split
169
+ if (tokens.length > maxTokens) {
170
+ // flush pending packed group
171
+ flushAndReset();
172
+
173
+ let offset = 0;
174
+ while (offset < tokens.length) {
175
+ const part = tokens.slice(offset, offset + maxTokens);
176
+
177
+ // Each split part is its own section (force packer state then flush)
178
+ (packer as any).__testSetState({
179
+ currentTokens: part,
180
+ currentScores: [chunk.score],
181
+ currentIsBm25AllTrue: this.isBm25Chunk(chunk),
182
+ });
183
+ packer.flush();
184
+ offset += part.length;
185
+ }
186
+ continue;
187
+ }
188
+
189
+ // Pack if it fits
190
+ if (currentLen + tokens.length <= maxTokens) {
191
+ packer.pushChunkTokens(tokens, chunk.score, this.isBm25Chunk(chunk));
192
+ currentLen += tokens.length;
193
+ } else {
194
+ // overflow: flush, then start new group
195
+ flushAndReset();
196
+ packer.pushChunkTokens(tokens, chunk.score, this.isBm25Chunk(chunk));
197
+ currentLen = tokens.length;
198
+ }
26
199
  }
27
200
 
28
- /**
29
- * Returns the chunks of the document that matched the query.
30
- */
31
- public get chunks(): QueryResult<DocumentChunkMetadata>[] {
32
- return this._chunks;
201
+ // final flush
202
+ packer.flush();
203
+ return packer.getSections();
204
+ }
205
+
206
+ /**
207
+ * Testable helper: build a single fallback section from the top-scoring chunk,
208
+ * truncated to exactly maxTokens tokens.
209
+ */
210
+ protected buildFallbackTopChunkSection(
211
+ docText: string,
212
+ chunks: QueryResult<DocumentChunkMetadata>[],
213
+ isBm25: boolean,
214
+ maxTokens: number
215
+ ): DocumentTextSection[] {
216
+ if (chunks.length === 0) return [];
217
+
218
+ const topChunk = chunks.reduce(
219
+ (prev, curr) => (curr.score > prev.score ? curr : prev),
220
+ chunks[0]
221
+ );
222
+
223
+ const start = Number(topChunk.item.metadata.startPos);
224
+ const end = Number(topChunk.item.metadata.endPos);
225
+ const chunkText = docText.substring(start, end + 1);
226
+ const chunkTokens = this._tokenizer.encode(chunkText);
227
+
228
+ const truncatedTokens = chunkTokens.slice(0, maxTokens);
229
+
230
+ return [
231
+ {
232
+ text: this._tokenizer.decode(truncatedTokens),
233
+ tokenCount: maxTokens,
234
+ score: topChunk.score,
235
+ isBm25,
236
+ },
237
+ ];
238
+ }
239
+
240
+ /**
241
+ * Internal helper: builds sections for either semantic or BM25 chunk lists using a heatmap.
242
+ */
243
+ protected buildSectionsFor(
244
+ docText: string,
245
+ chunks: QueryResult<DocumentChunkMetadata>[],
246
+ isBm25: boolean,
247
+ maxTokens: number,
248
+ maxSections: number,
249
+ overlappingChunks: boolean
250
+ ): DocumentTextSection[] {
251
+ if (chunks.length === 0) return [];
252
+
253
+ const connector = LocalDocumentResult.CONNECTOR;
254
+ const connectorTokens = this._tokenizer.encode(connector);
255
+
256
+ // Build heatmap: map each character position to accumulated score
257
+ const heatmap = new Map<number, number>();
258
+ for (const chunk of chunks) {
259
+ const start = Number(chunk.item.metadata.startPos);
260
+ const end = Number(chunk.item.metadata.endPos);
261
+ for (let pos = start; pos <= end; pos++) {
262
+ heatmap.set(pos, (heatmap.get(pos) || 0) + chunk.score);
263
+ }
33
264
  }
34
265
 
35
- /**
36
- * Returns the average score of the document result.
37
- */
38
- public get score(): number {
39
- return this._score;
266
+ interface Peak {
267
+ position: number;
268
+ score: number;
269
+ chunks: QueryResult<DocumentChunkMetadata>[];
40
270
  }
41
271
 
42
- /**
43
- * Renders all of the results chunks as spans of text (sections.)
44
- * @remarks
45
- * The returned sections will be sorted by document order and limited to maxTokens in length.
46
- * @param maxTokens Maximum number of tokens per section.
47
- * @returns Array of rendered text sections.
48
- */
49
- public async renderAllSections(maxTokens: number): Promise<DocumentTextSection[]> {
50
- // Load text from disk
51
- const text = await this.loadText();
52
-
53
- // Add chunks to a temp array and split any chunks that are longer than maxTokens.
54
- const chunks: SectionChunk[] = [];
55
- for (let i = 0; i < this._chunks.length; i++) {
56
- const chunk = this._chunks[i];
57
- const startPos = chunk.item.metadata.startPos;
58
- const endPos = chunk.item.metadata.endPos;
59
- const chunkText = text.substring(startPos, endPos + 1);
60
- const tokens = this._tokenizer.encode(chunkText);
61
- let offset = 0;
62
- while (offset < tokens.length) {
63
- const chunkLength = Math.min(maxTokens, tokens.length - offset);
64
- chunks.push({
65
- text: this._tokenizer.decode(tokens.slice(offset, offset + chunkLength)),
66
- startPos: startPos + offset,
67
- endPos: startPos + offset + chunkLength - 1,
68
- score: chunk.score,
69
- tokenCount: chunkLength,
70
- isBm25: false
71
- });
72
- offset += chunkLength;
73
- }
272
+ const peaks: Peak[] = [];
273
+ const sortedPositions = Array.from(heatmap.keys()).sort((a, b) => a - b);
274
+
275
+ let currentPeak: Peak | null = null;
276
+ const PEAK_THRESHOLD = 0.1;
277
+
278
+ for (const pos of sortedPositions) {
279
+ const score = heatmap.get(pos)!;
280
+
281
+ if (score < PEAK_THRESHOLD) {
282
+ if (currentPeak) {
283
+ peaks.push(currentPeak);
284
+ currentPeak = null;
74
285
  }
286
+ continue;
287
+ }
75
288
 
76
- // Sort chunks by startPos
77
- const sorted = chunks.sort((a, b) => a.startPos - b.startPos);
78
-
79
- // Generate sections
80
- const sections: Section[] = [];
81
- for (let i = 0; i < sorted.length; i++) {
82
- const chunk = sorted[i];
83
- let section = sections[sections.length - 1];
84
- if (!section || section.tokenCount + chunk.tokenCount > maxTokens) {
85
- section = {
86
- chunks: [],
87
- score: 0,
88
- tokenCount: 0
89
- };
90
- sections.push(section);
91
- }
92
- section.chunks.push(chunk);
93
- section.score += chunk.score;
94
- section.tokenCount += chunk.tokenCount;
289
+ if (!currentPeak) {
290
+ currentPeak = { position: pos, score, chunks: [] };
291
+ } else {
292
+ if (score > currentPeak.score) {
293
+ currentPeak.position = pos;
294
+ currentPeak.score = score;
95
295
  }
96
-
97
- // Normalize section scores
98
- sections.forEach(section => section.score /= section.chunks.length);
99
-
100
- // Return final rendered sections
101
- return sections.map(section => {
102
- let text = '';
103
- section.chunks.forEach(chunk => text += chunk.text);
104
- return {
105
- text: text,
106
- tokenCount: section.tokenCount,
107
- score: section.score,
108
- isBm25: false,
109
- };
110
- });
296
+ }
111
297
  }
298
+ if (currentPeak) peaks.push(currentPeak);
112
299
 
113
- /**
114
- * Renders the top spans of text (sections) of the document based on the query result.
115
- * @remarks
116
- * The returned sections will be sorted by relevance and limited to the top `maxSections`.
117
- * @param maxTokens Maximum number of tokens per section.
118
- * @param maxSections Maximum number of sections to return.
119
- * @param overlappingChunks Optional. If true, overlapping chunks of text will be added to each section until the maxTokens is reached.
120
- * @returns Array of rendered text sections.
121
- */
122
- public async renderSections(maxTokens: number, maxSections: number, overlappingChunks = true): Promise<DocumentTextSection[]> {
123
- // Load text from disk
124
- const text = await this.loadText();
125
-
126
- // First check to see if the entire document is shorter than maxTokens
127
- const length = await this.getLength();
128
- if (length <= maxTokens) {
129
- return [{
130
- text,
131
- tokenCount: length,
132
- score: 1.0,
133
- isBm25: false,
134
- }];
135
- }
300
+ // No-peaks fallback: create one at center of top chunk
301
+ if (peaks.length === 0) {
302
+ const topChunk = chunks.reduce(
303
+ (prev, curr) => (curr.score > prev.score ? curr : prev),
304
+ chunks[0]
305
+ );
306
+ const start = Number(topChunk.item.metadata.startPos);
307
+ const end = Number(topChunk.item.metadata.endPos);
308
+ const center = Math.floor((start + end) / 2);
309
+ peaks.push({ position: center, score: topChunk.score, chunks: [] });
310
+ }
136
311
 
137
- // Otherwise, we need to split the document into sections
138
- // - Add each chunk to a temp array and filter out any chunk that's longer then maxTokens.
139
- // - Sort the array by startPos to arrange chunks in document order.
140
- // - Generate a new array of sections by combining chunks until the maxTokens is reached for each section.
141
- // - Generate an aggregate score for each section by averaging the score of each chunk in the section.
142
- // - Sort the sections by score and limit to maxSections.
143
- // - For each remaining section combine adjacent chunks of text.
144
- // - Dynamically add overlapping chunks of text to each section until the maxTokens is reached.
145
- const chunks: SectionChunk[] = this._chunks.map(chunk => {
146
- const startPos = chunk.item.metadata.startPos;
147
- const endPos = chunk.item.metadata.endPos;
148
- const chunkText = text.substring(startPos, endPos + 1);
149
- return {
150
- text: chunkText,
151
- startPos,
152
- endPos,
153
- score: chunk.score,
154
- tokenCount: this._tokenizer.encode(chunkText).length,
155
- isBm25: Boolean(chunk.item.metadata.isBm25),
156
- };
157
- }).filter(chunk => chunk.tokenCount <= maxTokens).sort((a, b) => a.startPos - b.startPos);
158
-
159
- // Check for no chunks
160
- if (chunks.length === 0) {
161
- // Take the top chunk and return a subset of its text
162
- const topChunk = this._chunks[0];
163
- const startPos = topChunk.item.metadata.startPos;
164
- const endPos = topChunk.item.metadata.endPos;
165
- const chunkText = text.substring(startPos, endPos + 1);
166
- const tokens = this._tokenizer.encode(chunkText);
167
- return [{
168
- text: this._tokenizer.decode(tokens.slice(0, maxTokens)),
169
- tokenCount: maxTokens,
170
- score: topChunk.score,
171
- isBm25: false,
172
- }];
312
+ // Associate chunks to nearest peak
313
+ for (const chunk of chunks) {
314
+ const start = Number(chunk.item.metadata.startPos);
315
+ const end = Number(chunk.item.metadata.endPos);
316
+ const center = Math.floor((start + end) / 2);
317
+
318
+ let closestPeak = peaks[0];
319
+ let minDist = Math.abs(center - closestPeak.position);
320
+
321
+ for (const peak of peaks) {
322
+ const dist = Math.abs(center - peak.position);
323
+ if (dist < minDist) {
324
+ minDist = dist;
325
+ closestPeak = peak;
173
326
  }
327
+ }
328
+
329
+ closestPeak.chunks.push(chunk);
330
+ }
331
+
332
+ // Sort peaks by score desc
333
+ peaks.sort((a, b) => b.score - a.score);
334
+ const topPeaks = peaks.slice(0, maxSections);
335
+
336
+ const sections: DocumentTextSection[] = [];
337
+
338
+ for (const peak of topPeaks) {
339
+ const sortedChunks = peak.chunks.slice().sort((a, b) => {
340
+ const aCenter = Math.floor(
341
+ (Number(a.item.metadata.startPos) + Number(a.item.metadata.endPos)) / 2
342
+ );
343
+ const bCenter = Math.floor(
344
+ (Number(b.item.metadata.startPos) + Number(b.item.metadata.endPos)) / 2
345
+ );
346
+ return (
347
+ Math.abs(aCenter - peak.position) - Math.abs(bCenter - peak.position)
348
+ );
349
+ });
350
+
351
+ const selected: QueryResult<DocumentChunkMetadata>[] = [];
352
+ let currentTokenCount = 0;
353
+
354
+ for (const chunk of sortedChunks) {
355
+ const start = Number(chunk.item.metadata.startPos);
356
+ const end = Number(chunk.item.metadata.endPos);
357
+ const chunkText = docText.substring(start, end + 1);
358
+ const chunkTokens = this._tokenizer.encode(chunkText);
174
359
 
175
- // Generate semantic sections
176
- const sections: Section[] = [];
177
- for (let i = 0; i < chunks.length; i++) {
178
- const chunk = chunks[i];
179
- let section = sections[sections.length - 1];
180
- if (!chunk.isBm25) {
181
- if (!section || section.tokenCount + chunk.tokenCount > maxTokens) {
182
- section = {
183
- chunks: [],
184
- score: 0,
185
- tokenCount: 0
186
- };
187
- sections.push(section);
188
- }
189
- section.chunks.push(chunk);
190
- section.score += chunk.score;
191
- section.tokenCount += chunk.tokenCount;
192
- }
360
+ // Whole-chunk preference, skip oversize
361
+ if (chunkTokens.length > maxTokens) continue;
362
+
363
+ let tokensNeeded = chunkTokens.length;
364
+
365
+ if (selected.length > 0 && overlappingChunks) {
366
+ const isAdjacent = selected.some((s) => {
367
+ const sEnd = Number(s.item.metadata.endPos);
368
+ const sStart = Number(s.item.metadata.startPos);
369
+ return sEnd + 1 === start || end + 1 === sStart;
370
+ });
371
+
372
+ if (!isAdjacent) {
373
+ tokensNeeded += connectorTokens.length;
374
+ }
193
375
  }
194
376
 
195
- // Generate bm25 sections
196
- const bm25Sections: Section[] = [];
197
- for (let i = 0; i < chunks.length; i++) {
198
- const chunk = chunks[i];
199
- let section = bm25Sections[bm25Sections.length - 1];
200
- if (chunk.isBm25) {
201
- if (!section || section.tokenCount + chunk.tokenCount > maxTokens) {
202
- section = {
203
- chunks: [],
204
- score: 0,
205
- tokenCount: 0
206
- };
207
- bm25Sections.push(section);
208
- }
209
- section.chunks.push(chunk);
210
- section.score += chunk.score;
211
- section.tokenCount += chunk.tokenCount;
212
- }
213
- }
214
- // Normalize section scores
215
- sections.forEach(section => section.score /= section.chunks.length);
216
- bm25Sections.forEach(section => section.score /= section.chunks.length);
217
-
218
- // Sort sections by score and limit to maxSections
219
- sections.sort((a, b) => b.score - a.score);
220
- bm25Sections.sort((a, b) => b.score - a.score);
221
- if (sections.length > maxSections) {
222
- sections.splice(maxSections, sections.length - maxSections);
377
+ if (currentTokenCount + tokensNeeded <= maxTokens) {
378
+ selected.push(chunk);
379
+ currentTokenCount += tokensNeeded;
223
380
  }
224
- if (bm25Sections.length > maxSections) {
225
- bm25Sections.splice(maxSections, bm25Sections.length - maxSections);
381
+ }
382
+
383
+ // If nothing selected, fall back (required by contract)
384
+ if (selected.length === 0) {
385
+ return this.buildFallbackTopChunkSection(
386
+ docText,
387
+ chunks,
388
+ isBm25,
389
+ maxTokens
390
+ );
391
+ }
392
+
393
+ // Assemble selected in document order with connectors
394
+ const ordered = selected
395
+ .slice()
396
+ .sort(
397
+ (a, b) =>
398
+ Number(a.item.metadata.startPos) - Number(b.item.metadata.startPos)
399
+ );
400
+
401
+ let sectionText = "";
402
+ let sectionTokens: number[] = [];
403
+
404
+ for (let i = 0; i < ordered.length; i++) {
405
+ const curr = ordered[i];
406
+ const start = Number(curr.item.metadata.startPos);
407
+ const end = Number(curr.item.metadata.endPos);
408
+ const chunkText = docText.substring(start, end + 1);
409
+
410
+ if (i > 0 && overlappingChunks) {
411
+ const prev = ordered[i - 1];
412
+ const prevEnd = Number(prev.item.metadata.endPos);
413
+ if (prevEnd + 1 < start) {
414
+ sectionText += connector;
415
+ sectionTokens.push(...connectorTokens);
416
+ }
226
417
  }
227
418
 
228
- // Combine adjacent chunks of text
229
- sections.forEach(section => {
230
- for (let i = 0; i < section.chunks.length - 1; i++) {
231
- const chunk = section.chunks[i];
232
- const nextChunk = section.chunks[i + 1];
233
- if (chunk.endPos + 1 === nextChunk.startPos) {
234
- chunk.text += nextChunk.text;
235
- chunk.endPos = nextChunk.endPos;
236
- chunk.tokenCount += nextChunk.tokenCount;
237
- section.chunks.splice(i + 1, 1);
238
- i--;
239
- }
240
- }
241
- });
242
-
243
- // Add overlapping chunks of text to each section until the maxTokens is reached
244
- if (overlappingChunks) {
245
- const connector: SectionChunk = {
246
- text: '\n\n...\n\n',
247
- startPos: -1,
248
- endPos: -1,
249
- score: 0,
250
- tokenCount: this._tokenizer.encode('\n\n...\n\n').length,
251
- isBm25: false,
252
- };
253
- sections.forEach(section => {
254
- // Insert connectors between chunks
255
- if (section.chunks.length > 1) {
256
- for (let i = 0; i < section.chunks.length - 1; i++) {
257
- section.chunks.splice(i + 1, 0, connector);
258
- section.tokenCount += connector.tokenCount;
259
- i++;
260
- }
261
- }
262
-
263
- // Add chunks to beginning and end of the section until maxTokens is reached
264
- let budget = maxTokens - section.tokenCount;
265
- if (budget > 40) {
266
- const sectionStart = section.chunks[0].startPos;
267
- const sectionEnd = section.chunks[section.chunks.length - 1].endPos;
268
- if (sectionStart > 0) {
269
- const beforeTex = text.substring(0, section.chunks[0].startPos);
270
- const beforeTokens = this.encodeBeforeText(beforeTex, Math.ceil(budget/2));
271
- const beforeBudget = sectionEnd < text.length - 1 ? Math.min(beforeTokens.length, Math.ceil(budget/2)) : Math.min(beforeTokens.length, budget);
272
- const chunk: SectionChunk = {
273
- text: this._tokenizer.decode(beforeTokens.slice(-beforeBudget)),
274
- startPos: sectionStart - beforeBudget,
275
- endPos: sectionStart - 1,
276
- score: 0,
277
- tokenCount: beforeBudget,
278
- isBm25: false,
279
- };
280
- section.chunks.unshift(chunk);
281
- section.tokenCount += chunk.tokenCount;
282
- budget -= chunk.tokenCount;
283
- }
284
-
285
- if (sectionEnd < text.length - 1) {
286
- const afterText = text.substring(sectionEnd + 1);
287
- const afterTokens = this.encodeAfterText(afterText, budget);
288
- const afterBudget = Math.min(afterTokens.length, budget);
289
- const chunk: SectionChunk = {
290
- text: this._tokenizer.decode(afterTokens.slice(0, afterBudget)),
291
- startPos: sectionEnd + 1,
292
- endPos: sectionEnd + afterBudget,
293
- score: 0,
294
- tokenCount: afterBudget,
295
- isBm25: false,
296
- };
297
- section.chunks.push(chunk);
298
- section.tokenCount += chunk.tokenCount;
299
- budget -= chunk.tokenCount;
300
- }
301
- }
302
- });
419
+ sectionText += chunkText;
420
+ sectionTokens.push(...this._tokenizer.encode(chunkText));
421
+ }
422
+
423
+ // Optional expansion if budget remains
424
+ if (overlappingChunks) {
425
+ const budgetRemain = maxTokens - sectionTokens.length;
426
+ if (budgetRemain > 40) {
427
+ const firstStart = Math.min(
428
+ ...selected.map((c) => Number(c.item.metadata.startPos))
429
+ );
430
+ const lastEnd = Math.max(
431
+ ...selected.map((c) => Number(c.item.metadata.endPos))
432
+ );
433
+
434
+ const beforeRegion = docText.slice(0, firstStart);
435
+ const afterRegion = docText.slice(lastEnd + 1);
436
+
437
+ const beforeToksAll = this._tokenizer.encode(beforeRegion);
438
+ const afterToksAll = this._tokenizer.encode(afterRegion);
439
+
440
+ const beforeBudget = Math.min(
441
+ Math.ceil(budgetRemain / 2),
442
+ beforeToksAll.length
443
+ );
444
+ const afterBudget = Math.min(
445
+ budgetRemain - beforeBudget,
446
+ afterToksAll.length
447
+ );
448
+
449
+ const beforeTail = beforeToksAll.slice(
450
+ beforeToksAll.length - beforeBudget
451
+ );
452
+ const afterHead = afterToksAll.slice(0, afterBudget);
453
+
454
+ sectionText =
455
+ this._tokenizer.decode(beforeTail) +
456
+ sectionText +
457
+ this._tokenizer.decode(afterHead);
458
+
459
+ sectionTokens = [...beforeTail, ...sectionTokens, ...afterHead];
303
460
  }
461
+ }
304
462
 
305
- const semanticDocTextSections = sections.map(section => {
306
- let text = '';
307
- section.chunks.forEach(chunk => text += chunk.text);
308
- return {
309
- text: text,
310
- tokenCount: section.tokenCount,
311
- score: section.score,
312
- isBm25: false,
313
- };
314
- });
315
- const bm25DocTextSections = bm25Sections.map(section => {
316
- let text = '';
317
- section.chunks.forEach(chunk => text += chunk.text);
318
- return {
319
- text: text,
320
- tokenCount: section.tokenCount,
321
- score: section.score,
322
- isBm25: true,
323
- };
324
- });
325
-
326
- // Return final rendered sections
327
- return [...semanticDocTextSections, ...bm25DocTextSections];
463
+ const avgScore =
464
+ selected.reduce((sum, c) => sum + c.score, 0) / selected.length;
465
+
466
+ sections.push({
467
+ text: sectionText,
468
+ tokenCount: sectionTokens.length,
469
+ score: avgScore,
470
+ isBm25,
471
+ });
328
472
  }
329
473
 
330
- private encodeBeforeText(text: string, budget: number): number[] {
331
- const maxLength = budget * 8;
332
- const substr = text.length <= maxLength ? text : text.substring(text.length - maxLength);
333
- return this._tokenizer.encode(substr);
474
+ // If maxSections=0 (or slice emptied), this is reachable:
475
+ if (sections.length === 0) {
476
+ return this.buildFallbackTopChunkSection(docText, chunks, isBm25, maxTokens);
334
477
  }
335
-
336
- private encodeAfterText(text: string, budget: number): number[] {
337
- const maxLength = budget * 8;
338
- const substr = text.length <= maxLength ? text : text.substring(0, maxLength);
339
- return this._tokenizer.encode(substr);
478
+
479
+ return sections;
480
+ }
481
+
482
+ /**
483
+ * Renders the top spans of text (sections) of the document based on the query result.
484
+ */
485
+ public async renderSections(
486
+ maxTokens: number,
487
+ maxSections: number,
488
+ overlappingChunks = true
489
+ ): Promise<DocumentTextSection[]> {
490
+ const length = await this.getLength();
491
+ if (length <= maxTokens) {
492
+ const text = await this.loadText();
493
+ return [
494
+ {
495
+ text,
496
+ tokenCount: length,
497
+ score: 1.0,
498
+ isBm25: false,
499
+ },
500
+ ];
340
501
  }
341
502
 
342
- }
503
+ const docText = await this.loadText();
504
+
505
+ const semanticChunks = this._chunks.filter((c) => !this.isBm25Chunk(c));
506
+ const bm25Chunks = this._chunks.filter((c) => this.isBm25Chunk(c));
343
507
 
344
- interface SectionChunk {
345
- text: string;
346
- startPos: number;
347
- endPos: number;
348
- score: number;
349
- tokenCount: number;
350
- isBm25: boolean;
351
- }
508
+ const semSections = this.buildSectionsFor(
509
+ docText,
510
+ semanticChunks,
511
+ false,
512
+ maxTokens,
513
+ maxSections,
514
+ overlappingChunks
515
+ ).slice(0, maxSections);
352
516
 
353
- interface Section {
354
- chunks: SectionChunk[];
355
- score: number;
356
- tokenCount: number;
357
- }
517
+ const bmSections = this.buildSectionsFor(
518
+ docText,
519
+ bm25Chunks,
520
+ true,
521
+ maxTokens,
522
+ maxSections,
523
+ overlappingChunks
524
+ ).slice(0, maxSections);
358
525
 
526
+ return [...semSections, ...bmSections];
527
+ }
528
+ }