auto-coder 0.1.345__py3-none-any.whl → 0.1.347__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.345.dist-info → auto_coder-0.1.347.dist-info}/METADATA +1 -1
- {auto_coder-0.1.345.dist-info → auto_coder-0.1.347.dist-info}/RECORD +23 -21
- autocoder/auto_coder_runner.py +4 -5
- autocoder/common/auto_coder_lang.py +8 -0
- autocoder/common/v2/agent/agentic_edit.py +76 -3
- autocoder/common/v2/agent/agentic_edit_tools/__init__.py +2 -0
- autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +2 -2
- autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +42 -0
- autocoder/common/v2/agent/agentic_edit_types.py +4 -0
- autocoder/rag/cache/byzer_storage_cache.py +44 -74
- autocoder/rag/cache/failed_files_utils.py +39 -0
- autocoder/rag/cache/file_monitor_cache.py +3 -1
- autocoder/rag/cache/local_byzer_storage_cache.py +45 -73
- autocoder/rag/cache/local_duckdb_storage_cache.py +43 -13
- autocoder/rag/cache/simple_cache.py +40 -12
- autocoder/rag/document_retriever.py +17 -8
- autocoder/rag/long_context_rag.py +3 -2
- autocoder/rag/qa_conversation_strategy.py +164 -11
- autocoder/version.py +1 -1
- {auto_coder-0.1.345.dist-info → auto_coder-0.1.347.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.345.dist-info → auto_coder-0.1.347.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.345.dist-info → auto_coder-0.1.347.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.345.dist-info → auto_coder-0.1.347.dist-info}/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ autocoder/auto_coder_lang.py,sha256=Rtupq6N3_HT7JRhDKdgCBcwRaiAnyCOR_Gsp4jUomrI,
|
|
|
4
4
|
autocoder/auto_coder_rag.py,sha256=NesRm7sIJrRQL1xxm_lbMtM7gi-KrYv9f26RfBuloZE,35386
|
|
5
5
|
autocoder/auto_coder_rag_client_mcp.py,sha256=QRxUbjc6A8UmDMQ8lXgZkjgqtq3lgKYeatJbDY6rSo0,6270
|
|
6
6
|
autocoder/auto_coder_rag_mcp.py,sha256=-RrjNwFaS2e5v8XDIrKR-zlUNUE8UBaeOtojffBrvJo,8521
|
|
7
|
-
autocoder/auto_coder_runner.py,sha256
|
|
7
|
+
autocoder/auto_coder_runner.py,sha256=2hI2095L5GWP1J04Lr7xBBTDOcNq_l4-HctcJ8bVaFE,111833
|
|
8
8
|
autocoder/auto_coder_server.py,sha256=bLORGEclcVdbBVfM140JCI8WtdrU0jbgqdJIVVupiEU,20578
|
|
9
9
|
autocoder/benchmark.py,sha256=Ypomkdzd1T3GE6dRICY3Hj547dZ6_inqJbBJIp5QMco,4423
|
|
10
10
|
autocoder/chat_auto_coder.py,sha256=CthuvdjVjTQOVv-zREsl8OCsZHPSP9OQcIgHULrW2Ro,25842
|
|
@@ -14,7 +14,7 @@ autocoder/command_parser.py,sha256=fx1g9E6GaM273lGTcJqaFQ-hoksS_Ik2glBMnVltPCE,1
|
|
|
14
14
|
autocoder/lang.py,sha256=PFtATuOhHRnfpqHQkXr6p4C893JvpsgwTMif3l-GEi0,14321
|
|
15
15
|
autocoder/models.py,sha256=_SCar82QIeBFTZZBdM2jPS6atKVhHnvE0gX3V0CsxD4,11590
|
|
16
16
|
autocoder/run_context.py,sha256=IUfSO6_gp2Wt1blFWAmOpN0b0nDrTTk4LmtCYUBIoro,1643
|
|
17
|
-
autocoder/version.py,sha256=
|
|
17
|
+
autocoder/version.py,sha256=R1KcSahF3DMgAzBREOFaR3Pjcqm9a3u4b36Hb2RFm50,23
|
|
18
18
|
autocoder/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
19
|
autocoder/agent/agentic_edit.py,sha256=XsfePZ-t6M-uBSdG1VLZXk1goqXk2HPeJ_A8IYyBuWQ,58896
|
|
20
20
|
autocoder/agent/agentic_edit_types.py,sha256=oFcDd_cxJ2yH9Ed1uTpD3BipudgoIEWDMPb5pAkq4gI,3288
|
|
@@ -54,7 +54,7 @@ autocoder/common/action_yml_file_manager.py,sha256=DdF5P1R_B_chCnnqoA2IgogakWLZk
|
|
|
54
54
|
autocoder/common/anything2images.py,sha256=0ILBbWzY02M-CiWB-vzuomb_J1hVdxRcenAfIrAXq9M,25283
|
|
55
55
|
autocoder/common/anything2img.py,sha256=iZQmg8srXlD7N5uGl5b_ONKJMBjYoW8kPmokkG6ISF0,10118
|
|
56
56
|
autocoder/common/audio.py,sha256=Kn9nWKQddWnUrAz0a_ZUgjcu4VUU_IcZBigT7n3N3qc,7439
|
|
57
|
-
autocoder/common/auto_coder_lang.py,sha256=
|
|
57
|
+
autocoder/common/auto_coder_lang.py,sha256=bqBoICLIvi9l8jRCwcNLWR6n5pI3ix7YDPGpmqQDmgc,42677
|
|
58
58
|
autocoder/common/auto_configure.py,sha256=D4N-fl9v8bKM5-Ds-uhkC2uGDmHH_ZjLJ759F8KXMKs,13129
|
|
59
59
|
autocoder/common/buildin_tokenizer.py,sha256=L7d5t39ZFvUd6EoMPXUhYK1toD0FHlRH1jtjKRGokWU,1236
|
|
60
60
|
autocoder/common/chunk_validation.py,sha256=BrR_ZWavW8IANuueEE7hS8NFAwEvm8TX34WnPx_1hs8,3030
|
|
@@ -126,18 +126,19 @@ autocoder/common/v2/code_editblock_manager.py,sha256=G0CIuV9Ki0FqMLnpA8nBT4pnkCN
|
|
|
126
126
|
autocoder/common/v2/code_manager.py,sha256=C403bS-f6urixwitlKHcml-J03hci-UyNwHJOqBiY6Q,9182
|
|
127
127
|
autocoder/common/v2/code_strict_diff_manager.py,sha256=v-J1kDyLg7tLGg_6_lbO9S4fNkx7M_L8Xr2G7fPptiU,9347
|
|
128
128
|
autocoder/common/v2/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
129
|
-
autocoder/common/v2/agent/agentic_edit.py,sha256=
|
|
129
|
+
autocoder/common/v2/agent/agentic_edit.py,sha256=pxgKD-FZaaRQkPgWsxvLleUXDRFC8qSlYfNUHU6ZENU,94794
|
|
130
130
|
autocoder/common/v2/agent/agentic_edit_conversation.py,sha256=qLLhTegH619JQTp3s1bj5FVn2hAcoV-DlhGO3UyIOMc,7338
|
|
131
|
-
autocoder/common/v2/agent/agentic_edit_types.py,sha256=
|
|
131
|
+
autocoder/common/v2/agent/agentic_edit_types.py,sha256=VJMrictg6hJ3mC45VgQGRd43DyDUPDUvPV1Rf3z72NI,4776
|
|
132
132
|
autocoder/common/v2/agent/agentic_tool_display.py,sha256=WKirt-2V346KLnbHgH3NVJiK3xvriD9oaCWj2IdvzLU,7309
|
|
133
133
|
autocoder/common/v2/agent/ignore_utils.py,sha256=gnUchRzKMLbUm_jvnKL-r-K9MWKPtt-6iiuzijY7Es0,1717
|
|
134
|
-
autocoder/common/v2/agent/agentic_edit_tools/__init__.py,sha256=
|
|
135
|
-
autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py,sha256=
|
|
134
|
+
autocoder/common/v2/agent/agentic_edit_tools/__init__.py,sha256=RbPZZcZg_VnGssL577GxSyFrYrxQ_LopJ4G_-mY3z_Q,1337
|
|
135
|
+
autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py,sha256=bwtf4m9N82TCP3piK5UglJk1FVFFm7ZX59XerA2qxko,3131
|
|
136
136
|
autocoder/common/v2/agent/agentic_edit_tools/attempt_completion_tool_resolver.py,sha256=82ZGKeRBSDKeead_XVBW4FxpiE-5dS7tBOk_3RZ6B5s,1511
|
|
137
137
|
autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py,sha256=Zid2m1uZd-2wVFGc_n_KAViXZyNjbdLSpI5n7ut1RUQ,1036
|
|
138
138
|
autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py,sha256=GueQfIY2hVu2R5j9R5rBtn2znl5MlmEdGtsa6snsMHs,4112
|
|
139
139
|
autocoder/common/v2/agent/agentic_edit_tools/list_code_definition_names_tool_resolver.py,sha256=8QoMsADUDWliqiDt_dpguz31403syB8eeW0Pcw-qfb8,3842
|
|
140
140
|
autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py,sha256=d0LzGPA3zsIHK5s1-arPry6ddWFSymRsMY3VbkV6v5A,5795
|
|
141
|
+
autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py,sha256=dIdV12VuczHpHuHgx2B1j_3BZYc9PL0jfHCuBk9ryk8,2005
|
|
141
142
|
autocoder/common/v2/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py,sha256=lGT4_QYJK6Fa9f6HVSGo0cSsGK7qCsDYgJGUowNxPzk,1499
|
|
142
143
|
autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py,sha256=9Bh0KVbL0qiIqwChlb77biiBiETQ3zekxGe5Fj7hXAg,2800
|
|
143
144
|
autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py,sha256=lpD4fCbVR8GTrynqXON69IjM94nPy3nuUL62Ashm5O4,7988
|
|
@@ -211,11 +212,11 @@ autocoder/rag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
211
212
|
autocoder/rag/api_server.py,sha256=StGyxrM-7-W2vYHJq-i_Fv-MHrl9UgVWY272Hd-6VJ4,13090
|
|
212
213
|
autocoder/rag/conversation_to_queries.py,sha256=xwmErn4WbdADnhK1me-h_6fV3KYrl_y1qPNQl1aoI6o,4810
|
|
213
214
|
autocoder/rag/doc_filter.py,sha256=UduVO2mlrngwJICrefjDJTYfdmQ4GcRXrfWDQ7xXksk,14206
|
|
214
|
-
autocoder/rag/document_retriever.py,sha256=
|
|
215
|
+
autocoder/rag/document_retriever.py,sha256=rFwbAuHTvEFJq16HQNlmRLyJp2ddn2RNFslw_ncU7NI,8847
|
|
215
216
|
autocoder/rag/lang.py,sha256=HvcMeu6jReEJOGxyLMn4rwBoD-myFwmykS3VLceBJLs,3364
|
|
216
217
|
autocoder/rag/llm_wrapper.py,sha256=Ht5GF5yJtrztoliujsZzx_ooWZmHkd5xLZKcGEiicZw,4303
|
|
217
|
-
autocoder/rag/long_context_rag.py,sha256=
|
|
218
|
-
autocoder/rag/qa_conversation_strategy.py,sha256=
|
|
218
|
+
autocoder/rag/long_context_rag.py,sha256=syPIxO_TQJpBgjZ0taF-G7xVGvkNjKWL65KTI-sy4io,42234
|
|
219
|
+
autocoder/rag/qa_conversation_strategy.py,sha256=N4zcLstmTEZH4iGTnmNhzlRSRPfdZnTlKOXPqnqSEUQ,11727
|
|
219
220
|
autocoder/rag/rag_config.py,sha256=8LwFcTd8OJWWwi1_WY4IzjqgtT6RyE2j4PjxS5cCTDE,802
|
|
220
221
|
autocoder/rag/rag_entry.py,sha256=6TKtErZ0Us9XSV6HgRKXA6yR3SiZGPHpynOKSaR1wgE,2463
|
|
221
222
|
autocoder/rag/raw_rag.py,sha256=BOr0YGf3umjqXOIDVO1LXQ0bIHx8hzBdiubND2ezyxc,2946
|
|
@@ -232,13 +233,14 @@ autocoder/rag/utils.py,sha256=f21ybCAlYVgr3tJP9MkVoM9d82-uG5NHu2gsv2oaVBQ,4961
|
|
|
232
233
|
autocoder/rag/variable_holder.py,sha256=PFvBjFcR7-fNDD4Vcsc8CpH2Te057vcpwJMxtrfUgKI,75
|
|
233
234
|
autocoder/rag/cache/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
234
235
|
autocoder/rag/cache/base_cache.py,sha256=EaYYYbclMBvnlOUoM7qonnluwZX5oSvUjdvGvFun8_8,742
|
|
235
|
-
autocoder/rag/cache/byzer_storage_cache.py,sha256=
|
|
236
|
+
autocoder/rag/cache/byzer_storage_cache.py,sha256=buUrsY7-gWf6BXtzhFVKRJ-ME4LGrr9EEzjoSVu9G6g,27937
|
|
236
237
|
autocoder/rag/cache/cache_result_merge.py,sha256=VnTdbT2OMBmWl_83bqds97d9_M33IhPNX8tF7KH2GMM,10556
|
|
237
|
-
autocoder/rag/cache/
|
|
238
|
-
autocoder/rag/cache/
|
|
239
|
-
autocoder/rag/cache/
|
|
238
|
+
autocoder/rag/cache/failed_files_utils.py,sha256=kITguXANLC3EEJy5JoKzNXrtwvTkmZT-ANPwcno42Ck,1183
|
|
239
|
+
autocoder/rag/cache/file_monitor_cache.py,sha256=TelBRzw2uD9QLUIvyrdD6XWmgvb3MyTgx63ZrpptaXI,9512
|
|
240
|
+
autocoder/rag/cache/local_byzer_storage_cache.py,sha256=X7EVGa4xcmUWC7WLnyGPx17I1l9NvW3OHfkErO4Ww0M,30784
|
|
241
|
+
autocoder/rag/cache/local_duckdb_storage_cache.py,sha256=6NsNlqA8-nGSD8ILXQgO-6QbgE8eUy4uT3eM1t69nic,34447
|
|
240
242
|
autocoder/rag/cache/rag_file_meta.py,sha256=RQ3n4wfkHlB-1ljS3sFSi8ijbsUPeIqBSgjmmbRuwRI,20521
|
|
241
|
-
autocoder/rag/cache/simple_cache.py,sha256=
|
|
243
|
+
autocoder/rag/cache/simple_cache.py,sha256=yrGgRXGcMNrWSQOviPshm3Qlo2QDNVFoRZPLNTV_nQs,15997
|
|
242
244
|
autocoder/rag/loaders/__init__.py,sha256=EQHEZ5Cmz-mGP2SllUTvcIbYCnF7W149dNpNItfs0yE,304
|
|
243
245
|
autocoder/rag/loaders/docx_loader.py,sha256=ZswPqiiLngUEpzLhNNm1nmwEYV7ZHFEfIoXoG7c5GDU,614
|
|
244
246
|
autocoder/rag/loaders/excel_loader.py,sha256=Ue8YB1z_kBs8SjIPuBskyM08Q1JiONs_BJZPrzi59oo,896
|
|
@@ -277,9 +279,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
277
279
|
autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
278
280
|
autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=KW0mlmcHlStXi8-_6fXZ2-ifeJ5mgP0OV7DQFzCtIsw,14008
|
|
279
281
|
autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
280
|
-
auto_coder-0.1.
|
|
281
|
-
auto_coder-0.1.
|
|
282
|
-
auto_coder-0.1.
|
|
283
|
-
auto_coder-0.1.
|
|
284
|
-
auto_coder-0.1.
|
|
285
|
-
auto_coder-0.1.
|
|
282
|
+
auto_coder-0.1.347.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
283
|
+
auto_coder-0.1.347.dist-info/METADATA,sha256=-wG5ilXMo-fYflOC9zJZY2N0JdHBluyyxYNqxhzNVwQ,2728
|
|
284
|
+
auto_coder-0.1.347.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
285
|
+
auto_coder-0.1.347.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
|
|
286
|
+
auto_coder-0.1.347.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
|
|
287
|
+
auto_coder-0.1.347.dist-info/RECORD,,
|
autocoder/auto_coder_runner.py
CHANGED
|
@@ -40,7 +40,7 @@ from rich.markdown import Markdown
|
|
|
40
40
|
from byzerllm.utils.nontext import Image
|
|
41
41
|
import git
|
|
42
42
|
from autocoder.common import git_utils
|
|
43
|
-
from autocoder.chat_auto_coder_lang import get_message
|
|
43
|
+
from autocoder.chat_auto_coder_lang import get_message,get_message_with_format
|
|
44
44
|
from autocoder.agent.auto_guess_query import AutoGuessQuery
|
|
45
45
|
from autocoder.common.mcp_server import get_mcp_server
|
|
46
46
|
from autocoder.common.mcp_server_types import (
|
|
@@ -273,8 +273,8 @@ def initialize_system(args:InitializeSystemRequest):
|
|
|
273
273
|
else:
|
|
274
274
|
print(f" {message}")
|
|
275
275
|
|
|
276
|
-
def init_project():
|
|
277
|
-
if not os.path.exists(".auto-coder"):
|
|
276
|
+
def init_project():
|
|
277
|
+
if not os.path.exists(".auto-coder") or not os.path.exists("actions"):
|
|
278
278
|
first_time[0] = True
|
|
279
279
|
print_status(get_message("not_initialized"), "warning")
|
|
280
280
|
init_choice = input(
|
|
@@ -295,8 +295,7 @@ def initialize_system(args:InitializeSystemRequest):
|
|
|
295
295
|
|
|
296
296
|
if not os.path.exists(base_persist_dir):
|
|
297
297
|
os.makedirs(base_persist_dir, exist_ok=True)
|
|
298
|
-
print_status(
|
|
299
|
-
base_persist_dir), "success")
|
|
298
|
+
print_status(get_message_with_format("created_dir",path=base_persist_dir), "success")
|
|
300
299
|
|
|
301
300
|
if first_time[0]:
|
|
302
301
|
configure_project_type()
|
|
@@ -833,6 +833,14 @@ MESSAGES = {
|
|
|
833
833
|
"/agent/edit/user_query":{
|
|
834
834
|
"en":"User Query",
|
|
835
835
|
"zh":"用户查询"
|
|
836
|
+
},
|
|
837
|
+
"/agent/edit/apply_pre_changes":{
|
|
838
|
+
"en":"Commit user changes",
|
|
839
|
+
"zh":"提交用户修改"
|
|
840
|
+
},
|
|
841
|
+
"/agent/edit/apply_changes":{
|
|
842
|
+
"en":"Commit changes",
|
|
843
|
+
"zh":"提交修改"
|
|
836
844
|
}
|
|
837
845
|
}
|
|
838
846
|
|
|
@@ -52,8 +52,10 @@ from autocoder.common.v2.agent.agentic_edit_tools import ( # Import specific re
|
|
|
52
52
|
ExecuteCommandToolResolver, ReadFileToolResolver, WriteToFileToolResolver,
|
|
53
53
|
ReplaceInFileToolResolver, SearchFilesToolResolver, ListFilesToolResolver,
|
|
54
54
|
ListCodeDefinitionNamesToolResolver, AskFollowupQuestionToolResolver,
|
|
55
|
-
AttemptCompletionToolResolver, PlanModeRespondToolResolver, UseMcpToolResolver
|
|
55
|
+
AttemptCompletionToolResolver, PlanModeRespondToolResolver, UseMcpToolResolver,
|
|
56
|
+
ListPackageInfoToolResolver
|
|
56
57
|
)
|
|
58
|
+
|
|
57
59
|
from autocoder.common.v2.agent.agentic_edit_types import (AgenticEditRequest, ToolResult,
|
|
58
60
|
MemoryConfig, CommandConfig, BaseTool,
|
|
59
61
|
ExecuteCommandTool, ReadFileTool,
|
|
@@ -63,6 +65,7 @@ from autocoder.common.v2.agent.agentic_edit_types import (AgenticEditRequest, To
|
|
|
63
65
|
ListFilesTool,
|
|
64
66
|
ListCodeDefinitionNamesTool, AskFollowupQuestionTool,
|
|
65
67
|
AttemptCompletionTool, PlanModeRespondTool, UseMcpTool,
|
|
68
|
+
ListPackageInfoTool,
|
|
66
69
|
TOOL_MODEL_MAP,
|
|
67
70
|
# Event Types
|
|
68
71
|
LLMOutputEvent, LLMThinkingEvent, ToolCallEvent,
|
|
@@ -83,6 +86,7 @@ TOOL_RESOLVER_MAP: Dict[Type[BaseTool], Type[BaseToolResolver]] = {
|
|
|
83
86
|
SearchFilesTool: SearchFilesToolResolver,
|
|
84
87
|
ListFilesTool: ListFilesToolResolver,
|
|
85
88
|
ListCodeDefinitionNamesTool: ListCodeDefinitionNamesToolResolver,
|
|
89
|
+
ListPackageInfoTool: ListPackageInfoToolResolver,
|
|
86
90
|
AskFollowupQuestionTool: AskFollowupQuestionToolResolver,
|
|
87
91
|
AttemptCompletionTool: AttemptCompletionToolResolver, # Will stop the loop anyway
|
|
88
92
|
PlanModeRespondTool: PlanModeRespondToolResolver,
|
|
@@ -221,7 +225,7 @@ class AgenticEdit:
|
|
|
221
225
|
{{files}}
|
|
222
226
|
</files>
|
|
223
227
|
|
|
224
|
-
====
|
|
228
|
+
====
|
|
225
229
|
|
|
226
230
|
TOOL USE
|
|
227
231
|
|
|
@@ -245,6 +249,7 @@ class AgenticEdit:
|
|
|
245
249
|
|
|
246
250
|
Always adhere to this format for the tool use to ensure proper parsing and execution.
|
|
247
251
|
|
|
252
|
+
|
|
248
253
|
# Tools
|
|
249
254
|
|
|
250
255
|
## execute_command
|
|
@@ -258,6 +263,15 @@ class AgenticEdit:
|
|
|
258
263
|
<requires_approval>true or false</requires_approval>
|
|
259
264
|
</execute_command>
|
|
260
265
|
|
|
266
|
+
## list_package_info
|
|
267
|
+
Description: Request to retrieve information about a source code package, such as recent changes or documentation summary, to better understand the code context. It accepts a directory path (absolute or relative to the current project).
|
|
268
|
+
Parameters:
|
|
269
|
+
- path: (required) The source code package directory path.
|
|
270
|
+
Usage:
|
|
271
|
+
<list_package_info>
|
|
272
|
+
<path>relative/or/absolute/package/path</path>
|
|
273
|
+
</list_package_info>
|
|
274
|
+
|
|
261
275
|
## read_file
|
|
262
276
|
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string.
|
|
263
277
|
Parameters:
|
|
@@ -667,7 +681,36 @@ class AgenticEdit:
|
|
|
667
681
|
You can use the tool `read_file` to read these description files, which helps you decide exactly which files need detailed attention. Note that the `active.md` file does not contain information about all files within the directory—it only includes information
|
|
668
682
|
about the files that were recently changed.
|
|
669
683
|
{% endif %}
|
|
684
|
+
|
|
685
|
+
{% if extra_docs %}
|
|
686
|
+
====
|
|
687
|
+
|
|
688
|
+
RULES PROVIDED BY USER
|
|
689
|
+
|
|
690
|
+
The following rules are provided by the user, and you must follow them strictly.
|
|
691
|
+
|
|
692
|
+
{% for key, value in extra_docs.items() %}
|
|
693
|
+
### {{ key }}
|
|
694
|
+
{{ value }}
|
|
695
|
+
{% endfor %}
|
|
696
|
+
{% endif %}
|
|
670
697
|
"""
|
|
698
|
+
import os
|
|
699
|
+
extra_docs = {}
|
|
700
|
+
rules_dir = os.path.join(self.args.source_dir,
|
|
701
|
+
".auto-coder", "autocoderrules")
|
|
702
|
+
if os.path.isdir(rules_dir):
|
|
703
|
+
for fname in os.listdir(rules_dir):
|
|
704
|
+
if fname.endswith(".md"):
|
|
705
|
+
fpath = os.path.join(rules_dir, fname)
|
|
706
|
+
try:
|
|
707
|
+
with open(fpath, "r", encoding="utf-8") as f:
|
|
708
|
+
content = f.read()
|
|
709
|
+
key = os.path.splitext(fname)[0]
|
|
710
|
+
extra_docs[key] = content
|
|
711
|
+
except Exception:
|
|
712
|
+
continue
|
|
713
|
+
|
|
671
714
|
env_info = detect_env()
|
|
672
715
|
shell_type = "bash"
|
|
673
716
|
if shells.is_running_in_cmd():
|
|
@@ -687,6 +730,7 @@ class AgenticEdit:
|
|
|
687
730
|
"files": self.files.to_str(),
|
|
688
731
|
"mcp_server_info": self.mcp_server_info,
|
|
689
732
|
"enable_active_context": self.args.enable_active_context,
|
|
733
|
+
"extra_docs": extra_docs,
|
|
690
734
|
}
|
|
691
735
|
|
|
692
736
|
# Removed _execute_command_result and execute_auto_command methods
|
|
@@ -971,6 +1015,7 @@ class AgenticEdit:
|
|
|
971
1015
|
global_cancel.check_and_raise()
|
|
972
1016
|
|
|
973
1017
|
meta_holder.meta = metadata
|
|
1018
|
+
logger.info(f"metadata: {metadata.input_tokens_count}")
|
|
974
1019
|
if not content_chunk:
|
|
975
1020
|
continue
|
|
976
1021
|
buffer += content_chunk
|
|
@@ -1107,6 +1152,7 @@ class AgenticEdit:
|
|
|
1107
1152
|
standard event system format and writing them using the event manager.
|
|
1108
1153
|
"""
|
|
1109
1154
|
event_manager = get_event_manager(self.args.event_file)
|
|
1155
|
+
self.apply_pre_changes()
|
|
1110
1156
|
|
|
1111
1157
|
try:
|
|
1112
1158
|
event_stream = self.analyze(request)
|
|
@@ -1250,6 +1296,25 @@ class AgenticEdit:
|
|
|
1250
1296
|
# Re-raise the exception if needed, or handle appropriately
|
|
1251
1297
|
raise e
|
|
1252
1298
|
|
|
1299
|
+
def apply_pre_changes(self):
|
|
1300
|
+
# get the file name
|
|
1301
|
+
file_name = os.path.basename(self.args.file)
|
|
1302
|
+
if not self.args.skip_commit:
|
|
1303
|
+
try:
|
|
1304
|
+
get_event_manager(self.args.event_file).write_result(
|
|
1305
|
+
EventContentCreator.create_result(
|
|
1306
|
+
content=self.printer.get_message_from_key("/agent/edit/apply_pre_changes")), metadata=EventMetadata(
|
|
1307
|
+
action_file=self.args.file,
|
|
1308
|
+
is_streaming=False,
|
|
1309
|
+
path="/agent/edit/apply_pre_changes",
|
|
1310
|
+
stream_out_type="/agent/edit").to_dict())
|
|
1311
|
+
git_utils.commit_changes(
|
|
1312
|
+
self.args.source_dir, f"auto_coder_pre_{file_name}")
|
|
1313
|
+
except Exception as e:
|
|
1314
|
+
self.printer.print_in_terminal("git_init_required",
|
|
1315
|
+
source_dir=self.args.source_dir, error=str(e))
|
|
1316
|
+
return
|
|
1317
|
+
|
|
1253
1318
|
def apply_changes(self):
|
|
1254
1319
|
"""
|
|
1255
1320
|
Apply all tracked file changes to the original project directory.
|
|
@@ -1267,6 +1332,12 @@ class AgenticEdit:
|
|
|
1267
1332
|
f"{self.args.query}\nauto_coder_{file_name}",
|
|
1268
1333
|
)
|
|
1269
1334
|
|
|
1335
|
+
get_event_manager(self.args.event_file).write_result(
|
|
1336
|
+
EventContentCreator.create_result(
|
|
1337
|
+
content=self.printer.get_message_from_key("/agent/edit/apply_changes")), metadata=EventMetadata(
|
|
1338
|
+
action_file=self.args.file,
|
|
1339
|
+
is_streaming=False,
|
|
1340
|
+
stream_out_type="/agent/edit").to_dict())
|
|
1270
1341
|
action_yml_file_manager = ActionYmlFileManager(
|
|
1271
1342
|
self.args.source_dir)
|
|
1272
1343
|
action_file_name = os.path.basename(self.args.file)
|
|
@@ -1313,6 +1384,7 @@ class AgenticEdit:
|
|
|
1313
1384
|
f"[bold]{get_message('/agent/edit/user_query')}:[/bold]\n{request.user_input}", title=get_message("/agent/edit/objective"), border_style="blue"))
|
|
1314
1385
|
|
|
1315
1386
|
try:
|
|
1387
|
+
self.apply_pre_changes()
|
|
1316
1388
|
event_stream = self.analyze(request)
|
|
1317
1389
|
for event in event_stream:
|
|
1318
1390
|
if isinstance(event, LLMThinkingEvent):
|
|
@@ -1404,7 +1476,8 @@ class AgenticEdit:
|
|
|
1404
1476
|
logger.warning(
|
|
1405
1477
|
f"Error formatting tool result content: {e}")
|
|
1406
1478
|
panel_content.append(
|
|
1407
|
-
|
|
1479
|
+
# Fallback
|
|
1480
|
+
_format_content(str(result.content)))
|
|
1408
1481
|
|
|
1409
1482
|
# Print the base info panel
|
|
1410
1483
|
console.print(Panel("\n".join(
|
|
@@ -11,6 +11,7 @@ from .ask_followup_question_tool_resolver import AskFollowupQuestionToolResolver
|
|
|
11
11
|
from .attempt_completion_tool_resolver import AttemptCompletionToolResolver
|
|
12
12
|
from .plan_mode_respond_tool_resolver import PlanModeRespondToolResolver
|
|
13
13
|
from .use_mcp_tool_resolver import UseMcpToolResolver
|
|
14
|
+
from .list_package_info_tool_resolver import ListPackageInfoToolResolver
|
|
14
15
|
|
|
15
16
|
__all__ = [
|
|
16
17
|
"BaseToolResolver",
|
|
@@ -25,4 +26,5 @@ __all__ = [
|
|
|
25
26
|
"AttemptCompletionToolResolver",
|
|
26
27
|
"PlanModeRespondToolResolver",
|
|
27
28
|
"UseMcpToolResolver",
|
|
29
|
+
"ListPackageInfoToolResolver",
|
|
28
30
|
]
|
|
@@ -29,12 +29,12 @@ class AskFollowupQuestionToolResolver(BaseToolResolver):
|
|
|
29
29
|
This resolver doesn't directly ask the user but prepares the data for it.
|
|
30
30
|
"""
|
|
31
31
|
question = self.tool.question
|
|
32
|
-
options = self.tool.options
|
|
32
|
+
options = self.tool.options or []
|
|
33
33
|
options_text = "\n".join([f"{i+1}. {option}" for i, option in enumerate(options)])
|
|
34
34
|
if get_run_context().is_web():
|
|
35
35
|
answer = get_event_manager(
|
|
36
36
|
self.args.event_file).ask_user(prompt=question)
|
|
37
|
-
self.result_manager.append(content=answer + "\n" + options_text, meta={
|
|
37
|
+
self.result_manager.append(content=answer + ("\n" + options_text if options_text else ""), meta={
|
|
38
38
|
"action": "ask_user",
|
|
39
39
|
"input": {
|
|
40
40
|
"question": question
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver
|
|
5
|
+
from autocoder.common.v2.agent.agentic_edit_types import ListPackageInfoTool, ToolResult
|
|
6
|
+
from loguru import logger
|
|
7
|
+
import typing
|
|
8
|
+
|
|
9
|
+
if typing.TYPE_CHECKING:
|
|
10
|
+
from autocoder.common.v2.agent.agentic_edit import AgenticEdit
|
|
11
|
+
|
|
12
|
+
class ListPackageInfoToolResolver(BaseToolResolver):
|
|
13
|
+
def __init__(self, agent: Optional['AgenticEdit'], tool: ListPackageInfoTool, args):
|
|
14
|
+
super().__init__(agent, tool, args)
|
|
15
|
+
self.tool: ListPackageInfoTool = tool
|
|
16
|
+
|
|
17
|
+
def resolve(self) -> ToolResult:
|
|
18
|
+
source_dir = self.args.source_dir or "."
|
|
19
|
+
abs_source_dir = os.path.abspath(source_dir)
|
|
20
|
+
|
|
21
|
+
input_path = self.tool.path.strip()
|
|
22
|
+
abs_input_path = os.path.abspath(os.path.join(source_dir, input_path)) if not os.path.isabs(input_path) else input_path
|
|
23
|
+
|
|
24
|
+
# 校验输入目录是否在项目目录内
|
|
25
|
+
if not abs_input_path.startswith(abs_source_dir):
|
|
26
|
+
return ToolResult(success=False, message=f"Error: Access denied. Path outside project: {self.tool.path}")
|
|
27
|
+
|
|
28
|
+
rel_package_path = os.path.relpath(abs_input_path, abs_source_dir)
|
|
29
|
+
active_md_path = os.path.join(abs_source_dir, ".auto-coder", "active-context", rel_package_path, "active.md")
|
|
30
|
+
|
|
31
|
+
logger.info(f"Looking for package info at: {active_md_path}")
|
|
32
|
+
|
|
33
|
+
if not os.path.exists(active_md_path):
|
|
34
|
+
return ToolResult(success=True, message="No package info found for this path.", content="没有相关包信息。")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
with open(active_md_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
38
|
+
content = f.read()
|
|
39
|
+
return ToolResult(success=True, message="Successfully retrieved package info.", content=content)
|
|
40
|
+
except Exception as e:
|
|
41
|
+
logger.error(f"Error reading package info file: {e}")
|
|
42
|
+
return ToolResult(success=False, message=f"Error reading package info file: {e}")
|
|
@@ -57,6 +57,9 @@ class UseMcpTool(BaseTool):
|
|
|
57
57
|
tool_name: str
|
|
58
58
|
query:str
|
|
59
59
|
|
|
60
|
+
class ListPackageInfoTool(BaseTool):
|
|
61
|
+
path: str # 源码包目录,相对路径或绝对路径
|
|
62
|
+
|
|
60
63
|
# Event Types for Rich Output Streaming
|
|
61
64
|
class LLMOutputEvent(BaseModel):
|
|
62
65
|
"""Represents plain text output from the LLM."""
|
|
@@ -112,6 +115,7 @@ TOOL_MODEL_MAP: Dict[str, Type[BaseTool]] = {
|
|
|
112
115
|
"attempt_completion": AttemptCompletionTool,
|
|
113
116
|
"plan_mode_respond": PlanModeRespondTool,
|
|
114
117
|
"use_mcp_tool": UseMcpTool,
|
|
118
|
+
"list_package_info": ListPackageInfoTool,
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
class FileChangeEntry(BaseModel):
|
|
@@ -29,6 +29,7 @@ import hashlib
|
|
|
29
29
|
from typing import Union
|
|
30
30
|
from pydantic import BaseModel
|
|
31
31
|
from autocoder.rag.cache.cache_result_merge import CacheResultMerger, MergeStrategy
|
|
32
|
+
from .failed_files_utils import save_failed_files, load_failed_files
|
|
32
33
|
import time
|
|
33
34
|
|
|
34
35
|
if platform.system() != "Windows":
|
|
@@ -65,74 +66,18 @@ class ByzerStorageCache(BaseCacheManager):
|
|
|
65
66
|
ignore_spec,
|
|
66
67
|
required_exts,
|
|
67
68
|
extra_params: Optional[AutoCoderArgs] = None,
|
|
69
|
+
args=None,
|
|
70
|
+
llm=None,
|
|
68
71
|
):
|
|
69
72
|
"""
|
|
70
73
|
初始化基于云端 Byzer Storage 的 RAG 缓存管理器。
|
|
71
|
-
|
|
72
|
-
参数:
|
|
73
|
-
path: 需要索引的代码库根目录
|
|
74
|
-
ignore_spec: 指定哪些文件/目录应被忽略的规则
|
|
75
|
-
required_exts: 需要处理的文件扩展名列表
|
|
76
|
-
extra_params: 额外的配置参数,包含向量索引相关设置
|
|
77
|
-
|
|
78
|
-
缓存结构 (self.cache):
|
|
79
|
-
self.cache 是一个字典,键为文件路径,值为 CacheItem 对象:
|
|
80
|
-
{
|
|
81
|
-
"file_path1": CacheItem(
|
|
82
|
-
file_path: str, # 文件的绝对路径
|
|
83
|
-
relative_path: str, # 相对于项目根目录的路径
|
|
84
|
-
content: List[Dict], # 文件内容的结构化表示,每个元素是 SourceCode 对象的序列化
|
|
85
|
-
modify_time: float, # 文件最后修改时间的时间戳
|
|
86
|
-
md5: str # 文件内容的 MD5 哈希值,用于检测变更
|
|
87
|
-
),
|
|
88
|
-
"file_path2": CacheItem(...),
|
|
89
|
-
...
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
这个缓存有两层存储:
|
|
93
|
-
1. 本地文件缓存: 保存在项目根目录的 .cache/byzer_storage_speedup.jsonl 文件中
|
|
94
|
-
- 用于跟踪文件变更和快速加载
|
|
95
|
-
- 使用 JSONL 格式存储,每行是一个 CacheItem 的 JSON 表示
|
|
96
|
-
|
|
97
|
-
2. 云端 Byzer Storage 向量数据库:
|
|
98
|
-
- 存储文件内容的分块和向量嵌入
|
|
99
|
-
- 每个文件被分割成大小为 chunk_size 的文本块
|
|
100
|
-
- 每个块都会生成向量嵌入,用于语义搜索
|
|
101
|
-
- 存储结构包含: 文件路径、内容块、原始内容、向量嵌入、修改时间
|
|
102
|
-
|
|
103
|
-
源代码处理流程:
|
|
104
|
-
在缓存更新过程中使用了两个关键函数:
|
|
105
|
-
|
|
106
|
-
1. process_file_in_multi_process: 在多进程环境中处理文件
|
|
107
|
-
- 参数: file_info (文件信息元组)
|
|
108
|
-
- 返回值: List[SourceCode] 或 None
|
|
109
|
-
- 用途: 在初始构建缓存时并行处理多个文件
|
|
110
|
-
|
|
111
|
-
2. process_file_local: 在当前进程中处理单个文件
|
|
112
|
-
- 参数: file_path (文件路径)
|
|
113
|
-
- 返回值: List[SourceCode] 或 None
|
|
114
|
-
- 用途: 在检测到文件更新时处理单个文件
|
|
115
|
-
|
|
116
|
-
文件处理后,会:
|
|
117
|
-
1. 更新内存中的缓存 (self.cache)
|
|
118
|
-
2. 将缓存持久化到本地文件
|
|
119
|
-
3. 将内容分块并更新到 Byzer Storage 向量数据库
|
|
120
|
-
|
|
121
|
-
更新机制:
|
|
122
|
-
- 通过单独的线程异步处理文件变更
|
|
123
|
-
- 使用 MD5 哈希值检测文件是否发生变化
|
|
124
|
-
- 支持文件添加、更新和删除事件
|
|
125
|
-
- 使用向量数据库进行语义检索,支持相似度搜索
|
|
126
|
-
|
|
127
|
-
与 LocalByzerStorageCache 的区别:
|
|
128
|
-
- 使用云端 ByzerStorage 而非本地存储
|
|
129
|
-
- 适用于需要远程访问和共享索引的场景
|
|
130
|
-
- 支持大规模分布式检索和更高级的查询功能
|
|
131
74
|
"""
|
|
132
75
|
self.path = path
|
|
133
76
|
self.ignore_spec = ignore_spec
|
|
134
77
|
self.required_exts = required_exts
|
|
135
78
|
self.extra_params = extra_params
|
|
79
|
+
self.args = args
|
|
80
|
+
self.llm = llm
|
|
136
81
|
self.rag_build_name = extra_params.rag_build_name
|
|
137
82
|
self.storage = ByzerStorage("byzerai_store", "rag", self.rag_build_name)
|
|
138
83
|
self.queue = []
|
|
@@ -150,16 +95,20 @@ class ByzerStorageCache(BaseCacheManager):
|
|
|
150
95
|
self.cache_file = os.path.join(self.cache_dir, "byzer_storage_speedup.jsonl")
|
|
151
96
|
self.cache: Dict[str, CacheItem] = {}
|
|
152
97
|
|
|
98
|
+
# 创建缓存目录
|
|
99
|
+
if not os.path.exists(self.cache_dir):
|
|
100
|
+
os.makedirs(self.cache_dir)
|
|
101
|
+
|
|
102
|
+
# failed files support
|
|
103
|
+
self.failed_files_path = os.path.join(self.cache_dir, "failed_files.json")
|
|
104
|
+
self.failed_files = load_failed_files(self.failed_files_path)
|
|
105
|
+
|
|
153
106
|
self.lock = threading.Lock()
|
|
154
107
|
self.stop_event = threading.Event()
|
|
155
108
|
self.thread = threading.Thread(target=self.process_queue)
|
|
156
109
|
self.thread.daemon = True
|
|
157
110
|
self.thread.start()
|
|
158
111
|
|
|
159
|
-
# 创建缓存目录
|
|
160
|
-
if not os.path.exists(self.cache_dir):
|
|
161
|
-
os.makedirs(self.cache_dir)
|
|
162
|
-
|
|
163
112
|
# 加载缓存
|
|
164
113
|
self.cache = self._load_cache()
|
|
165
114
|
|
|
@@ -455,6 +404,10 @@ class ByzerStorageCache(BaseCacheManager):
|
|
|
455
404
|
for item in file_list.file_paths:
|
|
456
405
|
logger.info(f"[QUEUE PROCESSING] Processing file deletion: {item}")
|
|
457
406
|
del self.cache[item]
|
|
407
|
+
# remove from failed files if present
|
|
408
|
+
if item in self.failed_files:
|
|
409
|
+
self.failed_files.remove(item)
|
|
410
|
+
save_failed_files(self.failed_files_path, self.failed_files)
|
|
458
411
|
# Create a temporary FileInfo object
|
|
459
412
|
file_info = FileInfo(file_path=item, relative_path="", modify_time=0, file_md5="")
|
|
460
413
|
self.update_storage(file_info, is_delete=True)
|
|
@@ -463,16 +416,29 @@ class ByzerStorageCache(BaseCacheManager):
|
|
|
463
416
|
logger.info(f"[QUEUE PROCESSING] Processing add/update event, total files: {len(file_list.file_infos)}")
|
|
464
417
|
for file_info in file_list.file_infos:
|
|
465
418
|
logger.info(f"[QUEUE PROCESSING] Processing file update: {file_info.file_path}")
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
419
|
+
try:
|
|
420
|
+
content = process_file_local(self.fileinfo_to_tuple(file_info))
|
|
421
|
+
if content:
|
|
422
|
+
self.cache[file_info.file_path] = CacheItem(
|
|
423
|
+
file_path=file_info.file_path,
|
|
424
|
+
relative_path=file_info.relative_path,
|
|
425
|
+
content=[c.model_dump() for c in content],
|
|
426
|
+
modify_time=file_info.modify_time,
|
|
427
|
+
md5=file_info.file_md5,
|
|
428
|
+
)
|
|
429
|
+
self.update_storage(file_info, is_delete=False)
|
|
430
|
+
# remove from failed files if present
|
|
431
|
+
if file_info.file_path in self.failed_files:
|
|
432
|
+
self.failed_files.remove(file_info.file_path)
|
|
433
|
+
save_failed_files(self.failed_files_path, self.failed_files)
|
|
434
|
+
else:
|
|
435
|
+
logger.warning(f"Empty result for file: {file_info.file_path}, treat as parse failed, skipping cache update")
|
|
436
|
+
self.failed_files.add(file_info.file_path)
|
|
437
|
+
save_failed_files(self.failed_files_path, self.failed_files)
|
|
438
|
+
except Exception as e:
|
|
439
|
+
logger.error(f"Error in process_queue: {e}")
|
|
440
|
+
self.failed_files.add(file_info.file_path)
|
|
441
|
+
save_failed_files(self.failed_files_path, self.failed_files)
|
|
476
442
|
self.write_cache()
|
|
477
443
|
|
|
478
444
|
elapsed = time.time() - start_time
|
|
@@ -486,6 +452,10 @@ class ByzerStorageCache(BaseCacheManager):
|
|
|
486
452
|
current_files = set()
|
|
487
453
|
for file_info in self.get_all_files():
|
|
488
454
|
current_files.add(file_info.file_path)
|
|
455
|
+
# skip failed files
|
|
456
|
+
if file_info.file_path in self.failed_files:
|
|
457
|
+
logger.info(f"文件 {file_info.file_path} 之前解析失败,跳过此次更新")
|
|
458
|
+
continue
|
|
489
459
|
if (
|
|
490
460
|
file_info.file_path not in self.cache
|
|
491
461
|
or self.cache[file_info.file_path].md5 != file_info.file_md5
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
from loguru import logger
|
|
5
|
+
|
|
6
|
+
def load_failed_files(failed_files_path: str) -> set:
|
|
7
|
+
"""
|
|
8
|
+
Load the set of failed file paths from a JSON file.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
failed_files_path: Path to the JSON file storing failed files.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
A set of failed file paths.
|
|
15
|
+
"""
|
|
16
|
+
directory = os.path.dirname(failed_files_path)
|
|
17
|
+
if not os.path.exists(directory):
|
|
18
|
+
os.makedirs(directory, exist_ok=True)
|
|
19
|
+
if os.path.exists(failed_files_path):
|
|
20
|
+
try:
|
|
21
|
+
with open(failed_files_path, "r", encoding="utf-8") as f:
|
|
22
|
+
return set(json.load(f))
|
|
23
|
+
except Exception:
|
|
24
|
+
return set()
|
|
25
|
+
return set()
|
|
26
|
+
|
|
27
|
+
def save_failed_files(failed_files_path: str, failed_files: set) -> None:
|
|
28
|
+
"""
|
|
29
|
+
Save the set of failed file paths to a JSON file.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
failed_files_path: Path to the JSON file.
|
|
33
|
+
failed_files: A set of failed file paths.
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
with open(failed_files_path, "w", encoding="utf-8") as f:
|
|
37
|
+
json.dump(list(failed_files), f, ensure_ascii=False, indent=2)
|
|
38
|
+
except Exception as e:
|
|
39
|
+
logger.error(f"Error saving failed files list: {e}")
|