@nocobase/app-plugin-agents 1.0.0-beta.29 → 2.0.0-beta.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 (300) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +8 -19
  3. package/dist/client/api/client.d.ts +2 -2
  4. package/dist/client/api/client.d.ts.map +1 -1
  5. package/dist/client/api/client.js +2 -2
  6. package/dist/client/api/client.js.map +1 -1
  7. package/dist/client/api/runners.d.ts +2 -2
  8. package/dist/client/api/runners.d.ts.map +1 -1
  9. package/dist/client/api/runners.js +5 -2
  10. package/dist/client/api/runners.js.map +1 -1
  11. package/dist/client/chat/api.d.ts +1 -1
  12. package/dist/client/chat/api.d.ts.map +1 -1
  13. package/dist/client/chat/api.js +6 -2
  14. package/dist/client/chat/api.js.map +1 -1
  15. package/dist/client/chat/coalesced-invalidation.d.ts +3 -8
  16. package/dist/client/chat/coalesced-invalidation.d.ts.map +1 -1
  17. package/dist/client/chat/coalesced-invalidation.js +16 -21
  18. package/dist/client/chat/coalesced-invalidation.js.map +1 -1
  19. package/dist/client/chat/controller.d.ts +31 -7
  20. package/dist/client/chat/controller.d.ts.map +1 -1
  21. package/dist/client/chat/controller.js +616 -120
  22. package/dist/client/chat/controller.js.map +1 -1
  23. package/dist/client/chat/provider.d.ts.map +1 -1
  24. package/dist/client/chat/provider.js +7 -4
  25. package/dist/client/chat/provider.js.map +1 -1
  26. package/dist/client/chat/submissions.d.ts +2 -0
  27. package/dist/client/chat/submissions.d.ts.map +1 -1
  28. package/dist/client/chat/submissions.js +1 -0
  29. package/dist/client/chat/submissions.js.map +1 -1
  30. package/dist/client/chat/use-chat.d.ts +4 -2
  31. package/dist/client/chat/use-chat.d.ts.map +1 -1
  32. package/dist/client/chat/use-chat.js +84 -55
  33. package/dist/client/chat/use-chat.js.map +1 -1
  34. package/dist/client/components/ag-tag-tones.d.ts +7 -0
  35. package/dist/client/components/ag-tag-tones.d.ts.map +1 -0
  36. package/dist/client/components/ag-tag-tones.js +14 -0
  37. package/dist/client/components/ag-tag-tones.js.map +1 -0
  38. package/dist/client/components/ag-tag.d.ts +2 -1
  39. package/dist/client/components/ag-tag.d.ts.map +1 -1
  40. package/dist/client/components/ag-tag.js +1 -13
  41. package/dist/client/components/ag-tag.js.map +1 -1
  42. package/dist/client/components/quota-status.d.ts +2 -1
  43. package/dist/client/components/quota-status.d.ts.map +1 -1
  44. package/dist/client/components/quota-status.js +2 -2
  45. package/dist/client/components/quota-status.js.map +1 -1
  46. package/dist/client/components/ui/pagination.d.ts +6 -0
  47. package/dist/client/components/ui/pagination.d.ts.map +1 -0
  48. package/dist/client/components/ui/pagination.js +13 -0
  49. package/dist/client/components/ui/pagination.js.map +1 -0
  50. package/dist/client/hooks/use-notify.d.ts.map +1 -1
  51. package/dist/client/hooks/use-notify.js +4 -0
  52. package/dist/client/hooks/use-notify.js.map +1 -1
  53. package/dist/client/hooks/use-realtime-topic.d.ts +1 -1
  54. package/dist/client/hooks/use-realtime-topic.d.ts.map +1 -1
  55. package/dist/client/hooks/use-realtime-topic.js +8 -4
  56. package/dist/client/hooks/use-realtime-topic.js.map +1 -1
  57. package/dist/client/kit.d.ts.map +1 -1
  58. package/dist/client/kit.js +30 -5
  59. package/dist/client/kit.js.map +1 -1
  60. package/dist/client/locales/de-DE.d.ts.map +1 -1
  61. package/dist/client/locales/de-DE.js +26 -10
  62. package/dist/client/locales/de-DE.js.map +1 -1
  63. package/dist/client/locales/en-US.d.ts +11 -0
  64. package/dist/client/locales/en-US.d.ts.map +1 -1
  65. package/dist/client/locales/en-US.js +21 -10
  66. package/dist/client/locales/en-US.js.map +1 -1
  67. package/dist/client/locales/es-ES.d.ts.map +1 -1
  68. package/dist/client/locales/es-ES.js +26 -10
  69. package/dist/client/locales/es-ES.js.map +1 -1
  70. package/dist/client/locales/fr-FR.d.ts.map +1 -1
  71. package/dist/client/locales/fr-FR.js +26 -10
  72. package/dist/client/locales/fr-FR.js.map +1 -1
  73. package/dist/client/locales/id-ID.d.ts.map +1 -1
  74. package/dist/client/locales/id-ID.js +26 -10
  75. package/dist/client/locales/id-ID.js.map +1 -1
  76. package/dist/client/locales/ja-JP.d.ts.map +1 -1
  77. package/dist/client/locales/ja-JP.js +26 -10
  78. package/dist/client/locales/ja-JP.js.map +1 -1
  79. package/dist/client/locales/pt-BR.d.ts.map +1 -1
  80. package/dist/client/locales/pt-BR.js +26 -10
  81. package/dist/client/locales/pt-BR.js.map +1 -1
  82. package/dist/client/locales/runtimes.en-US.d.ts +5 -0
  83. package/dist/client/locales/runtimes.en-US.d.ts.map +1 -1
  84. package/dist/client/locales/runtimes.en-US.js +5 -0
  85. package/dist/client/locales/runtimes.en-US.js.map +1 -1
  86. package/dist/client/locales/runtimes.zh-CN.d.ts.map +1 -1
  87. package/dist/client/locales/runtimes.zh-CN.js +5 -0
  88. package/dist/client/locales/runtimes.zh-CN.js.map +1 -1
  89. package/dist/client/locales/vi-VN.d.ts.map +1 -1
  90. package/dist/client/locales/vi-VN.js +26 -10
  91. package/dist/client/locales/vi-VN.js.map +1 -1
  92. package/dist/client/locales/zh-CN.d.ts.map +1 -1
  93. package/dist/client/locales/zh-CN.js +21 -10
  94. package/dist/client/locales/zh-CN.js.map +1 -1
  95. package/dist/client/locales/zh-TW.d.ts.map +1 -1
  96. package/dist/client/locales/zh-TW.js +26 -10
  97. package/dist/client/locales/zh-TW.js.map +1 -1
  98. package/dist/client/model-suggestion.d.ts +2 -0
  99. package/dist/client/model-suggestion.d.ts.map +1 -1
  100. package/dist/client/model-suggestion.js +27 -2
  101. package/dist/client/model-suggestion.js.map +1 -1
  102. package/dist/client/pages/agents/agent-dialog.d.ts.map +1 -1
  103. package/dist/client/pages/agents/agent-dialog.js +10 -8
  104. package/dist/client/pages/agents/agent-dialog.js.map +1 -1
  105. package/dist/client/pages/agents/available-models-drawer.d.ts.map +1 -1
  106. package/dist/client/pages/agents/available-models-drawer.js +2 -1
  107. package/dist/client/pages/agents/available-models-drawer.js.map +1 -1
  108. package/dist/client/pages/agents/good-at-templates.d.ts +8 -0
  109. package/dist/client/pages/agents/good-at-templates.d.ts.map +1 -0
  110. package/dist/client/pages/agents/good-at-templates.js +29 -0
  111. package/dist/client/pages/agents/good-at-templates.js.map +1 -0
  112. package/dist/client/pages/runtimes/runner-cells.d.ts.map +1 -1
  113. package/dist/client/pages/runtimes/runner-cells.js +3 -1
  114. package/dist/client/pages/runtimes/runner-cells.js.map +1 -1
  115. package/dist/client/pages/runtimes/runner-pagination.d.ts +15 -0
  116. package/dist/client/pages/runtimes/runner-pagination.d.ts.map +1 -0
  117. package/dist/client/pages/runtimes/runner-pagination.js +9 -0
  118. package/dist/client/pages/runtimes/runner-pagination.js.map +1 -0
  119. package/dist/client/pages/runtimes/runner-runs.d.ts +3 -2
  120. package/dist/client/pages/runtimes/runner-runs.d.ts.map +1 -1
  121. package/dist/client/pages/runtimes/runner-runs.js +38 -15
  122. package/dist/client/pages/runtimes/runner-runs.js.map +1 -1
  123. package/dist/client/pages/runtimes/runner-sheet.js +1 -1
  124. package/dist/client/pages/runtimes/runner-sheet.js.map +1 -1
  125. package/dist/client/pages/runtimes/runner-workspaces.d.ts.map +1 -1
  126. package/dist/client/pages/runtimes/runner-workspaces.js +7 -3
  127. package/dist/client/pages/runtimes/runner-workspaces.js.map +1 -1
  128. package/dist/client/runs/refresh-run.d.ts +4 -0
  129. package/dist/client/runs/refresh-run.d.ts.map +1 -0
  130. package/dist/client/runs/refresh-run.js +8 -0
  131. package/dist/client/runs/refresh-run.js.map +1 -0
  132. package/dist/client/runs/run-hooks.d.ts.map +1 -1
  133. package/dist/client/runs/run-hooks.js +2 -1
  134. package/dist/client/runs/run-hooks.js.map +1 -1
  135. package/dist/database/migrations/.manifest.json +8 -0
  136. package/dist/database/migrations/202611010003_ag_runner_run_history_index.d.ts +4 -0
  137. package/dist/database/migrations/202611010003_ag_runner_run_history_index.d.ts.map +1 -0
  138. package/dist/database/migrations/202611010003_ag_runner_run_history_index.js +17 -0
  139. package/dist/database/migrations/202611010003_ag_runner_run_history_index.js.map +1 -0
  140. package/dist/database/migrations/202611070001_ag_conversation_project.d.ts +4 -0
  141. package/dist/database/migrations/202611070001_ag_conversation_project.d.ts.map +1 -0
  142. package/dist/database/migrations/202611070001_ag_conversation_project.js +17 -0
  143. package/dist/database/migrations/202611070001_ag_conversation_project.js.map +1 -0
  144. package/dist/package.json +4 -6
  145. package/dist/server/composition.d.ts +0 -17
  146. package/dist/server/composition.d.ts.map +1 -1
  147. package/dist/server/composition.js +7 -20
  148. package/dist/server/composition.js.map +1 -1
  149. package/dist/server/core/conversations/conversation.service.d.ts.map +1 -1
  150. package/dist/server/core/conversations/conversation.service.js +174 -22
  151. package/dist/server/core/conversations/conversation.service.js.map +1 -1
  152. package/dist/server/core/conversations/conversation.store.d.ts +1 -0
  153. package/dist/server/core/conversations/conversation.store.d.ts.map +1 -1
  154. package/dist/server/core/conversations/conversation.store.js.map +1 -1
  155. package/dist/server/core/conversations/index.d.ts +1 -1
  156. package/dist/server/core/conversations/index.d.ts.map +1 -1
  157. package/dist/server/core/conversations/index.js.map +1 -1
  158. package/dist/server/core/conversations/links.d.ts +3 -0
  159. package/dist/server/core/conversations/links.d.ts.map +1 -1
  160. package/dist/server/core/conversations/links.js +33 -0
  161. package/dist/server/core/conversations/links.js.map +1 -1
  162. package/dist/server/core/conversations/ports.d.ts +14 -0
  163. package/dist/server/core/conversations/ports.d.ts.map +1 -1
  164. package/dist/server/core/runs/claim.d.ts +7 -1
  165. package/dist/server/core/runs/claim.d.ts.map +1 -1
  166. package/dist/server/core/runs/claim.js +69 -17
  167. package/dist/server/core/runs/claim.js.map +1 -1
  168. package/dist/server/core/runs/ports.d.ts +2 -0
  169. package/dist/server/core/runs/ports.d.ts.map +1 -1
  170. package/dist/server/core/runs/ports.js.map +1 -1
  171. package/dist/server/core/runs/run.service.d.ts +6 -1
  172. package/dist/server/core/runs/run.service.d.ts.map +1 -1
  173. package/dist/server/core/runs/run.service.js +29 -1
  174. package/dist/server/core/runs/run.service.js.map +1 -1
  175. package/dist/server/core/runs/run.store.d.ts +1 -1
  176. package/dist/server/core/runs/run.store.d.ts.map +1 -1
  177. package/dist/server/core/runs/run.store.js +1 -1
  178. package/dist/server/core/runs/run.store.js.map +1 -1
  179. package/dist/server/core/runs/runner-view.d.ts +8 -4
  180. package/dist/server/core/runs/runner-view.d.ts.map +1 -1
  181. package/dist/server/core/runs/runner-view.js +89 -32
  182. package/dist/server/core/runs/runner-view.js.map +1 -1
  183. package/dist/server/kernel/events.d.ts +2 -0
  184. package/dist/server/kernel/events.d.ts.map +1 -1
  185. package/dist/server/kernel/events.js.map +1 -1
  186. package/dist/server/locales/de-DE.d.ts.map +1 -1
  187. package/dist/server/locales/de-DE.js +5 -0
  188. package/dist/server/locales/de-DE.js.map +1 -1
  189. package/dist/server/locales/en-US.d.ts +7 -1
  190. package/dist/server/locales/en-US.d.ts.map +1 -1
  191. package/dist/server/locales/en-US.js +5 -0
  192. package/dist/server/locales/en-US.js.map +1 -1
  193. package/dist/server/locales/es-ES.d.ts.map +1 -1
  194. package/dist/server/locales/es-ES.js +5 -0
  195. package/dist/server/locales/es-ES.js.map +1 -1
  196. package/dist/server/locales/fr-FR.d.ts.map +1 -1
  197. package/dist/server/locales/fr-FR.js +5 -0
  198. package/dist/server/locales/fr-FR.js.map +1 -1
  199. package/dist/server/locales/id-ID.d.ts.map +1 -1
  200. package/dist/server/locales/id-ID.js +5 -0
  201. package/dist/server/locales/id-ID.js.map +1 -1
  202. package/dist/server/locales/ja-JP.d.ts.map +1 -1
  203. package/dist/server/locales/ja-JP.js +5 -0
  204. package/dist/server/locales/ja-JP.js.map +1 -1
  205. package/dist/server/locales/pt-BR.d.ts.map +1 -1
  206. package/dist/server/locales/pt-BR.js +5 -0
  207. package/dist/server/locales/pt-BR.js.map +1 -1
  208. package/dist/server/locales/vi-VN.d.ts.map +1 -1
  209. package/dist/server/locales/vi-VN.js +5 -0
  210. package/dist/server/locales/vi-VN.js.map +1 -1
  211. package/dist/server/locales/zh-CN.d.ts +2 -2
  212. package/dist/server/locales/zh-CN.d.ts.map +1 -1
  213. package/dist/server/locales/zh-CN.js +5 -0
  214. package/dist/server/locales/zh-CN.js.map +1 -1
  215. package/dist/server/locales/zh-TW.d.ts.map +1 -1
  216. package/dist/server/locales/zh-TW.js +5 -0
  217. package/dist/server/locales/zh-TW.js.map +1 -1
  218. package/dist/server/online/agent/native/backend.d.ts.map +1 -1
  219. package/dist/server/online/agent/native/backend.js +2 -0
  220. package/dist/server/online/agent/native/backend.js.map +1 -1
  221. package/dist/server/online/agent/native/session.d.ts.map +1 -1
  222. package/dist/server/online/agent/native/session.js +3 -1
  223. package/dist/server/online/agent/native/session.js.map +1 -1
  224. package/dist/server/online/agent/runner/backend.d.ts.map +1 -1
  225. package/dist/server/online/agent/runner/backend.js +21 -9
  226. package/dist/server/online/agent/runner/backend.js.map +1 -1
  227. package/dist/server/online/agent/runner/projection.d.ts +1 -1
  228. package/dist/server/online/agent/runner/projection.d.ts.map +1 -1
  229. package/dist/server/online/agent/runner/projection.js +33 -3
  230. package/dist/server/online/agent/runner/projection.js.map +1 -1
  231. package/dist/server/online/agent/service.d.ts.map +1 -1
  232. package/dist/server/online/agent/service.js +3 -0
  233. package/dist/server/online/agent/service.js.map +1 -1
  234. package/dist/server/online/agent/types.d.ts +3 -0
  235. package/dist/server/online/agent/types.d.ts.map +1 -1
  236. package/dist/server/providers/agents.d.ts +5 -9
  237. package/dist/server/providers/agents.d.ts.map +1 -1
  238. package/dist/server/providers/agents.js +12 -13
  239. package/dist/server/providers/agents.js.map +1 -1
  240. package/dist/server/routes/chat.d.ts.map +1 -1
  241. package/dist/server/routes/chat.js +37 -0
  242. package/dist/server/routes/chat.js.map +1 -1
  243. package/dist/server/routes/runners/admin.d.ts.map +1 -1
  244. package/dist/server/routes/runners/admin.js +16 -7
  245. package/dist/server/routes/runners/admin.js.map +1 -1
  246. package/dist/server/routes/runners/runner.d.ts.map +1 -1
  247. package/dist/server/routes/runners/runner.js +6 -4
  248. package/dist/server/routes/runners/runner.js.map +1 -1
  249. package/dist/server/routes/schemas.d.ts +8 -2
  250. package/dist/server/routes/schemas.d.ts.map +1 -1
  251. package/dist/server/routes/schemas.js +46 -3
  252. package/dist/server/routes/schemas.js.map +1 -1
  253. package/dist/server/tokens.d.ts +1 -2
  254. package/dist/server/tokens.d.ts.map +1 -1
  255. package/dist/server/tokens.js.map +1 -1
  256. package/dist/shared/conversations.d.ts +20 -0
  257. package/dist/shared/conversations.d.ts.map +1 -1
  258. package/dist/shared/conversations.js.map +1 -1
  259. package/dist/shared/runners.d.ts +18 -2
  260. package/dist/shared/runners.d.ts.map +1 -1
  261. package/dist/shared/runners.js +20 -0
  262. package/dist/shared/runners.js.map +1 -1
  263. package/dist/shared/ui-message.d.ts +4 -2
  264. package/dist/shared/ui-message.d.ts.map +1 -1
  265. package/dist/shared/ui-message.js +2 -1
  266. package/dist/shared/ui-message.js.map +1 -1
  267. package/docs/ui-message-clients.md +5 -1
  268. package/docs/ui-messages.md +3 -1
  269. package/docs/vectors-migration.md +91 -0
  270. package/package.json +14 -16
  271. package/skills/nocobase-app-plugin-agents/SKILL.md +1 -1
  272. package/skills/nocobase-app-plugin-agents/references/extension-points.md +0 -1
  273. package/skills/nocobase-app-plugin-agents/references/model-services.md +3 -3
  274. package/skills/nocobase-app-plugin-agents/references/online-agent.md +6 -0
  275. package/skills/nocobase-app-plugin-agents/references/setup-and-config.md +0 -4
  276. package/skills/nocobase-app-plugin-agents/references/verification-and-troubleshooting.md +0 -2
  277. package/dist/server/vectors/index.d.ts +0 -7
  278. package/dist/server/vectors/index.d.ts.map +0 -1
  279. package/dist/server/vectors/index.js +0 -6
  280. package/dist/server/vectors/index.js.map +0 -1
  281. package/dist/server/vectors/naming.d.ts +0 -6
  282. package/dist/server/vectors/naming.d.ts.map +0 -1
  283. package/dist/server/vectors/naming.js +0 -15
  284. package/dist/server/vectors/naming.js.map +0 -1
  285. package/dist/server/vectors/pgvector.d.ts +0 -26
  286. package/dist/server/vectors/pgvector.d.ts.map +0 -1
  287. package/dist/server/vectors/pgvector.js +0 -346
  288. package/dist/server/vectors/pgvector.js.map +0 -1
  289. package/dist/server/vectors/sqlite-vec.d.ts +0 -14
  290. package/dist/server/vectors/sqlite-vec.d.ts.map +0 -1
  291. package/dist/server/vectors/sqlite-vec.js +0 -286
  292. package/dist/server/vectors/sqlite-vec.js.map +0 -1
  293. package/dist/server/vectors/types.d.ts +0 -256
  294. package/dist/server/vectors/types.d.ts.map +0 -1
  295. package/dist/server/vectors/types.js +0 -2
  296. package/dist/server/vectors/types.js.map +0 -1
  297. package/dist/server/vectors/vectors.d.ts +0 -34
  298. package/dist/server/vectors/vectors.d.ts.map +0 -1
  299. package/dist/server/vectors/vectors.js +0 -866
  300. package/dist/server/vectors/vectors.js.map +0 -1
@@ -1,866 +0,0 @@
1
- /**
2
- * Vector collections over a pluggable vector store, with the queue that embeds their items.
3
- *
4
- * - **Stores.** `stores` registers the store types, like model providers; `sqlite-vec` and `pgvector` are there by
5
- * default. The configuration names one (`agents.vectors.store`, `sqlite-vec` by default; `false` turns vectors off),
6
- * apart from the application's database. When the store is unavailable (sqlite-vec cannot load, pgvector cannot be
7
- * reached or has no extension), every collection says why with a code, queues nothing and its search answers null,
8
- * so the caller falls back to keyword search.
9
- * - **Indexes.** An index is one collection's items embedded with one model at one dimension (`agVectorIndexes`),
10
- * named `<collection>_<hash of store type and target, model, dimension and metric>`. Only the indexes of the store
11
- * configured now are used: pointing the store elsewhere retires the others and builds the index again there. The
12
- * registry and the queue stay in the application's database, so they change with the writes they follow.
13
- * `sync()` reads the collection's model: when its
14
- * index is not there it is created and every item of the collection's `items()` queued for it (`sync` returns once
15
- * they are queued; the worker embeds them in the background), while the ready
16
- * index keeps answering searches; once everything was queued and nothing is pending, it becomes ready and the
17
- * older indexes are retired (dropped from the store). The dimension is the model's configured one
18
- * (`dimensionsOf`), else what one probe embedding answers.
19
- * - **Writes** go to every index not retired (the ready one and the one being built). An item is an entry of each
20
- * (`agVectorEntries`) keyed by its id, with the hash of its text: an unchanged text only has its metadata updated in
21
- * the store; a changed or new one is queued.
22
- * - **The worker** of each instance takes due entries with a lease (so two instances never embed the same entry),
23
- * at most `BATCH` of one index at a time, lets the collection `prepare` their texts, embeds them in one call,
24
- * upserts them into the store and marks them done, dropping their text. A failure backs off exponentially; after
25
- * `MAX_ATTEMPTS` the entry is `failed` until the item is written again.
26
- */
27
- import { createHash, randomUUID } from 'node:crypto';
28
- import { resolve } from 'node:path';
29
- import { pgvectorStoreType } from './pgvector.js';
30
- import { SQLITE_VEC, sqliteVecStoreType } from './sqlite-vec.js';
31
- const INDEXES = 'agVectorIndexes';
32
- const ENTRIES = 'agVectorEntries';
33
- /** Values per embedding call. */
34
- export const BATCH = 64;
35
- export const MAX_ATTEMPTS = 5;
36
- /** The first retry's delay; each further one doubles, up to `BACKOFF_MAX_MS`. */
37
- export const BACKOFF_MS = 5_000;
38
- const BACKOFF_MAX_MS = 10 * 60_000;
39
- /** How long a worker holds the entries it took. */
40
- const LEASE_MS = 5 * 60_000;
41
- /** How often an idle worker looks for due entries. */
42
- const POLL_MS = 3_000;
43
- /** How long an unavailable store is believed before it is asked again. */
44
- const RECHECK_MS = 60_000;
45
- const ERROR_MAX = 1000;
46
- const iso = (value) => value === null
47
- ? null
48
- : value instanceof Date
49
- ? value.toISOString()
50
- : new Date(value).toISOString();
51
- const num = (value) => value === null ? 0 : Number(value);
52
- function metadataOf(value) {
53
- if (typeof value === 'string')
54
- try {
55
- return JSON.parse(value);
56
- }
57
- catch {
58
- return {};
59
- }
60
- return value && typeof value === 'object' ? value : {};
61
- }
62
- /** The hash an entry keeps of an item's text. */
63
- export function hashOf(item) {
64
- return createHash('sha256')
65
- .update(`${item.variant ?? ''}\0${item.text}`)
66
- .digest('hex');
67
- }
68
- const sameMetadata = (a, b) => {
69
- const keys = Object.keys(a);
70
- return (keys.length === Object.keys(b).length &&
71
- keys.every((key) => Object.is(a[key], b[key])));
72
- };
73
- /** Whether metadata passes a filter. */
74
- export function matches(metadata, filter) {
75
- return Object.entries(filter).every(([key, wanted]) => {
76
- const value = metadata[key];
77
- if (typeof wanted === 'object' && wanted !== null)
78
- return wanted.in.some((option) => String(option) === String(value));
79
- return value !== undefined && String(value) === String(wanted);
80
- });
81
- }
82
- const stateOf = (entry) => ({
83
- id: entry.itemId,
84
- state: entry.state,
85
- error: entry.state === 'failed' ? entry.error : null,
86
- metadata: metadataOf(entry.metadata),
87
- });
88
- const infoOf = (record, progress) => ({
89
- name: record.name,
90
- modelService: record.modelService,
91
- model: record.model,
92
- dimension: num(record.dimension),
93
- status: record.status === 'ready' ? 'ready' : 'building',
94
- createdAt: iso(record.createdAt),
95
- readyAt: iso(record.readyAt),
96
- ...progress,
97
- });
98
- const backoff = (attempts) => Math.min(BACKOFF_MAX_MS, BACKOFF_MS * 2 ** Math.max(0, attempts - 1));
99
- const NAME = /^[a-z0-9-]{1,40}$/u;
100
- export function createVectorStoreRegistry() {
101
- const types = new Map();
102
- return {
103
- register(type) {
104
- types.set(type.type, type);
105
- return () => {
106
- if (types.get(type.type) === type)
107
- types.delete(type.type);
108
- };
109
- },
110
- get: (type) => types.get(type),
111
- };
112
- }
113
- export function createVectors(deps) {
114
- const onError = deps.onError ??
115
- ((message, error) => console.error(message, error));
116
- const config = deps.config ?? {};
117
- const storeTypeName = config.store === false ? null : (config.store ?? SQLITE_VEC);
118
- const paths = deps.paths ?? {
119
- root: process.cwd(),
120
- storage: resolve(process.cwd(), 'storage'),
121
- };
122
- const stores = createVectorStoreRegistry();
123
- stores.register(sqliteVecStoreType);
124
- stores.register(pgvectorStoreType);
125
- const collections = new Map();
126
- const instance = randomUUID();
127
- const pollMs = deps.pollMs ?? POLL_MS;
128
- const indexes = (conn = deps.tx.read()) => conn.repository(INDEXES);
129
- const entries = (conn = deps.tx.read()) => conn.repository(ENTRIES);
130
- const now = () => deps.clock.now();
131
- // The store, made once from its type; its availability is asked again after `RECHECK_MS` when it was not there.
132
- let store = null;
133
- let checked = null;
134
- /** The store configured, made once from its type; null when vectors are off or its type is not registered. */
135
- function configured() {
136
- if (storeTypeName === null)
137
- return null;
138
- const type = stores.get(storeTypeName);
139
- if (!type)
140
- return null;
141
- if (!store || store.type !== type.type)
142
- store = type.create({ config, paths });
143
- return store;
144
- }
145
- async function usable() {
146
- if (storeTypeName === null)
147
- return {
148
- store: null,
149
- reason: { code: 'VECTORS_OFF', message: 'Vectors are turned off.' },
150
- };
151
- const current = configured();
152
- if (!current)
153
- return {
154
- store: null,
155
- reason: {
156
- code: 'VECTOR_STORE_UNKNOWN',
157
- message: `The vector store type ${storeTypeName} is not registered.`,
158
- },
159
- };
160
- const at = Date.now();
161
- if (!checked || (checked.problem && at - checked.at >= RECHECK_MS)) {
162
- const answer = await current.available().catch((error) => ({
163
- ok: false,
164
- code: 'VECTOR_STORE_FAILED',
165
- message: error instanceof Error ? error.message : String(error),
166
- }));
167
- checked = {
168
- at,
169
- problem: answer.ok
170
- ? null
171
- : { code: answer.code, message: answer.message },
172
- };
173
- if (checked.problem)
174
- onError(`The vector store ${current.type} is unavailable: ${checked.problem.message}`, null);
175
- }
176
- return checked.problem
177
- ? { store: null, reason: checked.problem }
178
- : { store: current, reason: null };
179
- }
180
- /** Whether an index lives in `target`, the store configured now. */
181
- const inStore = (record, target) => record.storeType === target.type && record.storeTarget === target.target;
182
- async function liveIndexes(collection) {
183
- return indexes().findMany({
184
- filter: (f) => f.and([
185
- f.string('collection').eq(collection),
186
- f.string('status').ne('retired'),
187
- ]),
188
- sort: (sort) => sort.field('createdAt').asc(),
189
- });
190
- }
191
- /** The newest ready index of a collection in `target`. */
192
- async function activeOf(collection, target) {
193
- const ready = (await liveIndexes(collection)).filter((record) => record.status === 'ready' && inStore(record, target));
194
- ready.sort((a, b) => (iso(a.readyAt) ?? '').localeCompare(iso(b.readyAt) ?? ''));
195
- return ready.at(-1) ?? null;
196
- }
197
- // ---- writes ----------------------------------------------------------------------------------------------------
198
- /** Writes items to the entries of `index`: unchanged text updates metadata, anything else is queued. */
199
- async function enqueue(target, index, items) {
200
- let queued = 0;
201
- const metadataOnly = [];
202
- const unique = new Map(items.map((item) => [item.id, item]));
203
- const list = [...unique.values()];
204
- for (let at = 0; at < list.length; at += 100) {
205
- const batch = list.slice(at, at + 100);
206
- await deps.tx.run(async ({ conn }) => {
207
- const existing = new Map((await entries(conn).findMany({
208
- filter: (f) => f.and([
209
- f.string('indexName').eq(index.name),
210
- f.or(batch.map((item) => f.string('itemId').eq(item.id))),
211
- ]),
212
- })).map((entry) => [entry.itemId, entry]));
213
- const stamp = now();
214
- for (const item of batch) {
215
- const hash = hashOf(item);
216
- const entry = existing.get(item.id);
217
- const values = {
218
- hash,
219
- text: item.text,
220
- variant: item.variant ?? null,
221
- metadata: item.metadata,
222
- state: 'pending',
223
- attempts: 0,
224
- nextAt: stamp.getTime(),
225
- error: null,
226
- updatedAt: stamp.toISOString(),
227
- };
228
- if (!entry) {
229
- await entries(conn).createOne({
230
- values: {
231
- id: deps.ids.next(),
232
- indexName: index.name,
233
- itemId: item.id,
234
- leaseUntil: null,
235
- leasedBy: null,
236
- ...values,
237
- },
238
- });
239
- queued += 1;
240
- }
241
- else if (entry.hash === hash && entry.state === 'done') {
242
- if (!sameMetadata(metadataOf(entry.metadata), item.metadata)) {
243
- await entries(conn).updateMany({
244
- filter: { id: entry.id },
245
- values: {
246
- metadata: item.metadata,
247
- updatedAt: stamp.toISOString(),
248
- },
249
- });
250
- metadataOnly.push({ id: item.id, metadata: item.metadata });
251
- }
252
- }
253
- else if (entry.hash === hash &&
254
- entry.state === 'pending' &&
255
- sameMetadata(metadataOf(entry.metadata), item.metadata)) {
256
- // Already queued as it is.
257
- }
258
- else {
259
- await entries(conn).updateMany({
260
- filter: { id: entry.id },
261
- values,
262
- });
263
- queued += 1;
264
- }
265
- }
266
- });
267
- }
268
- if (metadataOnly.length > 0)
269
- try {
270
- await target.update(index.name, metadataOnly);
271
- }
272
- catch (error) {
273
- // Embedded again, the item carries its metadata with it.
274
- onError(`The vector store could not update metadata in ${index.name}; the items are queued again.`, error);
275
- await entries().updateMany({
276
- filter: (f) => f.and([
277
- f.string('indexName').eq(index.name),
278
- f.or(metadataOnly.map((item) => f.string('itemId').eq(item.id))),
279
- ]),
280
- values: { state: 'pending', nextAt: now().getTime() },
281
- });
282
- queued += metadataOnly.length;
283
- }
284
- return queued;
285
- }
286
- /** Removes items from `index`'s entries and the store. */
287
- async function removeFrom(target, index, which) {
288
- if ('ids' in which) {
289
- if (which.ids.length === 0)
290
- return;
291
- for (let at = 0; at < which.ids.length; at += 100) {
292
- const batch = which.ids.slice(at, at + 100);
293
- await entries().deleteMany({
294
- filter: (f) => f.and([
295
- f.string('indexName').eq(index.name),
296
- f.or(batch.map((id) => f.string('itemId').eq(id))),
297
- ]),
298
- });
299
- }
300
- await target.delete(index.name, { ids: which.ids });
301
- return;
302
- }
303
- const doomed = (await allEntries(index.name))
304
- .filter((entry) => matches(metadataOf(entry.metadata), which.filter))
305
- .map((entry) => entry.itemId);
306
- for (let at = 0; at < doomed.length; at += 100) {
307
- const batch = doomed.slice(at, at + 100);
308
- await entries().deleteMany({
309
- filter: (f) => f.and([
310
- f.string('indexName').eq(index.name),
311
- f.or(batch.map((id) => f.string('itemId').eq(id))),
312
- ]),
313
- });
314
- }
315
- await target.delete(index.name, { filter: which.filter });
316
- }
317
- /** Every entry of an index, its item id and metadata only. */
318
- async function allEntries(indexName) {
319
- return entries().findMany({ filter: { indexName } });
320
- }
321
- // ---- indexes ---------------------------------------------------------------------------------------------------
322
- /** Marks a building index ready when everything was queued and nothing is pending, retiring the older ones. */
323
- async function settle(index) {
324
- if (index.status !== 'building' || index.enumeratedAt === null)
325
- return false;
326
- const pending = await entries().count({
327
- filter: { indexName: index.name, state: 'pending' },
328
- });
329
- if (pending > 0)
330
- return false;
331
- const claimed = await indexes().updateMany({
332
- filter: (f) => f.and([
333
- f.string('name').eq(index.name),
334
- f.string('status').eq('building'),
335
- ]),
336
- values: { status: 'ready', readyAt: now().toISOString() },
337
- });
338
- if (claimed.updatedCount !== 1)
339
- return false;
340
- for (const other of await liveIndexes(index.collection))
341
- if (other.name !== index.name)
342
- await retire(other);
343
- return true;
344
- }
345
- async function retire(index) {
346
- await indexes().updateMany({
347
- filter: { name: index.name },
348
- values: { status: 'retired' },
349
- });
350
- await entries().deleteMany({ filter: { indexName: index.name } });
351
- const { store: target } = await usable();
352
- if (target)
353
- await target
354
- .dropIndex(index.name)
355
- .catch((error) => onError(`The vector store could not drop ${index.name}.`, error));
356
- }
357
- /** Queues every item of the collection for a building index, then lets it settle. */
358
- async function enumerate(registered, target, index) {
359
- if (registered.enumerating.has(index.name))
360
- return;
361
- registered.enumerating.add(index.name);
362
- try {
363
- const seen = new Set();
364
- for await (const batch of registered.spec.items()) {
365
- if (collections.get(registered.spec.name) !== registered)
366
- return;
367
- for (const item of batch)
368
- seen.add(item.id);
369
- if ((await enqueue(target, index, batch)) > 0)
370
- wake();
371
- }
372
- // Entries of items no longer there (removed while the index was built from an older listing).
373
- const stale = (await allEntries(index.name))
374
- .map((entry) => entry.itemId)
375
- .filter((id) => !seen.has(id));
376
- if (stale.length > 0)
377
- await removeFrom(target, index, { ids: stale });
378
- await indexes().updateMany({
379
- filter: { name: index.name },
380
- values: { enumeratedAt: now().toISOString() },
381
- });
382
- const fresh = (await indexes().findMany({ filter: { name: index.name } }))[0];
383
- if (fresh)
384
- await settle(fresh);
385
- wake();
386
- }
387
- finally {
388
- registered.enumerating.delete(index.name);
389
- }
390
- }
391
- async function dimensionOf(spec, model) {
392
- const configured = await deps.dimensionsOf?.(model);
393
- if (configured && configured > 0)
394
- return configured;
395
- const probe = await deps.embed({
396
- model,
397
- values: ['dimension probe'],
398
- source: spec.source,
399
- });
400
- if (!(probe.dimension > 0))
401
- throw new Error(`The model ${model.modelService}/${model.model} answered no embedding.`);
402
- return probe.dimension;
403
- }
404
- async function sync(registered) {
405
- const { spec } = registered;
406
- const { store: target } = await usable();
407
- if (!target)
408
- return;
409
- const model = await spec.model();
410
- if (!model)
411
- return;
412
- const metric = spec.metric ?? 'cosine';
413
- const dimension = await dimensionOf(spec, model);
414
- const name = `${spec.name}_${createHash('sha256')
415
- .update([
416
- target.type,
417
- target.target,
418
- model.modelService,
419
- model.model,
420
- dimension,
421
- metric,
422
- ].join('\0'))
423
- .digest('hex')
424
- .slice(0, 10)}`;
425
- // Indexes of another store (or the same type pointed elsewhere) cannot serve from this one.
426
- for (const other of await liveIndexes(spec.name))
427
- if (!inStore(other, target))
428
- await retire(other);
429
- const live = await liveIndexes(spec.name);
430
- let index = live.find((record) => record.name === name);
431
- if (index?.status === 'ready') {
432
- for (const other of live)
433
- if (other.name !== name)
434
- await retire(other);
435
- return;
436
- }
437
- // Another model's index being built is no longer wanted; the ready one keeps serving.
438
- for (const other of live)
439
- if (other.name !== name && other.status === 'building')
440
- await retire(other);
441
- await target.createIndex({
442
- name,
443
- dimension,
444
- metric,
445
- ...(spec.filterable ? { filterable: spec.filterable } : {}),
446
- });
447
- if (!index) {
448
- const stamp = now().toISOString();
449
- await indexes().deleteMany({ filter: { name } });
450
- await indexes().createOne({
451
- values: {
452
- name,
453
- collection: spec.name,
454
- storeType: target.type,
455
- storeTarget: target.target,
456
- modelService: model.modelService,
457
- model: model.model,
458
- dimension,
459
- metric,
460
- status: 'building',
461
- enumeratedAt: null,
462
- createdAt: stamp,
463
- readyAt: null,
464
- },
465
- });
466
- index = (await indexes().findMany({ filter: { name } }))[0];
467
- }
468
- // Queues every item; the worker embeds them in the background.
469
- await enumerate(registered, target, index);
470
- }
471
- // ---- the worker ------------------------------------------------------------------------------------------------
472
- let running = false;
473
- let loop = null;
474
- let waiting = null;
475
- let abort = new AbortController();
476
- function wake() {
477
- const resolve = waiting;
478
- waiting = null;
479
- resolve?.();
480
- }
481
- /** Embeds one batch of due entries; false when nothing was due. */
482
- async function step() {
483
- if (collections.size === 0)
484
- return false;
485
- const { store: target } = await usable();
486
- if (!target)
487
- return false;
488
- const live = (await indexes().findMany({
489
- filter: (f) => f.and([
490
- f.string('status').ne('retired'),
491
- f.or([...collections.keys()].map((name) => f.string('collection').eq(name))),
492
- ]),
493
- })).filter((record) => inStore(record, target));
494
- if (live.length === 0)
495
- return false;
496
- const at = now().getTime();
497
- const due = (indexName) => entries().findMany({
498
- filter: (f) => f.and([
499
- indexName
500
- ? f.string('indexName').eq(indexName)
501
- : f.or(live.map((index) => f.string('indexName').eq(index.name))),
502
- f.string('state').eq('pending'),
503
- f.number('nextAt').lte(at),
504
- f.or([
505
- f.number('leaseUntil').empty(),
506
- f.number('leaseUntil').lt(at),
507
- ]),
508
- ]),
509
- sort: (sort) => sort.field('nextAt').asc(),
510
- limit: BATCH,
511
- });
512
- const first = (await due()).at(0);
513
- if (!first) {
514
- for (const index of live)
515
- await settle(index);
516
- return false;
517
- }
518
- const index = live.find((record) => record.name === first.indexName);
519
- const candidates = await due(index.name);
520
- const leaseUntil = at + LEASE_MS;
521
- await entries().updateMany({
522
- filter: (f) => f.and([
523
- f.or(candidates.map((entry) => f.string('id').eq(entry.id))),
524
- f.string('state').eq('pending'),
525
- f.or([f.number('leaseUntil').empty(), f.number('leaseUntil').lt(at)]),
526
- ]),
527
- values: { leasedBy: instance, leaseUntil },
528
- });
529
- const taken = await entries().findMany({
530
- filter: (f) => f.and([
531
- f.string('indexName').eq(index.name),
532
- f.string('leasedBy').eq(instance),
533
- f.number('leaseUntil').eq(leaseUntil),
534
- ]),
535
- });
536
- if (taken.length === 0)
537
- return true;
538
- const registered = collections.get(index.collection);
539
- if (!registered)
540
- return true;
541
- await embedBatch(registered.spec, target, index, taken);
542
- await settle(index);
543
- return true;
544
- }
545
- async function embedBatch(spec, target, index, taken) {
546
- const release = (ids, values) => ids.length === 0
547
- ? Promise.resolve()
548
- : entries().updateMany({
549
- filter: (f) => f.and([
550
- f.or(ids.map((id) => f.string('id').eq(id))),
551
- f.string('leasedBy').eq(instance),
552
- ]),
553
- values: {
554
- leasedBy: null,
555
- leaseUntil: null,
556
- updatedAt: now().toISOString(),
557
- ...values,
558
- },
559
- });
560
- const items = taken.map((entry) => ({
561
- id: entry.itemId,
562
- text: entry.text ?? '',
563
- metadata: metadataOf(entry.metadata),
564
- ...(entry.variant ? { variant: entry.variant } : {}),
565
- }));
566
- try {
567
- const texts = spec.prepare
568
- ? await spec.prepare(items, abort.signal)
569
- : items.map((item) => item.text);
570
- if (texts.length !== items.length)
571
- throw new Error('The collection prepared a different number of texts.');
572
- const answer = await deps.embed({
573
- model: { modelService: index.modelService, model: index.model },
574
- values: texts,
575
- source: spec.source,
576
- signal: abort.signal,
577
- });
578
- const dimension = num(index.dimension);
579
- if (answer.embeddings.length !== items.length ||
580
- answer.embeddings.some((vector) => vector.length !== dimension))
581
- throw new Error(`The model answered embeddings of another dimension than the index's ${dimension}.`);
582
- await target.upsert(index.name, items.map((item, at) => ({
583
- id: item.id,
584
- vector: answer.embeddings[at],
585
- metadata: item.metadata,
586
- })));
587
- // Done unless the item was written again meanwhile (another hash): then it stays queued for its new text.
588
- for (const entry of taken)
589
- await entries().updateMany({
590
- filter: (f) => f.and([
591
- f.string('id').eq(entry.id),
592
- f.string('hash').eq(entry.hash),
593
- f.string('leasedBy').eq(instance),
594
- ]),
595
- values: {
596
- state: 'done',
597
- text: null,
598
- error: null,
599
- attempts: 0,
600
- leasedBy: null,
601
- leaseUntil: null,
602
- updatedAt: now().toISOString(),
603
- },
604
- });
605
- await release(taken.map((entry) => entry.id), {});
606
- }
607
- catch (error) {
608
- if (abort.signal.aborted) {
609
- await release(taken.map((entry) => entry.id), {});
610
- return;
611
- }
612
- const message = (error instanceof Error ? error.message : String(error)).slice(0, ERROR_MAX);
613
- onError(`Vectors could not embed ${taken.length} items of ${index.name}.`, error);
614
- const stamp = now().getTime();
615
- for (const entry of taken) {
616
- const attempts = num(entry.attempts) + 1;
617
- await release([entry.id], {
618
- attempts,
619
- error: message,
620
- ...(attempts >= MAX_ATTEMPTS
621
- ? { state: 'failed' }
622
- : { nextAt: stamp + backoff(attempts) }),
623
- });
624
- }
625
- }
626
- }
627
- async function run() {
628
- while (running) {
629
- let worked = false;
630
- try {
631
- worked = await step();
632
- }
633
- catch (error) {
634
- onError('The vector worker failed a step.', error);
635
- }
636
- if (worked || !running)
637
- continue;
638
- await new Promise((resolve) => {
639
- const timer = setTimeout(() => {
640
- waiting = null;
641
- resolve();
642
- }, pollMs);
643
- waiting = () => {
644
- clearTimeout(timer);
645
- resolve();
646
- };
647
- });
648
- }
649
- }
650
- function collection(spec) {
651
- if (!NAME.test(spec.name))
652
- throw new Error(`The vector collection name ${spec.name} is not usable.`);
653
- const registered = { spec, enumerating: new Set() };
654
- collections.set(spec.name, registered);
655
- const mine = () => collections.get(spec.name) === registered;
656
- /** The live indexes to write to, synced first when there are none; null when there is nowhere to write. */
657
- async function targets() {
658
- const { store: target } = await usable();
659
- if (!target || !mine())
660
- return null;
661
- const here = async () => (await liveIndexes(spec.name)).filter((record) => inStore(record, target));
662
- let live = await here();
663
- if (live.length === 0) {
664
- await sync(registered);
665
- live = await here();
666
- }
667
- return live.length > 0 ? { store: target, indexes: live } : null;
668
- }
669
- /** The index item states are read from: the one searches read, else the one being built. */
670
- async function reported() {
671
- const { store: target } = await usable();
672
- if (!target || !(await spec.model()))
673
- return null;
674
- const active = await activeOf(spec.name, target);
675
- if (active)
676
- return active;
677
- return ((await liveIndexes(spec.name))
678
- .filter((record) => record.status === 'building' && inStore(record, target))
679
- .at(-1) ?? null);
680
- }
681
- return {
682
- async status() {
683
- const { store: target, reason } = await usable();
684
- const model = await spec.model();
685
- const live = target
686
- ? (await liveIndexes(spec.name)).filter((record) => inStore(record, target))
687
- : [];
688
- const active = target ? await activeOf(spec.name, target) : null;
689
- const building = live.filter((record) => record.status === 'building').at(-1) ?? null;
690
- const count = async (state) => {
691
- let total = 0;
692
- for (const index of live)
693
- total += await entries().count({
694
- filter: { indexName: index.name, state },
695
- });
696
- return total;
697
- };
698
- const info = async (record) => infoOf(record, {
699
- indexed: await entries().count({
700
- filter: { indexName: record.name, state: 'done' },
701
- }),
702
- total: await entries().count({
703
- filter: { indexName: record.name },
704
- }),
705
- });
706
- return {
707
- available: target !== null,
708
- store: storeInfo(),
709
- reason,
710
- model,
711
- active: active ? await info(active) : null,
712
- building: building ? await info(building) : null,
713
- pending: await count('pending'),
714
- failed: await count('failed'),
715
- };
716
- },
717
- async states(ids) {
718
- const index = await reported();
719
- if (!index)
720
- return null;
721
- const found = new Map();
722
- const unique = [...new Set(ids)];
723
- for (let at = 0; at < unique.length; at += 100) {
724
- const batch = unique.slice(at, at + 100);
725
- for (const entry of await entries().findMany({
726
- filter: (f) => f.and([
727
- f.string('indexName').eq(index.name),
728
- f.or(batch.map((id) => f.string('itemId').eq(id))),
729
- ]),
730
- }))
731
- found.set(entry.itemId, stateOf(entry));
732
- }
733
- return found;
734
- },
735
- async unfinished({ filter, limit }) {
736
- const index = await reported();
737
- if (!index)
738
- return null;
739
- const open = await entries().findMany({
740
- filter: (f) => f.and([
741
- f.string('indexName').eq(index.name),
742
- f.string('state').ne('done'),
743
- ]),
744
- });
745
- return open
746
- .map(stateOf)
747
- .filter((item) => !filter || matches(item.metadata, filter))
748
- .sort((a, b) => Number(b.state === 'failed') - Number(a.state === 'failed') ||
749
- a.id.localeCompare(b.id))
750
- .slice(0, Math.max(0, limit));
751
- },
752
- async upsert(items) {
753
- if (items.length === 0)
754
- return;
755
- const found = await targets();
756
- if (!found)
757
- return;
758
- let queued = 0;
759
- for (const index of found.indexes)
760
- queued += await enqueue(found.store, index, items);
761
- if (queued > 0)
762
- wake();
763
- },
764
- async replace(scope, items) {
765
- const found = await targets();
766
- if (!found)
767
- return;
768
- const keep = new Set(items.map((item) => item.id));
769
- let queued = 0;
770
- for (const index of found.indexes) {
771
- const others = (await allEntries(index.name))
772
- .filter((entry) => !keep.has(entry.itemId) &&
773
- matches(metadataOf(entry.metadata), scope))
774
- .map((entry) => entry.itemId);
775
- if (others.length > 0)
776
- await removeFrom(found.store, index, { ids: others });
777
- queued += await enqueue(found.store, index, items);
778
- }
779
- if (queued > 0)
780
- wake();
781
- },
782
- async remove(target) {
783
- const { store: found } = await usable();
784
- if (!found)
785
- return;
786
- for (const index of await liveIndexes(spec.name))
787
- if (inStore(index, found))
788
- await removeFrom(found, index, target);
789
- },
790
- async search(query, options) {
791
- const { store: target } = await usable();
792
- if (!target || !query.trim())
793
- return target ? [] : null;
794
- if (!(await spec.model()))
795
- return null;
796
- const active = await activeOf(spec.name, target);
797
- if (!active)
798
- return null;
799
- try {
800
- const answer = await deps.embed({
801
- model: { modelService: active.modelService, model: active.model },
802
- values: [query],
803
- source: spec.source,
804
- ...(options.signal ? { signal: options.signal } : {}),
805
- });
806
- const vector = answer.embeddings[0];
807
- if (!vector || vector.length !== num(active.dimension))
808
- return null;
809
- const found = await target.query(active.name, {
810
- vector,
811
- topK: options.topK,
812
- ...(options.filter ? { filter: options.filter } : {}),
813
- });
814
- return found;
815
- }
816
- catch (error) {
817
- onError(`Vectors could not search ${spec.name}.`, error);
818
- return null;
819
- }
820
- },
821
- sync: () => sync(registered),
822
- close() {
823
- if (mine())
824
- collections.delete(spec.name);
825
- },
826
- };
827
- }
828
- function storeInfo() {
829
- if (storeTypeName === null)
830
- return null;
831
- return { type: storeTypeName, target: configured()?.target ?? null };
832
- }
833
- return {
834
- stores,
835
- async status() {
836
- const { store: target, reason } = await usable();
837
- return { available: target !== null, store: storeInfo(), reason };
838
- },
839
- collection,
840
- start() {
841
- if (running)
842
- return;
843
- running = true;
844
- abort = new AbortController();
845
- loop = run();
846
- },
847
- async stop() {
848
- running = false;
849
- abort.abort();
850
- wake();
851
- await loop;
852
- loop = null;
853
- const current = store;
854
- store = null;
855
- checked = null;
856
- await current
857
- ?.close?.()
858
- .catch((error) => onError('The vector store could not be closed.', error));
859
- },
860
- async drain() {
861
- for (let guard = 0; guard < 10_000 && (await step()); guard += 1)
862
- ;
863
- },
864
- };
865
- }
866
- //# sourceMappingURL=vectors.js.map