auto-coder 1.0.0__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.
- auto_coder-2.0.0.dist-info/LICENSE +158 -0
- auto_coder-2.0.0.dist-info/METADATA +558 -0
- auto_coder-2.0.0.dist-info/RECORD +795 -0
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/entry_points.txt +3 -3
- autocoder/__init__.py +31 -0
- autocoder/agent/auto_filegroup.py +32 -13
- autocoder/agent/auto_learn_from_commit.py +9 -1
- autocoder/agent/base_agentic/__init__.py +3 -0
- autocoder/agent/base_agentic/agent_hub.py +1 -1
- autocoder/agent/base_agentic/base_agent.py +235 -136
- autocoder/agent/base_agentic/default_tools.py +119 -118
- autocoder/agent/base_agentic/test_base_agent.py +1 -1
- autocoder/agent/base_agentic/tool_registry.py +32 -20
- autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +24 -3
- autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +24 -11
- autocoder/agent/base_agentic/types.py +42 -0
- autocoder/agent/entry_command_agent/chat.py +73 -59
- autocoder/auto_coder.py +31 -40
- autocoder/auto_coder_rag.py +11 -1084
- autocoder/auto_coder_runner.py +970 -2345
- autocoder/auto_coder_terminal.py +26 -0
- autocoder/auto_coder_terminal_v3.py +190 -0
- autocoder/chat/conf_command.py +224 -124
- autocoder/chat/models_command.py +361 -299
- autocoder/chat/rules_command.py +79 -31
- autocoder/chat_auto_coder.py +988 -398
- autocoder/chat_auto_coder_lang.py +23 -732
- autocoder/commands/auto_command.py +25 -8
- autocoder/commands/auto_web.py +1 -1
- autocoder/commands/tools.py +44 -44
- autocoder/common/__init__.py +150 -128
- autocoder/common/ac_style_command_parser/__init__.py +39 -2
- autocoder/common/ac_style_command_parser/config.py +422 -0
- autocoder/common/ac_style_command_parser/parser.py +292 -78
- autocoder/common/ac_style_command_parser/test_parser.py +241 -16
- autocoder/common/ac_style_command_parser/test_typed_parser.py +342 -0
- autocoder/common/ac_style_command_parser/typed_parser.py +653 -0
- autocoder/common/action_yml_file_manager.py +25 -13
- autocoder/common/agent_events/__init__.py +52 -0
- autocoder/common/agent_events/agent_event_emitter.py +193 -0
- autocoder/common/agent_events/event_factory.py +177 -0
- autocoder/common/agent_events/examples.py +307 -0
- autocoder/common/agent_events/types.py +113 -0
- autocoder/common/agent_events/utils.py +68 -0
- autocoder/common/agent_hooks/__init__.py +44 -0
- autocoder/common/agent_hooks/examples.py +582 -0
- autocoder/common/agent_hooks/hook_executor.py +217 -0
- autocoder/common/agent_hooks/hook_manager.py +288 -0
- autocoder/common/agent_hooks/types.py +133 -0
- autocoder/common/agent_hooks/utils.py +99 -0
- autocoder/common/agent_query_queue/queue_executor.py +324 -0
- autocoder/common/agent_query_queue/queue_manager.py +325 -0
- autocoder/common/agents/__init__.py +11 -0
- autocoder/common/agents/agent_manager.py +323 -0
- autocoder/common/agents/agent_parser.py +189 -0
- autocoder/common/agents/example_usage.py +344 -0
- autocoder/common/agents/integration_example.py +330 -0
- autocoder/common/agents/test_agent_parser.py +545 -0
- autocoder/common/async_utils.py +101 -0
- autocoder/common/auto_coder_lang.py +23 -972
- autocoder/common/autocoderargs_parser/__init__.py +14 -0
- autocoder/common/autocoderargs_parser/parser.py +184 -0
- autocoder/common/autocoderargs_parser/tests/__init__.py +1 -0
- autocoder/common/autocoderargs_parser/tests/test_args_parser.py +235 -0
- autocoder/common/autocoderargs_parser/tests/test_token_parser.py +195 -0
- autocoder/common/autocoderargs_parser/token_parser.py +290 -0
- autocoder/common/buildin_tokenizer.py +2 -4
- autocoder/common/code_auto_generate.py +149 -74
- autocoder/common/code_auto_generate_diff.py +163 -70
- autocoder/common/code_auto_generate_editblock.py +179 -89
- autocoder/common/code_auto_generate_strict_diff.py +167 -72
- autocoder/common/code_auto_merge_editblock.py +13 -6
- autocoder/common/code_modification_ranker.py +1 -1
- autocoder/common/command_completer.py +3 -3
- autocoder/common/command_file_manager/manager.py +183 -47
- autocoder/common/command_file_manager/test_command_file_manager.py +507 -0
- autocoder/common/command_templates.py +1 -1
- autocoder/common/conf_utils.py +2 -4
- autocoder/common/conversations/config.py +11 -3
- autocoder/common/conversations/get_conversation_manager.py +100 -2
- autocoder/common/conversations/llm_stats_models.py +264 -0
- autocoder/common/conversations/manager.py +112 -28
- autocoder/common/conversations/models.py +16 -2
- autocoder/common/conversations/storage/index_manager.py +134 -10
- autocoder/common/core_config/__init__.py +63 -0
- autocoder/common/core_config/agentic_mode_manager.py +109 -0
- autocoder/common/core_config/base_manager.py +123 -0
- autocoder/common/core_config/compatibility.py +151 -0
- autocoder/common/core_config/config_manager.py +156 -0
- autocoder/common/core_config/conversation_manager.py +31 -0
- autocoder/common/core_config/exclude_manager.py +72 -0
- autocoder/common/core_config/file_manager.py +177 -0
- autocoder/common/core_config/human_as_model_manager.py +129 -0
- autocoder/common/core_config/lib_manager.py +54 -0
- autocoder/common/core_config/main_manager.py +81 -0
- autocoder/common/core_config/mode_manager.py +126 -0
- autocoder/common/core_config/models.py +70 -0
- autocoder/common/core_config/test_memory_manager.py +1056 -0
- autocoder/common/env_manager.py +282 -0
- autocoder/common/env_manager_usage_example.py +211 -0
- autocoder/common/file_checkpoint/conversation_checkpoint.py +19 -19
- autocoder/common/file_checkpoint/manager.py +264 -48
- autocoder/common/file_checkpoint/test_backup.py +1 -18
- autocoder/common/file_checkpoint/test_manager.py +270 -1
- autocoder/common/file_checkpoint/test_store.py +1 -17
- autocoder/common/file_handler/__init__.py +23 -0
- autocoder/common/file_handler/active_context_handler.py +159 -0
- autocoder/common/file_handler/add_files_handler.py +409 -0
- autocoder/common/file_handler/chat_handler.py +180 -0
- autocoder/common/file_handler/coding_handler.py +401 -0
- autocoder/common/file_handler/commit_handler.py +200 -0
- autocoder/common/file_handler/lib_handler.py +156 -0
- autocoder/common/file_handler/list_files_handler.py +111 -0
- autocoder/common/file_handler/mcp_handler.py +268 -0
- autocoder/common/file_handler/models_handler.py +493 -0
- autocoder/common/file_handler/remove_files_handler.py +172 -0
- autocoder/common/git_utils.py +44 -8
- autocoder/common/global_cancel.py +15 -6
- autocoder/common/ignorefiles/test_ignore_file_utils.py +1 -1
- autocoder/common/international/__init__.py +31 -0
- autocoder/common/international/demo_international.py +92 -0
- autocoder/common/international/message_manager.py +157 -0
- autocoder/common/international/messages/__init__.py +56 -0
- autocoder/common/international/messages/async_command_messages.py +507 -0
- autocoder/common/international/messages/auto_coder_messages.py +2208 -0
- autocoder/common/international/messages/chat_auto_coder_messages.py +1547 -0
- autocoder/common/international/messages/command_help_messages.py +986 -0
- autocoder/common/international/messages/conversation_command_messages.py +191 -0
- autocoder/common/international/messages/git_helper_plugin_messages.py +159 -0
- autocoder/common/international/messages/queue_command_messages.py +751 -0
- autocoder/common/international/messages/rules_command_messages.py +77 -0
- autocoder/common/international/messages/sdk_messages.py +1707 -0
- autocoder/common/international/messages/token_helper_plugin_messages.py +361 -0
- autocoder/common/international/messages/tool_display_messages.py +1212 -0
- autocoder/common/international/messages/workflow_exception_messages.py +473 -0
- autocoder/common/international/test_international.py +612 -0
- autocoder/common/linter_core/__init__.py +28 -0
- autocoder/common/linter_core/base_linter.py +61 -0
- autocoder/common/linter_core/config_loader.py +271 -0
- autocoder/common/linter_core/formatters/__init__.py +0 -0
- autocoder/common/linter_core/formatters/base_formatter.py +38 -0
- autocoder/common/linter_core/formatters/raw_formatter.py +17 -0
- autocoder/common/linter_core/linter.py +166 -0
- autocoder/common/linter_core/linter_factory.py +216 -0
- autocoder/common/linter_core/linter_manager.py +333 -0
- autocoder/common/linter_core/linters/__init__.py +9 -0
- autocoder/common/linter_core/linters/java_linter.py +342 -0
- autocoder/common/linter_core/linters/python_linter.py +115 -0
- autocoder/common/linter_core/linters/typescript_linter.py +119 -0
- autocoder/common/linter_core/models/__init__.py +7 -0
- autocoder/common/linter_core/models/lint_result.py +91 -0
- autocoder/common/linter_core/models.py +33 -0
- autocoder/common/linter_core/tests/__init__.py +3 -0
- autocoder/common/linter_core/tests/test_config_loader.py +323 -0
- autocoder/common/linter_core/tests/test_config_loading.py +308 -0
- autocoder/common/linter_core/tests/test_factory_manager.py +234 -0
- autocoder/common/linter_core/tests/test_formatters.py +147 -0
- autocoder/common/linter_core/tests/test_integration.py +317 -0
- autocoder/common/linter_core/tests/test_java_linter.py +496 -0
- autocoder/common/linter_core/tests/test_linters.py +265 -0
- autocoder/common/linter_core/tests/test_models.py +81 -0
- autocoder/common/linter_core/tests/verify_config_loading.py +296 -0
- autocoder/common/linter_core/tests/verify_fixes.py +183 -0
- autocoder/common/llm_friendly_package/__init__.py +31 -0
- autocoder/common/llm_friendly_package/base_manager.py +102 -0
- autocoder/common/llm_friendly_package/docs_manager.py +121 -0
- autocoder/common/llm_friendly_package/library_manager.py +171 -0
- autocoder/common/{llm_friendly_package.py → llm_friendly_package/main_manager.py} +204 -231
- autocoder/common/llm_friendly_package/models.py +40 -0
- autocoder/common/llm_friendly_package/test_llm_friendly_package.py +536 -0
- autocoder/common/llms/__init__.py +15 -0
- autocoder/common/llms/demo_error_handling.py +85 -0
- autocoder/common/llms/factory.py +142 -0
- autocoder/common/llms/manager.py +264 -0
- autocoder/common/llms/pricing.py +121 -0
- autocoder/common/llms/registry.py +288 -0
- autocoder/common/llms/schema.py +77 -0
- autocoder/common/llms/simple_demo.py +45 -0
- autocoder/common/llms/test_quick_model.py +116 -0
- autocoder/common/llms/test_remove_functionality.py +182 -0
- autocoder/common/llms/tests/__init__.py +1 -0
- autocoder/common/llms/tests/test_manager.py +330 -0
- autocoder/common/llms/tests/test_registry.py +364 -0
- autocoder/common/mcp_tools/__init__.py +62 -0
- autocoder/common/{mcp_tools.py → mcp_tools/executor.py} +49 -40
- autocoder/common/{mcp_hub.py → mcp_tools/hub.py} +42 -68
- autocoder/common/{mcp_server_install.py → mcp_tools/installer.py} +16 -28
- autocoder/common/{mcp_server.py → mcp_tools/server.py} +176 -48
- autocoder/common/mcp_tools/test_keyboard_interrupt.py +93 -0
- autocoder/common/mcp_tools/test_mcp_tools.py +391 -0
- autocoder/common/{mcp_server_types.py → mcp_tools/types.py} +121 -48
- autocoder/common/mcp_tools/verify_functionality.py +202 -0
- autocoder/common/model_speed_tester.py +32 -26
- autocoder/common/priority_directory_finder/__init__.py +142 -0
- autocoder/common/priority_directory_finder/examples.py +230 -0
- autocoder/common/priority_directory_finder/finder.py +283 -0
- autocoder/common/priority_directory_finder/models.py +236 -0
- autocoder/common/priority_directory_finder/test_priority_directory_finder.py +431 -0
- autocoder/common/project_scanner/__init__.py +18 -0
- autocoder/common/project_scanner/compat.py +77 -0
- autocoder/common/project_scanner/scanner.py +436 -0
- autocoder/common/project_tracker/__init__.py +27 -0
- autocoder/common/project_tracker/api.py +228 -0
- autocoder/common/project_tracker/demo.py +272 -0
- autocoder/common/project_tracker/tracker.py +487 -0
- autocoder/common/project_tracker/types.py +53 -0
- autocoder/common/pruner/__init__.py +67 -0
- autocoder/common/pruner/agentic_conversation_pruner.py +651 -102
- autocoder/common/pruner/conversation_message_ids_api.py +386 -0
- autocoder/common/pruner/conversation_message_ids_manager.py +347 -0
- autocoder/common/pruner/conversation_message_ids_pruner.py +473 -0
- autocoder/common/pruner/conversation_normalizer.py +347 -0
- autocoder/common/pruner/conversation_pruner.py +26 -6
- autocoder/common/pruner/test_agentic_conversation_pruner.py +554 -112
- autocoder/common/pruner/test_conversation_normalizer.py +502 -0
- autocoder/common/pruner/test_tool_content_detector.py +324 -0
- autocoder/common/pruner/tool_content_detector.py +227 -0
- autocoder/common/pruner/tools/__init__.py +18 -0
- autocoder/common/pruner/tools/query_message_ids.py +264 -0
- autocoder/common/pruner/tools/test_agentic_pruning_logic.py +432 -0
- autocoder/common/pruner/tools/test_message_ids_pruning_only.py +192 -0
- autocoder/common/pull_requests/__init__.py +9 -1
- autocoder/common/pull_requests/utils.py +122 -1
- autocoder/common/rag_manager/rag_manager.py +36 -40
- autocoder/common/rulefiles/__init__.py +53 -1
- autocoder/common/rulefiles/api.py +250 -0
- autocoder/common/rulefiles/core/__init__.py +14 -0
- autocoder/common/rulefiles/core/manager.py +241 -0
- autocoder/common/rulefiles/core/selector.py +805 -0
- autocoder/common/rulefiles/models/__init__.py +20 -0
- autocoder/common/rulefiles/models/index.py +16 -0
- autocoder/common/rulefiles/models/init_rule.py +18 -0
- autocoder/common/rulefiles/models/rule_file.py +18 -0
- autocoder/common/rulefiles/models/rule_relevance.py +14 -0
- autocoder/common/rulefiles/models/summary.py +16 -0
- autocoder/common/rulefiles/test_rulefiles.py +776 -0
- autocoder/common/rulefiles/utils/__init__.py +34 -0
- autocoder/common/rulefiles/utils/monitor.py +86 -0
- autocoder/common/rulefiles/utils/parser.py +230 -0
- autocoder/common/save_formatted_log.py +67 -10
- autocoder/common/search_replace.py +8 -1
- autocoder/common/search_replace_patch/__init__.py +24 -0
- autocoder/common/search_replace_patch/base.py +115 -0
- autocoder/common/search_replace_patch/manager.py +248 -0
- autocoder/common/search_replace_patch/patch_replacer.py +304 -0
- autocoder/common/search_replace_patch/similarity_replacer.py +306 -0
- autocoder/common/search_replace_patch/string_replacer.py +181 -0
- autocoder/common/search_replace_patch/tests/__init__.py +3 -0
- autocoder/common/search_replace_patch/tests/run_tests.py +126 -0
- autocoder/common/search_replace_patch/tests/test_base.py +188 -0
- autocoder/common/search_replace_patch/tests/test_empty_line_insert.py +233 -0
- autocoder/common/search_replace_patch/tests/test_integration.py +389 -0
- autocoder/common/search_replace_patch/tests/test_manager.py +351 -0
- autocoder/common/search_replace_patch/tests/test_patch_replacer.py +316 -0
- autocoder/common/search_replace_patch/tests/test_regex_replacer.py +306 -0
- autocoder/common/search_replace_patch/tests/test_similarity_replacer.py +384 -0
- autocoder/common/shell_commands/__init__.py +197 -0
- autocoder/common/shell_commands/background_process_notifier.py +346 -0
- autocoder/common/shell_commands/command_executor.py +1127 -0
- autocoder/common/shell_commands/error_recovery.py +541 -0
- autocoder/common/shell_commands/exceptions.py +120 -0
- autocoder/common/shell_commands/interactive_executor.py +476 -0
- autocoder/common/shell_commands/interactive_pexpect_process.py +623 -0
- autocoder/common/shell_commands/interactive_process.py +744 -0
- autocoder/common/shell_commands/interactive_session_manager.py +1014 -0
- autocoder/common/shell_commands/monitoring.py +529 -0
- autocoder/common/shell_commands/process_cleanup.py +386 -0
- autocoder/common/shell_commands/process_manager.py +606 -0
- autocoder/common/shell_commands/test_interactive_pexpect_process.py +281 -0
- autocoder/common/shell_commands/tests/__init__.py +6 -0
- autocoder/common/shell_commands/tests/conftest.py +118 -0
- autocoder/common/shell_commands/tests/test_background_process_notifier.py +703 -0
- autocoder/common/shell_commands/tests/test_command_executor.py +448 -0
- autocoder/common/shell_commands/tests/test_error_recovery.py +305 -0
- autocoder/common/shell_commands/tests/test_exceptions.py +299 -0
- autocoder/common/shell_commands/tests/test_execute_batch.py +588 -0
- autocoder/common/shell_commands/tests/test_indented_batch_commands.py +244 -0
- autocoder/common/shell_commands/tests/test_integration.py +664 -0
- autocoder/common/shell_commands/tests/test_monitoring.py +546 -0
- autocoder/common/shell_commands/tests/test_performance.py +632 -0
- autocoder/common/shell_commands/tests/test_process_cleanup.py +397 -0
- autocoder/common/shell_commands/tests/test_process_manager.py +606 -0
- autocoder/common/shell_commands/tests/test_timeout_config.py +343 -0
- autocoder/common/shell_commands/tests/test_timeout_manager.py +520 -0
- autocoder/common/shell_commands/timeout_config.py +315 -0
- autocoder/common/shell_commands/timeout_manager.py +352 -0
- autocoder/common/terminal_paste/__init__.py +14 -0
- autocoder/common/terminal_paste/demo.py +145 -0
- autocoder/common/terminal_paste/demo_paste_functionality.py +95 -0
- autocoder/common/terminal_paste/paste_handler.py +200 -0
- autocoder/common/terminal_paste/paste_manager.py +118 -0
- autocoder/common/terminal_paste/tests/__init__.py +1 -0
- autocoder/common/terminal_paste/tests/test_paste_handler.py +182 -0
- autocoder/common/terminal_paste/tests/test_paste_manager.py +126 -0
- autocoder/common/terminal_paste/utils.py +163 -0
- autocoder/common/test_autocoder_args.py +232 -0
- autocoder/common/test_env_manager.py +173 -0
- autocoder/common/test_env_manager_integration.py +159 -0
- autocoder/common/text_similarity/__init__.py +9 -0
- autocoder/common/text_similarity/demo.py +216 -0
- autocoder/common/text_similarity/examples.py +266 -0
- autocoder/common/text_similarity/test_text_similarity.py +306 -0
- autocoder/common/text_similarity/text_similarity.py +194 -0
- autocoder/common/text_similarity/utils.py +125 -0
- autocoder/common/todos/__init__.py +61 -0
- autocoder/common/todos/cache/__init__.py +16 -0
- autocoder/common/todos/cache/base_cache.py +89 -0
- autocoder/common/todos/cache/cache_manager.py +228 -0
- autocoder/common/todos/cache/memory_cache.py +225 -0
- autocoder/common/todos/config.py +155 -0
- autocoder/common/todos/exceptions.py +35 -0
- autocoder/common/todos/get_todo_manager.py +161 -0
- autocoder/common/todos/manager.py +537 -0
- autocoder/common/todos/models.py +239 -0
- autocoder/common/todos/storage/__init__.py +14 -0
- autocoder/common/todos/storage/base_storage.py +76 -0
- autocoder/common/todos/storage/file_storage.py +278 -0
- autocoder/common/tokens/counter.py +24 -2
- autocoder/common/tools_manager/__init__.py +17 -0
- autocoder/common/tools_manager/examples.py +162 -0
- autocoder/common/tools_manager/manager.py +385 -0
- autocoder/common/tools_manager/models.py +39 -0
- autocoder/common/tools_manager/test_tools_manager.py +303 -0
- autocoder/common/tools_manager/utils.py +191 -0
- autocoder/common/v2/agent/agentic_callbacks.py +270 -0
- autocoder/common/v2/agent/agentic_edit.py +2699 -1856
- autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
- autocoder/common/v2/agent/agentic_edit_tools/__init__.py +35 -1
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +10 -1
- autocoder/common/v2/agent/agentic_edit_tools/background_task_tool_resolver.py +1167 -0
- autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +2 -2
- autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_read_tool_resolver.py +214 -0
- autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_write_tool_resolver.py +299 -0
- autocoder/common/v2/agent/agentic_edit_tools/count_tokens_tool_resolver.py +290 -0
- autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +564 -29
- autocoder/common/v2/agent/agentic_edit_tools/execute_workflow_tool_resolver.py +485 -0
- autocoder/common/v2/agent/agentic_edit_tools/extract_to_text_tool_resolver.py +225 -0
- autocoder/common/v2/agent/agentic_edit_tools/lint_report.py +79 -0
- autocoder/common/v2/agent/agentic_edit_tools/linter_config_models.py +343 -0
- autocoder/common/v2/agent/agentic_edit_tools/linter_enabled_tool_resolver.py +189 -0
- autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +169 -101
- autocoder/common/v2/agent/agentic_edit_tools/load_extra_document_tool_resolver.py +349 -0
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +243 -50
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +667 -147
- autocoder/common/v2/agent/agentic_edit_tools/run_named_subagents_tool_resolver.py +691 -0
- autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +410 -86
- autocoder/common/v2/agent/agentic_edit_tools/session_interactive_tool_resolver.py +115 -0
- autocoder/common/v2/agent/agentic_edit_tools/session_start_tool_resolver.py +190 -0
- autocoder/common/v2/agent/agentic_edit_tools/session_stop_tool_resolver.py +76 -0
- autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +207 -192
- autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +80 -63
- autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +237 -233
- autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +2 -2
- autocoder/common/v2/agent/agentic_edit_tools/web_crawl_tool_resolver.py +557 -0
- autocoder/common/v2/agent/agentic_edit_tools/web_search_tool_resolver.py +600 -0
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +56 -121
- autocoder/common/v2/agent/agentic_edit_types.py +343 -9
- autocoder/common/v2/agent/runner/__init__.py +3 -3
- autocoder/common/v2/agent/runner/base_runner.py +12 -26
- autocoder/common/v2/agent/runner/{event_runner.py → file_based_event_runner.py} +3 -2
- autocoder/common/v2/agent/runner/sdk_runner.py +150 -8
- autocoder/common/v2/agent/runner/terminal_runner.py +170 -57
- autocoder/common/v2/agent/runner/tool_display.py +557 -159
- autocoder/common/v2/agent/test_agentic_callbacks.py +265 -0
- autocoder/common/v2/agent/test_agentic_edit.py +194 -0
- autocoder/common/v2/agent/tool_caller/__init__.py +24 -0
- autocoder/common/v2/agent/tool_caller/default_tool_resolver_map.py +135 -0
- autocoder/common/v2/agent/tool_caller/integration_test.py +172 -0
- autocoder/common/v2/agent/tool_caller/plugins/__init__.py +14 -0
- autocoder/common/v2/agent/tool_caller/plugins/base_plugin.py +126 -0
- autocoder/common/v2/agent/tool_caller/plugins/examples/__init__.py +13 -0
- autocoder/common/v2/agent/tool_caller/plugins/examples/logging_plugin.py +164 -0
- autocoder/common/v2/agent/tool_caller/plugins/examples/security_filter_plugin.py +198 -0
- autocoder/common/v2/agent/tool_caller/plugins/plugin_interface.py +141 -0
- autocoder/common/v2/agent/tool_caller/test_tool_caller.py +278 -0
- autocoder/common/v2/agent/tool_caller/tool_call_plugin_manager.py +331 -0
- autocoder/common/v2/agent/tool_caller/tool_caller.py +337 -0
- autocoder/common/v2/agent/tool_caller/usage_example.py +193 -0
- autocoder/common/v2/code_agentic_editblock_manager.py +4 -4
- autocoder/common/v2/code_auto_generate.py +136 -78
- autocoder/common/v2/code_auto_generate_diff.py +135 -79
- autocoder/common/v2/code_auto_generate_editblock.py +174 -99
- autocoder/common/v2/code_auto_generate_strict_diff.py +151 -71
- autocoder/common/v2/code_auto_merge.py +1 -1
- autocoder/common/v2/code_auto_merge_editblock.py +13 -1
- autocoder/common/v2/code_diff_manager.py +3 -3
- autocoder/common/v2/code_editblock_manager.py +4 -14
- autocoder/common/v2/code_manager.py +1 -1
- autocoder/common/v2/code_strict_diff_manager.py +2 -2
- autocoder/common/wrap_llm_hint/__init__.py +10 -0
- autocoder/common/wrap_llm_hint/test_wrap_llm_hint.py +1067 -0
- autocoder/common/wrap_llm_hint/utils.py +432 -0
- autocoder/common/wrap_llm_hint/wrap_llm_hint.py +323 -0
- autocoder/completer/__init__.py +8 -0
- autocoder/completer/command_completer_v2.py +1051 -0
- autocoder/default_project/__init__.py +501 -0
- autocoder/dispacher/__init__.py +4 -12
- autocoder/dispacher/actions/action.py +165 -7
- autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
- autocoder/index/entry.py +116 -124
- autocoder/{agent → index/filter}/agentic_filter.py +322 -333
- autocoder/index/filter/normal_filter.py +5 -11
- autocoder/index/filter/quick_filter.py +1 -1
- autocoder/index/index.py +36 -9
- autocoder/index/tests/__init__.py +1 -0
- autocoder/index/tests/run_tests.py +195 -0
- autocoder/index/tests/test_entry.py +303 -0
- autocoder/index/tests/test_index_manager.py +314 -0
- autocoder/index/tests/test_module_integration.py +300 -0
- autocoder/index/tests/test_symbols_utils.py +183 -0
- autocoder/inner/__init__.py +4 -0
- autocoder/inner/agentic.py +932 -0
- autocoder/inner/async_command_handler.py +992 -0
- autocoder/inner/conversation_command_handlers.py +623 -0
- autocoder/inner/merge_command_handler.py +213 -0
- autocoder/inner/queue_command_handler.py +684 -0
- autocoder/models.py +95 -266
- autocoder/plugins/git_helper_plugin.py +31 -29
- autocoder/plugins/token_helper_plugin.py +65 -46
- autocoder/pyproject/__init__.py +32 -29
- autocoder/rag/agentic_rag.py +215 -75
- autocoder/rag/cache/simple_cache.py +1 -2
- autocoder/rag/loaders/image_loader.py +1 -1
- autocoder/rag/long_context_rag.py +42 -26
- autocoder/rag/qa_conversation_strategy.py +1 -1
- autocoder/rag/terminal/__init__.py +17 -0
- autocoder/rag/terminal/args.py +581 -0
- autocoder/rag/terminal/bootstrap.py +61 -0
- autocoder/rag/terminal/command_handlers.py +653 -0
- autocoder/rag/terminal/formatters/__init__.py +20 -0
- autocoder/rag/terminal/formatters/base.py +70 -0
- autocoder/rag/terminal/formatters/json_format.py +66 -0
- autocoder/rag/terminal/formatters/stream_json.py +95 -0
- autocoder/rag/terminal/formatters/text.py +28 -0
- autocoder/rag/terminal/init.py +120 -0
- autocoder/rag/terminal/utils.py +106 -0
- autocoder/rag/test_agentic_rag.py +389 -0
- autocoder/rag/test_doc_filter.py +3 -3
- autocoder/rag/test_long_context_rag.py +1 -1
- autocoder/rag/test_token_limiter.py +517 -10
- autocoder/rag/token_counter.py +3 -0
- autocoder/rag/token_limiter.py +19 -15
- autocoder/rag/tools/__init__.py +26 -2
- autocoder/rag/tools/bochaai_example.py +343 -0
- autocoder/rag/tools/bochaai_sdk.py +541 -0
- autocoder/rag/tools/metaso_example.py +268 -0
- autocoder/rag/tools/metaso_sdk.py +417 -0
- autocoder/rag/tools/recall_tool.py +28 -7
- autocoder/rag/tools/run_integration_tests.py +204 -0
- autocoder/rag/tools/test_all_providers.py +318 -0
- autocoder/rag/tools/test_bochaai_integration.py +482 -0
- autocoder/rag/tools/test_final_integration.py +215 -0
- autocoder/rag/tools/test_metaso_integration.py +424 -0
- autocoder/rag/tools/test_metaso_real.py +171 -0
- autocoder/rag/tools/test_web_crawl_tool.py +639 -0
- autocoder/rag/tools/test_web_search_tool.py +509 -0
- autocoder/rag/tools/todo_read_tool.py +202 -0
- autocoder/rag/tools/todo_write_tool.py +412 -0
- autocoder/rag/tools/web_crawl_tool.py +634 -0
- autocoder/rag/tools/web_search_tool.py +558 -0
- autocoder/rag/tools/web_tools_example.py +119 -0
- autocoder/rag/types.py +16 -0
- autocoder/rag/variable_holder.py +4 -2
- autocoder/rags.py +86 -79
- autocoder/regexproject/__init__.py +23 -21
- autocoder/sdk/__init__.py +46 -190
- autocoder/sdk/api.py +370 -0
- autocoder/sdk/async_runner/__init__.py +26 -0
- autocoder/sdk/async_runner/async_executor.py +650 -0
- autocoder/sdk/async_runner/async_handler.py +356 -0
- autocoder/sdk/async_runner/markdown_processor.py +595 -0
- autocoder/sdk/async_runner/task_metadata.py +284 -0
- autocoder/sdk/async_runner/worktree_manager.py +438 -0
- autocoder/sdk/cli/__init__.py +2 -5
- autocoder/sdk/cli/formatters.py +28 -204
- autocoder/sdk/cli/handlers.py +77 -44
- autocoder/sdk/cli/main.py +154 -171
- autocoder/sdk/cli/options.py +95 -22
- autocoder/sdk/constants.py +139 -51
- autocoder/sdk/core/auto_coder_core.py +484 -109
- autocoder/sdk/core/bridge.py +297 -115
- autocoder/sdk/exceptions.py +18 -12
- autocoder/sdk/formatters/__init__.py +19 -0
- autocoder/sdk/formatters/input.py +64 -0
- autocoder/sdk/formatters/output.py +247 -0
- autocoder/sdk/formatters/stream.py +54 -0
- autocoder/sdk/models/__init__.py +6 -5
- autocoder/sdk/models/options.py +55 -18
- autocoder/sdk/utils/formatters.py +27 -195
- autocoder/suffixproject/__init__.py +28 -25
- autocoder/terminal/__init__.py +14 -0
- autocoder/terminal/app.py +454 -0
- autocoder/terminal/args.py +32 -0
- autocoder/terminal/bootstrap.py +178 -0
- autocoder/terminal/command_processor.py +521 -0
- autocoder/terminal/command_registry.py +57 -0
- autocoder/terminal/help.py +97 -0
- autocoder/terminal/tasks/__init__.py +5 -0
- autocoder/terminal/tasks/background.py +77 -0
- autocoder/terminal/tasks/task_event.py +70 -0
- autocoder/terminal/ui/__init__.py +13 -0
- autocoder/terminal/ui/completer.py +268 -0
- autocoder/terminal/ui/keybindings.py +75 -0
- autocoder/terminal/ui/session.py +41 -0
- autocoder/terminal/ui/toolbar.py +64 -0
- autocoder/terminal/utils/__init__.py +13 -0
- autocoder/terminal/utils/errors.py +18 -0
- autocoder/terminal/utils/paths.py +19 -0
- autocoder/terminal/utils/shell.py +43 -0
- autocoder/terminal_v3/__init__.py +10 -0
- autocoder/terminal_v3/app.py +201 -0
- autocoder/terminal_v3/handlers/__init__.py +5 -0
- autocoder/terminal_v3/handlers/command_handler.py +131 -0
- autocoder/terminal_v3/models/__init__.py +6 -0
- autocoder/terminal_v3/models/conversation_buffer.py +214 -0
- autocoder/terminal_v3/models/message.py +50 -0
- autocoder/terminal_v3/models/tool_display.py +247 -0
- autocoder/terminal_v3/ui/__init__.py +7 -0
- autocoder/terminal_v3/ui/keybindings.py +56 -0
- autocoder/terminal_v3/ui/layout.py +141 -0
- autocoder/terminal_v3/ui/styles.py +43 -0
- autocoder/tsproject/__init__.py +23 -23
- autocoder/utils/auto_coder_utils/chat_stream_out.py +1 -1
- autocoder/utils/llms.py +88 -80
- autocoder/utils/math_utils.py +101 -0
- autocoder/utils/model_provider_selector.py +16 -4
- autocoder/utils/operate_config_api.py +33 -5
- autocoder/utils/thread_utils.py +2 -2
- autocoder/version.py +4 -2
- autocoder/workflow_agents/__init__.py +84 -0
- autocoder/workflow_agents/agent.py +143 -0
- autocoder/workflow_agents/exceptions.py +573 -0
- autocoder/workflow_agents/executor.py +489 -0
- autocoder/workflow_agents/loader.py +737 -0
- autocoder/workflow_agents/runner.py +267 -0
- autocoder/workflow_agents/types.py +172 -0
- autocoder/workflow_agents/utils.py +434 -0
- autocoder/workflow_agents/workflow_manager.py +211 -0
- auto_coder-1.0.0.dist-info/METADATA +0 -396
- auto_coder-1.0.0.dist-info/RECORD +0 -442
- auto_coder-1.0.0.dist-info/licenses/LICENSE +0 -201
- autocoder/auto_coder_server.py +0 -672
- autocoder/benchmark.py +0 -138
- autocoder/common/ac_style_command_parser/example.py +0 -7
- autocoder/common/cleaner.py +0 -31
- autocoder/common/command_completer_v2.py +0 -615
- autocoder/common/context_pruner.py +0 -477
- autocoder/common/conversation_pruner.py +0 -132
- autocoder/common/directory_cache/__init__.py +0 -1
- autocoder/common/directory_cache/cache.py +0 -192
- autocoder/common/directory_cache/test_cache.py +0 -190
- autocoder/common/file_checkpoint/examples.py +0 -217
- autocoder/common/llm_friendly_package_example.py +0 -138
- autocoder/common/llm_friendly_package_test.py +0 -63
- autocoder/common/pull_requests/test_module.py +0 -1
- autocoder/common/rulefiles/autocoderrules_utils.py +0 -484
- autocoder/common/text.py +0 -30
- autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +0 -42
- autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.py +0 -70
- autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +0 -163
- autocoder/common/v2/agent/agentic_tool_display.py +0 -183
- autocoder/plugins/dynamic_completion_example.py +0 -148
- autocoder/plugins/sample_plugin.py +0 -160
- autocoder/sdk/cli/__main__.py +0 -26
- autocoder/sdk/cli/completion_wrapper.py +0 -38
- autocoder/sdk/cli/install_completion.py +0 -301
- autocoder/sdk/models/messages.py +0 -209
- autocoder/sdk/session/__init__.py +0 -32
- autocoder/sdk/session/session.py +0 -106
- autocoder/sdk/session/session_manager.py +0 -56
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/top_level.txt +0 -0
- /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
|
+
)
|