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.
- {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/METADATA +1 -1
- {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/RECORD +58 -55
- autocoder/agent/planner.py +4 -4
- autocoder/auto_coder.py +26 -21
- autocoder/auto_coder_server.py +7 -7
- autocoder/chat_auto_coder.py +203 -98
- autocoder/commands/auto_command.py +81 -4
- autocoder/commands/tools.py +48 -50
- autocoder/common/__init__.py +6 -1
- autocoder/common/auto_coder_lang.py +41 -3
- autocoder/common/code_auto_generate.py +3 -3
- autocoder/common/code_auto_generate_diff.py +12 -15
- autocoder/common/code_auto_generate_editblock.py +3 -3
- autocoder/common/code_auto_generate_strict_diff.py +3 -3
- autocoder/common/code_auto_merge.py +23 -3
- autocoder/common/code_auto_merge_diff.py +29 -4
- autocoder/common/code_auto_merge_editblock.py +25 -5
- autocoder/common/code_auto_merge_strict_diff.py +26 -6
- autocoder/common/code_modification_ranker.py +65 -3
- autocoder/common/command_completer.py +3 -0
- autocoder/common/command_generator.py +24 -8
- autocoder/common/command_templates.py +2 -2
- autocoder/common/conf_import_export.py +105 -0
- autocoder/common/conf_validator.py +7 -1
- autocoder/common/context_pruner.py +305 -0
- autocoder/common/files.py +41 -2
- autocoder/common/image_to_page.py +11 -11
- autocoder/common/index_import_export.py +38 -18
- autocoder/common/mcp_hub.py +3 -3
- autocoder/common/mcp_server.py +2 -2
- autocoder/common/shells.py +254 -13
- autocoder/common/stats_panel.py +126 -0
- autocoder/dispacher/actions/action.py +6 -18
- autocoder/dispacher/actions/copilot.py +2 -2
- autocoder/dispacher/actions/plugins/action_regex_project.py +1 -3
- autocoder/dispacher/actions/plugins/action_translate.py +1 -1
- autocoder/index/entry.py +8 -2
- autocoder/index/filter/normal_filter.py +13 -2
- autocoder/index/filter/quick_filter.py +127 -13
- autocoder/index/index.py +8 -7
- autocoder/models.py +2 -2
- autocoder/pyproject/__init__.py +5 -5
- autocoder/rag/cache/byzer_storage_cache.py +4 -4
- autocoder/rag/cache/file_monitor_cache.py +2 -2
- autocoder/rag/cache/simple_cache.py +4 -4
- autocoder/rag/long_context_rag.py +2 -2
- autocoder/regexproject/__init__.py +3 -2
- autocoder/suffixproject/__init__.py +3 -2
- autocoder/tsproject/__init__.py +3 -2
- autocoder/utils/conversation_store.py +1 -1
- autocoder/utils/operate_config_api.py +3 -3
- autocoder/utils/project_structure.py +258 -3
- autocoder/utils/thread_utils.py +6 -1
- autocoder/version.py +1 -1
- {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/entry_points.txt +0 -0
- {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.
|
|
264
|
-
3.
|
|
265
|
-
4.
|
|
266
|
-
5.
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
# 如果是新模型,添加到模型列表中
|
autocoder/pyproject/__init__.py
CHANGED
|
@@ -174,7 +174,8 @@ class PyProject:
|
|
|
174
174
|
return False
|
|
175
175
|
|
|
176
176
|
def output(self):
|
|
177
|
-
|
|
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
|
-
|
|
186
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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")
|
autocoder/tsproject/__init__.py
CHANGED
|
@@ -106,7 +106,8 @@ class TSProject:
|
|
|
106
106
|
return False
|
|
107
107
|
|
|
108
108
|
def output(self):
|
|
109
|
-
|
|
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)
|