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,297 @@
1
+ """
2
+ Agent hooks for automatic persistence integration.
3
+
4
+ Provides wrapper functions to add persistence capabilities to PraisonAI agents
5
+ without modifying the core SDK.
6
+ """
7
+
8
+ import time
9
+ import uuid
10
+ import logging
11
+ from typing import Any, Dict, List, Optional, Callable
12
+ from functools import wraps
13
+
14
+ from ..orchestrator import PersistenceOrchestrator
15
+ from ..conversation.base import ConversationMessage, ConversationSession
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ def wrap_agent_with_persistence(
21
+ agent,
22
+ orchestrator: PersistenceOrchestrator,
23
+ session_id: Optional[str] = None,
24
+ user_id: Optional[str] = None,
25
+ auto_resume: bool = True,
26
+ ):
27
+ """
28
+ Wrap an agent to add automatic persistence hooks.
29
+
30
+ This function wraps the agent's chat method to automatically:
31
+ 1. Load/create session on first message
32
+ 2. Persist each user message
33
+ 3. Persist each assistant response
34
+ 4. Update session metadata after each interaction
35
+
36
+ Args:
37
+ agent: PraisonAI Agent instance
38
+ orchestrator: PersistenceOrchestrator instance
39
+ session_id: Session ID (auto-generated if None)
40
+ user_id: User ID for the session
41
+ auto_resume: Whether to auto-resume existing session
42
+
43
+ Returns:
44
+ The wrapped agent with persistence capabilities
45
+
46
+ Example:
47
+ from praisonai.persistence import PersistenceOrchestrator
48
+ from praisonai.persistence.hooks import wrap_agent_with_persistence
49
+ from praisonaiagents import Agent
50
+
51
+ orchestrator = PersistenceOrchestrator(...)
52
+ agent = Agent(name="Assistant", role="Helper")
53
+
54
+ # Wrap agent with persistence
55
+ agent = wrap_agent_with_persistence(agent, orchestrator, session_id="my-session")
56
+
57
+ # Now all chats are automatically persisted
58
+ response = agent.chat("Hello!")
59
+ """
60
+ session_id = session_id or f"session-{uuid.uuid4().hex[:8]}"
61
+ user_id = user_id or getattr(agent, 'user_id', 'default')
62
+
63
+ # Store original chat method
64
+ original_chat = agent.chat
65
+ _session_initialized = [False] # Use list to allow mutation in closure
66
+
67
+ @wraps(original_chat)
68
+ def chat_with_persistence(prompt, *args, **kwargs):
69
+ nonlocal _session_initialized
70
+
71
+ # Initialize session on first call
72
+ if not _session_initialized[0]:
73
+ history = orchestrator.on_agent_start(
74
+ agent,
75
+ session_id=session_id,
76
+ user_id=user_id,
77
+ resume=auto_resume
78
+ )
79
+
80
+ # Inject history into agent's chat_history if resuming
81
+ if auto_resume and history:
82
+ agent.chat_history = [
83
+ {"role": msg.role, "content": msg.content}
84
+ for msg in history
85
+ ]
86
+ logger.info(f"Resumed session {session_id} with {len(history)} messages")
87
+
88
+ _session_initialized[0] = True
89
+
90
+ # Persist user message
91
+ orchestrator.on_message(session_id, "user", prompt if isinstance(prompt, str) else str(prompt))
92
+
93
+ # Call original chat
94
+ response = original_chat(prompt, *args, **kwargs)
95
+
96
+ # Persist assistant response
97
+ if response:
98
+ orchestrator.on_message(session_id, "assistant", response)
99
+
100
+ return response
101
+
102
+ # Replace chat method
103
+ agent.chat = chat_with_persistence
104
+
105
+ # Add persistence-related methods to agent
106
+ agent._persistence_orchestrator = orchestrator
107
+ agent._persistence_session_id = session_id
108
+
109
+ def get_session():
110
+ """Get the current session."""
111
+ return orchestrator.get_session(session_id)
112
+
113
+ def get_messages(limit: Optional[int] = None):
114
+ """Get messages from the current session."""
115
+ return orchestrator.get_messages(session_id, limit=limit)
116
+
117
+ def end_session():
118
+ """End the current session."""
119
+ orchestrator.on_agent_end(agent, session_id)
120
+
121
+ agent.get_session = get_session
122
+ agent.get_messages = get_messages
123
+ agent.end_session = end_session
124
+
125
+ return agent
126
+
127
+
128
+ class PersistentAgent:
129
+ """
130
+ A wrapper class that provides persistence capabilities for any agent.
131
+
132
+ This is an alternative to wrap_agent_with_persistence that uses composition
133
+ instead of monkey-patching.
134
+
135
+ Example:
136
+ from praisonai.persistence import PersistenceOrchestrator
137
+ from praisonai.persistence.hooks import PersistentAgent
138
+ from praisonaiagents import Agent
139
+
140
+ orchestrator = PersistenceOrchestrator(...)
141
+ base_agent = Agent(name="Assistant", role="Helper")
142
+
143
+ agent = PersistentAgent(base_agent, orchestrator, session_id="my-session")
144
+ response = agent.chat("Hello!")
145
+ """
146
+
147
+ def __init__(
148
+ self,
149
+ agent,
150
+ orchestrator: PersistenceOrchestrator,
151
+ session_id: Optional[str] = None,
152
+ user_id: Optional[str] = None,
153
+ auto_resume: bool = True,
154
+ ):
155
+ self._agent = agent
156
+ self._orchestrator = orchestrator
157
+ self._session_id = session_id or f"session-{uuid.uuid4().hex[:8]}"
158
+ self._user_id = user_id or getattr(agent, 'user_id', 'default')
159
+ self._auto_resume = auto_resume
160
+ self._session_initialized = False
161
+
162
+ def __getattr__(self, name):
163
+ """Delegate attribute access to wrapped agent."""
164
+ return getattr(self._agent, name)
165
+
166
+ def _ensure_session(self):
167
+ """Ensure session is initialized."""
168
+ if not self._session_initialized:
169
+ history = self._orchestrator.on_agent_start(
170
+ self._agent,
171
+ session_id=self._session_id,
172
+ user_id=self._user_id,
173
+ resume=self._auto_resume
174
+ )
175
+
176
+ if self._auto_resume and history:
177
+ self._agent.chat_history = [
178
+ {"role": msg.role, "content": msg.content}
179
+ for msg in history
180
+ ]
181
+ logger.info(f"Resumed session {self._session_id} with {len(history)} messages")
182
+
183
+ self._session_initialized = True
184
+
185
+ def chat(self, prompt, *args, **kwargs):
186
+ """Chat with automatic persistence."""
187
+ self._ensure_session()
188
+
189
+ # Persist user message
190
+ self._orchestrator.on_message(
191
+ self._session_id, "user",
192
+ prompt if isinstance(prompt, str) else str(prompt)
193
+ )
194
+
195
+ # Call agent chat
196
+ response = self._agent.chat(prompt, *args, **kwargs)
197
+
198
+ # Persist assistant response
199
+ if response:
200
+ self._orchestrator.on_message(self._session_id, "assistant", response)
201
+
202
+ return response
203
+
204
+ def get_session(self) -> Optional[ConversationSession]:
205
+ """Get the current session."""
206
+ return self._orchestrator.get_session(self._session_id)
207
+
208
+ def get_messages(self, limit: Optional[int] = None) -> List[ConversationMessage]:
209
+ """Get messages from the current session."""
210
+ return self._orchestrator.get_messages(self._session_id, limit=limit)
211
+
212
+ def end_session(self):
213
+ """End the current session."""
214
+ self._orchestrator.on_agent_end(self._agent, self._session_id)
215
+
216
+ @property
217
+ def session_id(self) -> str:
218
+ """Get the session ID."""
219
+ return self._session_id
220
+
221
+
222
+ def create_persistent_session(
223
+ orchestrator: PersistenceOrchestrator,
224
+ session_id: Optional[str] = None,
225
+ user_id: Optional[str] = None,
226
+ ):
227
+ """
228
+ Create a context manager for persistent sessions.
229
+
230
+ Example:
231
+ from praisonai.persistence import PersistenceOrchestrator
232
+ from praisonai.persistence.hooks import create_persistent_session
233
+ from praisonaiagents import Agent
234
+
235
+ orchestrator = PersistenceOrchestrator(...)
236
+
237
+ with create_persistent_session(orchestrator, session_id="my-session") as session:
238
+ agent = Agent(name="Assistant", role="Helper")
239
+
240
+ # Messages are persisted within the session context
241
+ session.persist_message("user", "Hello!")
242
+ response = agent.chat("Hello!")
243
+ session.persist_message("assistant", response)
244
+ """
245
+
246
+ class PersistentSession:
247
+ def __init__(self, orchestrator, session_id, user_id):
248
+ self.orchestrator = orchestrator
249
+ self.session_id = session_id or f"session-{uuid.uuid4().hex[:8]}"
250
+ self.user_id = user_id or "default"
251
+ self._started = False
252
+
253
+ def __enter__(self):
254
+ self.start()
255
+ return self
256
+
257
+ def __exit__(self, exc_type, exc_val, exc_tb):
258
+ self.end()
259
+ return False
260
+
261
+ def start(self, agent=None):
262
+ """Start the session."""
263
+ if not self._started:
264
+ class MockAgent:
265
+ name = "session-agent"
266
+
267
+ self.orchestrator.on_agent_start(
268
+ agent or MockAgent(),
269
+ session_id=self.session_id,
270
+ user_id=self.user_id
271
+ )
272
+ self._started = True
273
+
274
+ def end(self, agent=None):
275
+ """End the session."""
276
+ if self._started:
277
+ class MockAgent:
278
+ name = "session-agent"
279
+
280
+ self.orchestrator.on_agent_end(
281
+ agent or MockAgent(),
282
+ self.session_id
283
+ )
284
+
285
+ def persist_message(self, role: str, content: str):
286
+ """Persist a message to the session."""
287
+ self.orchestrator.on_message(self.session_id, role, content)
288
+
289
+ def get_messages(self, limit: Optional[int] = None):
290
+ """Get messages from the session."""
291
+ return self.orchestrator.get_messages(self.session_id, limit=limit)
292
+
293
+ def get_session(self):
294
+ """Get the session object."""
295
+ return self.orchestrator.get_session(self.session_id)
296
+
297
+ return PersistentSession(orchestrator, session_id, user_id)
@@ -0,0 +1,26 @@
1
+ """
2
+ KnowledgeStore implementations for vector embeddings and semantic search.
3
+
4
+ Supported backends:
5
+ - Qdrant (qdrant)
6
+ - Pinecone (pinecone)
7
+ - ChromaDB (chroma)
8
+ - Weaviate (weaviate)
9
+ - LanceDB (lancedb)
10
+ - Milvus (milvus)
11
+ - PGVector (pgvector)
12
+ - Redis Vector (redis)
13
+ - Cassandra Vector (cassandra)
14
+ - ClickHouse Vector (clickhouse)
15
+ """
16
+
17
+ __all__ = [
18
+ "KnowledgeStore",
19
+ "KnowledgeDocument",
20
+ ]
21
+
22
+ def __getattr__(name: str):
23
+ if name in ("KnowledgeStore", "KnowledgeDocument"):
24
+ from .base import KnowledgeStore, KnowledgeDocument
25
+ return locals()[name]
26
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,144 @@
1
+ """
2
+ Base interfaces for KnowledgeStore.
3
+
4
+ KnowledgeStore handles vector embeddings and semantic search for RAG.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from dataclasses import dataclass, field
9
+ from typing import Any, Dict, List, Optional
10
+ import time
11
+ import uuid
12
+ import hashlib
13
+
14
+
15
+ @dataclass
16
+ class KnowledgeDocument:
17
+ """A document with vector embedding for semantic search."""
18
+ id: str = field(default_factory=lambda: str(uuid.uuid4()))
19
+ content: str = ""
20
+ embedding: Optional[List[float]] = None
21
+ metadata: Optional[Dict[str, Any]] = None
22
+ content_hash: Optional[str] = None
23
+ created_at: float = field(default_factory=time.time)
24
+
25
+ def __post_init__(self):
26
+ if self.content and not self.content_hash:
27
+ self.content_hash = hashlib.sha256(self.content.encode()).hexdigest()[:16]
28
+
29
+ def to_dict(self) -> Dict[str, Any]:
30
+ return {
31
+ "id": self.id,
32
+ "content": self.content,
33
+ "embedding": self.embedding,
34
+ "metadata": self.metadata,
35
+ "content_hash": self.content_hash,
36
+ "created_at": self.created_at,
37
+ }
38
+
39
+ @classmethod
40
+ def from_dict(cls, data: Dict[str, Any]) -> "KnowledgeDocument":
41
+ return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})
42
+
43
+
44
+ class KnowledgeStore(ABC):
45
+ """
46
+ Abstract base class for vector/knowledge persistence.
47
+
48
+ Implementations handle document storage and semantic search for different backends:
49
+ - Qdrant, Pinecone, ChromaDB, Weaviate (dedicated vector DBs)
50
+ - LanceDB, Milvus (embedded/distributed vector DBs)
51
+ - PGVector, Redis, Cassandra, ClickHouse (vector extensions)
52
+ """
53
+
54
+ @abstractmethod
55
+ def create_collection(
56
+ self,
57
+ name: str,
58
+ dimension: int,
59
+ distance: str = "cosine",
60
+ metadata: Optional[Dict[str, Any]] = None
61
+ ) -> None:
62
+ """Create a new collection/index for vectors."""
63
+ raise NotImplementedError
64
+
65
+ @abstractmethod
66
+ def delete_collection(self, name: str) -> bool:
67
+ """Delete a collection and all its documents."""
68
+ raise NotImplementedError
69
+
70
+ @abstractmethod
71
+ def collection_exists(self, name: str) -> bool:
72
+ """Check if a collection exists."""
73
+ raise NotImplementedError
74
+
75
+ @abstractmethod
76
+ def list_collections(self) -> List[str]:
77
+ """List all collections."""
78
+ raise NotImplementedError
79
+
80
+ @abstractmethod
81
+ def insert(
82
+ self,
83
+ collection: str,
84
+ documents: List[KnowledgeDocument]
85
+ ) -> List[str]:
86
+ """Insert documents into a collection. Returns list of IDs."""
87
+ raise NotImplementedError
88
+
89
+ @abstractmethod
90
+ def upsert(
91
+ self,
92
+ collection: str,
93
+ documents: List[KnowledgeDocument]
94
+ ) -> List[str]:
95
+ """Insert or update documents. Returns list of IDs."""
96
+ raise NotImplementedError
97
+
98
+ @abstractmethod
99
+ def search(
100
+ self,
101
+ collection: str,
102
+ query_embedding: List[float],
103
+ limit: int = 5,
104
+ filters: Optional[Dict[str, Any]] = None,
105
+ score_threshold: Optional[float] = None
106
+ ) -> List[KnowledgeDocument]:
107
+ """Search for similar documents by embedding."""
108
+ raise NotImplementedError
109
+
110
+ @abstractmethod
111
+ def get(
112
+ self,
113
+ collection: str,
114
+ ids: List[str]
115
+ ) -> List[KnowledgeDocument]:
116
+ """Get documents by IDs."""
117
+ raise NotImplementedError
118
+
119
+ @abstractmethod
120
+ def delete(
121
+ self,
122
+ collection: str,
123
+ ids: Optional[List[str]] = None,
124
+ filters: Optional[Dict[str, Any]] = None
125
+ ) -> int:
126
+ """Delete documents by IDs or filters. Returns count deleted."""
127
+ raise NotImplementedError
128
+
129
+ @abstractmethod
130
+ def count(self, collection: str) -> int:
131
+ """Count documents in a collection."""
132
+ raise NotImplementedError
133
+
134
+ @abstractmethod
135
+ def close(self) -> None:
136
+ """Close the store and release resources."""
137
+ raise NotImplementedError
138
+
139
+ def __enter__(self):
140
+ return self
141
+
142
+ def __exit__(self, exc_type, exc_val, exc_tb):
143
+ self.close()
144
+ return False
@@ -0,0 +1,232 @@
1
+ """
2
+ Cassandra Vector implementation of KnowledgeStore.
3
+
4
+ Requires: cassandra-driver
5
+ Install: pip install cassandra-driver
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 CassandraKnowledgeStore(KnowledgeStore):
17
+ """
18
+ Cassandra-based knowledge store using vector search (SAI).
19
+
20
+ Requires Cassandra 5.0+ or DataStax Astra with vector search.
21
+
22
+ Example:
23
+ store = CassandraKnowledgeStore(
24
+ hosts=["localhost"],
25
+ keyspace="praisonai"
26
+ )
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ hosts: Optional[List[str]] = None,
32
+ keyspace: str = "praisonai",
33
+ username: Optional[str] = None,
34
+ password: Optional[str] = None,
35
+ port: int = 9042,
36
+ secure_connect_bundle: Optional[str] = None,
37
+ ):
38
+ try:
39
+ from cassandra.cluster import Cluster
40
+ from cassandra.auth import PlainTextAuthProvider
41
+ except ImportError:
42
+ raise ImportError(
43
+ "cassandra-driver is required for Cassandra support. "
44
+ "Install with: pip install cassandra-driver"
45
+ )
46
+
47
+ self._Cluster = Cluster
48
+ self.keyspace = keyspace
49
+
50
+ auth = None
51
+ if username and password:
52
+ auth = PlainTextAuthProvider(username=username, password=password)
53
+
54
+ if secure_connect_bundle:
55
+ # Astra DB connection
56
+ from cassandra.cluster import Cluster
57
+ cloud_config = {"secure_connect_bundle": secure_connect_bundle}
58
+ self._cluster = Cluster(cloud=cloud_config, auth_provider=auth)
59
+ else:
60
+ hosts = hosts or ["localhost"]
61
+ self._cluster = Cluster(hosts, port=port, auth_provider=auth)
62
+
63
+ self._session = self._cluster.connect()
64
+
65
+ # Create keyspace if not exists
66
+ self._session.execute(f"""
67
+ CREATE KEYSPACE IF NOT EXISTS {keyspace}
68
+ WITH replication = {{'class': 'SimpleStrategy', 'replication_factor': 1}}
69
+ """)
70
+ self._session.set_keyspace(keyspace)
71
+ logger.info(f"Connected to Cassandra keyspace: {keyspace}")
72
+
73
+ def create_collection(
74
+ self,
75
+ name: str,
76
+ dimension: int,
77
+ distance: str = "cosine",
78
+ metadata: Optional[Dict[str, Any]] = None
79
+ ) -> None:
80
+ """Create a new table with vector column."""
81
+ similarity_map = {"cosine": "COSINE", "euclidean": "EUCLIDEAN", "dot": "DOT_PRODUCT"}
82
+
83
+ self._session.execute(f"""
84
+ CREATE TABLE IF NOT EXISTS {name} (
85
+ id text PRIMARY KEY,
86
+ content text,
87
+ content_hash text,
88
+ created_at double,
89
+ embedding vector<float, {dimension}>
90
+ )
91
+ """)
92
+
93
+ # Create SAI index for vector search
94
+ self._session.execute(f"""
95
+ CREATE CUSTOM INDEX IF NOT EXISTS {name}_embedding_idx ON {name} (embedding)
96
+ USING 'StorageAttachedIndex'
97
+ WITH OPTIONS = {{'similarity_function': '{similarity_map.get(distance, "COSINE")}'}}
98
+ """)
99
+ logger.info(f"Created Cassandra table: {name}")
100
+
101
+ def delete_collection(self, name: str) -> bool:
102
+ """Delete a table."""
103
+ try:
104
+ self._session.execute(f"DROP TABLE IF EXISTS {name}")
105
+ return True
106
+ except Exception as e:
107
+ logger.warning(f"Failed to delete table {name}: {e}")
108
+ return False
109
+
110
+ def collection_exists(self, name: str) -> bool:
111
+ """Check if a table exists."""
112
+ result = self._session.execute("""
113
+ SELECT table_name FROM system_schema.tables
114
+ WHERE keyspace_name = %s AND table_name = %s
115
+ """, (self.keyspace, name))
116
+ return len(list(result)) > 0
117
+
118
+ def list_collections(self) -> List[str]:
119
+ """List all tables."""
120
+ result = self._session.execute("""
121
+ SELECT table_name FROM system_schema.tables WHERE keyspace_name = %s
122
+ """, (self.keyspace,))
123
+ return [row.table_name for row in result]
124
+
125
+ def insert(
126
+ self,
127
+ collection: str,
128
+ documents: List[KnowledgeDocument]
129
+ ) -> List[str]:
130
+ """Insert documents."""
131
+ ids = []
132
+ for doc in documents:
133
+ if doc.embedding is None:
134
+ raise ValueError(f"Document {doc.id} has no embedding")
135
+
136
+ self._session.execute(f"""
137
+ INSERT INTO {collection} (id, content, content_hash, created_at, embedding)
138
+ VALUES (%s, %s, %s, %s, %s)
139
+ """, (doc.id, doc.content, doc.content_hash or "", doc.created_at, doc.embedding))
140
+ ids.append(doc.id)
141
+
142
+ return ids
143
+
144
+ def upsert(
145
+ self,
146
+ collection: str,
147
+ documents: List[KnowledgeDocument]
148
+ ) -> List[str]:
149
+ """Insert or update documents."""
150
+ return self.insert(collection, documents) # Cassandra INSERT is upsert
151
+
152
+ def search(
153
+ self,
154
+ collection: str,
155
+ query_embedding: List[float],
156
+ limit: int = 5,
157
+ filters: Optional[Dict[str, Any]] = None,
158
+ score_threshold: Optional[float] = None
159
+ ) -> List[KnowledgeDocument]:
160
+ """Search for similar documents using ANN."""
161
+ result = self._session.execute(f"""
162
+ SELECT id, content, content_hash, created_at, similarity_cosine(embedding, %s) as score
163
+ FROM {collection}
164
+ ORDER BY embedding ANN OF %s
165
+ LIMIT %s
166
+ """, (query_embedding, query_embedding, limit))
167
+
168
+ documents = []
169
+ for row in result:
170
+ if score_threshold and row.score < score_threshold:
171
+ continue
172
+
173
+ doc = KnowledgeDocument(
174
+ id=row.id,
175
+ content=row.content,
176
+ embedding=None,
177
+ metadata={},
178
+ content_hash=row.content_hash,
179
+ created_at=row.created_at,
180
+ )
181
+ documents.append(doc)
182
+
183
+ return documents
184
+
185
+ def get(
186
+ self,
187
+ collection: str,
188
+ ids: List[str]
189
+ ) -> List[KnowledgeDocument]:
190
+ """Get documents by IDs."""
191
+ documents = []
192
+ for doc_id in ids:
193
+ result = self._session.execute(f"""
194
+ SELECT id, content, content_hash, created_at, embedding
195
+ FROM {collection} WHERE id = %s
196
+ """, (doc_id,))
197
+
198
+ for row in result:
199
+ doc = KnowledgeDocument(
200
+ id=row.id,
201
+ content=row.content,
202
+ embedding=list(row.embedding) if row.embedding else None,
203
+ metadata={},
204
+ content_hash=row.content_hash,
205
+ created_at=row.created_at,
206
+ )
207
+ documents.append(doc)
208
+
209
+ return documents
210
+
211
+ def delete(
212
+ self,
213
+ collection: str,
214
+ ids: Optional[List[str]] = None,
215
+ filters: Optional[Dict[str, Any]] = None
216
+ ) -> int:
217
+ """Delete documents."""
218
+ if ids:
219
+ for doc_id in ids:
220
+ self._session.execute(f"DELETE FROM {collection} WHERE id = %s", (doc_id,))
221
+ return len(ids)
222
+ return 0
223
+
224
+ def count(self, collection: str) -> int:
225
+ """Count documents."""
226
+ result = self._session.execute(f"SELECT COUNT(*) FROM {collection}")
227
+ return result.one()[0]
228
+
229
+ def close(self) -> None:
230
+ """Close the store."""
231
+ if self._cluster:
232
+ self._cluster.shutdown()