PraisonAI 3.0.0__py3-none-any.whl

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 (393) hide show
  1. praisonai/__init__.py +54 -0
  2. praisonai/__main__.py +15 -0
  3. praisonai/acp/__init__.py +54 -0
  4. praisonai/acp/config.py +159 -0
  5. praisonai/acp/server.py +587 -0
  6. praisonai/acp/session.py +219 -0
  7. praisonai/adapters/__init__.py +50 -0
  8. praisonai/adapters/readers.py +395 -0
  9. praisonai/adapters/rerankers.py +315 -0
  10. praisonai/adapters/retrievers.py +394 -0
  11. praisonai/adapters/vector_stores.py +409 -0
  12. praisonai/agent_scheduler.py +337 -0
  13. praisonai/agents_generator.py +903 -0
  14. praisonai/api/call.py +292 -0
  15. praisonai/auto.py +1197 -0
  16. praisonai/capabilities/__init__.py +275 -0
  17. praisonai/capabilities/a2a.py +140 -0
  18. praisonai/capabilities/assistants.py +283 -0
  19. praisonai/capabilities/audio.py +320 -0
  20. praisonai/capabilities/batches.py +469 -0
  21. praisonai/capabilities/completions.py +336 -0
  22. praisonai/capabilities/container_files.py +155 -0
  23. praisonai/capabilities/containers.py +93 -0
  24. praisonai/capabilities/embeddings.py +158 -0
  25. praisonai/capabilities/files.py +467 -0
  26. praisonai/capabilities/fine_tuning.py +293 -0
  27. praisonai/capabilities/guardrails.py +182 -0
  28. praisonai/capabilities/images.py +330 -0
  29. praisonai/capabilities/mcp.py +190 -0
  30. praisonai/capabilities/messages.py +270 -0
  31. praisonai/capabilities/moderations.py +154 -0
  32. praisonai/capabilities/ocr.py +217 -0
  33. praisonai/capabilities/passthrough.py +204 -0
  34. praisonai/capabilities/rag.py +207 -0
  35. praisonai/capabilities/realtime.py +160 -0
  36. praisonai/capabilities/rerank.py +165 -0
  37. praisonai/capabilities/responses.py +266 -0
  38. praisonai/capabilities/search.py +109 -0
  39. praisonai/capabilities/skills.py +133 -0
  40. praisonai/capabilities/vector_store_files.py +334 -0
  41. praisonai/capabilities/vector_stores.py +304 -0
  42. praisonai/capabilities/videos.py +141 -0
  43. praisonai/chainlit_ui.py +304 -0
  44. praisonai/chat/__init__.py +106 -0
  45. praisonai/chat/app.py +125 -0
  46. praisonai/cli/__init__.py +26 -0
  47. praisonai/cli/app.py +213 -0
  48. praisonai/cli/commands/__init__.py +75 -0
  49. praisonai/cli/commands/acp.py +70 -0
  50. praisonai/cli/commands/completion.py +333 -0
  51. praisonai/cli/commands/config.py +166 -0
  52. praisonai/cli/commands/debug.py +142 -0
  53. praisonai/cli/commands/diag.py +55 -0
  54. praisonai/cli/commands/doctor.py +166 -0
  55. praisonai/cli/commands/environment.py +179 -0
  56. praisonai/cli/commands/lsp.py +112 -0
  57. praisonai/cli/commands/mcp.py +210 -0
  58. praisonai/cli/commands/profile.py +457 -0
  59. praisonai/cli/commands/run.py +228 -0
  60. praisonai/cli/commands/schedule.py +150 -0
  61. praisonai/cli/commands/serve.py +97 -0
  62. praisonai/cli/commands/session.py +212 -0
  63. praisonai/cli/commands/traces.py +145 -0
  64. praisonai/cli/commands/version.py +101 -0
  65. praisonai/cli/configuration/__init__.py +18 -0
  66. praisonai/cli/configuration/loader.py +353 -0
  67. praisonai/cli/configuration/paths.py +114 -0
  68. praisonai/cli/configuration/schema.py +164 -0
  69. praisonai/cli/features/__init__.py +268 -0
  70. praisonai/cli/features/acp.py +236 -0
  71. praisonai/cli/features/action_orchestrator.py +546 -0
  72. praisonai/cli/features/agent_scheduler.py +773 -0
  73. praisonai/cli/features/agent_tools.py +474 -0
  74. praisonai/cli/features/agents.py +375 -0
  75. praisonai/cli/features/at_mentions.py +471 -0
  76. praisonai/cli/features/auto_memory.py +182 -0
  77. praisonai/cli/features/autonomy_mode.py +490 -0
  78. praisonai/cli/features/background.py +356 -0
  79. praisonai/cli/features/base.py +168 -0
  80. praisonai/cli/features/capabilities.py +1326 -0
  81. praisonai/cli/features/checkpoints.py +338 -0
  82. praisonai/cli/features/code_intelligence.py +652 -0
  83. praisonai/cli/features/compaction.py +294 -0
  84. praisonai/cli/features/compare.py +534 -0
  85. praisonai/cli/features/cost_tracker.py +514 -0
  86. praisonai/cli/features/debug.py +810 -0
  87. praisonai/cli/features/deploy.py +517 -0
  88. praisonai/cli/features/diag.py +289 -0
  89. praisonai/cli/features/doctor/__init__.py +63 -0
  90. praisonai/cli/features/doctor/checks/__init__.py +24 -0
  91. praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
  92. praisonai/cli/features/doctor/checks/config_checks.py +366 -0
  93. praisonai/cli/features/doctor/checks/db_checks.py +366 -0
  94. praisonai/cli/features/doctor/checks/env_checks.py +543 -0
  95. praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
  96. praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
  97. praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
  98. praisonai/cli/features/doctor/checks/network_checks.py +251 -0
  99. praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
  100. praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
  101. praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
  102. praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
  103. praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
  104. praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
  105. praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
  106. praisonai/cli/features/doctor/engine.py +266 -0
  107. praisonai/cli/features/doctor/formatters.py +310 -0
  108. praisonai/cli/features/doctor/handler.py +397 -0
  109. praisonai/cli/features/doctor/models.py +264 -0
  110. praisonai/cli/features/doctor/registry.py +239 -0
  111. praisonai/cli/features/endpoints.py +1019 -0
  112. praisonai/cli/features/eval.py +560 -0
  113. praisonai/cli/features/external_agents.py +231 -0
  114. praisonai/cli/features/fast_context.py +410 -0
  115. praisonai/cli/features/flow_display.py +566 -0
  116. praisonai/cli/features/git_integration.py +651 -0
  117. praisonai/cli/features/guardrail.py +171 -0
  118. praisonai/cli/features/handoff.py +185 -0
  119. praisonai/cli/features/hooks.py +583 -0
  120. praisonai/cli/features/image.py +384 -0
  121. praisonai/cli/features/interactive_runtime.py +585 -0
  122. praisonai/cli/features/interactive_tools.py +380 -0
  123. praisonai/cli/features/interactive_tui.py +603 -0
  124. praisonai/cli/features/jobs.py +632 -0
  125. praisonai/cli/features/knowledge.py +531 -0
  126. praisonai/cli/features/lite.py +244 -0
  127. praisonai/cli/features/lsp_cli.py +225 -0
  128. praisonai/cli/features/mcp.py +169 -0
  129. praisonai/cli/features/message_queue.py +587 -0
  130. praisonai/cli/features/metrics.py +211 -0
  131. praisonai/cli/features/n8n.py +673 -0
  132. praisonai/cli/features/observability.py +293 -0
  133. praisonai/cli/features/ollama.py +361 -0
  134. praisonai/cli/features/output_style.py +273 -0
  135. praisonai/cli/features/package.py +631 -0
  136. praisonai/cli/features/performance.py +308 -0
  137. praisonai/cli/features/persistence.py +636 -0
  138. praisonai/cli/features/profile.py +226 -0
  139. praisonai/cli/features/profiler/__init__.py +81 -0
  140. praisonai/cli/features/profiler/core.py +558 -0
  141. praisonai/cli/features/profiler/optimizations.py +652 -0
  142. praisonai/cli/features/profiler/suite.py +386 -0
  143. praisonai/cli/features/profiling.py +350 -0
  144. praisonai/cli/features/queue/__init__.py +73 -0
  145. praisonai/cli/features/queue/manager.py +395 -0
  146. praisonai/cli/features/queue/models.py +286 -0
  147. praisonai/cli/features/queue/persistence.py +564 -0
  148. praisonai/cli/features/queue/scheduler.py +484 -0
  149. praisonai/cli/features/queue/worker.py +372 -0
  150. praisonai/cli/features/recipe.py +1723 -0
  151. praisonai/cli/features/recipes.py +449 -0
  152. praisonai/cli/features/registry.py +229 -0
  153. praisonai/cli/features/repo_map.py +860 -0
  154. praisonai/cli/features/router.py +466 -0
  155. praisonai/cli/features/sandbox_executor.py +515 -0
  156. praisonai/cli/features/serve.py +829 -0
  157. praisonai/cli/features/session.py +222 -0
  158. praisonai/cli/features/skills.py +856 -0
  159. praisonai/cli/features/slash_commands.py +650 -0
  160. praisonai/cli/features/telemetry.py +179 -0
  161. praisonai/cli/features/templates.py +1384 -0
  162. praisonai/cli/features/thinking.py +305 -0
  163. praisonai/cli/features/todo.py +334 -0
  164. praisonai/cli/features/tools.py +680 -0
  165. praisonai/cli/features/tui/__init__.py +83 -0
  166. praisonai/cli/features/tui/app.py +580 -0
  167. praisonai/cli/features/tui/cli.py +566 -0
  168. praisonai/cli/features/tui/debug.py +511 -0
  169. praisonai/cli/features/tui/events.py +99 -0
  170. praisonai/cli/features/tui/mock_provider.py +328 -0
  171. praisonai/cli/features/tui/orchestrator.py +652 -0
  172. praisonai/cli/features/tui/screens/__init__.py +50 -0
  173. praisonai/cli/features/tui/screens/main.py +245 -0
  174. praisonai/cli/features/tui/screens/queue.py +174 -0
  175. praisonai/cli/features/tui/screens/session.py +124 -0
  176. praisonai/cli/features/tui/screens/settings.py +148 -0
  177. praisonai/cli/features/tui/widgets/__init__.py +56 -0
  178. praisonai/cli/features/tui/widgets/chat.py +261 -0
  179. praisonai/cli/features/tui/widgets/composer.py +224 -0
  180. praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
  181. praisonai/cli/features/tui/widgets/status.py +167 -0
  182. praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
  183. praisonai/cli/features/workflow.py +720 -0
  184. praisonai/cli/legacy.py +236 -0
  185. praisonai/cli/main.py +5559 -0
  186. praisonai/cli/schedule_cli.py +54 -0
  187. praisonai/cli/state/__init__.py +31 -0
  188. praisonai/cli/state/identifiers.py +161 -0
  189. praisonai/cli/state/sessions.py +313 -0
  190. praisonai/code/__init__.py +93 -0
  191. praisonai/code/agent_tools.py +344 -0
  192. praisonai/code/diff/__init__.py +21 -0
  193. praisonai/code/diff/diff_strategy.py +432 -0
  194. praisonai/code/tools/__init__.py +27 -0
  195. praisonai/code/tools/apply_diff.py +221 -0
  196. praisonai/code/tools/execute_command.py +275 -0
  197. praisonai/code/tools/list_files.py +274 -0
  198. praisonai/code/tools/read_file.py +206 -0
  199. praisonai/code/tools/search_replace.py +248 -0
  200. praisonai/code/tools/write_file.py +217 -0
  201. praisonai/code/utils/__init__.py +46 -0
  202. praisonai/code/utils/file_utils.py +307 -0
  203. praisonai/code/utils/ignore_utils.py +308 -0
  204. praisonai/code/utils/text_utils.py +276 -0
  205. praisonai/db/__init__.py +64 -0
  206. praisonai/db/adapter.py +531 -0
  207. praisonai/deploy/__init__.py +62 -0
  208. praisonai/deploy/api.py +231 -0
  209. praisonai/deploy/docker.py +454 -0
  210. praisonai/deploy/doctor.py +367 -0
  211. praisonai/deploy/main.py +327 -0
  212. praisonai/deploy/models.py +179 -0
  213. praisonai/deploy/providers/__init__.py +33 -0
  214. praisonai/deploy/providers/aws.py +331 -0
  215. praisonai/deploy/providers/azure.py +358 -0
  216. praisonai/deploy/providers/base.py +101 -0
  217. praisonai/deploy/providers/gcp.py +314 -0
  218. praisonai/deploy/schema.py +208 -0
  219. praisonai/deploy.py +185 -0
  220. praisonai/endpoints/__init__.py +53 -0
  221. praisonai/endpoints/a2u_server.py +410 -0
  222. praisonai/endpoints/discovery.py +165 -0
  223. praisonai/endpoints/providers/__init__.py +28 -0
  224. praisonai/endpoints/providers/a2a.py +253 -0
  225. praisonai/endpoints/providers/a2u.py +208 -0
  226. praisonai/endpoints/providers/agents_api.py +171 -0
  227. praisonai/endpoints/providers/base.py +231 -0
  228. praisonai/endpoints/providers/mcp.py +263 -0
  229. praisonai/endpoints/providers/recipe.py +206 -0
  230. praisonai/endpoints/providers/tools_mcp.py +150 -0
  231. praisonai/endpoints/registry.py +131 -0
  232. praisonai/endpoints/server.py +161 -0
  233. praisonai/inbuilt_tools/__init__.py +24 -0
  234. praisonai/inbuilt_tools/autogen_tools.py +117 -0
  235. praisonai/inc/__init__.py +2 -0
  236. praisonai/inc/config.py +96 -0
  237. praisonai/inc/models.py +155 -0
  238. praisonai/integrations/__init__.py +56 -0
  239. praisonai/integrations/base.py +303 -0
  240. praisonai/integrations/claude_code.py +270 -0
  241. praisonai/integrations/codex_cli.py +255 -0
  242. praisonai/integrations/cursor_cli.py +195 -0
  243. praisonai/integrations/gemini_cli.py +222 -0
  244. praisonai/jobs/__init__.py +67 -0
  245. praisonai/jobs/executor.py +425 -0
  246. praisonai/jobs/models.py +230 -0
  247. praisonai/jobs/router.py +314 -0
  248. praisonai/jobs/server.py +186 -0
  249. praisonai/jobs/store.py +203 -0
  250. praisonai/llm/__init__.py +66 -0
  251. praisonai/llm/registry.py +382 -0
  252. praisonai/mcp_server/__init__.py +152 -0
  253. praisonai/mcp_server/adapters/__init__.py +74 -0
  254. praisonai/mcp_server/adapters/agents.py +128 -0
  255. praisonai/mcp_server/adapters/capabilities.py +168 -0
  256. praisonai/mcp_server/adapters/cli_tools.py +568 -0
  257. praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
  258. praisonai/mcp_server/adapters/knowledge.py +93 -0
  259. praisonai/mcp_server/adapters/memory.py +104 -0
  260. praisonai/mcp_server/adapters/prompts.py +306 -0
  261. praisonai/mcp_server/adapters/resources.py +124 -0
  262. praisonai/mcp_server/adapters/tools_bridge.py +280 -0
  263. praisonai/mcp_server/auth/__init__.py +48 -0
  264. praisonai/mcp_server/auth/api_key.py +291 -0
  265. praisonai/mcp_server/auth/oauth.py +460 -0
  266. praisonai/mcp_server/auth/oidc.py +289 -0
  267. praisonai/mcp_server/auth/scopes.py +260 -0
  268. praisonai/mcp_server/cli.py +852 -0
  269. praisonai/mcp_server/elicitation.py +445 -0
  270. praisonai/mcp_server/icons.py +302 -0
  271. praisonai/mcp_server/recipe_adapter.py +573 -0
  272. praisonai/mcp_server/recipe_cli.py +824 -0
  273. praisonai/mcp_server/registry.py +703 -0
  274. praisonai/mcp_server/sampling.py +422 -0
  275. praisonai/mcp_server/server.py +490 -0
  276. praisonai/mcp_server/tasks.py +443 -0
  277. praisonai/mcp_server/transports/__init__.py +18 -0
  278. praisonai/mcp_server/transports/http_stream.py +376 -0
  279. praisonai/mcp_server/transports/stdio.py +132 -0
  280. praisonai/persistence/__init__.py +84 -0
  281. praisonai/persistence/config.py +238 -0
  282. praisonai/persistence/conversation/__init__.py +25 -0
  283. praisonai/persistence/conversation/async_mysql.py +427 -0
  284. praisonai/persistence/conversation/async_postgres.py +410 -0
  285. praisonai/persistence/conversation/async_sqlite.py +371 -0
  286. praisonai/persistence/conversation/base.py +151 -0
  287. praisonai/persistence/conversation/json_store.py +250 -0
  288. praisonai/persistence/conversation/mysql.py +387 -0
  289. praisonai/persistence/conversation/postgres.py +401 -0
  290. praisonai/persistence/conversation/singlestore.py +240 -0
  291. praisonai/persistence/conversation/sqlite.py +341 -0
  292. praisonai/persistence/conversation/supabase.py +203 -0
  293. praisonai/persistence/conversation/surrealdb.py +287 -0
  294. praisonai/persistence/factory.py +301 -0
  295. praisonai/persistence/hooks/__init__.py +18 -0
  296. praisonai/persistence/hooks/agent_hooks.py +297 -0
  297. praisonai/persistence/knowledge/__init__.py +26 -0
  298. praisonai/persistence/knowledge/base.py +144 -0
  299. praisonai/persistence/knowledge/cassandra.py +232 -0
  300. praisonai/persistence/knowledge/chroma.py +295 -0
  301. praisonai/persistence/knowledge/clickhouse.py +242 -0
  302. praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
  303. praisonai/persistence/knowledge/couchbase.py +286 -0
  304. praisonai/persistence/knowledge/lancedb.py +216 -0
  305. praisonai/persistence/knowledge/langchain_adapter.py +291 -0
  306. praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
  307. praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
  308. praisonai/persistence/knowledge/milvus.py +277 -0
  309. praisonai/persistence/knowledge/mongodb_vector.py +306 -0
  310. praisonai/persistence/knowledge/pgvector.py +335 -0
  311. praisonai/persistence/knowledge/pinecone.py +253 -0
  312. praisonai/persistence/knowledge/qdrant.py +301 -0
  313. praisonai/persistence/knowledge/redis_vector.py +291 -0
  314. praisonai/persistence/knowledge/singlestore_vector.py +299 -0
  315. praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
  316. praisonai/persistence/knowledge/upstash_vector.py +266 -0
  317. praisonai/persistence/knowledge/weaviate.py +223 -0
  318. praisonai/persistence/migrations/__init__.py +10 -0
  319. praisonai/persistence/migrations/manager.py +251 -0
  320. praisonai/persistence/orchestrator.py +406 -0
  321. praisonai/persistence/state/__init__.py +21 -0
  322. praisonai/persistence/state/async_mongodb.py +200 -0
  323. praisonai/persistence/state/base.py +107 -0
  324. praisonai/persistence/state/dynamodb.py +226 -0
  325. praisonai/persistence/state/firestore.py +175 -0
  326. praisonai/persistence/state/gcs.py +155 -0
  327. praisonai/persistence/state/memory.py +245 -0
  328. praisonai/persistence/state/mongodb.py +158 -0
  329. praisonai/persistence/state/redis.py +190 -0
  330. praisonai/persistence/state/upstash.py +144 -0
  331. praisonai/persistence/tests/__init__.py +3 -0
  332. praisonai/persistence/tests/test_all_backends.py +633 -0
  333. praisonai/profiler.py +1214 -0
  334. praisonai/recipe/__init__.py +134 -0
  335. praisonai/recipe/bridge.py +278 -0
  336. praisonai/recipe/core.py +893 -0
  337. praisonai/recipe/exceptions.py +54 -0
  338. praisonai/recipe/history.py +402 -0
  339. praisonai/recipe/models.py +266 -0
  340. praisonai/recipe/operations.py +440 -0
  341. praisonai/recipe/policy.py +422 -0
  342. praisonai/recipe/registry.py +849 -0
  343. praisonai/recipe/runtime.py +214 -0
  344. praisonai/recipe/security.py +711 -0
  345. praisonai/recipe/serve.py +859 -0
  346. praisonai/recipe/server.py +613 -0
  347. praisonai/scheduler/__init__.py +45 -0
  348. praisonai/scheduler/agent_scheduler.py +552 -0
  349. praisonai/scheduler/base.py +124 -0
  350. praisonai/scheduler/daemon_manager.py +225 -0
  351. praisonai/scheduler/state_manager.py +155 -0
  352. praisonai/scheduler/yaml_loader.py +193 -0
  353. praisonai/scheduler.py +194 -0
  354. praisonai/setup/__init__.py +1 -0
  355. praisonai/setup/build.py +21 -0
  356. praisonai/setup/post_install.py +23 -0
  357. praisonai/setup/setup_conda_env.py +25 -0
  358. praisonai/setup.py +16 -0
  359. praisonai/templates/__init__.py +116 -0
  360. praisonai/templates/cache.py +364 -0
  361. praisonai/templates/dependency_checker.py +358 -0
  362. praisonai/templates/discovery.py +391 -0
  363. praisonai/templates/loader.py +564 -0
  364. praisonai/templates/registry.py +511 -0
  365. praisonai/templates/resolver.py +206 -0
  366. praisonai/templates/security.py +327 -0
  367. praisonai/templates/tool_override.py +498 -0
  368. praisonai/templates/tools_doctor.py +256 -0
  369. praisonai/test.py +105 -0
  370. praisonai/train.py +562 -0
  371. praisonai/train_vision.py +306 -0
  372. praisonai/ui/agents.py +824 -0
  373. praisonai/ui/callbacks.py +57 -0
  374. praisonai/ui/chainlit_compat.py +246 -0
  375. praisonai/ui/chat.py +532 -0
  376. praisonai/ui/code.py +717 -0
  377. praisonai/ui/colab.py +474 -0
  378. praisonai/ui/colab_chainlit.py +81 -0
  379. praisonai/ui/components/aicoder.py +284 -0
  380. praisonai/ui/context.py +283 -0
  381. praisonai/ui/database_config.py +56 -0
  382. praisonai/ui/db.py +294 -0
  383. praisonai/ui/realtime.py +488 -0
  384. praisonai/ui/realtimeclient/__init__.py +756 -0
  385. praisonai/ui/realtimeclient/tools.py +242 -0
  386. praisonai/ui/sql_alchemy.py +710 -0
  387. praisonai/upload_vision.py +140 -0
  388. praisonai/version.py +1 -0
  389. praisonai-3.0.0.dist-info/METADATA +3493 -0
  390. praisonai-3.0.0.dist-info/RECORD +393 -0
  391. praisonai-3.0.0.dist-info/WHEEL +5 -0
  392. praisonai-3.0.0.dist-info/entry_points.txt +4 -0
  393. praisonai-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,295 @@
1
+ """
2
+ ChromaDB implementation of KnowledgeStore.
3
+
4
+ Requires: chromadb
5
+ Install: pip install chromadb
6
+ """
7
+
8
+ import logging
9
+ from typing import Any, Dict, List, Optional
10
+
11
+ from .base import KnowledgeStore, KnowledgeDocument
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class ChromaKnowledgeStore(KnowledgeStore):
17
+ """
18
+ ChromaDB-based knowledge store for vector search.
19
+
20
+ Zero-config embedded vector database.
21
+
22
+ Example:
23
+ store = ChromaKnowledgeStore(
24
+ path="./chroma_db"
25
+ )
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ path: Optional[str] = None,
31
+ host: Optional[str] = None,
32
+ port: int = 8000,
33
+ persistent: bool = True,
34
+ ):
35
+ """
36
+ Initialize ChromaDB knowledge store.
37
+
38
+ Args:
39
+ path: Path for persistent storage (embedded mode)
40
+ host: Chroma server host (client mode)
41
+ port: Chroma server port
42
+ persistent: Use persistent storage
43
+ """
44
+ try:
45
+ import chromadb
46
+ from chromadb.config import Settings
47
+ except ImportError:
48
+ raise ImportError(
49
+ "chromadb is required for ChromaDB support. "
50
+ "Install with: pip install chromadb"
51
+ )
52
+
53
+ self._chromadb = chromadb
54
+
55
+ if host:
56
+ # Client mode
57
+ self._client = chromadb.HttpClient(host=host, port=port)
58
+ elif path and persistent:
59
+ # Persistent embedded mode
60
+ self._client = chromadb.PersistentClient(
61
+ path=path,
62
+ settings=Settings(anonymized_telemetry=False)
63
+ )
64
+ else:
65
+ # In-memory mode
66
+ self._client = chromadb.Client(
67
+ settings=Settings(anonymized_telemetry=False)
68
+ )
69
+
70
+ self._collections: Dict[str, Any] = {}
71
+ logger.info(f"ChromaDB initialized (path={path}, host={host})")
72
+
73
+ def _get_collection(self, name: str):
74
+ """Get or cache a collection."""
75
+ if name not in self._collections:
76
+ self._collections[name] = self._client.get_collection(name)
77
+ return self._collections[name]
78
+
79
+ def create_collection(
80
+ self,
81
+ name: str,
82
+ dimension: int,
83
+ distance: str = "cosine",
84
+ metadata: Optional[Dict[str, Any]] = None
85
+ ) -> None:
86
+ """Create a new collection."""
87
+ distance_map = {
88
+ "cosine": "cosine",
89
+ "euclidean": "l2",
90
+ "dot": "ip",
91
+ }
92
+
93
+ self._client.create_collection(
94
+ name=name,
95
+ metadata={"hnsw:space": distance_map.get(distance, "cosine"), **(metadata or {})}
96
+ )
97
+ logger.info(f"Created ChromaDB collection: {name}")
98
+
99
+ def delete_collection(self, name: str) -> bool:
100
+ """Delete a collection."""
101
+ try:
102
+ self._client.delete_collection(name)
103
+ if name in self._collections:
104
+ del self._collections[name]
105
+ return True
106
+ except Exception as e:
107
+ logger.warning(f"Failed to delete collection {name}: {e}")
108
+ return False
109
+
110
+ def collection_exists(self, name: str) -> bool:
111
+ """Check if a collection exists."""
112
+ try:
113
+ self._client.get_collection(name)
114
+ return True
115
+ except Exception:
116
+ return False
117
+
118
+ def list_collections(self) -> List[str]:
119
+ """List all collections."""
120
+ return [c.name for c in self._client.list_collections()]
121
+
122
+ def insert(
123
+ self,
124
+ collection: str,
125
+ documents: List[KnowledgeDocument]
126
+ ) -> List[str]:
127
+ """Insert documents into a collection."""
128
+ col = self._get_collection(collection)
129
+
130
+ ids = []
131
+ embeddings = []
132
+ contents = []
133
+ metadatas = []
134
+
135
+ for doc in documents:
136
+ if doc.embedding is None:
137
+ raise ValueError(f"Document {doc.id} has no embedding")
138
+ ids.append(doc.id)
139
+ embeddings.append(doc.embedding)
140
+ contents.append(doc.content)
141
+ metadatas.append({
142
+ "content_hash": doc.content_hash or "",
143
+ "created_at": doc.created_at,
144
+ **(doc.metadata or {})
145
+ })
146
+
147
+ col.add(
148
+ ids=ids,
149
+ embeddings=embeddings,
150
+ documents=contents,
151
+ metadatas=metadatas,
152
+ )
153
+
154
+ return ids
155
+
156
+ def upsert(
157
+ self,
158
+ collection: str,
159
+ documents: List[KnowledgeDocument]
160
+ ) -> List[str]:
161
+ """Insert or update documents."""
162
+ col = self._get_collection(collection)
163
+
164
+ ids = []
165
+ embeddings = []
166
+ contents = []
167
+ metadatas = []
168
+
169
+ for doc in documents:
170
+ if doc.embedding is None:
171
+ raise ValueError(f"Document {doc.id} has no embedding")
172
+ ids.append(doc.id)
173
+ embeddings.append(doc.embedding)
174
+ contents.append(doc.content)
175
+ metadatas.append({
176
+ "content_hash": doc.content_hash or "",
177
+ "created_at": doc.created_at,
178
+ **(doc.metadata or {})
179
+ })
180
+
181
+ col.upsert(
182
+ ids=ids,
183
+ embeddings=embeddings,
184
+ documents=contents,
185
+ metadatas=metadatas,
186
+ )
187
+
188
+ return ids
189
+
190
+ def search(
191
+ self,
192
+ collection: str,
193
+ query_embedding: List[float],
194
+ limit: int = 5,
195
+ filters: Optional[Dict[str, Any]] = None,
196
+ score_threshold: Optional[float] = None
197
+ ) -> List[KnowledgeDocument]:
198
+ """Search for similar documents."""
199
+ col = self._get_collection(collection)
200
+
201
+ where = None
202
+ if filters:
203
+ where = {k: v for k, v in filters.items()}
204
+
205
+ results = col.query(
206
+ query_embeddings=[query_embedding],
207
+ n_results=limit,
208
+ where=where,
209
+ include=["documents", "metadatas", "distances"],
210
+ )
211
+
212
+ documents = []
213
+ if results["ids"] and results["ids"][0]:
214
+ for i, doc_id in enumerate(results["ids"][0]):
215
+ distance = results["distances"][0][i] if results.get("distances") else None
216
+
217
+ # Convert distance to similarity score (for cosine: 1 - distance)
218
+ if score_threshold is not None and distance is not None:
219
+ score = 1 - distance
220
+ if score < score_threshold:
221
+ continue
222
+
223
+ metadata = results["metadatas"][0][i] if results.get("metadatas") else {}
224
+ content_hash = metadata.pop("content_hash", None)
225
+ created_at = metadata.pop("created_at", 0)
226
+
227
+ doc = KnowledgeDocument(
228
+ id=doc_id,
229
+ content=results["documents"][0][i] if results.get("documents") else "",
230
+ embedding=None,
231
+ metadata=metadata,
232
+ content_hash=content_hash,
233
+ created_at=created_at,
234
+ )
235
+ documents.append(doc)
236
+
237
+ return documents
238
+
239
+ def get(
240
+ self,
241
+ collection: str,
242
+ ids: List[str]
243
+ ) -> List[KnowledgeDocument]:
244
+ """Get documents by IDs."""
245
+ col = self._get_collection(collection)
246
+
247
+ results = col.get(
248
+ ids=ids,
249
+ include=["documents", "metadatas", "embeddings"],
250
+ )
251
+
252
+ documents = []
253
+ if results["ids"]:
254
+ for i, doc_id in enumerate(results["ids"]):
255
+ metadata = results["metadatas"][i] if results.get("metadatas") else {}
256
+ content_hash = metadata.pop("content_hash", None)
257
+ created_at = metadata.pop("created_at", 0)
258
+
259
+ doc = KnowledgeDocument(
260
+ id=doc_id,
261
+ content=results["documents"][i] if results.get("documents") else "",
262
+ embedding=results["embeddings"][i] if results.get("embeddings") else None,
263
+ metadata=metadata,
264
+ content_hash=content_hash,
265
+ created_at=created_at,
266
+ )
267
+ documents.append(doc)
268
+
269
+ return documents
270
+
271
+ def delete(
272
+ self,
273
+ collection: str,
274
+ ids: Optional[List[str]] = None,
275
+ filters: Optional[Dict[str, Any]] = None
276
+ ) -> int:
277
+ """Delete documents."""
278
+ col = self._get_collection(collection)
279
+
280
+ if ids:
281
+ col.delete(ids=ids)
282
+ return len(ids)
283
+ elif filters:
284
+ col.delete(where=filters)
285
+ return -1 # Unknown count
286
+ return 0
287
+
288
+ def count(self, collection: str) -> int:
289
+ """Count documents in a collection."""
290
+ col = self._get_collection(collection)
291
+ return col.count()
292
+
293
+ def close(self) -> None:
294
+ """Close the store."""
295
+ self._collections.clear()
@@ -0,0 +1,242 @@
1
+ """
2
+ ClickHouse Vector implementation of KnowledgeStore.
3
+
4
+ Requires: clickhouse-connect
5
+ Install: pip install clickhouse-connect
6
+ """
7
+
8
+ import logging
9
+ from typing import Any, Dict, List, Optional
10
+
11
+ from .base import KnowledgeStore, KnowledgeDocument
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class ClickHouseKnowledgeStore(KnowledgeStore):
17
+ """
18
+ ClickHouse-based knowledge store using vector search.
19
+
20
+ Example:
21
+ store = ClickHouseKnowledgeStore(
22
+ host="localhost",
23
+ port=8123
24
+ )
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ host: str = "localhost",
30
+ port: int = 8123,
31
+ username: str = "default",
32
+ password: str = "",
33
+ database: str = "praisonai",
34
+ secure: bool = False,
35
+ ):
36
+ try:
37
+ import clickhouse_connect
38
+ except ImportError:
39
+ raise ImportError(
40
+ "clickhouse-connect is required for ClickHouse support. "
41
+ "Install with: pip install clickhouse-connect"
42
+ )
43
+
44
+ self._client = clickhouse_connect.get_client(
45
+ host=host,
46
+ port=port,
47
+ username=username,
48
+ password=password,
49
+ database=database,
50
+ secure=secure,
51
+ )
52
+ self.database = database
53
+
54
+ # Create database if not exists
55
+ self._client.command(f"CREATE DATABASE IF NOT EXISTS {database}")
56
+ logger.info(f"Connected to ClickHouse database: {database}")
57
+
58
+ def _table_name(self, collection: str) -> str:
59
+ return f"{self.database}.praison_vec_{collection}"
60
+
61
+ def create_collection(
62
+ self,
63
+ name: str,
64
+ dimension: int,
65
+ distance: str = "cosine",
66
+ metadata: Optional[Dict[str, Any]] = None
67
+ ) -> None:
68
+ """Create a new table with vector column."""
69
+ table = self._table_name(name)
70
+
71
+ self._client.command(f"""
72
+ CREATE TABLE IF NOT EXISTS {table} (
73
+ id String,
74
+ content String,
75
+ content_hash String,
76
+ created_at Float64,
77
+ embedding Array(Float32)
78
+ ) ENGINE = MergeTree()
79
+ ORDER BY id
80
+ """)
81
+ logger.info(f"Created ClickHouse table: {table}")
82
+
83
+ def delete_collection(self, name: str) -> bool:
84
+ """Delete a table."""
85
+ table = self._table_name(name)
86
+ try:
87
+ self._client.command(f"DROP TABLE IF EXISTS {table}")
88
+ return True
89
+ except Exception as e:
90
+ logger.warning(f"Failed to delete table {table}: {e}")
91
+ return False
92
+
93
+ def collection_exists(self, name: str) -> bool:
94
+ """Check if a table exists."""
95
+ table = self._table_name(name)
96
+ result = self._client.query(f"EXISTS TABLE {table}")
97
+ return result.result_rows[0][0] == 1
98
+
99
+ def list_collections(self) -> List[str]:
100
+ """List all collections."""
101
+ result = self._client.query(f"""
102
+ SELECT name FROM system.tables
103
+ WHERE database = '{self.database}' AND name LIKE 'praison_vec_%'
104
+ """)
105
+ return [row[0].replace("praison_vec_", "") for row in result.result_rows]
106
+
107
+ def insert(
108
+ self,
109
+ collection: str,
110
+ documents: List[KnowledgeDocument]
111
+ ) -> List[str]:
112
+ """Insert documents."""
113
+ table = self._table_name(collection)
114
+
115
+ data = []
116
+ ids = []
117
+ for doc in documents:
118
+ if doc.embedding is None:
119
+ raise ValueError(f"Document {doc.id} has no embedding")
120
+ data.append([
121
+ doc.id,
122
+ doc.content,
123
+ doc.content_hash or "",
124
+ doc.created_at,
125
+ doc.embedding,
126
+ ])
127
+ ids.append(doc.id)
128
+
129
+ self._client.insert(
130
+ table,
131
+ data,
132
+ column_names=["id", "content", "content_hash", "created_at", "embedding"]
133
+ )
134
+ return ids
135
+
136
+ def upsert(
137
+ self,
138
+ collection: str,
139
+ documents: List[KnowledgeDocument]
140
+ ) -> List[str]:
141
+ """Insert or update documents."""
142
+ # ClickHouse doesn't have native upsert for MergeTree
143
+ # Delete then insert
144
+ ids = [doc.id for doc in documents]
145
+ self.delete(collection, ids)
146
+ return self.insert(collection, documents)
147
+
148
+ def search(
149
+ self,
150
+ collection: str,
151
+ query_embedding: List[float],
152
+ limit: int = 5,
153
+ filters: Optional[Dict[str, Any]] = None,
154
+ score_threshold: Optional[float] = None
155
+ ) -> List[KnowledgeDocument]:
156
+ """Search for similar documents using cosine distance."""
157
+ table = self._table_name(collection)
158
+
159
+ # Convert embedding to string for query
160
+ embedding_str = "[" + ",".join(map(str, query_embedding)) + "]"
161
+
162
+ query = f"""
163
+ SELECT
164
+ id, content, content_hash, created_at,
165
+ 1 - cosineDistance(embedding, {embedding_str}) as score
166
+ FROM {table}
167
+ """
168
+
169
+ if score_threshold:
170
+ query += f" WHERE 1 - cosineDistance(embedding, {embedding_str}) >= {score_threshold}"
171
+
172
+ query += f" ORDER BY score DESC LIMIT {limit}"
173
+
174
+ result = self._client.query(query)
175
+
176
+ documents = []
177
+ for row in result.result_rows:
178
+ doc = KnowledgeDocument(
179
+ id=row[0],
180
+ content=row[1],
181
+ embedding=None,
182
+ metadata={},
183
+ content_hash=row[2],
184
+ created_at=row[3],
185
+ )
186
+ documents.append(doc)
187
+
188
+ return documents
189
+
190
+ def get(
191
+ self,
192
+ collection: str,
193
+ ids: List[str]
194
+ ) -> List[KnowledgeDocument]:
195
+ """Get documents by IDs."""
196
+ table = self._table_name(collection)
197
+
198
+ id_list = ", ".join([f"'{i}'" for i in ids])
199
+ result = self._client.query(f"""
200
+ SELECT id, content, content_hash, created_at, embedding
201
+ FROM {table} WHERE id IN ({id_list})
202
+ """)
203
+
204
+ documents = []
205
+ for row in result.result_rows:
206
+ doc = KnowledgeDocument(
207
+ id=row[0],
208
+ content=row[1],
209
+ embedding=list(row[4]) if row[4] else None,
210
+ metadata={},
211
+ content_hash=row[2],
212
+ created_at=row[3],
213
+ )
214
+ documents.append(doc)
215
+
216
+ return documents
217
+
218
+ def delete(
219
+ self,
220
+ collection: str,
221
+ ids: Optional[List[str]] = None,
222
+ filters: Optional[Dict[str, Any]] = None
223
+ ) -> int:
224
+ """Delete documents."""
225
+ table = self._table_name(collection)
226
+
227
+ if ids:
228
+ id_list = ", ".join([f"'{i}'" for i in ids])
229
+ self._client.command(f"ALTER TABLE {table} DELETE WHERE id IN ({id_list})")
230
+ return len(ids)
231
+ return 0
232
+
233
+ def count(self, collection: str) -> int:
234
+ """Count documents."""
235
+ table = self._table_name(collection)
236
+ result = self._client.query(f"SELECT COUNT(*) FROM {table}")
237
+ return result.result_rows[0][0]
238
+
239
+ def close(self) -> None:
240
+ """Close the store."""
241
+ if self._client:
242
+ self._client.close()