enya-agent 0.1.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 (389) hide show
  1. package/.env.example +20 -0
  2. package/.github/workflows/ci.yml +70 -0
  3. package/.github/workflows/publish.yml +250 -0
  4. package/.gitmodules +3 -0
  5. package/Cargo.lock +3584 -0
  6. package/Cargo.toml +97 -0
  7. package/crates/enact/Cargo.toml +27 -0
  8. package/crates/enact/src/lib.rs +60 -0
  9. package/crates/enact-a2a/Cargo.toml +25 -0
  10. package/crates/enact-a2a/src/lib.rs +411 -0
  11. package/crates/enact-channels/Cargo.toml +64 -0
  12. package/crates/enact-channels/examples/README.md +80 -0
  13. package/crates/enact-channels/examples/channel_bot.rs +169 -0
  14. package/crates/enact-channels/examples/telegram-echo.rs +34 -0
  15. package/crates/enact-channels/examples/whatsapp-echo.rs +142 -0
  16. package/crates/enact-channels/src/config.rs +213 -0
  17. package/crates/enact-channels/src/lib.rs +25 -0
  18. package/crates/enact-channels/src/runtime.rs +237 -0
  19. package/crates/enact-channels/src/security/mod.rs +5 -0
  20. package/crates/enact-channels/src/security/pairing.rs +205 -0
  21. package/crates/enact-channels/src/teams.rs +601 -0
  22. package/crates/enact-channels/src/telegram.rs +2833 -0
  23. package/crates/enact-channels/src/traits.rs +200 -0
  24. package/crates/enact-channels/src/webhook.rs +262 -0
  25. package/crates/enact-channels/src/whatsapp.rs +310 -0
  26. package/crates/enact-cli/Cargo.toml +40 -0
  27. package/crates/enact-cli/src/commands/doctor.rs +62 -0
  28. package/crates/enact-cli/src/commands/mod.rs +3 -0
  29. package/crates/enact-cli/src/commands/run.rs +69 -0
  30. package/crates/enact-cli/src/commands/serve.rs +81 -0
  31. package/crates/enact-cli/src/config.rs +2 -0
  32. package/crates/enact-cli/src/main.rs +79 -0
  33. package/crates/enact-config/Cargo.toml +36 -0
  34. package/crates/enact-config/ENV_VAR_MAPPING.md +135 -0
  35. package/crates/enact-config/QUICK_REFERENCE.md +92 -0
  36. package/crates/enact-config/README.md +107 -0
  37. package/crates/enact-config/TESTING.md +161 -0
  38. package/crates/enact-config/examples/test-env-vars.rs +100 -0
  39. package/crates/enact-config/src/config.rs +399 -0
  40. package/crates/enact-config/src/encrypted_store.rs +211 -0
  41. package/crates/enact-config/src/lib.rs +298 -0
  42. package/crates/enact-config/src/secrets.rs +149 -0
  43. package/crates/enact-config/src/sync.rs +260 -0
  44. package/crates/enact-config/test-env-vars.sh +34 -0
  45. package/crates/enact-config/tests/README.md +99 -0
  46. package/crates/enact-config/tests/config_integration_test.rs +202 -0
  47. package/crates/enact-config/tests/security_test.rs +140 -0
  48. package/crates/enact-context/Cargo.toml +41 -0
  49. package/crates/enact-context/src/budget.rs +314 -0
  50. package/crates/enact-context/src/calibrator.rs +535 -0
  51. package/crates/enact-context/src/compactor.rs +392 -0
  52. package/crates/enact-context/src/condenser.rs +826 -0
  53. package/crates/enact-context/src/lib.rs +94 -0
  54. package/crates/enact-context/src/segment.rs +238 -0
  55. package/crates/enact-context/src/step_context.rs +645 -0
  56. package/crates/enact-context/src/token_counter.rs +148 -0
  57. package/crates/enact-context/src/window.rs +372 -0
  58. package/crates/enact-core/Cargo.toml +42 -0
  59. package/crates/enact-core/README.md +98 -0
  60. package/crates/enact-core/src/background/executor.rs +524 -0
  61. package/crates/enact-core/src/background/mod.rs +48 -0
  62. package/crates/enact-core/src/background/target_binding.rs +390 -0
  63. package/crates/enact-core/src/background/trigger.rs +511 -0
  64. package/crates/enact-core/src/callable/callable.rs +152 -0
  65. package/crates/enact-core/src/callable/composite.rs +817 -0
  66. package/crates/enact-core/src/callable/graph.rs +104 -0
  67. package/crates/enact-core/src/callable/llm.rs +211 -0
  68. package/crates/enact-core/src/callable/mod.rs +64 -0
  69. package/crates/enact-core/src/callable/registry.rs +206 -0
  70. package/crates/enact-core/src/context/execution_context.rs +757 -0
  71. package/crates/enact-core/src/context/invocation.rs +99 -0
  72. package/crates/enact-core/src/context/mod.rs +50 -0
  73. package/crates/enact-core/src/context/tenant.rs +175 -0
  74. package/crates/enact-core/src/context/trace.rs +127 -0
  75. package/crates/enact-core/src/flow/conditional.rs +293 -0
  76. package/crates/enact-core/src/flow/mod.rs +43 -0
  77. package/crates/enact-core/src/flow/parallel.rs +437 -0
  78. package/crates/enact-core/src/flow/repeat.rs +534 -0
  79. package/crates/enact-core/src/flow/sequential.rs +248 -0
  80. package/crates/enact-core/src/graph/checkpoint.rs +79 -0
  81. package/crates/enact-core/src/graph/checkpoint_store.rs +76 -0
  82. package/crates/enact-core/src/graph/compiled.rs +189 -0
  83. package/crates/enact-core/src/graph/edge.rs +59 -0
  84. package/crates/enact-core/src/graph/graph_schema.rs +218 -0
  85. package/crates/enact-core/src/graph/loader.rs +155 -0
  86. package/crates/enact-core/src/graph/mod.rs +18 -0
  87. package/crates/enact-core/src/graph/node/function.rs +49 -0
  88. package/crates/enact-core/src/graph/node/mod.rs +48 -0
  89. package/crates/enact-core/src/graph/schema.rs +62 -0
  90. package/crates/enact-core/src/inbox/message.rs +405 -0
  91. package/crates/enact-core/src/inbox/mod.rs +31 -0
  92. package/crates/enact-core/src/inbox/store.rs +355 -0
  93. package/crates/enact-core/src/kernel/artifact/filesystem.rs +546 -0
  94. package/crates/enact-core/src/kernel/artifact/metadata.rs +283 -0
  95. package/crates/enact-core/src/kernel/artifact/mod.rs +27 -0
  96. package/crates/enact-core/src/kernel/artifact/store.rs +427 -0
  97. package/crates/enact-core/src/kernel/enforcement.rs +1315 -0
  98. package/crates/enact-core/src/kernel/error.rs +1200 -0
  99. package/crates/enact-core/src/kernel/event.rs +1394 -0
  100. package/crates/enact-core/src/kernel/execution_model.rs +831 -0
  101. package/crates/enact-core/src/kernel/execution_state.rs +189 -0
  102. package/crates/enact-core/src/kernel/execution_strategy.rs +117 -0
  103. package/crates/enact-core/src/kernel/ids.rs +2086 -0
  104. package/crates/enact-core/src/kernel/interrupt.rs +125 -0
  105. package/crates/enact-core/src/kernel/kernel.rs +1283 -0
  106. package/crates/enact-core/src/kernel/mod.rs +205 -0
  107. package/crates/enact-core/src/kernel/persistence/event_store.rs +270 -0
  108. package/crates/enact-core/src/kernel/persistence/message_store.rs +908 -0
  109. package/crates/enact-core/src/kernel/persistence/mod.rs +102 -0
  110. package/crates/enact-core/src/kernel/persistence/state_store.rs +228 -0
  111. package/crates/enact-core/src/kernel/persistence/vector_store.rs +299 -0
  112. package/crates/enact-core/src/kernel/reducer.rs +808 -0
  113. package/crates/enact-core/src/kernel/replay.rs +153 -0
  114. package/crates/enact-core/src/lib.rs +413 -0
  115. package/crates/enact-core/src/memory/episodic.rs +0 -0
  116. package/crates/enact-core/src/memory/mod.rs +6 -0
  117. package/crates/enact-core/src/memory/semantic.rs +0 -0
  118. package/crates/enact-core/src/memory/trait.rs +0 -0
  119. package/crates/enact-core/src/memory/vector_db.rs +0 -0
  120. package/crates/enact-core/src/memory/working.rs +0 -0
  121. package/crates/enact-core/src/policy/execution_policy.rs +292 -0
  122. package/crates/enact-core/src/policy/filters.rs +458 -0
  123. package/crates/enact-core/src/policy/input_processor.rs +407 -0
  124. package/crates/enact-core/src/policy/long_running.rs +134 -0
  125. package/crates/enact-core/src/policy/mod.rs +193 -0
  126. package/crates/enact-core/src/policy/pii_input.rs +274 -0
  127. package/crates/enact-core/src/policy/tenant_policy.rs +453 -0
  128. package/crates/enact-core/src/policy/tool_policy.rs +407 -0
  129. package/crates/enact-core/src/providers/mod.rs +63 -0
  130. package/crates/enact-core/src/providers/trait.rs +292 -0
  131. package/crates/enact-core/src/runner/callbacks.rs +6 -0
  132. package/crates/enact-core/src/runner/execution_runner.rs +476 -0
  133. package/crates/enact-core/src/runner/loop.rs +117 -0
  134. package/crates/enact-core/src/runner/mod.rs +58 -0
  135. package/crates/enact-core/src/runner/protected_runner.rs +280 -0
  136. package/crates/enact-core/src/signal/inmemory.rs +231 -0
  137. package/crates/enact-core/src/signal/mod.rs +108 -0
  138. package/crates/enact-core/src/streaming/event_logger.rs +195 -0
  139. package/crates/enact-core/src/streaming/event_stream.rs +1423 -0
  140. package/crates/enact-core/src/streaming/mod.rs +108 -0
  141. package/crates/enact-core/src/streaming/pause_cancel.rs +0 -0
  142. package/crates/enact-core/src/streaming/protected_emitter.rs +173 -0
  143. package/crates/enact-core/src/streaming/protection/context.rs +136 -0
  144. package/crates/enact-core/src/streaming/protection/encryption.rs +289 -0
  145. package/crates/enact-core/src/streaming/protection/mod.rs +43 -0
  146. package/crates/enact-core/src/streaming/protection/pii_protection.rs +243 -0
  147. package/crates/enact-core/src/streaming/protection/processor.rs +166 -0
  148. package/crates/enact-core/src/streaming/sse.rs +0 -0
  149. package/crates/enact-core/src/telemetry/exporter.rs +0 -0
  150. package/crates/enact-core/src/telemetry/init.rs +0 -0
  151. package/crates/enact-core/src/telemetry/mod.rs +49 -0
  152. package/crates/enact-core/src/telemetry/spans.rs +245 -0
  153. package/crates/enact-core/src/tool/agent_tool.rs +177 -0
  154. package/crates/enact-core/src/tool/browser/mod.rs +0 -0
  155. package/crates/enact-core/src/tool/browser/webdriver.rs +0 -0
  156. package/crates/enact-core/src/tool/cost.rs +247 -0
  157. package/crates/enact-core/src/tool/discovery.rs +0 -0
  158. package/crates/enact-core/src/tool/dispatcher.rs +347 -0
  159. package/crates/enact-core/src/tool/filesystem.rs +231 -0
  160. package/crates/enact-core/src/tool/function.rs +99 -0
  161. package/crates/enact-core/src/tool/git.rs +162 -0
  162. package/crates/enact-core/src/tool/http.rs +214 -0
  163. package/crates/enact-core/src/tool/mcp/client.rs +0 -0
  164. package/crates/enact-core/src/tool/mcp/mod.rs +0 -0
  165. package/crates/enact-core/src/tool/mod.rs +51 -0
  166. package/crates/enact-core/src/tool/reasoning/debugging.rs +0 -0
  167. package/crates/enact-core/src/tool/reasoning/mcts.rs +0 -0
  168. package/crates/enact-core/src/tool/reasoning/mod.rs +0 -0
  169. package/crates/enact-core/src/tool/reasoning/sequential.rs +0 -0
  170. package/crates/enact-core/src/tool/sandbox/dagger.rs +0 -0
  171. package/crates/enact-core/src/tool/sandbox/mod.rs +0 -0
  172. package/crates/enact-core/src/tool/shell.rs +147 -0
  173. package/crates/enact-core/src/tool/trait.rs +33 -0
  174. package/crates/enact-core/src/tool/web_search.rs +277 -0
  175. package/crates/enact-core/src/util/config.rs +0 -0
  176. package/crates/enact-core/src/util/errors.rs +0 -0
  177. package/crates/enact-core/src/util/mod.rs +6 -0
  178. package/crates/enact-core/tests/airgapped_e2e_test.rs +291 -0
  179. package/crates/enact-core/tests/e2e_agentic_loop.rs +119 -0
  180. package/crates/enact-core/tests/e2e_test.rs +259 -0
  181. package/crates/enact-core/tests/graph_test.rs +130 -0
  182. package/crates/enact-core/tests/stream_event_id_validation.rs +435 -0
  183. package/crates/enact-cron/Cargo.toml +28 -0
  184. package/crates/enact-cron/src/lib.rs +44 -0
  185. package/crates/enact-cron/src/schedule.rs +156 -0
  186. package/crates/enact-cron/src/store.rs +589 -0
  187. package/crates/enact-cron/src/types.rs +148 -0
  188. package/crates/enact-gateway/Cargo.toml +31 -0
  189. package/crates/enact-gateway/README.md +30 -0
  190. package/crates/enact-gateway/examples/whatsapp-gateway-runner-mock.rs +59 -0
  191. package/crates/enact-gateway/examples/whatsapp-gateway.rs +42 -0
  192. package/crates/enact-gateway/src/lib.rs +582 -0
  193. package/crates/enact-mcp/Cargo.toml +24 -0
  194. package/crates/enact-mcp/src/lib.rs +178 -0
  195. package/crates/enact-memory/Cargo.toml +25 -0
  196. package/crates/enact-memory/src/backend.rs +20 -0
  197. package/crates/enact-memory/src/chunker.rs +230 -0
  198. package/crates/enact-memory/src/embeddings.rs +221 -0
  199. package/crates/enact-memory/src/lib.rs +67 -0
  200. package/crates/enact-memory/src/markdown.rs +127 -0
  201. package/crates/enact-memory/src/none.rs +61 -0
  202. package/crates/enact-memory/src/sqlite.rs +276 -0
  203. package/crates/enact-memory/src/traits.rs +65 -0
  204. package/crates/enact-memory/src/vector.rs +198 -0
  205. package/crates/enact-oauth/Cargo.toml +27 -0
  206. package/crates/enact-oauth/src/lib.rs +584 -0
  207. package/crates/enact-observability/Cargo.toml +22 -0
  208. package/crates/enact-observability/src/lib.rs +197 -0
  209. package/crates/enact-providers/Cargo.toml +33 -0
  210. package/crates/enact-providers/examples/hello-agent.rs +33 -0
  211. package/crates/enact-providers/src/anthropic.rs +182 -0
  212. package/crates/enact-providers/src/azure.rs +96 -0
  213. package/crates/enact-providers/src/bridge.rs +221 -0
  214. package/crates/enact-providers/src/gemini.rs +227 -0
  215. package/crates/enact-providers/src/http.rs +78 -0
  216. package/crates/enact-providers/src/lib.rs +53 -0
  217. package/crates/enact-providers/src/openai_compatible.rs +167 -0
  218. package/crates/enact-providers/src/openrouter.rs +33 -0
  219. package/crates/enact-runner/Cargo.toml +24 -0
  220. package/crates/enact-runner/README.md +76 -0
  221. package/crates/enact-runner/src/compaction.rs +225 -0
  222. package/crates/enact-runner/src/config.rs +118 -0
  223. package/crates/enact-runner/src/lib.rs +63 -0
  224. package/crates/enact-runner/src/loop_driver.rs +414 -0
  225. package/crates/enact-runner/src/parser.rs +421 -0
  226. package/crates/enact-runner/src/retry.rs +262 -0
  227. package/crates/enact-runner/tests/integration.rs +278 -0
  228. package/crates/enact-security/Cargo.toml +22 -0
  229. package/crates/enact-security/src/audit.rs +375 -0
  230. package/crates/enact-security/src/lib.rs +37 -0
  231. package/crates/enact-security/src/policy.rs +406 -0
  232. package/crates/enact-skills/Cargo.toml +25 -0
  233. package/crates/enact-skills/src/lib.rs +506 -0
  234. package/crates/enact-tools/Cargo.toml +22 -0
  235. package/crates/enact-tools/src/file_read.rs +166 -0
  236. package/crates/enact-tools/src/file_write.rs +216 -0
  237. package/crates/enact-tools/src/git_operations.rs +513 -0
  238. package/crates/enact-tools/src/http_request.rs +417 -0
  239. package/crates/enact-tools/src/lib.rs +104 -0
  240. package/crates/enact-tools/src/security.rs +227 -0
  241. package/crates/enact-tools/src/shell.rs +191 -0
  242. package/crates/enact-tools/src/traits.rs +159 -0
  243. package/docs/Makefile +74 -0
  244. package/docs/config.toml +62 -0
  245. package/docs/content/_index.md +174 -0
  246. package/docs/content/a2a/_index.md +431 -0
  247. package/docs/content/api/_index.md +323 -0
  248. package/docs/content/channels/_index.md +160 -0
  249. package/docs/content/channels/teams.md +205 -0
  250. package/docs/content/channels/telegram.md +182 -0
  251. package/docs/content/channels/webhook.md +423 -0
  252. package/docs/content/channels/whatsapp.md +240 -0
  253. package/docs/content/cli/_index.md +261 -0
  254. package/docs/content/concepts/_index.md +273 -0
  255. package/docs/content/configuration/_index.md +241 -0
  256. package/docs/content/cron/_index.md +248 -0
  257. package/docs/content/developers/_index.md +278 -0
  258. package/docs/content/getting-started/_index.md +180 -0
  259. package/docs/content/installation/_index.md +186 -0
  260. package/docs/content/installation/uninstall.md +101 -0
  261. package/docs/content/installation/updating.md +120 -0
  262. package/docs/content/mcp/_index.md +215 -0
  263. package/docs/content/memory/_index.md +163 -0
  264. package/docs/content/oauth/_index.md +515 -0
  265. package/docs/content/providers/_index.md +206 -0
  266. package/docs/content/roadmap/_index.md +199 -0
  267. package/docs/content/security/_index.md +219 -0
  268. package/docs/content/skills/_index.md +228 -0
  269. package/docs/content/tools/_index.md +485 -0
  270. package/docs/content/troubleshooting/_index.md +259 -0
  271. package/docs/content/yaml-schema/_index.md +294 -0
  272. package/docs/static/giallo-dark.css +91 -0
  273. package/docs/static/giallo-light.css +91 -0
  274. package/docs/themes/tanuki/.github/workflows/deploy.yml +44 -0
  275. package/docs/themes/tanuki/LICENSE +21 -0
  276. package/docs/themes/tanuki/README.md +166 -0
  277. package/docs/themes/tanuki/examples/blog/config.toml +58 -0
  278. package/docs/themes/tanuki/examples/blog/content/_index.md +4 -0
  279. package/docs/themes/tanuki/examples/blog/content/about.md +33 -0
  280. package/docs/themes/tanuki/examples/blog/content/blog/_index.md +7 -0
  281. package/docs/themes/tanuki/examples/blog/content/blog/api-design-best-practices.md +245 -0
  282. package/docs/themes/tanuki/examples/blog/content/blog/building-accessible-websites.md +147 -0
  283. package/docs/themes/tanuki/examples/blog/content/blog/css-grid-vs-flexbox.md +165 -0
  284. package/docs/themes/tanuki/examples/blog/content/blog/customizing-catppuccin-colors.md +137 -0
  285. package/docs/themes/tanuki/examples/blog/content/blog/dark-mode-best-practices.md +82 -0
  286. package/docs/themes/tanuki/examples/blog/content/blog/docker-essentials.md +301 -0
  287. package/docs/themes/tanuki/examples/blog/content/blog/getting-started-with-zola.md +129 -0
  288. package/docs/themes/tanuki/examples/blog/content/blog/git-workflow-for-content.md +112 -0
  289. package/docs/themes/tanuki/examples/blog/content/blog/introduction-to-webassembly.md +183 -0
  290. package/docs/themes/tanuki/examples/blog/content/blog/modern-javascript-features.md +234 -0
  291. package/docs/themes/tanuki/examples/blog/content/blog/testing-strategies.md +311 -0
  292. package/docs/themes/tanuki/examples/blog/content/blog/typography-for-developers.md +104 -0
  293. package/docs/themes/tanuki/examples/blog/content/blog/welcome-to-tanuki.md +67 -0
  294. package/docs/themes/tanuki/examples/blog/content/blog/why-static-sites.md +85 -0
  295. package/docs/themes/tanuki/examples/blog/content/projects.md +64 -0
  296. package/docs/themes/tanuki/examples/book/config.toml +17 -0
  297. package/docs/themes/tanuki/examples/book/content/_index.md +12 -0
  298. package/docs/themes/tanuki/examples/book/content/chapter-1.md +90 -0
  299. package/docs/themes/tanuki/examples/book/content/chapter-2.md +143 -0
  300. package/docs/themes/tanuki/examples/book/content/chapter-3.md +217 -0
  301. package/docs/themes/tanuki/examples/book/content/chapter-4.md +224 -0
  302. package/docs/themes/tanuki/examples/book/content/chapter-5.md +297 -0
  303. package/docs/themes/tanuki/examples/book/content/print.md +6 -0
  304. package/docs/themes/tanuki/examples/docs/config.toml +28 -0
  305. package/docs/themes/tanuki/examples/docs/content/_index.md +20 -0
  306. package/docs/themes/tanuki/examples/docs/content/components.md +156 -0
  307. package/docs/themes/tanuki/examples/docs/content/configuration.md +94 -0
  308. package/docs/themes/tanuki/examples/docs/content/customization.md +202 -0
  309. package/docs/themes/tanuki/examples/docs/content/deployment.md +204 -0
  310. package/docs/themes/tanuki/examples/docs/content/installation.md +59 -0
  311. package/docs/themes/tanuki/examples/docs/content/print.md +6 -0
  312. package/docs/themes/tanuki/examples/docs/static/img/tanuki-icon.avif +0 -0
  313. package/docs/themes/tanuki/examples/index.html +2104 -0
  314. package/docs/themes/tanuki/mise.toml +108 -0
  315. package/docs/themes/tanuki/sass/base/_catppuccin.scss +164 -0
  316. package/docs/themes/tanuki/sass/base/_fonts.scss +64 -0
  317. package/docs/themes/tanuki/sass/base/_reset.scss +152 -0
  318. package/docs/themes/tanuki/sass/base/_typography.scss +523 -0
  319. package/docs/themes/tanuki/sass/components/_buttons.scss +209 -0
  320. package/docs/themes/tanuki/sass/components/_code.scss +457 -0
  321. package/docs/themes/tanuki/sass/components/_landing.scss +633 -0
  322. package/docs/themes/tanuki/sass/components/_layout.scss +294 -0
  323. package/docs/themes/tanuki/sass/components/_navigation.scss +1200 -0
  324. package/docs/themes/tanuki/sass/components/_print.scss +237 -0
  325. package/docs/themes/tanuki/sass/components/_search.scss +224 -0
  326. package/docs/themes/tanuki/sass/components/_sidebar.scss +473 -0
  327. package/docs/themes/tanuki/sass/components/_theme-toggle.scss +186 -0
  328. package/docs/themes/tanuki/sass/modes/_blog.scss +366 -0
  329. package/docs/themes/tanuki/sass/modes/_product.scss +875 -0
  330. package/docs/themes/tanuki/sass/modes/_raskell.scss +1696 -0
  331. package/docs/themes/tanuki/sass/patterns/_buttons.scss +183 -0
  332. package/docs/themes/tanuki/sass/patterns/_cards.scss +144 -0
  333. package/docs/themes/tanuki/sass/patterns/_index.scss +9 -0
  334. package/docs/themes/tanuki/sass/patterns/_lists.scss +259 -0
  335. package/docs/themes/tanuki/sass/patterns/_sections.scss +243 -0
  336. package/docs/themes/tanuki/sass/style.scss +47 -0
  337. package/docs/themes/tanuki/sass/tokens/_colors.scss +139 -0
  338. package/docs/themes/tanuki/sass/tokens/_spacing.scss +100 -0
  339. package/docs/themes/tanuki/sass/tokens/_typography.scss +186 -0
  340. package/docs/themes/tanuki/screenshot.png +0 -0
  341. package/docs/themes/tanuki/sentinel.kdl +59 -0
  342. package/docs/themes/tanuki/static/elasticlunr.min.js +10 -0
  343. package/docs/themes/tanuki/static/fonts/GEIST-LICENSE.txt +92 -0
  344. package/docs/themes/tanuki/static/fonts/Geist-Variable.woff2 +0 -0
  345. package/docs/themes/tanuki/static/fonts/GeistMono-Variable.woff2 +0 -0
  346. package/docs/themes/tanuki/static/img/tanuki-icon.avif +0 -0
  347. package/docs/themes/tanuki/static/img/tanuki-icon.png +0 -0
  348. package/docs/themes/tanuki/static/js/anchors.js +18 -0
  349. package/docs/themes/tanuki/static/js/app.js +274 -0
  350. package/docs/themes/tanuki/static/js/code.js +394 -0
  351. package/docs/themes/tanuki/static/js/navigation.js +778 -0
  352. package/docs/themes/tanuki/static/js/scroll-to-top.js +33 -0
  353. package/docs/themes/tanuki/static/js/search-raskell.js +240 -0
  354. package/docs/themes/tanuki/static/js/search.js +215 -0
  355. package/docs/themes/tanuki/static/js/theme.js +169 -0
  356. package/docs/themes/tanuki/static/syntax-dark.css +151 -0
  357. package/docs/themes/tanuki/static/syntax-light.css +151 -0
  358. package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm.js +486 -0
  359. package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm_bg.wasm +0 -0
  360. package/docs/themes/tanuki/templates/404.html +52 -0
  361. package/docs/themes/tanuki/templates/base.html +428 -0
  362. package/docs/themes/tanuki/templates/blog.html +66 -0
  363. package/docs/themes/tanuki/templates/home.html +108 -0
  364. package/docs/themes/tanuki/templates/index.html +178 -0
  365. package/docs/themes/tanuki/templates/landing.html +168 -0
  366. package/docs/themes/tanuki/templates/macros/nav.html +128 -0
  367. package/docs/themes/tanuki/templates/macros/posts.html +101 -0
  368. package/docs/themes/tanuki/templates/macros/ui.html +159 -0
  369. package/docs/themes/tanuki/templates/page.html +135 -0
  370. package/docs/themes/tanuki/templates/partials/footer.html +38 -0
  371. package/docs/themes/tanuki/templates/partials/header.html +366 -0
  372. package/docs/themes/tanuki/templates/partials/nav-buttons.html +55 -0
  373. package/docs/themes/tanuki/templates/partials/nav-overlay.html +81 -0
  374. package/docs/themes/tanuki/templates/partials/page-toc-panel.html +43 -0
  375. package/docs/themes/tanuki/templates/partials/search.html +52 -0
  376. package/docs/themes/tanuki/templates/partials/sidebar.html +107 -0
  377. package/docs/themes/tanuki/templates/partials/theme-toggle.html +35 -0
  378. package/docs/themes/tanuki/templates/partials/toc-overlay.html +146 -0
  379. package/docs/themes/tanuki/templates/partials/version-picker.html +38 -0
  380. package/docs/themes/tanuki/templates/print.html +244 -0
  381. package/docs/themes/tanuki/templates/section.html +186 -0
  382. package/docs/themes/tanuki/templates/taxonomy_list.html +18 -0
  383. package/docs/themes/tanuki/templates/taxonomy_single.html +31 -0
  384. package/docs/themes/tanuki/theme.toml +58 -0
  385. package/examples/hello-agent.rs +55 -0
  386. package/package.json +36 -0
  387. package/proto/config.proto +60 -0
  388. package/proto/events.proto +0 -0
  389. package/proto/runtime.proto +215 -0
@@ -0,0 +1,102 @@
1
+ //! Persistence Layer - Storage Abstractions
2
+ //!
3
+ //! This module defines the core storage traits for the Enact execution engine.
4
+ //! The kernel interacts only with these traits; concrete implementations live
5
+ //! in the `enact-persistence` crate.
6
+ //!
7
+ //! ## Store Roles
8
+ //!
9
+ //! | Store | Role | Mutable? | Authoritative? |
10
+ //! |-------|------|----------|----------------|
11
+ //! | EventStore | Source of truth | Append-only | Yes |
12
+ //! | StateStore | Cache/snapshot | Yes | No |
13
+ //! | VectorStore | Semantic recall | Yes | No |
14
+ //!
15
+ //! ## Key Invariants
16
+ //!
17
+ //! 1. Kernel NEVER imports concrete implementations
18
+ //! 2. All stores implement `StorageBackend` for mode validation
19
+ //! 3. EventStore is append-only and authoritative
20
+ //! 4. StateStore/VectorStore are non-authoritative caches
21
+ //!
22
+ //! @see docs/TECHNICAL/14-PERSISTENCE-LAYER.md
23
+
24
+ mod event_store;
25
+ mod message_store;
26
+ mod state_store;
27
+ mod vector_store;
28
+
29
+ pub use event_store::{EventStore, ExecutionEventData, StoredEvent};
30
+ pub use message_store::{
31
+ CostInfo, ExecutionStats, FinishReason, InMemoryMessageStore, Message, MessageMetadata,
32
+ MessagePart, MessageRole, MessageStore, Thread, TokenUsage,
33
+ };
34
+ pub use state_store::{ExecutionSnapshot, StateStore, StateStoreJsonExt};
35
+ pub use vector_store::{
36
+ CollectionInfo, DistanceMetric, VectorDocument, VectorFilter, VectorSearchResult, VectorStore,
37
+ };
38
+
39
+ use async_trait::async_trait;
40
+
41
+ /// Base trait for all storage backends
42
+ ///
43
+ /// This trait provides metadata about the backend's capabilities,
44
+ /// enabling runtime mode validation (e.g., air-gapped mode rejection
45
+ /// of network-requiring backends).
46
+ #[async_trait]
47
+ pub trait StorageBackend: Send + Sync {
48
+ /// Human-readable name of this backend (e.g., "sqlite", "postgres", "qdrant")
49
+ fn name(&self) -> &str;
50
+
51
+ /// Does this backend require network access?
52
+ ///
53
+ /// Returns `true` for backends like Postgres, Redis, remote Qdrant.
54
+ /// Returns `false` for SQLite, filesystem, embedded Qdrant.
55
+ fn requires_network(&self) -> bool;
56
+
57
+ /// Can this backend operate in air-gapped mode?
58
+ ///
59
+ /// Default implementation returns `!requires_network()`.
60
+ fn supports_air_gapped(&self) -> bool {
61
+ !self.requires_network()
62
+ }
63
+
64
+ /// Health check - verify the backend is operational
65
+ ///
66
+ /// Should return `Ok(())` if the backend is ready to accept operations.
67
+ /// Should return an error if the backend is unavailable or misconfigured.
68
+ async fn health_check(&self) -> anyhow::Result<()>;
69
+
70
+ /// Graceful shutdown
71
+ ///
72
+ /// Called when the runtime is shutting down. Implementations should
73
+ /// flush any pending writes and close connections.
74
+ async fn shutdown(&self) -> anyhow::Result<()> {
75
+ Ok(())
76
+ }
77
+ }
78
+
79
+ /// Store guarantees - documented contracts for each store type
80
+ pub mod guarantees {
81
+ //! # Store Guarantees
82
+ //!
83
+ //! These guarantees are contractual and must be tested.
84
+ //!
85
+ //! ## EventStore
86
+ //! - **Durability**: Event is durable before `append()` returns
87
+ //! - **Ordering**: Events are strictly ordered per execution
88
+ //! - **Immutability**: Events are append-only, never modified
89
+ //! - **Authority**: EventStore is the source of truth
90
+ //!
91
+ //! ## StateStore
92
+ //! - **Durability**: Best-effort - may be lost on failure
93
+ //! - **Freshness**: May be stale - always verify with EventStore
94
+ //! - **Authority**: Non-authoritative - cache only
95
+ //! - **Availability**: Designed for fast reads, not durability
96
+ //!
97
+ //! ## VectorStore
98
+ //! - **Consistency**: Eventually consistent
99
+ //! - **Authority**: Non-authoritative - for recall only
100
+ //! - **Availability**: Search may return stale results
101
+ //! - **Durability**: Documents are durable after `upsert()` returns
102
+ }
@@ -0,0 +1,228 @@
1
+ //! StateStore - Hot snapshots and working memory
2
+ //!
3
+ //! The StateStore provides fast access to execution state for resumption.
4
+ //! It is a **cache**, not a source of truth - the EventStore is authoritative.
5
+ //!
6
+ //! ## Guarantees
7
+ //!
8
+ //! - **Durability**: Best-effort - may be lost on failure
9
+ //! - **Freshness**: May be stale - always verify sequence with EventStore
10
+ //! - **Authority**: Non-authoritative - cache only
11
+ //! - **Availability**: Designed for fast reads, not durability
12
+ //!
13
+ //! ## Use Cases
14
+ //!
15
+ //! - Fast execution resumption (avoid replaying all events)
16
+ //! - Working memory during execution
17
+ //! - Temporary state that doesn't need event sourcing
18
+ //!
19
+ //! @see docs/TECHNICAL/14-PERSISTENCE-LAYER.md
20
+
21
+ use async_trait::async_trait;
22
+ use chrono::{DateTime, Utc};
23
+ use serde::{Deserialize, Serialize};
24
+ use std::collections::HashMap;
25
+ use std::time::Duration;
26
+
27
+ /// Extension trait for JSON operations on StateStore
28
+ ///
29
+ /// This is separate from StateStore to maintain dyn-compatibility.
30
+ #[async_trait]
31
+ pub trait StateStoreJsonExt: StateStore {
32
+ /// Set a JSON value
33
+ async fn set_json<T: Serialize + Send + Sync>(
34
+ &self,
35
+ key: &str,
36
+ value: &T,
37
+ ttl: Option<Duration>,
38
+ ) -> anyhow::Result<()> {
39
+ let bytes = serde_json::to_vec(value)?;
40
+ self.set(key, &bytes, ttl).await
41
+ }
42
+
43
+ /// Get a JSON value
44
+ async fn get_json<T: for<'de> Deserialize<'de>>(&self, key: &str) -> anyhow::Result<Option<T>> {
45
+ match self.get(key).await? {
46
+ Some(bytes) => Ok(Some(serde_json::from_slice(&bytes)?)),
47
+ None => Ok(None),
48
+ }
49
+ }
50
+ }
51
+
52
+ // Blanket implementation for all StateStore implementors
53
+ impl<S: StateStore + ?Sized> StateStoreJsonExt for S {}
54
+
55
+ use crate::kernel::{ExecutionId, ExecutionState, StepId, TenantId};
56
+
57
+ use super::StorageBackend;
58
+
59
+ /// Execution state snapshot
60
+ ///
61
+ /// This captures the current state of an execution for fast resumption.
62
+ /// The `sequence_number` should match the EventStore sequence to verify freshness.
63
+ #[derive(Debug, Clone, Serialize, Deserialize)]
64
+ pub struct ExecutionSnapshot {
65
+ /// The execution this snapshot is for
66
+ pub execution_id: ExecutionId,
67
+ /// Tenant for multi-tenancy isolation
68
+ pub tenant_id: TenantId,
69
+ /// Current execution state
70
+ pub state: ExecutionState,
71
+ /// Currently executing step (if any)
72
+ pub current_step_id: Option<StepId>,
73
+ /// Outputs from completed steps
74
+ pub step_outputs: HashMap<StepId, serde_json::Value>,
75
+ /// Working variables
76
+ pub variables: HashMap<String, serde_json::Value>,
77
+ /// When this snapshot was created
78
+ pub timestamp: DateTime<Utc>,
79
+ /// EventStore sequence number at snapshot time
80
+ ///
81
+ /// Used to verify freshness - if EventStore has more events,
82
+ /// the snapshot may be stale.
83
+ pub sequence_number: u64,
84
+ }
85
+
86
+ impl ExecutionSnapshot {
87
+ /// Create a new snapshot
88
+ pub fn new(
89
+ execution_id: ExecutionId,
90
+ tenant_id: TenantId,
91
+ state: ExecutionState,
92
+ sequence_number: u64,
93
+ ) -> Self {
94
+ Self {
95
+ execution_id,
96
+ tenant_id,
97
+ state,
98
+ current_step_id: None,
99
+ step_outputs: HashMap::new(),
100
+ variables: HashMap::new(),
101
+ timestamp: Utc::now(),
102
+ sequence_number,
103
+ }
104
+ }
105
+
106
+ /// Check if this snapshot is fresh compared to an EventStore sequence
107
+ pub fn is_fresh(&self, event_store_sequence: u64) -> bool {
108
+ self.sequence_number >= event_store_sequence
109
+ }
110
+ }
111
+
112
+ /// StateStore trait - mutable snapshot cache
113
+ ///
114
+ /// Provides fast state access for execution resumption and working memory.
115
+ /// This is NOT the source of truth - always verify freshness against EventStore.
116
+ #[async_trait]
117
+ pub trait StateStore: StorageBackend {
118
+ // =========================================================================
119
+ // Snapshot operations
120
+ // =========================================================================
121
+
122
+ /// Save a state snapshot
123
+ ///
124
+ /// # Guarantees
125
+ /// - Best-effort durability
126
+ /// - May be overwritten by subsequent saves
127
+ /// - May be lost on backend failure
128
+ async fn save_snapshot(&self, snapshot: ExecutionSnapshot) -> anyhow::Result<()>;
129
+
130
+ /// Load the latest snapshot for an execution
131
+ ///
132
+ /// Returns `None` if no snapshot exists.
133
+ ///
134
+ /// # Important
135
+ /// Always check `snapshot.sequence_number` against EventStore to verify freshness.
136
+ async fn load_snapshot(
137
+ &self,
138
+ execution_id: &ExecutionId,
139
+ ) -> anyhow::Result<Option<ExecutionSnapshot>>;
140
+
141
+ /// Delete a snapshot
142
+ ///
143
+ /// Called when an execution completes or is cleaned up.
144
+ async fn delete_snapshot(&self, execution_id: &ExecutionId) -> anyhow::Result<()>;
145
+
146
+ // =========================================================================
147
+ // Key-value operations (working memory)
148
+ // =========================================================================
149
+
150
+ /// Set a key-value pair
151
+ ///
152
+ /// # Arguments
153
+ /// * `key` - The key (should be namespaced, e.g., "exec:{id}:var:{name}")
154
+ /// * `value` - The value as bytes
155
+ /// * `ttl` - Optional time-to-live (auto-delete after this duration)
156
+ async fn set(&self, key: &str, value: &[u8], ttl: Option<Duration>) -> anyhow::Result<()>;
157
+
158
+ /// Get a value by key
159
+ ///
160
+ /// Returns `None` if the key doesn't exist or has expired.
161
+ async fn get(&self, key: &str) -> anyhow::Result<Option<Vec<u8>>>;
162
+
163
+ /// Delete a key
164
+ async fn delete(&self, key: &str) -> anyhow::Result<()>;
165
+
166
+ /// Check if a key exists
167
+ async fn exists(&self, key: &str) -> anyhow::Result<bool> {
168
+ Ok(self.get(key).await?.is_some())
169
+ }
170
+
171
+
172
+ // =========================================================================
173
+ // Batch operations
174
+ // =========================================================================
175
+
176
+ /// Delete all state for an execution
177
+ ///
178
+ /// Cleans up snapshot and any keys prefixed with the execution ID.
179
+ async fn delete_execution_state(&self, execution_id: &ExecutionId) -> anyhow::Result<()> {
180
+ self.delete_snapshot(execution_id).await
181
+ // Implementations may also clean up related keys
182
+ }
183
+
184
+ /// List snapshots for a tenant (for cleanup/admin)
185
+ async fn list_snapshots(
186
+ &self,
187
+ tenant_id: &TenantId,
188
+ limit: usize,
189
+ ) -> anyhow::Result<Vec<ExecutionId>>;
190
+ }
191
+
192
+ #[cfg(test)]
193
+ mod tests {
194
+ use super::*;
195
+
196
+ #[test]
197
+ fn test_snapshot_freshness() {
198
+ let snapshot = ExecutionSnapshot::new(
199
+ ExecutionId::new(),
200
+ TenantId::from("test"),
201
+ ExecutionState::Running,
202
+ 10,
203
+ );
204
+
205
+ assert!(snapshot.is_fresh(10));
206
+ assert!(snapshot.is_fresh(5));
207
+ assert!(!snapshot.is_fresh(15));
208
+ }
209
+
210
+ #[test]
211
+ fn test_snapshot_serialization() {
212
+ let mut snapshot = ExecutionSnapshot::new(
213
+ ExecutionId::new(),
214
+ TenantId::from("test"),
215
+ ExecutionState::Running,
216
+ 5,
217
+ );
218
+ snapshot
219
+ .variables
220
+ .insert("foo".to_string(), serde_json::json!("bar"));
221
+
222
+ let json = serde_json::to_string(&snapshot).unwrap();
223
+ let parsed: ExecutionSnapshot = serde_json::from_str(&json).unwrap();
224
+
225
+ assert_eq!(parsed.state, ExecutionState::Running);
226
+ assert_eq!(parsed.variables.get("foo").unwrap(), "bar");
227
+ }
228
+ }
@@ -0,0 +1,299 @@
1
+ //! VectorStore - Semantic memory and RAG
2
+ //!
3
+ //! The VectorStore provides semantic search capabilities for long-term memory
4
+ //! and retrieval-augmented generation (RAG).
5
+ //!
6
+ //! ## Guarantees
7
+ //!
8
+ //! - **Consistency**: Eventually consistent
9
+ //! - **Authority**: Non-authoritative - for recall only
10
+ //! - **Availability**: Search may return stale results
11
+ //! - **Durability**: Documents are durable after `upsert()` returns
12
+ //!
13
+ //! ## Use Cases
14
+ //!
15
+ //! - Long-term agent memory
16
+ //! - Document retrieval for RAG
17
+ //! - Semantic search over execution history
18
+ //!
19
+ //! @see docs/TECHNICAL/14-PERSISTENCE-LAYER.md
20
+
21
+ use async_trait::async_trait;
22
+ use serde::{Deserialize, Serialize};
23
+ use std::collections::HashMap;
24
+
25
+ use super::StorageBackend;
26
+
27
+ /// A document with embedding for vector storage
28
+ #[derive(Debug, Clone, Serialize, Deserialize)]
29
+ pub struct VectorDocument {
30
+ /// Unique identifier for this document
31
+ pub id: String,
32
+ /// The text content
33
+ pub content: String,
34
+ /// Pre-computed embedding vector
35
+ ///
36
+ /// Dimensions must match the collection's configured dimension.
37
+ pub embedding: Vec<f32>,
38
+ /// Arbitrary metadata for filtering
39
+ pub metadata: HashMap<String, serde_json::Value>,
40
+ }
41
+
42
+ impl VectorDocument {
43
+ /// Create a new document
44
+ pub fn new(id: impl Into<String>, content: impl Into<String>, embedding: Vec<f32>) -> Self {
45
+ Self {
46
+ id: id.into(),
47
+ content: content.into(),
48
+ embedding,
49
+ metadata: HashMap::new(),
50
+ }
51
+ }
52
+
53
+ /// Add metadata
54
+ pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
55
+ self.metadata.insert(key.into(), value);
56
+ self
57
+ }
58
+ }
59
+
60
+ /// Search result with similarity score
61
+ #[derive(Debug, Clone)]
62
+ pub struct VectorSearchResult {
63
+ /// The matched document
64
+ pub document: VectorDocument,
65
+ /// Similarity score (higher = more similar)
66
+ ///
67
+ /// Score semantics depend on the distance metric:
68
+ /// - Cosine: 0.0 to 1.0 (1.0 = identical)
69
+ /// - Euclidean: 0.0 to infinity (0.0 = identical)
70
+ pub score: f32,
71
+ }
72
+
73
+ /// Single filter condition
74
+ #[derive(Debug, Clone)]
75
+ pub struct FilterCondition {
76
+ pub key: String,
77
+ pub value: serde_json::Value,
78
+ }
79
+
80
+ impl FilterCondition {
81
+ pub fn new(key: impl Into<String>, value: serde_json::Value) -> Self {
82
+ Self {
83
+ key: key.into(),
84
+ value,
85
+ }
86
+ }
87
+ }
88
+
89
+ /// Filter for vector search
90
+ #[derive(Debug, Clone, Default)]
91
+ pub struct VectorFilter {
92
+ /// Metadata conditions (all must match)
93
+ pub conditions: HashMap<String, serde_json::Value>,
94
+ /// Any-of conditions (at least one must match)
95
+ pub any: Vec<FilterCondition>,
96
+ /// None-of conditions (must not match)
97
+ pub none: Vec<FilterCondition>,
98
+ }
99
+
100
+ impl VectorFilter {
101
+ /// Create a new empty filter
102
+ pub fn new() -> Self {
103
+ Self::default()
104
+ }
105
+
106
+ /// Add a condition
107
+ pub fn with_condition(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
108
+ self.conditions.insert(key.into(), value);
109
+ self
110
+ }
111
+
112
+ /// Add an any-of condition
113
+ pub fn with_any_condition(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
114
+ self.any.push(FilterCondition::new(key, value));
115
+ self
116
+ }
117
+
118
+ /// Add a none-of condition
119
+ pub fn with_none_condition(
120
+ mut self,
121
+ key: impl Into<String>,
122
+ value: serde_json::Value,
123
+ ) -> Self {
124
+ self.none.push(FilterCondition::new(key, value));
125
+ self
126
+ }
127
+
128
+ /// Check if filter is empty
129
+ pub fn is_empty(&self) -> bool {
130
+ self.conditions.is_empty() && self.any.is_empty() && self.none.is_empty()
131
+ }
132
+ }
133
+
134
+ /// VectorStore trait - semantic memory
135
+ ///
136
+ /// Provides vector similarity search for RAG and long-term memory.
137
+ #[async_trait]
138
+ pub trait VectorStore: StorageBackend {
139
+ // =========================================================================
140
+ // Collection management
141
+ // =========================================================================
142
+
143
+ /// Create a collection if it doesn't exist
144
+ ///
145
+ /// # Arguments
146
+ /// * `collection` - Collection name
147
+ /// * `dimension` - Vector dimension (must match embeddings)
148
+ async fn ensure_collection(&self, collection: &str, dimension: usize) -> anyhow::Result<()>;
149
+
150
+ /// Delete a collection and all its documents
151
+ async fn delete_collection(&self, collection: &str) -> anyhow::Result<()>;
152
+
153
+ /// List all collections
154
+ async fn list_collections(&self) -> anyhow::Result<Vec<String>>;
155
+
156
+ /// Check if a collection exists
157
+ async fn collection_exists(&self, collection: &str) -> anyhow::Result<bool>;
158
+
159
+ // =========================================================================
160
+ // Document operations
161
+ // =========================================================================
162
+
163
+ /// Upsert a document (insert or update)
164
+ ///
165
+ /// If a document with the same ID exists, it is replaced.
166
+ async fn upsert(&self, collection: &str, document: VectorDocument) -> anyhow::Result<()>;
167
+
168
+ /// Upsert multiple documents atomically
169
+ async fn upsert_batch(
170
+ &self,
171
+ collection: &str,
172
+ documents: Vec<VectorDocument>,
173
+ ) -> anyhow::Result<()>;
174
+
175
+ /// Get a document by ID
176
+ async fn get(&self, collection: &str, id: &str) -> anyhow::Result<Option<VectorDocument>>;
177
+
178
+ /// Delete a document by ID
179
+ async fn delete(&self, collection: &str, id: &str) -> anyhow::Result<()>;
180
+
181
+ /// Delete multiple documents by ID
182
+ async fn delete_batch(&self, collection: &str, ids: &[String]) -> anyhow::Result<()>;
183
+
184
+ // =========================================================================
185
+ // Search operations
186
+ // =========================================================================
187
+
188
+ /// Search for similar documents
189
+ ///
190
+ /// # Arguments
191
+ /// * `collection` - Collection to search
192
+ /// * `query_embedding` - Query vector (must match collection dimension)
193
+ /// * `limit` - Maximum number of results
194
+ /// * `filter` - Optional metadata filter
195
+ ///
196
+ /// # Returns
197
+ /// Results ordered by similarity (highest score first)
198
+ async fn search(
199
+ &self,
200
+ collection: &str,
201
+ query_embedding: &[f32],
202
+ limit: usize,
203
+ filter: Option<VectorFilter>,
204
+ ) -> anyhow::Result<Vec<VectorSearchResult>>;
205
+
206
+ /// Search with score threshold
207
+ ///
208
+ /// Only returns results with score >= min_score.
209
+ async fn search_with_threshold(
210
+ &self,
211
+ collection: &str,
212
+ query_embedding: &[f32],
213
+ limit: usize,
214
+ min_score: f32,
215
+ filter: Option<VectorFilter>,
216
+ ) -> anyhow::Result<Vec<VectorSearchResult>> {
217
+ let results = self.search(collection, query_embedding, limit, filter).await?;
218
+ Ok(results.into_iter().filter(|r| r.score >= min_score).collect())
219
+ }
220
+
221
+ // =========================================================================
222
+ // Utility operations
223
+ // =========================================================================
224
+
225
+ /// Count documents in a collection
226
+ async fn count(&self, collection: &str) -> anyhow::Result<u64>;
227
+
228
+ /// Get collection info (dimension, count, etc.)
229
+ async fn collection_info(&self, collection: &str) -> anyhow::Result<CollectionInfo>;
230
+ }
231
+
232
+ /// Information about a collection
233
+ #[derive(Debug, Clone)]
234
+ pub struct CollectionInfo {
235
+ /// Collection name
236
+ pub name: String,
237
+ /// Vector dimension
238
+ pub dimension: usize,
239
+ /// Number of documents
240
+ pub count: u64,
241
+ /// Distance metric used
242
+ pub distance_metric: DistanceMetric,
243
+ }
244
+
245
+ /// Distance metric for similarity calculation
246
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
247
+ pub enum DistanceMetric {
248
+ /// Cosine similarity (default)
249
+ #[default]
250
+ Cosine,
251
+ /// Euclidean distance
252
+ Euclidean,
253
+ /// Dot product
254
+ DotProduct,
255
+ }
256
+
257
+ #[cfg(test)]
258
+ mod tests {
259
+ use super::*;
260
+
261
+ #[test]
262
+ fn test_document_creation() {
263
+ let doc = VectorDocument::new("doc1", "Hello world", vec![0.1, 0.2, 0.3])
264
+ .with_metadata("source", serde_json::json!("test"))
265
+ .with_metadata("page", serde_json::json!(1));
266
+
267
+ assert_eq!(doc.id, "doc1");
268
+ assert_eq!(doc.content, "Hello world");
269
+ assert_eq!(doc.embedding.len(), 3);
270
+ assert_eq!(doc.metadata.get("source").unwrap(), "test");
271
+ assert_eq!(doc.metadata.get("page").unwrap(), 1);
272
+ }
273
+
274
+ #[test]
275
+ fn test_filter_creation() {
276
+ let filter = VectorFilter::new()
277
+ .with_condition("tenant_id", serde_json::json!("acme"))
278
+ .with_condition("status", serde_json::json!("active"))
279
+ .with_any_condition("visibility", serde_json::json!("team:legal"))
280
+ .with_none_condition("denyScopes", serde_json::json!("team:blocked"));
281
+
282
+ assert!(!filter.is_empty());
283
+ assert_eq!(filter.conditions.len(), 2);
284
+ assert_eq!(filter.any.len(), 1);
285
+ assert_eq!(filter.none.len(), 1);
286
+ }
287
+
288
+ #[test]
289
+ fn test_document_serialization() {
290
+ let doc = VectorDocument::new("doc1", "Test content", vec![0.1, 0.2])
291
+ .with_metadata("key", serde_json::json!("value"));
292
+
293
+ let json = serde_json::to_string(&doc).unwrap();
294
+ let parsed: VectorDocument = serde_json::from_str(&json).unwrap();
295
+
296
+ assert_eq!(parsed.id, "doc1");
297
+ assert_eq!(parsed.embedding, vec![0.1, 0.2]);
298
+ }
299
+ }