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,206 @@
1
+ """
2
+ Read File Tool for PraisonAI Code.
3
+
4
+ Provides functionality to read file contents with optional line ranges
5
+ and line number annotations.
6
+ """
7
+
8
+ import os
9
+ from typing import Optional, List, Dict, Any
10
+
11
+ from ..utils.file_utils import (
12
+ add_line_numbers,
13
+ is_binary_file,
14
+ file_exists,
15
+ is_path_within_directory,
16
+ )
17
+
18
+
19
+ def read_file(
20
+ path: str,
21
+ start_line: Optional[int] = None,
22
+ end_line: Optional[int] = None,
23
+ add_line_nums: bool = True,
24
+ workspace: Optional[str] = None,
25
+ encoding: str = 'utf-8',
26
+ ) -> Dict[str, Any]:
27
+ """
28
+ Read file contents with optional line range and line numbers.
29
+
30
+ This tool reads the contents of a file, optionally limiting to specific
31
+ line ranges and adding line number annotations for AI context.
32
+
33
+ Args:
34
+ path: Path to the file (absolute or relative to workspace)
35
+ start_line: First line to read (1-indexed, inclusive)
36
+ end_line: Last line to read (1-indexed, inclusive)
37
+ add_line_nums: Whether to add line numbers to output
38
+ workspace: Workspace root directory (for relative paths)
39
+ encoding: File encoding (default: utf-8)
40
+
41
+ Returns:
42
+ Dictionary with:
43
+ - success: bool
44
+ - content: str (file content, possibly with line numbers)
45
+ - total_lines: int (total lines in file)
46
+ - start_line: int (actual start line read)
47
+ - end_line: int (actual end line read)
48
+ - error: str (if success is False)
49
+
50
+ Example:
51
+ >>> result = read_file("src/main.py", start_line=1, end_line=50)
52
+ >>> if result['success']:
53
+ ... print(result['content'])
54
+ """
55
+ # Resolve path
56
+ if workspace and not os.path.isabs(path):
57
+ abs_path = os.path.abspath(os.path.join(workspace, path))
58
+ else:
59
+ abs_path = os.path.abspath(path)
60
+
61
+ # Security check - ensure path is within workspace if specified
62
+ if workspace:
63
+ if not is_path_within_directory(abs_path, workspace):
64
+ return {
65
+ 'success': False,
66
+ 'error': f"Path '{path}' is outside the workspace",
67
+ 'content': None,
68
+ 'total_lines': 0,
69
+ }
70
+
71
+ # Check if file exists
72
+ if not file_exists(abs_path):
73
+ return {
74
+ 'success': False,
75
+ 'error': f"File not found: {path}",
76
+ 'content': None,
77
+ 'total_lines': 0,
78
+ }
79
+
80
+ # Check if binary
81
+ if is_binary_file(abs_path):
82
+ return {
83
+ 'success': False,
84
+ 'error': f"Cannot read binary file: {path}",
85
+ 'content': None,
86
+ 'total_lines': 0,
87
+ }
88
+
89
+ try:
90
+ # Read file content
91
+ with open(abs_path, 'r', encoding=encoding, errors='replace') as f:
92
+ lines = f.readlines()
93
+
94
+ total_lines = len(lines)
95
+
96
+ # Determine line range
97
+ actual_start = start_line if start_line and start_line > 0 else 1
98
+ actual_end = end_line if end_line and end_line > 0 else total_lines
99
+
100
+ # Clamp to valid range
101
+ actual_start = max(1, min(actual_start, total_lines))
102
+ actual_end = max(actual_start, min(actual_end, total_lines))
103
+
104
+ # Extract lines (convert to 0-indexed)
105
+ selected_lines = lines[actual_start - 1:actual_end]
106
+ content = ''.join(selected_lines)
107
+
108
+ # Remove trailing newline if present
109
+ if content.endswith('\n'):
110
+ content = content[:-1]
111
+
112
+ # Add line numbers if requested
113
+ if add_line_nums:
114
+ content = add_line_numbers(content, actual_start)
115
+
116
+ return {
117
+ 'success': True,
118
+ 'content': content,
119
+ 'total_lines': total_lines,
120
+ 'start_line': actual_start,
121
+ 'end_line': actual_end,
122
+ 'path': path,
123
+ }
124
+
125
+ except PermissionError:
126
+ return {
127
+ 'success': False,
128
+ 'error': f"Permission denied: {path}",
129
+ 'content': None,
130
+ 'total_lines': 0,
131
+ }
132
+ except UnicodeDecodeError as e:
133
+ return {
134
+ 'success': False,
135
+ 'error': f"Encoding error reading {path}: {e}",
136
+ 'content': None,
137
+ 'total_lines': 0,
138
+ }
139
+ except Exception as e:
140
+ return {
141
+ 'success': False,
142
+ 'error': f"Error reading {path}: {str(e)}",
143
+ 'content': None,
144
+ 'total_lines': 0,
145
+ }
146
+
147
+
148
+ def read_multiple_files(
149
+ files: List[Dict[str, Any]],
150
+ workspace: Optional[str] = None,
151
+ add_line_nums: bool = True,
152
+ ) -> Dict[str, Any]:
153
+ """
154
+ Read multiple files in a single operation.
155
+
156
+ Args:
157
+ files: List of file specifications, each with:
158
+ - path: str (required)
159
+ - start_line: int (optional)
160
+ - end_line: int (optional)
161
+ workspace: Workspace root directory
162
+ add_line_nums: Whether to add line numbers
163
+
164
+ Returns:
165
+ Dictionary with:
166
+ - success: bool (True if all files read successfully)
167
+ - results: List of individual file results
168
+ - errors: List of error messages
169
+
170
+ Example:
171
+ >>> files = [
172
+ ... {'path': 'src/main.py', 'start_line': 1, 'end_line': 50},
173
+ ... {'path': 'src/utils.py'},
174
+ ... ]
175
+ >>> result = read_multiple_files(files, workspace='/project')
176
+ """
177
+ results = []
178
+ errors = []
179
+ all_success = True
180
+
181
+ for file_spec in files:
182
+ path = file_spec.get('path')
183
+ if not path:
184
+ errors.append("Missing 'path' in file specification")
185
+ all_success = False
186
+ continue
187
+
188
+ result = read_file(
189
+ path=path,
190
+ start_line=file_spec.get('start_line'),
191
+ end_line=file_spec.get('end_line'),
192
+ add_line_nums=add_line_nums,
193
+ workspace=workspace,
194
+ )
195
+
196
+ results.append(result)
197
+
198
+ if not result['success']:
199
+ all_success = False
200
+ errors.append(result.get('error', f"Failed to read {path}"))
201
+
202
+ return {
203
+ 'success': all_success,
204
+ 'results': results,
205
+ 'errors': errors if errors else None,
206
+ }
@@ -0,0 +1,248 @@
1
+ """
2
+ Search and Replace Tool for PraisonAI Code.
3
+
4
+ Provides functionality to perform multiple search/replace operations
5
+ on a file in a single operation.
6
+ """
7
+
8
+ import os
9
+ import re
10
+ from typing import Optional, List, Dict, Any
11
+
12
+ from ..utils.file_utils import (
13
+ file_exists,
14
+ is_path_within_directory,
15
+ )
16
+
17
+
18
+ def search_replace(
19
+ path: str,
20
+ operations: List[Dict[str, Any]],
21
+ workspace: Optional[str] = None,
22
+ backup: bool = False,
23
+ encoding: str = 'utf-8',
24
+ ) -> Dict[str, Any]:
25
+ """
26
+ Perform multiple search/replace operations on a file.
27
+
28
+ This tool allows precise modifications to file content by performing
29
+ multiple search and replace operations in sequence.
30
+
31
+ Args:
32
+ path: Path to the file (absolute or relative to workspace)
33
+ operations: List of operations, each with:
34
+ - search: str (text to find)
35
+ - replace: str (text to replace with)
36
+ - is_regex: bool (optional, treat search as regex)
37
+ - count: int (optional, max replacements, -1 for all)
38
+ workspace: Workspace root directory
39
+ backup: Whether to create a backup before modifying
40
+ encoding: File encoding
41
+
42
+ Returns:
43
+ Dictionary with:
44
+ - success: bool
45
+ - path: str
46
+ - operations_applied: int
47
+ - total_replacements: int
48
+ - failed_operations: list
49
+ - error: str (if success is False)
50
+
51
+ Example:
52
+ >>> operations = [
53
+ ... {'search': 'old_name', 'replace': 'new_name'},
54
+ ... {'search': r'def (\\w+)\\(', 'replace': r'def renamed_\\\\1(', 'is_regex': True},
55
+ ... ]
56
+ >>> result = search_replace("src/main.py", operations)
57
+ """
58
+ # Resolve path
59
+ if workspace and not os.path.isabs(path):
60
+ abs_path = os.path.abspath(os.path.join(workspace, path))
61
+ else:
62
+ abs_path = os.path.abspath(path)
63
+
64
+ # Security check
65
+ if workspace:
66
+ if not is_path_within_directory(abs_path, workspace):
67
+ return {
68
+ 'success': False,
69
+ 'error': f"Path '{path}' is outside the workspace",
70
+ 'path': path,
71
+ 'operations_applied': 0,
72
+ 'total_replacements': 0,
73
+ }
74
+
75
+ # Check if file exists
76
+ if not file_exists(abs_path):
77
+ return {
78
+ 'success': False,
79
+ 'error': f"File not found: {path}",
80
+ 'path': path,
81
+ 'operations_applied': 0,
82
+ 'total_replacements': 0,
83
+ }
84
+
85
+ if not operations:
86
+ return {
87
+ 'success': False,
88
+ 'error': "No operations provided",
89
+ 'path': path,
90
+ 'operations_applied': 0,
91
+ 'total_replacements': 0,
92
+ }
93
+
94
+ try:
95
+ # Read original content
96
+ with open(abs_path, 'r', encoding=encoding, errors='replace') as f:
97
+ content = f.read()
98
+
99
+ original_content = content
100
+ operations_applied = 0
101
+ total_replacements = 0
102
+ failed_operations = []
103
+
104
+ # Apply each operation
105
+ for i, op in enumerate(operations):
106
+ search = op.get('search')
107
+ replace = op.get('replace', '')
108
+ is_regex = op.get('is_regex', False)
109
+ count = op.get('count', -1) # -1 means replace all
110
+
111
+ if not search:
112
+ failed_operations.append({
113
+ 'index': i,
114
+ 'error': "Missing 'search' field",
115
+ })
116
+ continue
117
+
118
+ try:
119
+ if is_regex:
120
+ # Regex replacement
121
+ if count == -1:
122
+ new_content, num_subs = re.subn(search, replace, content)
123
+ else:
124
+ new_content, num_subs = re.subn(search, replace, content, count=count)
125
+ else:
126
+ # Literal replacement
127
+ if count == -1:
128
+ num_subs = content.count(search)
129
+ new_content = content.replace(search, replace)
130
+ else:
131
+ num_subs = min(content.count(search), count)
132
+ new_content = content
133
+ for _ in range(count):
134
+ if search in new_content:
135
+ new_content = new_content.replace(search, replace, 1)
136
+ else:
137
+ break
138
+
139
+ if num_subs > 0:
140
+ content = new_content
141
+ operations_applied += 1
142
+ total_replacements += num_subs
143
+ else:
144
+ failed_operations.append({
145
+ 'index': i,
146
+ 'search': search[:50],
147
+ 'error': "No matches found",
148
+ })
149
+
150
+ except re.error as e:
151
+ failed_operations.append({
152
+ 'index': i,
153
+ 'search': search[:50],
154
+ 'error': f"Invalid regex: {str(e)}",
155
+ })
156
+
157
+ # Check if any changes were made
158
+ if content == original_content:
159
+ return {
160
+ 'success': False,
161
+ 'error': "No changes were made - no matches found",
162
+ 'path': path,
163
+ 'operations_applied': 0,
164
+ 'total_replacements': 0,
165
+ 'failed_operations': failed_operations,
166
+ }
167
+
168
+ # Create backup if requested
169
+ backup_path = None
170
+ if backup:
171
+ import time
172
+ timestamp = int(time.time())
173
+ backup_path = f"{abs_path}.backup.{timestamp}"
174
+ with open(backup_path, 'w', encoding=encoding) as f:
175
+ f.write(original_content)
176
+
177
+ # Write the modified content
178
+ with open(abs_path, 'w', encoding=encoding) as f:
179
+ f.write(content)
180
+
181
+ return {
182
+ 'success': True,
183
+ 'path': path,
184
+ 'absolute_path': abs_path,
185
+ 'operations_applied': operations_applied,
186
+ 'total_replacements': total_replacements,
187
+ 'failed_operations': failed_operations if failed_operations else None,
188
+ 'backup_path': backup_path,
189
+ }
190
+
191
+ except PermissionError:
192
+ return {
193
+ 'success': False,
194
+ 'error': f"Permission denied: {path}",
195
+ 'path': path,
196
+ 'operations_applied': 0,
197
+ 'total_replacements': 0,
198
+ }
199
+ except Exception as e:
200
+ return {
201
+ 'success': False,
202
+ 'error': f"Error performing search/replace on {path}: {str(e)}",
203
+ 'path': path,
204
+ 'operations_applied': 0,
205
+ 'total_replacements': 0,
206
+ }
207
+
208
+
209
+ def simple_replace(
210
+ path: str,
211
+ search: str,
212
+ replace: str,
213
+ workspace: Optional[str] = None,
214
+ is_regex: bool = False,
215
+ count: int = -1,
216
+ backup: bool = False,
217
+ encoding: str = 'utf-8',
218
+ ) -> Dict[str, Any]:
219
+ """
220
+ Perform a single search/replace operation on a file.
221
+
222
+ Convenience wrapper around search_replace for single operations.
223
+
224
+ Args:
225
+ path: Path to the file
226
+ search: Text to find
227
+ replace: Text to replace with
228
+ workspace: Workspace root directory
229
+ is_regex: Whether search is a regex pattern
230
+ count: Max replacements (-1 for all)
231
+ backup: Whether to create a backup
232
+ encoding: File encoding
233
+
234
+ Returns:
235
+ Dictionary with success status and details
236
+ """
237
+ return search_replace(
238
+ path=path,
239
+ operations=[{
240
+ 'search': search,
241
+ 'replace': replace,
242
+ 'is_regex': is_regex,
243
+ 'count': count,
244
+ }],
245
+ workspace=workspace,
246
+ backup=backup,
247
+ encoding=encoding,
248
+ )
@@ -0,0 +1,217 @@
1
+ """
2
+ Write File Tool for PraisonAI Code.
3
+
4
+ Provides functionality to create or overwrite files with content.
5
+ """
6
+
7
+ import os
8
+ from typing import Optional, Dict, Any
9
+
10
+ from ..utils.file_utils import (
11
+ file_exists,
12
+ create_directories_for_file,
13
+ is_path_within_directory,
14
+ detect_line_ending,
15
+ normalize_line_endings,
16
+ )
17
+ from ..utils.text_utils import (
18
+ unescape_html_entities,
19
+ strip_markdown_code_fences,
20
+ )
21
+
22
+
23
+ def write_file(
24
+ path: str,
25
+ content: str,
26
+ workspace: Optional[str] = None,
27
+ create_directories: bool = True,
28
+ backup: bool = False,
29
+ strip_code_fences: bool = True,
30
+ encoding: str = 'utf-8',
31
+ ) -> Dict[str, Any]:
32
+ """
33
+ Write content to a file, creating it if it doesn't exist.
34
+
35
+ This tool writes content to a file, optionally creating parent
36
+ directories and handling common AI output artifacts like code fences.
37
+
38
+ Args:
39
+ path: Path to the file (absolute or relative to workspace)
40
+ content: Content to write to the file
41
+ workspace: Workspace root directory (for relative paths)
42
+ create_directories: Whether to create parent directories
43
+ backup: Whether to create a backup of existing files
44
+ strip_code_fences: Whether to strip markdown code fences
45
+ encoding: File encoding (default: utf-8)
46
+
47
+ Returns:
48
+ Dictionary with:
49
+ - success: bool
50
+ - path: str (the path written to)
51
+ - created: bool (True if file was created, False if overwritten)
52
+ - backup_path: str (if backup was created)
53
+ - error: str (if success is False)
54
+
55
+ Example:
56
+ >>> result = write_file("src/new_file.py", "print('hello')")
57
+ >>> if result['success']:
58
+ ... print(f"Wrote to {result['path']}")
59
+ """
60
+ # Resolve path
61
+ if workspace and not os.path.isabs(path):
62
+ abs_path = os.path.abspath(os.path.join(workspace, path))
63
+ else:
64
+ abs_path = os.path.abspath(path)
65
+
66
+ # Security check - ensure path is within workspace if specified
67
+ if workspace:
68
+ if not is_path_within_directory(abs_path, workspace):
69
+ return {
70
+ 'success': False,
71
+ 'error': f"Path '{path}' is outside the workspace",
72
+ 'path': path,
73
+ }
74
+
75
+ # Process content
76
+ processed_content = content
77
+
78
+ # Strip markdown code fences if requested
79
+ if strip_code_fences:
80
+ processed_content = strip_markdown_code_fences(processed_content)
81
+
82
+ # Unescape HTML entities (common in AI output)
83
+ processed_content = unescape_html_entities(processed_content)
84
+
85
+ # Check if file exists
86
+ file_existed = file_exists(abs_path)
87
+ backup_path = None
88
+
89
+ try:
90
+ # Create backup if requested and file exists
91
+ if backup and file_existed:
92
+ import time
93
+ timestamp = int(time.time())
94
+ backup_path = f"{abs_path}.backup.{timestamp}"
95
+ with open(abs_path, 'r', encoding=encoding, errors='replace') as src:
96
+ with open(backup_path, 'w', encoding=encoding) as dst:
97
+ dst.write(src.read())
98
+
99
+ # Create parent directories if needed
100
+ if create_directories:
101
+ if not create_directories_for_file(abs_path):
102
+ return {
103
+ 'success': False,
104
+ 'error': f"Failed to create directories for {path}",
105
+ 'path': path,
106
+ }
107
+
108
+ # Preserve line endings if file exists
109
+ if file_existed:
110
+ try:
111
+ with open(abs_path, 'r', encoding=encoding, errors='replace') as f:
112
+ original_content = f.read()
113
+ original_line_ending = detect_line_ending(original_content)
114
+ processed_content = normalize_line_endings(processed_content, original_line_ending)
115
+ except Exception:
116
+ pass # If we can't read, just use the content as-is
117
+
118
+ # Write the file
119
+ with open(abs_path, 'w', encoding=encoding) as f:
120
+ f.write(processed_content)
121
+
122
+ return {
123
+ 'success': True,
124
+ 'path': path,
125
+ 'absolute_path': abs_path,
126
+ 'created': not file_existed,
127
+ 'backup_path': backup_path,
128
+ 'bytes_written': len(processed_content.encode(encoding)),
129
+ }
130
+
131
+ except PermissionError:
132
+ return {
133
+ 'success': False,
134
+ 'error': f"Permission denied: {path}",
135
+ 'path': path,
136
+ }
137
+ except OSError as e:
138
+ return {
139
+ 'success': False,
140
+ 'error': f"OS error writing {path}: {str(e)}",
141
+ 'path': path,
142
+ }
143
+ except Exception as e:
144
+ return {
145
+ 'success': False,
146
+ 'error': f"Error writing {path}: {str(e)}",
147
+ 'path': path,
148
+ }
149
+
150
+
151
+ def append_to_file(
152
+ path: str,
153
+ content: str,
154
+ workspace: Optional[str] = None,
155
+ create_if_missing: bool = True,
156
+ encoding: str = 'utf-8',
157
+ ) -> Dict[str, Any]:
158
+ """
159
+ Append content to an existing file.
160
+
161
+ Args:
162
+ path: Path to the file
163
+ content: Content to append
164
+ workspace: Workspace root directory
165
+ create_if_missing: Create file if it doesn't exist
166
+ encoding: File encoding
167
+
168
+ Returns:
169
+ Dictionary with success status and details
170
+ """
171
+ # Resolve path
172
+ if workspace and not os.path.isabs(path):
173
+ abs_path = os.path.abspath(os.path.join(workspace, path))
174
+ else:
175
+ abs_path = os.path.abspath(path)
176
+
177
+ # Security check
178
+ if workspace:
179
+ if not is_path_within_directory(abs_path, workspace):
180
+ return {
181
+ 'success': False,
182
+ 'error': f"Path '{path}' is outside the workspace",
183
+ 'path': path,
184
+ }
185
+
186
+ file_existed = file_exists(abs_path)
187
+
188
+ if not file_existed and not create_if_missing:
189
+ return {
190
+ 'success': False,
191
+ 'error': f"File not found: {path}",
192
+ 'path': path,
193
+ }
194
+
195
+ try:
196
+ # Create directories if needed
197
+ if not file_existed:
198
+ create_directories_for_file(abs_path)
199
+
200
+ # Append to file
201
+ with open(abs_path, 'a', encoding=encoding) as f:
202
+ f.write(content)
203
+
204
+ return {
205
+ 'success': True,
206
+ 'path': path,
207
+ 'absolute_path': abs_path,
208
+ 'created': not file_existed,
209
+ 'bytes_appended': len(content.encode(encoding)),
210
+ }
211
+
212
+ except Exception as e:
213
+ return {
214
+ 'success': False,
215
+ 'error': f"Error appending to {path}: {str(e)}",
216
+ 'path': path,
217
+ }