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,250 @@
1
+ """
2
+ JSON file-based implementation of ConversationStore.
3
+
4
+ Simple file-based storage for development and testing.
5
+ No external dependencies required.
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ import time
11
+ from pathlib import Path
12
+ from typing import Dict, List, Optional
13
+ import threading
14
+
15
+ from .base import ConversationStore, ConversationSession, ConversationMessage
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class JSONConversationStore(ConversationStore):
21
+ """
22
+ JSON file-based conversation store.
23
+
24
+ Stores sessions and messages in JSON files.
25
+ Suitable for development, testing, and small-scale deployments.
26
+
27
+ Example:
28
+ store = JSONConversationStore(path="./data/conversations")
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ path: str = "./praisonai_conversations",
34
+ pretty: bool = True,
35
+ ):
36
+ """
37
+ Initialize JSON conversation store.
38
+
39
+ Args:
40
+ path: Directory path for JSON files
41
+ pretty: Use pretty-printed JSON (default: True)
42
+ """
43
+ self.path = Path(path)
44
+ self.pretty = pretty
45
+ self._lock = threading.Lock()
46
+
47
+ # Create directory if it doesn't exist
48
+ self.path.mkdir(parents=True, exist_ok=True)
49
+
50
+ # Sessions index file
51
+ self._index_file = self.path / "_sessions_index.json"
52
+ self._load_index()
53
+
54
+ def _load_index(self):
55
+ """Load or create sessions index."""
56
+ if self._index_file.exists():
57
+ with open(self._index_file, 'r') as f:
58
+ self._index = json.load(f)
59
+ else:
60
+ self._index = {"sessions": {}}
61
+ self._save_index()
62
+
63
+ def _save_index(self):
64
+ """Save sessions index."""
65
+ with open(self._index_file, 'w') as f:
66
+ if self.pretty:
67
+ json.dump(self._index, f, indent=2, default=str)
68
+ else:
69
+ json.dump(self._index, f, default=str)
70
+
71
+ def _session_file(self, session_id: str) -> Path:
72
+ """Get path to session file."""
73
+ return self.path / f"{session_id}.json"
74
+
75
+ def _load_session_data(self, session_id: str) -> Optional[Dict]:
76
+ """Load session data from file."""
77
+ file_path = self._session_file(session_id)
78
+ if file_path.exists():
79
+ with open(file_path, 'r') as f:
80
+ return json.load(f)
81
+ return None
82
+
83
+ def _save_session_data(self, session_id: str, data: Dict):
84
+ """Save session data to file."""
85
+ file_path = self._session_file(session_id)
86
+ with open(file_path, 'w') as f:
87
+ if self.pretty:
88
+ json.dump(data, f, indent=2, default=str)
89
+ else:
90
+ json.dump(data, f, default=str)
91
+
92
+ def create_session(self, session: ConversationSession) -> ConversationSession:
93
+ """Create a new session."""
94
+ with self._lock:
95
+ data = {
96
+ "session": session.to_dict(),
97
+ "messages": []
98
+ }
99
+ self._save_session_data(session.session_id, data)
100
+
101
+ # Update index
102
+ self._index["sessions"][session.session_id] = {
103
+ "user_id": session.user_id,
104
+ "agent_id": session.agent_id,
105
+ "name": session.name,
106
+ "created_at": session.created_at,
107
+ "updated_at": session.updated_at,
108
+ }
109
+ self._save_index()
110
+
111
+ return session
112
+
113
+ def get_session(self, session_id: str) -> Optional[ConversationSession]:
114
+ """Get a session by ID."""
115
+ data = self._load_session_data(session_id)
116
+ if data and "session" in data:
117
+ return ConversationSession.from_dict(data["session"])
118
+ return None
119
+
120
+ def update_session(self, session: ConversationSession) -> ConversationSession:
121
+ """Update an existing session."""
122
+ with self._lock:
123
+ data = self._load_session_data(session.session_id)
124
+ if not data:
125
+ raise ValueError(f"Session {session.session_id} not found")
126
+
127
+ session.updated_at = time.time()
128
+ data["session"] = session.to_dict()
129
+ self._save_session_data(session.session_id, data)
130
+
131
+ # Update index
132
+ self._index["sessions"][session.session_id].update({
133
+ "name": session.name,
134
+ "updated_at": session.updated_at,
135
+ })
136
+ self._save_index()
137
+
138
+ return session
139
+
140
+ def delete_session(self, session_id: str) -> bool:
141
+ """Delete a session and all its messages."""
142
+ with self._lock:
143
+ file_path = self._session_file(session_id)
144
+ if file_path.exists():
145
+ file_path.unlink()
146
+
147
+ # Update index
148
+ if session_id in self._index["sessions"]:
149
+ del self._index["sessions"][session_id]
150
+ self._save_index()
151
+
152
+ return True
153
+ return False
154
+
155
+ def list_sessions(
156
+ self,
157
+ user_id: Optional[str] = None,
158
+ agent_id: Optional[str] = None,
159
+ limit: int = 100,
160
+ offset: int = 0
161
+ ) -> List[ConversationSession]:
162
+ """List sessions, optionally filtered by user or agent."""
163
+ sessions = []
164
+
165
+ for sid, info in self._index["sessions"].items():
166
+ if user_id and info.get("user_id") != user_id:
167
+ continue
168
+ if agent_id and info.get("agent_id") != agent_id:
169
+ continue
170
+
171
+ session = self.get_session(sid)
172
+ if session:
173
+ sessions.append(session)
174
+
175
+ # Sort by updated_at descending
176
+ sessions.sort(key=lambda s: s.updated_at, reverse=True)
177
+
178
+ return sessions[offset:offset + limit]
179
+
180
+ def add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
181
+ """Add a message to a session."""
182
+ with self._lock:
183
+ data = self._load_session_data(session_id)
184
+ if not data:
185
+ raise ValueError(f"Session {session_id} not found")
186
+
187
+ message.session_id = session_id
188
+ data["messages"].append(message.to_dict())
189
+
190
+ # Update session timestamp
191
+ data["session"]["updated_at"] = time.time()
192
+
193
+ self._save_session_data(session_id, data)
194
+
195
+ # Update index
196
+ if session_id in self._index["sessions"]:
197
+ self._index["sessions"][session_id]["updated_at"] = time.time()
198
+ self._save_index()
199
+
200
+ return message
201
+
202
+ def get_messages(
203
+ self,
204
+ session_id: str,
205
+ limit: Optional[int] = None,
206
+ before: Optional[float] = None,
207
+ after: Optional[float] = None
208
+ ) -> List[ConversationMessage]:
209
+ """Get messages from a session."""
210
+ data = self._load_session_data(session_id)
211
+ if not data:
212
+ return []
213
+
214
+ messages = [ConversationMessage.from_dict(m) for m in data.get("messages", [])]
215
+
216
+ # Apply filters
217
+ if before:
218
+ messages = [m for m in messages if m.created_at < before]
219
+ if after:
220
+ messages = [m for m in messages if m.created_at > after]
221
+
222
+ # Sort by created_at
223
+ messages.sort(key=lambda m: m.created_at)
224
+
225
+ if limit:
226
+ messages = messages[-limit:]
227
+
228
+ return messages
229
+
230
+ def delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
231
+ """Delete messages. If message_ids is None, delete all messages in session."""
232
+ with self._lock:
233
+ data = self._load_session_data(session_id)
234
+ if not data:
235
+ return 0
236
+
237
+ if message_ids is None:
238
+ count = len(data["messages"])
239
+ data["messages"] = []
240
+ else:
241
+ original_count = len(data["messages"])
242
+ data["messages"] = [m for m in data["messages"] if m.get("id") not in message_ids]
243
+ count = original_count - len(data["messages"])
244
+
245
+ self._save_session_data(session_id, data)
246
+ return count
247
+
248
+ def close(self) -> None:
249
+ """Close the store."""
250
+ pass # No resources to release
@@ -0,0 +1,387 @@
1
+ """
2
+ MySQL implementation of ConversationStore.
3
+
4
+ Requires: mysql-connector-python or pymysql
5
+ Install: pip install mysql-connector-python
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ from typing import Any, List, Optional
11
+
12
+ from .base import ConversationStore, ConversationSession, ConversationMessage
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class MySQLConversationStore(ConversationStore):
18
+ """
19
+ MySQL-based conversation store.
20
+
21
+ Example:
22
+ store = MySQLConversationStore(
23
+ url="mysql://user:password@localhost:3306/praisonai"
24
+ )
25
+ """
26
+
27
+ SCHEMA_VERSION = "1.0.0"
28
+
29
+ def __init__(
30
+ self,
31
+ url: Optional[str] = None,
32
+ host: str = "localhost",
33
+ port: int = 3306,
34
+ database: str = "praisonai",
35
+ user: str = "root",
36
+ password: str = "",
37
+ table_prefix: str = "praison_",
38
+ auto_create_tables: bool = True,
39
+ pool_size: int = 5,
40
+ ):
41
+ """
42
+ Initialize MySQL conversation store.
43
+
44
+ Args:
45
+ url: Full connection URL (overrides individual params)
46
+ host: Database host
47
+ port: Database port
48
+ database: Database name
49
+ user: Database user
50
+ password: Database password
51
+ table_prefix: Prefix for table names
52
+ auto_create_tables: Create tables if they don't exist
53
+ pool_size: Connection pool size
54
+ """
55
+ try:
56
+ import mysql.connector
57
+ from mysql.connector import pooling
58
+ except ImportError:
59
+ raise ImportError(
60
+ "mysql-connector-python is required for MySQL support. "
61
+ "Install with: pip install mysql-connector-python"
62
+ )
63
+
64
+ self._mysql = mysql.connector
65
+ self.table_prefix = table_prefix
66
+ self.sessions_table = f"{table_prefix}sessions"
67
+ self.messages_table = f"{table_prefix}messages"
68
+
69
+ # Parse URL if provided
70
+ if url:
71
+ # Parse mysql://user:pass@host:port/database
72
+ from urllib.parse import urlparse
73
+ parsed = urlparse(url)
74
+ host = parsed.hostname or host
75
+ port = parsed.port or port
76
+ database = parsed.path.lstrip("/") or database
77
+ user = parsed.username or user
78
+ password = parsed.password or password
79
+
80
+ # Create connection pool
81
+ self._pool = pooling.MySQLConnectionPool(
82
+ pool_name="praison_mysql_pool",
83
+ pool_size=pool_size,
84
+ host=host,
85
+ port=port,
86
+ database=database,
87
+ user=user,
88
+ password=password,
89
+ )
90
+
91
+ if auto_create_tables:
92
+ self._create_tables()
93
+
94
+ def _get_conn(self):
95
+ """Get a connection from the pool."""
96
+ return self._pool.get_connection()
97
+
98
+ def _create_tables(self) -> None:
99
+ """Create tables if they don't exist."""
100
+ conn = self._get_conn()
101
+ try:
102
+ cur = conn.cursor()
103
+
104
+ # Sessions table
105
+ cur.execute(f"""
106
+ CREATE TABLE IF NOT EXISTS {self.sessions_table} (
107
+ session_id VARCHAR(255) PRIMARY KEY,
108
+ user_id VARCHAR(255),
109
+ agent_id VARCHAR(255),
110
+ name VARCHAR(255),
111
+ state JSON,
112
+ metadata JSON,
113
+ created_at DOUBLE,
114
+ updated_at DOUBLE,
115
+ INDEX idx_user (user_id),
116
+ INDEX idx_agent (agent_id),
117
+ INDEX idx_updated (updated_at DESC)
118
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
119
+ """)
120
+
121
+ # Messages table
122
+ cur.execute(f"""
123
+ CREATE TABLE IF NOT EXISTS {self.messages_table} (
124
+ id VARCHAR(255) PRIMARY KEY,
125
+ session_id VARCHAR(255) NOT NULL,
126
+ role VARCHAR(50) NOT NULL,
127
+ content TEXT,
128
+ tool_calls JSON,
129
+ tool_call_id VARCHAR(255),
130
+ metadata JSON,
131
+ created_at DOUBLE,
132
+ INDEX idx_session (session_id),
133
+ INDEX idx_created (session_id, created_at),
134
+ FOREIGN KEY (session_id) REFERENCES {self.sessions_table}(session_id) ON DELETE CASCADE
135
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
136
+ """)
137
+
138
+ conn.commit()
139
+ logger.info(f"MySQL tables created: {self.sessions_table}, {self.messages_table}")
140
+ finally:
141
+ cur.close()
142
+ conn.close()
143
+
144
+ def create_session(self, session: ConversationSession) -> ConversationSession:
145
+ """Create a new session."""
146
+ conn = self._get_conn()
147
+ try:
148
+ cur = conn.cursor()
149
+ cur.execute(f"""
150
+ INSERT INTO {self.sessions_table}
151
+ (session_id, user_id, agent_id, name, state, metadata, created_at, updated_at)
152
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
153
+ """, (
154
+ session.session_id,
155
+ session.user_id,
156
+ session.agent_id,
157
+ session.name,
158
+ json.dumps(session.state) if session.state else None,
159
+ json.dumps(session.metadata) if session.metadata else None,
160
+ session.created_at,
161
+ session.updated_at,
162
+ ))
163
+ conn.commit()
164
+ return session
165
+ finally:
166
+ cur.close()
167
+ conn.close()
168
+
169
+ def get_session(self, session_id: str) -> Optional[ConversationSession]:
170
+ """Get a session by ID."""
171
+ conn = self._get_conn()
172
+ try:
173
+ cur = conn.cursor(dictionary=True)
174
+ cur.execute(f"""
175
+ SELECT * FROM {self.sessions_table} WHERE session_id = %s
176
+ """, (session_id,))
177
+ row = cur.fetchone()
178
+ if not row:
179
+ return None
180
+ return ConversationSession(
181
+ session_id=row["session_id"],
182
+ user_id=row["user_id"],
183
+ agent_id=row["agent_id"],
184
+ name=row["name"],
185
+ state=row["state"] if isinstance(row["state"], dict) else (json.loads(row["state"]) if row["state"] else None),
186
+ metadata=row["metadata"] if isinstance(row["metadata"], dict) else (json.loads(row["metadata"]) if row["metadata"] else None),
187
+ created_at=row["created_at"],
188
+ updated_at=row["updated_at"],
189
+ )
190
+ finally:
191
+ cur.close()
192
+ conn.close()
193
+
194
+ def update_session(self, session: ConversationSession) -> ConversationSession:
195
+ """Update an existing session."""
196
+ conn = self._get_conn()
197
+ try:
198
+ cur = conn.cursor()
199
+ cur.execute(f"""
200
+ UPDATE {self.sessions_table}
201
+ SET user_id = %s, agent_id = %s, name = %s,
202
+ state = %s, metadata = %s, updated_at = %s
203
+ WHERE session_id = %s
204
+ """, (
205
+ session.user_id,
206
+ session.agent_id,
207
+ session.name,
208
+ json.dumps(session.state) if session.state else None,
209
+ json.dumps(session.metadata) if session.metadata else None,
210
+ session.updated_at,
211
+ session.session_id,
212
+ ))
213
+ conn.commit()
214
+ return session
215
+ finally:
216
+ cur.close()
217
+ conn.close()
218
+
219
+ def delete_session(self, session_id: str) -> bool:
220
+ """Delete a session and all its messages."""
221
+ conn = self._get_conn()
222
+ try:
223
+ cur = conn.cursor()
224
+ cur.execute(f"""
225
+ DELETE FROM {self.sessions_table} WHERE session_id = %s
226
+ """, (session_id,))
227
+ deleted = cur.rowcount > 0
228
+ conn.commit()
229
+ return deleted
230
+ finally:
231
+ cur.close()
232
+ conn.close()
233
+
234
+ def list_sessions(
235
+ self,
236
+ user_id: Optional[str] = None,
237
+ agent_id: Optional[str] = None,
238
+ limit: int = 100,
239
+ offset: int = 0
240
+ ) -> List[ConversationSession]:
241
+ """List sessions."""
242
+ conn = self._get_conn()
243
+ try:
244
+ cur = conn.cursor(dictionary=True)
245
+
246
+ conditions = []
247
+ params: List[Any] = []
248
+
249
+ if user_id:
250
+ conditions.append("user_id = %s")
251
+ params.append(user_id)
252
+ if agent_id:
253
+ conditions.append("agent_id = %s")
254
+ params.append(agent_id)
255
+
256
+ where_clause = ""
257
+ if conditions:
258
+ where_clause = "WHERE " + " AND ".join(conditions)
259
+
260
+ params.extend([limit, offset])
261
+
262
+ cur.execute(f"""
263
+ SELECT * FROM {self.sessions_table}
264
+ {where_clause}
265
+ ORDER BY updated_at DESC
266
+ LIMIT %s OFFSET %s
267
+ """, params)
268
+
269
+ return [
270
+ ConversationSession(
271
+ session_id=row["session_id"],
272
+ user_id=row["user_id"],
273
+ agent_id=row["agent_id"],
274
+ name=row["name"],
275
+ state=row["state"] if isinstance(row["state"], dict) else (json.loads(row["state"]) if row["state"] else None),
276
+ metadata=row["metadata"] if isinstance(row["metadata"], dict) else (json.loads(row["metadata"]) if row["metadata"] else None),
277
+ created_at=row["created_at"],
278
+ updated_at=row["updated_at"],
279
+ )
280
+ for row in cur.fetchall()
281
+ ]
282
+ finally:
283
+ cur.close()
284
+ conn.close()
285
+
286
+ def add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
287
+ """Add a message to a session."""
288
+ message.session_id = session_id
289
+ conn = self._get_conn()
290
+ try:
291
+ cur = conn.cursor()
292
+ cur.execute(f"""
293
+ INSERT INTO {self.messages_table}
294
+ (id, session_id, role, content, tool_calls, tool_call_id, metadata, created_at)
295
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
296
+ """, (
297
+ message.id,
298
+ session_id,
299
+ message.role,
300
+ message.content,
301
+ json.dumps(message.tool_calls) if message.tool_calls else None,
302
+ message.tool_call_id,
303
+ json.dumps(message.metadata) if message.metadata else None,
304
+ message.created_at,
305
+ ))
306
+ conn.commit()
307
+ return message
308
+ finally:
309
+ cur.close()
310
+ conn.close()
311
+
312
+ def get_messages(
313
+ self,
314
+ session_id: str,
315
+ limit: Optional[int] = None,
316
+ before: Optional[float] = None,
317
+ after: Optional[float] = None
318
+ ) -> List[ConversationMessage]:
319
+ """Get messages from a session."""
320
+ conn = self._get_conn()
321
+ try:
322
+ cur = conn.cursor(dictionary=True)
323
+
324
+ conditions = ["session_id = %s"]
325
+ params: List[Any] = [session_id]
326
+
327
+ if before:
328
+ conditions.append("created_at < %s")
329
+ params.append(before)
330
+ if after:
331
+ conditions.append("created_at > %s")
332
+ params.append(after)
333
+
334
+ where_clause = "WHERE " + " AND ".join(conditions)
335
+ limit_clause = f"LIMIT {limit}" if limit else ""
336
+
337
+ cur.execute(f"""
338
+ SELECT * FROM {self.messages_table}
339
+ {where_clause}
340
+ ORDER BY created_at ASC
341
+ {limit_clause}
342
+ """, params)
343
+
344
+ return [
345
+ ConversationMessage(
346
+ id=row["id"],
347
+ session_id=row["session_id"],
348
+ role=row["role"],
349
+ content=row["content"],
350
+ tool_calls=row["tool_calls"] if isinstance(row["tool_calls"], list) else (json.loads(row["tool_calls"]) if row["tool_calls"] else None),
351
+ tool_call_id=row["tool_call_id"],
352
+ metadata=row["metadata"] if isinstance(row["metadata"], dict) else (json.loads(row["metadata"]) if row["metadata"] else None),
353
+ created_at=row["created_at"],
354
+ )
355
+ for row in cur.fetchall()
356
+ ]
357
+ finally:
358
+ cur.close()
359
+ conn.close()
360
+
361
+ def delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
362
+ """Delete messages."""
363
+ conn = self._get_conn()
364
+ try:
365
+ cur = conn.cursor()
366
+
367
+ if message_ids:
368
+ placeholders = ",".join(["%s"] * len(message_ids))
369
+ cur.execute(f"""
370
+ DELETE FROM {self.messages_table}
371
+ WHERE session_id = %s AND id IN ({placeholders})
372
+ """, [session_id] + message_ids)
373
+ else:
374
+ cur.execute(f"""
375
+ DELETE FROM {self.messages_table} WHERE session_id = %s
376
+ """, (session_id,))
377
+
378
+ deleted = cur.rowcount
379
+ conn.commit()
380
+ return deleted
381
+ finally:
382
+ cur.close()
383
+ conn.close()
384
+
385
+ def close(self) -> None:
386
+ """Close the store."""
387
+ pass # Pool handles cleanup