auto-coder 0.1.364__py3-none-any.whl → 0.1.365__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.364.dist-info → auto_coder-0.1.365.dist-info}/METADATA +1 -1
- {auto_coder-0.1.364.dist-info → auto_coder-0.1.365.dist-info}/RECORD +19 -18
- autocoder/auto_coder.py +46 -2
- autocoder/common/__init__.py +3 -0
- autocoder/common/file_checkpoint/conversation_checkpoint.py +182 -0
- autocoder/common/file_checkpoint/manager.py +208 -1
- autocoder/common/utils_code_auto_generate.py +2 -1
- autocoder/common/v2/agent/agentic_edit.py +291 -92
- autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +83 -43
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +115 -28
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +169 -61
- autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +101 -56
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +95 -76
- autocoder/common/v2/agent/agentic_edit_types.py +4 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.364.dist-info → auto_coder-0.1.365.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.364.dist-info → auto_coder-0.1.365.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.364.dist-info → auto_coder-0.1.365.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.364.dist-info → auto_coder-0.1.365.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
autocoder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
autocoder/auto_coder.py,sha256=
|
|
2
|
+
autocoder/auto_coder.py,sha256=7602L3tG0JErNxh8vkLAmGUgv2c-DGPzPCkmWIQt9bs,69757
|
|
3
3
|
autocoder/auto_coder_lang.py,sha256=Rtupq6N3_HT7JRhDKdgCBcwRaiAnyCOR_Gsp4jUomrI,3229
|
|
4
4
|
autocoder/auto_coder_rag.py,sha256=ru5o86IaKylyVRlVORmnrdf3Q1To2eWi2KLdT9FMW0k,37580
|
|
5
5
|
autocoder/auto_coder_rag_client_mcp.py,sha256=QRxUbjc6A8UmDMQ8lXgZkjgqtq3lgKYeatJbDY6rSo0,6270
|
|
@@ -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=Gu50IATQtZtgEir1PpCfwgH6o4ygw6XqqbQRj3lx5dU,13798
|
|
16
16
|
autocoder/run_context.py,sha256=IUfSO6_gp2Wt1blFWAmOpN0b0nDrTTk4LmtCYUBIoro,1643
|
|
17
|
-
autocoder/version.py,sha256=
|
|
17
|
+
autocoder/version.py,sha256=IcO5L1bOdV3qLFshmlEIP9ycK_uaklkhdNmRNqLG91g,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
|
|
@@ -78,7 +78,7 @@ autocoder/commands/auto_web.py,sha256=K0Gv7lil5UqmExr_sAJNOcwNxw_q1vhvre1jjQ7tA3
|
|
|
78
78
|
autocoder/commands/tools.py,sha256=IX_zx5mWAvQDED7wUHTqNtrCmLNo9ztFV1aZ6AflY4o,34292
|
|
79
79
|
autocoder/common/JupyterClient.py,sha256=O-wi6pXeAEYhAY24kDa0BINrLYvKS6rKyWe98pDClS0,2816
|
|
80
80
|
autocoder/common/ShellClient.py,sha256=fM1q8t_XMSbLBl2zkCNC2J9xuyKN3eXzGm6hHhqL2WY,2286
|
|
81
|
-
autocoder/common/__init__.py,sha256=
|
|
81
|
+
autocoder/common/__init__.py,sha256=qZWLTeEjimIGDUvbAq_QV1o5jgMiD1k4TQcoU0-wOo8,14887
|
|
82
82
|
autocoder/common/action_yml_file_manager.py,sha256=DdF5P1R_B_chCnnqoA2IgogakWLZk_nItiJZUfX0_Wo,17857
|
|
83
83
|
autocoder/common/anything2images.py,sha256=0ILBbWzY02M-CiWB-vzuomb_J1hVdxRcenAfIrAXq9M,25283
|
|
84
84
|
autocoder/common/anything2img.py,sha256=iZQmg8srXlD7N5uGl5b_ONKJMBjYoW8kPmokkG6ISF0,10118
|
|
@@ -141,7 +141,7 @@ autocoder/common/test_run_cmd.py,sha256=0piPrNnxTPS8vJRnsVH6-lgB5zeLaXSRY5pPH13H
|
|
|
141
141
|
autocoder/common/text.py,sha256=KGRQq314GHBmY4MWG8ossRoQi1_DTotvhxchpn78c-k,1003
|
|
142
142
|
autocoder/common/token_cost_caculate.py,sha256=MSWJtl7YpQSUt-gFQoqUcJMblyPqHXe2ZioiZOFkV80,10085
|
|
143
143
|
autocoder/common/types.py,sha256=Cw_4RH-rGmAgQE-Ck69maMAMqlPCDA4Yj37QmuUY0mQ,713
|
|
144
|
-
autocoder/common/utils_code_auto_generate.py,sha256=
|
|
144
|
+
autocoder/common/utils_code_auto_generate.py,sha256=zol3E5VQgriGzpR0p51yYnx8unthyurYM83QIudkxeI,4592
|
|
145
145
|
autocoder/common/conversations/__init__.py,sha256=xGZeOFrDsgg2fkPK1zmvYBDhAyX66FtgOcZaxhYKJXU,1338
|
|
146
146
|
autocoder/common/conversations/compatibility.py,sha256=WuBXB4-dw5X9LUMsB16VWbihvRZQ1tT99m6zuBwDfqE,9606
|
|
147
147
|
autocoder/common/conversations/conversation_manager.py,sha256=ZhuhfSdOTncqgy3nHPoEU7Cg0dCsSl-VPcvLbUlL2Tk,18295
|
|
@@ -151,8 +151,9 @@ autocoder/common/directory_cache/cache.py,sha256=Jknygb_U6DkF04_SX04IwsOcQdd-2QQ
|
|
|
151
151
|
autocoder/common/directory_cache/test_cache.py,sha256=0iQkHaZQPhZBwSS6dwK_je93QMLbYGY0BYrTSt45Cao,6610
|
|
152
152
|
autocoder/common/file_checkpoint/__init__.py,sha256=qwoM0tIU-IMr-zGVCMN8yZtmz0NWpRe027l8z8sCjjk,847
|
|
153
153
|
autocoder/common/file_checkpoint/backup.py,sha256=JO26vOG9k7d8b5jgT24PdccSrTuPqKghp1nz5cmjSiE,8813
|
|
154
|
+
autocoder/common/file_checkpoint/conversation_checkpoint.py,sha256=SFSTjA0fF5rsHlYdLQ-Dr9dfDl5JihndhjeqhN3OuMY,6322
|
|
154
155
|
autocoder/common/file_checkpoint/examples.py,sha256=HTik8E0ddvKjEPGwzizWJBHIP9URrWRyRUOKSjYRUG8,6272
|
|
155
|
-
autocoder/common/file_checkpoint/manager.py,sha256=
|
|
156
|
+
autocoder/common/file_checkpoint/manager.py,sha256=mkEPURbF109qnCwXOikNmJI8INhdfCwBW1v6sRB7ubY,23757
|
|
156
157
|
autocoder/common/file_checkpoint/models.py,sha256=dcZL2QGnklsa_BV_QY81fH-H5hYfhelXrH6GSrubMZo,4730
|
|
157
158
|
autocoder/common/file_checkpoint/store.py,sha256=dgQe-1O_gPJ3QU6tHihGRp0G2jgD2IDxQ-w9zM6Yq54,12920
|
|
158
159
|
autocoder/common/file_checkpoint/test_backup.py,sha256=Z9Y2RyGqxwKPNc7nW-2jtsMAYzqt0qZGzLoq3pn2zCI,8930
|
|
@@ -186,9 +187,9 @@ autocoder/common/v2/code_editblock_manager.py,sha256=DMwJw-FAM6VyaBQV3p4xespHpgZ
|
|
|
186
187
|
autocoder/common/v2/code_manager.py,sha256=C403bS-f6urixwitlKHcml-J03hci-UyNwHJOqBiY6Q,9182
|
|
187
188
|
autocoder/common/v2/code_strict_diff_manager.py,sha256=Bys7tFAq4G03R1zUZuxrszBTvP4QB96jIw2y5BDLyRM,9424
|
|
188
189
|
autocoder/common/v2/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
189
|
-
autocoder/common/v2/agent/agentic_edit.py,sha256=
|
|
190
|
+
autocoder/common/v2/agent/agentic_edit.py,sha256=Fx_T6YBWPa4tlTLY8dbecsd1n5a93dGE7b4gygmK56I,115043
|
|
190
191
|
autocoder/common/v2/agent/agentic_edit_conversation.py,sha256=pFgWPWHKhZ4J9EcFmIdiGsrSolTZuYcH1qkgKdD8nwk,7726
|
|
191
|
-
autocoder/common/v2/agent/agentic_edit_types.py,sha256=
|
|
192
|
+
autocoder/common/v2/agent/agentic_edit_types.py,sha256=nEcZc2MOZ_fQLaJX-YDha_x9Iim22ao4tykYM2iIy4k,4908
|
|
192
193
|
autocoder/common/v2/agent/agentic_tool_display.py,sha256=-a-JTQLc4q03E_rdIILKMI0B6DHN-5gcGlrqq-mBYK4,7239
|
|
193
194
|
autocoder/common/v2/agent/ignore_utils.py,sha256=gnUchRzKMLbUm_jvnKL-r-K9MWKPtt-6iiuzijY7Es0,1717
|
|
194
195
|
autocoder/common/v2/agent/agentic_edit_tools/__init__.py,sha256=RbPZZcZg_VnGssL577GxSyFrYrxQ_LopJ4G_-mY3z_Q,1337
|
|
@@ -197,16 +198,16 @@ autocoder/common/v2/agent/agentic_edit_tools/attempt_completion_tool_resolver.py
|
|
|
197
198
|
autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py,sha256=Zid2m1uZd-2wVFGc_n_KAViXZyNjbdLSpI5n7ut1RUQ,1036
|
|
198
199
|
autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py,sha256=sX00xzczfmyW6yPG3nMm0xO8p-WARQTiD4jcoUiTxsg,3844
|
|
199
200
|
autocoder/common/v2/agent/agentic_edit_tools/list_code_definition_names_tool_resolver.py,sha256=8QoMsADUDWliqiDt_dpguz31403syB8eeW0Pcw-qfb8,3842
|
|
200
|
-
autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py,sha256=
|
|
201
|
+
autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py,sha256=1tJX9RYRU0zRjKZMzFlZzKm-4R32CzRnZ0UQJj4pxAk,8074
|
|
201
202
|
autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py,sha256=dIdV12VuczHpHuHgx2B1j_3BZYc9PL0jfHCuBk9ryk8,2005
|
|
202
203
|
autocoder/common/v2/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py,sha256=lGT4_QYJK6Fa9f6HVSGo0cSsGK7qCsDYgJGUowNxPzk,1499
|
|
203
|
-
autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py,sha256=
|
|
204
|
-
autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py,sha256=
|
|
205
|
-
autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py,sha256=
|
|
204
|
+
autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py,sha256=6JhjM3VXV3BGelh1dNcdr-M5FoVPoqLkls1-y8ND8_c,6721
|
|
205
|
+
autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py,sha256=J-GOlaLMwEu83v9VuHIstTpRR_vgPMR9wP_AzoK173s,18009
|
|
206
|
+
autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py,sha256=VtDRDFJ2qzACy2jpD1ZNhywgCT3_iYUmGoUhHHmMX3o,9581
|
|
206
207
|
autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py,sha256=9eBo3WLkrr77iNotwIwVmH1ZL3UY0JQgLpdAIc9wTTM,6127
|
|
207
208
|
autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py,sha256=ZWRPsJny_My4UMzovrB8J2_x5N0rEW-xx3DVI-kDRFI,15870
|
|
208
209
|
autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py,sha256=wM2Xy4bcnD0TSLEmcM8rvvyyWenN5_KQnJMO6hJ8lTE,1716
|
|
209
|
-
autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py,sha256=
|
|
210
|
+
autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py,sha256=ISJ2jNz0Dfv1W7P2A_mYSM0vPdN0yW1w7Qqt6QjTk98,11170
|
|
210
211
|
autocoder/compilers/__init__.py,sha256=C0HOms70QA747XD0uZEMmGtRFcIPenohyqECNStv0Bw,1647
|
|
211
212
|
autocoder/compilers/base_compiler.py,sha256=dsTzMO4H_RoqWfE-SntIk2B52hWuvSlWVLtkdCbHgGs,3244
|
|
212
213
|
autocoder/compilers/compiler_config_api.py,sha256=QRSwWm_EX7jSeZ3dtQqM9HI__x5aZ7U0c3fHIW_2N48,13839
|
|
@@ -353,9 +354,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
353
354
|
autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
354
355
|
autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=t902pKxQ5xM7zgIHiAOsTPLwxhE6VuvXAqPy751S7fg,14096
|
|
355
356
|
autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
356
|
-
auto_coder-0.1.
|
|
357
|
-
auto_coder-0.1.
|
|
358
|
-
auto_coder-0.1.
|
|
359
|
-
auto_coder-0.1.
|
|
360
|
-
auto_coder-0.1.
|
|
361
|
-
auto_coder-0.1.
|
|
357
|
+
auto_coder-0.1.365.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
358
|
+
auto_coder-0.1.365.dist-info/METADATA,sha256=vo07pJAu6M2IMTWjRq9caGUXRwr88NMs05TIHDGUJKs,2775
|
|
359
|
+
auto_coder-0.1.365.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
360
|
+
auto_coder-0.1.365.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
|
|
361
|
+
auto_coder-0.1.365.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
|
|
362
|
+
auto_coder-0.1.365.dist-info/RECORD,,
|
autocoder/auto_coder.py
CHANGED
|
@@ -376,8 +376,8 @@ def main(input_args: Optional[List[str]] = None):
|
|
|
376
376
|
llm.setup_sub_client("inference_model", inference_model)
|
|
377
377
|
|
|
378
378
|
if args.index_filter_model:
|
|
379
|
-
|
|
380
|
-
|
|
379
|
+
model_name = args.index_filter_model.strip()
|
|
380
|
+
model_info = models_module.get_model_by_name(model_name)
|
|
381
381
|
index_filter_model = byzerllm.SimpleByzerLLM(default_model_name=model_name)
|
|
382
382
|
index_filter_model.deploy(
|
|
383
383
|
model_path="",
|
|
@@ -393,6 +393,41 @@ def main(input_args: Optional[List[str]] = None):
|
|
|
393
393
|
)
|
|
394
394
|
llm.setup_sub_client("index_filter_model", index_filter_model)
|
|
395
395
|
|
|
396
|
+
if args.context_prune_model:
|
|
397
|
+
model_name = args.context_prune_model.strip()
|
|
398
|
+
model_info = models_module.get_model_by_name(model_name)
|
|
399
|
+
context_prune_model = byzerllm.SimpleByzerLLM(default_model_name=model_name)
|
|
400
|
+
context_prune_model.deploy(
|
|
401
|
+
model_path="",
|
|
402
|
+
pretrained_model_type=model_info["model_type"],
|
|
403
|
+
udf_name=model_name,
|
|
404
|
+
infer_params={
|
|
405
|
+
"saas.base_url": model_info["base_url"],
|
|
406
|
+
"saas.api_key": model_info["api_key"],
|
|
407
|
+
"saas.model": model_info["model_name"],
|
|
408
|
+
"saas.is_reasoning": model_info["is_reasoning"],
|
|
409
|
+
"saas.max_output_tokens": model_info.get("max_output_tokens", 8096)
|
|
410
|
+
}
|
|
411
|
+
)
|
|
412
|
+
llm.setup_sub_client("context_prune_model", context_prune_model)
|
|
413
|
+
|
|
414
|
+
if args.conversation_prune_model:
|
|
415
|
+
model_name = args.conversation_prune_model.strip()
|
|
416
|
+
model_info = models_module.get_model_by_name(model_name)
|
|
417
|
+
conversation_prune_model = byzerllm.SimpleByzerLLM(default_model_name=model_name)
|
|
418
|
+
conversation_prune_model.deploy(
|
|
419
|
+
model_path="",
|
|
420
|
+
pretrained_model_type=model_info["model_type"],
|
|
421
|
+
udf_name=model_name,
|
|
422
|
+
infer_params={
|
|
423
|
+
"saas.base_url": model_info["base_url"],
|
|
424
|
+
"saas.api_key": model_info["api_key"],
|
|
425
|
+
"saas.model": model_info["model_name"],
|
|
426
|
+
"saas.is_reasoning": model_info["is_reasoning"],
|
|
427
|
+
"saas.max_output_tokens": model_info.get("max_output_tokens", 8096)
|
|
428
|
+
}
|
|
429
|
+
)
|
|
430
|
+
llm.setup_sub_client("conversation_prune_model", conversation_prune_model)
|
|
396
431
|
|
|
397
432
|
if args.product_mode == "pro":
|
|
398
433
|
if args.code_model:
|
|
@@ -437,6 +472,15 @@ def main(input_args: Optional[List[str]] = None):
|
|
|
437
472
|
index_filter_model.setup_default_model_name(args.index_filter_model)
|
|
438
473
|
llm.setup_sub_client("index_filter_model", index_filter_model)
|
|
439
474
|
|
|
475
|
+
if args.context_prune_model:
|
|
476
|
+
context_prune_model = byzerllm.ByzerLLM()
|
|
477
|
+
context_prune_model.setup_default_model_name(args.context_prune_model)
|
|
478
|
+
llm.setup_sub_client("context_prune_model", context_prune_model)
|
|
479
|
+
|
|
480
|
+
if args.conversation_prune_model:
|
|
481
|
+
conversation_prune_model = byzerllm.ByzerLLM()
|
|
482
|
+
conversation_prune_model.setup_default_model_name(args.conversation_prune_model)
|
|
483
|
+
llm.setup_sub_client("conversation_prune_model", conversation_prune_model)
|
|
440
484
|
|
|
441
485
|
if get_run_context().mode != RunMode.WEB and args.human_as_model:
|
|
442
486
|
|
autocoder/common/__init__.py
CHANGED
|
@@ -254,6 +254,8 @@ class AutoCoderArgs(pydantic.BaseModel):
|
|
|
254
254
|
emb_model: Optional[str] = ""
|
|
255
255
|
code_model: Optional[str] = ""
|
|
256
256
|
generate_rerank_model: Optional[str] = ""
|
|
257
|
+
context_prune_model: Optional[str] = ""
|
|
258
|
+
conversation_prune_model: Optional[str] = ""
|
|
257
259
|
inference_model: Optional[str] = ""
|
|
258
260
|
system_prompt: Optional[str] = ""
|
|
259
261
|
planner_model: Optional[str] = ""
|
|
@@ -399,6 +401,7 @@ class AutoCoderArgs(pydantic.BaseModel):
|
|
|
399
401
|
|
|
400
402
|
context_prune_strategy: Optional[str] = "extract"
|
|
401
403
|
context_prune: Optional[bool] = True
|
|
404
|
+
context_prune_safe_zone_tokens: Optional[int] = 32*1024
|
|
402
405
|
context_prune_sliding_window_size: Optional[int] = 1000
|
|
403
406
|
context_prune_sliding_window_overlap: Optional[int] = 100
|
|
404
407
|
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import time
|
|
4
|
+
import uuid
|
|
5
|
+
from typing import List, Dict, Any, Optional
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
from loguru import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ConversationCheckpoint(BaseModel):
|
|
11
|
+
"""对话检查点,用于保存特定时刻的对话状态"""
|
|
12
|
+
|
|
13
|
+
checkpoint_id: str # 检查点ID,与变更组ID对应
|
|
14
|
+
timestamp: float # 创建时间戳
|
|
15
|
+
conversations: List[Dict[str, Any]] # 对话历史
|
|
16
|
+
metadata: Optional[Dict[str, Any]] = None # 元数据,可包含额外信息
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ConversationCheckpointStore:
|
|
20
|
+
"""对话检查点存储管理器"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, store_dir: Optional[str] = None, max_history: int = 50):
|
|
23
|
+
"""
|
|
24
|
+
初始化对话检查点存储
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
store_dir: 存储目录,默认为用户主目录下的.autocoder/conversation_checkpoints
|
|
28
|
+
max_history: 最大保存的历史版本数量
|
|
29
|
+
"""
|
|
30
|
+
if store_dir is None:
|
|
31
|
+
home_dir = os.path.expanduser("~")
|
|
32
|
+
store_dir = os.path.join(home_dir, ".autocoder", "conversation_checkpoints")
|
|
33
|
+
|
|
34
|
+
self.store_dir = os.path.abspath(store_dir)
|
|
35
|
+
self.max_history = max_history
|
|
36
|
+
|
|
37
|
+
# 确保存储目录存在
|
|
38
|
+
os.makedirs(self.store_dir, exist_ok=True)
|
|
39
|
+
logger.info(f"对话检查点存储目录: {self.store_dir}")
|
|
40
|
+
|
|
41
|
+
def save_checkpoint(self, checkpoint: ConversationCheckpoint) -> str:
|
|
42
|
+
"""
|
|
43
|
+
保存对话检查点
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
checkpoint: 对话检查点对象
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
str: 检查点ID
|
|
50
|
+
"""
|
|
51
|
+
# 确保检查点有ID
|
|
52
|
+
if not checkpoint.checkpoint_id:
|
|
53
|
+
checkpoint.checkpoint_id = str(uuid.uuid4())
|
|
54
|
+
|
|
55
|
+
# 确保时间戳存在
|
|
56
|
+
if not checkpoint.timestamp:
|
|
57
|
+
checkpoint.timestamp = time.time()
|
|
58
|
+
|
|
59
|
+
# 构建文件路径
|
|
60
|
+
file_path = os.path.join(self.store_dir, f"{checkpoint.checkpoint_id}.json")
|
|
61
|
+
|
|
62
|
+
# 保存为JSON文件
|
|
63
|
+
try:
|
|
64
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
65
|
+
f.write(checkpoint.json(ensure_ascii=False, indent=2))
|
|
66
|
+
|
|
67
|
+
logger.info(f"已保存对话检查点: {checkpoint.checkpoint_id}")
|
|
68
|
+
|
|
69
|
+
# 检查并清理过期的检查点
|
|
70
|
+
self._cleanup_old_checkpoints()
|
|
71
|
+
|
|
72
|
+
return checkpoint.checkpoint_id
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.error(f"保存对话检查点失败: {str(e)}")
|
|
75
|
+
return ""
|
|
76
|
+
|
|
77
|
+
def get_checkpoint(self, checkpoint_id: str) -> Optional[ConversationCheckpoint]:
|
|
78
|
+
"""
|
|
79
|
+
获取指定ID的对话检查点
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
checkpoint_id: 检查点ID
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Optional[ConversationCheckpoint]: 对话检查点对象,如果不存在则返回None
|
|
86
|
+
"""
|
|
87
|
+
file_path = os.path.join(self.store_dir, f"{checkpoint_id}.json")
|
|
88
|
+
|
|
89
|
+
if not os.path.exists(file_path):
|
|
90
|
+
logger.warning(f"对话检查点不存在: {checkpoint_id}")
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
95
|
+
data = json.load(f)
|
|
96
|
+
|
|
97
|
+
return ConversationCheckpoint(**data)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
logger.error(f"读取对话检查点失败: {str(e)}")
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
def get_latest_checkpoint(self) -> Optional[ConversationCheckpoint]:
|
|
103
|
+
"""
|
|
104
|
+
获取最新的对话检查点
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Optional[ConversationCheckpoint]: 最新的对话检查点对象,如果不存在则返回None
|
|
108
|
+
"""
|
|
109
|
+
checkpoints = self._get_all_checkpoints()
|
|
110
|
+
|
|
111
|
+
if not checkpoints:
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
# 按时间戳降序排序
|
|
115
|
+
checkpoints.sort(key=lambda x: x.timestamp, reverse=True)
|
|
116
|
+
|
|
117
|
+
return checkpoints[0] if checkpoints else None
|
|
118
|
+
|
|
119
|
+
def delete_checkpoint(self, checkpoint_id: str) -> bool:
|
|
120
|
+
"""
|
|
121
|
+
删除指定的对话检查点
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
checkpoint_id: 检查点ID
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
bool: 是否成功删除
|
|
128
|
+
"""
|
|
129
|
+
file_path = os.path.join(self.store_dir, f"{checkpoint_id}.json")
|
|
130
|
+
|
|
131
|
+
if not os.path.exists(file_path):
|
|
132
|
+
logger.warning(f"要删除的对话检查点不存在: {checkpoint_id}")
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
os.remove(file_path)
|
|
137
|
+
logger.info(f"已删除对话检查点: {checkpoint_id}")
|
|
138
|
+
return True
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.error(f"删除对话检查点失败: {str(e)}")
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
def _get_all_checkpoints(self) -> List[ConversationCheckpoint]:
|
|
144
|
+
"""
|
|
145
|
+
获取所有对话检查点
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
List[ConversationCheckpoint]: 对话检查点列表
|
|
149
|
+
"""
|
|
150
|
+
checkpoints = []
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
for filename in os.listdir(self.store_dir):
|
|
154
|
+
if filename.endswith('.json'):
|
|
155
|
+
file_path = os.path.join(self.store_dir, filename)
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
159
|
+
data = json.load(f)
|
|
160
|
+
|
|
161
|
+
checkpoint = ConversationCheckpoint(**data)
|
|
162
|
+
checkpoints.append(checkpoint)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.error(f"读取对话检查点文件失败 {filename}: {str(e)}")
|
|
165
|
+
except Exception as e:
|
|
166
|
+
logger.error(f"获取所有对话检查点失败: {str(e)}")
|
|
167
|
+
|
|
168
|
+
return checkpoints
|
|
169
|
+
|
|
170
|
+
def _cleanup_old_checkpoints(self):
|
|
171
|
+
"""清理过期的检查点,保持历史记录数量在限制范围内"""
|
|
172
|
+
checkpoints = self._get_all_checkpoints()
|
|
173
|
+
|
|
174
|
+
if len(checkpoints) <= self.max_history:
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
# 按时间戳降序排序
|
|
178
|
+
checkpoints.sort(key=lambda x: x.timestamp, reverse=True)
|
|
179
|
+
|
|
180
|
+
# 删除超出限制的旧检查点
|
|
181
|
+
for checkpoint in checkpoints[self.max_history:]:
|
|
182
|
+
self.delete_checkpoint(checkpoint.checkpoint_id)
|
|
@@ -16,6 +16,7 @@ from autocoder.common.file_checkpoint.models import (
|
|
|
16
16
|
)
|
|
17
17
|
from autocoder.common.file_checkpoint.backup import FileBackupManager
|
|
18
18
|
from autocoder.common.file_checkpoint.store import FileChangeStore
|
|
19
|
+
from autocoder.common.file_checkpoint.conversation_checkpoint import ConversationCheckpointStore,ConversationCheckpoint
|
|
19
20
|
|
|
20
21
|
logger = logging.getLogger(__name__)
|
|
21
22
|
|
|
@@ -24,7 +25,8 @@ class FileChangeManager:
|
|
|
24
25
|
"""文件变更管理器,提供高层次的API接口"""
|
|
25
26
|
|
|
26
27
|
def __init__(self, project_dir: str, backup_dir: Optional[str] = None,
|
|
27
|
-
store_dir: Optional[str] = None, max_history: int = 50
|
|
28
|
+
store_dir: Optional[str] = None, max_history: int = 50,
|
|
29
|
+
conversation_store_dir: Optional[str] = None):
|
|
28
30
|
"""
|
|
29
31
|
初始化文件变更管理器
|
|
30
32
|
|
|
@@ -33,10 +35,61 @@ class FileChangeManager:
|
|
|
33
35
|
backup_dir: 备份文件存储目录,默认为用户主目录下的.autocoder/backups
|
|
34
36
|
store_dir: 变更记录存储目录,默认为用户主目录下的.autocoder/changes
|
|
35
37
|
max_history: 最大保存的历史版本数量
|
|
38
|
+
conversation_store_dir: 对话检查点存储目录
|
|
36
39
|
"""
|
|
37
40
|
self.project_dir = os.path.abspath(project_dir)
|
|
38
41
|
self.backup_manager = FileBackupManager(backup_dir)
|
|
39
42
|
self.change_store = FileChangeStore(store_dir, max_history)
|
|
43
|
+
|
|
44
|
+
# 初始化对话检查点存储
|
|
45
|
+
if conversation_store_dir is None and store_dir is not None:
|
|
46
|
+
# 默认在变更记录存储目录的同级目录创建 conversation_checkpoints 目录
|
|
47
|
+
parent_dir = os.path.dirname(store_dir)
|
|
48
|
+
conversation_store_dir = os.path.join(parent_dir, "conversation_checkpoints")
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
self.conversation_store = ConversationCheckpointStore(conversation_store_dir, max_history)
|
|
52
|
+
logger.info(f"对话检查点存储初始化成功: {conversation_store_dir}")
|
|
53
|
+
except ImportError as e:
|
|
54
|
+
logger.warning(f"对话检查点存储初始化失败: {str(e)}")
|
|
55
|
+
self.conversation_store = None
|
|
56
|
+
|
|
57
|
+
def apply_changes_with_conversation(self, changes: Dict[str, FileChange],
|
|
58
|
+
conversations: List[Dict[str, Any]],
|
|
59
|
+
change_group_id: Optional[str] = None,
|
|
60
|
+
metadata: Optional[Dict[str, Any]] = None) -> ApplyResult:
|
|
61
|
+
"""
|
|
62
|
+
应用文件变更并保存对话状态
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
changes: 文件变更字典,格式为 {file_path: FileChange}
|
|
66
|
+
conversations: 当前对话历史
|
|
67
|
+
change_group_id: 变更组ID,用于将相关变更归为一组
|
|
68
|
+
metadata: 元数据,可包含额外信息
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
ApplyResult: 应用结果对象
|
|
72
|
+
"""
|
|
73
|
+
# 应用文件变更
|
|
74
|
+
result = self.apply_changes(changes, change_group_id)
|
|
75
|
+
|
|
76
|
+
if result.success and self.conversation_store is not None:
|
|
77
|
+
try:
|
|
78
|
+
|
|
79
|
+
# 创建并保存对话检查点
|
|
80
|
+
checkpoint_id = change_group_id or result.change_ids[0] if result.change_ids else str(uuid.uuid4())
|
|
81
|
+
checkpoint = ConversationCheckpoint(
|
|
82
|
+
checkpoint_id=checkpoint_id,
|
|
83
|
+
timestamp=time.time(),
|
|
84
|
+
conversations=conversations,
|
|
85
|
+
metadata=metadata
|
|
86
|
+
)
|
|
87
|
+
self.conversation_store.save_checkpoint(checkpoint)
|
|
88
|
+
logger.info(f"已保存对话检查点: {checkpoint_id}")
|
|
89
|
+
except Exception as e:
|
|
90
|
+
logger.error(f"保存对话检查点失败: {str(e)}")
|
|
91
|
+
|
|
92
|
+
return result
|
|
40
93
|
|
|
41
94
|
def apply_changes(self, changes: Dict[str, FileChange], change_group_id: Optional[str] = None) -> ApplyResult:
|
|
42
95
|
"""
|
|
@@ -152,6 +205,27 @@ class FileChangeManager:
|
|
|
152
205
|
|
|
153
206
|
return diff_results
|
|
154
207
|
|
|
208
|
+
def undo_last_change_with_conversation(self) -> Tuple[UndoResult, Optional[ConversationCheckpoint]]:
|
|
209
|
+
"""
|
|
210
|
+
撤销最近的一次变更并恢复对话状态
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Tuple[UndoResult, Optional[ConversationCheckpoint]]: 撤销结果和恢复的对话检查点
|
|
214
|
+
"""
|
|
215
|
+
# 获取最近的变更记录
|
|
216
|
+
latest_changes = self.change_store.get_latest_changes(limit=1)
|
|
217
|
+
if not latest_changes:
|
|
218
|
+
return UndoResult(success=False, errors={"general": "没有找到最近的变更记录"}), None
|
|
219
|
+
|
|
220
|
+
latest_change = latest_changes[0]
|
|
221
|
+
|
|
222
|
+
# 如果最近的变更属于一个组,撤销整个组
|
|
223
|
+
if latest_change.group_id:
|
|
224
|
+
return self.undo_change_group_with_conversation(latest_change.group_id)
|
|
225
|
+
else:
|
|
226
|
+
# 否则只撤销这一个变更
|
|
227
|
+
return self.undo_change_with_conversation(latest_change.change_id)
|
|
228
|
+
|
|
155
229
|
def undo_last_change(self) -> UndoResult:
|
|
156
230
|
"""
|
|
157
231
|
撤销最近的一次变更
|
|
@@ -173,6 +247,39 @@ class FileChangeManager:
|
|
|
173
247
|
# 否则只撤销这一个变更
|
|
174
248
|
return self.undo_change(latest_change.change_id)
|
|
175
249
|
|
|
250
|
+
def undo_change_with_conversation(self, change_id: str) -> Tuple[UndoResult, Optional[ConversationCheckpoint]]:
|
|
251
|
+
"""
|
|
252
|
+
撤销指定的变更并恢复对话状态
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
change_id: 变更记录ID
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Tuple[UndoResult, Optional[ConversationCheckpoint]]: 撤销结果和恢复的对话检查点
|
|
259
|
+
"""
|
|
260
|
+
# 获取变更记录
|
|
261
|
+
change_record = self.change_store.get_change(change_id)
|
|
262
|
+
if change_record is None:
|
|
263
|
+
return UndoResult(success=False, errors={"general": f"变更记录 {change_id} 不存在"}), None
|
|
264
|
+
|
|
265
|
+
# 获取关联的对话检查点
|
|
266
|
+
checkpoint = None
|
|
267
|
+
checkpoint_id = change_record.group_id or change_id
|
|
268
|
+
if self.conversation_store is not None:
|
|
269
|
+
try:
|
|
270
|
+
checkpoint = self.conversation_store.get_checkpoint(checkpoint_id)
|
|
271
|
+
if checkpoint:
|
|
272
|
+
logger.info(f"找到关联的对话检查点: {checkpoint_id}")
|
|
273
|
+
else:
|
|
274
|
+
logger.info(f"未找到关联的对话检查点: {checkpoint_id}")
|
|
275
|
+
except Exception as e:
|
|
276
|
+
logger.error(f"获取对话检查点失败: {str(e)}")
|
|
277
|
+
|
|
278
|
+
# 撤销文件变更
|
|
279
|
+
undo_result = self.undo_change(change_id)
|
|
280
|
+
|
|
281
|
+
return undo_result, checkpoint
|
|
282
|
+
|
|
176
283
|
def undo_change(self, change_id: str) -> UndoResult:
|
|
177
284
|
"""
|
|
178
285
|
撤销指定的变更
|
|
@@ -234,6 +341,38 @@ class FileChangeManager:
|
|
|
234
341
|
|
|
235
342
|
return result
|
|
236
343
|
|
|
344
|
+
def undo_change_group_with_conversation(self, group_id: str) -> Tuple[UndoResult, Optional[ConversationCheckpoint]]:
|
|
345
|
+
"""
|
|
346
|
+
撤销指定组的所有变更并恢复对话状态
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
group_id: 变更组ID
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Tuple[UndoResult, Optional[ConversationCheckpoint]]: 撤销结果和恢复的对话检查点
|
|
353
|
+
"""
|
|
354
|
+
# 获取组内的所有变更记录
|
|
355
|
+
changes = self.change_store.get_changes_by_group(group_id)
|
|
356
|
+
if not changes:
|
|
357
|
+
return UndoResult(success=False, errors={"general": f"变更组 {group_id} 不存在或为空"}), None
|
|
358
|
+
|
|
359
|
+
# 获取关联的对话检查点
|
|
360
|
+
checkpoint = None
|
|
361
|
+
if self.conversation_store is not None:
|
|
362
|
+
try:
|
|
363
|
+
checkpoint = self.conversation_store.get_checkpoint(group_id)
|
|
364
|
+
if checkpoint:
|
|
365
|
+
logger.info(f"找到关联的对话检查点: {group_id}")
|
|
366
|
+
else:
|
|
367
|
+
logger.info(f"未找到关联的对话检查点: {group_id}")
|
|
368
|
+
except Exception as e:
|
|
369
|
+
logger.error(f"获取对话检查点失败: {str(e)}")
|
|
370
|
+
|
|
371
|
+
# 撤销文件变更
|
|
372
|
+
undo_result = self.undo_change_group(group_id)
|
|
373
|
+
|
|
374
|
+
return undo_result, checkpoint
|
|
375
|
+
|
|
237
376
|
def undo_change_group(self, group_id: str) -> UndoResult:
|
|
238
377
|
"""
|
|
239
378
|
撤销指定组的所有变更
|
|
@@ -265,6 +404,39 @@ class FileChangeManager:
|
|
|
265
404
|
|
|
266
405
|
return result
|
|
267
406
|
|
|
407
|
+
def undo_to_version_with_conversation(self, version_id: str) -> Tuple[UndoResult, Optional[ConversationCheckpoint]]:
|
|
408
|
+
"""
|
|
409
|
+
撤销到指定的历史版本并恢复对话状态
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
version_id: 目标版本ID(变更记录ID)
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
Tuple[UndoResult, Optional[ConversationCheckpoint]]: 撤销结果和恢复的对话检查点
|
|
416
|
+
"""
|
|
417
|
+
# 获取目标版本的变更记录
|
|
418
|
+
target_change = self.change_store.get_change(version_id)
|
|
419
|
+
if target_change is None:
|
|
420
|
+
return UndoResult(success=False, errors={"general": f"变更记录 {version_id} 不存在"}), None
|
|
421
|
+
|
|
422
|
+
# 获取关联的对话检查点
|
|
423
|
+
checkpoint = None
|
|
424
|
+
checkpoint_id = target_change.group_id or version_id
|
|
425
|
+
if self.conversation_store is not None:
|
|
426
|
+
try:
|
|
427
|
+
checkpoint = self.conversation_store.get_checkpoint(checkpoint_id)
|
|
428
|
+
if checkpoint:
|
|
429
|
+
logger.info(f"找到关联的对话检查点: {checkpoint_id}")
|
|
430
|
+
else:
|
|
431
|
+
logger.info(f"未找到关联的对话检查点: {checkpoint_id}")
|
|
432
|
+
except Exception as e:
|
|
433
|
+
logger.error(f"获取对话检查点失败: {str(e)}")
|
|
434
|
+
|
|
435
|
+
# 撤销文件变更
|
|
436
|
+
undo_result = self.undo_to_version(version_id)
|
|
437
|
+
|
|
438
|
+
return undo_result, checkpoint
|
|
439
|
+
|
|
268
440
|
def undo_to_version(self, version_id: str) -> UndoResult:
|
|
269
441
|
"""
|
|
270
442
|
撤销到指定的历史版本
|
|
@@ -359,6 +531,41 @@ class FileChangeManager:
|
|
|
359
531
|
"""
|
|
360
532
|
return self.change_store.get_change_groups(limit)
|
|
361
533
|
|
|
534
|
+
def get_available_checkpoints(self, limit: int = 10) -> List[Dict[str, Any]]:
|
|
535
|
+
"""
|
|
536
|
+
获取可用的检查点列表,包含对话状态信息
|
|
537
|
+
|
|
538
|
+
Args:
|
|
539
|
+
limit: 返回的检查点数量限制
|
|
540
|
+
|
|
541
|
+
Returns:
|
|
542
|
+
List[Dict[str, Any]]: 检查点信息列表
|
|
543
|
+
"""
|
|
544
|
+
# 获取变更组列表
|
|
545
|
+
change_groups = self.get_change_groups(limit)
|
|
546
|
+
|
|
547
|
+
# 构建检查点信息
|
|
548
|
+
checkpoints = []
|
|
549
|
+
for group_id, timestamp, count in change_groups:
|
|
550
|
+
has_conversation = False
|
|
551
|
+
|
|
552
|
+
# 检查是否有对话检查点
|
|
553
|
+
if self.conversation_store is not None:
|
|
554
|
+
try:
|
|
555
|
+
checkpoint = self.conversation_store.get_checkpoint(group_id)
|
|
556
|
+
has_conversation = checkpoint is not None
|
|
557
|
+
except Exception as e:
|
|
558
|
+
logger.error(f"获取对话检查点失败: {str(e)}")
|
|
559
|
+
|
|
560
|
+
checkpoints.append({
|
|
561
|
+
"id": group_id,
|
|
562
|
+
"timestamp": timestamp,
|
|
563
|
+
"changes_count": count,
|
|
564
|
+
"has_conversation": has_conversation
|
|
565
|
+
})
|
|
566
|
+
|
|
567
|
+
return checkpoints
|
|
568
|
+
|
|
362
569
|
def get_diff_text(self, old_content: str, new_content: str) -> str:
|
|
363
570
|
"""
|
|
364
571
|
获取两个文本内容的差异文本
|
|
@@ -89,7 +89,8 @@ def stream_chat_with_continue(
|
|
|
89
89
|
content = res[0]
|
|
90
90
|
current_content += content
|
|
91
91
|
if current_metadata is None:
|
|
92
|
-
current_metadata = res[1]
|
|
92
|
+
current_metadata = res[1]
|
|
93
|
+
metadatas[count] = res[1]
|
|
93
94
|
else:
|
|
94
95
|
metadatas[count] = res[1]
|
|
95
96
|
current_metadata.finish_reason = res[1].finish_reason
|