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,1056 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test for core_config memory manager module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import tempfile
|
|
8
|
+
import json
|
|
9
|
+
import threading
|
|
10
|
+
import pytest
|
|
11
|
+
from unittest.mock import patch
|
|
12
|
+
|
|
13
|
+
from autocoder.common.core_config import (
|
|
14
|
+
MemoryManager,
|
|
15
|
+
CoreMemory,
|
|
16
|
+
get_memory_manager,
|
|
17
|
+
save_memory,
|
|
18
|
+
save_memory_with_new_memory,
|
|
19
|
+
load_memory,
|
|
20
|
+
get_memory,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TestCoreMemory:
|
|
25
|
+
"""Test CoreMemory data class"""
|
|
26
|
+
|
|
27
|
+
def test_init_default_values(self):
|
|
28
|
+
"""Test CoreMemory initialization with default values"""
|
|
29
|
+
memory = CoreMemory()
|
|
30
|
+
assert memory.conversation == []
|
|
31
|
+
assert memory.current_files == {"files": [], "groups": {}, "groups_info": {}, "current_groups": []}
|
|
32
|
+
assert memory.conf == {}
|
|
33
|
+
assert memory.exclude_dirs == []
|
|
34
|
+
assert memory.exclude_files == []
|
|
35
|
+
assert memory.libs == {}
|
|
36
|
+
assert memory.mode == "normal"
|
|
37
|
+
|
|
38
|
+
def test_to_dict(self):
|
|
39
|
+
"""Test CoreMemory to_dict method"""
|
|
40
|
+
memory = CoreMemory()
|
|
41
|
+
memory.conf["test"] = "value"
|
|
42
|
+
memory.conversation.append({"role": "user", "content": "test"})
|
|
43
|
+
|
|
44
|
+
data = memory.to_dict()
|
|
45
|
+
assert data["conf"]["test"] == "value"
|
|
46
|
+
assert len(data["conversation"]) == 1
|
|
47
|
+
assert data["mode"] == "normal"
|
|
48
|
+
|
|
49
|
+
def test_from_dict(self):
|
|
50
|
+
"""Test CoreMemory from_dict method"""
|
|
51
|
+
data = {
|
|
52
|
+
"conversation": [{"role": "user", "content": "test"}],
|
|
53
|
+
"current_files": {"files": ["file1.py"], "groups": {"test": ["file2.py"]}},
|
|
54
|
+
"conf": {"model": "gpt-4"},
|
|
55
|
+
"exclude_dirs": ["node_modules"],
|
|
56
|
+
"mode": "test_mode"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
memory = CoreMemory.from_dict(data)
|
|
60
|
+
assert len(memory.conversation) == 1
|
|
61
|
+
assert memory.current_files["files"] == ["file1.py"]
|
|
62
|
+
assert memory.conf["model"] == "gpt-4"
|
|
63
|
+
assert memory.exclude_dirs == ["node_modules"]
|
|
64
|
+
assert memory.mode == "test_mode"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TestMemoryManager:
|
|
68
|
+
"""Test MemoryManager class"""
|
|
69
|
+
|
|
70
|
+
@pytest.fixture
|
|
71
|
+
def temp_dir(self):
|
|
72
|
+
"""Create a temporary directory for testing"""
|
|
73
|
+
temp_dir = tempfile.mkdtemp()
|
|
74
|
+
yield temp_dir
|
|
75
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
76
|
+
|
|
77
|
+
def test_init_creates_directories(self, temp_dir):
|
|
78
|
+
"""Test that MemoryManager creates necessary directories"""
|
|
79
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
80
|
+
|
|
81
|
+
expected_dir = os.path.join(temp_dir, ".auto-coder", "plugins", "chat-auto-coder")
|
|
82
|
+
assert os.path.exists(expected_dir)
|
|
83
|
+
# memory.json is created after the first save
|
|
84
|
+
manager.save_memory()
|
|
85
|
+
assert os.path.exists(os.path.join(expected_dir, "memory.json"))
|
|
86
|
+
|
|
87
|
+
def test_singleton_pattern(self, temp_dir):
|
|
88
|
+
"""Test that MemoryManager follows singleton pattern"""
|
|
89
|
+
manager1 = MemoryManager(project_root=temp_dir)
|
|
90
|
+
manager2 = MemoryManager(project_root=temp_dir)
|
|
91
|
+
assert manager1 is manager2
|
|
92
|
+
|
|
93
|
+
# Different project root should create different instance
|
|
94
|
+
temp_dir2 = tempfile.mkdtemp()
|
|
95
|
+
try:
|
|
96
|
+
manager3 = MemoryManager(project_root=temp_dir2)
|
|
97
|
+
assert manager3 is not manager1
|
|
98
|
+
finally:
|
|
99
|
+
shutil.rmtree(temp_dir2, ignore_errors=True)
|
|
100
|
+
|
|
101
|
+
def test_config_management(self, temp_dir):
|
|
102
|
+
"""Test configuration get/set operations"""
|
|
103
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
104
|
+
|
|
105
|
+
# Set config
|
|
106
|
+
manager.set_config("test_key", "test_value")
|
|
107
|
+
manager.set_config("nested.key", {"inner": "value"})
|
|
108
|
+
|
|
109
|
+
# Get config
|
|
110
|
+
assert manager.get_config("test_key") == "test_value"
|
|
111
|
+
assert manager.get_config("nested.key")["inner"] == "value"
|
|
112
|
+
assert manager.get_config("non_existent") is None
|
|
113
|
+
assert manager.get_config("non_existent", "default") == "default"
|
|
114
|
+
|
|
115
|
+
# Delete config
|
|
116
|
+
assert manager.delete_config("test_key") is True
|
|
117
|
+
assert manager.get_config("test_key") is None
|
|
118
|
+
assert manager.delete_config("non_existent") is False
|
|
119
|
+
|
|
120
|
+
def test_extended_config_management(self, temp_dir):
|
|
121
|
+
"""Test extended configuration management methods"""
|
|
122
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
123
|
+
|
|
124
|
+
# Test batch setting
|
|
125
|
+
config_data = {
|
|
126
|
+
"model": "gpt-4",
|
|
127
|
+
"api_key": "test_key",
|
|
128
|
+
"temperature": 0.7,
|
|
129
|
+
"max_tokens": 1000
|
|
130
|
+
}
|
|
131
|
+
manager.set_configs(config_data)
|
|
132
|
+
|
|
133
|
+
# Test get_all_config
|
|
134
|
+
all_config = manager.get_all_config()
|
|
135
|
+
assert all_config["model"] == "gpt-4"
|
|
136
|
+
assert all_config["api_key"] == "test_key"
|
|
137
|
+
assert all_config["temperature"] == 0.7
|
|
138
|
+
assert all_config["max_tokens"] == 1000
|
|
139
|
+
|
|
140
|
+
# Test update_config (should add/update)
|
|
141
|
+
update_data = {
|
|
142
|
+
"model": "gpt-3.5-turbo", # Update existing
|
|
143
|
+
"new_param": "new_value" # Add new
|
|
144
|
+
}
|
|
145
|
+
manager.update_config(update_data)
|
|
146
|
+
assert manager.get_config("model") == "gpt-3.5-turbo"
|
|
147
|
+
assert manager.get_config("new_param") == "new_value"
|
|
148
|
+
assert manager.get_config("api_key") == "test_key" # Should remain
|
|
149
|
+
|
|
150
|
+
# Test has_config
|
|
151
|
+
assert manager.has_config("model") is True
|
|
152
|
+
assert manager.has_config("non_existent") is False
|
|
153
|
+
|
|
154
|
+
# Test get_config_keys
|
|
155
|
+
keys = manager.get_config_keys()
|
|
156
|
+
assert "model" in keys
|
|
157
|
+
assert "api_key" in keys
|
|
158
|
+
assert "new_param" in keys
|
|
159
|
+
|
|
160
|
+
# Test get_config_count
|
|
161
|
+
count = manager.get_config_count()
|
|
162
|
+
assert count == 5 # model, api_key, temperature, max_tokens, new_param
|
|
163
|
+
|
|
164
|
+
# Test clear_config
|
|
165
|
+
manager.clear_config()
|
|
166
|
+
assert manager.get_config_count() == 0
|
|
167
|
+
assert manager.get_all_config() == {}
|
|
168
|
+
|
|
169
|
+
def test_nested_config_management(self, temp_dir):
|
|
170
|
+
"""Test nested configuration management with dot notation"""
|
|
171
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
172
|
+
|
|
173
|
+
# Test set_nested_config
|
|
174
|
+
manager.set_nested_config("database.host", "localhost")
|
|
175
|
+
manager.set_nested_config("database.port", 5432)
|
|
176
|
+
manager.set_nested_config("database.credentials.username", "admin")
|
|
177
|
+
manager.set_nested_config("database.credentials.password", "secret")
|
|
178
|
+
manager.set_nested_config("model.llm.name", "gpt-4")
|
|
179
|
+
manager.set_nested_config("model.llm.temperature", 0.7)
|
|
180
|
+
|
|
181
|
+
# Test get_nested_config
|
|
182
|
+
assert manager.get_nested_config("database.host") == "localhost"
|
|
183
|
+
assert manager.get_nested_config("database.port") == 5432
|
|
184
|
+
assert manager.get_nested_config("database.credentials.username") == "admin"
|
|
185
|
+
assert manager.get_nested_config("database.credentials.password") == "secret"
|
|
186
|
+
assert manager.get_nested_config("model.llm.name") == "gpt-4"
|
|
187
|
+
assert manager.get_nested_config("model.llm.temperature") == 0.7
|
|
188
|
+
|
|
189
|
+
# Test get_nested_config with default
|
|
190
|
+
assert manager.get_nested_config("non.existent.key") is None
|
|
191
|
+
assert manager.get_nested_config("non.existent.key", "default") == "default"
|
|
192
|
+
|
|
193
|
+
# Test has_nested_config
|
|
194
|
+
assert manager.has_nested_config("database.host") is True
|
|
195
|
+
assert manager.has_nested_config("database.credentials.username") is True
|
|
196
|
+
assert manager.has_nested_config("model.llm") is True # Should work for parent keys too
|
|
197
|
+
assert manager.has_nested_config("non.existent.key") is False
|
|
198
|
+
|
|
199
|
+
# Verify structure in regular config
|
|
200
|
+
all_config = manager.get_all_config()
|
|
201
|
+
assert all_config["database"]["host"] == "localhost"
|
|
202
|
+
assert all_config["database"]["port"] == 5432
|
|
203
|
+
assert all_config["database"]["credentials"]["username"] == "admin"
|
|
204
|
+
assert all_config["model"]["llm"]["name"] == "gpt-4"
|
|
205
|
+
|
|
206
|
+
# Test delete_nested_config
|
|
207
|
+
assert manager.delete_nested_config("database.credentials.password") is True
|
|
208
|
+
assert manager.has_nested_config("database.credentials.password") is False
|
|
209
|
+
assert manager.has_nested_config("database.credentials.username") is True # Should remain
|
|
210
|
+
|
|
211
|
+
# Test deleting non-existent key
|
|
212
|
+
assert manager.delete_nested_config("non.existent.key") is False
|
|
213
|
+
|
|
214
|
+
# Test overwriting non-dict value
|
|
215
|
+
manager.set_config("simple_key", "simple_value")
|
|
216
|
+
manager.set_nested_config("simple_key.nested", "new_value")
|
|
217
|
+
assert manager.get_nested_config("simple_key.nested") == "new_value"
|
|
218
|
+
# Original simple value should be replaced with dict
|
|
219
|
+
assert isinstance(manager.get_config("simple_key"), dict)
|
|
220
|
+
|
|
221
|
+
def test_file_management(self, temp_dir):
|
|
222
|
+
"""Test file management operations"""
|
|
223
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
224
|
+
|
|
225
|
+
# Set files
|
|
226
|
+
files = ["file1.py", "file2.py", "file3.py"]
|
|
227
|
+
manager.set_current_files(files)
|
|
228
|
+
|
|
229
|
+
# Get files
|
|
230
|
+
current_files = manager.get_current_files()
|
|
231
|
+
assert set(current_files) == set(files)
|
|
232
|
+
|
|
233
|
+
# Clear files by setting empty list
|
|
234
|
+
manager.set_current_files([])
|
|
235
|
+
assert manager.get_current_files() == []
|
|
236
|
+
|
|
237
|
+
def test_group_management(self, temp_dir):
|
|
238
|
+
"""Test group management operations"""
|
|
239
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
240
|
+
|
|
241
|
+
# Set groups
|
|
242
|
+
manager.set_group("test_group", ["file1.py", "file2.py"])
|
|
243
|
+
manager.set_group("another_group", ["file3.py"])
|
|
244
|
+
|
|
245
|
+
# Get groups
|
|
246
|
+
groups = manager.get_groups()
|
|
247
|
+
assert "test_group" in groups
|
|
248
|
+
assert "another_group" in groups
|
|
249
|
+
assert set(groups["test_group"]) == {"file1.py", "file2.py"}
|
|
250
|
+
|
|
251
|
+
def test_conversation_management(self, temp_dir):
|
|
252
|
+
"""Test conversation management through memory dict"""
|
|
253
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
254
|
+
|
|
255
|
+
# Get memory and add messages
|
|
256
|
+
memory = manager.get_memory()
|
|
257
|
+
memory.conversation.append({"role": "user", "content": "Hello"})
|
|
258
|
+
memory.conversation.append({"role": "assistant", "content": "Hi there!"})
|
|
259
|
+
manager.save_memory()
|
|
260
|
+
|
|
261
|
+
# Reload and verify
|
|
262
|
+
manager2 = MemoryManager(project_root=temp_dir)
|
|
263
|
+
memory2 = manager2.get_memory()
|
|
264
|
+
assert len(memory2.conversation) == 2
|
|
265
|
+
assert memory2.conversation[0]["role"] == "user"
|
|
266
|
+
assert memory2.conversation[0]["content"] == "Hello"
|
|
267
|
+
assert memory2.conversation[1]["role"] == "assistant"
|
|
268
|
+
|
|
269
|
+
# Clear conversation
|
|
270
|
+
memory2.conversation = []
|
|
271
|
+
manager2.save_memory()
|
|
272
|
+
memory3 = manager2.get_memory()
|
|
273
|
+
assert memory3.conversation == []
|
|
274
|
+
|
|
275
|
+
def test_exclude_dirs_management(self, temp_dir):
|
|
276
|
+
"""Test exclude directories management"""
|
|
277
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
278
|
+
|
|
279
|
+
# Set exclude dirs through memory
|
|
280
|
+
memory = manager.get_memory()
|
|
281
|
+
memory.exclude_dirs = ["node_modules", "__pycache__", ".git"]
|
|
282
|
+
manager.save_memory()
|
|
283
|
+
|
|
284
|
+
# Verify
|
|
285
|
+
memory2 = manager.get_memory()
|
|
286
|
+
assert set(memory2.exclude_dirs) == {"node_modules", "__pycache__", ".git"}
|
|
287
|
+
|
|
288
|
+
# Add exclude dir
|
|
289
|
+
memory2.exclude_dirs.append("dist")
|
|
290
|
+
manager.save_memory()
|
|
291
|
+
memory3 = manager.get_memory()
|
|
292
|
+
assert "dist" in memory3.exclude_dirs
|
|
293
|
+
|
|
294
|
+
# Remove exclude dir
|
|
295
|
+
memory3.exclude_dirs.remove("dist")
|
|
296
|
+
manager.save_memory()
|
|
297
|
+
memory4 = manager.get_memory()
|
|
298
|
+
assert "dist" not in memory4.exclude_dirs
|
|
299
|
+
|
|
300
|
+
def test_mode_management(self, temp_dir):
|
|
301
|
+
"""Test mode management"""
|
|
302
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
303
|
+
|
|
304
|
+
# Default mode
|
|
305
|
+
memory = manager.get_memory()
|
|
306
|
+
assert memory.mode == "normal"
|
|
307
|
+
|
|
308
|
+
# Set mode
|
|
309
|
+
memory.mode = "test_mode"
|
|
310
|
+
manager.save_memory()
|
|
311
|
+
|
|
312
|
+
# Verify
|
|
313
|
+
memory2 = manager.get_memory()
|
|
314
|
+
assert memory2.mode == "test_mode"
|
|
315
|
+
|
|
316
|
+
def test_persistence(self, temp_dir):
|
|
317
|
+
"""Test that data persists across instances"""
|
|
318
|
+
# First instance
|
|
319
|
+
manager1 = MemoryManager(project_root=temp_dir)
|
|
320
|
+
manager1.set_config("persist_test", "value1")
|
|
321
|
+
memory1 = manager1.get_memory()
|
|
322
|
+
memory1.conversation.append({"role": "user", "content": "test message"})
|
|
323
|
+
manager1.save_memory()
|
|
324
|
+
manager1.set_current_files(["persistent_file.py"])
|
|
325
|
+
|
|
326
|
+
# Force new instance by clearing singleton cache
|
|
327
|
+
MemoryManager._instances.clear()
|
|
328
|
+
|
|
329
|
+
# Second instance should load persisted data
|
|
330
|
+
manager2 = MemoryManager(project_root=temp_dir)
|
|
331
|
+
assert manager2.get_config("persist_test") == "value1"
|
|
332
|
+
memory2 = manager2.get_memory()
|
|
333
|
+
assert len(memory2.conversation) == 1
|
|
334
|
+
assert "persistent_file.py" in manager2.get_current_files()
|
|
335
|
+
|
|
336
|
+
def test_thread_safety(self, temp_dir):
|
|
337
|
+
"""Test thread safety of memory manager"""
|
|
338
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
339
|
+
results = []
|
|
340
|
+
|
|
341
|
+
def worker(i):
|
|
342
|
+
try:
|
|
343
|
+
# Each thread sets its own config
|
|
344
|
+
manager.set_config(f"thread_{i}", f"value_{i}")
|
|
345
|
+
# Read back to verify
|
|
346
|
+
value = manager.get_config(f"thread_{i}")
|
|
347
|
+
results.append((i, value == f"value_{i}"))
|
|
348
|
+
except Exception as e:
|
|
349
|
+
results.append((i, False, str(e)))
|
|
350
|
+
|
|
351
|
+
# Run multiple threads
|
|
352
|
+
threads = []
|
|
353
|
+
for i in range(10):
|
|
354
|
+
t = threading.Thread(target=worker, args=(i,))
|
|
355
|
+
threads.append(t)
|
|
356
|
+
t.start()
|
|
357
|
+
|
|
358
|
+
# Wait for all threads
|
|
359
|
+
for t in threads:
|
|
360
|
+
t.join()
|
|
361
|
+
|
|
362
|
+
# Verify all operations succeeded
|
|
363
|
+
assert len(results) == 10
|
|
364
|
+
for result in results:
|
|
365
|
+
assert result[1] is True, f"Thread {result[0]} failed"
|
|
366
|
+
|
|
367
|
+
def test_file_convenience_methods(self, temp_dir):
|
|
368
|
+
"""Test new file management convenience methods"""
|
|
369
|
+
manager = MemoryManager(temp_dir)
|
|
370
|
+
|
|
371
|
+
# Test add_files
|
|
372
|
+
files_to_add = ["/path/to/file1.py", "/path/to/file2.py"]
|
|
373
|
+
new_files = manager.add_files(files_to_add)
|
|
374
|
+
assert new_files == files_to_add
|
|
375
|
+
assert manager.get_current_files() == files_to_add
|
|
376
|
+
|
|
377
|
+
# Test adding duplicate files (should be ignored)
|
|
378
|
+
duplicate_files = ["/path/to/file1.py", "/path/to/file3.py"]
|
|
379
|
+
new_files = manager.add_files(duplicate_files)
|
|
380
|
+
assert new_files == ["/path/to/file3.py"] # Only the new file
|
|
381
|
+
expected_files = ["/path/to/file1.py", "/path/to/file2.py", "/path/to/file3.py"]
|
|
382
|
+
assert manager.get_current_files() == expected_files
|
|
383
|
+
|
|
384
|
+
# Test remove_files
|
|
385
|
+
removed_files = manager.remove_files(["/path/to/file2.py"])
|
|
386
|
+
assert removed_files == ["/path/to/file2.py"]
|
|
387
|
+
expected_files = ["/path/to/file1.py", "/path/to/file3.py"]
|
|
388
|
+
assert manager.get_current_files() == expected_files
|
|
389
|
+
|
|
390
|
+
# Test clear_files
|
|
391
|
+
manager.clear_files()
|
|
392
|
+
assert manager.get_current_files() == []
|
|
393
|
+
|
|
394
|
+
def test_file_groups_convenience_methods(self, temp_dir):
|
|
395
|
+
"""Test new file groups convenience methods"""
|
|
396
|
+
manager = MemoryManager(temp_dir)
|
|
397
|
+
|
|
398
|
+
# Setup some current files
|
|
399
|
+
current_files = ["/backend/api.py", "/backend/db.py", "/frontend/app.js"]
|
|
400
|
+
manager.set_current_files(current_files)
|
|
401
|
+
|
|
402
|
+
# Test add_file_group with current files
|
|
403
|
+
manager.add_file_group("backend", []) # Use current files
|
|
404
|
+
assert manager.get_file_group("backend") == current_files
|
|
405
|
+
|
|
406
|
+
# Test add_file_group with specific files
|
|
407
|
+
frontend_files = ["/frontend/app.js", "/frontend/components.js"]
|
|
408
|
+
manager.add_file_group("frontend", frontend_files)
|
|
409
|
+
assert manager.get_file_group("frontend") == frontend_files
|
|
410
|
+
|
|
411
|
+
# Test has_file_group
|
|
412
|
+
assert manager.has_file_group("backend") == True
|
|
413
|
+
assert manager.has_file_group("frontend") == True
|
|
414
|
+
assert manager.has_file_group("nonexistent") == False
|
|
415
|
+
|
|
416
|
+
# Test delete_file_group
|
|
417
|
+
success = manager.delete_file_group("backend")
|
|
418
|
+
assert success == True
|
|
419
|
+
assert manager.has_file_group("backend") == False
|
|
420
|
+
|
|
421
|
+
# Test deleting non-existent group
|
|
422
|
+
success = manager.delete_file_group("nonexistent")
|
|
423
|
+
assert success == False
|
|
424
|
+
|
|
425
|
+
def test_groups_info_convenience_methods(self, temp_dir):
|
|
426
|
+
"""Test new groups info convenience methods"""
|
|
427
|
+
manager = MemoryManager(temp_dir)
|
|
428
|
+
|
|
429
|
+
# Setup a file group
|
|
430
|
+
manager.set_file_group("api", ["/api/routes.py", "/api/models.py"])
|
|
431
|
+
|
|
432
|
+
# Test set_group_query_prefix
|
|
433
|
+
manager.set_group_query_prefix("api", "API相关的路由和模型文件")
|
|
434
|
+
assert manager.get_group_query_prefix("api") == "API相关的路由和模型文件"
|
|
435
|
+
|
|
436
|
+
# Test get_groups_info
|
|
437
|
+
groups_info = manager.get_groups_info()
|
|
438
|
+
assert "api" in groups_info
|
|
439
|
+
assert groups_info["api"]["query_prefix"] == "API相关的路由和模型文件"
|
|
440
|
+
|
|
441
|
+
# Test set_group_info
|
|
442
|
+
custom_info = {
|
|
443
|
+
"query_prefix": "后端API",
|
|
444
|
+
"description": "处理数据库和API逻辑",
|
|
445
|
+
"priority": "high"
|
|
446
|
+
}
|
|
447
|
+
manager.set_group_info("api", custom_info)
|
|
448
|
+
assert manager.get_group_info("api") == custom_info
|
|
449
|
+
|
|
450
|
+
def test_current_groups_convenience_methods(self, temp_dir):
|
|
451
|
+
"""Test new current groups convenience methods"""
|
|
452
|
+
manager = MemoryManager(temp_dir)
|
|
453
|
+
|
|
454
|
+
# Setup some groups
|
|
455
|
+
manager.set_file_group("backend", ["/api.py"])
|
|
456
|
+
manager.set_file_group("frontend", ["/app.js"])
|
|
457
|
+
manager.set_file_group("utils", ["/utils.py"])
|
|
458
|
+
|
|
459
|
+
# Test add_current_group
|
|
460
|
+
manager.add_current_group("backend")
|
|
461
|
+
assert manager.get_current_groups() == ["backend"]
|
|
462
|
+
|
|
463
|
+
manager.add_current_group("frontend")
|
|
464
|
+
assert "backend" in manager.get_current_groups()
|
|
465
|
+
assert "frontend" in manager.get_current_groups()
|
|
466
|
+
|
|
467
|
+
# Test set_current_groups
|
|
468
|
+
manager.set_current_groups(["backend", "utils"])
|
|
469
|
+
assert manager.get_current_groups() == ["backend", "utils"]
|
|
470
|
+
|
|
471
|
+
# Test remove_current_group
|
|
472
|
+
success = manager.remove_current_group("backend")
|
|
473
|
+
assert success == True
|
|
474
|
+
assert manager.get_current_groups() == ["utils"]
|
|
475
|
+
|
|
476
|
+
# Test removing non-existent group
|
|
477
|
+
success = manager.remove_current_group("nonexistent")
|
|
478
|
+
assert success == False
|
|
479
|
+
|
|
480
|
+
# Test clear_current_groups
|
|
481
|
+
manager.clear_current_groups()
|
|
482
|
+
assert manager.get_current_groups() == []
|
|
483
|
+
|
|
484
|
+
def test_merge_groups_convenience_methods(self, temp_dir):
|
|
485
|
+
"""Test merge groups to current files convenience methods"""
|
|
486
|
+
manager = MemoryManager(temp_dir)
|
|
487
|
+
|
|
488
|
+
# Setup file groups
|
|
489
|
+
manager.set_file_group("backend", ["/api.py", "/db.py"])
|
|
490
|
+
manager.set_file_group("frontend", ["/app.js", "/components.js"])
|
|
491
|
+
manager.set_file_group("tests", ["/test_api.py"])
|
|
492
|
+
|
|
493
|
+
# Test merging existing groups
|
|
494
|
+
missing_groups = manager.merge_groups_to_current_files(["backend", "frontend"])
|
|
495
|
+
assert missing_groups == []
|
|
496
|
+
|
|
497
|
+
# Verify merged files
|
|
498
|
+
current_files = set(manager.get_current_files())
|
|
499
|
+
expected_files = {"/api.py", "/db.py", "/app.js", "/components.js"}
|
|
500
|
+
assert current_files == expected_files
|
|
501
|
+
|
|
502
|
+
# Verify current groups
|
|
503
|
+
current_groups = manager.get_current_groups()
|
|
504
|
+
assert "backend" in current_groups
|
|
505
|
+
assert "frontend" in current_groups
|
|
506
|
+
|
|
507
|
+
# Test merging with some non-existent groups
|
|
508
|
+
missing_groups = manager.merge_groups_to_current_files(["tests", "nonexistent"])
|
|
509
|
+
assert missing_groups == ["nonexistent"]
|
|
510
|
+
|
|
511
|
+
# Verify only existing group was merged
|
|
512
|
+
current_files = set(manager.get_current_files())
|
|
513
|
+
expected_files = {"/test_api.py"}
|
|
514
|
+
assert current_files == expected_files
|
|
515
|
+
assert manager.get_current_groups() == ["tests"]
|
|
516
|
+
|
|
517
|
+
def test_exclude_files_convenience_methods(self, temp_dir):
|
|
518
|
+
"""Test new exclude files convenience methods"""
|
|
519
|
+
manager = MemoryManager(temp_dir)
|
|
520
|
+
|
|
521
|
+
# Test add_exclude_files
|
|
522
|
+
patterns_to_add = ["regex://.*\\.log$", "regex://.*\\.tmp$"]
|
|
523
|
+
new_patterns = manager.add_exclude_files(patterns_to_add)
|
|
524
|
+
assert new_patterns == patterns_to_add
|
|
525
|
+
assert manager.get_exclude_files() == patterns_to_add
|
|
526
|
+
|
|
527
|
+
# Test adding duplicate patterns (should be ignored)
|
|
528
|
+
duplicate_patterns = ["regex://.*\\.log$", "regex://.*\\.cache$"]
|
|
529
|
+
new_patterns = manager.add_exclude_files(duplicate_patterns)
|
|
530
|
+
assert new_patterns == ["regex://.*\\.cache$"] # Only the new pattern
|
|
531
|
+
expected_patterns = ["regex://.*\\.log$", "regex://.*\\.tmp$", "regex://.*\\.cache$"]
|
|
532
|
+
assert manager.get_exclude_files() == expected_patterns
|
|
533
|
+
|
|
534
|
+
# Test remove_exclude_files
|
|
535
|
+
removed_patterns = manager.remove_exclude_files(["regex://.*\\.tmp$"])
|
|
536
|
+
assert removed_patterns == ["regex://.*\\.tmp$"]
|
|
537
|
+
expected_patterns = ["regex://.*\\.log$", "regex://.*\\.cache$"]
|
|
538
|
+
assert manager.get_exclude_files() == expected_patterns
|
|
539
|
+
|
|
540
|
+
# Test clear_exclude_files
|
|
541
|
+
manager.clear_exclude_files()
|
|
542
|
+
assert manager.get_exclude_files() == []
|
|
543
|
+
|
|
544
|
+
def test_exclude_dirs_convenience_methods(self, temp_dir):
|
|
545
|
+
"""Test new exclude dirs convenience methods"""
|
|
546
|
+
manager = MemoryManager(temp_dir)
|
|
547
|
+
|
|
548
|
+
# Test add_exclude_dirs
|
|
549
|
+
dirs_to_add = ["node_modules", "dist"]
|
|
550
|
+
new_dirs = manager.add_exclude_dirs(dirs_to_add)
|
|
551
|
+
assert new_dirs == dirs_to_add
|
|
552
|
+
assert manager.get_exclude_dirs() == dirs_to_add
|
|
553
|
+
|
|
554
|
+
# Test adding duplicate dirs (should be ignored)
|
|
555
|
+
duplicate_dirs = ["node_modules", "build"]
|
|
556
|
+
new_dirs = manager.add_exclude_dirs(duplicate_dirs)
|
|
557
|
+
assert new_dirs == ["build"] # Only the new dir
|
|
558
|
+
expected_dirs = ["node_modules", "dist", "build"]
|
|
559
|
+
assert manager.get_exclude_dirs() == expected_dirs
|
|
560
|
+
|
|
561
|
+
# Test remove_exclude_dirs
|
|
562
|
+
removed_dirs = manager.remove_exclude_dirs(["dist"])
|
|
563
|
+
assert removed_dirs == ["dist"]
|
|
564
|
+
expected_dirs = ["node_modules", "build"]
|
|
565
|
+
assert manager.get_exclude_dirs() == expected_dirs
|
|
566
|
+
|
|
567
|
+
# Test clear_exclude_dirs
|
|
568
|
+
manager.clear_exclude_dirs()
|
|
569
|
+
assert manager.get_exclude_dirs() == []
|
|
570
|
+
|
|
571
|
+
def test_libs_convenience_methods(self, temp_dir):
|
|
572
|
+
"""Test new libs convenience methods"""
|
|
573
|
+
manager = MemoryManager(temp_dir)
|
|
574
|
+
|
|
575
|
+
# Test add_lib
|
|
576
|
+
success = manager.add_lib("numpy", {"version": "1.21.0"})
|
|
577
|
+
assert success == True
|
|
578
|
+
assert manager.has_lib("numpy") == True
|
|
579
|
+
|
|
580
|
+
# Test adding duplicate lib (should fail)
|
|
581
|
+
success = manager.add_lib("numpy", {"version": "1.22.0"})
|
|
582
|
+
assert success == False
|
|
583
|
+
|
|
584
|
+
# Test get_lib_config
|
|
585
|
+
config = manager.get_lib_config("numpy")
|
|
586
|
+
assert config == {"version": "1.21.0"}
|
|
587
|
+
|
|
588
|
+
# Test set_lib_config
|
|
589
|
+
new_config = {"version": "1.22.0", "extras": ["dev"]}
|
|
590
|
+
manager.set_lib_config("numpy", new_config)
|
|
591
|
+
assert manager.get_lib_config("numpy") == new_config
|
|
592
|
+
|
|
593
|
+
# Test get_libs
|
|
594
|
+
libs = manager.get_libs()
|
|
595
|
+
assert "numpy" in libs
|
|
596
|
+
assert libs["numpy"] == new_config
|
|
597
|
+
|
|
598
|
+
# Test lib proxy
|
|
599
|
+
manager.set_lib_proxy("https://custom-proxy.com/packages")
|
|
600
|
+
assert manager.get_lib_proxy() == "https://custom-proxy.com/packages"
|
|
601
|
+
|
|
602
|
+
# Test remove_lib
|
|
603
|
+
success = manager.remove_lib("numpy")
|
|
604
|
+
assert success == True
|
|
605
|
+
assert manager.has_lib("numpy") == False
|
|
606
|
+
|
|
607
|
+
# Test removing non-existent lib
|
|
608
|
+
success = manager.remove_lib("nonexistent")
|
|
609
|
+
assert success == False
|
|
610
|
+
|
|
611
|
+
def test_conversation_convenience_methods(self, temp_dir):
|
|
612
|
+
"""Test new conversation convenience methods"""
|
|
613
|
+
manager = MemoryManager(temp_dir)
|
|
614
|
+
|
|
615
|
+
# Test add_conversation_message
|
|
616
|
+
manager.add_conversation_message("user", "Hello")
|
|
617
|
+
manager.add_conversation_message("assistant", "Hi there!")
|
|
618
|
+
|
|
619
|
+
conversation = manager.get_conversation()
|
|
620
|
+
assert len(conversation) == 2
|
|
621
|
+
assert conversation[0] == {"role": "user", "content": "Hello"}
|
|
622
|
+
assert conversation[1] == {"role": "assistant", "content": "Hi there!"}
|
|
623
|
+
|
|
624
|
+
# Test set_conversation
|
|
625
|
+
new_conversation = [
|
|
626
|
+
{"role": "user", "content": "New conversation"},
|
|
627
|
+
{"role": "assistant", "content": "Got it!"}
|
|
628
|
+
]
|
|
629
|
+
manager.set_conversation(new_conversation)
|
|
630
|
+
assert manager.get_conversation() == new_conversation
|
|
631
|
+
|
|
632
|
+
# Test clear_conversation
|
|
633
|
+
manager.clear_conversation()
|
|
634
|
+
assert manager.get_conversation() == []
|
|
635
|
+
|
|
636
|
+
def test_mode_management(self, temp_dir):
|
|
637
|
+
"""Test mode management functionality."""
|
|
638
|
+
manager = MemoryManager(temp_dir)
|
|
639
|
+
|
|
640
|
+
# Test default mode
|
|
641
|
+
assert manager.get_mode() == "normal"
|
|
642
|
+
|
|
643
|
+
# Test setting modes
|
|
644
|
+
manager.set_mode("normal")
|
|
645
|
+
assert manager.get_mode() == "normal"
|
|
646
|
+
assert manager.is_normal_mode() == True
|
|
647
|
+
assert manager.is_auto_detect_mode() == False
|
|
648
|
+
assert manager.is_voice_input_mode() == False
|
|
649
|
+
|
|
650
|
+
manager.set_mode("voice_input")
|
|
651
|
+
assert manager.get_mode() == "voice_input"
|
|
652
|
+
assert manager.is_voice_input_mode() == True
|
|
653
|
+
|
|
654
|
+
# Test invalid mode
|
|
655
|
+
try:
|
|
656
|
+
manager.set_mode("invalid_mode")
|
|
657
|
+
assert False, "Should have raised ValueError"
|
|
658
|
+
except ValueError as e:
|
|
659
|
+
assert "Invalid mode" in str(e)
|
|
660
|
+
|
|
661
|
+
# Test mode cycling
|
|
662
|
+
manager.set_mode("normal")
|
|
663
|
+
next_mode = manager.cycle_mode()
|
|
664
|
+
assert next_mode == "auto_detect"
|
|
665
|
+
assert manager.get_mode() == "auto_detect"
|
|
666
|
+
|
|
667
|
+
next_mode = manager.cycle_mode()
|
|
668
|
+
assert next_mode == "voice_input"
|
|
669
|
+
assert manager.get_mode() == "voice_input"
|
|
670
|
+
|
|
671
|
+
next_mode = manager.cycle_mode()
|
|
672
|
+
assert next_mode == "normal"
|
|
673
|
+
assert manager.get_mode() == "normal"
|
|
674
|
+
|
|
675
|
+
# Test available modes
|
|
676
|
+
available_modes = manager.get_available_modes()
|
|
677
|
+
assert "normal" in available_modes
|
|
678
|
+
assert "auto_detect" in available_modes
|
|
679
|
+
assert "voice_input" in available_modes
|
|
680
|
+
assert "shell" in available_modes
|
|
681
|
+
|
|
682
|
+
# Test reset to default
|
|
683
|
+
manager.reset_mode_to_default()
|
|
684
|
+
assert manager.get_mode() == "normal"
|
|
685
|
+
|
|
686
|
+
# Test ensure valid mode
|
|
687
|
+
manager._memory.mode = "invalid" # Directly set invalid mode
|
|
688
|
+
valid_mode = manager.ensure_mode_valid()
|
|
689
|
+
assert valid_mode == "normal"
|
|
690
|
+
assert manager.get_mode() == "normal"
|
|
691
|
+
|
|
692
|
+
# Test persistence
|
|
693
|
+
manager.set_mode("voice_input")
|
|
694
|
+
|
|
695
|
+
# Create new manager instance to test persistence
|
|
696
|
+
manager2 = MemoryManager(temp_dir)
|
|
697
|
+
assert manager2.get_mode() == "voice_input"
|
|
698
|
+
|
|
699
|
+
def test_shell_mode_functionality(self, temp_dir):
|
|
700
|
+
"""Test shell mode specific functionality."""
|
|
701
|
+
manager = MemoryManager(temp_dir)
|
|
702
|
+
|
|
703
|
+
# Test shell mode setting
|
|
704
|
+
manager.set_mode("shell")
|
|
705
|
+
assert manager.get_mode() == "shell"
|
|
706
|
+
assert manager.is_shell_mode() == True
|
|
707
|
+
assert manager.is_normal_mode() == False
|
|
708
|
+
assert manager.is_auto_detect_mode() == False
|
|
709
|
+
assert manager.is_voice_input_mode() == False
|
|
710
|
+
|
|
711
|
+
# Test mode cycling includes shell mode
|
|
712
|
+
manager.set_mode("voice_input")
|
|
713
|
+
next_mode = manager.cycle_mode()
|
|
714
|
+
assert next_mode == "shell"
|
|
715
|
+
assert manager.get_mode() == "shell"
|
|
716
|
+
|
|
717
|
+
# Test cycling from shell back to normal
|
|
718
|
+
next_mode = manager.cycle_mode()
|
|
719
|
+
assert next_mode == "normal"
|
|
720
|
+
assert manager.get_mode() == "normal"
|
|
721
|
+
|
|
722
|
+
# Test complete cycle: normal → auto_detect → voice_input → shell → normal
|
|
723
|
+
manager.set_mode("normal")
|
|
724
|
+
|
|
725
|
+
next_mode = manager.cycle_mode()
|
|
726
|
+
assert next_mode == "auto_detect"
|
|
727
|
+
|
|
728
|
+
next_mode = manager.cycle_mode()
|
|
729
|
+
assert next_mode == "voice_input"
|
|
730
|
+
|
|
731
|
+
next_mode = manager.cycle_mode()
|
|
732
|
+
assert next_mode == "shell"
|
|
733
|
+
|
|
734
|
+
next_mode = manager.cycle_mode()
|
|
735
|
+
assert next_mode == "normal"
|
|
736
|
+
|
|
737
|
+
# Test shell mode persistence
|
|
738
|
+
manager.set_mode("shell")
|
|
739
|
+
manager.save_memory()
|
|
740
|
+
|
|
741
|
+
# Create new manager instance to test persistence
|
|
742
|
+
MemoryManager._instances.clear()
|
|
743
|
+
manager2 = MemoryManager(temp_dir)
|
|
744
|
+
assert manager2.get_mode() == "shell"
|
|
745
|
+
assert manager2.is_shell_mode() == True
|
|
746
|
+
|
|
747
|
+
def test_human_as_model_management(self, temp_dir):
|
|
748
|
+
"""Test human_as_model management functionality."""
|
|
749
|
+
manager = MemoryManager(temp_dir)
|
|
750
|
+
|
|
751
|
+
# Test default status
|
|
752
|
+
assert manager.get_human_as_model() == False
|
|
753
|
+
assert manager.get_human_as_model_string() == "false"
|
|
754
|
+
assert manager.is_human_as_model_enabled() == False
|
|
755
|
+
|
|
756
|
+
# Test enabling
|
|
757
|
+
manager.enable_human_as_model()
|
|
758
|
+
assert manager.get_human_as_model() == True
|
|
759
|
+
assert manager.get_human_as_model_string() == "true"
|
|
760
|
+
assert manager.is_human_as_model_enabled() == True
|
|
761
|
+
|
|
762
|
+
# Test disabling
|
|
763
|
+
manager.disable_human_as_model()
|
|
764
|
+
assert manager.get_human_as_model() == False
|
|
765
|
+
assert manager.get_human_as_model_string() == "false"
|
|
766
|
+
|
|
767
|
+
# Test setting with boolean
|
|
768
|
+
manager.set_human_as_model(True)
|
|
769
|
+
assert manager.get_human_as_model() == True
|
|
770
|
+
|
|
771
|
+
manager.set_human_as_model(False)
|
|
772
|
+
assert manager.get_human_as_model() == False
|
|
773
|
+
|
|
774
|
+
# Test toggling
|
|
775
|
+
initial_status = manager.get_human_as_model()
|
|
776
|
+
new_status = manager.toggle_human_as_model()
|
|
777
|
+
assert new_status != initial_status
|
|
778
|
+
assert manager.get_human_as_model() == new_status
|
|
779
|
+
|
|
780
|
+
# Test toggle again
|
|
781
|
+
newer_status = manager.toggle_human_as_model()
|
|
782
|
+
assert newer_status == initial_status
|
|
783
|
+
assert manager.get_human_as_model() == initial_status
|
|
784
|
+
|
|
785
|
+
# Test setting from string
|
|
786
|
+
manager.set_human_as_model_from_string("true")
|
|
787
|
+
assert manager.get_human_as_model() == True
|
|
788
|
+
|
|
789
|
+
manager.set_human_as_model_from_string("false")
|
|
790
|
+
assert manager.get_human_as_model() == False
|
|
791
|
+
|
|
792
|
+
manager.set_human_as_model_from_string("1")
|
|
793
|
+
assert manager.get_human_as_model() == True
|
|
794
|
+
|
|
795
|
+
manager.set_human_as_model_from_string("0")
|
|
796
|
+
assert manager.get_human_as_model() == False
|
|
797
|
+
|
|
798
|
+
manager.set_human_as_model_from_string("yes")
|
|
799
|
+
assert manager.get_human_as_model() == True
|
|
800
|
+
|
|
801
|
+
manager.set_human_as_model_from_string("no")
|
|
802
|
+
assert manager.get_human_as_model() == False
|
|
803
|
+
|
|
804
|
+
# Test ensure config
|
|
805
|
+
manager._memory.conf = {} # Clear config
|
|
806
|
+
status = manager.ensure_human_as_model_config()
|
|
807
|
+
assert status == False # Should default to False
|
|
808
|
+
assert "human_as_model" in manager._memory.conf
|
|
809
|
+
assert manager._memory.conf["human_as_model"] == "false"
|
|
810
|
+
|
|
811
|
+
# Test persistence
|
|
812
|
+
manager.set_human_as_model(True)
|
|
813
|
+
|
|
814
|
+
# Create new manager instance to test persistence
|
|
815
|
+
manager2 = MemoryManager(temp_dir)
|
|
816
|
+
assert manager2.get_human_as_model() == True
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
class TestCompatibilityFunctions:
|
|
820
|
+
"""Test compatibility functions"""
|
|
821
|
+
|
|
822
|
+
@pytest.fixture
|
|
823
|
+
def temp_cwd(self):
|
|
824
|
+
"""Create temporary directory and change to it"""
|
|
825
|
+
original_cwd = os.getcwd()
|
|
826
|
+
temp_dir = tempfile.mkdtemp()
|
|
827
|
+
os.chdir(temp_dir)
|
|
828
|
+
yield temp_dir
|
|
829
|
+
os.chdir(original_cwd)
|
|
830
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
831
|
+
|
|
832
|
+
def test_get_memory(self, temp_cwd):
|
|
833
|
+
"""Test get_memory function"""
|
|
834
|
+
memory = get_memory()
|
|
835
|
+
assert isinstance(memory, dict)
|
|
836
|
+
assert "conversation" in memory
|
|
837
|
+
assert "current_files" in memory
|
|
838
|
+
assert "conf" in memory
|
|
839
|
+
assert "exclude_dirs" in memory
|
|
840
|
+
assert "mode" in memory
|
|
841
|
+
|
|
842
|
+
def test_save_and_load_memory(self, temp_cwd):
|
|
843
|
+
"""Test save_memory and load_memory functions"""
|
|
844
|
+
# Get initial memory
|
|
845
|
+
memory = get_memory()
|
|
846
|
+
|
|
847
|
+
# Modify memory
|
|
848
|
+
memory["conf"]["test_key"] = "test_value"
|
|
849
|
+
memory["conversation"].append({"role": "user", "content": "test"})
|
|
850
|
+
|
|
851
|
+
# Save memory
|
|
852
|
+
save_memory()
|
|
853
|
+
|
|
854
|
+
# Load memory
|
|
855
|
+
loaded_memory = load_memory()
|
|
856
|
+
assert loaded_memory["conf"]["test_key"] == "test_value"
|
|
857
|
+
assert len(loaded_memory["conversation"]) == 1
|
|
858
|
+
|
|
859
|
+
def test_save_memory_with_new_memory(self, temp_cwd):
|
|
860
|
+
"""Test save_memory_with_new_memory function"""
|
|
861
|
+
new_memory = {
|
|
862
|
+
"conversation": [{"role": "assistant", "content": "new message"}],
|
|
863
|
+
"current_files": {"files": ["new_file.py"], "groups": {}},
|
|
864
|
+
"conf": {"new_config": "new_value"},
|
|
865
|
+
"exclude_dirs": ["new_exclude"],
|
|
866
|
+
"mode": "new_mode"
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
# Save new memory
|
|
870
|
+
save_memory_with_new_memory(new_memory)
|
|
871
|
+
|
|
872
|
+
# Verify
|
|
873
|
+
loaded_memory = load_memory()
|
|
874
|
+
assert loaded_memory["conf"]["new_config"] == "new_value"
|
|
875
|
+
assert loaded_memory["mode"] == "new_mode"
|
|
876
|
+
assert "new_file.py" in loaded_memory["current_files"]["files"]
|
|
877
|
+
|
|
878
|
+
def test_get_memory_manager_function(self, temp_cwd):
|
|
879
|
+
"""Test get_memory_manager function"""
|
|
880
|
+
# Without project root (uses cwd)
|
|
881
|
+
manager1 = get_memory_manager()
|
|
882
|
+
# Use os.path.realpath to resolve symlinks
|
|
883
|
+
assert os.path.realpath(manager1.project_root) == os.path.realpath(temp_cwd)
|
|
884
|
+
|
|
885
|
+
# With explicit project root
|
|
886
|
+
sub_dir = os.path.join(temp_cwd, "subproject")
|
|
887
|
+
os.makedirs(sub_dir)
|
|
888
|
+
manager2 = get_memory_manager(sub_dir)
|
|
889
|
+
assert os.path.realpath(manager2.project_root) == os.path.realpath(sub_dir)
|
|
890
|
+
assert manager2 is not manager1
|
|
891
|
+
|
|
892
|
+
def test_mode_compatibility_functions(self, temp_cwd):
|
|
893
|
+
"""Test mode management compatibility functions"""
|
|
894
|
+
from autocoder.common.core_config import get_mode, set_mode, cycle_mode
|
|
895
|
+
|
|
896
|
+
# Test default mode
|
|
897
|
+
assert get_mode() == "normal"
|
|
898
|
+
|
|
899
|
+
# Test setting mode
|
|
900
|
+
set_mode("normal")
|
|
901
|
+
assert get_mode() == "normal"
|
|
902
|
+
|
|
903
|
+
set_mode("voice_input")
|
|
904
|
+
assert get_mode() == "voice_input"
|
|
905
|
+
|
|
906
|
+
# Test cycling mode
|
|
907
|
+
set_mode("normal")
|
|
908
|
+
next_mode = cycle_mode()
|
|
909
|
+
assert next_mode == "auto_detect"
|
|
910
|
+
assert get_mode() == "auto_detect"
|
|
911
|
+
|
|
912
|
+
next_mode = cycle_mode()
|
|
913
|
+
assert next_mode == "voice_input"
|
|
914
|
+
assert get_mode() == "voice_input"
|
|
915
|
+
|
|
916
|
+
next_mode = cycle_mode()
|
|
917
|
+
assert next_mode == "normal"
|
|
918
|
+
assert get_mode() == "normal"
|
|
919
|
+
|
|
920
|
+
def test_human_as_model_compatibility_functions(self, temp_cwd):
|
|
921
|
+
"""Test human_as_model management compatibility functions"""
|
|
922
|
+
from autocoder.common.core_config import (
|
|
923
|
+
get_human_as_model,
|
|
924
|
+
set_human_as_model,
|
|
925
|
+
toggle_human_as_model,
|
|
926
|
+
get_human_as_model_string
|
|
927
|
+
)
|
|
928
|
+
|
|
929
|
+
# Test default status
|
|
930
|
+
assert get_human_as_model() == False
|
|
931
|
+
assert get_human_as_model_string() == "false"
|
|
932
|
+
|
|
933
|
+
# Test setting
|
|
934
|
+
set_human_as_model(True)
|
|
935
|
+
assert get_human_as_model() == True
|
|
936
|
+
assert get_human_as_model_string() == "true"
|
|
937
|
+
|
|
938
|
+
set_human_as_model(False)
|
|
939
|
+
assert get_human_as_model() == False
|
|
940
|
+
assert get_human_as_model_string() == "false"
|
|
941
|
+
|
|
942
|
+
# Test toggling
|
|
943
|
+
initial_status = get_human_as_model()
|
|
944
|
+
new_status = toggle_human_as_model()
|
|
945
|
+
assert new_status != initial_status
|
|
946
|
+
assert get_human_as_model() == new_status
|
|
947
|
+
|
|
948
|
+
# Test toggle again
|
|
949
|
+
newer_status = toggle_human_as_model()
|
|
950
|
+
assert newer_status == initial_status
|
|
951
|
+
assert get_human_as_model() == initial_status
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
class TestEdgeCases:
|
|
955
|
+
"""Test edge cases and error handling"""
|
|
956
|
+
|
|
957
|
+
@pytest.fixture
|
|
958
|
+
def temp_dir(self):
|
|
959
|
+
"""Create a temporary directory for testing"""
|
|
960
|
+
temp_dir = tempfile.mkdtemp()
|
|
961
|
+
yield temp_dir
|
|
962
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
963
|
+
|
|
964
|
+
def test_corrupted_memory_file(self, temp_dir):
|
|
965
|
+
"""Test handling of corrupted memory.json file"""
|
|
966
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
967
|
+
|
|
968
|
+
# Write corrupted JSON
|
|
969
|
+
memory_path = os.path.join(
|
|
970
|
+
temp_dir, ".auto-coder", "plugins", "chat-auto-coder", "memory.json"
|
|
971
|
+
)
|
|
972
|
+
with open(memory_path, "w") as f:
|
|
973
|
+
f.write("{ corrupted json")
|
|
974
|
+
|
|
975
|
+
# Should handle gracefully and use default memory
|
|
976
|
+
MemoryManager._instances.clear() # Force reload
|
|
977
|
+
# The implementation currently raises JSONDecodeError, so let's check for that
|
|
978
|
+
try:
|
|
979
|
+
manager2 = MemoryManager(project_root=temp_dir)
|
|
980
|
+
# If no error, the implementation was changed to handle it
|
|
981
|
+
memory = manager2.get_memory_dict()
|
|
982
|
+
assert "conversation" in memory # Should have default structure
|
|
983
|
+
except json.JSONDecodeError:
|
|
984
|
+
# Current implementation doesn't handle corrupted JSON
|
|
985
|
+
# This is expected behavior for now
|
|
986
|
+
pass
|
|
987
|
+
|
|
988
|
+
def test_readonly_directory(self, temp_dir):
|
|
989
|
+
"""Test handling of read-only directory"""
|
|
990
|
+
# Skip this test on Windows as permission handling is different
|
|
991
|
+
if os.name == 'nt':
|
|
992
|
+
pytest.skip("Permission test not applicable on Windows")
|
|
993
|
+
|
|
994
|
+
# Create directory structure
|
|
995
|
+
base_dir = os.path.join(temp_dir, ".auto-coder", "plugins", "chat-auto-coder")
|
|
996
|
+
os.makedirs(base_dir)
|
|
997
|
+
|
|
998
|
+
# Make directory read-only
|
|
999
|
+
os.chmod(base_dir, 0o444)
|
|
1000
|
+
|
|
1001
|
+
try:
|
|
1002
|
+
# Should handle gracefully
|
|
1003
|
+
# The implementation currently raises PermissionError, which is reasonable
|
|
1004
|
+
with pytest.raises(PermissionError):
|
|
1005
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
1006
|
+
manager.set_config("test", "value")
|
|
1007
|
+
finally:
|
|
1008
|
+
# Restore permissions for cleanup
|
|
1009
|
+
os.chmod(base_dir, 0o755)
|
|
1010
|
+
|
|
1011
|
+
def test_concurrent_access(self, temp_dir):
|
|
1012
|
+
"""Test concurrent access to memory file"""
|
|
1013
|
+
# Use threading instead of multiprocessing for simpler testing
|
|
1014
|
+
import threading
|
|
1015
|
+
|
|
1016
|
+
results = []
|
|
1017
|
+
|
|
1018
|
+
def worker_thread(temp_dir, worker_id):
|
|
1019
|
+
"""Worker thread that modifies memory"""
|
|
1020
|
+
try:
|
|
1021
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
1022
|
+
for i in range(5):
|
|
1023
|
+
manager.set_config(f"worker_{worker_id}_key_{i}", f"value_{i}")
|
|
1024
|
+
manager.save_memory()
|
|
1025
|
+
results.append((worker_id, True))
|
|
1026
|
+
except Exception as e:
|
|
1027
|
+
results.append((worker_id, False, str(e)))
|
|
1028
|
+
|
|
1029
|
+
# Run multiple threads
|
|
1030
|
+
threads = []
|
|
1031
|
+
for i in range(3):
|
|
1032
|
+
t = threading.Thread(target=worker_thread, args=(temp_dir, i))
|
|
1033
|
+
threads.append(t)
|
|
1034
|
+
t.start()
|
|
1035
|
+
|
|
1036
|
+
# Wait for all threads
|
|
1037
|
+
for t in threads:
|
|
1038
|
+
t.join()
|
|
1039
|
+
|
|
1040
|
+
# Verify all threads completed successfully
|
|
1041
|
+
assert len(results) == 3
|
|
1042
|
+
for result in results:
|
|
1043
|
+
assert result[1] is True, f"Worker {result[0]} failed"
|
|
1044
|
+
|
|
1045
|
+
# Verify data integrity
|
|
1046
|
+
manager = MemoryManager(project_root=temp_dir)
|
|
1047
|
+
memory = manager.get_memory()
|
|
1048
|
+
config = memory.conf
|
|
1049
|
+
|
|
1050
|
+
# Should have some keys from each worker
|
|
1051
|
+
worker_keys = [k for k in config.keys() if k.startswith("worker_")]
|
|
1052
|
+
assert len(worker_keys) > 0 # At least some keys should be saved
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
if __name__ == "__main__":
|
|
1056
|
+
pytest.main([__file__, "-v"])
|