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,291 @@
1
+ """
2
+ Redis Vector implementation of KnowledgeStore.
3
+
4
+ Requires: redis
5
+ Install: pip install redis
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ from typing import Any, Dict, List, Optional
11
+
12
+ from .base import KnowledgeStore, KnowledgeDocument
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class RedisVectorKnowledgeStore(KnowledgeStore):
18
+ """
19
+ Redis-based knowledge store using RediSearch for vector search.
20
+
21
+ Requires Redis Stack or Redis with RediSearch module.
22
+
23
+ Example:
24
+ store = RedisVectorKnowledgeStore(
25
+ url="redis://localhost:6379"
26
+ )
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ url: Optional[str] = None,
32
+ host: str = "localhost",
33
+ port: int = 6379,
34
+ password: Optional[str] = None,
35
+ prefix: str = "praison:vec:",
36
+ ):
37
+ try:
38
+ import redis
39
+ from redis.commands.search.field import VectorField, TextField, NumericField
40
+ from redis.commands.search.indexDefinition import IndexDefinition, IndexType
41
+ from redis.commands.search.query import Query
42
+ except ImportError:
43
+ raise ImportError(
44
+ "redis is required for Redis Vector support. "
45
+ "Install with: pip install redis"
46
+ )
47
+
48
+ self._redis = redis
49
+ self._VectorField = VectorField
50
+ self._TextField = TextField
51
+ self._NumericField = NumericField
52
+ self._IndexDefinition = IndexDefinition
53
+ self._IndexType = IndexType
54
+ self._Query = Query
55
+
56
+ self.prefix = prefix
57
+
58
+ if url:
59
+ self._client = redis.from_url(url, decode_responses=False)
60
+ else:
61
+ self._client = redis.Redis(
62
+ host=host, port=port, password=password, decode_responses=False
63
+ )
64
+
65
+ self._client.ping()
66
+ logger.info(f"Connected to Redis at {url or f'{host}:{port}'}")
67
+
68
+ def _index_name(self, collection: str) -> str:
69
+ return f"{self.prefix}{collection}:idx"
70
+
71
+ def _doc_key(self, collection: str, doc_id: str) -> str:
72
+ return f"{self.prefix}{collection}:{doc_id}"
73
+
74
+ def create_collection(
75
+ self,
76
+ name: str,
77
+ dimension: int,
78
+ distance: str = "cosine",
79
+ metadata: Optional[Dict[str, Any]] = None
80
+ ) -> None:
81
+ """Create a new index."""
82
+ index_name = self._index_name(name)
83
+
84
+ distance_map = {"cosine": "COSINE", "euclidean": "L2", "dot": "IP"}
85
+
86
+ schema = (
87
+ self._TextField("content"),
88
+ self._TextField("content_hash"),
89
+ self._NumericField("created_at"),
90
+ self._VectorField(
91
+ "embedding",
92
+ "HNSW",
93
+ {
94
+ "TYPE": "FLOAT32",
95
+ "DIM": dimension,
96
+ "DISTANCE_METRIC": distance_map.get(distance, "COSINE"),
97
+ }
98
+ ),
99
+ )
100
+
101
+ definition = self._IndexDefinition(
102
+ prefix=[f"{self.prefix}{name}:"],
103
+ index_type=self._IndexType.HASH,
104
+ )
105
+
106
+ try:
107
+ self._client.ft(index_name).create_index(schema, definition=definition)
108
+ logger.info(f"Created Redis index: {index_name}")
109
+ except Exception as e:
110
+ if "Index already exists" not in str(e):
111
+ raise
112
+
113
+ def delete_collection(self, name: str) -> bool:
114
+ """Delete an index and all documents."""
115
+ index_name = self._index_name(name)
116
+ try:
117
+ self._client.ft(index_name).dropindex(delete_documents=True)
118
+ return True
119
+ except Exception as e:
120
+ logger.warning(f"Failed to delete index {index_name}: {e}")
121
+ return False
122
+
123
+ def collection_exists(self, name: str) -> bool:
124
+ """Check if an index exists."""
125
+ index_name = self._index_name(name)
126
+ try:
127
+ self._client.ft(index_name).info()
128
+ return True
129
+ except Exception:
130
+ return False
131
+
132
+ def list_collections(self) -> List[str]:
133
+ """List all collections."""
134
+ try:
135
+ indexes = self._client.execute_command("FT._LIST")
136
+ prefix = f"{self.prefix}"
137
+ return [
138
+ idx.decode().replace(prefix, "").replace(":idx", "")
139
+ for idx in indexes
140
+ if idx.decode().startswith(prefix)
141
+ ]
142
+ except Exception:
143
+ return []
144
+
145
+ def insert(
146
+ self,
147
+ collection: str,
148
+ documents: List[KnowledgeDocument]
149
+ ) -> List[str]:
150
+ """Insert documents."""
151
+ import numpy as np
152
+
153
+ ids = []
154
+ pipe = self._client.pipeline()
155
+
156
+ for doc in documents:
157
+ if doc.embedding is None:
158
+ raise ValueError(f"Document {doc.id} has no embedding")
159
+
160
+ key = self._doc_key(collection, doc.id)
161
+ embedding_bytes = np.array(doc.embedding, dtype=np.float32).tobytes()
162
+
163
+ pipe.hset(key, mapping={
164
+ "content": doc.content,
165
+ "content_hash": doc.content_hash or "",
166
+ "created_at": doc.created_at,
167
+ "embedding": embedding_bytes,
168
+ })
169
+ ids.append(doc.id)
170
+
171
+ pipe.execute()
172
+ return ids
173
+
174
+ def upsert(
175
+ self,
176
+ collection: str,
177
+ documents: List[KnowledgeDocument]
178
+ ) -> List[str]:
179
+ """Insert or update documents."""
180
+ return self.insert(collection, documents)
181
+
182
+ def search(
183
+ self,
184
+ collection: str,
185
+ query_embedding: List[float],
186
+ limit: int = 5,
187
+ filters: Optional[Dict[str, Any]] = None,
188
+ score_threshold: Optional[float] = None
189
+ ) -> List[KnowledgeDocument]:
190
+ """Search for similar documents."""
191
+ import numpy as np
192
+
193
+ index_name = self._index_name(collection)
194
+ query_bytes = np.array(query_embedding, dtype=np.float32).tobytes()
195
+
196
+ query_str = f"*=>[KNN {limit} @embedding $vec AS score]"
197
+
198
+ query = (
199
+ self._Query(query_str)
200
+ .return_fields("content", "content_hash", "created_at", "score")
201
+ .sort_by("score")
202
+ .dialect(2)
203
+ )
204
+
205
+ results = self._client.ft(index_name).search(
206
+ query, query_params={"vec": query_bytes}
207
+ )
208
+
209
+ documents = []
210
+ for doc in results.docs:
211
+ score = float(doc.score) if hasattr(doc, "score") else 0
212
+ # Redis returns distance, convert to similarity for cosine
213
+ similarity = 1 - score
214
+
215
+ if score_threshold and similarity < score_threshold:
216
+ continue
217
+
218
+ doc_id = doc.id.decode() if isinstance(doc.id, bytes) else doc.id
219
+ doc_id = doc_id.split(":")[-1] # Remove prefix
220
+
221
+ knowledge_doc = KnowledgeDocument(
222
+ id=doc_id,
223
+ content=doc.content.decode() if isinstance(doc.content, bytes) else doc.content,
224
+ embedding=None,
225
+ metadata={},
226
+ content_hash=doc.content_hash.decode() if isinstance(doc.content_hash, bytes) else doc.content_hash,
227
+ created_at=float(doc.created_at) if doc.created_at else 0,
228
+ )
229
+ documents.append(knowledge_doc)
230
+
231
+ return documents
232
+
233
+ def get(
234
+ self,
235
+ collection: str,
236
+ ids: List[str]
237
+ ) -> List[KnowledgeDocument]:
238
+ """Get documents by IDs."""
239
+ import numpy as np
240
+
241
+ documents = []
242
+ for doc_id in ids:
243
+ key = self._doc_key(collection, doc_id)
244
+ data = self._client.hgetall(key)
245
+
246
+ if data:
247
+ embedding = None
248
+ if b"embedding" in data:
249
+ embedding = np.frombuffer(data[b"embedding"], dtype=np.float32).tolist()
250
+
251
+ doc = KnowledgeDocument(
252
+ id=doc_id,
253
+ content=data.get(b"content", b"").decode(),
254
+ embedding=embedding,
255
+ metadata={},
256
+ content_hash=data.get(b"content_hash", b"").decode(),
257
+ created_at=float(data.get(b"created_at", 0)),
258
+ )
259
+ documents.append(doc)
260
+
261
+ return documents
262
+
263
+ def delete(
264
+ self,
265
+ collection: str,
266
+ ids: Optional[List[str]] = None,
267
+ filters: Optional[Dict[str, Any]] = None
268
+ ) -> int:
269
+ """Delete documents."""
270
+ if ids:
271
+ pipe = self._client.pipeline()
272
+ for doc_id in ids:
273
+ key = self._doc_key(collection, doc_id)
274
+ pipe.delete(key)
275
+ pipe.execute()
276
+ return len(ids)
277
+ return 0
278
+
279
+ def count(self, collection: str) -> int:
280
+ """Count documents."""
281
+ index_name = self._index_name(collection)
282
+ try:
283
+ info = self._client.ft(index_name).info()
284
+ return int(info.get("num_docs", 0))
285
+ except Exception:
286
+ return 0
287
+
288
+ def close(self) -> None:
289
+ """Close the store."""
290
+ if self._client:
291
+ self._client.close()
@@ -0,0 +1,299 @@
1
+ """
2
+ SingleStore Vector implementation of KnowledgeStore.
3
+
4
+ Requires: singlestoredb
5
+ Install: pip install singlestoredb
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 SingleStoreVectorKnowledgeStore(KnowledgeStore):
17
+ """
18
+ SingleStore vector store for knowledge/RAG.
19
+
20
+ Uses SingleStore's native vector capabilities.
21
+
22
+ Example:
23
+ store = SingleStoreVectorKnowledgeStore(
24
+ url="singlestoredb://user:pass@host:3306/db"
25
+ )
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ url: Optional[str] = None,
31
+ host: str = "localhost",
32
+ port: int = 3306,
33
+ database: str = "praisonai",
34
+ user: str = "root",
35
+ password: str = "",
36
+ table_prefix: str = "praisonai_",
37
+ embedding_dim: int = 1536,
38
+ ):
39
+ """
40
+ Initialize SingleStore Vector store.
41
+
42
+ Args:
43
+ url: SingleStore connection URL
44
+ host: Database host
45
+ port: Database port
46
+ database: Database name
47
+ user: Database user
48
+ password: Database password
49
+ table_prefix: Prefix for table names
50
+ embedding_dim: Embedding dimension
51
+ """
52
+ self.url = url
53
+ self.host = host
54
+ self.port = port
55
+ self.database = database
56
+ self.user = user
57
+ self.password = password
58
+ self.table_prefix = table_prefix
59
+ self.embedding_dim = embedding_dim
60
+
61
+ self._conn = None
62
+ self._initialized = False
63
+
64
+ def _init_client(self):
65
+ """Initialize SingleStore client lazily."""
66
+ if self._initialized:
67
+ return
68
+
69
+ try:
70
+ import singlestoredb as s2
71
+ except ImportError:
72
+ raise ImportError(
73
+ "singlestoredb is required for SingleStore Vector support. "
74
+ "Install with: pip install singlestoredb"
75
+ )
76
+
77
+ if self.url:
78
+ self._conn = s2.connect(self.url)
79
+ else:
80
+ self._conn = s2.connect(
81
+ host=self.host,
82
+ port=self.port,
83
+ database=self.database,
84
+ user=self.user,
85
+ password=self.password
86
+ )
87
+
88
+ self._initialized = True
89
+
90
+ def create_collection(
91
+ self,
92
+ name: str,
93
+ dimension: int = 1536,
94
+ distance: str = "cosine",
95
+ metadata: Optional[Dict[str, Any]] = None
96
+ ) -> None:
97
+ """Create a vector table."""
98
+ self._init_client()
99
+
100
+ table_name = f"{self.table_prefix}{name}"
101
+
102
+ with self._conn.cursor() as cur:
103
+ cur.execute(f"""
104
+ CREATE TABLE IF NOT EXISTS {table_name} (
105
+ id VARCHAR(255) PRIMARY KEY,
106
+ content TEXT,
107
+ embedding VECTOR({dimension}),
108
+ metadata JSON,
109
+ content_hash VARCHAR(64),
110
+ created_at DOUBLE
111
+ )
112
+ """)
113
+
114
+ def delete_collection(self, name: str) -> bool:
115
+ """Delete a vector table."""
116
+ self._init_client()
117
+
118
+ table_name = f"{self.table_prefix}{name}"
119
+ try:
120
+ with self._conn.cursor() as cur:
121
+ cur.execute(f"DROP TABLE IF EXISTS {table_name}")
122
+ return True
123
+ except Exception as e:
124
+ logger.error(f"Failed to delete collection {name}: {e}")
125
+ return False
126
+
127
+ def collection_exists(self, name: str) -> bool:
128
+ """Check if table exists."""
129
+ self._init_client()
130
+
131
+ table_name = f"{self.table_prefix}{name}"
132
+ with self._conn.cursor() as cur:
133
+ cur.execute(f"""
134
+ SELECT COUNT(*) FROM information_schema.tables
135
+ WHERE table_schema = %s AND table_name = %s
136
+ """, (self.database, table_name))
137
+ return cur.fetchone()[0] > 0
138
+
139
+ def list_collections(self) -> List[str]:
140
+ """List all vector tables."""
141
+ self._init_client()
142
+
143
+ with self._conn.cursor() as cur:
144
+ cur.execute(f"""
145
+ SELECT table_name FROM information_schema.tables
146
+ WHERE table_schema = %s AND table_name LIKE %s
147
+ """, (self.database, f"{self.table_prefix}%"))
148
+ return [row[0][len(self.table_prefix):] for row in cur.fetchall()]
149
+
150
+ def insert(
151
+ self,
152
+ collection: str,
153
+ documents: List[KnowledgeDocument]
154
+ ) -> List[str]:
155
+ """Insert documents."""
156
+ self._init_client()
157
+
158
+ table_name = f"{self.table_prefix}{collection}"
159
+ ids = []
160
+
161
+ with self._conn.cursor() as cur:
162
+ for doc in documents:
163
+ embedding_str = str(doc.embedding) if doc.embedding else None
164
+ cur.execute(f"""
165
+ INSERT INTO {table_name} (id, content, embedding, metadata, content_hash, created_at)
166
+ VALUES (%s, %s, %s, %s, %s, %s)
167
+ """, (doc.id, doc.content, embedding_str,
168
+ str(doc.metadata) if doc.metadata else None,
169
+ doc.content_hash, doc.created_at))
170
+ ids.append(doc.id)
171
+
172
+ return ids
173
+
174
+ def upsert(
175
+ self,
176
+ collection: str,
177
+ documents: List[KnowledgeDocument]
178
+ ) -> List[str]:
179
+ """Upsert documents."""
180
+ self._init_client()
181
+
182
+ table_name = f"{self.table_prefix}{collection}"
183
+ ids = []
184
+
185
+ with self._conn.cursor() as cur:
186
+ for doc in documents:
187
+ embedding_str = str(doc.embedding) if doc.embedding else None
188
+ cur.execute(f"""
189
+ REPLACE INTO {table_name} (id, content, embedding, metadata, content_hash, created_at)
190
+ VALUES (%s, %s, %s, %s, %s, %s)
191
+ """, (doc.id, doc.content, embedding_str,
192
+ str(doc.metadata) if doc.metadata else None,
193
+ doc.content_hash, doc.created_at))
194
+ ids.append(doc.id)
195
+
196
+ return ids
197
+
198
+ def search(
199
+ self,
200
+ collection: str,
201
+ query_embedding: List[float],
202
+ limit: int = 5,
203
+ filters: Optional[Dict[str, Any]] = None,
204
+ score_threshold: Optional[float] = None
205
+ ) -> List[KnowledgeDocument]:
206
+ """Search for similar documents using vector search."""
207
+ self._init_client()
208
+
209
+ table_name = f"{self.table_prefix}{collection}"
210
+ embedding_str = str(query_embedding)
211
+
212
+ with self._conn.cursor() as cur:
213
+ cur.execute(f"""
214
+ SELECT id, content, metadata, content_hash, created_at,
215
+ DOT_PRODUCT(embedding, %s) as score
216
+ FROM {table_name}
217
+ ORDER BY score DESC
218
+ LIMIT %s
219
+ """, (embedding_str, limit))
220
+
221
+ documents = []
222
+ for row in cur.fetchall():
223
+ score = row[5] if len(row) > 5 else 0
224
+ if score_threshold and score < score_threshold:
225
+ continue
226
+
227
+ documents.append(KnowledgeDocument(
228
+ id=row[0],
229
+ content=row[1],
230
+ metadata=eval(row[2]) if row[2] else None,
231
+ content_hash=row[3],
232
+ created_at=row[4]
233
+ ))
234
+
235
+ return documents
236
+
237
+ def get(
238
+ self,
239
+ collection: str,
240
+ ids: List[str]
241
+ ) -> List[KnowledgeDocument]:
242
+ """Get documents by IDs."""
243
+ self._init_client()
244
+
245
+ table_name = f"{self.table_prefix}{collection}"
246
+ placeholders = ','.join(['%s'] * len(ids))
247
+
248
+ with self._conn.cursor() as cur:
249
+ cur.execute(f"""
250
+ SELECT id, content, metadata, content_hash, created_at
251
+ FROM {table_name}
252
+ WHERE id IN ({placeholders})
253
+ """, ids)
254
+
255
+ return [
256
+ KnowledgeDocument(
257
+ id=row[0],
258
+ content=row[1],
259
+ metadata=eval(row[2]) if row[2] else None,
260
+ content_hash=row[3],
261
+ created_at=row[4]
262
+ )
263
+ for row in cur.fetchall()
264
+ ]
265
+
266
+ def delete(
267
+ self,
268
+ collection: str,
269
+ ids: Optional[List[str]] = None,
270
+ filters: Optional[Dict[str, Any]] = None
271
+ ) -> int:
272
+ """Delete documents."""
273
+ self._init_client()
274
+
275
+ table_name = f"{self.table_prefix}{collection}"
276
+
277
+ with self._conn.cursor() as cur:
278
+ if ids:
279
+ placeholders = ','.join(['%s'] * len(ids))
280
+ cur.execute(f"DELETE FROM {table_name} WHERE id IN ({placeholders})", ids)
281
+ else:
282
+ cur.execute(f"DELETE FROM {table_name}")
283
+ return cur.rowcount
284
+
285
+ def count(self, collection: str) -> int:
286
+ """Count documents."""
287
+ self._init_client()
288
+
289
+ table_name = f"{self.table_prefix}{collection}"
290
+ with self._conn.cursor() as cur:
291
+ cur.execute(f"SELECT COUNT(*) FROM {table_name}")
292
+ return cur.fetchone()[0]
293
+
294
+ def close(self) -> None:
295
+ """Close the connection."""
296
+ if self._conn:
297
+ self._conn.close()
298
+ self._conn = None
299
+ self._initialized = False