auto-coder 0.1.400__py3-none-any.whl → 2.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.

Potentially problematic release.


This version of auto-coder might be problematic. Click here for more details.

Files changed (579) hide show
  1. auto_coder-2.0.0.dist-info/LICENSE +158 -0
  2. auto_coder-2.0.0.dist-info/METADATA +558 -0
  3. auto_coder-2.0.0.dist-info/RECORD +795 -0
  4. {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
  5. {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/entry_points.txt +3 -3
  6. autocoder/__init__.py +31 -0
  7. autocoder/agent/auto_filegroup.py +32 -13
  8. autocoder/agent/auto_learn_from_commit.py +9 -1
  9. autocoder/agent/base_agentic/__init__.py +3 -0
  10. autocoder/agent/base_agentic/agent_hub.py +1 -1
  11. autocoder/agent/base_agentic/base_agent.py +235 -136
  12. autocoder/agent/base_agentic/default_tools.py +119 -118
  13. autocoder/agent/base_agentic/test_base_agent.py +1 -1
  14. autocoder/agent/base_agentic/tool_registry.py +32 -20
  15. autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +25 -4
  16. autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +24 -11
  17. autocoder/agent/base_agentic/types.py +42 -0
  18. autocoder/agent/entry_command_agent/chat.py +73 -59
  19. autocoder/auto_coder.py +31 -40
  20. autocoder/auto_coder_rag.py +11 -1084
  21. autocoder/auto_coder_runner.py +1029 -2310
  22. autocoder/auto_coder_terminal.py +26 -0
  23. autocoder/auto_coder_terminal_v3.py +190 -0
  24. autocoder/chat/conf_command.py +224 -124
  25. autocoder/chat/models_command.py +361 -299
  26. autocoder/chat/rules_command.py +79 -31
  27. autocoder/chat_auto_coder.py +1021 -372
  28. autocoder/chat_auto_coder_lang.py +23 -732
  29. autocoder/commands/auto_command.py +26 -9
  30. autocoder/commands/auto_web.py +1 -1
  31. autocoder/commands/tools.py +44 -44
  32. autocoder/common/__init__.py +150 -128
  33. autocoder/common/ac_style_command_parser/__init__.py +39 -2
  34. autocoder/common/ac_style_command_parser/config.py +422 -0
  35. autocoder/common/ac_style_command_parser/parser.py +292 -78
  36. autocoder/common/ac_style_command_parser/test_parser.py +241 -16
  37. autocoder/common/ac_style_command_parser/test_typed_parser.py +342 -0
  38. autocoder/common/ac_style_command_parser/typed_parser.py +653 -0
  39. autocoder/common/action_yml_file_manager.py +25 -13
  40. autocoder/common/agent_events/__init__.py +52 -0
  41. autocoder/common/agent_events/agent_event_emitter.py +193 -0
  42. autocoder/common/agent_events/event_factory.py +177 -0
  43. autocoder/common/agent_events/examples.py +307 -0
  44. autocoder/common/agent_events/types.py +113 -0
  45. autocoder/common/agent_events/utils.py +68 -0
  46. autocoder/common/agent_hooks/__init__.py +44 -0
  47. autocoder/common/agent_hooks/examples.py +582 -0
  48. autocoder/common/agent_hooks/hook_executor.py +217 -0
  49. autocoder/common/agent_hooks/hook_manager.py +288 -0
  50. autocoder/common/agent_hooks/types.py +133 -0
  51. autocoder/common/agent_hooks/utils.py +99 -0
  52. autocoder/common/agent_query_queue/queue_executor.py +324 -0
  53. autocoder/common/agent_query_queue/queue_manager.py +325 -0
  54. autocoder/common/agents/__init__.py +11 -0
  55. autocoder/common/agents/agent_manager.py +323 -0
  56. autocoder/common/agents/agent_parser.py +189 -0
  57. autocoder/common/agents/example_usage.py +344 -0
  58. autocoder/common/agents/integration_example.py +330 -0
  59. autocoder/common/agents/test_agent_parser.py +545 -0
  60. autocoder/common/async_utils.py +101 -0
  61. autocoder/common/auto_coder_lang.py +23 -972
  62. autocoder/common/autocoderargs_parser/__init__.py +14 -0
  63. autocoder/common/autocoderargs_parser/parser.py +184 -0
  64. autocoder/common/autocoderargs_parser/tests/__init__.py +1 -0
  65. autocoder/common/autocoderargs_parser/tests/test_args_parser.py +235 -0
  66. autocoder/common/autocoderargs_parser/tests/test_token_parser.py +195 -0
  67. autocoder/common/autocoderargs_parser/token_parser.py +290 -0
  68. autocoder/common/buildin_tokenizer.py +2 -4
  69. autocoder/common/code_auto_generate.py +149 -74
  70. autocoder/common/code_auto_generate_diff.py +163 -70
  71. autocoder/common/code_auto_generate_editblock.py +179 -89
  72. autocoder/common/code_auto_generate_strict_diff.py +167 -72
  73. autocoder/common/code_auto_merge_editblock.py +13 -6
  74. autocoder/common/code_modification_ranker.py +1 -1
  75. autocoder/common/command_completer.py +3 -3
  76. autocoder/common/command_file_manager/manager.py +183 -47
  77. autocoder/common/command_file_manager/test_command_file_manager.py +507 -0
  78. autocoder/common/command_templates.py +1 -1
  79. autocoder/common/conf_utils.py +2 -4
  80. autocoder/common/conversations/config.py +11 -3
  81. autocoder/common/conversations/get_conversation_manager.py +100 -2
  82. autocoder/common/conversations/llm_stats_models.py +264 -0
  83. autocoder/common/conversations/manager.py +112 -28
  84. autocoder/common/conversations/models.py +16 -2
  85. autocoder/common/conversations/storage/index_manager.py +134 -10
  86. autocoder/common/core_config/__init__.py +63 -0
  87. autocoder/common/core_config/agentic_mode_manager.py +109 -0
  88. autocoder/common/core_config/base_manager.py +123 -0
  89. autocoder/common/core_config/compatibility.py +151 -0
  90. autocoder/common/core_config/config_manager.py +156 -0
  91. autocoder/common/core_config/conversation_manager.py +31 -0
  92. autocoder/common/core_config/exclude_manager.py +72 -0
  93. autocoder/common/core_config/file_manager.py +177 -0
  94. autocoder/common/core_config/human_as_model_manager.py +129 -0
  95. autocoder/common/core_config/lib_manager.py +54 -0
  96. autocoder/common/core_config/main_manager.py +81 -0
  97. autocoder/common/core_config/mode_manager.py +126 -0
  98. autocoder/common/core_config/models.py +70 -0
  99. autocoder/common/core_config/test_memory_manager.py +1056 -0
  100. autocoder/common/env_manager.py +282 -0
  101. autocoder/common/env_manager_usage_example.py +211 -0
  102. autocoder/common/file_checkpoint/conversation_checkpoint.py +19 -19
  103. autocoder/common/file_checkpoint/manager.py +264 -48
  104. autocoder/common/file_checkpoint/test_backup.py +1 -18
  105. autocoder/common/file_checkpoint/test_manager.py +270 -1
  106. autocoder/common/file_checkpoint/test_store.py +1 -17
  107. autocoder/common/file_handler/__init__.py +23 -0
  108. autocoder/common/file_handler/active_context_handler.py +159 -0
  109. autocoder/common/file_handler/add_files_handler.py +409 -0
  110. autocoder/common/file_handler/chat_handler.py +180 -0
  111. autocoder/common/file_handler/coding_handler.py +401 -0
  112. autocoder/common/file_handler/commit_handler.py +200 -0
  113. autocoder/common/file_handler/lib_handler.py +156 -0
  114. autocoder/common/file_handler/list_files_handler.py +111 -0
  115. autocoder/common/file_handler/mcp_handler.py +268 -0
  116. autocoder/common/file_handler/models_handler.py +493 -0
  117. autocoder/common/file_handler/remove_files_handler.py +172 -0
  118. autocoder/common/file_monitor/test_file_monitor.py +307 -0
  119. autocoder/common/git_utils.py +51 -10
  120. autocoder/common/global_cancel.py +15 -6
  121. autocoder/common/ignorefiles/test_ignore_file_utils.py +1 -1
  122. autocoder/common/international/__init__.py +31 -0
  123. autocoder/common/international/demo_international.py +92 -0
  124. autocoder/common/international/message_manager.py +157 -0
  125. autocoder/common/international/messages/__init__.py +56 -0
  126. autocoder/common/international/messages/async_command_messages.py +507 -0
  127. autocoder/common/international/messages/auto_coder_messages.py +2208 -0
  128. autocoder/common/international/messages/chat_auto_coder_messages.py +1547 -0
  129. autocoder/common/international/messages/command_help_messages.py +986 -0
  130. autocoder/common/international/messages/conversation_command_messages.py +191 -0
  131. autocoder/common/international/messages/git_helper_plugin_messages.py +159 -0
  132. autocoder/common/international/messages/queue_command_messages.py +751 -0
  133. autocoder/common/international/messages/rules_command_messages.py +77 -0
  134. autocoder/common/international/messages/sdk_messages.py +1707 -0
  135. autocoder/common/international/messages/token_helper_plugin_messages.py +361 -0
  136. autocoder/common/international/messages/tool_display_messages.py +1212 -0
  137. autocoder/common/international/messages/workflow_exception_messages.py +473 -0
  138. autocoder/common/international/test_international.py +612 -0
  139. autocoder/common/linter_core/__init__.py +28 -0
  140. autocoder/common/linter_core/base_linter.py +61 -0
  141. autocoder/common/linter_core/config_loader.py +271 -0
  142. autocoder/common/linter_core/formatters/__init__.py +0 -0
  143. autocoder/common/linter_core/formatters/base_formatter.py +38 -0
  144. autocoder/common/linter_core/formatters/raw_formatter.py +17 -0
  145. autocoder/common/linter_core/linter.py +166 -0
  146. autocoder/common/linter_core/linter_factory.py +216 -0
  147. autocoder/common/linter_core/linter_manager.py +333 -0
  148. autocoder/common/linter_core/linters/__init__.py +9 -0
  149. autocoder/common/linter_core/linters/java_linter.py +342 -0
  150. autocoder/common/linter_core/linters/python_linter.py +115 -0
  151. autocoder/common/linter_core/linters/typescript_linter.py +119 -0
  152. autocoder/common/linter_core/models/__init__.py +7 -0
  153. autocoder/common/linter_core/models/lint_result.py +91 -0
  154. autocoder/common/linter_core/models.py +33 -0
  155. autocoder/common/linter_core/tests/__init__.py +3 -0
  156. autocoder/common/linter_core/tests/test_config_loader.py +323 -0
  157. autocoder/common/linter_core/tests/test_config_loading.py +308 -0
  158. autocoder/common/linter_core/tests/test_factory_manager.py +234 -0
  159. autocoder/common/linter_core/tests/test_formatters.py +147 -0
  160. autocoder/common/linter_core/tests/test_integration.py +317 -0
  161. autocoder/common/linter_core/tests/test_java_linter.py +496 -0
  162. autocoder/common/linter_core/tests/test_linters.py +265 -0
  163. autocoder/common/linter_core/tests/test_models.py +81 -0
  164. autocoder/common/linter_core/tests/verify_config_loading.py +296 -0
  165. autocoder/common/linter_core/tests/verify_fixes.py +183 -0
  166. autocoder/common/llm_friendly_package/__init__.py +31 -0
  167. autocoder/common/llm_friendly_package/base_manager.py +102 -0
  168. autocoder/common/llm_friendly_package/docs_manager.py +121 -0
  169. autocoder/common/llm_friendly_package/library_manager.py +171 -0
  170. autocoder/common/{llm_friendly_package.py → llm_friendly_package/main_manager.py} +204 -231
  171. autocoder/common/llm_friendly_package/models.py +40 -0
  172. autocoder/common/llm_friendly_package/test_llm_friendly_package.py +536 -0
  173. autocoder/common/llms/__init__.py +15 -0
  174. autocoder/common/llms/demo_error_handling.py +85 -0
  175. autocoder/common/llms/factory.py +142 -0
  176. autocoder/common/llms/manager.py +264 -0
  177. autocoder/common/llms/pricing.py +121 -0
  178. autocoder/common/llms/registry.py +288 -0
  179. autocoder/common/llms/schema.py +77 -0
  180. autocoder/common/llms/simple_demo.py +45 -0
  181. autocoder/common/llms/test_quick_model.py +116 -0
  182. autocoder/common/llms/test_remove_functionality.py +182 -0
  183. autocoder/common/llms/tests/__init__.py +1 -0
  184. autocoder/common/llms/tests/test_manager.py +330 -0
  185. autocoder/common/llms/tests/test_registry.py +364 -0
  186. autocoder/common/mcp_tools/__init__.py +62 -0
  187. autocoder/common/{mcp_tools.py → mcp_tools/executor.py} +49 -40
  188. autocoder/common/{mcp_hub.py → mcp_tools/hub.py} +42 -68
  189. autocoder/common/{mcp_server_install.py → mcp_tools/installer.py} +16 -28
  190. autocoder/common/{mcp_server.py → mcp_tools/server.py} +176 -48
  191. autocoder/common/mcp_tools/test_keyboard_interrupt.py +93 -0
  192. autocoder/common/mcp_tools/test_mcp_tools.py +391 -0
  193. autocoder/common/{mcp_server_types.py → mcp_tools/types.py} +121 -48
  194. autocoder/common/mcp_tools/verify_functionality.py +202 -0
  195. autocoder/common/model_speed_tester.py +32 -26
  196. autocoder/common/priority_directory_finder/__init__.py +142 -0
  197. autocoder/common/priority_directory_finder/examples.py +230 -0
  198. autocoder/common/priority_directory_finder/finder.py +283 -0
  199. autocoder/common/priority_directory_finder/models.py +236 -0
  200. autocoder/common/priority_directory_finder/test_priority_directory_finder.py +431 -0
  201. autocoder/common/project_scanner/__init__.py +18 -0
  202. autocoder/common/project_scanner/compat.py +77 -0
  203. autocoder/common/project_scanner/scanner.py +436 -0
  204. autocoder/common/project_tracker/__init__.py +27 -0
  205. autocoder/common/project_tracker/api.py +228 -0
  206. autocoder/common/project_tracker/demo.py +272 -0
  207. autocoder/common/project_tracker/tracker.py +487 -0
  208. autocoder/common/project_tracker/types.py +53 -0
  209. autocoder/common/pruner/__init__.py +67 -0
  210. autocoder/common/pruner/agentic_conversation_pruner.py +746 -0
  211. autocoder/common/{context_pruner.py → pruner/context_pruner.py} +137 -40
  212. autocoder/common/pruner/conversation_message_ids_api.py +386 -0
  213. autocoder/common/pruner/conversation_message_ids_manager.py +347 -0
  214. autocoder/common/pruner/conversation_message_ids_pruner.py +473 -0
  215. autocoder/common/pruner/conversation_normalizer.py +347 -0
  216. autocoder/common/{conversation_pruner.py → pruner/conversation_pruner.py} +26 -6
  217. autocoder/common/pruner/test_agentic_conversation_pruner.py +784 -0
  218. autocoder/common/pruner/test_context_pruner.py +546 -0
  219. autocoder/common/pruner/test_conversation_normalizer.py +502 -0
  220. autocoder/common/pruner/test_tool_content_detector.py +324 -0
  221. autocoder/common/pruner/tool_content_detector.py +227 -0
  222. autocoder/common/pruner/tools/__init__.py +18 -0
  223. autocoder/common/pruner/tools/query_message_ids.py +264 -0
  224. autocoder/common/pruner/tools/test_agentic_pruning_logic.py +432 -0
  225. autocoder/common/pruner/tools/test_message_ids_pruning_only.py +192 -0
  226. autocoder/common/pull_requests/__init__.py +9 -1
  227. autocoder/common/pull_requests/utils.py +122 -1
  228. autocoder/common/rag_manager/rag_manager.py +36 -40
  229. autocoder/common/rulefiles/__init__.py +53 -1
  230. autocoder/common/rulefiles/api.py +250 -0
  231. autocoder/common/rulefiles/core/__init__.py +14 -0
  232. autocoder/common/rulefiles/core/manager.py +241 -0
  233. autocoder/common/rulefiles/core/selector.py +805 -0
  234. autocoder/common/rulefiles/models/__init__.py +20 -0
  235. autocoder/common/rulefiles/models/index.py +16 -0
  236. autocoder/common/rulefiles/models/init_rule.py +18 -0
  237. autocoder/common/rulefiles/models/rule_file.py +18 -0
  238. autocoder/common/rulefiles/models/rule_relevance.py +14 -0
  239. autocoder/common/rulefiles/models/summary.py +16 -0
  240. autocoder/common/rulefiles/test_rulefiles.py +776 -0
  241. autocoder/common/rulefiles/utils/__init__.py +34 -0
  242. autocoder/common/rulefiles/utils/monitor.py +86 -0
  243. autocoder/common/rulefiles/utils/parser.py +230 -0
  244. autocoder/common/save_formatted_log.py +67 -10
  245. autocoder/common/search_replace.py +8 -1
  246. autocoder/common/search_replace_patch/__init__.py +24 -0
  247. autocoder/common/search_replace_patch/base.py +115 -0
  248. autocoder/common/search_replace_patch/manager.py +248 -0
  249. autocoder/common/search_replace_patch/patch_replacer.py +304 -0
  250. autocoder/common/search_replace_patch/similarity_replacer.py +306 -0
  251. autocoder/common/search_replace_patch/string_replacer.py +181 -0
  252. autocoder/common/search_replace_patch/tests/__init__.py +3 -0
  253. autocoder/common/search_replace_patch/tests/run_tests.py +126 -0
  254. autocoder/common/search_replace_patch/tests/test_base.py +188 -0
  255. autocoder/common/search_replace_patch/tests/test_empty_line_insert.py +233 -0
  256. autocoder/common/search_replace_patch/tests/test_integration.py +389 -0
  257. autocoder/common/search_replace_patch/tests/test_manager.py +351 -0
  258. autocoder/common/search_replace_patch/tests/test_patch_replacer.py +316 -0
  259. autocoder/common/search_replace_patch/tests/test_regex_replacer.py +306 -0
  260. autocoder/common/search_replace_patch/tests/test_similarity_replacer.py +384 -0
  261. autocoder/common/shell_commands/__init__.py +197 -0
  262. autocoder/common/shell_commands/background_process_notifier.py +346 -0
  263. autocoder/common/shell_commands/command_executor.py +1127 -0
  264. autocoder/common/shell_commands/error_recovery.py +541 -0
  265. autocoder/common/shell_commands/exceptions.py +120 -0
  266. autocoder/common/shell_commands/interactive_executor.py +476 -0
  267. autocoder/common/shell_commands/interactive_pexpect_process.py +623 -0
  268. autocoder/common/shell_commands/interactive_process.py +744 -0
  269. autocoder/common/shell_commands/interactive_session_manager.py +1014 -0
  270. autocoder/common/shell_commands/monitoring.py +529 -0
  271. autocoder/common/shell_commands/process_cleanup.py +386 -0
  272. autocoder/common/shell_commands/process_manager.py +606 -0
  273. autocoder/common/shell_commands/test_interactive_pexpect_process.py +281 -0
  274. autocoder/common/shell_commands/tests/__init__.py +6 -0
  275. autocoder/common/shell_commands/tests/conftest.py +118 -0
  276. autocoder/common/shell_commands/tests/test_background_process_notifier.py +703 -0
  277. autocoder/common/shell_commands/tests/test_command_executor.py +448 -0
  278. autocoder/common/shell_commands/tests/test_error_recovery.py +305 -0
  279. autocoder/common/shell_commands/tests/test_exceptions.py +299 -0
  280. autocoder/common/shell_commands/tests/test_execute_batch.py +588 -0
  281. autocoder/common/shell_commands/tests/test_indented_batch_commands.py +244 -0
  282. autocoder/common/shell_commands/tests/test_integration.py +664 -0
  283. autocoder/common/shell_commands/tests/test_monitoring.py +546 -0
  284. autocoder/common/shell_commands/tests/test_performance.py +632 -0
  285. autocoder/common/shell_commands/tests/test_process_cleanup.py +397 -0
  286. autocoder/common/shell_commands/tests/test_process_manager.py +606 -0
  287. autocoder/common/shell_commands/tests/test_timeout_config.py +343 -0
  288. autocoder/common/shell_commands/tests/test_timeout_manager.py +520 -0
  289. autocoder/common/shell_commands/timeout_config.py +315 -0
  290. autocoder/common/shell_commands/timeout_manager.py +352 -0
  291. autocoder/common/terminal_paste/__init__.py +14 -0
  292. autocoder/common/terminal_paste/demo.py +145 -0
  293. autocoder/common/terminal_paste/demo_paste_functionality.py +95 -0
  294. autocoder/common/terminal_paste/paste_handler.py +200 -0
  295. autocoder/common/terminal_paste/paste_manager.py +118 -0
  296. autocoder/common/terminal_paste/tests/__init__.py +1 -0
  297. autocoder/common/terminal_paste/tests/test_paste_handler.py +182 -0
  298. autocoder/common/terminal_paste/tests/test_paste_manager.py +126 -0
  299. autocoder/common/terminal_paste/utils.py +163 -0
  300. autocoder/common/test_autocoder_args.py +232 -0
  301. autocoder/common/test_env_manager.py +173 -0
  302. autocoder/common/test_env_manager_integration.py +159 -0
  303. autocoder/common/text_similarity/__init__.py +9 -0
  304. autocoder/common/text_similarity/demo.py +216 -0
  305. autocoder/common/text_similarity/examples.py +266 -0
  306. autocoder/common/text_similarity/test_text_similarity.py +306 -0
  307. autocoder/common/text_similarity/text_similarity.py +194 -0
  308. autocoder/common/text_similarity/utils.py +125 -0
  309. autocoder/common/todos/__init__.py +61 -0
  310. autocoder/common/todos/cache/__init__.py +16 -0
  311. autocoder/common/todos/cache/base_cache.py +89 -0
  312. autocoder/common/todos/cache/cache_manager.py +228 -0
  313. autocoder/common/todos/cache/memory_cache.py +225 -0
  314. autocoder/common/todos/config.py +155 -0
  315. autocoder/common/todos/exceptions.py +35 -0
  316. autocoder/common/todos/get_todo_manager.py +161 -0
  317. autocoder/common/todos/manager.py +537 -0
  318. autocoder/common/todos/models.py +239 -0
  319. autocoder/common/todos/storage/__init__.py +14 -0
  320. autocoder/common/todos/storage/base_storage.py +76 -0
  321. autocoder/common/todos/storage/file_storage.py +278 -0
  322. autocoder/common/tokens/__init__.py +15 -0
  323. autocoder/common/tokens/counter.py +44 -2
  324. autocoder/common/tools_manager/__init__.py +17 -0
  325. autocoder/common/tools_manager/examples.py +162 -0
  326. autocoder/common/tools_manager/manager.py +385 -0
  327. autocoder/common/tools_manager/models.py +39 -0
  328. autocoder/common/tools_manager/test_tools_manager.py +303 -0
  329. autocoder/common/tools_manager/utils.py +191 -0
  330. autocoder/common/v2/agent/agentic_callbacks.py +270 -0
  331. autocoder/common/v2/agent/agentic_edit.py +2729 -2052
  332. autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
  333. autocoder/common/v2/agent/agentic_edit_tools/__init__.py +43 -2
  334. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
  335. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_read_tool_resolver.py +40 -0
  336. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +52 -0
  337. autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +8 -0
  338. autocoder/common/v2/agent/agentic_edit_tools/background_task_tool_resolver.py +1167 -0
  339. autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +2 -2
  340. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_read_tool_resolver.py +214 -0
  341. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_write_tool_resolver.py +299 -0
  342. autocoder/common/v2/agent/agentic_edit_tools/count_tokens_tool_resolver.py +290 -0
  343. autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +565 -30
  344. autocoder/common/v2/agent/agentic_edit_tools/execute_workflow_tool_resolver.py +485 -0
  345. autocoder/common/v2/agent/agentic_edit_tools/extract_to_text_tool_resolver.py +225 -0
  346. autocoder/common/v2/agent/agentic_edit_tools/lint_report.py +79 -0
  347. autocoder/common/v2/agent/agentic_edit_tools/linter_config_models.py +343 -0
  348. autocoder/common/v2/agent/agentic_edit_tools/linter_enabled_tool_resolver.py +189 -0
  349. autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +169 -101
  350. autocoder/common/v2/agent/agentic_edit_tools/load_extra_document_tool_resolver.py +349 -0
  351. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +244 -51
  352. autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +667 -147
  353. autocoder/common/v2/agent/agentic_edit_tools/run_named_subagents_tool_resolver.py +691 -0
  354. autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +409 -140
  355. autocoder/common/v2/agent/agentic_edit_tools/session_interactive_tool_resolver.py +115 -0
  356. autocoder/common/v2/agent/agentic_edit_tools/session_start_tool_resolver.py +190 -0
  357. autocoder/common/v2/agent/agentic_edit_tools/session_stop_tool_resolver.py +76 -0
  358. autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +209 -194
  359. autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +135 -0
  360. autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +328 -0
  361. autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +2 -2
  362. autocoder/common/v2/agent/agentic_edit_tools/web_crawl_tool_resolver.py +557 -0
  363. autocoder/common/v2/agent/agentic_edit_tools/web_search_tool_resolver.py +600 -0
  364. autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +56 -121
  365. autocoder/common/v2/agent/agentic_edit_types.py +386 -10
  366. autocoder/common/v2/agent/runner/__init__.py +31 -0
  367. autocoder/common/v2/agent/runner/base_runner.py +92 -0
  368. autocoder/common/v2/agent/runner/file_based_event_runner.py +217 -0
  369. autocoder/common/v2/agent/runner/sdk_runner.py +182 -0
  370. autocoder/common/v2/agent/runner/terminal_runner.py +396 -0
  371. autocoder/common/v2/agent/runner/tool_display.py +589 -0
  372. autocoder/common/v2/agent/test_agentic_callbacks.py +265 -0
  373. autocoder/common/v2/agent/test_agentic_edit.py +194 -0
  374. autocoder/common/v2/agent/tool_caller/__init__.py +24 -0
  375. autocoder/common/v2/agent/tool_caller/default_tool_resolver_map.py +135 -0
  376. autocoder/common/v2/agent/tool_caller/integration_test.py +172 -0
  377. autocoder/common/v2/agent/tool_caller/plugins/__init__.py +14 -0
  378. autocoder/common/v2/agent/tool_caller/plugins/base_plugin.py +126 -0
  379. autocoder/common/v2/agent/tool_caller/plugins/examples/__init__.py +13 -0
  380. autocoder/common/v2/agent/tool_caller/plugins/examples/logging_plugin.py +164 -0
  381. autocoder/common/v2/agent/tool_caller/plugins/examples/security_filter_plugin.py +198 -0
  382. autocoder/common/v2/agent/tool_caller/plugins/plugin_interface.py +141 -0
  383. autocoder/common/v2/agent/tool_caller/test_tool_caller.py +278 -0
  384. autocoder/common/v2/agent/tool_caller/tool_call_plugin_manager.py +331 -0
  385. autocoder/common/v2/agent/tool_caller/tool_caller.py +337 -0
  386. autocoder/common/v2/agent/tool_caller/usage_example.py +193 -0
  387. autocoder/common/v2/code_agentic_editblock_manager.py +4 -4
  388. autocoder/common/v2/code_auto_generate.py +136 -78
  389. autocoder/common/v2/code_auto_generate_diff.py +135 -79
  390. autocoder/common/v2/code_auto_generate_editblock.py +174 -99
  391. autocoder/common/v2/code_auto_generate_strict_diff.py +151 -71
  392. autocoder/common/v2/code_auto_merge.py +1 -1
  393. autocoder/common/v2/code_auto_merge_editblock.py +13 -1
  394. autocoder/common/v2/code_diff_manager.py +3 -3
  395. autocoder/common/v2/code_editblock_manager.py +4 -14
  396. autocoder/common/v2/code_manager.py +1 -1
  397. autocoder/common/v2/code_strict_diff_manager.py +2 -2
  398. autocoder/common/wrap_llm_hint/__init__.py +10 -0
  399. autocoder/common/wrap_llm_hint/test_wrap_llm_hint.py +1067 -0
  400. autocoder/common/wrap_llm_hint/utils.py +432 -0
  401. autocoder/common/wrap_llm_hint/wrap_llm_hint.py +323 -0
  402. autocoder/completer/__init__.py +8 -0
  403. autocoder/completer/command_completer_v2.py +1051 -0
  404. autocoder/default_project/__init__.py +501 -0
  405. autocoder/dispacher/__init__.py +4 -12
  406. autocoder/dispacher/actions/action.py +165 -7
  407. autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
  408. autocoder/index/entry.py +117 -125
  409. autocoder/{agent → index/filter}/agentic_filter.py +323 -334
  410. autocoder/index/filter/normal_filter.py +5 -11
  411. autocoder/index/filter/quick_filter.py +1 -1
  412. autocoder/index/index.py +36 -9
  413. autocoder/index/tests/__init__.py +1 -0
  414. autocoder/index/tests/run_tests.py +195 -0
  415. autocoder/index/tests/test_entry.py +303 -0
  416. autocoder/index/tests/test_index_manager.py +314 -0
  417. autocoder/index/tests/test_module_integration.py +300 -0
  418. autocoder/index/tests/test_symbols_utils.py +183 -0
  419. autocoder/inner/__init__.py +4 -0
  420. autocoder/inner/agentic.py +932 -0
  421. autocoder/inner/async_command_handler.py +992 -0
  422. autocoder/inner/conversation_command_handlers.py +623 -0
  423. autocoder/inner/merge_command_handler.py +213 -0
  424. autocoder/inner/queue_command_handler.py +684 -0
  425. autocoder/models.py +95 -266
  426. autocoder/plugins/git_helper_plugin.py +31 -29
  427. autocoder/plugins/token_helper_plugin.py +156 -37
  428. autocoder/pyproject/__init__.py +32 -29
  429. autocoder/rag/agentic_rag.py +215 -75
  430. autocoder/rag/cache/simple_cache.py +1 -2
  431. autocoder/rag/loaders/image_loader.py +1 -1
  432. autocoder/rag/long_context_rag.py +42 -26
  433. autocoder/rag/qa_conversation_strategy.py +1 -1
  434. autocoder/rag/terminal/__init__.py +17 -0
  435. autocoder/rag/terminal/args.py +581 -0
  436. autocoder/rag/terminal/bootstrap.py +61 -0
  437. autocoder/rag/terminal/command_handlers.py +653 -0
  438. autocoder/rag/terminal/formatters/__init__.py +20 -0
  439. autocoder/rag/terminal/formatters/base.py +70 -0
  440. autocoder/rag/terminal/formatters/json_format.py +66 -0
  441. autocoder/rag/terminal/formatters/stream_json.py +95 -0
  442. autocoder/rag/terminal/formatters/text.py +28 -0
  443. autocoder/rag/terminal/init.py +120 -0
  444. autocoder/rag/terminal/utils.py +106 -0
  445. autocoder/rag/test_agentic_rag.py +389 -0
  446. autocoder/rag/test_doc_filter.py +3 -3
  447. autocoder/rag/test_long_context_rag.py +1 -1
  448. autocoder/rag/test_token_limiter.py +517 -10
  449. autocoder/rag/token_counter.py +3 -0
  450. autocoder/rag/token_limiter.py +19 -15
  451. autocoder/rag/tools/__init__.py +26 -2
  452. autocoder/rag/tools/bochaai_example.py +343 -0
  453. autocoder/rag/tools/bochaai_sdk.py +541 -0
  454. autocoder/rag/tools/metaso_example.py +268 -0
  455. autocoder/rag/tools/metaso_sdk.py +417 -0
  456. autocoder/rag/tools/recall_tool.py +28 -7
  457. autocoder/rag/tools/run_integration_tests.py +204 -0
  458. autocoder/rag/tools/test_all_providers.py +318 -0
  459. autocoder/rag/tools/test_bochaai_integration.py +482 -0
  460. autocoder/rag/tools/test_final_integration.py +215 -0
  461. autocoder/rag/tools/test_metaso_integration.py +424 -0
  462. autocoder/rag/tools/test_metaso_real.py +171 -0
  463. autocoder/rag/tools/test_web_crawl_tool.py +639 -0
  464. autocoder/rag/tools/test_web_search_tool.py +509 -0
  465. autocoder/rag/tools/todo_read_tool.py +202 -0
  466. autocoder/rag/tools/todo_write_tool.py +412 -0
  467. autocoder/rag/tools/web_crawl_tool.py +634 -0
  468. autocoder/rag/tools/web_search_tool.py +558 -0
  469. autocoder/rag/tools/web_tools_example.py +119 -0
  470. autocoder/rag/types.py +16 -0
  471. autocoder/rag/variable_holder.py +4 -2
  472. autocoder/rags.py +86 -79
  473. autocoder/regexproject/__init__.py +23 -21
  474. autocoder/run_context.py +9 -0
  475. autocoder/sdk/__init__.py +50 -161
  476. autocoder/sdk/api.py +370 -0
  477. autocoder/sdk/async_runner/__init__.py +26 -0
  478. autocoder/sdk/async_runner/async_executor.py +650 -0
  479. autocoder/sdk/async_runner/async_handler.py +356 -0
  480. autocoder/sdk/async_runner/markdown_processor.py +595 -0
  481. autocoder/sdk/async_runner/task_metadata.py +284 -0
  482. autocoder/sdk/async_runner/worktree_manager.py +438 -0
  483. autocoder/sdk/cli/__init__.py +2 -5
  484. autocoder/sdk/cli/formatters.py +28 -204
  485. autocoder/sdk/cli/handlers.py +77 -44
  486. autocoder/sdk/cli/main.py +158 -170
  487. autocoder/sdk/cli/options.py +95 -22
  488. autocoder/sdk/constants.py +139 -51
  489. autocoder/sdk/core/auto_coder_core.py +484 -267
  490. autocoder/sdk/core/bridge.py +298 -118
  491. autocoder/sdk/exceptions.py +18 -12
  492. autocoder/sdk/formatters/__init__.py +19 -0
  493. autocoder/sdk/formatters/input.py +64 -0
  494. autocoder/sdk/formatters/output.py +247 -0
  495. autocoder/sdk/formatters/stream.py +54 -0
  496. autocoder/sdk/models/__init__.py +6 -5
  497. autocoder/sdk/models/options.py +55 -18
  498. autocoder/sdk/utils/formatters.py +27 -195
  499. autocoder/suffixproject/__init__.py +28 -25
  500. autocoder/terminal/__init__.py +14 -0
  501. autocoder/terminal/app.py +454 -0
  502. autocoder/terminal/args.py +32 -0
  503. autocoder/terminal/bootstrap.py +178 -0
  504. autocoder/terminal/command_processor.py +521 -0
  505. autocoder/terminal/command_registry.py +57 -0
  506. autocoder/terminal/help.py +97 -0
  507. autocoder/terminal/tasks/__init__.py +5 -0
  508. autocoder/terminal/tasks/background.py +77 -0
  509. autocoder/terminal/tasks/task_event.py +70 -0
  510. autocoder/terminal/ui/__init__.py +13 -0
  511. autocoder/terminal/ui/completer.py +268 -0
  512. autocoder/terminal/ui/keybindings.py +75 -0
  513. autocoder/terminal/ui/session.py +41 -0
  514. autocoder/terminal/ui/toolbar.py +64 -0
  515. autocoder/terminal/utils/__init__.py +13 -0
  516. autocoder/terminal/utils/errors.py +18 -0
  517. autocoder/terminal/utils/paths.py +19 -0
  518. autocoder/terminal/utils/shell.py +43 -0
  519. autocoder/terminal_v3/__init__.py +10 -0
  520. autocoder/terminal_v3/app.py +201 -0
  521. autocoder/terminal_v3/handlers/__init__.py +5 -0
  522. autocoder/terminal_v3/handlers/command_handler.py +131 -0
  523. autocoder/terminal_v3/models/__init__.py +6 -0
  524. autocoder/terminal_v3/models/conversation_buffer.py +214 -0
  525. autocoder/terminal_v3/models/message.py +50 -0
  526. autocoder/terminal_v3/models/tool_display.py +247 -0
  527. autocoder/terminal_v3/ui/__init__.py +7 -0
  528. autocoder/terminal_v3/ui/keybindings.py +56 -0
  529. autocoder/terminal_v3/ui/layout.py +141 -0
  530. autocoder/terminal_v3/ui/styles.py +43 -0
  531. autocoder/tsproject/__init__.py +23 -23
  532. autocoder/utils/auto_coder_utils/chat_stream_out.py +1 -1
  533. autocoder/utils/llms.py +88 -80
  534. autocoder/utils/math_utils.py +101 -0
  535. autocoder/utils/model_provider_selector.py +16 -4
  536. autocoder/utils/operate_config_api.py +33 -5
  537. autocoder/utils/thread_utils.py +2 -2
  538. autocoder/version.py +4 -2
  539. autocoder/workflow_agents/__init__.py +84 -0
  540. autocoder/workflow_agents/agent.py +143 -0
  541. autocoder/workflow_agents/exceptions.py +573 -0
  542. autocoder/workflow_agents/executor.py +489 -0
  543. autocoder/workflow_agents/loader.py +737 -0
  544. autocoder/workflow_agents/runner.py +267 -0
  545. autocoder/workflow_agents/types.py +172 -0
  546. autocoder/workflow_agents/utils.py +434 -0
  547. autocoder/workflow_agents/workflow_manager.py +211 -0
  548. auto_coder-0.1.400.dist-info/METADATA +0 -396
  549. auto_coder-0.1.400.dist-info/RECORD +0 -425
  550. auto_coder-0.1.400.dist-info/licenses/LICENSE +0 -201
  551. autocoder/auto_coder_server.py +0 -672
  552. autocoder/benchmark.py +0 -138
  553. autocoder/common/ac_style_command_parser/example.py +0 -7
  554. autocoder/common/cleaner.py +0 -31
  555. autocoder/common/command_completer_v2.py +0 -615
  556. autocoder/common/directory_cache/__init__.py +0 -1
  557. autocoder/common/directory_cache/cache.py +0 -192
  558. autocoder/common/directory_cache/test_cache.py +0 -190
  559. autocoder/common/file_checkpoint/examples.py +0 -217
  560. autocoder/common/llm_friendly_package_example.py +0 -138
  561. autocoder/common/llm_friendly_package_test.py +0 -63
  562. autocoder/common/pull_requests/test_module.py +0 -1
  563. autocoder/common/rulefiles/autocoderrules_utils.py +0 -484
  564. autocoder/common/text.py +0 -30
  565. autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +0 -42
  566. autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.py +0 -70
  567. autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +0 -163
  568. autocoder/common/v2/agent/agentic_tool_display.py +0 -183
  569. autocoder/plugins/dynamic_completion_example.py +0 -148
  570. autocoder/plugins/sample_plugin.py +0 -160
  571. autocoder/sdk/cli/__main__.py +0 -26
  572. autocoder/sdk/cli/completion_wrapper.py +0 -38
  573. autocoder/sdk/cli/install_completion.py +0 -301
  574. autocoder/sdk/models/messages.py +0 -209
  575. autocoder/sdk/session/__init__.py +0 -32
  576. autocoder/sdk/session/session.py +0 -106
  577. autocoder/sdk/session/session_manager.py +0 -56
  578. {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/top_level.txt +0 -0
  579. /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
@@ -0,0 +1,557 @@
1
+ """
2
+ WebCrawlToolResolver Module
3
+
4
+ This module implements the WebCrawlToolResolver class for providing
5
+ web crawling functionality based on Firecrawl, Metaso in the AgenticEdit framework.
6
+ Supports concurrent crawling with multiple API keys using thread pools.
7
+ """
8
+
9
+ import os
10
+ import traceback
11
+ import time
12
+ import json
13
+ from typing import Dict, Any, List, Optional
14
+ from concurrent.futures import ThreadPoolExecutor, as_completed
15
+ from threading import Lock
16
+
17
+ from loguru import logger
18
+
19
+ from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver
20
+ from autocoder.common.v2.agent.agentic_edit_types import WebCrawlTool, ToolResult
21
+ from autocoder.common import AutoCoderArgs
22
+ import typing
23
+
24
+ if typing.TYPE_CHECKING:
25
+ from autocoder.common.v2.agent.agentic_edit import AgenticEdit
26
+
27
+
28
+ class WebCrawlToolResolver(BaseToolResolver):
29
+ """Web crawling tool resolver implementing concurrent crawling logic"""
30
+
31
+ def __init__(self, agent: Optional['AgenticEdit'], tool: WebCrawlTool, args: AutoCoderArgs):
32
+ super().__init__(agent, tool, args)
33
+ self.tool: WebCrawlTool = tool
34
+ self._results_lock = Lock()
35
+ self._all_results = []
36
+
37
+ def _get_available_providers(self) -> List[Dict[str, Any]]:
38
+ """Get all available provider configurations"""
39
+ providers = []
40
+
41
+ # Check Metaso keys
42
+ metaso_keys = []
43
+ if self.args.metaso_api_key:
44
+ if ',' in self.args.metaso_api_key:
45
+ metaso_keys = [key.strip() for key in self.args.metaso_api_key.split(',') if key.strip()]
46
+ else:
47
+ metaso_keys = [self.args.metaso_api_key]
48
+ elif os.getenv('METASO_API_KEY'):
49
+ env_keys = os.getenv('METASO_API_KEY')
50
+ if ',' in env_keys:
51
+ metaso_keys = [key.strip() for key in env_keys.split(',') if key.strip()]
52
+ else:
53
+ metaso_keys = [env_keys]
54
+
55
+ for key in metaso_keys:
56
+ providers.append({
57
+ 'type': 'metaso',
58
+ 'api_key': key,
59
+ 'priority': 2
60
+ })
61
+
62
+ # Check Firecrawl keys
63
+ firecrawl_keys = []
64
+ if self.args.firecrawl_api_key:
65
+ if ',' in self.args.firecrawl_api_key:
66
+ firecrawl_keys = [key.strip() for key in self.args.firecrawl_api_key.split(',') if key.strip()]
67
+ else:
68
+ firecrawl_keys = [self.args.firecrawl_api_key]
69
+ elif os.getenv('FIRECRAWL_API_KEY'):
70
+ env_keys = os.getenv('FIRECRAWL_API_KEY')
71
+ if ',' in env_keys:
72
+ firecrawl_keys = [key.strip() for key in env_keys.split(',') if key.strip()]
73
+ else:
74
+ firecrawl_keys = [env_keys]
75
+
76
+ for key in firecrawl_keys:
77
+ providers.append({
78
+ 'type': 'firecrawl',
79
+ 'api_key': key,
80
+ 'priority': 3
81
+ })
82
+
83
+ # Sort by priority
84
+ providers.sort(key=lambda x: x['priority'])
85
+ return providers
86
+
87
+ def _crawl_with_metaso(self, api_key: str) -> ToolResult:
88
+ """Use Metaso for crawling (single page reading or multi-page crawling)"""
89
+ logger.info(f"🔍 Starting Metaso crawl (key: ...{api_key[-4:]}): {self.tool.url}")
90
+ try:
91
+ # Dynamically import to avoid dependency issues
92
+ try:
93
+ from autocoder.rag.tools.metaso_sdk import MetasoClient
94
+ except ImportError:
95
+ return ToolResult(
96
+ success=False,
97
+ message="Metaso SDK not installed, please check dependencies",
98
+ content=[]
99
+ )
100
+
101
+ # Initialize Metaso client
102
+ client = MetasoClient(api_key=api_key)
103
+
104
+ # Prepare crawling parameters
105
+ exclude_paths_list = None
106
+ if self.tool.exclude_paths:
107
+ exclude_paths_list = [p.strip() for p in self.tool.exclude_paths.split(',') if p.strip()]
108
+
109
+ include_paths_list = None
110
+ if self.tool.include_paths:
111
+ include_paths_list = [p.strip() for p in self.tool.include_paths.split(',') if p.strip()]
112
+
113
+ allow_subdomains = self.tool.allow_subdomains.lower() == "true"
114
+
115
+ # If only crawling one page (limit=1), use read method directly
116
+ if self.tool.limit == 1:
117
+ logger.info(f"Using Metaso to read single page: {self.tool.url}")
118
+ content = client.read(self.tool.url, format="text/plain")
119
+
120
+ if content.startswith("Error:"):
121
+ return ToolResult(
122
+ success=False,
123
+ message=f"Metaso reading failed: {content}",
124
+ content=[]
125
+ )
126
+
127
+ result_item = {
128
+ "url": self.tool.url,
129
+ "title": "",
130
+ "content": content,
131
+ "markdown": content,
132
+ "links": [],
133
+ "metadata": {
134
+ "source": "metaso",
135
+ "api_key_suffix": api_key[-4:] if len(api_key) > 4 else "****"
136
+ }
137
+ }
138
+
139
+ return ToolResult(
140
+ success=True,
141
+ message=f"Successfully read 1 page (using Metaso)",
142
+ content=[result_item]
143
+ )
144
+
145
+ # Multi-page crawling
146
+ logger.info(f"Using Metaso to start web crawling, URL: {self.tool.url}")
147
+ crawl_results = client.crawl(
148
+ url=self.tool.url,
149
+ limit=self.tool.limit,
150
+ max_depth=self.tool.max_depth,
151
+ exclude_paths=exclude_paths_list,
152
+ include_paths=include_paths_list,
153
+ allow_subdomains=allow_subdomains
154
+ )
155
+
156
+ if not crawl_results:
157
+ return ToolResult(
158
+ success=False,
159
+ message="Metaso crawling returned no results",
160
+ content=[]
161
+ )
162
+
163
+ # Add API key identifier to each result
164
+ for result in crawl_results:
165
+ if 'metadata' not in result:
166
+ result['metadata'] = {}
167
+ result['metadata']['api_key_suffix'] = api_key[-4:] if len(api_key) > 4 else "****"
168
+
169
+ return ToolResult(
170
+ success=True,
171
+ message=f"Successfully crawled {len(crawl_results)} pages (using Metaso)",
172
+ content=crawl_results
173
+ )
174
+
175
+ except Exception as e:
176
+ logger.error(f"❌ Metaso crawling failed (key: ...{api_key[-4:]}): {str(e)}")
177
+ return ToolResult(
178
+ success=False,
179
+ message=f"Metaso crawling failed (key: ...{api_key[-4:]}): {str(e)}",
180
+ content=[]
181
+ )
182
+
183
+ def _crawl_with_firecrawl(self, api_key: str) -> ToolResult:
184
+ """Use Firecrawl for crawling with simplified API"""
185
+ logger.info(f"🔥 Starting Firecrawl crawl (key: ...{api_key[-4:]}): {self.tool.url}")
186
+ try:
187
+ # Import Firecrawl SDK - try multiple import options
188
+ try:
189
+ from firecrawl import FirecrawlApp
190
+ use_app_class = True
191
+ except ImportError:
192
+ try:
193
+ from firecrawl import Firecrawl
194
+ use_app_class = False
195
+ except ImportError:
196
+ return ToolResult(
197
+ success=False,
198
+ message="Firecrawl SDK not installed, please run: pip install firecrawl-py",
199
+ content=[]
200
+ )
201
+
202
+ # Initialize Firecrawl client using the available class
203
+ if use_app_class:
204
+ firecrawl = FirecrawlApp(api_key=api_key)
205
+ logger.info("Using FirecrawlApp class")
206
+ else:
207
+ firecrawl = Firecrawl(api_key=api_key)
208
+ logger.info("Using Firecrawl class")
209
+
210
+ # For single page (limit=1), use simple scrape method
211
+ if self.tool.limit == 1:
212
+ logger.info("Using simple scrape for single page")
213
+
214
+ # Both FirecrawlApp and Firecrawl use the same 'scrape' method
215
+ scrape_result = firecrawl.scrape(self.tool.url, formats=['markdown'])
216
+
217
+ # Process single page result
218
+ if not scrape_result:
219
+ return ToolResult(
220
+ success=False,
221
+ message="Firecrawl scrape returned empty result",
222
+ content=[]
223
+ )
224
+
225
+ # Convert to consistent format - scrape_result is a Document object
226
+ result_item = {
227
+ "url": self.tool.url,
228
+ "title": getattr(scrape_result.metadata, 'title', '') if scrape_result.metadata else '',
229
+ "content": scrape_result.markdown or '',
230
+ "markdown": scrape_result.markdown or '',
231
+ "links": scrape_result.links or [],
232
+ "metadata": {
233
+ "source": "firecrawl",
234
+ "api_key_suffix": api_key[-4:] if len(api_key) > 4 else "****"
235
+ }
236
+ }
237
+
238
+ # Add original metadata if available
239
+ if scrape_result.metadata:
240
+ result_item['metadata'].update(scrape_result.metadata.dict() if hasattr(scrape_result.metadata, 'dict') else {})
241
+
242
+ return ToolResult(
243
+ success=True,
244
+ message=f"Successfully scraped 1 page using Firecrawl (key: ...{api_key[-4:]})",
245
+ content=[result_item]
246
+ )
247
+
248
+ # Multi-page crawling using the unified 'crawl' method
249
+ logger.info(f"Using multi-page crawling for {self.tool.limit} pages")
250
+
251
+ try:
252
+ # Prepare crawl parameters
253
+ crawl_options = {}
254
+
255
+ if self.tool.max_depth is not None:
256
+ crawl_options['max_discovery_depth'] = self.tool.max_depth
257
+ if self.tool.limit is not None:
258
+ crawl_options['limit'] = self.tool.limit
259
+ if self.tool.exclude_paths:
260
+ crawl_options['exclude_paths'] = [p.strip() for p in self.tool.exclude_paths.split(',') if p.strip()]
261
+ if self.tool.include_paths:
262
+ crawl_options['include_paths'] = [p.strip() for p in self.tool.include_paths.split(',') if p.strip()]
263
+ if self.tool.allow_subdomains is not None:
264
+ crawl_options['allow_subdomains'] = self.tool.allow_subdomains.lower() == "true"
265
+ if self.tool.crawl_entire_domain is not None:
266
+ crawl_options['crawl_entire_domain'] = self.tool.crawl_entire_domain.lower() == "true"
267
+
268
+ logger.info(f"Starting crawl with options: {crawl_options}")
269
+
270
+ # Both FirecrawlApp and Firecrawl use the same 'crawl' method
271
+ crawl_result = firecrawl.crawl(self.tool.url, **crawl_options)
272
+
273
+ # Process crawl results - crawl_result is a CrawlJob object
274
+ crawl_results = []
275
+
276
+ # CrawlJob.data contains List[Document]
277
+ data = crawl_result.data or []
278
+ logger.info(f"Processing {len(data)} crawled documents")
279
+
280
+ for doc in data:
281
+ # doc is a Document object
282
+ result_item = {
283
+ "url": getattr(doc.metadata, 'source_url', '') if doc.metadata else '',
284
+ "title": getattr(doc.metadata, 'title', '') if doc.metadata else '',
285
+ "content": doc.markdown or '',
286
+ "markdown": doc.markdown or '',
287
+ "links": doc.links or [],
288
+ "metadata": {
289
+ "source": "firecrawl",
290
+ "api_key_suffix": api_key[-4:] if len(api_key) > 4 else "****"
291
+ }
292
+ }
293
+
294
+ # Add original metadata if available
295
+ if doc.metadata:
296
+ try:
297
+ if hasattr(doc.metadata, 'dict'):
298
+ result_item['metadata'].update(doc.metadata.dict())
299
+ elif hasattr(doc.metadata, '__dict__'):
300
+ result_item['metadata'].update(doc.metadata.__dict__)
301
+ except Exception as e:
302
+ logger.warning(f"Failed to extract metadata: {e}")
303
+
304
+ crawl_results.append(result_item)
305
+
306
+ return ToolResult(
307
+ success=True,
308
+ message=f"Successfully crawled {len(crawl_results)} pages using Firecrawl (key: ...{api_key[-4:]})",
309
+ content=crawl_results
310
+ )
311
+
312
+ except Exception as crawl_error:
313
+ logger.error(f"Multi-page crawling failed: {crawl_error}")
314
+ # Fallback to single page scrape
315
+ logger.info("Attempting fallback to single page scrape")
316
+ try:
317
+ scrape_result = firecrawl.scrape(self.tool.url, formats=['markdown'])
318
+
319
+ result_item = {
320
+ "url": self.tool.url,
321
+ "title": getattr(scrape_result.metadata, 'title', '') if scrape_result.metadata else '',
322
+ "content": scrape_result.markdown or '',
323
+ "markdown": scrape_result.markdown or '',
324
+ "links": scrape_result.links or [],
325
+ "metadata": {
326
+ "source": "firecrawl",
327
+ "api_key_suffix": api_key[-4:] if len(api_key) > 4 else "****"
328
+ }
329
+ }
330
+
331
+ # Add original metadata if available
332
+ if scrape_result.metadata:
333
+ result_item['metadata'].update(scrape_result.metadata.dict() if hasattr(scrape_result.metadata, 'dict') else {})
334
+
335
+ return ToolResult(
336
+ success=True,
337
+ message=f"Fallback successful: scraped 1 page using Firecrawl (key: ...{api_key[-4:]}) - multi-page failed: {str(crawl_error)}",
338
+ content=[result_item]
339
+ )
340
+ except Exception as fallback_error:
341
+ return ToolResult(
342
+ success=False,
343
+ message=f"Both crawling and fallback failed. Crawl error: {str(crawl_error)}, Fallback error: {str(fallback_error)}",
344
+ content=[]
345
+ )
346
+
347
+ except Exception as e:
348
+ logger.error(f"❌ Firecrawl crawling failed (key: ...{api_key[-4:]}): {str(e)}")
349
+ return ToolResult(
350
+ success=False,
351
+ message=f"Firecrawl crawling failed (key: ...{api_key[-4:]}): {str(e)}",
352
+ content=[]
353
+ )
354
+
355
+ def _crawl_with_provider(self, provider: Dict[str, Any]) -> ToolResult:
356
+ """Use specified provider for crawling"""
357
+ provider_type = provider['type']
358
+ api_key = provider['api_key']
359
+
360
+ if provider_type == 'metaso':
361
+ return self._crawl_with_metaso(api_key)
362
+ elif provider_type == 'firecrawl':
363
+ return self._crawl_with_firecrawl(api_key)
364
+ else:
365
+ return ToolResult(
366
+ success=False,
367
+ message=f"Unsupported provider type: {provider_type}",
368
+ content=[]
369
+ )
370
+
371
+ def _merge_results(self, results: List[ToolResult]) -> ToolResult:
372
+ """Merge multiple crawling results"""
373
+ successful_results = [r for r in results if r.success]
374
+ failed_results = [r for r in results if not r.success]
375
+
376
+ logger.info(f"📄 Merging results: {len(successful_results)} successful, {len(failed_results)} failed")
377
+
378
+ if not successful_results:
379
+ # All requests failed
380
+ error_messages = [r.message for r in failed_results]
381
+ return ToolResult(
382
+ success=False,
383
+ message=f"All crawling requests failed: {'; '.join(error_messages)}",
384
+ content=[]
385
+ )
386
+
387
+ # Merge successful results
388
+ all_content = []
389
+ total_pages = 0
390
+ providers_used = set()
391
+
392
+ for result in successful_results:
393
+ if result.content:
394
+ all_content.extend(result.content)
395
+ total_pages += len(result.content)
396
+ # Extract provider information from results
397
+ for item in result.content:
398
+ if 'metadata' in item and 'source' in item['metadata']:
399
+ providers_used.add(item['metadata']['source'])
400
+ elif 'metadata' in item and 'api_key_suffix' in item['metadata']:
401
+ # Infer provider from API key suffix
402
+ if 'metaso' in result.message.lower():
403
+ providers_used.add('metaso')
404
+ elif 'firecrawl' in result.message.lower():
405
+ providers_used.add('firecrawl')
406
+
407
+ # Deduplicate (based on URL + source combination)
408
+ # Keep results from different sources even if they have the same URL
409
+ seen_url_source_pairs = set()
410
+ unique_content = []
411
+ for item in all_content:
412
+ url = item.get('url', '')
413
+ source = item.get('metadata', {}).get('source', 'unknown')
414
+ url_source_key = (url, source)
415
+
416
+ if url and url_source_key not in seen_url_source_pairs:
417
+ seen_url_source_pairs.add(url_source_key)
418
+ unique_content.append(item)
419
+ elif not url: # If no URL, also keep it
420
+ unique_content.append(item)
421
+
422
+ # If we have multiple results for the same URL from different sources, log it
423
+ url_count = {}
424
+ for item in unique_content:
425
+ url = item.get('url', '')
426
+ if url:
427
+ url_count[url] = url_count.get(url, 0) + 1
428
+
429
+ multi_source_urls = [url for url, count in url_count.items() if count > 1]
430
+ if multi_source_urls:
431
+ logger.info(f"📊 Found {len(multi_source_urls)} URLs crawled by multiple sources: {multi_source_urls[:3]}{'...' if len(multi_source_urls) > 3 else ''}")
432
+
433
+ providers_str = ', '.join(sorted(providers_used)) if providers_used else 'unknown'
434
+ success_count = len(successful_results)
435
+ fail_count = len(failed_results)
436
+
437
+ message = f"Successfully crawled {len(unique_content)} pages (using {providers_str})"
438
+ if fail_count > 0:
439
+ # 收集失败原因的详细信息
440
+ failed_details = []
441
+ for failed_result in failed_results:
442
+ # 尝试从错误消息中提取 provider 信息
443
+ if 'metaso' in failed_result.message.lower():
444
+ failed_details.append(f"Metaso: {failed_result.message}")
445
+ elif 'firecrawl' in failed_result.message.lower():
446
+ failed_details.append(f"Firecrawl: {failed_result.message}")
447
+ else:
448
+ failed_details.append(f"Unknown: {failed_result.message}")
449
+
450
+ message += f", {fail_count} API keys failed"
451
+ if failed_details:
452
+ logger.warning(f"❌ 失败的 API 详情: {'; '.join(failed_details)}")
453
+ # 在消息中也包含失败详情(但保持简洁)
454
+ if len(failed_details) == 1:
455
+ message += f" ({failed_details[0]})"
456
+
457
+ return ToolResult(
458
+ success=True,
459
+ message=message,
460
+ content=unique_content
461
+ )
462
+
463
+ def resolve(self) -> ToolResult:
464
+ """Implement web crawling tool resolution logic, supporting multi-key concurrency"""
465
+ try:
466
+ # Get all available providers
467
+ providers = self._get_available_providers()
468
+
469
+ if not providers:
470
+ # No API key configured, guide to use curl
471
+ curl_command = f"curl -s -L '{self.tool.url}'"
472
+
473
+ # Add more curl options based on parameters
474
+ curl_suggestions = []
475
+ curl_suggestions.append(f"curl -s -L '{self.tool.url}' # Basic web content retrieval")
476
+ curl_suggestions.append(f"curl -s -L -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' '{self.tool.url}' # Add user agent")
477
+ curl_suggestions.append(f"curl -s -L --max-time 30 '{self.tool.url}' # Set timeout")
478
+ curl_suggestions.append(f"curl -s -L -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' '{self.tool.url}' # Set Accept header")
479
+
480
+ if self.tool.limit and self.tool.limit == 1:
481
+ curl_suggestions.append(f"# For single page scraping, you can use curl directly")
482
+ else:
483
+ curl_suggestions.append(f"# For multi-page crawling, recommend using wget recursive download:")
484
+ max_depth = self.tool.max_depth or 2
485
+ wget_cmd = f"wget --recursive --level={max_depth} --no-parent --reject='*.css,*.js,*.png,*.jpg,*.gif,*.pdf' --user-agent='Mozilla/5.0' '{self.tool.url}'"
486
+ curl_suggestions.append(wget_cmd)
487
+
488
+ if self.tool.exclude_paths:
489
+ curl_suggestions.append(f"# Exclude paths: {self.tool.exclude_paths}")
490
+ curl_suggestions.append(f"# Can use wget's --exclude-directories option")
491
+
492
+ if self.tool.include_paths:
493
+ curl_suggestions.append(f"# Include paths: {self.tool.include_paths}")
494
+ curl_suggestions.append(f"# Can use wget's --include-directories option")
495
+
496
+ suggestion_text = "\n".join(curl_suggestions)
497
+
498
+ return ToolResult(
499
+ success=False,
500
+ message=f"No web crawling API key configured (Metaso, Firecrawl). Recommend using curl or wget commands to get web content:\n\n{suggestion_text}\n\nConfiguration instructions:\n- Metaso: Set --metaso_api_key or METASO_API_KEY environment variable\n- Firecrawl: Set --firecrawl_api_key or FIRECRAWL_API_KEY environment variable",
501
+ content={
502
+ "suggested_commands": curl_suggestions,
503
+ "target_url": self.tool.url,
504
+ "curl_basic": curl_command,
505
+ "wget_recursive": f"wget --recursive --level={self.tool.max_depth or 2} --no-parent --reject='*.css,*.js,*.png,*.jpg,*.gif,*.pdf' --user-agent='Mozilla/5.0' '{self.tool.url}'"
506
+ }
507
+ )
508
+
509
+ logger.info(f"🚀 Found {len(providers)} available API configurations, starting concurrent crawling")
510
+ for i, provider in enumerate(providers, 1):
511
+ logger.info(f" {i}. {provider['type'].upper()} API (key: ...{provider['api_key'][-4:]})")
512
+
513
+ # If only one provider, call directly
514
+ if len(providers) == 1:
515
+ logger.info(f"📝 Using single provider: {providers[0]['type'].upper()}")
516
+ return self._crawl_with_provider(providers[0])
517
+
518
+ # Use thread pool for concurrent execution with multiple providers
519
+ logger.info(f"🏁 Starting concurrent execution with {len(providers)} providers")
520
+ results = []
521
+ max_workers = min(len(providers), 5) # Limit maximum concurrency
522
+
523
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
524
+ # Submit all tasks
525
+ future_to_provider = {
526
+ executor.submit(self._crawl_with_provider, provider): provider
527
+ for provider in providers
528
+ }
529
+
530
+ # Collect results
531
+ for future in as_completed(future_to_provider):
532
+ provider = future_to_provider[future]
533
+ try:
534
+ result = future.result(timeout=300) # 5 minute timeout
535
+ results.append(result)
536
+ if result.success:
537
+ logger.info(f"✅ Provider {provider['type']} (key: ...{provider['api_key'][-4:]}) completed successfully")
538
+ else:
539
+ logger.warning(f"❌ Provider {provider['type']} (key: ...{provider['api_key'][-4:]}) failed: {result.message}")
540
+ except Exception as e:
541
+ logger.error(f"❌ Provider {provider['type']} (key: ...{provider['api_key'][-4:]}) execution exception: {str(e)}")
542
+ results.append(ToolResult(
543
+ success=False,
544
+ message=f"Provider {provider['type']} execution exception: {str(e)}",
545
+ content=[]
546
+ ))
547
+
548
+ # Merge results
549
+ return self._merge_results(results)
550
+
551
+ except Exception as e:
552
+ logger.error(f"Web crawling tool execution failed: {str(e)}")
553
+ return ToolResult(
554
+ success=False,
555
+ message=f"Web crawling tool execution failed: {str(e)}",
556
+ content=traceback.format_exc()
557
+ )