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,471 @@
1
+ """
2
+ @ Mention Autocomplete for PraisonAI CLI.
3
+
4
+ Provides file/directory autocomplete when user types @.
5
+ Uses only stdlib imports for zero performance impact.
6
+
7
+ Features:
8
+ - Detect @ trigger in input
9
+ - Fuzzy search files/directories
10
+ - Cache results for performance
11
+ - Integrate with prompt_toolkit
12
+ """
13
+
14
+ import os
15
+ import time
16
+ import fnmatch
17
+ from dataclasses import dataclass
18
+ from typing import List, Optional, Dict, Tuple
19
+
20
+
21
+ # ============================================================================
22
+ # Data Classes
23
+ # ============================================================================
24
+
25
+ @dataclass
26
+ class AtMentionContext:
27
+ """Context for an @ mention in user input."""
28
+ is_active: bool
29
+ query: str
30
+ start_pos: int
31
+ mention_type: str = "file" # file, directory, web, etc.
32
+
33
+
34
+ @dataclass
35
+ class FileSuggestion:
36
+ """A file or directory suggestion."""
37
+ path: str
38
+ file_type: str # "file" or "directory"
39
+ score: int = 100
40
+ display: str = ""
41
+
42
+ def __post_init__(self):
43
+ if not self.display:
44
+ self.display = self.path
45
+
46
+
47
+ # ============================================================================
48
+ # @ Mention Detection
49
+ # ============================================================================
50
+
51
+ def detect_at_mention(text: str, cursor_pos: int) -> Optional[AtMentionContext]:
52
+ """
53
+ Detect if cursor is within an @ mention context.
54
+
55
+ Scans backward from cursor to find @ symbol.
56
+ Returns None if not in @ context.
57
+
58
+ Args:
59
+ text: Full input text
60
+ cursor_pos: Current cursor position
61
+
62
+ Returns:
63
+ AtMentionContext if in @ mention, None otherwise
64
+ """
65
+ if cursor_pos <= 0 or cursor_pos > len(text):
66
+ return None
67
+
68
+ # Scan backward from cursor to find @
69
+ at_pos = -1
70
+ for i in range(cursor_pos - 1, -1, -1):
71
+ char = text[i]
72
+
73
+ # Found @
74
+ if char == '@':
75
+ at_pos = i
76
+ break
77
+
78
+ # Space/newline breaks @ context
79
+ if char in ' \t\n\r':
80
+ return None
81
+
82
+ if at_pos == -1:
83
+ return None
84
+
85
+ # Extract query between @ and cursor
86
+ query = text[at_pos + 1:cursor_pos]
87
+
88
+ return AtMentionContext(
89
+ is_active=True,
90
+ query=query,
91
+ start_pos=at_pos
92
+ )
93
+
94
+
95
+ # ============================================================================
96
+ # File Search Service
97
+ # ============================================================================
98
+
99
+ class FileSearchService:
100
+ """
101
+ Service for searching files in a directory.
102
+
103
+ Features:
104
+ - Walk directory tree
105
+ - Fuzzy match file paths
106
+ - Cache results with TTL
107
+ - Respect .gitignore (basic)
108
+ - Support absolute paths (@/Users/...) and home paths (@~/...)
109
+ """
110
+
111
+ def __init__(
112
+ self,
113
+ root_dir: str,
114
+ cache_ttl: int = 30,
115
+ max_depth: int = 5,
116
+ ignore_patterns: Optional[List[str]] = None
117
+ ):
118
+ self._root = os.path.abspath(root_dir)
119
+ self._cache_ttl = cache_ttl
120
+ self._max_depth = max_depth
121
+ self._ignore_patterns = ignore_patterns or [
122
+ '.git', '__pycache__', 'node_modules', '.venv', 'venv',
123
+ '*.pyc', '*.pyo', '.DS_Store', '*.egg-info'
124
+ ]
125
+
126
+ # Cache: query -> (results, timestamp)
127
+ self._cache: Dict[str, Tuple[List[FileSuggestion], float]] = {}
128
+
129
+ # File list cache per directory
130
+ self._file_list: Optional[List[str]] = None
131
+ self._file_list_time: float = 0
132
+ self._file_list_dir: Optional[str] = None
133
+
134
+ def _should_ignore(self, name: str) -> bool:
135
+ """Check if file/dir should be ignored."""
136
+ for pattern in self._ignore_patterns:
137
+ if fnmatch.fnmatch(name, pattern):
138
+ return True
139
+ return False
140
+
141
+ def _get_file_list(self, search_dir: str = None) -> List[str]:
142
+ """Get list of all files (cached)."""
143
+ now = time.time()
144
+ target_dir = search_dir or self._root
145
+
146
+ # Return cached if fresh and same directory
147
+ if (self._file_list is not None and
148
+ self._file_list_dir == target_dir and
149
+ (now - self._file_list_time) < self._cache_ttl):
150
+ return self._file_list
151
+
152
+ files = []
153
+
154
+ try:
155
+ for root, dirs, filenames in os.walk(target_dir):
156
+ # Calculate depth
157
+ rel_root = os.path.relpath(root, target_dir)
158
+ depth = 0 if rel_root == '.' else rel_root.count(os.sep) + 1
159
+
160
+ if depth > self._max_depth:
161
+ dirs.clear() # Don't descend further
162
+ continue
163
+
164
+ # Filter ignored directories
165
+ dirs[:] = [d for d in dirs if not self._should_ignore(d)]
166
+
167
+ # Add directories
168
+ for d in dirs:
169
+ rel_path = os.path.relpath(os.path.join(root, d), target_dir)
170
+ files.append(rel_path + os.sep)
171
+
172
+ # Add files
173
+ for f in filenames:
174
+ if not self._should_ignore(f):
175
+ rel_path = os.path.relpath(os.path.join(root, f), target_dir)
176
+ files.append(rel_path)
177
+ except OSError:
178
+ pass
179
+
180
+ self._file_list = files
181
+ self._file_list_time = now
182
+ self._file_list_dir = target_dir
183
+ return files
184
+
185
+ def _fuzzy_match(self, text: str, query: str) -> int:
186
+ """
187
+ Simple fuzzy match scoring.
188
+
189
+ Returns score 0-100, higher is better.
190
+ """
191
+ if not query:
192
+ return 50 # Default score for empty query
193
+
194
+ text_lower = text.lower()
195
+ query_lower = query.lower()
196
+
197
+ # Exact match
198
+ if text_lower == query_lower:
199
+ return 100
200
+
201
+ # Starts with
202
+ if text_lower.startswith(query_lower):
203
+ return 90
204
+
205
+ # Contains
206
+ if query_lower in text_lower:
207
+ return 80
208
+
209
+ # Fuzzy: all query chars appear in order
210
+ text_idx = 0
211
+ query_idx = 0
212
+ matches = 0
213
+
214
+ while text_idx < len(text_lower) and query_idx < len(query_lower):
215
+ if text_lower[text_idx] == query_lower[query_idx]:
216
+ matches += 1
217
+ query_idx += 1
218
+ text_idx += 1
219
+
220
+ if query_idx == len(query_lower):
221
+ # All query chars found
222
+ return 50 + int(30 * matches / len(text))
223
+
224
+ return 0
225
+
226
+ def search(self, query: str, max_results: int = 20) -> List[FileSuggestion]:
227
+ """
228
+ Search for files matching query.
229
+
230
+ Supports:
231
+ - Relative paths: @file.txt, @src/main.py
232
+ - Home paths: @~/Documents, @~/.config
233
+ - Absolute paths: @/Users/name/file.txt, @/etc/hosts
234
+
235
+ Args:
236
+ query: Search query (fuzzy matched)
237
+ max_results: Maximum results to return
238
+
239
+ Returns:
240
+ List of FileSuggestion sorted by score
241
+ """
242
+ # Determine search directory based on query
243
+ search_dir = self._root
244
+ search_query = query
245
+ prefix = ""
246
+
247
+ # Handle home directory paths (@~/)
248
+ if query.startswith('~'):
249
+ expanded = os.path.expanduser(query)
250
+ # Find the directory part and filename part
251
+ if os.path.isdir(expanded):
252
+ search_dir = expanded
253
+ search_query = ""
254
+ prefix = query.rstrip('/') + '/'
255
+ else:
256
+ parent = os.path.dirname(expanded)
257
+ if os.path.isdir(parent):
258
+ search_dir = parent
259
+ search_query = os.path.basename(expanded)
260
+ prefix = os.path.dirname(query).rstrip('/') + '/'
261
+ else:
262
+ # Just expand ~ and search from home
263
+ search_dir = os.path.expanduser('~')
264
+ search_query = query[2:] if query.startswith('~/') else query[1:]
265
+ prefix = "~/"
266
+
267
+ # Handle absolute paths (@/Users/..., @/etc/...)
268
+ elif query.startswith('/'):
269
+ if os.path.isdir(query):
270
+ search_dir = query
271
+ search_query = ""
272
+ prefix = query.rstrip('/') + '/'
273
+ else:
274
+ parent = os.path.dirname(query)
275
+ if os.path.isdir(parent):
276
+ search_dir = parent
277
+ search_query = os.path.basename(query)
278
+ prefix = parent.rstrip('/') + '/'
279
+ else:
280
+ # Search from root with the query
281
+ search_dir = '/'
282
+ search_query = query[1:]
283
+ prefix = "/"
284
+
285
+ # Check cache
286
+ cache_key = f"{search_dir}:{search_query}:{max_results}"
287
+ now = time.time()
288
+
289
+ if cache_key in self._cache:
290
+ results, cached_time = self._cache[cache_key]
291
+ if (now - cached_time) < self._cache_ttl:
292
+ return results
293
+
294
+ # Get all files from the appropriate directory
295
+ all_files = self._get_file_list(search_dir)
296
+
297
+ # Score and filter
298
+ scored: List[Tuple[int, str]] = []
299
+ for path in all_files:
300
+ score = self._fuzzy_match(path, search_query)
301
+ if score > 0:
302
+ scored.append((score, path))
303
+
304
+ # Sort by score descending
305
+ scored.sort(key=lambda x: (-x[0], x[1]))
306
+
307
+ # Build results with proper prefix
308
+ results = []
309
+ for score, path in scored[:max_results]:
310
+ file_type = "directory" if path.endswith(os.sep) else "file"
311
+ display_path = prefix + path if prefix else path
312
+ results.append(FileSuggestion(
313
+ path=display_path,
314
+ file_type=file_type,
315
+ score=score
316
+ ))
317
+
318
+ # Cache results
319
+ self._cache[cache_key] = (results, now)
320
+
321
+ return results
322
+
323
+ def clear_cache(self) -> None:
324
+ """Clear all caches."""
325
+ self._cache.clear()
326
+ self._file_list = None
327
+
328
+
329
+ # ============================================================================
330
+ # Completers for prompt_toolkit
331
+ # ============================================================================
332
+
333
+ # Lazy import to avoid performance impact
334
+ _Completer = None
335
+ _Completion = None
336
+
337
+ def _get_prompt_toolkit():
338
+ """Lazy import prompt_toolkit."""
339
+ global _Completer, _Completion
340
+ if _Completer is None:
341
+ from prompt_toolkit.completion import Completer, Completion
342
+ _Completer = Completer
343
+ _Completion = Completion
344
+ return _Completer, _Completion
345
+
346
+
347
+ class AtMentionCompleter:
348
+ """
349
+ Completer for @ mentions.
350
+
351
+ Shows file suggestions when user types @.
352
+ Implements prompt_toolkit Completer interface.
353
+ """
354
+
355
+ def __init__(self, root_dir: str):
356
+ self._file_service = FileSearchService(root_dir)
357
+
358
+ def get_completions(self, document, complete_event):
359
+ """Get completions for @ mentions."""
360
+ _, Completion = _get_prompt_toolkit()
361
+
362
+ text = document.text_before_cursor
363
+ cursor_pos = len(text)
364
+
365
+ # Detect @ context
366
+ context = detect_at_mention(text, cursor_pos)
367
+ if context is None or not context.is_active:
368
+ return
369
+
370
+ # Search files
371
+ results = self._file_service.search(context.query, max_results=15)
372
+
373
+ # Yield completions
374
+ for suggestion in results:
375
+ # Icon based on type
376
+ icon = "📁 " if suggestion.file_type == "directory" else "📄 "
377
+ display = f"{icon}{suggestion.path}"
378
+
379
+ # Calculate replacement position
380
+ # Replace from @ to cursor
381
+ start_position = -(cursor_pos - context.start_pos)
382
+
383
+ yield Completion(
384
+ text=f"@{suggestion.path}",
385
+ start_position=start_position,
386
+ display=display
387
+ )
388
+
389
+
390
+ def create_combined_completer(commands: List[str], root_dir: str):
391
+ """
392
+ Factory function to create a CombinedCompleter.
393
+
394
+ Returns a proper Completer subclass that handles both / and @.
395
+ """
396
+ Completer, Completion = _get_prompt_toolkit()
397
+ file_service = FileSearchService(root_dir)
398
+
399
+ class CombinedCompleter(Completer):
400
+ """
401
+ Combined completer for both / commands and @ mentions.
402
+
403
+ Properly inherits from prompt_toolkit Completer.
404
+ """
405
+
406
+ def get_completions(self, document, complete_event):
407
+ """Get completions for / or @."""
408
+ text = document.text_before_cursor.lstrip()
409
+
410
+ # Check for slash command
411
+ if text.startswith('/'):
412
+ cmd_text = text[1:].lower()
413
+ for cmd in commands:
414
+ if cmd.lower().startswith(cmd_text):
415
+ yield Completion(
416
+ f'/{cmd}',
417
+ start_position=-len(text),
418
+ display=f'/{cmd}'
419
+ )
420
+ return
421
+
422
+ # Check for @ mention
423
+ full_text = document.text_before_cursor
424
+ cursor_pos = len(full_text)
425
+
426
+ context = detect_at_mention(full_text, cursor_pos)
427
+ if context is None or not context.is_active:
428
+ return
429
+
430
+ # Search files
431
+ results = file_service.search(context.query, max_results=15)
432
+
433
+ # Yield completions
434
+ for suggestion in results:
435
+ icon = "📁 " if suggestion.file_type == "directory" else "📄 "
436
+ display = f"{icon}{suggestion.path}"
437
+ start_position = -(cursor_pos - context.start_pos)
438
+
439
+ yield Completion(
440
+ text=f"@{suggestion.path}",
441
+ start_position=start_position,
442
+ display=display
443
+ )
444
+
445
+ return CombinedCompleter()
446
+
447
+
448
+ # Backward compatible alias
449
+ class CombinedCompleter:
450
+ """
451
+ Wrapper class for backward compatibility.
452
+
453
+ Use create_combined_completer() for proper Completer subclass.
454
+ """
455
+
456
+ def __new__(cls, commands: List[str], root_dir: str):
457
+ return create_combined_completer(commands, root_dir)
458
+
459
+
460
+ # ============================================================================
461
+ # Exports
462
+ # ============================================================================
463
+
464
+ __all__ = [
465
+ 'AtMentionContext',
466
+ 'FileSuggestion',
467
+ 'detect_at_mention',
468
+ 'FileSearchService',
469
+ 'AtMentionCompleter',
470
+ 'CombinedCompleter',
471
+ ]
@@ -0,0 +1,182 @@
1
+ """
2
+ Auto Memory Handler for CLI.
3
+
4
+ Provides automatic memory extraction and storage.
5
+ Usage: praisonai "Learn about user preferences" --auto-memory
6
+ """
7
+
8
+ from typing import Any, Dict, Tuple
9
+ from .base import FlagHandler
10
+
11
+
12
+ class AutoMemoryHandler(FlagHandler):
13
+ """
14
+ Handler for --auto-memory flag.
15
+
16
+ Automatically extracts and stores important information from conversations.
17
+
18
+ Example:
19
+ praisonai "Learn about user preferences" --auto-memory
20
+ praisonai "Remember this context" --auto-memory --user-id myuser
21
+ """
22
+
23
+ @property
24
+ def feature_name(self) -> str:
25
+ return "auto_memory"
26
+
27
+ @property
28
+ def flag_name(self) -> str:
29
+ return "auto-memory"
30
+
31
+ @property
32
+ def flag_help(self) -> str:
33
+ return "Enable automatic memory extraction and storage"
34
+
35
+ def check_dependencies(self) -> Tuple[bool, str]:
36
+ """Check if AutoMemory is available."""
37
+ try:
38
+ import importlib.util
39
+ if importlib.util.find_spec("praisonaiagents") is not None:
40
+ return True, ""
41
+ return False, "praisonaiagents not installed"
42
+ except ImportError:
43
+ return False, "praisonaiagents not installed. Install with: pip install praisonaiagents"
44
+
45
+ def _get_auto_memory(self, user_id: str = None):
46
+ """Get AutoMemory instance lazily."""
47
+ try:
48
+ from praisonaiagents.memory import AutoMemory, FileMemory
49
+ # AutoMemory requires a FileMemory instance
50
+ memory = FileMemory(user_id=user_id or "default")
51
+ return AutoMemory(memory=memory)
52
+ except ImportError:
53
+ self.print_status(
54
+ "AutoMemory requires praisonaiagents. Install with: pip install praisonaiagents",
55
+ "error"
56
+ )
57
+ return None
58
+
59
+ def apply_to_agent_config(self, config: Dict[str, Any], flag_value: Any) -> Dict[str, Any]:
60
+ """
61
+ Apply auto memory configuration.
62
+
63
+ Args:
64
+ config: Agent configuration dictionary
65
+ flag_value: Boolean or dict with user_id
66
+
67
+ Returns:
68
+ Modified configuration
69
+ """
70
+ if flag_value:
71
+ config['auto_memory'] = True
72
+ if isinstance(flag_value, dict):
73
+ config['auto_memory_user_id'] = flag_value.get('user_id', 'default')
74
+ return config
75
+
76
+ def extract_memories(self, text: str, user_id: str = None, user_message: str = None) -> list:
77
+ """
78
+ Extract and store memories from text.
79
+
80
+ Args:
81
+ text: Assistant response text to extract memories from
82
+ user_id: User ID for memory isolation
83
+ user_message: Original user message (for context)
84
+
85
+ Returns:
86
+ List of extracted memories
87
+ """
88
+ auto_memory = self._get_auto_memory(user_id)
89
+ if not auto_memory:
90
+ return []
91
+
92
+ try:
93
+ # Use process_interaction which extracts AND stores memories
94
+ memories = auto_memory.process_interaction(
95
+ user_message=user_message or text,
96
+ assistant_response=text if user_message else None,
97
+ store=True # Store the memories
98
+ )
99
+ if memories:
100
+ self.print_status(f"🧠 Extracted and stored {len(memories)} memories", "success")
101
+ return memories
102
+ except Exception as e:
103
+ self.log(f"Memory extraction failed: {e}", "error")
104
+
105
+ return []
106
+
107
+ def store_memory(self, content: str, user_id: str = None, importance: float = 0.5) -> bool:
108
+ """
109
+ Store a memory.
110
+
111
+ Args:
112
+ content: Memory content
113
+ user_id: User ID for memory isolation
114
+ importance: Importance score (0-1)
115
+
116
+ Returns:
117
+ True if successful
118
+ """
119
+ auto_memory = self._get_auto_memory(user_id)
120
+ if not auto_memory:
121
+ return False
122
+
123
+ try:
124
+ if hasattr(auto_memory, 'store'):
125
+ auto_memory.store(content, importance=importance)
126
+ elif hasattr(auto_memory, 'add'):
127
+ auto_memory.add(content, importance=importance)
128
+
129
+ self.print_status("✅ Memory stored", "success")
130
+ return True
131
+ except Exception as e:
132
+ self.log(f"Memory storage failed: {e}", "error")
133
+ return False
134
+
135
+ def post_process_result(self, result: Any, flag_value: Any, user_message: str = None) -> Any:
136
+ """
137
+ Post-process result to extract and store memories.
138
+
139
+ Args:
140
+ result: Agent output
141
+ flag_value: Boolean or dict with configuration
142
+ user_message: Original user message for context
143
+
144
+ Returns:
145
+ Original result (memories are stored)
146
+ """
147
+ if not flag_value:
148
+ return result
149
+
150
+ user_id = None
151
+ if isinstance(flag_value, dict):
152
+ user_id = flag_value.get('user_id')
153
+ user_message = flag_value.get('user_message', user_message)
154
+
155
+ # Extract and store memories from result
156
+ text = str(result)
157
+ memories = self.extract_memories(text, user_id, user_message=user_message)
158
+
159
+ if memories:
160
+ self.print_status("\n🧠 Auto-extracted Memories:", "info")
161
+ for mem in memories:
162
+ mem_type = mem.get('type', 'unknown')
163
+ content = mem.get('content', str(mem))[:50]
164
+ self.print_status(f" • {mem_type}: {content}...", "info")
165
+
166
+ return result
167
+
168
+ def execute(self, text: str = None, user_id: str = None, **kwargs) -> Dict[str, Any]:
169
+ """
170
+ Execute auto memory extraction.
171
+
172
+ Args:
173
+ text: Text to process
174
+ user_id: User ID for memory isolation
175
+
176
+ Returns:
177
+ Dictionary of extracted memories
178
+ """
179
+ if not text:
180
+ return {}
181
+
182
+ return self.extract_memories(text, user_id)