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,371 @@
1
+ """
2
+ Async SQLite implementation of ConversationStore.
3
+
4
+ Requires: aiosqlite
5
+ Install: pip install aiosqlite
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 AsyncSQLiteConversationStore(ConversationStore):
20
+ """
21
+ Async SQLite conversation store using aiosqlite.
22
+
23
+ Provides async database operations for SQLite.
24
+
25
+ Example:
26
+ store = AsyncSQLiteConversationStore(path="./conversations.db")
27
+ await store.init()
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ path: str = "praisonai_conversations.db",
33
+ table_prefix: str = "praisonai_",
34
+ ):
35
+ """
36
+ Initialize async SQLite store.
37
+
38
+ Args:
39
+ path: Path to SQLite database file
40
+ table_prefix: Prefix for table names
41
+ """
42
+ self.path = path
43
+ self.table_prefix = table_prefix
44
+ self._conn = None
45
+ self._initialized = False
46
+
47
+ async def init(self):
48
+ """Initialize connection and create tables."""
49
+ if self._initialized:
50
+ return
51
+
52
+ try:
53
+ import aiosqlite
54
+ except ImportError:
55
+ raise ImportError(
56
+ "aiosqlite is required for async SQLite support. "
57
+ "Install with: pip install aiosqlite"
58
+ )
59
+
60
+ self._aiosqlite = aiosqlite
61
+ self._conn = await aiosqlite.connect(self.path)
62
+ self._conn.row_factory = aiosqlite.Row
63
+ await self._create_tables()
64
+ self._initialized = True
65
+
66
+ async def _create_tables(self):
67
+ """Create required tables if they don't exist."""
68
+ sessions_table = f"{self.table_prefix}sessions"
69
+ messages_table = f"{self.table_prefix}messages"
70
+
71
+ await self._conn.execute(f"""
72
+ CREATE TABLE IF NOT EXISTS {sessions_table} (
73
+ session_id TEXT PRIMARY KEY,
74
+ user_id TEXT,
75
+ agent_id TEXT,
76
+ name TEXT,
77
+ metadata TEXT,
78
+ created_at REAL,
79
+ updated_at REAL
80
+ )
81
+ """)
82
+
83
+ await self._conn.execute(f"""
84
+ CREATE TABLE IF NOT EXISTS {messages_table} (
85
+ id TEXT PRIMARY KEY,
86
+ session_id TEXT REFERENCES {sessions_table}(session_id) ON DELETE CASCADE,
87
+ role TEXT,
88
+ content TEXT,
89
+ metadata TEXT,
90
+ created_at REAL
91
+ )
92
+ """)
93
+
94
+ await self._conn.execute(f"""
95
+ CREATE INDEX IF NOT EXISTS idx_{messages_table}_session
96
+ ON {messages_table}(session_id)
97
+ """)
98
+
99
+ await self._conn.commit()
100
+
101
+ async def async_create_session(self, session: ConversationSession) -> ConversationSession:
102
+ """Create a new session asynchronously."""
103
+ if not self._initialized:
104
+ await self.init()
105
+
106
+ table = f"{self.table_prefix}sessions"
107
+ await self._conn.execute(f"""
108
+ INSERT INTO {table} (session_id, user_id, agent_id, name, metadata, created_at, updated_at)
109
+ VALUES (?, ?, ?, ?, ?, ?, ?)
110
+ """, (session.session_id, session.user_id, session.agent_id, session.name,
111
+ json.dumps(session.metadata) if session.metadata else None,
112
+ session.created_at, session.updated_at))
113
+ await self._conn.commit()
114
+
115
+ return session
116
+
117
+ def create_session(self, session: ConversationSession) -> ConversationSession:
118
+ """Sync wrapper for create_session."""
119
+ return asyncio.get_event_loop().run_until_complete(
120
+ self.async_create_session(session)
121
+ )
122
+
123
+ async def async_get_session(self, session_id: str) -> Optional[ConversationSession]:
124
+ """Get a session by ID asynchronously."""
125
+ if not self._initialized:
126
+ await self.init()
127
+
128
+ table = f"{self.table_prefix}sessions"
129
+ async with self._conn.execute(f"""
130
+ SELECT * FROM {table} WHERE session_id = ?
131
+ """, (session_id,)) as cursor:
132
+ row = await cursor.fetchone()
133
+
134
+ if row:
135
+ return ConversationSession(
136
+ session_id=row['session_id'],
137
+ user_id=row['user_id'],
138
+ agent_id=row['agent_id'],
139
+ name=row['name'],
140
+ metadata=json.loads(row['metadata']) if row['metadata'] else None,
141
+ created_at=row['created_at'],
142
+ updated_at=row['updated_at']
143
+ )
144
+ return None
145
+
146
+ def get_session(self, session_id: str) -> Optional[ConversationSession]:
147
+ """Sync wrapper for get_session."""
148
+ return asyncio.get_event_loop().run_until_complete(
149
+ self.async_get_session(session_id)
150
+ )
151
+
152
+ async def async_update_session(self, session: ConversationSession) -> ConversationSession:
153
+ """Update an existing session asynchronously."""
154
+ if not self._initialized:
155
+ await self.init()
156
+
157
+ session.updated_at = time.time()
158
+ table = f"{self.table_prefix}sessions"
159
+
160
+ await self._conn.execute(f"""
161
+ UPDATE {table}
162
+ SET name = ?, metadata = ?, updated_at = ?
163
+ WHERE session_id = ?
164
+ """, (session.name, json.dumps(session.metadata) if session.metadata else None,
165
+ session.updated_at, session.session_id))
166
+ await self._conn.commit()
167
+
168
+ return session
169
+
170
+ def update_session(self, session: ConversationSession) -> ConversationSession:
171
+ """Sync wrapper for update_session."""
172
+ return asyncio.get_event_loop().run_until_complete(
173
+ self.async_update_session(session)
174
+ )
175
+
176
+ async def async_delete_session(self, session_id: str) -> bool:
177
+ """Delete a session asynchronously."""
178
+ if not self._initialized:
179
+ await self.init()
180
+
181
+ table = f"{self.table_prefix}sessions"
182
+ cursor = await self._conn.execute(f"""
183
+ DELETE FROM {table} WHERE session_id = ?
184
+ """, (session_id,))
185
+ await self._conn.commit()
186
+
187
+ return cursor.rowcount > 0
188
+
189
+ def delete_session(self, session_id: str) -> bool:
190
+ """Sync wrapper for delete_session."""
191
+ return asyncio.get_event_loop().run_until_complete(
192
+ self.async_delete_session(session_id)
193
+ )
194
+
195
+ async def async_list_sessions(
196
+ self,
197
+ user_id: Optional[str] = None,
198
+ agent_id: Optional[str] = None,
199
+ limit: int = 100,
200
+ offset: int = 0
201
+ ) -> List[ConversationSession]:
202
+ """List sessions asynchronously."""
203
+ if not self._initialized:
204
+ await self.init()
205
+
206
+ table = f"{self.table_prefix}sessions"
207
+ conditions = []
208
+ params = []
209
+
210
+ if user_id:
211
+ conditions.append("user_id = ?")
212
+ params.append(user_id)
213
+
214
+ if agent_id:
215
+ conditions.append("agent_id = ?")
216
+ params.append(agent_id)
217
+
218
+ where_clause = f"WHERE {' AND '.join(conditions)}" if conditions else ""
219
+ params.extend([limit, offset])
220
+
221
+ async with self._conn.execute(f"""
222
+ SELECT * FROM {table} {where_clause}
223
+ ORDER BY updated_at DESC
224
+ LIMIT ? OFFSET ?
225
+ """, params) as cursor:
226
+ rows = await cursor.fetchall()
227
+
228
+ return [
229
+ ConversationSession(
230
+ session_id=row['session_id'],
231
+ user_id=row['user_id'],
232
+ agent_id=row['agent_id'],
233
+ name=row['name'],
234
+ metadata=json.loads(row['metadata']) if row['metadata'] else None,
235
+ created_at=row['created_at'],
236
+ updated_at=row['updated_at']
237
+ )
238
+ for row in rows
239
+ ]
240
+
241
+ def list_sessions(
242
+ self,
243
+ user_id: Optional[str] = None,
244
+ agent_id: Optional[str] = None,
245
+ limit: int = 100,
246
+ offset: int = 0
247
+ ) -> List[ConversationSession]:
248
+ """Sync wrapper for list_sessions."""
249
+ return asyncio.get_event_loop().run_until_complete(
250
+ self.async_list_sessions(user_id, agent_id, limit, offset)
251
+ )
252
+
253
+ async def async_add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
254
+ """Add a message asynchronously."""
255
+ if not self._initialized:
256
+ await self.init()
257
+
258
+ message.session_id = session_id
259
+ table = f"{self.table_prefix}messages"
260
+
261
+ await self._conn.execute(f"""
262
+ INSERT INTO {table} (id, session_id, role, content, metadata, created_at)
263
+ VALUES (?, ?, ?, ?, ?, ?)
264
+ """, (message.id, session_id, message.role, message.content,
265
+ json.dumps(message.metadata) if message.metadata else None,
266
+ message.created_at))
267
+ await self._conn.commit()
268
+
269
+ return message
270
+
271
+ def add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
272
+ """Sync wrapper for add_message."""
273
+ return asyncio.get_event_loop().run_until_complete(
274
+ self.async_add_message(session_id, message)
275
+ )
276
+
277
+ async def async_get_messages(
278
+ self,
279
+ session_id: str,
280
+ limit: Optional[int] = None,
281
+ before: Optional[float] = None,
282
+ after: Optional[float] = None
283
+ ) -> List[ConversationMessage]:
284
+ """Get messages asynchronously."""
285
+ if not self._initialized:
286
+ await self.init()
287
+
288
+ table = f"{self.table_prefix}messages"
289
+ conditions = ["session_id = ?"]
290
+ params = [session_id]
291
+
292
+ if before:
293
+ conditions.append("created_at < ?")
294
+ params.append(before)
295
+
296
+ if after:
297
+ conditions.append("created_at > ?")
298
+ params.append(after)
299
+
300
+ where_clause = f"WHERE {' AND '.join(conditions)}"
301
+ limit_clause = f"LIMIT ?" if limit else ""
302
+ if limit:
303
+ params.append(limit)
304
+
305
+ async with self._conn.execute(f"""
306
+ SELECT * FROM {table} {where_clause}
307
+ ORDER BY created_at ASC {limit_clause}
308
+ """, params) as cursor:
309
+ rows = await cursor.fetchall()
310
+
311
+ return [
312
+ ConversationMessage(
313
+ id=row['id'],
314
+ session_id=row['session_id'],
315
+ role=row['role'],
316
+ content=row['content'],
317
+ metadata=json.loads(row['metadata']) if row['metadata'] else None,
318
+ created_at=row['created_at']
319
+ )
320
+ for row in rows
321
+ ]
322
+
323
+ def get_messages(
324
+ self,
325
+ session_id: str,
326
+ limit: Optional[int] = None,
327
+ before: Optional[float] = None,
328
+ after: Optional[float] = None
329
+ ) -> List[ConversationMessage]:
330
+ """Sync wrapper for get_messages."""
331
+ return asyncio.get_event_loop().run_until_complete(
332
+ self.async_get_messages(session_id, limit, before, after)
333
+ )
334
+
335
+ async def async_delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
336
+ """Delete messages asynchronously."""
337
+ if not self._initialized:
338
+ await self.init()
339
+
340
+ table = f"{self.table_prefix}messages"
341
+
342
+ if message_ids is None:
343
+ cursor = await self._conn.execute(f"""
344
+ DELETE FROM {table} WHERE session_id = ?
345
+ """, (session_id,))
346
+ else:
347
+ placeholders = ','.join(['?' for _ in message_ids])
348
+ cursor = await self._conn.execute(f"""
349
+ DELETE FROM {table} WHERE session_id = ? AND id IN ({placeholders})
350
+ """, [session_id] + message_ids)
351
+
352
+ await self._conn.commit()
353
+ return cursor.rowcount
354
+
355
+ def delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
356
+ """Sync wrapper for delete_messages."""
357
+ return asyncio.get_event_loop().run_until_complete(
358
+ self.async_delete_messages(session_id, message_ids)
359
+ )
360
+
361
+ async def async_close(self) -> None:
362
+ """Close the connection."""
363
+ if self._conn:
364
+ await self._conn.close()
365
+ self._conn = None
366
+ self._initialized = False
367
+
368
+ def close(self) -> None:
369
+ """Sync wrapper for close."""
370
+ if self._conn:
371
+ asyncio.get_event_loop().run_until_complete(self.async_close())
@@ -0,0 +1,151 @@
1
+ """
2
+ Base interfaces for ConversationStore.
3
+
4
+ ConversationStore handles session and message persistence for conversation history.
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
+
13
+
14
+ @dataclass
15
+ class ConversationMessage:
16
+ """A single message in a conversation."""
17
+ id: str = field(default_factory=lambda: str(uuid.uuid4()))
18
+ session_id: str = ""
19
+ role: str = "" # user, assistant, system, tool
20
+ content: str = ""
21
+ tool_calls: Optional[List[Dict[str, Any]]] = None
22
+ tool_call_id: Optional[str] = None
23
+ metadata: Optional[Dict[str, Any]] = None
24
+ created_at: float = field(default_factory=time.time)
25
+
26
+ def to_dict(self) -> Dict[str, Any]:
27
+ return {
28
+ "id": self.id,
29
+ "session_id": self.session_id,
30
+ "role": self.role,
31
+ "content": self.content,
32
+ "tool_calls": self.tool_calls,
33
+ "tool_call_id": self.tool_call_id,
34
+ "metadata": self.metadata,
35
+ "created_at": self.created_at,
36
+ }
37
+
38
+ @classmethod
39
+ def from_dict(cls, data: Dict[str, Any]) -> "ConversationMessage":
40
+ return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})
41
+
42
+
43
+ @dataclass
44
+ class ConversationSession:
45
+ """A conversation session containing messages."""
46
+ session_id: str = field(default_factory=lambda: str(uuid.uuid4()))
47
+ user_id: Optional[str] = None
48
+ agent_id: Optional[str] = None
49
+ name: Optional[str] = None
50
+ state: Optional[Dict[str, Any]] = None
51
+ metadata: Optional[Dict[str, Any]] = None
52
+ created_at: float = field(default_factory=time.time)
53
+ updated_at: float = field(default_factory=time.time)
54
+
55
+ def to_dict(self) -> Dict[str, Any]:
56
+ return {
57
+ "session_id": self.session_id,
58
+ "user_id": self.user_id,
59
+ "agent_id": self.agent_id,
60
+ "name": self.name,
61
+ "state": self.state,
62
+ "metadata": self.metadata,
63
+ "created_at": self.created_at,
64
+ "updated_at": self.updated_at,
65
+ }
66
+
67
+ @classmethod
68
+ def from_dict(cls, data: Dict[str, Any]) -> "ConversationSession":
69
+ return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})
70
+
71
+
72
+ class ConversationStore(ABC):
73
+ """
74
+ Abstract base class for conversation persistence.
75
+
76
+ Implementations handle session and message storage for different backends:
77
+ - PostgreSQL, MySQL, SQLite (relational)
78
+ - SingleStore, Supabase, SurrealDB (hybrid)
79
+ """
80
+
81
+ @abstractmethod
82
+ def create_session(self, session: ConversationSession) -> ConversationSession:
83
+ """Create a new session."""
84
+ raise NotImplementedError
85
+
86
+ @abstractmethod
87
+ def get_session(self, session_id: str) -> Optional[ConversationSession]:
88
+ """Get a session by ID."""
89
+ raise NotImplementedError
90
+
91
+ @abstractmethod
92
+ def update_session(self, session: ConversationSession) -> ConversationSession:
93
+ """Update an existing session."""
94
+ raise NotImplementedError
95
+
96
+ @abstractmethod
97
+ def delete_session(self, session_id: str) -> bool:
98
+ """Delete a session and all its messages."""
99
+ raise NotImplementedError
100
+
101
+ @abstractmethod
102
+ def list_sessions(
103
+ self,
104
+ user_id: Optional[str] = None,
105
+ agent_id: Optional[str] = None,
106
+ limit: int = 100,
107
+ offset: int = 0
108
+ ) -> List[ConversationSession]:
109
+ """List sessions, optionally filtered by user or agent."""
110
+ raise NotImplementedError
111
+
112
+ @abstractmethod
113
+ def add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
114
+ """Add a message to a session."""
115
+ raise NotImplementedError
116
+
117
+ @abstractmethod
118
+ def get_messages(
119
+ self,
120
+ session_id: str,
121
+ limit: Optional[int] = None,
122
+ before: Optional[float] = None,
123
+ after: Optional[float] = None
124
+ ) -> List[ConversationMessage]:
125
+ """Get messages from a session."""
126
+ raise NotImplementedError
127
+
128
+ @abstractmethod
129
+ def delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
130
+ """Delete messages. If message_ids is None, delete all messages in session."""
131
+ raise NotImplementedError
132
+
133
+ def upsert_session(self, session: ConversationSession) -> ConversationSession:
134
+ """Create or update a session."""
135
+ existing = self.get_session(session.session_id)
136
+ if existing:
137
+ session.updated_at = time.time()
138
+ return self.update_session(session)
139
+ return self.create_session(session)
140
+
141
+ @abstractmethod
142
+ def close(self) -> None:
143
+ """Close the store and release resources."""
144
+ raise NotImplementedError
145
+
146
+ def __enter__(self):
147
+ return self
148
+
149
+ def __exit__(self, exc_type, exc_val, exc_tb):
150
+ self.close()
151
+ return False