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,107 @@
1
+ """
2
+ Base interfaces for StateStore.
3
+
4
+ StateStore handles fast key-value state and caching.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from typing import Any, Dict, List, Optional
9
+ import json
10
+
11
+
12
+ class StateStore(ABC):
13
+ """
14
+ Abstract base class for key-value state persistence.
15
+
16
+ Implementations handle fast state/cache storage for different backends:
17
+ - Redis (dedicated KV store)
18
+ - DynamoDB, Firestore (serverless document stores)
19
+ - MongoDB (document store)
20
+ - Upstash (serverless Redis)
21
+ - In-memory / JSON file (zero-dependency fallback)
22
+ """
23
+
24
+ @abstractmethod
25
+ def get(self, key: str) -> Optional[Any]:
26
+ """Get a value by key. Returns None if not found."""
27
+ raise NotImplementedError
28
+
29
+ @abstractmethod
30
+ def set(
31
+ self,
32
+ key: str,
33
+ value: Any,
34
+ ttl: Optional[int] = None
35
+ ) -> None:
36
+ """Set a value. TTL is in seconds."""
37
+ raise NotImplementedError
38
+
39
+ @abstractmethod
40
+ def delete(self, key: str) -> bool:
41
+ """Delete a key. Returns True if key existed."""
42
+ raise NotImplementedError
43
+
44
+ @abstractmethod
45
+ def exists(self, key: str) -> bool:
46
+ """Check if a key exists."""
47
+ raise NotImplementedError
48
+
49
+ @abstractmethod
50
+ def keys(self, pattern: str = "*") -> List[str]:
51
+ """List keys matching pattern."""
52
+ raise NotImplementedError
53
+
54
+ @abstractmethod
55
+ def ttl(self, key: str) -> Optional[int]:
56
+ """Get remaining TTL in seconds. Returns None if no TTL or key doesn't exist."""
57
+ raise NotImplementedError
58
+
59
+ @abstractmethod
60
+ def expire(self, key: str, ttl: int) -> bool:
61
+ """Set TTL on existing key. Returns True if key exists."""
62
+ raise NotImplementedError
63
+
64
+ def get_json(self, key: str) -> Optional[Any]:
65
+ """Get and deserialize JSON value."""
66
+ value = self.get(key)
67
+ if value is None:
68
+ return None
69
+ if isinstance(value, str):
70
+ return json.loads(value)
71
+ return value
72
+
73
+ def set_json(self, key: str, value: Any, ttl: Optional[int] = None) -> None:
74
+ """Serialize and set JSON value."""
75
+ self.set(key, json.dumps(value), ttl)
76
+
77
+ @abstractmethod
78
+ def hget(self, key: str, field: str) -> Optional[Any]:
79
+ """Get a field from a hash."""
80
+ raise NotImplementedError
81
+
82
+ @abstractmethod
83
+ def hset(self, key: str, field: str, value: Any) -> None:
84
+ """Set a field in a hash."""
85
+ raise NotImplementedError
86
+
87
+ @abstractmethod
88
+ def hgetall(self, key: str) -> Dict[str, Any]:
89
+ """Get all fields from a hash."""
90
+ raise NotImplementedError
91
+
92
+ @abstractmethod
93
+ def hdel(self, key: str, *fields: str) -> int:
94
+ """Delete fields from a hash. Returns count deleted."""
95
+ raise NotImplementedError
96
+
97
+ @abstractmethod
98
+ def close(self) -> None:
99
+ """Close the store and release resources."""
100
+ raise NotImplementedError
101
+
102
+ def __enter__(self):
103
+ return self
104
+
105
+ def __exit__(self, exc_type, exc_val, exc_tb):
106
+ self.close()
107
+ return False
@@ -0,0 +1,226 @@
1
+ """
2
+ DynamoDB implementation of StateStore.
3
+
4
+ Requires: boto3
5
+ Install: pip install boto3
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ import os
11
+ import time
12
+ from typing import Any, Dict, List, Optional
13
+
14
+ from .base import StateStore
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class DynamoDBStateStore(StateStore):
20
+ """
21
+ DynamoDB-based state store.
22
+
23
+ Example:
24
+ store = DynamoDBStateStore(
25
+ table_name="praisonai_state",
26
+ region="us-east-1"
27
+ )
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ table_name: str = "praisonai_state",
33
+ region: Optional[str] = None,
34
+ endpoint_url: Optional[str] = None,
35
+ auto_create_table: bool = True,
36
+ ):
37
+ try:
38
+ import boto3
39
+ except ImportError:
40
+ raise ImportError(
41
+ "boto3 is required for DynamoDB support. "
42
+ "Install with: pip install boto3"
43
+ )
44
+
45
+ region = region or os.getenv("AWS_REGION", "us-east-1")
46
+
47
+ self._dynamodb = boto3.resource(
48
+ "dynamodb",
49
+ region_name=region,
50
+ endpoint_url=endpoint_url,
51
+ )
52
+ self._client = boto3.client(
53
+ "dynamodb",
54
+ region_name=region,
55
+ endpoint_url=endpoint_url,
56
+ )
57
+ self.table_name = table_name
58
+
59
+ if auto_create_table:
60
+ self._create_table()
61
+
62
+ self._table = self._dynamodb.Table(table_name)
63
+ logger.info(f"Connected to DynamoDB table: {table_name}")
64
+
65
+ def _create_table(self) -> None:
66
+ """Create table if not exists."""
67
+ try:
68
+ self._client.describe_table(TableName=self.table_name)
69
+ except self._client.exceptions.ResourceNotFoundException:
70
+ self._client.create_table(
71
+ TableName=self.table_name,
72
+ KeySchema=[{"AttributeName": "pk", "KeyType": "HASH"}],
73
+ AttributeDefinitions=[{"AttributeName": "pk", "AttributeType": "S"}],
74
+ BillingMode="PAY_PER_REQUEST",
75
+ )
76
+ waiter = self._client.get_waiter("table_exists")
77
+ waiter.wait(TableName=self.table_name)
78
+
79
+ # Enable TTL
80
+ self._client.update_time_to_live(
81
+ TableName=self.table_name,
82
+ TimeToLiveSpecification={"Enabled": True, "AttributeName": "ttl"}
83
+ )
84
+ logger.info(f"Created DynamoDB table: {self.table_name}")
85
+
86
+ def get(self, key: str) -> Optional[Any]:
87
+ """Get a value by key."""
88
+ response = self._table.get_item(Key={"pk": key})
89
+ item = response.get("Item")
90
+
91
+ if not item:
92
+ return None
93
+
94
+ # Check TTL
95
+ if item.get("ttl") and item["ttl"] <= int(time.time()):
96
+ return None
97
+
98
+ value = item.get("value")
99
+ if isinstance(value, str):
100
+ try:
101
+ return json.loads(value)
102
+ except json.JSONDecodeError:
103
+ return value
104
+ return value
105
+
106
+ def set(
107
+ self,
108
+ key: str,
109
+ value: Any,
110
+ ttl: Optional[int] = None
111
+ ) -> None:
112
+ """Set a value with optional TTL."""
113
+ item = {
114
+ "pk": key,
115
+ "value": json.dumps(value) if not isinstance(value, str) else value,
116
+ "updated_at": int(time.time()),
117
+ }
118
+
119
+ if ttl:
120
+ item["ttl"] = int(time.time()) + ttl
121
+
122
+ self._table.put_item(Item=item)
123
+
124
+ def delete(self, key: str) -> bool:
125
+ """Delete a key."""
126
+ response = self._table.delete_item(
127
+ Key={"pk": key},
128
+ ReturnValues="ALL_OLD"
129
+ )
130
+ return "Attributes" in response
131
+
132
+ def exists(self, key: str) -> bool:
133
+ """Check if a key exists."""
134
+ response = self._table.get_item(
135
+ Key={"pk": key},
136
+ ProjectionExpression="pk,#t",
137
+ ExpressionAttributeNames={"#t": "ttl"}
138
+ )
139
+ item = response.get("Item")
140
+ if not item:
141
+ return False
142
+ if item.get("ttl") and item["ttl"] <= int(time.time()):
143
+ return False
144
+ return True
145
+
146
+ def keys(self, pattern: str = "*") -> List[str]:
147
+ """List keys matching pattern."""
148
+ # DynamoDB scan is expensive, use with caution
149
+ response = self._table.scan(ProjectionExpression="pk")
150
+ keys = [item["pk"] for item in response.get("Items", [])]
151
+
152
+ if pattern != "*":
153
+ import fnmatch
154
+ keys = [k for k in keys if fnmatch.fnmatch(k, pattern)]
155
+
156
+ return keys
157
+
158
+ def ttl(self, key: str) -> Optional[int]:
159
+ """Get remaining TTL in seconds."""
160
+ response = self._table.get_item(
161
+ Key={"pk": key},
162
+ ProjectionExpression="#t",
163
+ ExpressionAttributeNames={"#t": "ttl"}
164
+ )
165
+ item = response.get("Item")
166
+ if not item or "ttl" not in item:
167
+ return None
168
+
169
+ remaining = item["ttl"] - int(time.time())
170
+ if remaining <= 0:
171
+ return None
172
+ return remaining
173
+
174
+ def expire(self, key: str, ttl: int) -> bool:
175
+ """Set TTL on existing key."""
176
+ try:
177
+ self._table.update_item(
178
+ Key={"pk": key},
179
+ UpdateExpression="SET #t = :ttl",
180
+ ExpressionAttributeNames={"#t": "ttl"},
181
+ ExpressionAttributeValues={":ttl": int(time.time()) + ttl},
182
+ ConditionExpression="attribute_exists(pk)"
183
+ )
184
+ return True
185
+ except self._client.exceptions.ConditionalCheckFailedException:
186
+ return False
187
+
188
+ def hget(self, key: str, field: str) -> Optional[Any]:
189
+ """Get a field from a hash."""
190
+ value = self.get(key)
191
+ if not isinstance(value, dict):
192
+ return None
193
+ return value.get(field)
194
+
195
+ def hset(self, key: str, field: str, value: Any) -> None:
196
+ """Set a field in a hash."""
197
+ current = self.get(key)
198
+ if not isinstance(current, dict):
199
+ current = {}
200
+ current[field] = value
201
+ self.set(key, current)
202
+
203
+ def hgetall(self, key: str) -> Dict[str, Any]:
204
+ """Get all fields from a hash."""
205
+ value = self.get(key)
206
+ if not isinstance(value, dict):
207
+ return {}
208
+ return value
209
+
210
+ def hdel(self, key: str, *fields: str) -> int:
211
+ """Delete fields from a hash."""
212
+ current = self.get(key)
213
+ if not isinstance(current, dict):
214
+ return 0
215
+ count = 0
216
+ for field in fields:
217
+ if field in current:
218
+ del current[field]
219
+ count += 1
220
+ if count > 0:
221
+ self.set(key, current)
222
+ return count
223
+
224
+ def close(self) -> None:
225
+ """Close the store."""
226
+ pass # boto3 handles cleanup
@@ -0,0 +1,175 @@
1
+ """
2
+ Firestore implementation of StateStore.
3
+
4
+ Requires: google-cloud-firestore
5
+ Install: pip install google-cloud-firestore
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ import os
11
+ import time
12
+ from typing import Any, Dict, List, Optional
13
+
14
+ from .base import StateStore
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class FirestoreStateStore(StateStore):
20
+ """
21
+ Firestore-based state store.
22
+
23
+ Example:
24
+ store = FirestoreStateStore(
25
+ project="my-project",
26
+ collection="praisonai_state"
27
+ )
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ project: Optional[str] = None,
33
+ collection: str = "praisonai_state",
34
+ credentials_path: Optional[str] = None,
35
+ ):
36
+ try:
37
+ from google.cloud import firestore
38
+ except ImportError:
39
+ raise ImportError(
40
+ "google-cloud-firestore is required for Firestore support. "
41
+ "Install with: pip install google-cloud-firestore"
42
+ )
43
+
44
+ if credentials_path:
45
+ os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = credentials_path
46
+
47
+ project = project or os.getenv("FIRESTORE_PROJECT")
48
+ self._client = firestore.Client(project=project)
49
+ self._collection = self._client.collection(collection)
50
+
51
+ logger.info(f"Connected to Firestore collection: {collection}")
52
+
53
+ def get(self, key: str) -> Optional[Any]:
54
+ """Get a value by key."""
55
+ doc = self._collection.document(key).get()
56
+ if not doc.exists:
57
+ return None
58
+
59
+ data = doc.to_dict()
60
+
61
+ # Check TTL
62
+ if data.get("expires_at") and data["expires_at"] <= time.time():
63
+ self._collection.document(key).delete()
64
+ return None
65
+
66
+ return data.get("value")
67
+
68
+ def set(
69
+ self,
70
+ key: str,
71
+ value: Any,
72
+ ttl: Optional[int] = None
73
+ ) -> None:
74
+ """Set a value with optional TTL."""
75
+ data = {"value": value, "updated_at": time.time()}
76
+
77
+ if ttl:
78
+ data["expires_at"] = time.time() + ttl
79
+
80
+ self._collection.document(key).set(data)
81
+
82
+ def delete(self, key: str) -> bool:
83
+ """Delete a key."""
84
+ doc = self._collection.document(key)
85
+ if doc.get().exists:
86
+ doc.delete()
87
+ return True
88
+ return False
89
+
90
+ def exists(self, key: str) -> bool:
91
+ """Check if a key exists."""
92
+ doc = self._collection.document(key).get()
93
+ if not doc.exists:
94
+ return False
95
+
96
+ data = doc.to_dict()
97
+ if data.get("expires_at") and data["expires_at"] <= time.time():
98
+ return False
99
+
100
+ return True
101
+
102
+ def keys(self, pattern: str = "*") -> List[str]:
103
+ """List keys matching pattern."""
104
+ docs = self._collection.stream()
105
+ keys = [doc.id for doc in docs]
106
+
107
+ if pattern != "*":
108
+ import fnmatch
109
+ keys = [k for k in keys if fnmatch.fnmatch(k, pattern)]
110
+
111
+ return keys
112
+
113
+ def ttl(self, key: str) -> Optional[int]:
114
+ """Get remaining TTL in seconds."""
115
+ doc = self._collection.document(key).get()
116
+ if not doc.exists:
117
+ return None
118
+
119
+ data = doc.to_dict()
120
+ if "expires_at" not in data:
121
+ return None
122
+
123
+ remaining = data["expires_at"] - time.time()
124
+ if remaining <= 0:
125
+ return None
126
+ return int(remaining)
127
+
128
+ def expire(self, key: str, ttl: int) -> bool:
129
+ """Set TTL on existing key."""
130
+ doc = self._collection.document(key)
131
+ if not doc.get().exists:
132
+ return False
133
+
134
+ doc.update({"expires_at": time.time() + ttl})
135
+ return True
136
+
137
+ def hget(self, key: str, field: str) -> Optional[Any]:
138
+ """Get a field from a hash."""
139
+ value = self.get(key)
140
+ if not isinstance(value, dict):
141
+ return None
142
+ return value.get(field)
143
+
144
+ def hset(self, key: str, field: str, value: Any) -> None:
145
+ """Set a field in a hash."""
146
+ current = self.get(key)
147
+ if not isinstance(current, dict):
148
+ current = {}
149
+ current[field] = value
150
+ self.set(key, current)
151
+
152
+ def hgetall(self, key: str) -> Dict[str, Any]:
153
+ """Get all fields from a hash."""
154
+ value = self.get(key)
155
+ if not isinstance(value, dict):
156
+ return {}
157
+ return value
158
+
159
+ def hdel(self, key: str, *fields: str) -> int:
160
+ """Delete fields from a hash."""
161
+ current = self.get(key)
162
+ if not isinstance(current, dict):
163
+ return 0
164
+ count = 0
165
+ for field in fields:
166
+ if field in current:
167
+ del current[field]
168
+ count += 1
169
+ if count > 0:
170
+ self.set(key, current)
171
+ return count
172
+
173
+ def close(self) -> None:
174
+ """Close the store."""
175
+ pass # Firestore client handles cleanup
@@ -0,0 +1,155 @@
1
+ """
2
+ Google Cloud Storage implementation of StateStore.
3
+
4
+ Requires: google-cloud-storage
5
+ Install: pip install google-cloud-storage
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ import time
11
+ from typing import Any, Dict, List, Optional
12
+
13
+ from .base import StateStore
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class GCSStateStore(StateStore):
19
+ """
20
+ Google Cloud Storage-based state store.
21
+
22
+ Stores state as JSON objects in GCS buckets.
23
+
24
+ Example:
25
+ store = GCSStateStore(
26
+ bucket_name="my-praisonai-state",
27
+ prefix="state/"
28
+ )
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ bucket_name: str,
34
+ prefix: str = "praisonai_state/",
35
+ project: Optional[str] = None,
36
+ credentials_path: Optional[str] = None,
37
+ ):
38
+ """
39
+ Initialize GCS state store.
40
+
41
+ Args:
42
+ bucket_name: GCS bucket name
43
+ prefix: Object prefix for state keys
44
+ project: GCP project ID (optional)
45
+ credentials_path: Path to service account JSON (optional)
46
+ """
47
+ try:
48
+ from google.cloud import storage
49
+ if credentials_path:
50
+ self._client = storage.Client.from_service_account_json(
51
+ credentials_path, project=project
52
+ )
53
+ else:
54
+ self._client = storage.Client(project=project)
55
+ except ImportError:
56
+ raise ImportError(
57
+ "google-cloud-storage is required for GCS support. "
58
+ "Install with: pip install google-cloud-storage"
59
+ )
60
+
61
+ self.bucket_name = bucket_name
62
+ self.prefix = prefix
63
+ self._bucket = self._client.bucket(bucket_name)
64
+
65
+ def _key_to_path(self, key: str) -> str:
66
+ """Convert key to GCS object path."""
67
+ return f"{self.prefix}{key}.json"
68
+
69
+ def get(self, key: str) -> Optional[Dict[str, Any]]:
70
+ """Get state by key."""
71
+ try:
72
+ blob = self._bucket.blob(self._key_to_path(key))
73
+ if blob.exists():
74
+ data = json.loads(blob.download_as_text())
75
+ # Check TTL
76
+ if "_ttl_expires" in data:
77
+ if time.time() > data["_ttl_expires"]:
78
+ self.delete(key)
79
+ return None
80
+ del data["_ttl_expires"]
81
+ return data
82
+ return None
83
+ except Exception as e:
84
+ logger.error(f"Error getting state {key}: {e}")
85
+ return None
86
+
87
+ def set(self, key: str, value: Dict[str, Any], ttl: Optional[int] = None) -> bool:
88
+ """Set state by key with optional TTL in seconds."""
89
+ try:
90
+ data = value.copy()
91
+ if ttl:
92
+ data["_ttl_expires"] = time.time() + ttl
93
+
94
+ blob = self._bucket.blob(self._key_to_path(key))
95
+ blob.upload_from_string(
96
+ json.dumps(data, default=str),
97
+ content_type="application/json"
98
+ )
99
+ return True
100
+ except Exception as e:
101
+ logger.error(f"Error setting state {key}: {e}")
102
+ return False
103
+
104
+ def delete(self, key: str) -> bool:
105
+ """Delete state by key."""
106
+ try:
107
+ blob = self._bucket.blob(self._key_to_path(key))
108
+ if blob.exists():
109
+ blob.delete()
110
+ return True
111
+ return False
112
+ except Exception as e:
113
+ logger.error(f"Error deleting state {key}: {e}")
114
+ return False
115
+
116
+ def exists(self, key: str) -> bool:
117
+ """Check if key exists."""
118
+ try:
119
+ blob = self._bucket.blob(self._key_to_path(key))
120
+ return blob.exists()
121
+ except Exception:
122
+ return False
123
+
124
+ def list_keys(self, prefix: Optional[str] = None) -> List[str]:
125
+ """List all keys with optional prefix filter."""
126
+ try:
127
+ search_prefix = self.prefix
128
+ if prefix:
129
+ search_prefix = f"{self.prefix}{prefix}"
130
+
131
+ blobs = self._client.list_blobs(self.bucket_name, prefix=search_prefix)
132
+ keys = []
133
+ for blob in blobs:
134
+ # Remove prefix and .json suffix
135
+ key = blob.name[len(self.prefix):]
136
+ if key.endswith(".json"):
137
+ key = key[:-5]
138
+ keys.append(key)
139
+ return keys
140
+ except Exception as e:
141
+ logger.error(f"Error listing keys: {e}")
142
+ return []
143
+
144
+ def clear(self, prefix: Optional[str] = None) -> int:
145
+ """Clear all keys with optional prefix filter."""
146
+ keys = self.list_keys(prefix)
147
+ count = 0
148
+ for key in keys:
149
+ if self.delete(key):
150
+ count += 1
151
+ return count
152
+
153
+ def close(self) -> None:
154
+ """Close the store."""
155
+ self._client.close()