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,410 @@
1
+ """
2
+ Async PostgreSQL implementation of ConversationStore.
3
+
4
+ Requires: asyncpg
5
+ Install: pip install asyncpg
6
+ """
7
+
8
+ import asyncio
9
+ import json
10
+ import logging
11
+ import time
12
+ from typing import List, Optional
13
+
14
+ from .base import ConversationStore, ConversationSession, ConversationMessage
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class AsyncPostgresConversationStore(ConversationStore):
20
+ """
21
+ Async PostgreSQL conversation store using asyncpg.
22
+
23
+ Provides high-performance async database operations.
24
+
25
+ Example:
26
+ store = AsyncPostgresConversationStore(
27
+ url="postgresql://user:pass@localhost:5432/praisonai"
28
+ )
29
+ await store.init()
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ url: Optional[str] = None,
35
+ host: str = "localhost",
36
+ port: int = 5432,
37
+ database: str = "praisonai",
38
+ user: str = "postgres",
39
+ password: str = "",
40
+ table_prefix: str = "praisonai_",
41
+ pool_size: int = 10,
42
+ ):
43
+ """
44
+ Initialize async PostgreSQL store.
45
+
46
+ Args:
47
+ url: PostgreSQL connection URL (takes precedence)
48
+ host: Database host
49
+ port: Database port
50
+ database: Database name
51
+ user: Database user
52
+ password: Database password
53
+ table_prefix: Prefix for table names
54
+ pool_size: Connection pool size
55
+ """
56
+ self.url = url
57
+ self.host = host
58
+ self.port = port
59
+ self.database = database
60
+ self.user = user
61
+ self.password = password
62
+ self.table_prefix = table_prefix
63
+ self.pool_size = pool_size
64
+ self._pool = None
65
+ self._initialized = False
66
+
67
+ async def init(self):
68
+ """Initialize connection pool and create tables."""
69
+ if self._initialized:
70
+ return
71
+
72
+ try:
73
+ import asyncpg
74
+ except ImportError:
75
+ raise ImportError(
76
+ "asyncpg is required for async PostgreSQL support. "
77
+ "Install with: pip install asyncpg"
78
+ )
79
+
80
+ if self.url:
81
+ self._pool = await asyncpg.create_pool(self.url, min_size=1, max_size=self.pool_size)
82
+ else:
83
+ self._pool = await asyncpg.create_pool(
84
+ host=self.host,
85
+ port=self.port,
86
+ database=self.database,
87
+ user=self.user,
88
+ password=self.password,
89
+ min_size=1,
90
+ max_size=self.pool_size
91
+ )
92
+
93
+ await self._create_tables()
94
+ self._initialized = True
95
+
96
+ async def _create_tables(self):
97
+ """Create required tables if they don't exist."""
98
+ sessions_table = f"{self.table_prefix}sessions"
99
+ messages_table = f"{self.table_prefix}messages"
100
+
101
+ async with self._pool.acquire() as conn:
102
+ await conn.execute(f"""
103
+ CREATE TABLE IF NOT EXISTS {sessions_table} (
104
+ session_id VARCHAR(255) PRIMARY KEY,
105
+ user_id VARCHAR(255),
106
+ agent_id VARCHAR(255),
107
+ name VARCHAR(255),
108
+ metadata JSONB,
109
+ created_at DOUBLE PRECISION,
110
+ updated_at DOUBLE PRECISION
111
+ )
112
+ """)
113
+
114
+ await conn.execute(f"""
115
+ CREATE TABLE IF NOT EXISTS {messages_table} (
116
+ id VARCHAR(255) PRIMARY KEY,
117
+ session_id VARCHAR(255) REFERENCES {sessions_table}(session_id) ON DELETE CASCADE,
118
+ role VARCHAR(50),
119
+ content TEXT,
120
+ metadata JSONB,
121
+ created_at DOUBLE PRECISION
122
+ )
123
+ """)
124
+
125
+ await conn.execute(f"""
126
+ CREATE INDEX IF NOT EXISTS idx_{messages_table}_session
127
+ ON {messages_table}(session_id)
128
+ """)
129
+
130
+ async def async_create_session(self, session: ConversationSession) -> ConversationSession:
131
+ """Create a new session asynchronously."""
132
+ if not self._initialized:
133
+ await self.init()
134
+
135
+ table = f"{self.table_prefix}sessions"
136
+ async with self._pool.acquire() as conn:
137
+ await conn.execute(f"""
138
+ INSERT INTO {table} (session_id, user_id, agent_id, name, metadata, created_at, updated_at)
139
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
140
+ """, session.session_id, session.user_id, session.agent_id, session.name,
141
+ json.dumps(session.metadata) if session.metadata else None,
142
+ session.created_at, session.updated_at)
143
+
144
+ return session
145
+
146
+ def create_session(self, session: ConversationSession) -> ConversationSession:
147
+ """Sync wrapper for create_session."""
148
+ return asyncio.get_event_loop().run_until_complete(
149
+ self.async_create_session(session)
150
+ )
151
+
152
+ async def async_get_session(self, session_id: str) -> Optional[ConversationSession]:
153
+ """Get a session by ID asynchronously."""
154
+ if not self._initialized:
155
+ await self.init()
156
+
157
+ table = f"{self.table_prefix}sessions"
158
+ async with self._pool.acquire() as conn:
159
+ row = await conn.fetchrow(f"""
160
+ SELECT * FROM {table} WHERE session_id = $1
161
+ """, session_id)
162
+
163
+ if row:
164
+ return ConversationSession(
165
+ session_id=row['session_id'],
166
+ user_id=row['user_id'],
167
+ agent_id=row['agent_id'],
168
+ name=row['name'],
169
+ metadata=json.loads(row['metadata']) if row['metadata'] else None,
170
+ created_at=row['created_at'],
171
+ updated_at=row['updated_at']
172
+ )
173
+ return None
174
+
175
+ def get_session(self, session_id: str) -> Optional[ConversationSession]:
176
+ """Sync wrapper for get_session."""
177
+ return asyncio.get_event_loop().run_until_complete(
178
+ self.async_get_session(session_id)
179
+ )
180
+
181
+ async def async_update_session(self, session: ConversationSession) -> ConversationSession:
182
+ """Update an existing session asynchronously."""
183
+ if not self._initialized:
184
+ await self.init()
185
+
186
+ session.updated_at = time.time()
187
+ table = f"{self.table_prefix}sessions"
188
+
189
+ async with self._pool.acquire() as conn:
190
+ await conn.execute(f"""
191
+ UPDATE {table}
192
+ SET name = $2, metadata = $3, updated_at = $4
193
+ WHERE session_id = $1
194
+ """, session.session_id, session.name,
195
+ json.dumps(session.metadata) if session.metadata else None,
196
+ session.updated_at)
197
+
198
+ return session
199
+
200
+ def update_session(self, session: ConversationSession) -> ConversationSession:
201
+ """Sync wrapper for update_session."""
202
+ return asyncio.get_event_loop().run_until_complete(
203
+ self.async_update_session(session)
204
+ )
205
+
206
+ async def async_delete_session(self, session_id: str) -> bool:
207
+ """Delete a session asynchronously."""
208
+ if not self._initialized:
209
+ await self.init()
210
+
211
+ table = f"{self.table_prefix}sessions"
212
+ async with self._pool.acquire() as conn:
213
+ result = await conn.execute(f"""
214
+ DELETE FROM {table} WHERE session_id = $1
215
+ """, session_id)
216
+
217
+ return "DELETE 1" in result
218
+
219
+ def delete_session(self, session_id: str) -> bool:
220
+ """Sync wrapper for delete_session."""
221
+ return asyncio.get_event_loop().run_until_complete(
222
+ self.async_delete_session(session_id)
223
+ )
224
+
225
+ async def async_list_sessions(
226
+ self,
227
+ user_id: Optional[str] = None,
228
+ agent_id: Optional[str] = None,
229
+ limit: int = 100,
230
+ offset: int = 0
231
+ ) -> List[ConversationSession]:
232
+ """List sessions asynchronously."""
233
+ if not self._initialized:
234
+ await self.init()
235
+
236
+ table = f"{self.table_prefix}sessions"
237
+ conditions = []
238
+ params = []
239
+ param_idx = 1
240
+
241
+ if user_id:
242
+ conditions.append(f"user_id = ${param_idx}")
243
+ params.append(user_id)
244
+ param_idx += 1
245
+
246
+ if agent_id:
247
+ conditions.append(f"agent_id = ${param_idx}")
248
+ params.append(agent_id)
249
+ param_idx += 1
250
+
251
+ where_clause = f"WHERE {' AND '.join(conditions)}" if conditions else ""
252
+ params.extend([limit, offset])
253
+
254
+ async with self._pool.acquire() as conn:
255
+ rows = await conn.fetch(f"""
256
+ SELECT * FROM {table} {where_clause}
257
+ ORDER BY updated_at DESC
258
+ LIMIT ${param_idx} OFFSET ${param_idx + 1}
259
+ """, *params)
260
+
261
+ return [
262
+ ConversationSession(
263
+ session_id=row['session_id'],
264
+ user_id=row['user_id'],
265
+ agent_id=row['agent_id'],
266
+ name=row['name'],
267
+ metadata=json.loads(row['metadata']) if row['metadata'] else None,
268
+ created_at=row['created_at'],
269
+ updated_at=row['updated_at']
270
+ )
271
+ for row in rows
272
+ ]
273
+
274
+ def list_sessions(
275
+ self,
276
+ user_id: Optional[str] = None,
277
+ agent_id: Optional[str] = None,
278
+ limit: int = 100,
279
+ offset: int = 0
280
+ ) -> List[ConversationSession]:
281
+ """Sync wrapper for list_sessions."""
282
+ return asyncio.get_event_loop().run_until_complete(
283
+ self.async_list_sessions(user_id, agent_id, limit, offset)
284
+ )
285
+
286
+ async def async_add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
287
+ """Add a message asynchronously."""
288
+ if not self._initialized:
289
+ await self.init()
290
+
291
+ message.session_id = session_id
292
+ table = f"{self.table_prefix}messages"
293
+
294
+ async with self._pool.acquire() as conn:
295
+ await conn.execute(f"""
296
+ INSERT INTO {table} (id, session_id, role, content, metadata, created_at)
297
+ VALUES ($1, $2, $3, $4, $5, $6)
298
+ """, message.id, session_id, message.role, message.content,
299
+ json.dumps(message.metadata) if message.metadata else None,
300
+ message.created_at)
301
+
302
+ return message
303
+
304
+ def add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
305
+ """Sync wrapper for add_message."""
306
+ return asyncio.get_event_loop().run_until_complete(
307
+ self.async_add_message(session_id, message)
308
+ )
309
+
310
+ async def async_get_messages(
311
+ self,
312
+ session_id: str,
313
+ limit: Optional[int] = None,
314
+ before: Optional[float] = None,
315
+ after: Optional[float] = None
316
+ ) -> List[ConversationMessage]:
317
+ """Get messages asynchronously."""
318
+ if not self._initialized:
319
+ await self.init()
320
+
321
+ table = f"{self.table_prefix}messages"
322
+ conditions = ["session_id = $1"]
323
+ params = [session_id]
324
+ param_idx = 2
325
+
326
+ if before:
327
+ conditions.append(f"created_at < ${param_idx}")
328
+ params.append(before)
329
+ param_idx += 1
330
+
331
+ if after:
332
+ conditions.append(f"created_at > ${param_idx}")
333
+ params.append(after)
334
+ param_idx += 1
335
+
336
+ where_clause = f"WHERE {' AND '.join(conditions)}"
337
+ limit_clause = f"LIMIT ${param_idx}" if limit else ""
338
+ if limit:
339
+ params.append(limit)
340
+
341
+ async with self._pool.acquire() as conn:
342
+ rows = await conn.fetch(f"""
343
+ SELECT * FROM {table} {where_clause}
344
+ ORDER BY created_at ASC {limit_clause}
345
+ """, *params)
346
+
347
+ return [
348
+ ConversationMessage(
349
+ id=row['id'],
350
+ session_id=row['session_id'],
351
+ role=row['role'],
352
+ content=row['content'],
353
+ metadata=json.loads(row['metadata']) if row['metadata'] else None,
354
+ created_at=row['created_at']
355
+ )
356
+ for row in rows
357
+ ]
358
+
359
+ def get_messages(
360
+ self,
361
+ session_id: str,
362
+ limit: Optional[int] = None,
363
+ before: Optional[float] = None,
364
+ after: Optional[float] = None
365
+ ) -> List[ConversationMessage]:
366
+ """Sync wrapper for get_messages."""
367
+ return asyncio.get_event_loop().run_until_complete(
368
+ self.async_get_messages(session_id, limit, before, after)
369
+ )
370
+
371
+ async def async_delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
372
+ """Delete messages asynchronously."""
373
+ if not self._initialized:
374
+ await self.init()
375
+
376
+ table = f"{self.table_prefix}messages"
377
+
378
+ async with self._pool.acquire() as conn:
379
+ if message_ids is None:
380
+ result = await conn.execute(f"""
381
+ DELETE FROM {table} WHERE session_id = $1
382
+ """, session_id)
383
+ else:
384
+ result = await conn.execute(f"""
385
+ DELETE FROM {table} WHERE session_id = $1 AND id = ANY($2)
386
+ """, session_id, message_ids)
387
+
388
+ # Parse count from result like "DELETE 5"
389
+ try:
390
+ return int(result.split()[-1])
391
+ except (ValueError, IndexError):
392
+ return 0
393
+
394
+ def delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
395
+ """Sync wrapper for delete_messages."""
396
+ return asyncio.get_event_loop().run_until_complete(
397
+ self.async_delete_messages(session_id, message_ids)
398
+ )
399
+
400
+ async def async_close(self) -> None:
401
+ """Close the connection pool."""
402
+ if self._pool:
403
+ await self._pool.close()
404
+ self._pool = None
405
+ self._initialized = False
406
+
407
+ def close(self) -> None:
408
+ """Sync wrapper for close."""
409
+ if self._pool:
410
+ asyncio.get_event_loop().run_until_complete(self.async_close())