auto-coder 0.1.334__py3-none-any.whl → 0.1.336__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 (67) hide show
  1. {auto_coder-0.1.334.dist-info → auto_coder-0.1.336.dist-info}/METADATA +2 -2
  2. {auto_coder-0.1.334.dist-info → auto_coder-0.1.336.dist-info}/RECORD +67 -32
  3. autocoder/agent/agentic_edit.py +833 -0
  4. autocoder/agent/agentic_edit_tools/__init__.py +28 -0
  5. autocoder/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +32 -0
  6. autocoder/agent/agentic_edit_tools/attempt_completion_tool_resolver.py +29 -0
  7. autocoder/agent/agentic_edit_tools/base_tool_resolver.py +29 -0
  8. autocoder/agent/agentic_edit_tools/execute_command_tool_resolver.py +84 -0
  9. autocoder/agent/agentic_edit_tools/list_code_definition_names_tool_resolver.py +75 -0
  10. autocoder/agent/agentic_edit_tools/list_files_tool_resolver.py +62 -0
  11. autocoder/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py +30 -0
  12. autocoder/agent/agentic_edit_tools/read_file_tool_resolver.py +36 -0
  13. autocoder/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +95 -0
  14. autocoder/agent/agentic_edit_tools/search_files_tool_resolver.py +70 -0
  15. autocoder/agent/agentic_edit_tools/use_mcp_tool_resolver.py +55 -0
  16. autocoder/agent/agentic_edit_tools/write_to_file_tool_resolver.py +98 -0
  17. autocoder/agent/agentic_edit_types.py +124 -0
  18. autocoder/auto_coder.py +39 -18
  19. autocoder/auto_coder_rag.py +18 -9
  20. autocoder/auto_coder_runner.py +50 -5
  21. autocoder/chat_auto_coder_lang.py +18 -2
  22. autocoder/commands/tools.py +5 -1
  23. autocoder/common/__init__.py +2 -0
  24. autocoder/common/auto_coder_lang.py +40 -8
  25. autocoder/common/code_auto_generate_diff.py +1 -1
  26. autocoder/common/code_auto_generate_editblock.py +1 -1
  27. autocoder/common/code_auto_generate_strict_diff.py +1 -1
  28. autocoder/common/mcp_hub.py +185 -2
  29. autocoder/common/mcp_server.py +243 -306
  30. autocoder/common/mcp_server_install.py +269 -0
  31. autocoder/common/mcp_server_types.py +169 -0
  32. autocoder/common/stream_out_type.py +3 -0
  33. autocoder/common/v2/agent/__init__.py +0 -0
  34. autocoder/common/v2/agent/agentic_edit.py +1302 -0
  35. autocoder/common/v2/agent/agentic_edit_tools/__init__.py +28 -0
  36. autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +70 -0
  37. autocoder/common/v2/agent/agentic_edit_tools/attempt_completion_tool_resolver.py +35 -0
  38. autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +33 -0
  39. autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +88 -0
  40. autocoder/common/v2/agent/agentic_edit_tools/list_code_definition_names_tool_resolver.py +80 -0
  41. autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +105 -0
  42. autocoder/common/v2/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py +35 -0
  43. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +51 -0
  44. autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +144 -0
  45. autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +99 -0
  46. autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +46 -0
  47. autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +58 -0
  48. autocoder/common/v2/agent/agentic_edit_types.py +162 -0
  49. autocoder/common/v2/agent/agentic_tool_display.py +184 -0
  50. autocoder/common/v2/code_agentic_editblock_manager.py +812 -0
  51. autocoder/common/v2/code_auto_generate.py +1 -1
  52. autocoder/common/v2/code_auto_generate_diff.py +1 -1
  53. autocoder/common/v2/code_auto_generate_editblock.py +1 -1
  54. autocoder/common/v2/code_auto_generate_strict_diff.py +1 -1
  55. autocoder/common/v2/code_editblock_manager.py +151 -178
  56. autocoder/compilers/provided_compiler.py +3 -2
  57. autocoder/events/event_manager.py +4 -4
  58. autocoder/events/event_types.py +1 -0
  59. autocoder/memory/active_context_manager.py +2 -29
  60. autocoder/models.py +10 -2
  61. autocoder/shadows/shadow_manager.py +1 -1
  62. autocoder/utils/llms.py +4 -2
  63. autocoder/version.py +1 -1
  64. {auto_coder-0.1.334.dist-info → auto_coder-0.1.336.dist-info}/LICENSE +0 -0
  65. {auto_coder-0.1.334.dist-info → auto_coder-0.1.336.dist-info}/WHEEL +0 -0
  66. {auto_coder-0.1.334.dist-info → auto_coder-0.1.336.dist-info}/entry_points.txt +0 -0
  67. {auto_coder-0.1.334.dist-info → auto_coder-0.1.336.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,812 @@
1
+ from typing import List, Dict, Tuple, Optional, Any
2
+ import os
3
+ import time
4
+
5
+ import byzerllm
6
+
7
+ from autocoder.common.types import Mode, CodeGenerateResult, MergeCodeWithoutEffect
8
+ from autocoder.common import AutoCoderArgs, SourceCodeList, SourceCode
9
+ from autocoder.common.action_yml_file_manager import ActionYmlFileManager
10
+ from autocoder.common import sys_prompt
11
+ from autocoder.compilers.shadow_compiler import ShadowCompiler
12
+ from autocoder.privacy.model_filter import ModelPathFilter
13
+ from autocoder.common.utils_code_auto_generate import chat_with_continue, stream_chat_with_continue, ChatWithContinueResult
14
+ from autocoder.utils.auto_coder_utils.chat_stream_out import stream_out
15
+ from autocoder.common.stream_out_type import LintStreamOutType, CompileStreamOutType, UnmergedBlocksStreamOutType, ContextMissingCheckStreamOutType
16
+ from autocoder.common.auto_coder_lang import get_message_with_format
17
+ from autocoder.common.printer import Printer
18
+ from autocoder.rag.token_counter import count_tokens
19
+ from autocoder.utils import llms as llm_utils
20
+ from autocoder.memory.active_context_manager import ActiveContextManager
21
+ from autocoder.common.v2.code_auto_generate_editblock import CodeAutoGenerateEditBlock
22
+ from autocoder.common.v2.code_auto_merge_editblock import CodeAutoMergeEditBlock
23
+ from autocoder.shadows.shadow_manager import ShadowManager
24
+ from autocoder.linters.shadow_linter import ShadowLinter
25
+ from autocoder.linters.models import IssueSeverity
26
+ from loguru import logger
27
+ from autocoder.common.global_cancel import global_cancel
28
+ from autocoder.linters.models import ProjectLintResult
29
+ from autocoder.common.token_cost_caculate import TokenCostCalculator
30
+ from autocoder.events.event_manager_singleton import get_event_manager
31
+ from autocoder.events.event_types import Event, EventType, EventMetadata
32
+ from autocoder.events import event_content as EventContentCreator
33
+
34
+
35
+ class CodeEditBlockManager:
36
+ """
37
+ A class that combines code generation, linting, and merging with automatic error correction.
38
+ It generates code, lints it, and if there are errors, regenerates the code up to 5 times
39
+ before merging the final result.
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ llm: byzerllm.ByzerLLM,
45
+ args: AutoCoderArgs,
46
+ action=None,
47
+ fence_0: str = "```",
48
+ fence_1: str = "```",
49
+ ) -> None:
50
+ self.llm = llm
51
+ self.args = args
52
+ self.action = action
53
+ self.fence_0 = fence_0
54
+ self.fence_1 = fence_1
55
+ self.generate_times_same_model = args.generate_times_same_model
56
+ self.auto_fix_lint_max_attempts = args.auto_fix_lint_max_attempts
57
+ self.auto_fix_compile_max_attempts = args.auto_fix_compile_max_attempts
58
+ self.printer = Printer()
59
+
60
+ # Initialize sub-components
61
+ self.code_generator = CodeAutoGenerateEditBlock(
62
+ llm, args, action, fence_0, fence_1)
63
+ self.code_merger = CodeAutoMergeEditBlock(llm, args, fence_0, fence_1)
64
+
65
+ # Create shadow manager for linting
66
+ self.shadow_manager = ShadowManager(
67
+ args.source_dir, args.event_file, args.ignore_clean_shadows)
68
+ self.shadow_linter = ShadowLinter(self.shadow_manager, verbose=False)
69
+ self.shadow_compiler = ShadowCompiler(
70
+ self.shadow_manager, verbose=False)
71
+
72
+ @byzerllm.prompt()
73
+ def fix_linter_errors(self, query: str, lint_issues: str) -> str:
74
+ """
75
+ Linter 检测到的问题:
76
+ <lint_issues>
77
+ {{ lint_issues }}
78
+ </lint_issues>
79
+
80
+ 用户原始需求:
81
+ <user_query_wrapper>
82
+ {{ query }}
83
+ </user_query_wrapper>
84
+
85
+ 修复上述问题,请确保代码质量问题被解决,同时保持代码的原有功能。
86
+ 请严格遵守*SEARCH/REPLACE block*的格式。
87
+ """
88
+ @byzerllm.prompt()
89
+ def fix_compile_errors(self, query: str, compile_errors: str) -> str:
90
+ """
91
+ 编译错误:
92
+ <compile_errors>
93
+ {{ compile_errors }}
94
+ </compile_errors>
95
+
96
+ 用户原始需求:
97
+ <user_query_wrapper>
98
+ {{ query }}
99
+ </user_query_wrapper>
100
+
101
+ 修复上述问题,请确保代码质量问题被解决,同时保持代码的原有功能。
102
+ 请严格遵守*SEARCH/REPLACE block*的格式。
103
+ """
104
+
105
+ @byzerllm.prompt()
106
+ def fix_missing_context(self, query: str, original_code: str, missing_files: str) -> str:
107
+ """
108
+ 下面是你根据格式要求输出的一份修改代码:
109
+ <original_code>
110
+ {{ original_code }}
111
+ </original_code>
112
+
113
+ 我发现你尝试修改以下文件,但这些文件没有在上下文中提供,所以你无法看到它们的内容:
114
+ <missing_files>
115
+ {{ missing_files }}
116
+ </missing_files>
117
+
118
+ 下面是用户原始的需求:
119
+ <user_query_wrapper>
120
+ {{ query }}
121
+ </user_query_wrapper>
122
+
123
+ 我已经将这些文件添加到上下文中,请重新生成代码,确保使用SEARCH/REPLACE格式正确修改这些文件。
124
+ 对于每个文件,请确保SEARCH部分包含文件中的实际内容,而不是空的SEARCH块。
125
+ """
126
+
127
+ @byzerllm.prompt()
128
+ def fix_unmerged_blocks(self, query: str, original_code: str, unmerged_blocks: str) -> str:
129
+ """
130
+
131
+ 下面是你根据格式要求输出的一份修改代码:
132
+ <original_code>
133
+ {{ original_code }}
134
+ </original_code>
135
+
136
+ 但是我发现下面的代码块无法合并:
137
+ <unmerged_blocks>
138
+ {{ unmerged_blocks }}
139
+ </unmerged_blocks>
140
+
141
+ 下面是用户原始的需求:
142
+ <user_query_wrapper>
143
+ {{ query }}
144
+ </user_query_wrapper>
145
+
146
+ 请根据反馈,回顾之前的格式要求,重新生成一份修改代码,确保所有代码块都能够正确合并。
147
+ """
148
+
149
+ def _create_shadow_files_from_edits(self, generation_result: CodeGenerateResult) -> Dict[str, str]:
150
+ """
151
+ 从编辑块内容中提取代码并创建临时影子文件用于检查。
152
+
153
+ 参数:
154
+ generation_result (CodeGenerateResult): 包含SEARCH/REPLACE块的内容
155
+
156
+ 返回:
157
+ Dict[str, str]: 映射 {影子文件路径: 内容}
158
+ """
159
+ result = self.code_merger.choose_best_choice(generation_result)
160
+ merge = self.code_merger._merge_code_without_effect(result.contents[0])
161
+ shadow_files = {}
162
+ for file_path, new_content in merge.success_blocks:
163
+ self.shadow_manager.update_file(file_path, new_content)
164
+ shadow_files[self.shadow_manager.to_shadow_path(
165
+ file_path)] = new_content
166
+
167
+ return shadow_files
168
+
169
+ def _format_lint_issues(self, lint_results: ProjectLintResult, levels: List[IssueSeverity] = [IssueSeverity.ERROR]) -> str:
170
+ """
171
+ 将linter结果格式化为字符串供模型使用
172
+
173
+ 参数:
174
+ lint_results: Linter结果对象
175
+ level: 过滤问题的级别
176
+
177
+ 返回:
178
+ str: 格式化的问题描述
179
+ """
180
+ formatted_issues = []
181
+
182
+ for file_path, result in lint_results.file_results.items():
183
+ file_has_issues = False
184
+ file_issues = []
185
+
186
+ for issue in result.issues:
187
+ if issue.severity.value not in levels:
188
+ continue
189
+
190
+ if not file_has_issues:
191
+ file_has_issues = True
192
+ file_issues.append(f"文件: {file_path}")
193
+
194
+ severity = "错误" if issue.severity == IssueSeverity.ERROR else "警告" if issue.severity == IssueSeverity.WARNING else "信息"
195
+ line_info = f"第{issue.position.line}行"
196
+ if issue.position.column:
197
+ line_info += f", 第{issue.position.column}列"
198
+
199
+ file_issues.append(
200
+ f" - [{severity}] {line_info}: {issue.message} (规则: {issue.code})"
201
+ )
202
+
203
+ if file_has_issues:
204
+ formatted_issues.extend(file_issues)
205
+ formatted_issues.append("") # 空行分隔不同文件
206
+
207
+ return "\n".join(formatted_issues)
208
+
209
+ def _count_errors(self, lint_results: ProjectLintResult, levels: List[IssueSeverity] = [IssueSeverity.ERROR]) -> int:
210
+ """
211
+ 计算lint结果中的错误数量
212
+
213
+ 参数:
214
+ lint_results: Linter结果对象
215
+
216
+ 返回:
217
+ int: 错误数量
218
+ """
219
+ error_count = 0
220
+
221
+ for _, result in lint_results.file_results.items():
222
+ if IssueSeverity.ERROR in levels:
223
+ error_count += result.error_count
224
+ if IssueSeverity.WARNING in levels:
225
+ error_count += result.warning_count
226
+ if IssueSeverity.INFO in levels:
227
+ error_count += result.info_count
228
+ if IssueSeverity.HINT in levels:
229
+ error_count += result.hint_count
230
+
231
+ return error_count
232
+
233
+ def _fix_missing_context(self, query: str, generation_result: CodeGenerateResult, source_code_list: SourceCodeList) -> CodeGenerateResult:
234
+ """
235
+ 检查是否有空的SEARCH块但目标文件存在,如果有,将文件添加到动态上下文并重新生成代码
236
+
237
+ 参数:
238
+ query (str): 用户查询
239
+ generation_result (CodeGenerateResult): 生成的代码结果
240
+ source_code_list (SourceCodeList): 源代码列表
241
+
242
+ 返回:
243
+ CodeGenerateResult: 修复后的代码结果
244
+ """
245
+ get_event_manager(self.args.event_file).write_result(
246
+ EventContentCreator.create_result(
247
+ content=self.printer.get_message_from_key("/context/check/start")),
248
+ metadata={
249
+ # Using a placeholder type, replace if ContextFixStreamOutType is defined
250
+ "stream_out_type": ContextMissingCheckStreamOutType.CONTEXT_MISSING_CHECK.value,
251
+ "action_file": self.args.file,
252
+ "path": "/context/check/start"
253
+ }
254
+ )
255
+
256
+ def write_end_event():
257
+ get_event_manager(self.args.event_file).write_result(
258
+ EventContentCreator.create_result(
259
+ content=self.printer.get_message_from_key("/context/check/end")),
260
+ metadata={
261
+ # Using a placeholder type, replace if ContextFixStreamOutType is defined
262
+ "stream_out_type": ContextMissingCheckStreamOutType.CONTEXT_MISSING_CHECK.value,
263
+ "action_file": self.args.file,
264
+ "path": "/context/check/end"
265
+ }
266
+ )
267
+
268
+ token_cost_calculator = TokenCostCalculator(args=self.args)
269
+
270
+ # 获取编辑块
271
+ codes = self.code_merger.get_edits(generation_result.contents[0])
272
+
273
+ # 检查是否有空的SEARCH块但目标文件存在
274
+ missing_files = []
275
+ for file_path, head, update in codes:
276
+ # 如果SEARCH块为空,检查文件是否存在但不在上下文中
277
+ if not head and os.path.exists(file_path):
278
+ # 检查文件是否已经在上下文中
279
+ in_context = False
280
+ if hasattr(self.args, 'urls') and self.args.urls:
281
+ in_context = file_path in self.args.urls
282
+ if hasattr(self.args, 'dynamic_urls') and self.args.dynamic_urls and not in_context:
283
+ in_context = file_path in self.args.dynamic_urls
284
+
285
+ # 如果文件存在但不在上下文中,添加到缺失文件列表
286
+ if not in_context:
287
+ missing_files.append(file_path)
288
+ # 将文件添加到动态上下文中
289
+ if not hasattr(self.args, 'dynamic_urls'):
290
+ self.args.dynamic_urls = []
291
+ if file_path not in self.args.dynamic_urls:
292
+ self.args.dynamic_urls.append(file_path)
293
+
294
+ # 如果没有缺失文件,直接返回原结果
295
+ if not missing_files:
296
+ write_end_event()
297
+ return generation_result
298
+
299
+ # 格式化缺失文件列表
300
+ missing_files_text = "\n".join(missing_files)
301
+
302
+ # 打印当前修复状态
303
+ self.printer.print_in_terminal(
304
+ "missing_context_attempt_status",
305
+ style="yellow",
306
+ missing_files=missing_files_text
307
+ )
308
+
309
+ # 更新源代码列表,包含新添加的文件
310
+ updated_source_code_list = SourceCodeList([])
311
+ for source in source_code_list.sources:
312
+ updated_source_code_list.sources.append(source)
313
+
314
+ # 添加缺失的文件到源代码列表
315
+ for file_path in missing_files:
316
+ if os.path.exists(file_path):
317
+ with open(file_path, 'r') as f:
318
+ file_content = f.read()
319
+ source = SourceCode(module_name=file_path,
320
+ source_code=file_content)
321
+ updated_source_code_list.sources.append(source)
322
+
323
+ # 更新action yml文件
324
+ if missing_files and hasattr(self.args, 'dynamic_urls') and self.args.dynamic_urls:
325
+ action_yml_file_manager = ActionYmlFileManager(
326
+ self.args.source_dir)
327
+ action_file_name = os.path.basename(self.args.file)
328
+ update_yaml_success = action_yml_file_manager.update_yaml_field(
329
+ action_file_name, "dynamic_urls", self.args.dynamic_urls)
330
+ if not update_yaml_success:
331
+ self.printer.print_in_terminal(
332
+ "yaml_save_error", style="red", yaml_file=action_file_name)
333
+
334
+ # 准备修复提示
335
+ fix_prompt = self.fix_missing_context.prompt(
336
+ query=query,
337
+ original_code=generation_result.contents[0],
338
+ missing_files=missing_files_text
339
+ )
340
+
341
+ logger.info(f"fix_missing_context_prompt: {fix_prompt}")
342
+
343
+ # 使用修复提示重新生成代码
344
+ start_time = time.time()
345
+ generation_result = self.code_generator.single_round_run(
346
+ fix_prompt, updated_source_code_list)
347
+
348
+ # 计算这次修复缺失上下文花费的token情况
349
+ token_cost_calculator.track_token_usage_by_generate(
350
+ llm=self.llm,
351
+ generate=generation_result,
352
+ operation_name="code_generation_complete",
353
+ start_time=start_time,
354
+ end_time=time.time()
355
+ )
356
+
357
+ # 选择最佳结果
358
+ generation_result = self.code_merger.choose_best_choice(
359
+ generation_result)
360
+ # 因为已经排完结果,就不要触发后面的排序了,所以只要保留第一个即可。
361
+ generation_result = CodeGenerateResult(contents=[generation_result.contents[0]], conversations=[
362
+ generation_result.conversations[0]], metadata=generation_result.metadata)
363
+
364
+ write_end_event()
365
+ return generation_result
366
+
367
+ def _fix_unmerged_blocks(self, query: str, generation_result: CodeGenerateResult, source_code_list: SourceCodeList) -> CodeGenerateResult:
368
+ """
369
+ 修复未合并的代码块,最多尝试指定次数
370
+
371
+ 参数:
372
+ query (str): 用户查询
373
+ generation_result (CodeGenerateResult): 生成的代码结果
374
+ source_code_list (SourceCodeList): 源代码列表
375
+
376
+ 返回:
377
+ CodeGenerateResult: 修复后的代码结果
378
+ """
379
+ token_cost_calculator = TokenCostCalculator(args=self.args)
380
+ merge = self.code_merger._merge_code_without_effect(
381
+ generation_result.contents[0])
382
+
383
+ if not self.args.enable_auto_fix_merge or not merge.failed_blocks:
384
+ return generation_result
385
+
386
+ get_event_manager(self.args.event_file).write_result(
387
+ EventContentCreator.create_result(
388
+ content=self.printer.get_message_from_key("/unmerged_blocks/check/start")),
389
+ metadata={
390
+ "stream_out_type": UnmergedBlocksStreamOutType.UNMERGED_BLOCKS.value,
391
+ "action_file": self.args.file,
392
+ "path": "/unmerged_blocks/check/start"
393
+ }
394
+ )
395
+
396
+ def _format_blocks(merge: MergeCodeWithoutEffect) -> Tuple[str, str]:
397
+ unmerged_formatted_text = ""
398
+ for file_path, head, update in merge.failed_blocks:
399
+ unmerged_formatted_text += "```lang\n"
400
+ unmerged_formatted_text += f"##File: {file_path}\n"
401
+ unmerged_formatted_text += "<<<<<<< SEARCH\n"
402
+ unmerged_formatted_text += head
403
+ unmerged_formatted_text += "=======\n"
404
+ unmerged_formatted_text += update
405
+ unmerged_formatted_text += ">>>>>>> REPLACE\n"
406
+ unmerged_formatted_text += "```"
407
+ unmerged_formatted_text += "\n"
408
+
409
+ merged_formatted_text = ""
410
+ if merge.merged_blocks:
411
+ for file_path, head, update in merge.merged_blocks:
412
+ merged_formatted_text += "```lang\n"
413
+ merged_formatted_text += f"##File: {file_path}\n"
414
+ merged_formatted_text += head
415
+ merged_formatted_text += "=======\n"
416
+ merged_formatted_text += update
417
+ merged_formatted_text += "```"
418
+ merged_formatted_text += "\n"
419
+
420
+ get_event_manager(self.args.event_file).write_result(EventContentCreator.create_result(
421
+ content=EventContentCreator.ResultContent(content=f"Unmerged blocks:\n\n {unmerged_formatted_text}",
422
+ metadata={
423
+ "merged_blocks": merge.success_blocks,
424
+ "failed_blocks": merge.failed_blocks
425
+ }
426
+ ).to_dict(),
427
+ metadata={
428
+ "stream_out_type": UnmergedBlocksStreamOutType.UNMERGED_BLOCKS.value,
429
+ "action_file": self.args.file,
430
+ "path": "/unmerged_blocks/check/message"
431
+ }
432
+ ))
433
+ return (unmerged_formatted_text, merged_formatted_text)
434
+
435
+ for attempt in range(self.args.auto_fix_merge_max_attempts):
436
+ global_cancel.check_and_raise()
437
+ unmerged_formatted_text, merged_formatted_text = _format_blocks(
438
+ merge)
439
+ fix_prompt = self.fix_unmerged_blocks.prompt(
440
+ query=query,
441
+ original_code=generation_result.contents[0],
442
+ unmerged_blocks=unmerged_formatted_text
443
+ )
444
+
445
+ logger.info(f"fix_prompt: {fix_prompt}")
446
+
447
+ # 打印当前修复尝试状态
448
+ self.printer.print_in_terminal(
449
+ "unmerged_blocks_attempt_status",
450
+ style="yellow",
451
+ attempt=(attempt + 1),
452
+ max_correction_attempts=self.args.auto_fix_merge_max_attempts
453
+ )
454
+
455
+ get_event_manager(self.args.event_file).write_result(EventContentCreator.create_result(
456
+ content=EventContentCreator.ResultContent(content=f"Unmerged blocks attempt {attempt + 1}/{self.args.auto_fix_merge_max_attempts}: {unmerged_formatted_text}",
457
+ metadata={}
458
+ ).to_dict(),
459
+ metadata={
460
+ "stream_out_type": UnmergedBlocksStreamOutType.UNMERGED_BLOCKS.value,
461
+ "action_file": self.args.file,
462
+ "path": "/unmerged_blocks/check/message"
463
+ }
464
+ ))
465
+
466
+ # 使用修复提示重新生成代码
467
+ start_time = time.time()
468
+ generation_result = self.code_generator.single_round_run(
469
+ fix_prompt, source_code_list)
470
+
471
+ # 计算这次修复未合并块花费的token情况
472
+ token_cost_calculator.track_token_usage_by_generate(
473
+ llm=self.llm,
474
+ generate=generation_result,
475
+ operation_name="code_generation_complete",
476
+ start_time=start_time,
477
+ end_time=time.time()
478
+ )
479
+
480
+ # 检查修复后的代码是否仍有未合并块
481
+ generation_result = self.code_merger.choose_best_choice(
482
+ generation_result)
483
+ # 因为已经排完结果,就不要触发后面的排序了,所以只要保留第一个即可。
484
+ generation_result = CodeGenerateResult(contents=[generation_result.contents[0]], conversations=[
485
+ generation_result.conversations[0]], metadata=generation_result.metadata)
486
+ merge = self.code_merger._merge_code_without_effect(
487
+ generation_result.contents[0])
488
+
489
+ # 如果没有失败的块,则修复成功,退出循环
490
+ if not merge.failed_blocks:
491
+ self.printer.print_in_terminal(
492
+ "unmerged_blocks_fixed", style="green")
493
+ break
494
+
495
+ # 如果是最后一次尝试仍未成功,打印警告
496
+ if attempt == self.args.auto_fix_merge_max_attempts - 1:
497
+ self.printer.print_in_terminal(
498
+ "max_unmerged_blocks_attempts_reached", style="yellow")
499
+ get_event_manager(self.args.event_file).write_result(EventContentCreator.create_result(
500
+ content=EventContentCreator.ResultContent(content=self.printer.get_message_from_key("max_unmerged_blocks_attempts_reached"),
501
+ metadata={}
502
+ ).to_dict(),
503
+ metadata={
504
+ "stream_out_type": UnmergedBlocksStreamOutType.UNMERGED_BLOCKS.value,
505
+ "action_file": self.args.file
506
+ }
507
+ ))
508
+ raise Exception(self.printer.get_message_from_key(
509
+ "max_unmerged_blocks_attempts_reached"))
510
+
511
+ get_event_manager(self.args.event_file).write_result(
512
+ EventContentCreator.create_result(
513
+ content=self.printer.get_message_from_key("/unmerged_blocks/check/end")),
514
+ metadata={
515
+ "stream_out_type": UnmergedBlocksStreamOutType.UNMERGED_BLOCKS.value,
516
+ "action_file": self.args.file,
517
+ "path": "/unmerged_blocks/check/end"
518
+ }
519
+ )
520
+ return generation_result
521
+
522
+ def _fix_lint_errors(self, query: str, generation_result: CodeGenerateResult, source_code_list: SourceCodeList) -> CodeGenerateResult:
523
+ """
524
+ 自动修复lint错误,最多尝试指定次数
525
+
526
+ 参数:
527
+ query (str): 用户查询
528
+ generation_result (CodeGenerateResult): 生成的代码结果
529
+ source_code_list (SourceCodeList): 源代码列表
530
+
531
+ 返回:
532
+ CodeGenerateResult: 修复后的代码结果
533
+ """
534
+ get_event_manager(self.args.event_file).write_result(
535
+ EventContentCreator.create_result(
536
+ content=self.printer.get_message_from_key("/lint/check/start")),
537
+ metadata={
538
+ "stream_out_type": LintStreamOutType.LINT.value,
539
+ "action_file": self.args.file,
540
+ "path": "/lint/check/start"
541
+ }
542
+ )
543
+
544
+ token_cost_calculator = TokenCostCalculator(args=self.args)
545
+
546
+ for attempt in range(self.auto_fix_lint_max_attempts):
547
+ global_cancel.check_and_raise()
548
+ # 代码生成结果更新到影子文件里去
549
+ self.shadow_manager.clean_shadows()
550
+ shadow_files = self._create_shadow_files_from_edits(
551
+ generation_result)
552
+
553
+ if not shadow_files:
554
+ self.printer.print_in_terminal(
555
+ "no_files_to_lint", style="yellow")
556
+ break
557
+
558
+ # 运行linter
559
+ lint_results = self.shadow_linter.lint_all_shadow_files()
560
+ error_count = self._count_errors(lint_results)
561
+
562
+ # 如果没有错误则完成
563
+ if error_count == 0:
564
+ self.printer.print_in_terminal(
565
+ "no_lint_errors_found", style="green")
566
+ break
567
+
568
+ # 格式化lint问题
569
+ formatted_issues = self._format_lint_issues(
570
+ lint_results, [IssueSeverity.ERROR, IssueSeverity.WARNING])
571
+
572
+ # 打印当前错误
573
+ self.printer.print_in_terminal(
574
+ "lint_attempt_status",
575
+ style="yellow",
576
+ attempt=(attempt + 1),
577
+ max_correction_attempts=self.auto_fix_lint_max_attempts,
578
+ error_count=error_count,
579
+ formatted_issues=formatted_issues
580
+ )
581
+
582
+ # 把lint结果记录到事件系统
583
+ get_event_manager(self.args.event_file).write_result(EventContentCreator.create_result(
584
+ content=EventContentCreator.ResultContent(content=f"Lint attempt {attempt + 1}/{self.auto_fix_lint_max_attempts}: Found {error_count} issues:\n {formatted_issues}",
585
+ metadata={}
586
+ ).to_dict(),
587
+ metadata={
588
+ "stream_out_type": LintStreamOutType.LINT.value,
589
+ "action_file": self.args.file,
590
+ "path": "/lint/check/message"
591
+ }
592
+ ))
593
+
594
+ if attempt == self.auto_fix_lint_max_attempts - 1:
595
+ self.printer.print_in_terminal(
596
+ "max_attempts_reached", style="yellow")
597
+ break
598
+
599
+ # 准备修复提示
600
+ fix_prompt = self.fix_linter_errors.prompt(
601
+ query=query,
602
+ lint_issues=formatted_issues
603
+ )
604
+
605
+ # 将 shadow_files 转化为 source_code_list
606
+ start_time = time.time()
607
+ source_code_list = self.code_merger.get_source_code_list_from_shadow_files(
608
+ shadow_files)
609
+ generation_result = self.code_generator.single_round_run(
610
+ fix_prompt, source_code_list)
611
+
612
+ # 计算这次修复lint问题花费的token情况
613
+ token_cost_calculator.track_token_usage_by_generate(
614
+ llm=self.llm,
615
+ generate=generation_result,
616
+ operation_name="code_generation_complete",
617
+ start_time=start_time,
618
+ end_time=time.time()
619
+ )
620
+
621
+ get_event_manager(self.args.event_file).write_result(
622
+ EventContentCreator.create_result(
623
+ content=self.printer.get_message_from_key("/lint/check/end")),
624
+ metadata={
625
+ "stream_out_type": LintStreamOutType.LINT.value,
626
+ "action_file": self.args.file,
627
+ "path": "/lint/check/end"
628
+ }
629
+ )
630
+ return generation_result
631
+
632
+ def _fix_compile_errors(self, query: str, generation_result: CodeGenerateResult, source_code_list: SourceCodeList) -> CodeGenerateResult:
633
+ """
634
+ 自动修复编译错误,最多尝试指定次数
635
+
636
+ 参数:
637
+ query (str): 用户查询
638
+ generation_result (CodeGenerateResult): 生成的代码结果
639
+ source_code_list (SourceCodeList): 源代码列表
640
+
641
+ 返回:
642
+ CodeGenerateResult: 修复后的代码结果
643
+ """
644
+ if not self.args.enable_auto_fix_compile:
645
+ return generation_result
646
+
647
+ get_event_manager(self.args.event_file).write_result(
648
+ EventContentCreator.create_result(
649
+ content=self.printer.get_message_from_key("/compile/check/start")),
650
+ metadata={
651
+ "stream_out_type": CompileStreamOutType.COMPILE.value,
652
+ "action_file": self.args.file,
653
+ "path": "/compile/check/start"
654
+ }
655
+ )
656
+
657
+ token_cost_calculator = TokenCostCalculator(args=self.args)
658
+
659
+ for attempt in range(self.auto_fix_compile_max_attempts):
660
+ global_cancel.check_and_raise()
661
+ # 先更新增量影子系统的文件
662
+ self.shadow_manager.clean_shadows()
663
+ shadow_files = self._create_shadow_files_from_edits(
664
+ generation_result)
665
+
666
+ # 在影子系统生成完整的项目,然后编译
667
+ compile_result = self.shadow_compiler.compile_all_shadow_files()
668
+
669
+ # 如果编译成功,则退出,继续往后走
670
+ if compile_result.success or compile_result.total_errors == 0:
671
+ self.printer.print_in_terminal(
672
+ "compile_success", style="green")
673
+ break
674
+
675
+ # 如果有错误,则把compile结果记录到事件系统
676
+ get_event_manager(self.args.event_file).write_result(EventContentCreator.create_result(
677
+ content=EventContentCreator.ResultContent(content=f"Compile attempt {attempt + 1}/{self.auto_fix_compile_max_attempts}: Found {compile_result.total_errors} errors:\n {compile_result.to_str()}",
678
+ metadata={}
679
+ ).to_dict(),
680
+ metadata={
681
+ "stream_out_type": CompileStreamOutType.COMPILE.value,
682
+ "action_file": self.args.file,
683
+ "path": "/compile/check/message"
684
+ }
685
+ ))
686
+
687
+ if attempt == self.auto_fix_compile_max_attempts - 1:
688
+ self.printer.print_in_terminal(
689
+ "max_compile_attempts_reached", style="yellow")
690
+ break
691
+
692
+ # 打印当前compile错误
693
+ self.printer.print_in_terminal("compile_attempt_status",
694
+ style="yellow",
695
+ attempt=(attempt + 1), max_correction_attempts=self.auto_fix_compile_max_attempts,
696
+ error_count=compile_result.total_errors,
697
+ formatted_issues=compile_result.to_str())
698
+
699
+ fix_compile_prompt = self.fix_compile_errors.prompt(
700
+ query=query,
701
+ compile_errors=compile_result.to_str()
702
+ )
703
+
704
+ # 将 shadow_files 转化为 source_code_list
705
+ start_time = time.time()
706
+ source_code_list = self.code_merger.get_source_code_list_from_shadow_files(
707
+ shadow_files)
708
+ generation_result = self.code_generator.single_round_run(
709
+ fix_compile_prompt, source_code_list)
710
+
711
+ # 计算这次修复compile问题花费的token情况
712
+ token_cost_calculator.track_token_usage_by_generate(
713
+ llm=self.llm,
714
+ generate=generation_result,
715
+ operation_name="code_generation_complete",
716
+ start_time=start_time,
717
+ end_time=time.time()
718
+ )
719
+
720
+ # Log end only if enabled
721
+ if self.args.enable_auto_fix_compile:
722
+ get_event_manager(self.args.event_file).write_result(
723
+ EventContentCreator.create_result(
724
+ content=self.printer.get_message_from_key("/compile/check/end")),
725
+ metadata={
726
+ "stream_out_type": CompileStreamOutType.COMPILE.value,
727
+ "action_file": self.args.file,
728
+ "path": "/compile/check/end"
729
+ }
730
+ )
731
+ return generation_result
732
+
733
+ def generate_and_fix(self, query: str, source_code_list: SourceCodeList) -> CodeGenerateResult:
734
+ """
735
+ 生成代码,运行linter/compile,修复错误,最多尝试指定次数
736
+
737
+ 参数:
738
+ query (str): 用户查询
739
+ source_code_list (SourceCodeList): 源代码列表
740
+
741
+ 返回:
742
+ CodeGenerateResult: 生成的代码结果
743
+ """
744
+ # 初始代码生成
745
+ self.printer.print_in_terminal("generating_initial_code")
746
+ start_time = time.time()
747
+ generation_result = self.code_generator.single_round_run(
748
+ query, source_code_list)
749
+
750
+ token_cost_calculator = TokenCostCalculator(args=self.args)
751
+ token_cost_calculator.track_token_usage_by_generate(
752
+ llm=self.llm,
753
+ generate=generation_result,
754
+ operation_name="code_generation_complete",
755
+ start_time=start_time,
756
+ end_time=time.time()
757
+ )
758
+
759
+ # 确保结果非空
760
+ if not generation_result.contents:
761
+ self.printer.print_in_terminal("generation_failed", style="red")
762
+ return generation_result
763
+
764
+ # 可能第一次触发排序
765
+ generation_result = self.code_merger.choose_best_choice(
766
+ generation_result)
767
+
768
+ # 因为已经排完结果,就不要触发后面的排序了,所以只要保留第一个即可。
769
+ generation_result = CodeGenerateResult(contents=[generation_result.contents[0]], conversations=[
770
+ generation_result.conversations[0]], metadata=generation_result.metadata)
771
+
772
+ # 修复缺少上下文的文件
773
+ generation_result = self._fix_missing_context(
774
+ query, generation_result, source_code_list)
775
+
776
+ # 修复未合并的代码块
777
+ generation_result = self._fix_unmerged_blocks(
778
+ query, generation_result, source_code_list)
779
+
780
+ # 修复lint错误
781
+ generation_result = self._fix_lint_errors(
782
+ query, generation_result, source_code_list)
783
+
784
+ # 修复编译错误
785
+ generation_result = self._fix_compile_errors(
786
+ query, generation_result, source_code_list)
787
+
788
+ # self.shadow_manager.clean_shadows()
789
+
790
+ # 返回最终结果
791
+ return generation_result
792
+
793
+ def run(self, query: str, source_code_list: SourceCodeList) -> CodeGenerateResult:
794
+ """
795
+ 执行完整的代码生成、修复、合并流程
796
+
797
+ 参数:
798
+ query (str): 用户查询
799
+ source_code_list (SourceCodeList): 源代码列表
800
+
801
+ 返回:
802
+ CodeGenerateResult: 生成和修复的代码结果
803
+ """
804
+ # 生成代码并自动修复lint错误
805
+
806
+ generation_result = self.generate_and_fix(query, source_code_list)
807
+ global_cancel.check_and_raise()
808
+
809
+ # 合并代码
810
+ self.code_merger.merge_code(generation_result)
811
+
812
+ return generation_result