auto-coder 1.0.0__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (574) hide show
  1. auto_coder-2.0.0.dist-info/LICENSE +158 -0
  2. auto_coder-2.0.0.dist-info/METADATA +558 -0
  3. auto_coder-2.0.0.dist-info/RECORD +795 -0
  4. {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
  5. {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/entry_points.txt +3 -3
  6. autocoder/__init__.py +31 -0
  7. autocoder/agent/auto_filegroup.py +32 -13
  8. autocoder/agent/auto_learn_from_commit.py +9 -1
  9. autocoder/agent/base_agentic/__init__.py +3 -0
  10. autocoder/agent/base_agentic/agent_hub.py +1 -1
  11. autocoder/agent/base_agentic/base_agent.py +235 -136
  12. autocoder/agent/base_agentic/default_tools.py +119 -118
  13. autocoder/agent/base_agentic/test_base_agent.py +1 -1
  14. autocoder/agent/base_agentic/tool_registry.py +32 -20
  15. autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +24 -3
  16. autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +24 -11
  17. autocoder/agent/base_agentic/types.py +42 -0
  18. autocoder/agent/entry_command_agent/chat.py +73 -59
  19. autocoder/auto_coder.py +31 -40
  20. autocoder/auto_coder_rag.py +11 -1084
  21. autocoder/auto_coder_runner.py +970 -2345
  22. autocoder/auto_coder_terminal.py +26 -0
  23. autocoder/auto_coder_terminal_v3.py +190 -0
  24. autocoder/chat/conf_command.py +224 -124
  25. autocoder/chat/models_command.py +361 -299
  26. autocoder/chat/rules_command.py +79 -31
  27. autocoder/chat_auto_coder.py +988 -398
  28. autocoder/chat_auto_coder_lang.py +23 -732
  29. autocoder/commands/auto_command.py +25 -8
  30. autocoder/commands/auto_web.py +1 -1
  31. autocoder/commands/tools.py +44 -44
  32. autocoder/common/__init__.py +150 -128
  33. autocoder/common/ac_style_command_parser/__init__.py +39 -2
  34. autocoder/common/ac_style_command_parser/config.py +422 -0
  35. autocoder/common/ac_style_command_parser/parser.py +292 -78
  36. autocoder/common/ac_style_command_parser/test_parser.py +241 -16
  37. autocoder/common/ac_style_command_parser/test_typed_parser.py +342 -0
  38. autocoder/common/ac_style_command_parser/typed_parser.py +653 -0
  39. autocoder/common/action_yml_file_manager.py +25 -13
  40. autocoder/common/agent_events/__init__.py +52 -0
  41. autocoder/common/agent_events/agent_event_emitter.py +193 -0
  42. autocoder/common/agent_events/event_factory.py +177 -0
  43. autocoder/common/agent_events/examples.py +307 -0
  44. autocoder/common/agent_events/types.py +113 -0
  45. autocoder/common/agent_events/utils.py +68 -0
  46. autocoder/common/agent_hooks/__init__.py +44 -0
  47. autocoder/common/agent_hooks/examples.py +582 -0
  48. autocoder/common/agent_hooks/hook_executor.py +217 -0
  49. autocoder/common/agent_hooks/hook_manager.py +288 -0
  50. autocoder/common/agent_hooks/types.py +133 -0
  51. autocoder/common/agent_hooks/utils.py +99 -0
  52. autocoder/common/agent_query_queue/queue_executor.py +324 -0
  53. autocoder/common/agent_query_queue/queue_manager.py +325 -0
  54. autocoder/common/agents/__init__.py +11 -0
  55. autocoder/common/agents/agent_manager.py +323 -0
  56. autocoder/common/agents/agent_parser.py +189 -0
  57. autocoder/common/agents/example_usage.py +344 -0
  58. autocoder/common/agents/integration_example.py +330 -0
  59. autocoder/common/agents/test_agent_parser.py +545 -0
  60. autocoder/common/async_utils.py +101 -0
  61. autocoder/common/auto_coder_lang.py +23 -972
  62. autocoder/common/autocoderargs_parser/__init__.py +14 -0
  63. autocoder/common/autocoderargs_parser/parser.py +184 -0
  64. autocoder/common/autocoderargs_parser/tests/__init__.py +1 -0
  65. autocoder/common/autocoderargs_parser/tests/test_args_parser.py +235 -0
  66. autocoder/common/autocoderargs_parser/tests/test_token_parser.py +195 -0
  67. autocoder/common/autocoderargs_parser/token_parser.py +290 -0
  68. autocoder/common/buildin_tokenizer.py +2 -4
  69. autocoder/common/code_auto_generate.py +149 -74
  70. autocoder/common/code_auto_generate_diff.py +163 -70
  71. autocoder/common/code_auto_generate_editblock.py +179 -89
  72. autocoder/common/code_auto_generate_strict_diff.py +167 -72
  73. autocoder/common/code_auto_merge_editblock.py +13 -6
  74. autocoder/common/code_modification_ranker.py +1 -1
  75. autocoder/common/command_completer.py +3 -3
  76. autocoder/common/command_file_manager/manager.py +183 -47
  77. autocoder/common/command_file_manager/test_command_file_manager.py +507 -0
  78. autocoder/common/command_templates.py +1 -1
  79. autocoder/common/conf_utils.py +2 -4
  80. autocoder/common/conversations/config.py +11 -3
  81. autocoder/common/conversations/get_conversation_manager.py +100 -2
  82. autocoder/common/conversations/llm_stats_models.py +264 -0
  83. autocoder/common/conversations/manager.py +112 -28
  84. autocoder/common/conversations/models.py +16 -2
  85. autocoder/common/conversations/storage/index_manager.py +134 -10
  86. autocoder/common/core_config/__init__.py +63 -0
  87. autocoder/common/core_config/agentic_mode_manager.py +109 -0
  88. autocoder/common/core_config/base_manager.py +123 -0
  89. autocoder/common/core_config/compatibility.py +151 -0
  90. autocoder/common/core_config/config_manager.py +156 -0
  91. autocoder/common/core_config/conversation_manager.py +31 -0
  92. autocoder/common/core_config/exclude_manager.py +72 -0
  93. autocoder/common/core_config/file_manager.py +177 -0
  94. autocoder/common/core_config/human_as_model_manager.py +129 -0
  95. autocoder/common/core_config/lib_manager.py +54 -0
  96. autocoder/common/core_config/main_manager.py +81 -0
  97. autocoder/common/core_config/mode_manager.py +126 -0
  98. autocoder/common/core_config/models.py +70 -0
  99. autocoder/common/core_config/test_memory_manager.py +1056 -0
  100. autocoder/common/env_manager.py +282 -0
  101. autocoder/common/env_manager_usage_example.py +211 -0
  102. autocoder/common/file_checkpoint/conversation_checkpoint.py +19 -19
  103. autocoder/common/file_checkpoint/manager.py +264 -48
  104. autocoder/common/file_checkpoint/test_backup.py +1 -18
  105. autocoder/common/file_checkpoint/test_manager.py +270 -1
  106. autocoder/common/file_checkpoint/test_store.py +1 -17
  107. autocoder/common/file_handler/__init__.py +23 -0
  108. autocoder/common/file_handler/active_context_handler.py +159 -0
  109. autocoder/common/file_handler/add_files_handler.py +409 -0
  110. autocoder/common/file_handler/chat_handler.py +180 -0
  111. autocoder/common/file_handler/coding_handler.py +401 -0
  112. autocoder/common/file_handler/commit_handler.py +200 -0
  113. autocoder/common/file_handler/lib_handler.py +156 -0
  114. autocoder/common/file_handler/list_files_handler.py +111 -0
  115. autocoder/common/file_handler/mcp_handler.py +268 -0
  116. autocoder/common/file_handler/models_handler.py +493 -0
  117. autocoder/common/file_handler/remove_files_handler.py +172 -0
  118. autocoder/common/git_utils.py +44 -8
  119. autocoder/common/global_cancel.py +15 -6
  120. autocoder/common/ignorefiles/test_ignore_file_utils.py +1 -1
  121. autocoder/common/international/__init__.py +31 -0
  122. autocoder/common/international/demo_international.py +92 -0
  123. autocoder/common/international/message_manager.py +157 -0
  124. autocoder/common/international/messages/__init__.py +56 -0
  125. autocoder/common/international/messages/async_command_messages.py +507 -0
  126. autocoder/common/international/messages/auto_coder_messages.py +2208 -0
  127. autocoder/common/international/messages/chat_auto_coder_messages.py +1547 -0
  128. autocoder/common/international/messages/command_help_messages.py +986 -0
  129. autocoder/common/international/messages/conversation_command_messages.py +191 -0
  130. autocoder/common/international/messages/git_helper_plugin_messages.py +159 -0
  131. autocoder/common/international/messages/queue_command_messages.py +751 -0
  132. autocoder/common/international/messages/rules_command_messages.py +77 -0
  133. autocoder/common/international/messages/sdk_messages.py +1707 -0
  134. autocoder/common/international/messages/token_helper_plugin_messages.py +361 -0
  135. autocoder/common/international/messages/tool_display_messages.py +1212 -0
  136. autocoder/common/international/messages/workflow_exception_messages.py +473 -0
  137. autocoder/common/international/test_international.py +612 -0
  138. autocoder/common/linter_core/__init__.py +28 -0
  139. autocoder/common/linter_core/base_linter.py +61 -0
  140. autocoder/common/linter_core/config_loader.py +271 -0
  141. autocoder/common/linter_core/formatters/__init__.py +0 -0
  142. autocoder/common/linter_core/formatters/base_formatter.py +38 -0
  143. autocoder/common/linter_core/formatters/raw_formatter.py +17 -0
  144. autocoder/common/linter_core/linter.py +166 -0
  145. autocoder/common/linter_core/linter_factory.py +216 -0
  146. autocoder/common/linter_core/linter_manager.py +333 -0
  147. autocoder/common/linter_core/linters/__init__.py +9 -0
  148. autocoder/common/linter_core/linters/java_linter.py +342 -0
  149. autocoder/common/linter_core/linters/python_linter.py +115 -0
  150. autocoder/common/linter_core/linters/typescript_linter.py +119 -0
  151. autocoder/common/linter_core/models/__init__.py +7 -0
  152. autocoder/common/linter_core/models/lint_result.py +91 -0
  153. autocoder/common/linter_core/models.py +33 -0
  154. autocoder/common/linter_core/tests/__init__.py +3 -0
  155. autocoder/common/linter_core/tests/test_config_loader.py +323 -0
  156. autocoder/common/linter_core/tests/test_config_loading.py +308 -0
  157. autocoder/common/linter_core/tests/test_factory_manager.py +234 -0
  158. autocoder/common/linter_core/tests/test_formatters.py +147 -0
  159. autocoder/common/linter_core/tests/test_integration.py +317 -0
  160. autocoder/common/linter_core/tests/test_java_linter.py +496 -0
  161. autocoder/common/linter_core/tests/test_linters.py +265 -0
  162. autocoder/common/linter_core/tests/test_models.py +81 -0
  163. autocoder/common/linter_core/tests/verify_config_loading.py +296 -0
  164. autocoder/common/linter_core/tests/verify_fixes.py +183 -0
  165. autocoder/common/llm_friendly_package/__init__.py +31 -0
  166. autocoder/common/llm_friendly_package/base_manager.py +102 -0
  167. autocoder/common/llm_friendly_package/docs_manager.py +121 -0
  168. autocoder/common/llm_friendly_package/library_manager.py +171 -0
  169. autocoder/common/{llm_friendly_package.py → llm_friendly_package/main_manager.py} +204 -231
  170. autocoder/common/llm_friendly_package/models.py +40 -0
  171. autocoder/common/llm_friendly_package/test_llm_friendly_package.py +536 -0
  172. autocoder/common/llms/__init__.py +15 -0
  173. autocoder/common/llms/demo_error_handling.py +85 -0
  174. autocoder/common/llms/factory.py +142 -0
  175. autocoder/common/llms/manager.py +264 -0
  176. autocoder/common/llms/pricing.py +121 -0
  177. autocoder/common/llms/registry.py +288 -0
  178. autocoder/common/llms/schema.py +77 -0
  179. autocoder/common/llms/simple_demo.py +45 -0
  180. autocoder/common/llms/test_quick_model.py +116 -0
  181. autocoder/common/llms/test_remove_functionality.py +182 -0
  182. autocoder/common/llms/tests/__init__.py +1 -0
  183. autocoder/common/llms/tests/test_manager.py +330 -0
  184. autocoder/common/llms/tests/test_registry.py +364 -0
  185. autocoder/common/mcp_tools/__init__.py +62 -0
  186. autocoder/common/{mcp_tools.py → mcp_tools/executor.py} +49 -40
  187. autocoder/common/{mcp_hub.py → mcp_tools/hub.py} +42 -68
  188. autocoder/common/{mcp_server_install.py → mcp_tools/installer.py} +16 -28
  189. autocoder/common/{mcp_server.py → mcp_tools/server.py} +176 -48
  190. autocoder/common/mcp_tools/test_keyboard_interrupt.py +93 -0
  191. autocoder/common/mcp_tools/test_mcp_tools.py +391 -0
  192. autocoder/common/{mcp_server_types.py → mcp_tools/types.py} +121 -48
  193. autocoder/common/mcp_tools/verify_functionality.py +202 -0
  194. autocoder/common/model_speed_tester.py +32 -26
  195. autocoder/common/priority_directory_finder/__init__.py +142 -0
  196. autocoder/common/priority_directory_finder/examples.py +230 -0
  197. autocoder/common/priority_directory_finder/finder.py +283 -0
  198. autocoder/common/priority_directory_finder/models.py +236 -0
  199. autocoder/common/priority_directory_finder/test_priority_directory_finder.py +431 -0
  200. autocoder/common/project_scanner/__init__.py +18 -0
  201. autocoder/common/project_scanner/compat.py +77 -0
  202. autocoder/common/project_scanner/scanner.py +436 -0
  203. autocoder/common/project_tracker/__init__.py +27 -0
  204. autocoder/common/project_tracker/api.py +228 -0
  205. autocoder/common/project_tracker/demo.py +272 -0
  206. autocoder/common/project_tracker/tracker.py +487 -0
  207. autocoder/common/project_tracker/types.py +53 -0
  208. autocoder/common/pruner/__init__.py +67 -0
  209. autocoder/common/pruner/agentic_conversation_pruner.py +651 -102
  210. autocoder/common/pruner/conversation_message_ids_api.py +386 -0
  211. autocoder/common/pruner/conversation_message_ids_manager.py +347 -0
  212. autocoder/common/pruner/conversation_message_ids_pruner.py +473 -0
  213. autocoder/common/pruner/conversation_normalizer.py +347 -0
  214. autocoder/common/pruner/conversation_pruner.py +26 -6
  215. autocoder/common/pruner/test_agentic_conversation_pruner.py +554 -112
  216. autocoder/common/pruner/test_conversation_normalizer.py +502 -0
  217. autocoder/common/pruner/test_tool_content_detector.py +324 -0
  218. autocoder/common/pruner/tool_content_detector.py +227 -0
  219. autocoder/common/pruner/tools/__init__.py +18 -0
  220. autocoder/common/pruner/tools/query_message_ids.py +264 -0
  221. autocoder/common/pruner/tools/test_agentic_pruning_logic.py +432 -0
  222. autocoder/common/pruner/tools/test_message_ids_pruning_only.py +192 -0
  223. autocoder/common/pull_requests/__init__.py +9 -1
  224. autocoder/common/pull_requests/utils.py +122 -1
  225. autocoder/common/rag_manager/rag_manager.py +36 -40
  226. autocoder/common/rulefiles/__init__.py +53 -1
  227. autocoder/common/rulefiles/api.py +250 -0
  228. autocoder/common/rulefiles/core/__init__.py +14 -0
  229. autocoder/common/rulefiles/core/manager.py +241 -0
  230. autocoder/common/rulefiles/core/selector.py +805 -0
  231. autocoder/common/rulefiles/models/__init__.py +20 -0
  232. autocoder/common/rulefiles/models/index.py +16 -0
  233. autocoder/common/rulefiles/models/init_rule.py +18 -0
  234. autocoder/common/rulefiles/models/rule_file.py +18 -0
  235. autocoder/common/rulefiles/models/rule_relevance.py +14 -0
  236. autocoder/common/rulefiles/models/summary.py +16 -0
  237. autocoder/common/rulefiles/test_rulefiles.py +776 -0
  238. autocoder/common/rulefiles/utils/__init__.py +34 -0
  239. autocoder/common/rulefiles/utils/monitor.py +86 -0
  240. autocoder/common/rulefiles/utils/parser.py +230 -0
  241. autocoder/common/save_formatted_log.py +67 -10
  242. autocoder/common/search_replace.py +8 -1
  243. autocoder/common/search_replace_patch/__init__.py +24 -0
  244. autocoder/common/search_replace_patch/base.py +115 -0
  245. autocoder/common/search_replace_patch/manager.py +248 -0
  246. autocoder/common/search_replace_patch/patch_replacer.py +304 -0
  247. autocoder/common/search_replace_patch/similarity_replacer.py +306 -0
  248. autocoder/common/search_replace_patch/string_replacer.py +181 -0
  249. autocoder/common/search_replace_patch/tests/__init__.py +3 -0
  250. autocoder/common/search_replace_patch/tests/run_tests.py +126 -0
  251. autocoder/common/search_replace_patch/tests/test_base.py +188 -0
  252. autocoder/common/search_replace_patch/tests/test_empty_line_insert.py +233 -0
  253. autocoder/common/search_replace_patch/tests/test_integration.py +389 -0
  254. autocoder/common/search_replace_patch/tests/test_manager.py +351 -0
  255. autocoder/common/search_replace_patch/tests/test_patch_replacer.py +316 -0
  256. autocoder/common/search_replace_patch/tests/test_regex_replacer.py +306 -0
  257. autocoder/common/search_replace_patch/tests/test_similarity_replacer.py +384 -0
  258. autocoder/common/shell_commands/__init__.py +197 -0
  259. autocoder/common/shell_commands/background_process_notifier.py +346 -0
  260. autocoder/common/shell_commands/command_executor.py +1127 -0
  261. autocoder/common/shell_commands/error_recovery.py +541 -0
  262. autocoder/common/shell_commands/exceptions.py +120 -0
  263. autocoder/common/shell_commands/interactive_executor.py +476 -0
  264. autocoder/common/shell_commands/interactive_pexpect_process.py +623 -0
  265. autocoder/common/shell_commands/interactive_process.py +744 -0
  266. autocoder/common/shell_commands/interactive_session_manager.py +1014 -0
  267. autocoder/common/shell_commands/monitoring.py +529 -0
  268. autocoder/common/shell_commands/process_cleanup.py +386 -0
  269. autocoder/common/shell_commands/process_manager.py +606 -0
  270. autocoder/common/shell_commands/test_interactive_pexpect_process.py +281 -0
  271. autocoder/common/shell_commands/tests/__init__.py +6 -0
  272. autocoder/common/shell_commands/tests/conftest.py +118 -0
  273. autocoder/common/shell_commands/tests/test_background_process_notifier.py +703 -0
  274. autocoder/common/shell_commands/tests/test_command_executor.py +448 -0
  275. autocoder/common/shell_commands/tests/test_error_recovery.py +305 -0
  276. autocoder/common/shell_commands/tests/test_exceptions.py +299 -0
  277. autocoder/common/shell_commands/tests/test_execute_batch.py +588 -0
  278. autocoder/common/shell_commands/tests/test_indented_batch_commands.py +244 -0
  279. autocoder/common/shell_commands/tests/test_integration.py +664 -0
  280. autocoder/common/shell_commands/tests/test_monitoring.py +546 -0
  281. autocoder/common/shell_commands/tests/test_performance.py +632 -0
  282. autocoder/common/shell_commands/tests/test_process_cleanup.py +397 -0
  283. autocoder/common/shell_commands/tests/test_process_manager.py +606 -0
  284. autocoder/common/shell_commands/tests/test_timeout_config.py +343 -0
  285. autocoder/common/shell_commands/tests/test_timeout_manager.py +520 -0
  286. autocoder/common/shell_commands/timeout_config.py +315 -0
  287. autocoder/common/shell_commands/timeout_manager.py +352 -0
  288. autocoder/common/terminal_paste/__init__.py +14 -0
  289. autocoder/common/terminal_paste/demo.py +145 -0
  290. autocoder/common/terminal_paste/demo_paste_functionality.py +95 -0
  291. autocoder/common/terminal_paste/paste_handler.py +200 -0
  292. autocoder/common/terminal_paste/paste_manager.py +118 -0
  293. autocoder/common/terminal_paste/tests/__init__.py +1 -0
  294. autocoder/common/terminal_paste/tests/test_paste_handler.py +182 -0
  295. autocoder/common/terminal_paste/tests/test_paste_manager.py +126 -0
  296. autocoder/common/terminal_paste/utils.py +163 -0
  297. autocoder/common/test_autocoder_args.py +232 -0
  298. autocoder/common/test_env_manager.py +173 -0
  299. autocoder/common/test_env_manager_integration.py +159 -0
  300. autocoder/common/text_similarity/__init__.py +9 -0
  301. autocoder/common/text_similarity/demo.py +216 -0
  302. autocoder/common/text_similarity/examples.py +266 -0
  303. autocoder/common/text_similarity/test_text_similarity.py +306 -0
  304. autocoder/common/text_similarity/text_similarity.py +194 -0
  305. autocoder/common/text_similarity/utils.py +125 -0
  306. autocoder/common/todos/__init__.py +61 -0
  307. autocoder/common/todos/cache/__init__.py +16 -0
  308. autocoder/common/todos/cache/base_cache.py +89 -0
  309. autocoder/common/todos/cache/cache_manager.py +228 -0
  310. autocoder/common/todos/cache/memory_cache.py +225 -0
  311. autocoder/common/todos/config.py +155 -0
  312. autocoder/common/todos/exceptions.py +35 -0
  313. autocoder/common/todos/get_todo_manager.py +161 -0
  314. autocoder/common/todos/manager.py +537 -0
  315. autocoder/common/todos/models.py +239 -0
  316. autocoder/common/todos/storage/__init__.py +14 -0
  317. autocoder/common/todos/storage/base_storage.py +76 -0
  318. autocoder/common/todos/storage/file_storage.py +278 -0
  319. autocoder/common/tokens/counter.py +24 -2
  320. autocoder/common/tools_manager/__init__.py +17 -0
  321. autocoder/common/tools_manager/examples.py +162 -0
  322. autocoder/common/tools_manager/manager.py +385 -0
  323. autocoder/common/tools_manager/models.py +39 -0
  324. autocoder/common/tools_manager/test_tools_manager.py +303 -0
  325. autocoder/common/tools_manager/utils.py +191 -0
  326. autocoder/common/v2/agent/agentic_callbacks.py +270 -0
  327. autocoder/common/v2/agent/agentic_edit.py +2699 -1856
  328. autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
  329. autocoder/common/v2/agent/agentic_edit_tools/__init__.py +35 -1
  330. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
  331. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +10 -1
  332. autocoder/common/v2/agent/agentic_edit_tools/background_task_tool_resolver.py +1167 -0
  333. autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +2 -2
  334. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_read_tool_resolver.py +214 -0
  335. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_write_tool_resolver.py +299 -0
  336. autocoder/common/v2/agent/agentic_edit_tools/count_tokens_tool_resolver.py +290 -0
  337. autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +564 -29
  338. autocoder/common/v2/agent/agentic_edit_tools/execute_workflow_tool_resolver.py +485 -0
  339. autocoder/common/v2/agent/agentic_edit_tools/extract_to_text_tool_resolver.py +225 -0
  340. autocoder/common/v2/agent/agentic_edit_tools/lint_report.py +79 -0
  341. autocoder/common/v2/agent/agentic_edit_tools/linter_config_models.py +343 -0
  342. autocoder/common/v2/agent/agentic_edit_tools/linter_enabled_tool_resolver.py +189 -0
  343. autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +169 -101
  344. autocoder/common/v2/agent/agentic_edit_tools/load_extra_document_tool_resolver.py +349 -0
  345. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +243 -50
  346. autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +667 -147
  347. autocoder/common/v2/agent/agentic_edit_tools/run_named_subagents_tool_resolver.py +691 -0
  348. autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +410 -86
  349. autocoder/common/v2/agent/agentic_edit_tools/session_interactive_tool_resolver.py +115 -0
  350. autocoder/common/v2/agent/agentic_edit_tools/session_start_tool_resolver.py +190 -0
  351. autocoder/common/v2/agent/agentic_edit_tools/session_stop_tool_resolver.py +76 -0
  352. autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +207 -192
  353. autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +80 -63
  354. autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +237 -233
  355. autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +2 -2
  356. autocoder/common/v2/agent/agentic_edit_tools/web_crawl_tool_resolver.py +557 -0
  357. autocoder/common/v2/agent/agentic_edit_tools/web_search_tool_resolver.py +600 -0
  358. autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +56 -121
  359. autocoder/common/v2/agent/agentic_edit_types.py +343 -9
  360. autocoder/common/v2/agent/runner/__init__.py +3 -3
  361. autocoder/common/v2/agent/runner/base_runner.py +12 -26
  362. autocoder/common/v2/agent/runner/{event_runner.py → file_based_event_runner.py} +3 -2
  363. autocoder/common/v2/agent/runner/sdk_runner.py +150 -8
  364. autocoder/common/v2/agent/runner/terminal_runner.py +170 -57
  365. autocoder/common/v2/agent/runner/tool_display.py +557 -159
  366. autocoder/common/v2/agent/test_agentic_callbacks.py +265 -0
  367. autocoder/common/v2/agent/test_agentic_edit.py +194 -0
  368. autocoder/common/v2/agent/tool_caller/__init__.py +24 -0
  369. autocoder/common/v2/agent/tool_caller/default_tool_resolver_map.py +135 -0
  370. autocoder/common/v2/agent/tool_caller/integration_test.py +172 -0
  371. autocoder/common/v2/agent/tool_caller/plugins/__init__.py +14 -0
  372. autocoder/common/v2/agent/tool_caller/plugins/base_plugin.py +126 -0
  373. autocoder/common/v2/agent/tool_caller/plugins/examples/__init__.py +13 -0
  374. autocoder/common/v2/agent/tool_caller/plugins/examples/logging_plugin.py +164 -0
  375. autocoder/common/v2/agent/tool_caller/plugins/examples/security_filter_plugin.py +198 -0
  376. autocoder/common/v2/agent/tool_caller/plugins/plugin_interface.py +141 -0
  377. autocoder/common/v2/agent/tool_caller/test_tool_caller.py +278 -0
  378. autocoder/common/v2/agent/tool_caller/tool_call_plugin_manager.py +331 -0
  379. autocoder/common/v2/agent/tool_caller/tool_caller.py +337 -0
  380. autocoder/common/v2/agent/tool_caller/usage_example.py +193 -0
  381. autocoder/common/v2/code_agentic_editblock_manager.py +4 -4
  382. autocoder/common/v2/code_auto_generate.py +136 -78
  383. autocoder/common/v2/code_auto_generate_diff.py +135 -79
  384. autocoder/common/v2/code_auto_generate_editblock.py +174 -99
  385. autocoder/common/v2/code_auto_generate_strict_diff.py +151 -71
  386. autocoder/common/v2/code_auto_merge.py +1 -1
  387. autocoder/common/v2/code_auto_merge_editblock.py +13 -1
  388. autocoder/common/v2/code_diff_manager.py +3 -3
  389. autocoder/common/v2/code_editblock_manager.py +4 -14
  390. autocoder/common/v2/code_manager.py +1 -1
  391. autocoder/common/v2/code_strict_diff_manager.py +2 -2
  392. autocoder/common/wrap_llm_hint/__init__.py +10 -0
  393. autocoder/common/wrap_llm_hint/test_wrap_llm_hint.py +1067 -0
  394. autocoder/common/wrap_llm_hint/utils.py +432 -0
  395. autocoder/common/wrap_llm_hint/wrap_llm_hint.py +323 -0
  396. autocoder/completer/__init__.py +8 -0
  397. autocoder/completer/command_completer_v2.py +1051 -0
  398. autocoder/default_project/__init__.py +501 -0
  399. autocoder/dispacher/__init__.py +4 -12
  400. autocoder/dispacher/actions/action.py +165 -7
  401. autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
  402. autocoder/index/entry.py +116 -124
  403. autocoder/{agent → index/filter}/agentic_filter.py +322 -333
  404. autocoder/index/filter/normal_filter.py +5 -11
  405. autocoder/index/filter/quick_filter.py +1 -1
  406. autocoder/index/index.py +36 -9
  407. autocoder/index/tests/__init__.py +1 -0
  408. autocoder/index/tests/run_tests.py +195 -0
  409. autocoder/index/tests/test_entry.py +303 -0
  410. autocoder/index/tests/test_index_manager.py +314 -0
  411. autocoder/index/tests/test_module_integration.py +300 -0
  412. autocoder/index/tests/test_symbols_utils.py +183 -0
  413. autocoder/inner/__init__.py +4 -0
  414. autocoder/inner/agentic.py +932 -0
  415. autocoder/inner/async_command_handler.py +992 -0
  416. autocoder/inner/conversation_command_handlers.py +623 -0
  417. autocoder/inner/merge_command_handler.py +213 -0
  418. autocoder/inner/queue_command_handler.py +684 -0
  419. autocoder/models.py +95 -266
  420. autocoder/plugins/git_helper_plugin.py +31 -29
  421. autocoder/plugins/token_helper_plugin.py +65 -46
  422. autocoder/pyproject/__init__.py +32 -29
  423. autocoder/rag/agentic_rag.py +215 -75
  424. autocoder/rag/cache/simple_cache.py +1 -2
  425. autocoder/rag/loaders/image_loader.py +1 -1
  426. autocoder/rag/long_context_rag.py +42 -26
  427. autocoder/rag/qa_conversation_strategy.py +1 -1
  428. autocoder/rag/terminal/__init__.py +17 -0
  429. autocoder/rag/terminal/args.py +581 -0
  430. autocoder/rag/terminal/bootstrap.py +61 -0
  431. autocoder/rag/terminal/command_handlers.py +653 -0
  432. autocoder/rag/terminal/formatters/__init__.py +20 -0
  433. autocoder/rag/terminal/formatters/base.py +70 -0
  434. autocoder/rag/terminal/formatters/json_format.py +66 -0
  435. autocoder/rag/terminal/formatters/stream_json.py +95 -0
  436. autocoder/rag/terminal/formatters/text.py +28 -0
  437. autocoder/rag/terminal/init.py +120 -0
  438. autocoder/rag/terminal/utils.py +106 -0
  439. autocoder/rag/test_agentic_rag.py +389 -0
  440. autocoder/rag/test_doc_filter.py +3 -3
  441. autocoder/rag/test_long_context_rag.py +1 -1
  442. autocoder/rag/test_token_limiter.py +517 -10
  443. autocoder/rag/token_counter.py +3 -0
  444. autocoder/rag/token_limiter.py +19 -15
  445. autocoder/rag/tools/__init__.py +26 -2
  446. autocoder/rag/tools/bochaai_example.py +343 -0
  447. autocoder/rag/tools/bochaai_sdk.py +541 -0
  448. autocoder/rag/tools/metaso_example.py +268 -0
  449. autocoder/rag/tools/metaso_sdk.py +417 -0
  450. autocoder/rag/tools/recall_tool.py +28 -7
  451. autocoder/rag/tools/run_integration_tests.py +204 -0
  452. autocoder/rag/tools/test_all_providers.py +318 -0
  453. autocoder/rag/tools/test_bochaai_integration.py +482 -0
  454. autocoder/rag/tools/test_final_integration.py +215 -0
  455. autocoder/rag/tools/test_metaso_integration.py +424 -0
  456. autocoder/rag/tools/test_metaso_real.py +171 -0
  457. autocoder/rag/tools/test_web_crawl_tool.py +639 -0
  458. autocoder/rag/tools/test_web_search_tool.py +509 -0
  459. autocoder/rag/tools/todo_read_tool.py +202 -0
  460. autocoder/rag/tools/todo_write_tool.py +412 -0
  461. autocoder/rag/tools/web_crawl_tool.py +634 -0
  462. autocoder/rag/tools/web_search_tool.py +558 -0
  463. autocoder/rag/tools/web_tools_example.py +119 -0
  464. autocoder/rag/types.py +16 -0
  465. autocoder/rag/variable_holder.py +4 -2
  466. autocoder/rags.py +86 -79
  467. autocoder/regexproject/__init__.py +23 -21
  468. autocoder/sdk/__init__.py +46 -190
  469. autocoder/sdk/api.py +370 -0
  470. autocoder/sdk/async_runner/__init__.py +26 -0
  471. autocoder/sdk/async_runner/async_executor.py +650 -0
  472. autocoder/sdk/async_runner/async_handler.py +356 -0
  473. autocoder/sdk/async_runner/markdown_processor.py +595 -0
  474. autocoder/sdk/async_runner/task_metadata.py +284 -0
  475. autocoder/sdk/async_runner/worktree_manager.py +438 -0
  476. autocoder/sdk/cli/__init__.py +2 -5
  477. autocoder/sdk/cli/formatters.py +28 -204
  478. autocoder/sdk/cli/handlers.py +77 -44
  479. autocoder/sdk/cli/main.py +154 -171
  480. autocoder/sdk/cli/options.py +95 -22
  481. autocoder/sdk/constants.py +139 -51
  482. autocoder/sdk/core/auto_coder_core.py +484 -109
  483. autocoder/sdk/core/bridge.py +297 -115
  484. autocoder/sdk/exceptions.py +18 -12
  485. autocoder/sdk/formatters/__init__.py +19 -0
  486. autocoder/sdk/formatters/input.py +64 -0
  487. autocoder/sdk/formatters/output.py +247 -0
  488. autocoder/sdk/formatters/stream.py +54 -0
  489. autocoder/sdk/models/__init__.py +6 -5
  490. autocoder/sdk/models/options.py +55 -18
  491. autocoder/sdk/utils/formatters.py +27 -195
  492. autocoder/suffixproject/__init__.py +28 -25
  493. autocoder/terminal/__init__.py +14 -0
  494. autocoder/terminal/app.py +454 -0
  495. autocoder/terminal/args.py +32 -0
  496. autocoder/terminal/bootstrap.py +178 -0
  497. autocoder/terminal/command_processor.py +521 -0
  498. autocoder/terminal/command_registry.py +57 -0
  499. autocoder/terminal/help.py +97 -0
  500. autocoder/terminal/tasks/__init__.py +5 -0
  501. autocoder/terminal/tasks/background.py +77 -0
  502. autocoder/terminal/tasks/task_event.py +70 -0
  503. autocoder/terminal/ui/__init__.py +13 -0
  504. autocoder/terminal/ui/completer.py +268 -0
  505. autocoder/terminal/ui/keybindings.py +75 -0
  506. autocoder/terminal/ui/session.py +41 -0
  507. autocoder/terminal/ui/toolbar.py +64 -0
  508. autocoder/terminal/utils/__init__.py +13 -0
  509. autocoder/terminal/utils/errors.py +18 -0
  510. autocoder/terminal/utils/paths.py +19 -0
  511. autocoder/terminal/utils/shell.py +43 -0
  512. autocoder/terminal_v3/__init__.py +10 -0
  513. autocoder/terminal_v3/app.py +201 -0
  514. autocoder/terminal_v3/handlers/__init__.py +5 -0
  515. autocoder/terminal_v3/handlers/command_handler.py +131 -0
  516. autocoder/terminal_v3/models/__init__.py +6 -0
  517. autocoder/terminal_v3/models/conversation_buffer.py +214 -0
  518. autocoder/terminal_v3/models/message.py +50 -0
  519. autocoder/terminal_v3/models/tool_display.py +247 -0
  520. autocoder/terminal_v3/ui/__init__.py +7 -0
  521. autocoder/terminal_v3/ui/keybindings.py +56 -0
  522. autocoder/terminal_v3/ui/layout.py +141 -0
  523. autocoder/terminal_v3/ui/styles.py +43 -0
  524. autocoder/tsproject/__init__.py +23 -23
  525. autocoder/utils/auto_coder_utils/chat_stream_out.py +1 -1
  526. autocoder/utils/llms.py +88 -80
  527. autocoder/utils/math_utils.py +101 -0
  528. autocoder/utils/model_provider_selector.py +16 -4
  529. autocoder/utils/operate_config_api.py +33 -5
  530. autocoder/utils/thread_utils.py +2 -2
  531. autocoder/version.py +4 -2
  532. autocoder/workflow_agents/__init__.py +84 -0
  533. autocoder/workflow_agents/agent.py +143 -0
  534. autocoder/workflow_agents/exceptions.py +573 -0
  535. autocoder/workflow_agents/executor.py +489 -0
  536. autocoder/workflow_agents/loader.py +737 -0
  537. autocoder/workflow_agents/runner.py +267 -0
  538. autocoder/workflow_agents/types.py +172 -0
  539. autocoder/workflow_agents/utils.py +434 -0
  540. autocoder/workflow_agents/workflow_manager.py +211 -0
  541. auto_coder-1.0.0.dist-info/METADATA +0 -396
  542. auto_coder-1.0.0.dist-info/RECORD +0 -442
  543. auto_coder-1.0.0.dist-info/licenses/LICENSE +0 -201
  544. autocoder/auto_coder_server.py +0 -672
  545. autocoder/benchmark.py +0 -138
  546. autocoder/common/ac_style_command_parser/example.py +0 -7
  547. autocoder/common/cleaner.py +0 -31
  548. autocoder/common/command_completer_v2.py +0 -615
  549. autocoder/common/context_pruner.py +0 -477
  550. autocoder/common/conversation_pruner.py +0 -132
  551. autocoder/common/directory_cache/__init__.py +0 -1
  552. autocoder/common/directory_cache/cache.py +0 -192
  553. autocoder/common/directory_cache/test_cache.py +0 -190
  554. autocoder/common/file_checkpoint/examples.py +0 -217
  555. autocoder/common/llm_friendly_package_example.py +0 -138
  556. autocoder/common/llm_friendly_package_test.py +0 -63
  557. autocoder/common/pull_requests/test_module.py +0 -1
  558. autocoder/common/rulefiles/autocoderrules_utils.py +0 -484
  559. autocoder/common/text.py +0 -30
  560. autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +0 -42
  561. autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.py +0 -70
  562. autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +0 -163
  563. autocoder/common/v2/agent/agentic_tool_display.py +0 -183
  564. autocoder/plugins/dynamic_completion_example.py +0 -148
  565. autocoder/plugins/sample_plugin.py +0 -160
  566. autocoder/sdk/cli/__main__.py +0 -26
  567. autocoder/sdk/cli/completion_wrapper.py +0 -38
  568. autocoder/sdk/cli/install_completion.py +0 -301
  569. autocoder/sdk/models/messages.py +0 -209
  570. autocoder/sdk/session/__init__.py +0 -32
  571. autocoder/sdk/session/session.py +0 -106
  572. autocoder/sdk/session/session_manager.py +0 -56
  573. {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/top_level.txt +0 -0
  574. /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
@@ -0,0 +1,737 @@
1
+ """
2
+ Workflow YAML 加载器
3
+
4
+ 负责从 YAML 文件加载 workflow 配置并转换为 Python 数据结构。
5
+ """
6
+
7
+ import yaml
8
+ from pathlib import Path
9
+ from typing import Any, Dict, List, Optional
10
+ from loguru import logger
11
+
12
+ from autocoder.workflow_agents.types import (
13
+ WorkflowSpec,
14
+ MetadataConfig,
15
+ SpecConfig,
16
+ GlobalsConfig,
17
+ ConversationConfig,
18
+ AttemptConfig,
19
+ AgentSpec,
20
+ StepSpec,
21
+ WhenConfig,
22
+ RegexCondition,
23
+ JsonPathCondition,
24
+ OutputConfig,
25
+ StepConversationConfig,
26
+ )
27
+ from autocoder.workflow_agents.exceptions import (
28
+ WorkflowFileNotFoundError,
29
+ WorkflowParseError,
30
+ WorkflowValidationError,
31
+ )
32
+
33
+
34
+ def load_workflow_from_yaml(yaml_path: str) -> WorkflowSpec:
35
+ """
36
+ 从 YAML 文件加载 workflow 配置
37
+
38
+ Args:
39
+ yaml_path: YAML 文件路径
40
+
41
+ Returns:
42
+ WorkflowSpec 对象
43
+
44
+ Raises:
45
+ WorkflowFileNotFoundError: 如果文件不存在
46
+ WorkflowParseError: 如果 YAML 解析失败
47
+ WorkflowValidationError: 如果配置格式不正确
48
+ """
49
+ yaml_file = Path(yaml_path)
50
+ if not yaml_file.exists():
51
+ raise WorkflowFileNotFoundError(
52
+ workflow_name=yaml_path, searched_paths=[str(yaml_file.absolute())]
53
+ )
54
+
55
+ try:
56
+ with open(yaml_file, "r", encoding="utf-8") as f:
57
+ data = yaml.safe_load(f)
58
+ except yaml.YAMLError as e:
59
+ # 尝试提取行号
60
+ line_number = None
61
+ if hasattr(e, "problem_mark") and e.problem_mark:
62
+ line_number = e.problem_mark.line + 1 # YAML 行号从0开始
63
+
64
+ raise WorkflowParseError(
65
+ yaml_path=str(yaml_file), parse_error=e, line_number=line_number
66
+ )
67
+ except Exception as e:
68
+ raise WorkflowParseError(yaml_path=str(yaml_file), parse_error=e)
69
+
70
+ if data is None:
71
+ raise WorkflowValidationError(
72
+ message="YAML 文件为空或仅包含注释",
73
+ suggestion="请确保 YAML 文件包含有效的 workflow 配置",
74
+ )
75
+
76
+ return parse_workflow_spec(data, yaml_path=str(yaml_file))
77
+
78
+
79
+ def parse_workflow_spec(data: Dict[str, Any], yaml_path: str = None) -> WorkflowSpec:
80
+ """
81
+ 解析 YAML 数据为 WorkflowSpec
82
+
83
+ Args:
84
+ data: YAML 解析后的字典
85
+ yaml_path: YAML 文件路径(可选,用于错误提示)
86
+
87
+ Returns:
88
+ WorkflowSpec 对象
89
+
90
+ Raises:
91
+ WorkflowValidationError: 如果配置格式不正确
92
+ """
93
+ # 验证顶层必需字段
94
+ _validate_required_field(data, "apiVersion", "workflow 配置", yaml_path)
95
+ _validate_required_field(data, "kind", "workflow 配置", yaml_path)
96
+ _validate_required_field(data, "spec", "workflow 配置", yaml_path)
97
+
98
+ # 验证顶层字段值
99
+ api_version = data["apiVersion"]
100
+ if api_version != "autocoder/v1":
101
+ raise WorkflowValidationError(
102
+ message=f"不支持的 apiVersion",
103
+ field_path="apiVersion",
104
+ expected="autocoder/v1",
105
+ actual=api_version,
106
+ suggestion="请将 apiVersion 修改为 'autocoder/v1'",
107
+ )
108
+
109
+ kind = data["kind"]
110
+ if kind != "SubagentWorkflow":
111
+ raise WorkflowValidationError(
112
+ message=f"不支持的 kind",
113
+ field_path="kind",
114
+ expected="SubagentWorkflow",
115
+ actual=kind,
116
+ suggestion="请将 kind 修改为 'SubagentWorkflow'",
117
+ )
118
+
119
+ # 解析 metadata
120
+ metadata_data = data.get("metadata", {})
121
+
122
+ # 验证 metadata.name
123
+ workflow_name = metadata_data.get("name")
124
+ if not workflow_name or not workflow_name.strip():
125
+ raise WorkflowValidationError(
126
+ message="metadata.name 不能为空",
127
+ field_path="metadata.name",
128
+ expected="非空字符串",
129
+ actual=str(workflow_name),
130
+ suggestion="请设置一个有意义的 workflow 名称,如 'my-workflow'",
131
+ )
132
+
133
+ metadata = MetadataConfig(
134
+ name=workflow_name,
135
+ description=metadata_data.get("description", ""),
136
+ )
137
+
138
+ # 解析 spec
139
+ spec_data = data.get("spec", {})
140
+ if not isinstance(spec_data, dict):
141
+ raise WorkflowValidationError(
142
+ message="spec 必须是一个字典对象",
143
+ field_path="spec",
144
+ expected="字典对象",
145
+ actual=str(type(spec_data).__name__),
146
+ suggestion="请确保 spec: 后面跟的是字典格式的配置",
147
+ )
148
+
149
+ spec = parse_spec_config(spec_data, yaml_path=yaml_path)
150
+
151
+ return WorkflowSpec(
152
+ apiVersion=data["apiVersion"],
153
+ kind=data["kind"],
154
+ metadata=metadata,
155
+ spec=spec,
156
+ )
157
+
158
+
159
+ def parse_spec_config(data: Dict[str, Any], yaml_path: str = None) -> SpecConfig:
160
+ """
161
+ 解析 spec 配置
162
+
163
+ Args:
164
+ data: spec 字典
165
+ yaml_path: YAML 文件路径(可选,用于错误提示)
166
+
167
+ Returns:
168
+ SpecConfig 对象
169
+
170
+ Raises:
171
+ WorkflowValidationError: 如果配置格式不正确
172
+ """
173
+ # 解析 globals
174
+ globals_data = data.get("globals", {})
175
+ globals_config = _parse_globals_config(globals_data)
176
+
177
+ # 解析 vars
178
+ vars_data = data.get("vars", {})
179
+ if not isinstance(vars_data, dict):
180
+ raise WorkflowValidationError(
181
+ message="spec.vars 必须是字典对象",
182
+ field_path="spec.vars",
183
+ expected="字典对象",
184
+ actual=str(type(vars_data).__name__),
185
+ suggestion="将 vars 定义为键值对,如: vars:\\n project_type: '*'",
186
+ )
187
+
188
+ # 解析 conversation
189
+ conv_data = data.get("conversation", {})
190
+ conversation_config = _parse_conversation_config(conv_data)
191
+
192
+ # 解析 attempt
193
+ attempt_data = data.get("attempt", {})
194
+ attempt_config = _parse_attempt_config(attempt_data)
195
+
196
+ # 解析 agents
197
+ agents_data = data.get("agents", [])
198
+ if not isinstance(agents_data, list):
199
+ raise WorkflowValidationError(
200
+ message="spec.agents 必须是列表",
201
+ field_path="spec.agents",
202
+ expected="列表(数组)",
203
+ actual=str(type(agents_data).__name__),
204
+ suggestion="确保 agents: 后面跟的是列表格式,每项以 - 开头",
205
+ )
206
+
207
+ if not agents_data:
208
+ raise WorkflowValidationError(
209
+ message="spec.agents 不能为空",
210
+ field_path="spec.agents",
211
+ expected="至少一个 agent 定义",
212
+ actual="空列表",
213
+ suggestion="请定义至少一个 agent,例如:\\n - id: context\\n path: context.md",
214
+ )
215
+
216
+ agents = [
217
+ parse_agent_spec(agent_data, i) for i, agent_data in enumerate(agents_data)
218
+ ]
219
+
220
+ # 验证 agent ID 唯一性
221
+ agent_ids = [a.id for a in agents]
222
+ duplicates = [aid for aid in agent_ids if agent_ids.count(aid) > 1]
223
+ if duplicates:
224
+ raise WorkflowValidationError(
225
+ message=f"发现重复的 agent ID: {', '.join(set(duplicates))}",
226
+ field_path="spec.agents",
227
+ suggestion="每个 agent 的 id 必须唯一,请修改重复的 ID",
228
+ )
229
+
230
+ # 解析 steps
231
+ steps_data = data.get("steps", [])
232
+ if not isinstance(steps_data, list):
233
+ raise WorkflowValidationError(
234
+ message="spec.steps 必须是列表",
235
+ field_path="spec.steps",
236
+ expected="列表(数组)",
237
+ actual=str(type(steps_data).__name__),
238
+ suggestion="确保 steps: 后面跟的是列表格式,每项以 - 开头",
239
+ )
240
+
241
+ if not steps_data:
242
+ raise WorkflowValidationError(
243
+ message="spec.steps 不能为空",
244
+ field_path="spec.steps",
245
+ expected="至少一个 step 定义",
246
+ actual="空列表",
247
+ suggestion="请定义至少一个 step,例如:\\n - id: step1\\n agent: context",
248
+ )
249
+
250
+ steps = [
251
+ parse_step_spec(step_data, i, agent_ids)
252
+ for i, step_data in enumerate(steps_data)
253
+ ]
254
+
255
+ # 验证 step ID 唯一性
256
+ step_ids = [s.id for s in steps]
257
+ duplicates = [sid for sid in step_ids if step_ids.count(sid) > 1]
258
+ if duplicates:
259
+ raise WorkflowValidationError(
260
+ message=f"发现重复的 step ID: {', '.join(set(duplicates))}",
261
+ field_path="spec.steps",
262
+ suggestion="每个 step 的 id 必须唯一,请修改重复的 ID",
263
+ )
264
+
265
+ return SpecConfig(
266
+ globals=globals_config,
267
+ vars=vars_data,
268
+ conversation=conversation_config,
269
+ attempt=attempt_config,
270
+ agents=agents,
271
+ steps=steps,
272
+ )
273
+
274
+
275
+ def parse_agent_spec(data: Dict[str, Any], index: int = None) -> AgentSpec:
276
+ """
277
+ 解析 agent 配置
278
+
279
+ Args:
280
+ data: agent 字典
281
+ index: agent 在列表中的索引(用于错误提示)
282
+
283
+ Returns:
284
+ AgentSpec 对象
285
+
286
+ Raises:
287
+ WorkflowValidationError: 如果缺少必需字段或格式不正确
288
+ """
289
+ context = f"spec.agents[{index}]" if index is not None else "agent 配置"
290
+
291
+ _validate_required_field(data, "id", context)
292
+ _validate_required_field(data, "path", context)
293
+
294
+ # 验证 runner 类型
295
+ runner = data.get("runner", "sdk")
296
+ if runner not in ["sdk", "terminal"]:
297
+ raise WorkflowValidationError(
298
+ message=f"agent runner 类型无效",
299
+ field_path=f"{context}.runner",
300
+ expected="'sdk' 或 'terminal'",
301
+ actual=f"'{runner}'",
302
+ suggestion=f"请将 agent '{data['id']}' 的 runner 修改为 'sdk' 或 'terminal'",
303
+ )
304
+
305
+ return AgentSpec(
306
+ id=data["id"],
307
+ path=data["path"],
308
+ runner=runner,
309
+ model=data.get("model"),
310
+ retry=data.get("retry"),
311
+ timeout_sec=data.get("timeout_sec"),
312
+ )
313
+
314
+
315
+ def parse_step_spec(
316
+ data: Dict[str, Any], index: int = None, available_agent_ids: list = None
317
+ ) -> StepSpec:
318
+ """
319
+ 解析 step 配置
320
+
321
+ Args:
322
+ data: step 字典
323
+ index: step 在列表中的索引(用于错误提示)
324
+ available_agent_ids: 可用的 agent ID 列表(用于验证)
325
+
326
+ Returns:
327
+ StepSpec 对象
328
+
329
+ Raises:
330
+ WorkflowValidationError: 如果缺少必需字段或格式不正确
331
+ """
332
+ context = f"spec.steps[{index}]" if index is not None else "step 配置"
333
+
334
+ _validate_required_field(data, "id", context)
335
+ _validate_required_field(data, "agent", context)
336
+
337
+ step_id = data["id"]
338
+ agent_id = data["agent"]
339
+
340
+ # 验证 agent 引用是否存在
341
+ if available_agent_ids and agent_id not in available_agent_ids:
342
+ raise WorkflowValidationError(
343
+ message=f"步骤引用的 agent 不存在: '{agent_id}'",
344
+ field_path=f"{context}.agent",
345
+ expected=f"以下之一: {', '.join(available_agent_ids)}",
346
+ actual=f"'{agent_id}'",
347
+ suggestion=f"请将 agent 修改为已定义的 agent ID,或在 spec.agents 中添加 '{agent_id}' 的定义",
348
+ )
349
+
350
+ # 验证 needs 字段
351
+ needs = data.get("needs", [])
352
+ if not isinstance(needs, list):
353
+ raise WorkflowValidationError(
354
+ message="needs 必须是列表",
355
+ field_path=f"{context}.needs",
356
+ expected="列表(数组)",
357
+ actual=str(type(needs).__name__),
358
+ suggestion="将 needs 定义为列表,如: needs: [step1, step2]",
359
+ )
360
+
361
+ # 解析 when
362
+ when_data = data.get("when")
363
+ when_config = parse_when_config(when_data, f"{context}.when") if when_data else None
364
+
365
+ # 解析 outputs
366
+ outputs_data = data.get("outputs", {})
367
+ if not isinstance(outputs_data, dict):
368
+ raise WorkflowValidationError(
369
+ message="outputs 必须是字典对象",
370
+ field_path=f"{context}.outputs",
371
+ expected="字典对象",
372
+ actual=str(type(outputs_data).__name__),
373
+ suggestion="将 outputs 定义为键值对",
374
+ )
375
+ outputs = {
376
+ key: parse_output_config(value, f"{context}.outputs.{key}")
377
+ for key, value in outputs_data.items()
378
+ }
379
+
380
+ # 解析 conversation
381
+ conv_data = data.get("conversation")
382
+ conversation = (
383
+ parse_step_conversation_config(conv_data, context) if conv_data else None
384
+ )
385
+
386
+ return StepSpec(
387
+ id=step_id,
388
+ agent=agent_id,
389
+ needs=needs,
390
+ with_args=data.get("with", {}),
391
+ when=when_config,
392
+ outputs=outputs,
393
+ conversation=conversation,
394
+ )
395
+
396
+
397
+ def parse_when_config(data: Dict[str, Any], context: str = "when 配置") -> WhenConfig:
398
+ """
399
+ 解析 when 条件配置
400
+
401
+ Args:
402
+ data: when 字典
403
+ context: 上下文描述(用于错误提示)
404
+
405
+ Returns:
406
+ WhenConfig 对象
407
+
408
+ Raises:
409
+ WorkflowValidationError: 如果条件配置格式不正确
410
+ """
411
+ regex_config = None
412
+ jsonpath_config = None
413
+
414
+ if "regex" in data:
415
+ regex_data = data["regex"]
416
+ if not isinstance(regex_data, dict):
417
+ raise WorkflowValidationError(
418
+ message="regex 条件必须是字典对象",
419
+ field_path=f"{context}.regex",
420
+ expected="字典对象",
421
+ actual=str(type(regex_data).__name__),
422
+ suggestion="请使用字典格式定义 regex 条件",
423
+ )
424
+
425
+ # 验证必需字段
426
+ if "pattern" not in regex_data or not regex_data["pattern"]:
427
+ raise WorkflowValidationError(
428
+ message="regex 条件缺少 pattern 字段",
429
+ field_path=f"{context}.regex.pattern",
430
+ expected="非空正则表达式",
431
+ actual=str(regex_data.get("pattern")),
432
+ suggestion="请提供有效的正则表达式 pattern",
433
+ )
434
+
435
+ regex_config = RegexCondition(
436
+ input=regex_data.get("input", ""),
437
+ pattern=regex_data["pattern"],
438
+ flags=regex_data.get("flags"),
439
+ group=regex_data.get("group"),
440
+ )
441
+
442
+ if "jsonpath" in data:
443
+ jsonpath_data = data["jsonpath"]
444
+ if not isinstance(jsonpath_data, dict):
445
+ raise WorkflowValidationError(
446
+ message="jsonpath 条件必须是字典对象",
447
+ field_path=f"{context}.jsonpath",
448
+ expected="字典对象",
449
+ actual=str(type(jsonpath_data).__name__),
450
+ suggestion="请使用字典格式定义 jsonpath 条件",
451
+ )
452
+
453
+ # 验证必需字段
454
+ if "path" not in jsonpath_data or not jsonpath_data["path"]:
455
+ raise WorkflowValidationError(
456
+ message="jsonpath 条件缺少 path 字段",
457
+ field_path=f"{context}.jsonpath.path",
458
+ expected="非空 JSONPath 表达式",
459
+ actual=str(jsonpath_data.get("path")),
460
+ suggestion="请提供有效的 JSONPath 表达式,如 '$.files'",
461
+ )
462
+
463
+ jsonpath_config = JsonPathCondition(
464
+ input=jsonpath_data.get("input", ""),
465
+ path=jsonpath_data["path"],
466
+ exists=jsonpath_data.get("exists"),
467
+ equals=jsonpath_data.get("equals"),
468
+ contains=jsonpath_data.get("contains"),
469
+ )
470
+
471
+ return WhenConfig(regex=regex_config, jsonpath=jsonpath_config)
472
+
473
+
474
+ def parse_output_config(value: Any, context: str = "output 配置") -> OutputConfig:
475
+ """
476
+ 解析 output 配置
477
+
478
+ Args:
479
+ value: output 值(可能是字符串或字典)
480
+ context: 上下文描述(用于错误提示)
481
+
482
+ Returns:
483
+ OutputConfig 对象
484
+
485
+ Raises:
486
+ WorkflowValidationError: 如果配置格式不正确
487
+ """
488
+ if isinstance(value, str):
489
+ # 直接字符串,如 "${attempt_result}"
490
+ # 保存到 template 字段,以便后续渲染
491
+ return OutputConfig(template=value)
492
+
493
+ if isinstance(value, dict):
494
+ # 验证至少有一种提取方法
495
+ has_jsonpath = "jsonpath" in value and value["jsonpath"]
496
+ has_regex = "regex" in value and value["regex"]
497
+ has_template = "template" in value and value["template"]
498
+
499
+ if not (has_jsonpath or has_regex or has_template):
500
+ raise WorkflowValidationError(
501
+ message="output 配置必须指定至少一种提取方法",
502
+ field_path=context,
503
+ expected="jsonpath, regex 或 template 之一",
504
+ actual="无有效提取方法",
505
+ suggestion="请添加 jsonpath, regex 或直接使用字符串模板",
506
+ )
507
+
508
+ return OutputConfig(
509
+ jsonpath=value.get("jsonpath"),
510
+ regex=value.get("regex"),
511
+ regex_group=value.get("group"),
512
+ template=value.get("template"),
513
+ )
514
+
515
+ raise WorkflowValidationError(
516
+ message="output 配置格式不正确",
517
+ field_path=context,
518
+ expected="字符串或字典对象",
519
+ actual=str(type(value).__name__),
520
+ suggestion="使用字符串(如 '${attempt_result}')或字典(如 {jsonpath: '$.files'})",
521
+ )
522
+
523
+
524
+ def parse_step_conversation_config(
525
+ data: Dict[str, Any], context: str = "conversation 配置"
526
+ ) -> StepConversationConfig:
527
+ """
528
+ 解析 step 级别的 conversation 配置
529
+
530
+ Args:
531
+ data: conversation 字典
532
+ context: 上下文描述(用于错误提示)
533
+
534
+ Returns:
535
+ StepConversationConfig 对象
536
+
537
+ Raises:
538
+ WorkflowValidationError: 如果配置格式不正确
539
+ """
540
+ action = data.get("action", "resume")
541
+
542
+ # 验证 action 值
543
+ valid_actions = ["new", "resume", "continue"]
544
+ if action not in valid_actions:
545
+ raise WorkflowValidationError(
546
+ message=f"无效的 conversation action",
547
+ field_path=f"{context}.conversation.action",
548
+ expected=f"以下之一: {', '.join(valid_actions)}",
549
+ actual=f"'{action}'",
550
+ suggestion=f"请将 action 修改为 {', '.join(valid_actions)} 之一",
551
+ )
552
+
553
+ return StepConversationConfig(
554
+ action=action,
555
+ conversation_id=data.get("conversation_id"),
556
+ )
557
+
558
+
559
+ def _validate_required_field(
560
+ data: Dict[str, Any], field: str, context: str, yaml_path: str = None
561
+ ) -> None:
562
+ """
563
+ 验证必需字段是否存在
564
+
565
+ Args:
566
+ data: 数据字典
567
+ field: 字段名
568
+ context: 上下文描述(用于错误消息)
569
+ yaml_path: YAML 文件路径(可选,用于错误提示)
570
+
571
+ Raises:
572
+ WorkflowValidationError: 如果字段缺失或为空
573
+ """
574
+ if field not in data:
575
+ raise WorkflowValidationError(
576
+ message=f"缺少必需字段: '{field}'",
577
+ field_path=f"{context}.{field}",
578
+ expected=f"必需字段 '{field}'",
579
+ actual="字段不存在",
580
+ suggestion=f"请在 {context} 中添加 '{field}' 字段",
581
+ )
582
+
583
+ value = data[field]
584
+ if value is None or (isinstance(value, str) and not value.strip()):
585
+ raise WorkflowValidationError(
586
+ message=f"字段 '{field}' 不能为空",
587
+ field_path=f"{context}.{field}",
588
+ expected="非空值",
589
+ actual=str(value),
590
+ suggestion=f"请为 '{field}' 提供有效的值",
591
+ )
592
+
593
+
594
+ def _parse_globals_config(data: Dict[str, Any]) -> GlobalsConfig:
595
+ """
596
+ 解析 globals 配置并进行类型验证
597
+
598
+ Args:
599
+ data: globals 字典
600
+
601
+ Returns:
602
+ GlobalsConfig 对象
603
+
604
+ Raises:
605
+ WorkflowValidationError: 如果配置类型不正确
606
+ """
607
+ # 验证数值类型字段
608
+ max_turns = data.get("max_turns", 6)
609
+ if not isinstance(max_turns, int) or max_turns < 1:
610
+ raise WorkflowValidationError(
611
+ message="globals.max_turns 必须是正整数",
612
+ field_path="spec.globals.max_turns",
613
+ expected="正整数(>= 1)",
614
+ actual=str(max_turns),
615
+ suggestion="将 max_turns 设置为正整数,如 6",
616
+ )
617
+
618
+ retries = data.get("retries", 2)
619
+ if not isinstance(retries, int) or retries < 0:
620
+ raise WorkflowValidationError(
621
+ message="globals.retries 必须是非负整数",
622
+ field_path="spec.globals.retries",
623
+ expected="非负整数(>= 0)",
624
+ actual=str(retries),
625
+ suggestion="将 retries 设置为非负整数,如 2",
626
+ )
627
+
628
+ timeout_sec = data.get("timeout_sec", 300)
629
+ if not isinstance(timeout_sec, int) or timeout_sec < 1:
630
+ raise WorkflowValidationError(
631
+ message="globals.timeout_sec 必须是正整数",
632
+ field_path="spec.globals.timeout_sec",
633
+ expected="正整数(>= 1)",
634
+ actual=str(timeout_sec),
635
+ suggestion="将 timeout_sec 设置为正整数(秒),如 300",
636
+ )
637
+
638
+ # 验证布尔类型字段
639
+ include_rules = data.get("include_rules", False)
640
+ if not isinstance(include_rules, bool):
641
+ raise WorkflowValidationError(
642
+ message="globals.include_rules 必须是布尔值",
643
+ field_path="spec.globals.include_rules",
644
+ expected="true 或 false",
645
+ actual=str(include_rules),
646
+ suggestion="将 include_rules 设置为 true 或 false",
647
+ )
648
+
649
+ return GlobalsConfig(
650
+ model=data.get("model", "v3_chat"),
651
+ product_mode=data.get("product_mode", "lite"),
652
+ max_turns=max_turns,
653
+ retries=retries,
654
+ timeout_sec=timeout_sec,
655
+ include_rules=include_rules,
656
+ )
657
+
658
+
659
+ def _parse_conversation_config(data: Dict[str, Any]) -> ConversationConfig:
660
+ """
661
+ 解析 conversation 配置并进行值验证
662
+
663
+ Args:
664
+ data: conversation 字典
665
+
666
+ Returns:
667
+ ConversationConfig 对象
668
+
669
+ Raises:
670
+ WorkflowValidationError: 如果配置值不正确
671
+ """
672
+ start = data.get("start", "current")
673
+ valid_start_values = ["current", "new"]
674
+ if start not in valid_start_values:
675
+ raise WorkflowValidationError(
676
+ message="conversation.start 值无效",
677
+ field_path="spec.conversation.start",
678
+ expected=f"以下之一: {', '.join(valid_start_values)}",
679
+ actual=f"'{start}'",
680
+ suggestion=f"请将 start 修改为 {' 或 '.join(valid_start_values)}",
681
+ )
682
+
683
+ default_action = data.get("default_action", "resume")
684
+ valid_actions = ["resume", "new", "continue"]
685
+ if default_action not in valid_actions:
686
+ raise WorkflowValidationError(
687
+ message="conversation.default_action 值无效",
688
+ field_path="spec.conversation.default_action",
689
+ expected=f"以下之一: {', '.join(valid_actions)}",
690
+ actual=f"'{default_action}'",
691
+ suggestion=f"请将 default_action 修改为 {', '.join(valid_actions)} 之一",
692
+ )
693
+
694
+ return ConversationConfig(
695
+ start=start,
696
+ default_action=default_action,
697
+ )
698
+
699
+
700
+ def _parse_attempt_config(data: Dict[str, Any]) -> AttemptConfig:
701
+ """
702
+ 解析 attempt 配置并进行值验证
703
+
704
+ Args:
705
+ data: attempt 字典
706
+
707
+ Returns:
708
+ AttemptConfig 对象
709
+
710
+ Raises:
711
+ WorkflowValidationError: 如果配置值不正确
712
+ """
713
+ format_type = data.get("format", "json")
714
+ valid_formats = ["json", "text"]
715
+ if format_type not in valid_formats:
716
+ raise WorkflowValidationError(
717
+ message="attempt.format 值无效",
718
+ field_path="spec.attempt.format",
719
+ expected=f"以下之一: {', '.join(valid_formats)}",
720
+ actual=f"'{format_type}'",
721
+ suggestion=f"请将 format 修改为 {' 或 '.join(valid_formats)}",
722
+ )
723
+
724
+ jsonpaths = data.get("jsonpaths", {})
725
+ if not isinstance(jsonpaths, dict):
726
+ raise WorkflowValidationError(
727
+ message="attempt.jsonpaths 必须是字典对象",
728
+ field_path="spec.attempt.jsonpaths",
729
+ expected="字典对象",
730
+ actual=str(type(jsonpaths).__name__),
731
+ suggestion="将 jsonpaths 定义为键值对",
732
+ )
733
+
734
+ return AttemptConfig(
735
+ format=format_type,
736
+ jsonpaths=jsonpaths,
737
+ )