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
@@ -20,6 +20,8 @@ from byzerllm import MetaHolder
20
20
 
21
21
  from autocoder.utils.llms import get_llm_names, get_model_info
22
22
  from loguru import logger
23
+ from byzerllm.utils.client.code_utils import extract_code
24
+ import json
23
25
 
24
26
 
25
27
  def get_file_path(file_path):
@@ -32,6 +34,15 @@ class QuickFilterResult(BaseModel):
32
34
  files: Dict[str, TargetFile]
33
35
  has_error: bool
34
36
  error_message: Optional[str] = None
37
+ file_positions: Optional[Dict[str, int]] = {}
38
+
39
+ def get_sorted_file_positions(self) -> List[str]:
40
+ """
41
+ 返回按 value 排序的文件列表
42
+ """
43
+ if not self.file_positions:
44
+ return []
45
+ return [file_path for file_path, _ in sorted(self.file_positions.items(), key=lambda x: x[1])]
35
46
 
36
47
 
37
48
  class QuickFilter():
@@ -82,6 +93,7 @@ class QuickFilter():
82
93
  self.index_manager.index_filter_llm)
83
94
  model_name = ",".join(model_names)
84
95
  files: Dict[str, TargetFile] = {}
96
+ file_positions: Dict[str, int] = {}
85
97
 
86
98
  # 获取模型价格信息
87
99
  model_info_map = {}
@@ -166,7 +178,7 @@ class QuickFilter():
166
178
  )
167
179
 
168
180
  if file_number_list:
169
- for file_number in file_number_list.file_list:
181
+ for index,file_number in enumerate(file_number_list.file_list):
170
182
  if file_number < 0 or file_number >= len(chunk):
171
183
  self.printer.print_in_terminal(
172
184
  "invalid_file_number",
@@ -182,9 +194,11 @@ class QuickFilter():
182
194
  reason=self.printer.get_message_from_key(
183
195
  "quick_filter_reason")
184
196
  )
197
+ file_positions[file_path] = index
185
198
  return QuickFilterResult(
186
199
  files=files,
187
- has_error=False
200
+ has_error=False,
201
+ file_positions=file_positions
188
202
  )
189
203
 
190
204
  except Exception as e:
@@ -212,6 +226,7 @@ class QuickFilter():
212
226
 
213
227
  # 合并所有结果
214
228
  final_files: Dict[str, TargetFile] = {}
229
+ final_file_positions: Dict[str, int] = {}
215
230
  has_error = False
216
231
  error_messages: List[str] = []
217
232
 
@@ -222,16 +237,57 @@ class QuickFilter():
222
237
  error_messages.append(result.error_message)
223
238
  final_files.update(result.files)
224
239
 
240
+
241
+ for result in results:
242
+ if result.has_error:
243
+ has_error = True
244
+ if result.error_message:
245
+ error_messages.append(result.error_message)
246
+ ## 实现多个 result.file_positions 交织排序
247
+ # 比如第一个是 {file_path_1_0: 0, file_path_1_1: 1, file_path_1_2: 2}
248
+ # 第二个是 {file_path_2_0: 0, file_path_2_1: 1}
249
+ # 第三个是 {file_path_3_0: 0, file_path_3_1: 1, file_path_3_2: 2, file_path_3_3: 3}
250
+ # 收集逻辑为所以 0 的为一组,然后序号为 0,1,2, 所有1 的为一组,序号是 3,4,5,依次往下推
251
+ # {file_path_1_0: 0, file_path_2_0: 1, file_path_3_0: 2, file_path_1_1: 3, file_path_2_1: 4, file_path_3_1: 5}
252
+ #
253
+ # 获取所有结果的最大 position 值
254
+ max_position = max([max(pos.values()) for pos in [result.file_positions for result in results if result.file_positions]] + [0])
255
+
256
+ # 创建一个映射表,用于记录每个 position 对应的文件路径
257
+ position_map = {}
258
+ for result in results:
259
+ if result.file_positions:
260
+ for file_path, position in result.file_positions.items():
261
+ if position not in position_map:
262
+ position_map[position] = []
263
+ position_map[position].append(file_path)
264
+
265
+ # 重新排序文件路径
266
+ new_file_positions = {}
267
+ current_index = 0
268
+ for position in range(max_position + 1):
269
+ if position in position_map:
270
+ for file_path in position_map[position]:
271
+ new_file_positions[file_path] = current_index
272
+ current_index += 1
273
+
274
+ # 更新 final_file_positions
275
+ final_file_positions.update(new_file_positions)
276
+
225
277
  return QuickFilterResult(
226
278
  files=final_files,
227
279
  has_error=has_error,
228
280
  error_message="\n".join(error_messages) if error_messages else None
229
281
  )
282
+
230
283
 
231
284
  @byzerllm.prompt()
232
285
  def quick_filter_files(self, file_meta_list: List[IndexItem], query: str) -> str:
233
286
  '''
234
- 当用户提一个需求的时候,我们需要找到相关的文件,然后阅读这些文件,并且修改其中部分文件。
287
+ 当用户提一个需求的时候,我们要找到两种类型的源码文件:
288
+ 1. 根据需求需要被修改的文件,我们叫 edited_files
289
+ 2. 为了能够完成修改这些文件,还需要的一些额外参考文件, 我们叫 reference_files
290
+
235
291
  现在,给定下面的索引文件:
236
292
 
237
293
  <index>
@@ -258,12 +314,13 @@ class QuickFilter():
258
314
  }
259
315
  ```
260
316
 
261
- 特别注意
262
- 1. 如果用户的query @文件 或者 @@符号,那么被@的文件或者@@的符号必须要返回,并且尝试通过索引文件诸如导入语句等信息找到这些文件依赖的其他文件,再分析这些文件是否需要提供才能满足后续编码。
263
- 2. 如果 query 里是一段历史对话,那么对话里的内容提及的文件路径必须要返回。
264
- 3. 想想,如果是你需要修改代码,然后满足这个需求,根据索引文件,你希望查看哪些文件,修改哪些文件,然后返回这些文件。
265
- 4. 如果用户需求为空,则直接返回空列表即可。
266
- 5. 返回的 json格式数据不允许有注释
317
+ 特别注意:
318
+ 1. 如果用户的query里有 @文件 或者 @@符号,那么被@的文件或者@@的符号必须要返回。
319
+ 2. 根据需求以及根据 @文件 或者 @@符号 找到的文件,猜测需要被修改的edited_files文件,然后尝试通过索引文件诸如导入语句等信息找到这些文件依赖的其他文件得到 reference_files。
320
+ 3. file_list 里的文件序号,按被 @ 或者 @@ 文件,edited_files文件,reference_files文件的顺序排列。注意,reference_files 你要根据需求来猜测是否需要,过滤掉不相关的,避免返回文件数过多。
321
+ 4. 如果 query 里是一段历史对话,那么对话里的内容提及的文件路径必须要返回。
322
+ 5. 如果用户需求为空,则直接返回空列表即可。
323
+ 6. 返回的 json格式数据不允许有注释
267
324
  '''
268
325
 
269
326
  file_meta_str = "\n".join(
@@ -273,9 +330,58 @@ class QuickFilter():
273
330
  "query": query
274
331
  }
275
332
  return context
333
+
334
+
335
+ def _extract_code_snippets_from_overflow_files(self, validated_file_numbers: List[int],index_items: List[IndexItem], conversations: List[Dict[str, str]]):
336
+ token_count = 0
337
+ selected_files = []
338
+ selected_file_contents = []
339
+ full_file_tokens = int(self.max_tokens * 0.8)
340
+ for file_number in validated_file_numbers:
341
+ file_path = get_file_path(index_items[file_number].module_name)
342
+ with open(file_path, "r", encoding="utf-8") as f:
343
+ content = f.read()
344
+ tokens = count_tokens(content)
345
+ if token_count + tokens <= full_file_tokens:
346
+ selected_files.append(file_number)
347
+ selected_file_contents.append(content)
348
+ token_count += tokens
349
+ else:
350
+ # 对超出部分抽取代码片段
351
+ try:
352
+ extracted_info = (
353
+ self.extract_code_snippets_from_files.options(
354
+ {"llm_config": {"max_length": 100}}
355
+ )
356
+ .with_llm(self.index_manager.index_filter_llm)
357
+ .run(conversations, [content])
358
+ )
359
+ json_str = extract_code(extracted_info)[0][1]
360
+ json_objs = json.loads(json_str)
361
+
362
+ new_content = ""
363
+
364
+ if json_objs:
365
+ for json_obj in json_objs:
366
+ start_line = json_obj["start_line"] - 1
367
+ end_line = json_obj["end_line"]
368
+ chunk = "\n".join(content.split("\n")[start_line:end_line])
369
+ new_content += chunk + "\n"
370
+
371
+ token_count += count_tokens(new_content)
372
+ if token_count >= self.max_tokens:
373
+ break
374
+ else:
375
+ selected_files.append(file_number)
376
+ selected_file_contents.append(new_content)
377
+ except Exception as e:
378
+ logger.error(f"Failed to extract code snippets from {file_path}: {e}")
379
+ return selected_files
380
+
276
381
 
277
382
  def filter(self, index_items: List[IndexItem], query: str) -> QuickFilterResult:
278
383
  final_files: Dict[str, TargetFile] = {}
384
+ final_file_positions: Dict[str, int] = {}
279
385
  start_time = time.monotonic()
280
386
 
281
387
  prompt_str = self.quick_filter_files.prompt(index_items, query)
@@ -385,6 +491,7 @@ class QuickFilter():
385
491
  )
386
492
 
387
493
  if file_number_list:
494
+ validated_file_numbers = []
388
495
  for file_number in file_number_list.file_list:
389
496
  if file_number < 0 or file_number >= len(index_items):
390
497
  self.printer.print_in_terminal(
@@ -394,14 +501,21 @@ class QuickFilter():
394
501
  total_files=len(index_items)
395
502
  )
396
503
  continue
397
- final_files[get_file_path(index_items[file_number].module_name)] = TargetFile(
504
+ validated_file_numbers.append(file_number)
505
+
506
+ # 将最终选中的文件加入final_files
507
+ for index,file_number in enumerate(validated_file_numbers):
508
+ file_path = get_file_path(index_items[file_number].module_name)
509
+ final_files[file_path] = TargetFile(
398
510
  file_path=index_items[file_number].module_name,
399
- reason=self.printer.get_message_from_key(
400
- "quick_filter_reason")
511
+ reason=self.printer.get_message_from_key("quick_filter_reason")
401
512
  )
513
+ final_file_positions[file_path] = index
514
+
402
515
  end_time = time.monotonic()
403
516
  self.stats["timings"]["quick_filter"] = end_time - start_time
404
517
  return QuickFilterResult(
405
518
  files=final_files,
406
- has_error=False
519
+ has_error=False,
520
+ file_positions=final_file_positions
407
521
  )
autocoder/index/index.py CHANGED
@@ -26,6 +26,7 @@ from autocoder.index.types import (
26
26
  )
27
27
  from autocoder.common.global_cancel import global_cancel
28
28
  from autocoder.utils.llms import get_llm_names
29
+ from autocoder.rag.token_counter import count_tokens
29
30
  class IndexManager:
30
31
  def __init__(
31
32
  self, llm: byzerllm.ByzerLLM, sources: List[SourceCode], args: AutoCoderArgs
@@ -257,13 +258,13 @@ class IndexManager:
257
258
  total_input_cost = 0.0
258
259
  total_output_cost = 0.0
259
260
 
260
- if len(source.source_code) > self.max_input_length:
261
+ if count_tokens(source.source_code) > self.args.conversation_prune_safe_zone_tokens:
261
262
  self.printer.print_in_terminal(
262
263
  "index_file_too_large",
263
264
  style="yellow",
264
265
  file_path=source.module_name,
265
266
  file_size=len(source.source_code),
266
- max_length=self.max_input_length
267
+ max_length=self.args.conversation_prune_safe_zone_tokens
267
268
  )
268
269
  chunks = self.split_text_into_chunks(
269
270
  source_code, self.max_input_length - 1000
@@ -341,7 +342,7 @@ class IndexManager:
341
342
 
342
343
  def build_index(self):
343
344
  if os.path.exists(self.index_file):
344
- with open(self.index_file, "r") as file:
345
+ with open(self.index_file, "r",encoding="utf-8") as file:
345
346
  index_data = json.load(file)
346
347
  else:
347
348
  index_data = {}
@@ -432,13 +433,13 @@ class IndexManager:
432
433
  index_data[module_name] = result
433
434
  updated_sources.append(module_name)
434
435
  if len(updated_sources) > 5:
435
- with open(self.index_file, "w") as file:
436
+ with open(self.index_file, "w",encoding="utf-8") as file:
436
437
  json.dump(index_data, file, ensure_ascii=False, indent=2)
437
438
  updated_sources = []
438
439
 
439
440
  # 如果 updated_sources 或 keys_to_remove 有值,则保存索引文件
440
441
  if updated_sources or keys_to_remove:
441
- with open(self.index_file, "w") as file:
442
+ with open(self.index_file, "w",encoding="utf-8") as file:
442
443
  json.dump(index_data, file, ensure_ascii=False, indent=2)
443
444
 
444
445
  print("")
@@ -460,14 +461,14 @@ class IndexManager:
460
461
  if not os.path.exists(self.index_file):
461
462
  return []
462
463
 
463
- with open(self.index_file, "r") as file:
464
+ with open(self.index_file, "r",encoding="utf-8") as file:
464
465
  return file.read()
465
466
 
466
467
  def read_index(self) -> List[IndexItem]:
467
468
  if not os.path.exists(self.index_file):
468
469
  return []
469
470
 
470
- with open(self.index_file, "r") as file:
471
+ with open(self.index_file, "r",encoding="utf-8") as file:
471
472
  index_data = json.load(file)
472
473
 
473
474
  index_items = []
autocoder/models.py CHANGED
@@ -97,7 +97,7 @@ def load_models() -> List[Dict]:
97
97
  if model.get("api_key_path",""):
98
98
  api_key_file = os.path.join(api_key_dir, model["api_key_path"])
99
99
  if os.path.exists(api_key_file):
100
- with open(api_key_file, "r") as f:
100
+ with open(api_key_file, "r",encoding="utf-8") as f:
101
101
  model["api_key"] = f.read()
102
102
  return target_models
103
103
 
@@ -269,7 +269,7 @@ def update_model_with_api_key(name: str, api_key: str) -> Dict:
269
269
  api_key_dir = os.path.expanduser("~/.auto-coder/keys")
270
270
  os.makedirs(api_key_dir, exist_ok=True)
271
271
  api_key_file = os.path.join(api_key_dir, api_key_path)
272
- with open(api_key_file, "w") as f:
272
+ with open(api_key_file, "w",encoding="utf-8") as f:
273
273
  f.write(api_key.strip())
274
274
 
275
275
  # 如果是新模型,添加到模型列表中
@@ -174,7 +174,8 @@ class PyProject:
174
174
  return False
175
175
 
176
176
  def output(self):
177
- return open(self.target_file, "r").read()
177
+ with open(self.target_file, "r",encoding="utf-8") as file:
178
+ return file.read()
178
179
 
179
180
  def is_python_file(self, file_path):
180
181
  return file_path.endswith(".py")
@@ -182,9 +183,8 @@ class PyProject:
182
183
  def read_file_content(self, file_path):
183
184
  if self.args.auto_merge == "strict_diff":
184
185
  result = []
185
- with open(file_path, "r") as file:
186
- for line_number, line in enumerate(file, start=1):
187
- result.append(f"{line_number}:{line}")
186
+ for line_number, line in FileUtils.read_file_with_line_numbers(file_path,line_number_start=1):
187
+ result.append(f"{line_number}:{line}")
188
188
  return "\n".join(result)
189
189
 
190
190
  return FileUtils.read_file(file_path)
@@ -357,7 +357,7 @@ class PyProject:
357
357
  self.clone_repository()
358
358
 
359
359
  if self.target_file:
360
- with open(self.target_file, "w") as file:
360
+ with open(self.target_file, "w",encoding="utf-8") as file:
361
361
 
362
362
  for code in self.get_rest_source_codes():
363
363
  self.sources.append(code)
@@ -126,7 +126,7 @@ class ByzerStorageCache(BaseCacheManager):
126
126
  """Load cache from file"""
127
127
  if os.path.exists(self.cache_file):
128
128
  try:
129
- with open(self.cache_file, "r") as f:
129
+ with open(self.cache_file, "r",encoding="utf-8") as f:
130
130
  lines = f.readlines()
131
131
  cache = {}
132
132
  for line in lines:
@@ -147,7 +147,7 @@ class ByzerStorageCache(BaseCacheManager):
147
147
 
148
148
  if not fcntl:
149
149
  try:
150
- with open(cache_file, "w") as f:
150
+ with open(cache_file, "w",encoding="utf-8") as f:
151
151
  for data in self.cache.values():
152
152
  json.dump(data, f, ensure_ascii=False)
153
153
  f.write("\n")
@@ -155,12 +155,12 @@ class ByzerStorageCache(BaseCacheManager):
155
155
  logger.error(f"Error writing cache file: {str(e)}")
156
156
  else:
157
157
  lock_file = cache_file + ".lock"
158
- with open(lock_file, "w") as lockf:
158
+ with open(lock_file, "w",encoding="utf-8") as lockf:
159
159
  try:
160
160
  # 获取文件锁
161
161
  fcntl.flock(lockf, fcntl.LOCK_EX | fcntl.LOCK_NB)
162
162
  # 写入缓存文件
163
- with open(cache_file, "w") as f:
163
+ with open(cache_file, "w",encoding="utf-8") as f:
164
164
  for data in self.cache.values():
165
165
  json.dump(data, f, ensure_ascii=False)
166
166
  f.write("\n")
@@ -106,11 +106,11 @@ class AutoCoderRAGDocListener(BaseCacheManager):
106
106
  gitignore_path = os.path.join(self.path, ".gitignore")
107
107
 
108
108
  if os.path.exists(serveignore_path):
109
- with open(serveignore_path, "r") as ignore_file:
109
+ with open(serveignore_path, "r",encoding="utf-8") as ignore_file:
110
110
  patterns = ignore_file.readlines()
111
111
  return [pattern.strip() for pattern in patterns]
112
112
  elif os.path.exists(gitignore_path):
113
- with open(gitignore_path, "r") as ignore_file:
113
+ with open(gitignore_path, "r",encoding="utf-8") as ignore_file:
114
114
  patterns = ignore_file.readlines()
115
115
  return [pattern.strip() for pattern in patterns]
116
116
  return []
@@ -160,7 +160,7 @@ class AutoCoderRAGAsyncUpdateQueue(BaseCacheManager):
160
160
 
161
161
  cache = {}
162
162
  if os.path.exists(cache_file):
163
- with open(cache_file, "r") as f:
163
+ with open(cache_file, "r",encoding="utf-8") as f:
164
164
  for line in f:
165
165
  data = json.loads(line)
166
166
  cache[data["file_path"]] = data
@@ -171,7 +171,7 @@ class AutoCoderRAGAsyncUpdateQueue(BaseCacheManager):
171
171
  cache_file = os.path.join(cache_dir, "cache.jsonl")
172
172
 
173
173
  if not fcntl:
174
- with open(cache_file, "w") as f:
174
+ with open(cache_file, "w",encoding="utf-8") as f:
175
175
  for data in self.cache.values():
176
176
  try:
177
177
  json.dump(data, f, ensure_ascii=False)
@@ -181,12 +181,12 @@ class AutoCoderRAGAsyncUpdateQueue(BaseCacheManager):
181
181
  f"Failed to write {data['file_path']} to .cache/cache.jsonl: {e}")
182
182
  else:
183
183
  lock_file = cache_file + ".lock"
184
- with open(lock_file, "w") as lockf:
184
+ with open(lock_file, "w",encoding="utf-8") as lockf:
185
185
  try:
186
186
  # 获取文件锁
187
187
  fcntl.flock(lockf, fcntl.LOCK_EX | fcntl.LOCK_NB)
188
188
  # 写入缓存文件
189
- with open(cache_file, "w") as f:
189
+ with open(cache_file, "w",encoding="utf-8") as f:
190
190
  for data in self.cache.values():
191
191
  try:
192
192
  json.dump(data, f, ensure_ascii=False)
@@ -245,10 +245,10 @@ class LongContextRAG:
245
245
  gitignore_path = os.path.join(self.path, ".gitignore")
246
246
 
247
247
  if os.path.exists(serveignore_path):
248
- with open(serveignore_path, "r") as ignore_file:
248
+ with open(serveignore_path, "r",encoding="utf-8") as ignore_file:
249
249
  return pathspec.PathSpec.from_lines("gitwildmatch", ignore_file)
250
250
  elif os.path.exists(gitignore_path):
251
- with open(gitignore_path, "r") as ignore_file:
251
+ with open(gitignore_path, "r",encoding="utf-8") as ignore_file:
252
252
  return pathspec.PathSpec.from_lines("gitwildmatch", ignore_file)
253
253
  return None
254
254
 
@@ -78,7 +78,8 @@ class RegexProject:
78
78
  raise ValueError("Invalid project_type format. Expected 'regex//<pattern>'")
79
79
 
80
80
  def output(self):
81
- return open(self.target_file, "r").read()
81
+ with open(self.target_file, "r",encoding="utf-8") as file:
82
+ return file.read()
82
83
 
83
84
  def is_regex_match(self, file_path):
84
85
  return re.search(self.regex_pattern, file_path) is not None
@@ -231,7 +232,7 @@ class RegexProject:
231
232
  self.clone_repository()
232
233
 
233
234
  if self.target_file:
234
- with open(self.target_file, "w") as file:
235
+ with open(self.target_file, "w",encoding="utf-8") as file:
235
236
  for code in self.get_source_codes():
236
237
  self.sources.append(code)
237
238
  file.write(f"##File: {code.module_name}\n")
@@ -114,7 +114,8 @@ class SuffixProject:
114
114
  return False
115
115
 
116
116
  def output(self):
117
- return open(self.target_file, "r").read()
117
+ with open(self.target_file, "r",encoding="utf-8") as file:
118
+ return file.read()
118
119
 
119
120
  def is_suffix_file(self, file_path):
120
121
  return any([file_path.endswith(suffix) for suffix in self.suffixs])
@@ -273,7 +274,7 @@ class SuffixProject:
273
274
  self.clone_repository()
274
275
 
275
276
  if self.target_file:
276
- with open(self.target_file, "w") as file:
277
+ with open(self.target_file, "w",encoding="utf-8") as file:
277
278
  for code in self.get_source_codes():
278
279
  self.sources.append(code)
279
280
  file.write(f"##File: {code.module_name}\n")
@@ -106,7 +106,8 @@ class TSProject:
106
106
  return False
107
107
 
108
108
  def output(self):
109
- return open(self.target_file, "r").read()
109
+ with open(self.target_file, "r",encoding="utf-8") as file:
110
+ return file.read()
110
111
 
111
112
  def read_file_content(self, file_path):
112
113
  return FileUtils.read_file(file_path)
@@ -308,7 +309,7 @@ class TSProject:
308
309
  self.clone_repository()
309
310
 
310
311
  if self.target_file:
311
- with open(self.target_file, "w") as file:
312
+ with open(self.target_file, "w",encoding="utf-8") as file:
312
313
  for code in self.get_rest_source_codes():
313
314
  self.sources.append(code)
314
315
  file.write(f"##File: {code.module_name}\n")
@@ -34,7 +34,7 @@ def load_code_model_conversation_from_store(args: AutoCoderArgs):
34
34
  return []
35
35
 
36
36
  conversations = []
37
- with open(conversation_file, "r") as f:
37
+ with open(conversation_file, "r",encoding="utf-8") as f:
38
38
  for line in f:
39
39
  conversations.append(json.loads(line))
40
40
 
@@ -15,7 +15,7 @@ import hashlib
15
15
  def convert_yaml_to_config(yaml_file: str):
16
16
 
17
17
  args = AutoCoderArgs()
18
- with open(yaml_file, "r") as f:
18
+ with open(yaml_file, "r",encoding="utf-8") as f:
19
19
  config = yaml.safe_load(f)
20
20
  config = load_include_files(config, yaml_file)
21
21
  for key, value in config.items():
@@ -75,7 +75,7 @@ def get_llm_friendly_package_docs(memory,
75
75
  if return_paths:
76
76
  docs.append(file_path)
77
77
  else:
78
- with open(file_path, "r") as f:
78
+ with open(file_path, "r",encoding="utf-8") as f:
79
79
  docs.append(f.read())
80
80
 
81
81
  return docs
@@ -130,7 +130,7 @@ def get_llm(memory, model:Optional[str]=None):
130
130
  # 临时保存yaml文件,然后读取yaml文件,转换为args
131
131
  temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
132
132
  try:
133
- with open(temp_yaml, "w") as f:
133
+ with open(temp_yaml, "w",encoding="utf-8") as f:
134
134
  f.write(convert_yaml_config_to_str(
135
135
  yaml_config=yaml_config))
136
136
  args = convert_yaml_to_config(temp_yaml)