auto-coder 1.0.0__py3-none-any.whl → 2.0.1__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.1.dist-info/LICENSE +158 -0
- auto_coder-2.0.1.dist-info/METADATA +558 -0
- auto_coder-2.0.1.dist-info/RECORD +795 -0
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.1.dist-info}/WHEEL +1 -1
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.1.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 +77 -73
- autocoder/auto_coder.py +31 -40
- autocoder/auto_coder_rag.py +11 -1084
- autocoder/auto_coder_runner.py +962 -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 +409 -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 +316 -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 +356 -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 +1094 -0
- autocoder/default_project/__init__.py +501 -0
- autocoder/dispacher/__init__.py +4 -12
- autocoder/dispacher/actions/action.py +400 -129
- autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
- autocoder/index/entry.py +117 -125
- 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 +923 -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 +665 -0
- autocoder/workflow_agents/loader.py +749 -0
- autocoder/workflow_agents/runner.py +267 -0
- autocoder/workflow_agents/types.py +173 -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.1.dist-info}/top_level.txt +0 -0
- /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Error recovery module for shell command execution.
|
|
3
|
+
|
|
4
|
+
This module provides error recovery functionality including:
|
|
5
|
+
- Error handling strategies
|
|
6
|
+
- Automatic recovery mechanisms
|
|
7
|
+
- Retry logic with backoff
|
|
8
|
+
- Error categorization and logging
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import time
|
|
12
|
+
import random
|
|
13
|
+
from typing import Dict, Type, Callable, Optional, Any, List, Union
|
|
14
|
+
from loguru import logger
|
|
15
|
+
|
|
16
|
+
from .exceptions import (
|
|
17
|
+
CommandExecutionError,
|
|
18
|
+
CommandTimeoutError,
|
|
19
|
+
ProcessCleanupError,
|
|
20
|
+
ProcessNotFoundError,
|
|
21
|
+
ErrorRecoveryFailedError
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ErrorRecoveryManager:
|
|
26
|
+
"""
|
|
27
|
+
Manager for error recovery and handling.
|
|
28
|
+
|
|
29
|
+
This class provides comprehensive error recovery mechanisms including
|
|
30
|
+
retry logic, error categorization, and custom recovery strategies.
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
recovery_strategies: Dictionary mapping exception types to recovery functions
|
|
34
|
+
retry_config: Configuration for retry behavior
|
|
35
|
+
max_recovery_attempts: Maximum number of recovery attempts
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, max_recovery_attempts: int = 3):
|
|
39
|
+
"""
|
|
40
|
+
Initialize error recovery manager.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
max_recovery_attempts: Maximum number of recovery attempts
|
|
44
|
+
"""
|
|
45
|
+
self.max_recovery_attempts = max_recovery_attempts
|
|
46
|
+
self.recovery_strategies: Dict[Type[Exception], Callable] = {
|
|
47
|
+
CommandTimeoutError: self._handle_timeout_error,
|
|
48
|
+
ProcessCleanupError: self._handle_cleanup_error,
|
|
49
|
+
ProcessNotFoundError: self._handle_process_not_found_error,
|
|
50
|
+
CommandExecutionError: self._handle_execution_error,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
logger.debug(f"ErrorRecoveryManager initialized with max_attempts={max_recovery_attempts}")
|
|
54
|
+
|
|
55
|
+
def register_recovery_strategy(
|
|
56
|
+
self,
|
|
57
|
+
exception_type: Type[Exception],
|
|
58
|
+
strategy: Callable
|
|
59
|
+
) -> None:
|
|
60
|
+
"""
|
|
61
|
+
Register a custom recovery strategy for an exception type.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
exception_type: The exception type to handle
|
|
65
|
+
strategy: The recovery function to use
|
|
66
|
+
"""
|
|
67
|
+
self.recovery_strategies[exception_type] = strategy
|
|
68
|
+
logger.debug(f"Registered recovery strategy for {exception_type.__name__}")
|
|
69
|
+
|
|
70
|
+
def handle_error(
|
|
71
|
+
self,
|
|
72
|
+
error: Exception,
|
|
73
|
+
context: Optional[Dict[str, Any]] = None,
|
|
74
|
+
retry_count: int = 0
|
|
75
|
+
) -> bool:
|
|
76
|
+
"""
|
|
77
|
+
Handle an error with appropriate recovery strategy.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
error: The exception to handle
|
|
81
|
+
context: Additional context information
|
|
82
|
+
retry_count: Current retry attempt number
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
True if recovery was successful, False otherwise
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
ErrorRecoveryFailedError: If all recovery attempts fail
|
|
89
|
+
"""
|
|
90
|
+
if context is None:
|
|
91
|
+
context = {}
|
|
92
|
+
|
|
93
|
+
error_type = type(error)
|
|
94
|
+
|
|
95
|
+
logger.warning(f"Handling error: {error_type.__name__}: {error}")
|
|
96
|
+
|
|
97
|
+
# Check if we have a recovery strategy for this error type
|
|
98
|
+
strategy = self._get_recovery_strategy(error_type)
|
|
99
|
+
if not strategy:
|
|
100
|
+
logger.error(f"No recovery strategy found for {error_type.__name__}")
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
# Attempt recovery
|
|
104
|
+
try:
|
|
105
|
+
success = strategy(error, context, retry_count)
|
|
106
|
+
|
|
107
|
+
if success:
|
|
108
|
+
logger.info(f"Successfully recovered from {error_type.__name__}")
|
|
109
|
+
return True
|
|
110
|
+
else:
|
|
111
|
+
logger.warning(f"Recovery failed for {error_type.__name__}")
|
|
112
|
+
|
|
113
|
+
# Check if we should retry
|
|
114
|
+
if retry_count < self.max_recovery_attempts:
|
|
115
|
+
logger.debug(f"Retrying recovery, attempt {retry_count + 1}/{self.max_recovery_attempts}")
|
|
116
|
+
return self.handle_error(error, context, retry_count + 1)
|
|
117
|
+
else:
|
|
118
|
+
logger.error(f"Max recovery attempts reached for {error_type.__name__}")
|
|
119
|
+
raise ErrorRecoveryFailedError(error, retry_count)
|
|
120
|
+
|
|
121
|
+
except Exception as e:
|
|
122
|
+
logger.error(f"Error during recovery attempt: {e}")
|
|
123
|
+
if retry_count < self.max_recovery_attempts:
|
|
124
|
+
return self.handle_error(error, context, retry_count + 1)
|
|
125
|
+
else:
|
|
126
|
+
raise ErrorRecoveryFailedError(error, retry_count)
|
|
127
|
+
|
|
128
|
+
def _get_recovery_strategy(self, error_type: Type[Exception]) -> Optional[Callable]:
|
|
129
|
+
"""
|
|
130
|
+
Get the appropriate recovery strategy for an error type.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
error_type: The exception type
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Recovery strategy function or None
|
|
137
|
+
"""
|
|
138
|
+
# Check for exact match first
|
|
139
|
+
if error_type in self.recovery_strategies:
|
|
140
|
+
return self.recovery_strategies[error_type]
|
|
141
|
+
|
|
142
|
+
# Check for parent class matches
|
|
143
|
+
for exception_type, strategy in self.recovery_strategies.items():
|
|
144
|
+
if issubclass(error_type, exception_type):
|
|
145
|
+
return strategy
|
|
146
|
+
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
def _handle_timeout_error(
|
|
150
|
+
self,
|
|
151
|
+
error: CommandTimeoutError,
|
|
152
|
+
context: Dict[str, Any],
|
|
153
|
+
retry_count: int
|
|
154
|
+
) -> bool:
|
|
155
|
+
"""
|
|
156
|
+
Handle command timeout errors.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
error: The timeout error
|
|
160
|
+
context: Context information
|
|
161
|
+
retry_count: Current retry attempt
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
True if recovery successful
|
|
165
|
+
"""
|
|
166
|
+
logger.debug(f"Handling timeout error for command: {error.command}")
|
|
167
|
+
|
|
168
|
+
# Strategy 1: Increase timeout and retry
|
|
169
|
+
if retry_count == 0:
|
|
170
|
+
new_timeout = error.timeout * 1.5
|
|
171
|
+
logger.debug(f"Increasing timeout to {new_timeout}s and retrying")
|
|
172
|
+
context['timeout'] = new_timeout
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
# Strategy 2: Try with even longer timeout
|
|
176
|
+
elif retry_count == 1:
|
|
177
|
+
new_timeout = error.timeout * 2.0
|
|
178
|
+
logger.debug(f"Increasing timeout to {new_timeout}s (final attempt)")
|
|
179
|
+
context['timeout'] = new_timeout
|
|
180
|
+
return True
|
|
181
|
+
|
|
182
|
+
# Strategy 3: Give up
|
|
183
|
+
else:
|
|
184
|
+
logger.error(f"Unable to recover from timeout error after {retry_count} attempts")
|
|
185
|
+
return False
|
|
186
|
+
|
|
187
|
+
def _handle_cleanup_error(
|
|
188
|
+
self,
|
|
189
|
+
error: ProcessCleanupError,
|
|
190
|
+
context: Dict[str, Any],
|
|
191
|
+
retry_count: int
|
|
192
|
+
) -> bool:
|
|
193
|
+
"""
|
|
194
|
+
Handle process cleanup errors.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
error: The cleanup error
|
|
198
|
+
context: Context information
|
|
199
|
+
retry_count: Current retry attempt
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
True if recovery successful
|
|
203
|
+
"""
|
|
204
|
+
logger.debug(f"Handling cleanup error for PID: {error.pid}")
|
|
205
|
+
|
|
206
|
+
# Strategy 1: Wait and retry cleanup
|
|
207
|
+
if retry_count == 0:
|
|
208
|
+
logger.debug("Waiting before retry cleanup")
|
|
209
|
+
time.sleep(1.0)
|
|
210
|
+
return True
|
|
211
|
+
|
|
212
|
+
# Strategy 2: Force cleanup with longer timeout
|
|
213
|
+
elif retry_count == 1:
|
|
214
|
+
logger.debug("Attempting force cleanup with longer timeout")
|
|
215
|
+
context['force_cleanup'] = True
|
|
216
|
+
context['cleanup_timeout'] = 15.0
|
|
217
|
+
return True
|
|
218
|
+
|
|
219
|
+
# Strategy 3: Log and continue (non-fatal)
|
|
220
|
+
else:
|
|
221
|
+
logger.warning(f"Unable to cleanup process {error.pid}, continuing anyway")
|
|
222
|
+
return True # Consider cleanup errors non-fatal
|
|
223
|
+
|
|
224
|
+
def _handle_process_not_found_error(
|
|
225
|
+
self,
|
|
226
|
+
error: ProcessNotFoundError,
|
|
227
|
+
context: Dict[str, Any],
|
|
228
|
+
retry_count: int
|
|
229
|
+
) -> bool:
|
|
230
|
+
"""
|
|
231
|
+
Handle process not found errors.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
error: The process not found error
|
|
235
|
+
context: Context information
|
|
236
|
+
retry_count: Current retry attempt
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
True if recovery successful
|
|
240
|
+
"""
|
|
241
|
+
logger.debug(f"Handling process not found error for PID: {error.pid}")
|
|
242
|
+
|
|
243
|
+
# Process not found is usually not a real error - process already exited
|
|
244
|
+
logger.debug(f"Process {error.pid} not found, likely already exited")
|
|
245
|
+
return True
|
|
246
|
+
|
|
247
|
+
def _handle_execution_error(
|
|
248
|
+
self,
|
|
249
|
+
error: CommandExecutionError,
|
|
250
|
+
context: Dict[str, Any],
|
|
251
|
+
retry_count: int
|
|
252
|
+
) -> bool:
|
|
253
|
+
"""
|
|
254
|
+
Handle general command execution errors.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
error: The execution error
|
|
258
|
+
context: Context information
|
|
259
|
+
retry_count: Current retry attempt
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
True if recovery successful
|
|
263
|
+
"""
|
|
264
|
+
logger.debug(f"Handling execution error: {error}")
|
|
265
|
+
|
|
266
|
+
# Strategy 1: Simple retry
|
|
267
|
+
if retry_count == 0:
|
|
268
|
+
logger.debug("Retrying command execution")
|
|
269
|
+
return True
|
|
270
|
+
|
|
271
|
+
# Strategy 2: Wait and retry
|
|
272
|
+
elif retry_count == 1:
|
|
273
|
+
logger.debug("Waiting before retry")
|
|
274
|
+
time.sleep(0.5)
|
|
275
|
+
return True
|
|
276
|
+
|
|
277
|
+
# Strategy 3: Give up
|
|
278
|
+
else:
|
|
279
|
+
logger.error(f"Unable to recover from execution error after {retry_count} attempts")
|
|
280
|
+
return False
|
|
281
|
+
|
|
282
|
+
def _validate_delays(self, delays: List[float]) -> List[float]:
|
|
283
|
+
"""
|
|
284
|
+
Validate retry delays.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
delays: List of delays to validate
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Validated delays
|
|
291
|
+
|
|
292
|
+
Raises:
|
|
293
|
+
ValueError: If delays are invalid
|
|
294
|
+
"""
|
|
295
|
+
if not delays:
|
|
296
|
+
raise ValueError("Delays cannot be empty")
|
|
297
|
+
|
|
298
|
+
# Fix negative delays by setting them to 0
|
|
299
|
+
validated_delays = []
|
|
300
|
+
for delay in delays:
|
|
301
|
+
if delay < 0:
|
|
302
|
+
validated_delays.append(0.0) # Set negative delays to 0
|
|
303
|
+
else:
|
|
304
|
+
validated_delays.append(delay)
|
|
305
|
+
|
|
306
|
+
return validated_delays
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class RetryConfig:
|
|
310
|
+
"""
|
|
311
|
+
Configuration for retry behavior.
|
|
312
|
+
|
|
313
|
+
Attributes:
|
|
314
|
+
max_retries: Maximum number of retries (0 means no retries)
|
|
315
|
+
delays: Custom delay sequence (if None, uses exponential backoff)
|
|
316
|
+
backoff_factor: Factor for exponential backoff
|
|
317
|
+
jitter: Whether to add random jitter to delays
|
|
318
|
+
retry_on_timeout: Whether to retry on timeout
|
|
319
|
+
retry_on_error: Whether to retry on other errors
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
def __init__(
|
|
323
|
+
self,
|
|
324
|
+
max_retries: Optional[int] = None,
|
|
325
|
+
delays: Optional[List[float]] = None,
|
|
326
|
+
backoff_factor: float = 2.0,
|
|
327
|
+
jitter: bool = True,
|
|
328
|
+
retry_on_timeout: bool = True,
|
|
329
|
+
retry_on_error: bool = True,
|
|
330
|
+
# Backward compatibility parameters
|
|
331
|
+
max_attempts: Optional[int] = None,
|
|
332
|
+
base_delay: Optional[float] = None,
|
|
333
|
+
max_delay: Optional[float] = None,
|
|
334
|
+
retry_on_cleanup_error: Optional[bool] = None
|
|
335
|
+
):
|
|
336
|
+
"""
|
|
337
|
+
Initialize retry configuration.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
max_retries: Maximum number of retries (0 means no retries)
|
|
341
|
+
delays: Custom delay sequence (if None, uses exponential backoff)
|
|
342
|
+
backoff_factor: Factor for exponential backoff
|
|
343
|
+
jitter: Whether to add random jitter to delays
|
|
344
|
+
retry_on_timeout: Whether to retry on timeout
|
|
345
|
+
retry_on_error: Whether to retry on other errors
|
|
346
|
+
|
|
347
|
+
# Backward compatibility:
|
|
348
|
+
max_attempts: Old name for max_retries
|
|
349
|
+
base_delay: Base delay for old exponential backoff
|
|
350
|
+
max_delay: Maximum delay for old exponential backoff
|
|
351
|
+
retry_on_cleanup_error: Old name for retry_on_error
|
|
352
|
+
"""
|
|
353
|
+
# Handle backward compatibility
|
|
354
|
+
if max_attempts is not None:
|
|
355
|
+
max_retries = max_attempts
|
|
356
|
+
if max_retries is None:
|
|
357
|
+
max_retries = 3
|
|
358
|
+
|
|
359
|
+
if retry_on_cleanup_error is not None:
|
|
360
|
+
retry_on_error = retry_on_cleanup_error
|
|
361
|
+
|
|
362
|
+
if max_retries < 0:
|
|
363
|
+
raise ValueError("max_retries cannot be negative")
|
|
364
|
+
|
|
365
|
+
self.max_retries = max_retries
|
|
366
|
+
# For backward compatibility
|
|
367
|
+
self.max_attempts = max_retries
|
|
368
|
+
|
|
369
|
+
self.backoff_factor = backoff_factor
|
|
370
|
+
self.jitter = jitter
|
|
371
|
+
self.retry_on_timeout = retry_on_timeout
|
|
372
|
+
self.retry_on_error = retry_on_error
|
|
373
|
+
self.retry_on_cleanup_error = retry_on_error # Backward compatibility
|
|
374
|
+
|
|
375
|
+
# Handle old-style delay configuration
|
|
376
|
+
if base_delay is not None or max_delay is not None:
|
|
377
|
+
# Use old exponential backoff style
|
|
378
|
+
self.base_delay = base_delay if base_delay is not None else 1.0
|
|
379
|
+
self.max_delay = max_delay if max_delay is not None else 60.0
|
|
380
|
+
|
|
381
|
+
if max_retries == 0:
|
|
382
|
+
self.delays = []
|
|
383
|
+
else:
|
|
384
|
+
# Generate delays using old style
|
|
385
|
+
self.delays = []
|
|
386
|
+
for i in range(max_retries):
|
|
387
|
+
delay = self.base_delay * (backoff_factor ** i)
|
|
388
|
+
delay = min(delay, self.max_delay)
|
|
389
|
+
# Fix negative delays
|
|
390
|
+
delay = max(0.0, delay)
|
|
391
|
+
self.delays.append(delay)
|
|
392
|
+
else:
|
|
393
|
+
# Use new style delay configuration
|
|
394
|
+
self.base_delay = 1.0 # Default for backward compatibility
|
|
395
|
+
self.max_delay = 60.0 # Default for backward compatibility
|
|
396
|
+
|
|
397
|
+
if delays is not None:
|
|
398
|
+
# Validate and fix delays directly
|
|
399
|
+
if not delays:
|
|
400
|
+
raise ValueError("Delays cannot be empty")
|
|
401
|
+
|
|
402
|
+
# Fix negative delays by setting them to 0
|
|
403
|
+
validated_delays = []
|
|
404
|
+
for delay in delays:
|
|
405
|
+
if delay < 0:
|
|
406
|
+
validated_delays.append(0.0) # Set negative delays to 0
|
|
407
|
+
else:
|
|
408
|
+
validated_delays.append(delay)
|
|
409
|
+
|
|
410
|
+
self.delays = validated_delays
|
|
411
|
+
else:
|
|
412
|
+
# Generate default delays based on max_retries
|
|
413
|
+
if max_retries == 0:
|
|
414
|
+
self.delays = [] # No retries, no delays needed
|
|
415
|
+
else:
|
|
416
|
+
self.delays = [min(60.0, backoff_factor ** i) for i in range(max_retries)]
|
|
417
|
+
|
|
418
|
+
def get_delay(self, attempt: int) -> float:
|
|
419
|
+
"""
|
|
420
|
+
Get the delay for a specific retry attempt.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
attempt: The retry attempt number (0-based)
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
Delay in seconds
|
|
427
|
+
"""
|
|
428
|
+
# Ensure attempt is within bounds, use last delay for out-of-bounds
|
|
429
|
+
if attempt < 0:
|
|
430
|
+
attempt = 0
|
|
431
|
+
elif attempt >= len(self.delays):
|
|
432
|
+
attempt = len(self.delays) - 1 if self.delays else 0
|
|
433
|
+
|
|
434
|
+
# If no delays configured, return 0
|
|
435
|
+
if not self.delays:
|
|
436
|
+
return 0.0
|
|
437
|
+
|
|
438
|
+
delay = self.delays[attempt]
|
|
439
|
+
if self.jitter:
|
|
440
|
+
delay += random.uniform(-0.1 * delay, 0.1 * delay)
|
|
441
|
+
# Ensure delay is never negative
|
|
442
|
+
return max(0.0, delay)
|
|
443
|
+
|
|
444
|
+
def should_retry(self, error: Exception, attempt: int) -> bool:
|
|
445
|
+
"""
|
|
446
|
+
Determine if an error should be retried.
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
error: The exception that occurred
|
|
450
|
+
attempt: The current attempt number
|
|
451
|
+
|
|
452
|
+
Returns:
|
|
453
|
+
True if should retry
|
|
454
|
+
"""
|
|
455
|
+
if attempt >= self.max_attempts:
|
|
456
|
+
return False
|
|
457
|
+
|
|
458
|
+
if isinstance(error, CommandTimeoutError):
|
|
459
|
+
return self.retry_on_timeout
|
|
460
|
+
|
|
461
|
+
if isinstance(error, ProcessCleanupError):
|
|
462
|
+
return self.retry_on_error
|
|
463
|
+
|
|
464
|
+
if isinstance(error, ProcessNotFoundError):
|
|
465
|
+
return False # Don't retry process not found errors
|
|
466
|
+
|
|
467
|
+
if isinstance(error, CommandExecutionError):
|
|
468
|
+
return self.retry_on_error # Retry general execution errors
|
|
469
|
+
|
|
470
|
+
return False
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def retry_with_backoff(
|
|
474
|
+
func: Callable,
|
|
475
|
+
retry_config: RetryConfig,
|
|
476
|
+
error_recovery_manager: Optional[ErrorRecoveryManager] = None,
|
|
477
|
+
*args,
|
|
478
|
+
**kwargs
|
|
479
|
+
) -> Any:
|
|
480
|
+
"""
|
|
481
|
+
Retry a function with exponential backoff.
|
|
482
|
+
|
|
483
|
+
Args:
|
|
484
|
+
func: Function to retry
|
|
485
|
+
retry_config: Retry configuration
|
|
486
|
+
error_recovery_manager: Optional error recovery manager
|
|
487
|
+
*args: Function arguments
|
|
488
|
+
**kwargs: Function keyword arguments
|
|
489
|
+
|
|
490
|
+
Returns:
|
|
491
|
+
Function result
|
|
492
|
+
|
|
493
|
+
Raises:
|
|
494
|
+
The last exception if all retries fail
|
|
495
|
+
"""
|
|
496
|
+
last_exception = None
|
|
497
|
+
|
|
498
|
+
# Use max_attempts directly from config (which handles backward compatibility)
|
|
499
|
+
max_attempts = max(1, retry_config.max_attempts)
|
|
500
|
+
|
|
501
|
+
for attempt in range(max_attempts):
|
|
502
|
+
try:
|
|
503
|
+
return func(*args, **kwargs)
|
|
504
|
+
except Exception as e:
|
|
505
|
+
last_exception = e
|
|
506
|
+
|
|
507
|
+
# If this is the last attempt or we shouldn't retry, break
|
|
508
|
+
if attempt >= max_attempts or not retry_config.should_retry(e, attempt):
|
|
509
|
+
logger.debug(f"Not retrying {type(e).__name__} on attempt {attempt}")
|
|
510
|
+
break
|
|
511
|
+
|
|
512
|
+
# Try error recovery if available
|
|
513
|
+
if error_recovery_manager:
|
|
514
|
+
try:
|
|
515
|
+
if error_recovery_manager.handle_error(e, retry_count=attempt):
|
|
516
|
+
logger.debug(f"Error recovery successful for attempt {attempt}")
|
|
517
|
+
continue
|
|
518
|
+
except ErrorRecoveryFailedError:
|
|
519
|
+
logger.debug(f"Error recovery failed for attempt {attempt}")
|
|
520
|
+
break
|
|
521
|
+
|
|
522
|
+
# Calculate delay
|
|
523
|
+
delay = retry_config.get_delay(attempt)
|
|
524
|
+
logger.debug(f"Retrying in {delay}s (attempt {attempt + 1}/{max_attempts})")
|
|
525
|
+
time.sleep(delay)
|
|
526
|
+
|
|
527
|
+
# All retries failed
|
|
528
|
+
if last_exception:
|
|
529
|
+
raise last_exception
|
|
530
|
+
else:
|
|
531
|
+
raise CommandExecutionError("All retry attempts failed")
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def create_default_error_recovery_manager() -> ErrorRecoveryManager:
|
|
535
|
+
"""
|
|
536
|
+
Create a default error recovery manager with standard strategies.
|
|
537
|
+
|
|
538
|
+
Returns:
|
|
539
|
+
Configured ErrorRecoveryManager instance
|
|
540
|
+
"""
|
|
541
|
+
return ErrorRecoveryManager(max_recovery_attempts=3)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom exceptions for shell command execution with timeout support.
|
|
3
|
+
|
|
4
|
+
This module defines all custom exceptions used in the shell commands module
|
|
5
|
+
for proper error handling and recovery.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Optional, Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CommandExecutionError(Exception):
|
|
12
|
+
"""
|
|
13
|
+
Base exception for command execution errors.
|
|
14
|
+
|
|
15
|
+
This is the base class for all command execution related exceptions.
|
|
16
|
+
"""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CommandTimeoutError(CommandExecutionError):
|
|
21
|
+
"""
|
|
22
|
+
Exception raised when a command execution times out.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
command: The command that timed out
|
|
26
|
+
timeout: The timeout value in seconds
|
|
27
|
+
pid: The process ID that timed out (if available)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, command: str, timeout: float, pid: Optional[int] = None):
|
|
31
|
+
self.command = command
|
|
32
|
+
self.timeout = timeout
|
|
33
|
+
self.pid = pid
|
|
34
|
+
|
|
35
|
+
message = f"Command '{command}' timed out after {timeout} seconds"
|
|
36
|
+
if pid:
|
|
37
|
+
message += f" (PID: {pid})"
|
|
38
|
+
|
|
39
|
+
super().__init__(message)
|
|
40
|
+
|
|
41
|
+
def __str__(self) -> str:
|
|
42
|
+
return super().__str__()
|
|
43
|
+
|
|
44
|
+
def __repr__(self) -> str:
|
|
45
|
+
return f"CommandTimeoutError(command={self.command!r}, timeout={self.timeout}, pid={self.pid})"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ProcessCleanupError(CommandExecutionError):
|
|
49
|
+
"""
|
|
50
|
+
Exception raised when process cleanup fails.
|
|
51
|
+
|
|
52
|
+
Attributes:
|
|
53
|
+
pid: The process ID that failed to cleanup
|
|
54
|
+
message: Detailed error message
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, pid: int, message: str):
|
|
58
|
+
self.pid = pid
|
|
59
|
+
self.original_message = message
|
|
60
|
+
super().__init__(f"Failed to cleanup process {pid}: {message}")
|
|
61
|
+
|
|
62
|
+
def __repr__(self) -> str:
|
|
63
|
+
return f"ProcessCleanupError(pid={self.pid}, message={self.original_message!r})"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ProcessNotFoundError(CommandExecutionError):
|
|
67
|
+
"""
|
|
68
|
+
Exception raised when a process is not found during cleanup or management.
|
|
69
|
+
|
|
70
|
+
Attributes:
|
|
71
|
+
pid: The process ID that was not found
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(self, pid: int):
|
|
75
|
+
self.pid = pid
|
|
76
|
+
super().__init__(f"Process {pid} not found")
|
|
77
|
+
|
|
78
|
+
def __repr__(self) -> str:
|
|
79
|
+
return f"ProcessNotFoundError(pid={self.pid})"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TimeoutConfigError(CommandExecutionError):
|
|
83
|
+
"""
|
|
84
|
+
Exception raised when there's an error in timeout configuration.
|
|
85
|
+
|
|
86
|
+
Attributes:
|
|
87
|
+
config_field: The configuration field that caused the error
|
|
88
|
+
value: The invalid value
|
|
89
|
+
reason: The reason why the value is invalid
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def __init__(self, config_field: str, value: Any, reason: str):
|
|
93
|
+
self.config_field = config_field
|
|
94
|
+
self.value = value
|
|
95
|
+
self.reason = reason
|
|
96
|
+
super().__init__(f"Invalid timeout configuration for '{config_field}': {reason} (value: {value})")
|
|
97
|
+
|
|
98
|
+
def __repr__(self) -> str:
|
|
99
|
+
return f"TimeoutConfigError(config_field={self.config_field!r}, value={self.value!r}, reason={self.reason!r})"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ErrorRecoveryFailedError(CommandExecutionError):
|
|
103
|
+
"""
|
|
104
|
+
Exception raised when error recovery mechanisms fail.
|
|
105
|
+
|
|
106
|
+
Attributes:
|
|
107
|
+
original_error: The original error that triggered recovery
|
|
108
|
+
recovery_attempts: Number of recovery attempts made
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def __init__(self, original_error: Exception, recovery_attempts: int):
|
|
112
|
+
self.original_error = original_error
|
|
113
|
+
self.recovery_attempts = recovery_attempts
|
|
114
|
+
super().__init__(
|
|
115
|
+
f"Error recovery failed after {recovery_attempts} attempts. "
|
|
116
|
+
f"Original error: {original_error}"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def __repr__(self) -> str:
|
|
120
|
+
return f"ErrorRecoveryFailedError(original_error={self.original_error!r}, recovery_attempts={self.recovery_attempts})"
|