auto-coder 0.1.263__py3-none-any.whl → 0.1.265__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 (58) hide show
  1. {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/METADATA +1 -1
  2. {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/RECORD +58 -55
  3. autocoder/agent/planner.py +4 -4
  4. autocoder/auto_coder.py +26 -21
  5. autocoder/auto_coder_server.py +7 -7
  6. autocoder/chat_auto_coder.py +203 -98
  7. autocoder/commands/auto_command.py +81 -4
  8. autocoder/commands/tools.py +48 -50
  9. autocoder/common/__init__.py +6 -1
  10. autocoder/common/auto_coder_lang.py +41 -3
  11. autocoder/common/code_auto_generate.py +3 -3
  12. autocoder/common/code_auto_generate_diff.py +12 -15
  13. autocoder/common/code_auto_generate_editblock.py +3 -3
  14. autocoder/common/code_auto_generate_strict_diff.py +3 -3
  15. autocoder/common/code_auto_merge.py +23 -3
  16. autocoder/common/code_auto_merge_diff.py +29 -4
  17. autocoder/common/code_auto_merge_editblock.py +25 -5
  18. autocoder/common/code_auto_merge_strict_diff.py +26 -6
  19. autocoder/common/code_modification_ranker.py +65 -3
  20. autocoder/common/command_completer.py +3 -0
  21. autocoder/common/command_generator.py +24 -8
  22. autocoder/common/command_templates.py +2 -2
  23. autocoder/common/conf_import_export.py +105 -0
  24. autocoder/common/conf_validator.py +7 -1
  25. autocoder/common/context_pruner.py +305 -0
  26. autocoder/common/files.py +41 -2
  27. autocoder/common/image_to_page.py +11 -11
  28. autocoder/common/index_import_export.py +38 -18
  29. autocoder/common/mcp_hub.py +3 -3
  30. autocoder/common/mcp_server.py +2 -2
  31. autocoder/common/shells.py +254 -13
  32. autocoder/common/stats_panel.py +126 -0
  33. autocoder/dispacher/actions/action.py +6 -18
  34. autocoder/dispacher/actions/copilot.py +2 -2
  35. autocoder/dispacher/actions/plugins/action_regex_project.py +1 -3
  36. autocoder/dispacher/actions/plugins/action_translate.py +1 -1
  37. autocoder/index/entry.py +8 -2
  38. autocoder/index/filter/normal_filter.py +13 -2
  39. autocoder/index/filter/quick_filter.py +127 -13
  40. autocoder/index/index.py +8 -7
  41. autocoder/models.py +2 -2
  42. autocoder/pyproject/__init__.py +5 -5
  43. autocoder/rag/cache/byzer_storage_cache.py +4 -4
  44. autocoder/rag/cache/file_monitor_cache.py +2 -2
  45. autocoder/rag/cache/simple_cache.py +4 -4
  46. autocoder/rag/long_context_rag.py +2 -2
  47. autocoder/regexproject/__init__.py +3 -2
  48. autocoder/suffixproject/__init__.py +3 -2
  49. autocoder/tsproject/__init__.py +3 -2
  50. autocoder/utils/conversation_store.py +1 -1
  51. autocoder/utils/operate_config_api.py +3 -3
  52. autocoder/utils/project_structure.py +258 -3
  53. autocoder/utils/thread_utils.py +6 -1
  54. autocoder/version.py +1 -1
  55. {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/LICENSE +0 -0
  56. {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/WHEEL +0 -0
  57. {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/entry_points.txt +0 -0
  58. {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/top_level.txt +0 -0
@@ -138,6 +138,11 @@ class CommandConfig(BaseModel):
138
138
  lib: SkipValidation[Callable]
139
139
  execute_shell_command: SkipValidation[Callable]
140
140
  generate_shell_command: SkipValidation[Callable]
141
+ conf_export: SkipValidation[Callable]
142
+ conf_import: SkipValidation[Callable]
143
+ index_export: SkipValidation[Callable]
144
+ index_import: SkipValidation[Callable]
145
+ exclude_files: SkipValidation[Callable]
141
146
 
142
147
 
143
148
 
@@ -298,6 +303,7 @@ class CommandAutoTuner:
298
303
  try:
299
304
  response = to_model(content, AutoCommandResponse)
300
305
  if response.suggestions:
306
+ print(response)
301
307
  command = response.suggestions[0].command
302
308
  parameters = response.suggestions[0].parameters
303
309
  if parameters:
@@ -306,9 +312,9 @@ class CommandAutoTuner:
306
312
  params_str = ""
307
313
  return f"{command}({params_str})"
308
314
  else:
309
- return printer.get_message_from_key("satisfied_prompt", style="green")
310
- except Exception as e:
311
- logger.error(f"Error extracting command response: {e}")
315
+ return printer.get_message_from_key("satisfied_prompt")
316
+ except Exception as e:
317
+ logger.error(f"Error extracting command response: {str(e)}")
312
318
  return content
313
319
 
314
320
  model_name = ",".join(llms_utils.get_llm_names(self.llm))
@@ -1111,7 +1117,72 @@ class CommandAutoTuner:
1111
1117
  </usage>
1112
1118
  </command>
1113
1119
 
1114
- </commands>
1120
+ <command>
1121
+ <name>conf_export</name>
1122
+ <description>配置管理命令,用于管理和控制配置。</description>
1123
+ <usage>
1124
+ 该命令导出当前软件的配置,并保存到指定路径。
1125
+
1126
+ 使用例子:
1127
+ conf_export(path="导出路径,通常是.json文件")
1128
+
1129
+ </usage>
1130
+ </command>
1131
+
1132
+ <command>
1133
+ <name>conf_import</name>
1134
+ <description>配置管理命令,用于管理和控制配置。</description>
1135
+ <usage>
1136
+ 该命令导入指定路径的配置文件到当前软件。
1137
+
1138
+ 使用例子:
1139
+ conf_import(path="导入路径,通常是.json文件")
1140
+
1141
+ </usage>
1142
+ </command>
1143
+
1144
+ <command>
1145
+ <name>index_export</name>
1146
+ <description>索引管理命令,用于管理和控制索引。</description>
1147
+ <usage>
1148
+ 该命令导出当前软件的索引,并保存到指定路径。
1149
+
1150
+ 使用例子:
1151
+ index_export(path="导出路径,通常是.json文件")
1152
+
1153
+ </usage>
1154
+ </command>
1155
+
1156
+ <command>
1157
+ <name>index_import</name>
1158
+ <description>索引管理命令,用于管理和控制索引。</description>
1159
+ <usage>
1160
+ 该命令导入指定路径的索引文件到当前软件。
1161
+
1162
+ 使用例子:
1163
+ index_import(path="导入路径,通常最后是.json文件")
1164
+
1165
+ </usage>
1166
+ </command>
1167
+
1168
+ <command>
1169
+ <name>exclude_files</name>
1170
+ <description>排除指定文件。</description>
1171
+ <usage>
1172
+ 该命令接受一个参数 file_patterns, 为要排除的文件模式字符串列表。
1173
+
1174
+ 使用例子,比如你想要排除 package-lock.json 文件,你可以这样调用:
1175
+
1176
+ exclude_files(file_patterns=["regex://.*/package-lock\.json"])
1177
+
1178
+ 注意:
1179
+ - 文件模式字符串必须以 regex:// 开头
1180
+ - regex:// 后面部分是标准的正则表达式
1181
+ </usage>
1182
+ </command>
1183
+ </commands>
1184
+
1185
+
1115
1186
  '''
1116
1187
 
1117
1188
  def execute_auto_command(self, command: str, parameters: Dict[str, Any]) -> None:
@@ -1135,6 +1206,11 @@ class CommandAutoTuner:
1135
1206
  "models": self.command_config.models,
1136
1207
  "execute_shell_command": self.command_config.execute_shell_command,
1137
1208
  "generate_shell_command": self.command_config.generate_shell_command,
1209
+ "conf_export": self.command_config.conf_export,
1210
+ "conf_import": self.command_config.conf_import,
1211
+ "index_export": self.command_config.index_export,
1212
+ "index_import": self.command_config.index_import,
1213
+ "exclude_files": self.command_config.exclude_files,
1138
1214
 
1139
1215
  "run_python": self.tools.run_python_code,
1140
1216
  "get_related_files_by_symbols": self.tools.get_related_files_by_symbols,
@@ -1146,6 +1222,7 @@ class CommandAutoTuner:
1146
1222
  "get_project_related_files": self.tools.get_project_related_files,
1147
1223
  "ask_user":self.tools.ask_user,
1148
1224
  "read_file_with_keyword_ranges": self.tools.read_file_with_keyword_ranges,
1225
+
1149
1226
 
1150
1227
 
1151
1228
  }
@@ -27,6 +27,7 @@ from autocoder.utils.queue_communicate import (
27
27
  )
28
28
  import sys
29
29
  import io
30
+ from autocoder.common import files as files_utils
30
31
 
31
32
  @byzerllm.prompt()
32
33
  def detect_rm_command(command: str) -> Bool:
@@ -286,34 +287,33 @@ class AutoCommandTools:
286
287
 
287
288
  result = []
288
289
  try:
289
- with open(absolute_path, 'r', encoding='utf-8') as f:
290
- lines = f.readlines()
290
+
291
+ lines = files_utils.read_lines(absolute_path)
292
+ # Find all lines containing the keyword
293
+ keyword_lines = []
294
+ for i, line in enumerate(lines):
295
+ if keyword.lower() in line.lower():
296
+ keyword_lines.append(i)
297
+
298
+ # Process each keyword line and its surrounding range
299
+ processed_ranges = set()
300
+ for line_num in keyword_lines:
301
+ # Calculate range boundaries
302
+ start = max(0, line_num - before_size)
303
+ end = min(len(lines), line_num + after_size + 1)
291
304
 
292
- # Find all lines containing the keyword
293
- keyword_lines = []
294
- for i, line in enumerate(lines):
295
- if keyword.lower() in line.lower():
296
- keyword_lines.append(i)
305
+ # Check if this range overlaps with any previously processed range
306
+ range_key = (start, end)
307
+ if range_key in processed_ranges:
308
+ continue
297
309
 
298
- # Process each keyword line and its surrounding range
299
- processed_ranges = set()
300
- for line_num in keyword_lines:
301
- # Calculate range boundaries
302
- start = max(0, line_num - before_size)
303
- end = min(len(lines), line_num + after_size + 1)
304
-
305
- # Check if this range overlaps with any previously processed range
306
- range_key = (start, end)
307
- if range_key in processed_ranges:
308
- continue
309
-
310
- processed_ranges.add(range_key)
311
-
312
- # Format the content block
313
- content = f"##File: {absolute_path}\n"
314
- content += f"##Line: {start+1}-{end}\n\n"
315
- content += "".join(lines[start:end])
316
- result.append(content)
310
+ processed_ranges.add(range_key)
311
+
312
+ # Format the content block
313
+ content = f"##File: {absolute_path}\n"
314
+ content += f"##Line: {start+1}-{end}\n\n"
315
+ content += "".join(lines[start:end])
316
+ result.append(content)
317
317
 
318
318
  except Exception as e:
319
319
  v = f"Error reading file {absolute_path}: {str(e)}"
@@ -403,23 +403,22 @@ class AutoCommandTools:
403
403
  if path in os.path.join(root, file):
404
404
  absolute_path = os.path.join(root, file)
405
405
  break
406
-
407
- with open(absolute_path, "r", encoding="utf-8") as f:
408
- if path in file_line_ranges:
409
- # Read specific line ranges
410
- lines = f.readlines()
411
- filtered_lines = []
412
- for start, end in file_line_ranges[path]:
413
- # Adjust for 0-based indexing
414
- start = max(0, start - 1)
415
- end = min(len(lines), end)
416
- content = "".join(lines[start:end])
417
- filtered_lines.extend(f"##File: {absolute_path}\n##Line: {start}-{end}\n\n{content}")
418
- source_code = "".join(filtered_lines)
419
- else:
420
- # Read entire file if no range specified
421
- content = f.read()
422
- source_code = f"##File: {absolute_path}\n\n{content}"
406
+
407
+ if path in file_line_ranges:
408
+ # Read specific line ranges
409
+ lines = files_utils.read_lines(absolute_path)
410
+ filtered_lines = []
411
+ for start, end in file_line_ranges[path]:
412
+ # Adjust for 0-based indexing
413
+ start = max(0, start - 1)
414
+ end = min(len(lines), end)
415
+ content = "".join(lines[start:end])
416
+ filtered_lines.extend(f"##File: {absolute_path}\n##Line: {start}-{end}\n\n{content}")
417
+ source_code = "".join(filtered_lines)
418
+ else:
419
+ # Read entire file if no range specified
420
+ content = files_utils.read_file(absolute_path)
421
+ source_code = f"##File: {absolute_path}\n\n{content}"
423
422
 
424
423
  sc = SourceCode(module_name=absolute_path, source_code=source_code)
425
424
  source_code_str += f"{sc.source_code}\n\n"
@@ -510,13 +509,12 @@ class AutoCommandTools:
510
509
  for file in files:
511
510
  file_path = os.path.join(root, file)
512
511
  try:
513
- with open(file_path, "r", encoding="utf-8") as f:
514
- content = f.read()
515
- if keyword.lower() in content.lower():
516
- matched_files.append(file_path)
517
- # Limit to first 10 matches
518
- if len(matched_files) >= 10:
519
- break
512
+ content = files_utils.read_file(file_path)
513
+ if keyword.lower() in content.lower():
514
+ matched_files.append(file_path)
515
+ # Limit to first 10 matches
516
+ if len(matched_files) >= 10:
517
+ break
520
518
  except Exception:
521
519
  # Skip files that can't be read
522
520
  pass
@@ -6,7 +6,6 @@ import os
6
6
  import time
7
7
  from typing import List, Dict, Any, Optional, Union
8
8
 
9
-
10
9
  class SourceCode(pydantic.BaseModel):
11
10
  module_name: str
12
11
  source_code: str
@@ -359,6 +358,9 @@ class AutoCoderArgs(pydantic.BaseModel):
359
358
  data_cells_max_num: Optional[int] = 2000
360
359
  generate_times_same_model: Optional[int] = 1
361
360
  rank_times_same_model: Optional[int] = 1
361
+
362
+ # block:给定每个文件修改的代码块 file: 给定每个文件修改前后内容
363
+ rank_strategy: Optional[str] = "file"
362
364
 
363
365
  action: List[str] = []
364
366
  enable_global_memory: Optional[bool] = True
@@ -374,6 +376,9 @@ class AutoCoderArgs(pydantic.BaseModel):
374
376
  conversation_prune_group_size: Optional[int] = 4
375
377
  conversation_prune_strategy: Optional[str] = "summarize"
376
378
 
379
+ context_prune_strategy: Optional[str] = "score"
380
+ context_prune: Optional[bool] = True
381
+
377
382
  auto_command_max_iterations: Optional[int] = 10
378
383
 
379
384
  skip_commit: Optional[bool] = False
@@ -3,6 +3,7 @@ from byzerllm.utils import format_str_jinja2
3
3
 
4
4
  MESSAGES = {
5
5
  "en": {
6
+ "invalid_file_pattern": "Invalid file pattern: {{file_pattern}}. e.g. regex://.*/package-lock\\.json",
6
7
  "config_validation_error": "Config validation error: {{error}}",
7
8
  "invalid_boolean_value": "Value '{{value}}' is not a valid boolean(true/false)",
8
9
  "invalid_integer_value": "Value '{{value}}' is not a valid integer",
@@ -137,9 +138,42 @@ MESSAGES = {
137
138
  "invalid_enum_value": "Value '{{value}}' is not in allowed values ({{allowed}})",
138
139
  "no_changes_made": "⚠️ no changes made, the reason may be that the text block generated by the coding function has a problem, so it cannot be merged into the project",
139
140
  "conversation_pruning_start": "⚠️ Conversation pruning started, total tokens: {{total_tokens}}, safe zone: {{safe_zone}}",
140
- "invalid_file_number": "⚠️ Invalid file number {{file_number}}, total files: {{total_files}}"
141
- },
141
+ "invalid_file_number": "⚠️ Invalid file number {{file_number}}, total files: {{total_files}}",
142
+ "all_merge_results_failed": "⚠️ All merge attempts failed, returning first candidate",
143
+ "only_one_merge_result_success": "✅ Only one merge result succeeded, returning that candidate",
144
+ "conf_import_success": "Successfully imported configuration: {{path}}",
145
+ "conf_export_success": "Successfully exported configuration: {{path}}",
146
+ "conf_import_error": "Error importing configuration: {{error}}",
147
+ "conf_export_error": "Error exporting configuration: {{error}}",
148
+ "conf_import_invalid_format": "Invalid import configuration format, expected 'key:value'",
149
+ "conf_export_invalid_format": "Invalid export configuration format, expected 'key:value'",
150
+ "conf_import_file_not_found": "Import configuration file not found: {{file_path}}",
151
+ "conf_export_file_not_found": "Export configuration file not found: {{file_path}}",
152
+ "conf_import_file_empty": "Import configuration file is empty: {{file_path}}",
153
+ "conf_export_file_empty": "Export configuration file is empty: {{file_path}}",
154
+ "generated_shell_script": "Generated Shell Script",
155
+ "confirm_execute_shell_script": "Do you want to execute this shell script?",
156
+ "shell_script_not_executed": "Shell script was not executed",
157
+ "conf_not_found": "Configuration file not found: {{path}}",
158
+ "index_export_success": "Index exported successfully: {{path}}",
159
+ "index_import_success": "Index imported successfully: {{path}}",
160
+ },
142
161
  "zh": {
162
+ "invalid_file_pattern": "无效的文件模式: {{file_pattern}}. 例如: regex://.*/package-lock\\.json",
163
+ "conf_not_found": "未找到配置文件: {{path}}",
164
+ "conf_import_success": "成功导入配置: {{path}}",
165
+ "conf_export_success": "成功导出配置: {{path}}",
166
+ "conf_import_error": "导入配置出错: {{error}}",
167
+ "conf_export_error": "导出配置出错: {{error}}",
168
+ "conf_import_invalid_format": "导入配置格式无效, 应为 'key:value' 格式",
169
+ "conf_export_invalid_format": "导出配置格式无效, 应为 'key:value' 格式",
170
+ "conf_import_file_not_found": "未找到导入配置文件: {{file_path}}",
171
+ "conf_export_file_not_found": "未找到导出配置文件: {{file_path}}",
172
+ "conf_import_file_empty": "导入配置文件为空: {{file_path}}",
173
+ "conf_export_file_empty": "导出配置文件为空: {{file_path}}",
174
+ "generated_shell_script": "生成的 Shell 脚本",
175
+ "confirm_execute_shell_script": "您要执行此 shell 脚本吗?",
176
+ "shell_script_not_executed": "Shell 脚本未执行",
143
177
  "config_validation_error": "配置验证错误: {{error}}",
144
178
  "invalid_boolean_value": "值 '{{value}}' 不是有效的布尔值(true/false)",
145
179
  "invalid_integer_value": "值 '{{value}}' 不是有效的整数",
@@ -272,7 +306,11 @@ MESSAGES = {
272
306
  "auto_command_analyzed": "被选择指令",
273
307
  "invalid_enum_value": "值 '{{value}}' 不在允许的值列表中 ({{allowed}})",
274
308
  "conversation_pruning_start": "⚠️ 对话长度 {{total_tokens}} tokens 超过安全阈值 {{safe_zone}},开始修剪对话。",
275
- "invalid_file_number": "⚠️ 无效的文件编号 {{file_number}},总文件数为 {{total_files}}"
309
+ "invalid_file_number": "⚠️ 无效的文件编号 {{file_number}},总文件数为 {{total_files}}",
310
+ "all_merge_results_failed": "⚠️ 所有合并尝试都失败,返回第一个候选",
311
+ "only_one_merge_result_success": "✅ 只有一个合并结果成功,返回该候选",
312
+ "index_export_success": "索引导出成功: {{path}}",
313
+ "index_import_success": "索引导入成功: {{path}}",
276
314
  }}
277
315
 
278
316
 
@@ -197,7 +197,7 @@ class CodeAutoGenerate:
197
197
  instruction=query, content=source_content
198
198
  )
199
199
 
200
- with open(self.args.target_file, "w") as file:
200
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
201
201
  file.write(init_prompt)
202
202
 
203
203
  conversations = []
@@ -298,7 +298,7 @@ class CodeAutoGenerate:
298
298
 
299
299
  conversations = [{"role": "user", "content": init_prompt}]
300
300
 
301
- with open(self.args.target_file, "w") as file:
301
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
302
302
  file.write(init_prompt)
303
303
 
304
304
  t = self.llm.chat_oai(conversations=conversations, llm_config=llm_config)
@@ -320,7 +320,7 @@ class CodeAutoGenerate:
320
320
 
321
321
  conversations.append({"role": "user", "content": "继续"})
322
322
 
323
- with open(self.args.target_file, "w") as file:
323
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
324
324
  file.write("继续")
325
325
 
326
326
  t = self.llm.chat_oai(conversations=conversations, llm_config=llm_config)
@@ -315,10 +315,7 @@ class CodeAutoGenerateDiff:
315
315
  elif self.args.template == "auto_implement":
316
316
  init_prompt = self.auto_implement_function.prompt(
317
317
  instruction=query, content=source_content
318
- )
319
-
320
- with open(self.args.target_file, "w") as file:
321
- file.write(init_prompt)
318
+ )
322
319
 
323
320
  conversations = []
324
321
 
@@ -359,16 +356,16 @@ class CodeAutoGenerateDiff:
359
356
  with ThreadPoolExecutor(max_workers=len(self.llms) * self.generate_times_same_model) as executor:
360
357
  futures = []
361
358
  for llm in self.llms:
359
+
360
+ model_names_list = llm_utils.get_llm_names(llm)
361
+ model_name = None
362
+ if model_names_list:
363
+ model_name = model_names_list[0]
364
+
362
365
  for _ in range(self.generate_times_same_model):
363
- model_names_list = llm_utils.get_llm_names(llm)
364
- model_name = None
365
- if model_names_list:
366
- model_name = model_names_list[0]
367
-
368
- for _ in range(self.generate_times_same_model):
369
- model_names.append(model_name)
370
- futures.append(executor.submit(
371
- chat_with_continue, llm=llm, conversations=conversations, llm_config=llm_config))
366
+ model_names.append(model_name)
367
+ futures.append(executor.submit(
368
+ chat_with_continue, llm=llm, conversations=conversations, llm_config=llm_config))
372
369
 
373
370
  temp_results = [future.result() for future in futures]
374
371
  for result in temp_results:
@@ -447,7 +444,7 @@ class CodeAutoGenerateDiff:
447
444
  # conversations.append({"role": "system", "content": sys_prompt.prompt()})
448
445
  conversations.append({"role": "user", "content": init_prompt})
449
446
 
450
- with open(self.args.target_file, "w") as file:
447
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
451
448
  file.write(init_prompt)
452
449
 
453
450
  code_llm = self.llms[0]
@@ -467,7 +464,7 @@ class CodeAutoGenerateDiff:
467
464
 
468
465
  conversations.append({"role": "user", "content": "继续"})
469
466
 
470
- with open(self.args.target_file, "w") as file:
467
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
471
468
  file.write("继续")
472
469
 
473
470
  t = code_llm.chat_oai(
@@ -418,7 +418,7 @@ class CodeAutoGenerateEditBlock:
418
418
  instruction=query, content=source_content
419
419
  )
420
420
 
421
- with open(self.args.target_file, "w") as file:
421
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
422
422
  file.write(init_prompt)
423
423
 
424
424
  conversations = []
@@ -538,7 +538,7 @@ class CodeAutoGenerateEditBlock:
538
538
  # conversations.append({"role": "system", "content": sys_prompt.prompt()})
539
539
  conversations.append({"role": "user", "content": init_prompt})
540
540
 
541
- with open(self.args.target_file, "w") as file:
541
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
542
542
  file.write(init_prompt)
543
543
 
544
544
  code_llm = self.llms[0]
@@ -558,7 +558,7 @@ class CodeAutoGenerateEditBlock:
558
558
 
559
559
  conversations.append({"role": "user", "content": "继续"})
560
560
 
561
- with open(self.args.target_file, "w") as file:
561
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
562
562
  file.write("继续")
563
563
 
564
564
  t = code_llm.chat_oai(
@@ -287,7 +287,7 @@ class CodeAutoGenerateStrictDiff:
287
287
  instruction=query, content=source_content
288
288
  )
289
289
 
290
- with open(self.args.target_file, "w") as file:
290
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
291
291
  file.write(init_prompt)
292
292
 
293
293
  conversations = []
@@ -417,7 +417,7 @@ class CodeAutoGenerateStrictDiff:
417
417
  # conversations.append({"role": "system", "content": sys_prompt.prompt()})
418
418
  conversations.append({"role": "user", "content": init_prompt})
419
419
 
420
- with open(self.args.target_file, "w") as file:
420
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
421
421
  file.write(init_prompt)
422
422
 
423
423
  code_llm = self.llms[0]
@@ -437,7 +437,7 @@ class CodeAutoGenerateStrictDiff:
437
437
 
438
438
  conversations.append({"role": "user", "content": "继续"})
439
439
 
440
- with open(self.args.target_file, "w") as file:
440
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
441
441
  file.write("继续")
442
442
 
443
443
  t = code_llm.chat_oai(
@@ -73,15 +73,35 @@ class CodeAutoMerge:
73
73
  def choose_best_choice(self, generate_result: CodeGenerateResult) -> CodeGenerateResult:
74
74
  if len(generate_result.contents) == 1:
75
75
  return generate_result
76
+
77
+ merge_results = []
78
+ for content,conversations in zip(generate_result.contents,generate_result.conversations):
79
+ merge_result = self._merge_code_without_effect(content)
80
+ merge_results.append(merge_result)
76
81
 
82
+ # If all merge results are None, return first one
83
+ if all(len(result.failed_blocks) != 0 for result in merge_results):
84
+ self.printer.print_in_terminal("all_merge_results_failed")
85
+ return CodeGenerateResult(contents=[generate_result.contents[0]], conversations=[generate_result.conversations[0]])
86
+
87
+ # If only one merge result is not None, return that one
88
+ not_none_indices = [i for i, result in enumerate(merge_results) if len(result.failed_blocks) == 0]
89
+ if len(not_none_indices) == 1:
90
+ idx = not_none_indices[0]
91
+ self.printer.print_in_terminal("only_one_merge_result_success")
92
+ return CodeGenerateResult(contents=[generate_result.contents[idx]], conversations=[generate_result.conversations[idx]])
93
+
94
+ # 最后,如果有多个,那么根据质量排序再返回
77
95
  ranker = CodeModificationRanker(self.llm, self.args)
78
- ranked_result = ranker.rank_modifications(generate_result)
79
- # Filter out contents with failed blocks
96
+ ranked_result = ranker.rank_modifications(generate_result,merge_results)
97
+
98
+ ## 得到的结果,再做一次合并,第一个通过的返回 , 返回做合并有点重复低效,未来修改。
80
99
  for content,conversations in zip(ranked_result.contents,ranked_result.conversations):
81
100
  merge_result = self._merge_code_without_effect(content)
82
101
  if not merge_result.failed_blocks:
83
102
  return CodeGenerateResult(contents=[content], conversations=[conversations])
84
- # If all have failed blocks, return the first one
103
+
104
+ # 最后保底,但实际不会出现
85
105
  return CodeGenerateResult(contents=[ranked_result.contents[0]], conversations=[ranked_result.conversations[0]])
86
106
 
87
107
 
@@ -387,15 +387,35 @@ class CodeAutoMergeDiff:
387
387
  def choose_best_choice(self, generate_result: CodeGenerateResult) -> CodeGenerateResult:
388
388
  if len(generate_result.contents) == 1:
389
389
  return generate_result
390
+
391
+ merge_results = []
392
+ for content,conversations in zip(generate_result.contents,generate_result.conversations):
393
+ merge_result = self._merge_code_without_effect(content)
394
+ merge_results.append(merge_result)
390
395
 
396
+ # If all merge results are None, return first one
397
+ if all(len(result.failed_blocks) != 0 for result in merge_results):
398
+ self.printer.print_in_terminal("all_merge_results_failed")
399
+ return CodeGenerateResult(contents=[generate_result.contents[0]], conversations=[generate_result.conversations[0]])
400
+
401
+ # If only one merge result is not None, return that one
402
+ not_none_indices = [i for i, result in enumerate(merge_results) if len(result.failed_blocks) == 0]
403
+ if len(not_none_indices) == 1:
404
+ idx = not_none_indices[0]
405
+ self.printer.print_in_terminal("only_one_merge_result_success")
406
+ return CodeGenerateResult(contents=[generate_result.contents[idx]], conversations=[generate_result.conversations[idx]])
407
+
408
+ # 最后,如果有多个,那么根据质量排序再返回
391
409
  ranker = CodeModificationRanker(self.llm, self.args)
392
- ranked_result = ranker.rank_modifications(generate_result)
393
- # Filter out contents with failed blocks
410
+ ranked_result = ranker.rank_modifications(generate_result,merge_results)
411
+
412
+ ## 得到的结果,再做一次合并,第一个通过的返回 , 返回做合并有点重复低效,未来修改。
394
413
  for content,conversations in zip(ranked_result.contents,ranked_result.conversations):
395
414
  merge_result = self._merge_code_without_effect(content)
396
415
  if not merge_result.failed_blocks:
397
416
  return CodeGenerateResult(contents=[content], conversations=[conversations])
398
- # If all have failed blocks, return the first one
417
+
418
+ # 最后保底,但实际不会出现
399
419
  return CodeGenerateResult(contents=[ranked_result.contents[0]], conversations=[ranked_result.conversations[0]])
400
420
 
401
421
  @byzerllm.prompt(render="jinja2")
@@ -440,6 +460,11 @@ class CodeAutoMergeDiff:
440
460
  errors = []
441
461
  for path, hunk in uniq:
442
462
  full_path = self.abs_root_path(path)
463
+
464
+ if not os.path.exists(full_path):
465
+ with open(full_path, "w") as f:
466
+ f.write("")
467
+
443
468
  content = FileUtils.read_file(full_path)
444
469
 
445
470
  original, _ = hunk_to_before_after(hunk)
@@ -506,7 +531,7 @@ class CodeAutoMergeDiff:
506
531
  def _merge_code(self, content: str,force_skip_git:bool=False):
507
532
  total = 0
508
533
 
509
- file_content = open(self.args.file).read()
534
+ file_content = FileUtils.read_file(self.args.file)
510
535
  md5 = hashlib.md5(file_content.encode('utf-8')).hexdigest()
511
536
  # get the file name
512
537
  file_name = os.path.basename(self.args.file)
@@ -164,15 +164,35 @@ class CodeAutoMergeEditBlock:
164
164
  def choose_best_choice(self, generate_result: CodeGenerateResult) -> CodeGenerateResult:
165
165
  if len(generate_result.contents) == 1:
166
166
  return generate_result
167
-
167
+
168
+ merge_results = []
169
+ for content,conversations in zip(generate_result.contents,generate_result.conversations):
170
+ merge_result = self._merge_code_without_effect(content)
171
+ merge_results.append(merge_result)
172
+
173
+ # If all merge results are None, return first one
174
+ if all(len(result.failed_blocks) != 0 for result in merge_results):
175
+ self.printer.print_in_terminal("all_merge_results_failed")
176
+ return CodeGenerateResult(contents=[generate_result.contents[0]], conversations=[generate_result.conversations[0]])
177
+
178
+ # If only one merge result is not None, return that one
179
+ not_none_indices = [i for i, result in enumerate(merge_results) if len(result.failed_blocks) == 0]
180
+ if len(not_none_indices) == 1:
181
+ idx = not_none_indices[0]
182
+ self.printer.print_in_terminal("only_one_merge_result_success")
183
+ return CodeGenerateResult(contents=[generate_result.contents[idx]], conversations=[generate_result.conversations[idx]])
184
+
185
+ # 最后,如果有多个,那么根据质量排序再返回
168
186
  ranker = CodeModificationRanker(self.llm, self.args)
169
- ranked_result = ranker.rank_modifications(generate_result)
170
- # Filter out contents with failed blocks
187
+ ranked_result = ranker.rank_modifications(generate_result,merge_results)
188
+
189
+ ## 得到的结果,再做一次合并,第一个通过的返回 , 返回做合并有点重复低效,未来修改。
171
190
  for content,conversations in zip(ranked_result.contents,ranked_result.conversations):
172
191
  merge_result = self._merge_code_without_effect(content)
173
192
  if not merge_result.failed_blocks:
174
193
  return CodeGenerateResult(contents=[content], conversations=[conversations])
175
- # If all have failed blocks, return the first one
194
+
195
+ # 最后保底,但实际不会出现
176
196
  return CodeGenerateResult(contents=[ranked_result.contents[0]], conversations=[ranked_result.conversations[0]])
177
197
 
178
198
  @byzerllm.prompt()
@@ -269,7 +289,7 @@ class CodeAutoMergeEditBlock:
269
289
  )
270
290
 
271
291
  def _merge_code(self, content: str, force_skip_git: bool = False):
272
- file_content = open(self.args.file).read()
292
+ file_content = FileUtils.read_file(self.args.file)
273
293
  md5 = hashlib.md5(file_content.encode("utf-8")).hexdigest()
274
294
  file_name = os.path.basename(self.args.file)
275
295