auto-coder 0.1.337__py3-none-any.whl → 0.1.339__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.337.dist-info → auto_coder-0.1.339.dist-info}/METADATA +1 -1
- {auto_coder-0.1.337.dist-info → auto_coder-0.1.339.dist-info}/RECORD +14 -14
- autocoder/auto_coder_runner.py +3 -1
- autocoder/common/auto_coder_lang.py +36 -0
- autocoder/common/v2/agent/agentic_edit.py +118 -84
- autocoder/common/v2/agent/agentic_edit_conversation.py +73 -8
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +17 -8
- autocoder/common/v2/agent/agentic_edit_types.py +5 -0
- autocoder/common/v2/agent/agentic_tool_display.py +2 -2
- autocoder/version.py +1 -1
- {auto_coder-0.1.337.dist-info → auto_coder-0.1.339.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.337.dist-info → auto_coder-0.1.339.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.337.dist-info → auto_coder-0.1.339.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.337.dist-info → auto_coder-0.1.339.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=Xrzeo9u7UIvIjKgaV12O-XItl10dhjogp-7TNIvqAP8,111518
|
|
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=-seC5wEXkr8QKedh44SePAzcBOumRQcM3gKZh2mLQy0,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=ozoGTy4ZFn3YsO5zWhvAGCu54mK4LtnRfC2yCvrMc_8,42462
|
|
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,10 +126,10 @@ 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=
|
|
130
|
-
autocoder/common/v2/agent/agentic_edit_conversation.py,sha256=
|
|
131
|
-
autocoder/common/v2/agent/agentic_edit_types.py,sha256=
|
|
132
|
-
autocoder/common/v2/agent/agentic_tool_display.py,sha256=
|
|
129
|
+
autocoder/common/v2/agent/agentic_edit.py,sha256=KnnzwDb54ptMtFgvGjCTOuzHd0Er4LRDuOeHHSLT9qY,88698
|
|
130
|
+
autocoder/common/v2/agent/agentic_edit_conversation.py,sha256=2GZrw5f8Sh9GgUDnuFc2v7Q4syrm77kKEfXI6k6uEFY,7310
|
|
131
|
+
autocoder/common/v2/agent/agentic_edit_types.py,sha256=6qBLLmvdlcsbzrpMHsYQVIHqbOWubMXOnmkqTs1pBWQ,4629
|
|
132
|
+
autocoder/common/v2/agent/agentic_tool_display.py,sha256=WKirt-2V346KLnbHgH3NVJiK3xvriD9oaCWj2IdvzLU,7309
|
|
133
133
|
autocoder/common/v2/agent/agentic_edit_tools/__init__.py,sha256=wGICCc1dYh07osB21j62zOQ9Ws0PyyOQ12UYRHmHrtI,1229
|
|
134
134
|
autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py,sha256=pjrukXjWXMIfUAUzoHzr7j2Onf1L7bxmjsUR1gGaFoA,2809
|
|
135
135
|
autocoder/common/v2/agent/agentic_edit_tools/attempt_completion_tool_resolver.py,sha256=82ZGKeRBSDKeead_XVBW4FxpiE-5dS7tBOk_3RZ6B5s,1511
|
|
@@ -139,7 +139,7 @@ autocoder/common/v2/agent/agentic_edit_tools/list_code_definition_names_tool_res
|
|
|
139
139
|
autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py,sha256=ERM5E7s2azQ8vcvogan4A_LZci8Pmhmxw1uQaNQhon4,5469
|
|
140
140
|
autocoder/common/v2/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py,sha256=SZwFUxK6d2BaKWqQXi_c3IVe2iffviF6VUXJA9T9sx0,1492
|
|
141
141
|
autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py,sha256=9Bh0KVbL0qiIqwChlb77biiBiETQ3zekxGe5Fj7hXAg,2800
|
|
142
|
-
autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py,sha256=
|
|
142
|
+
autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py,sha256=lpD4fCbVR8GTrynqXON69IjM94nPy3nuUL62Ashm5O4,7988
|
|
143
143
|
autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py,sha256=K-TcqY0z7nDupMkTRDAJdqW3z2Y_RUM_wUb-pOEVQRI,6044
|
|
144
144
|
autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py,sha256=wM2Xy4bcnD0TSLEmcM8rvvyyWenN5_KQnJMO6hJ8lTE,1716
|
|
145
145
|
autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py,sha256=UO4SrkDek3WDlRdlHH022W1roSNMdMcipJqDxRBlheM,3044
|
|
@@ -276,9 +276,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
276
276
|
autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
277
277
|
autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=KW0mlmcHlStXi8-_6fXZ2-ifeJ5mgP0OV7DQFzCtIsw,14008
|
|
278
278
|
autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
279
|
-
auto_coder-0.1.
|
|
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.
|
|
279
|
+
auto_coder-0.1.339.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
280
|
+
auto_coder-0.1.339.dist-info/METADATA,sha256=MnX-I9AFRF5xVGXxuurajWhBPbN6GwMpfyx4aOZK4do,2747
|
|
281
|
+
auto_coder-0.1.339.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
282
|
+
auto_coder-0.1.339.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
|
|
283
|
+
auto_coder-0.1.339.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
|
|
284
|
+
auto_coder-0.1.339.dist-info/RECORD,,
|
autocoder/auto_coder_runner.py
CHANGED
|
@@ -2835,7 +2835,9 @@ def auto_command(query: str,extra_args: Dict[str,Any]={}):
|
|
|
2835
2835
|
agent = AgenticEdit(llm=llm,args=args,files=SourceCodeList(sources=sources),
|
|
2836
2836
|
conversation_history=[],
|
|
2837
2837
|
memory_config=MemoryConfig(memory=memory,
|
|
2838
|
-
save_memory_func=save_memory), command_config=CommandConfig
|
|
2838
|
+
save_memory_func=save_memory), command_config=CommandConfig,
|
|
2839
|
+
conversation_name="current"
|
|
2840
|
+
)
|
|
2839
2841
|
agent.run_in_terminal(AgenticEditRequest(user_input=query))
|
|
2840
2842
|
return
|
|
2841
2843
|
|
|
@@ -837,6 +837,42 @@ MESSAGES = {
|
|
|
837
837
|
}
|
|
838
838
|
|
|
839
839
|
|
|
840
|
+
# 新增 ReplaceInFileToolResolver 国际化消息
|
|
841
|
+
MESSAGES.update({
|
|
842
|
+
"replace_in_file.access_denied": {
|
|
843
|
+
"en": "Error: Access denied. Attempted to modify file outside the project directory: {{file_path}}",
|
|
844
|
+
"zh": "错误:拒绝访问。尝试修改项目目录之外的文件:{{file_path}}"
|
|
845
|
+
},
|
|
846
|
+
"replace_in_file.file_not_found": {
|
|
847
|
+
"en": "Error: File not found at path: {{file_path}}",
|
|
848
|
+
"zh": "错误:未找到文件路径:{{file_path}}"
|
|
849
|
+
},
|
|
850
|
+
"replace_in_file.read_error": {
|
|
851
|
+
"en": "An error occurred while reading the file for replacement: {{error}}",
|
|
852
|
+
"zh": "读取待替换文件时发生错误:{{error}}"
|
|
853
|
+
},
|
|
854
|
+
"replace_in_file.no_valid_blocks": {
|
|
855
|
+
"en": "Error: No valid SEARCH/REPLACE blocks found in the provided diff.",
|
|
856
|
+
"zh": "错误:在提供的diff中未找到有效的SEARCH/REPLACE代码块。"
|
|
857
|
+
},
|
|
858
|
+
"replace_in_file.apply_failed": {
|
|
859
|
+
"en": "Failed to apply any changes. Errors:\n{{errors}}",
|
|
860
|
+
"zh": "未能应用任何更改。错误信息:\n{{errors}}"
|
|
861
|
+
},
|
|
862
|
+
"replace_in_file.apply_success": {
|
|
863
|
+
"en": "Successfully applied {{applied}}/{{total}} changes to file: {{file_path}}.",
|
|
864
|
+
"zh": "成功应用了 {{applied}}/{{total}} 个更改到文件:{{file_path}}。"
|
|
865
|
+
},
|
|
866
|
+
"replace_in_file.apply_success_with_warnings": {
|
|
867
|
+
"en": "Successfully applied {{applied}}/{{total}} changes to file: {{file_path}}.\nWarnings:\n{{errors}}",
|
|
868
|
+
"zh": "成功应用了 {{applied}}/{{total}} 个更改到文件:{{file_path}}。\n警告信息:\n{{errors}}"
|
|
869
|
+
},
|
|
870
|
+
"replace_in_file.write_error": {
|
|
871
|
+
"en": "An error occurred while writing the modified file: {{error}}",
|
|
872
|
+
"zh": "写入修改后的文件时发生错误:{{error}}"
|
|
873
|
+
}
|
|
874
|
+
})
|
|
875
|
+
|
|
840
876
|
def get_system_language():
|
|
841
877
|
try:
|
|
842
878
|
return locale.getdefaultlocale()[0][:2]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from autocoder.common.v2.agent.agentic_edit_conversation import AgenticConversation
|
|
1
2
|
from enum import Enum
|
|
2
3
|
from enum import Enum
|
|
3
4
|
import json
|
|
@@ -64,7 +65,7 @@ from autocoder.common.v2.agent.agentic_edit_types import (AgenticEditRequest, To
|
|
|
64
65
|
TOOL_MODEL_MAP,
|
|
65
66
|
# Event Types
|
|
66
67
|
LLMOutputEvent, LLMThinkingEvent, ToolCallEvent,
|
|
67
|
-
ToolResultEvent, CompletionEvent, ErrorEvent,TokenUsageEvent,
|
|
68
|
+
ToolResultEvent, CompletionEvent, PlanModeRespondEvent, ErrorEvent, TokenUsageEvent,
|
|
68
69
|
# Import specific tool types for display mapping
|
|
69
70
|
ReadFileTool, WriteToFileTool, ReplaceInFileTool, ExecuteCommandTool,
|
|
70
71
|
ListFilesTool, SearchFilesTool, ListCodeDefinitionNamesTool,
|
|
@@ -87,6 +88,7 @@ TOOL_RESOLVER_MAP: Dict[Type[BaseTool], Type[BaseToolResolver]] = {
|
|
|
87
88
|
UseMcpTool: UseMcpToolResolver,
|
|
88
89
|
}
|
|
89
90
|
|
|
91
|
+
|
|
90
92
|
# --- Tool Display Customization is now handled by agentic_tool_display.py ---
|
|
91
93
|
|
|
92
94
|
|
|
@@ -99,6 +101,7 @@ class AgenticEdit:
|
|
|
99
101
|
args: AutoCoderArgs,
|
|
100
102
|
memory_config: MemoryConfig,
|
|
101
103
|
command_config: Optional[CommandConfig] = None,
|
|
104
|
+
conversation_name: str = "current"
|
|
102
105
|
):
|
|
103
106
|
self.llm = llm
|
|
104
107
|
self.args = args
|
|
@@ -113,27 +116,30 @@ class AgenticEdit:
|
|
|
113
116
|
self.project_type_analyzer = ProjectTypeAnalyzer(
|
|
114
117
|
args=args, llm=self.llm)
|
|
115
118
|
|
|
119
|
+
self.conversation_manager = AgenticConversation(
|
|
120
|
+
args, self.conversation_history, conversation_name=conversation_name)
|
|
121
|
+
|
|
116
122
|
self.shadow_manager = ShadowManager(
|
|
117
123
|
args.source_dir, args.event_file, args.ignore_clean_shadows)
|
|
118
124
|
self.shadow_linter = ShadowLinter(self.shadow_manager, verbose=False)
|
|
119
125
|
self.shadow_compiler = ShadowCompiler(
|
|
120
|
-
self.shadow_manager, verbose=False)
|
|
121
|
-
|
|
126
|
+
self.shadow_manager, verbose=False)
|
|
127
|
+
|
|
122
128
|
self.mcp_server_info = ""
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
try:
|
|
130
|
+
self.mcp_server = get_mcp_server()
|
|
131
|
+
mcp_server_info_response = self.mcp_server.send_request(
|
|
132
|
+
McpServerInfoRequest(
|
|
133
|
+
model=args.inference_model or args.model,
|
|
134
|
+
product_mode=args.product_mode,
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
self.mcp_server_info = mcp_server_info_response.result
|
|
138
|
+
except Exception as e:
|
|
139
|
+
logger.error(f"Error getting MCP server info: {str(e)}")
|
|
134
140
|
|
|
135
141
|
# 变更跟踪信息
|
|
136
|
-
# 格式: { file_path: FileChangeEntry(...) }
|
|
142
|
+
# 格式: { file_path: FileChangeEntry(...) }
|
|
137
143
|
self.file_changes: Dict[str, FileChangeEntry] = {}
|
|
138
144
|
|
|
139
145
|
def record_file_change(self, file_path: str, change_type: str, diff: Optional[str] = None, content: Optional[str] = None):
|
|
@@ -148,7 +154,8 @@ class AgenticEdit:
|
|
|
148
154
|
"""
|
|
149
155
|
entry = self.file_changes.get(file_path)
|
|
150
156
|
if entry is None:
|
|
151
|
-
entry = FileChangeEntry(
|
|
157
|
+
entry = FileChangeEntry(
|
|
158
|
+
type=change_type, diffs=[], content=content)
|
|
152
159
|
self.file_changes[file_path] = entry
|
|
153
160
|
else:
|
|
154
161
|
# 文件已经存在,可能之前是 added,现在又被 modified,或者多次 modified
|
|
@@ -185,8 +192,10 @@ class AgenticEdit:
|
|
|
185
192
|
for fname in files:
|
|
186
193
|
shadow_file_path = os.path.join(root, fname)
|
|
187
194
|
try:
|
|
188
|
-
project_file_path = self.shadow_manager.from_shadow_path(
|
|
189
|
-
|
|
195
|
+
project_file_path = self.shadow_manager.from_shadow_path(
|
|
196
|
+
shadow_file_path)
|
|
197
|
+
rel_path = os.path.relpath(
|
|
198
|
+
project_file_path, self.args.source_dir)
|
|
190
199
|
changed_files.append(rel_path)
|
|
191
200
|
except Exception:
|
|
192
201
|
# 非映射关系,忽略
|
|
@@ -377,7 +386,7 @@ class AgenticEdit:
|
|
|
377
386
|
</options>
|
|
378
387
|
</plan_mode_respond>
|
|
379
388
|
|
|
380
|
-
##
|
|
389
|
+
## mcp_tool
|
|
381
390
|
Description: Request to execute a tool via the Model Context Protocol (MCP) server. Use this when you need to execute a tool that is not natively supported by the agentic edit tools.
|
|
382
391
|
Parameters:
|
|
383
392
|
- server_name: (optional) The name of the MCP server to use. If not provided, the tool will automatically choose the best server based on the query.
|
|
@@ -391,7 +400,7 @@ class AgenticEdit:
|
|
|
391
400
|
Your query here
|
|
392
401
|
</query>
|
|
393
402
|
</use_mcp_tool>
|
|
394
|
-
|
|
403
|
+
|
|
395
404
|
{%if mcp_server_info %}
|
|
396
405
|
### MCP_SERVER_LIST
|
|
397
406
|
{{mcp_server_info}}
|
|
@@ -463,24 +472,13 @@ class AgenticEdit:
|
|
|
463
472
|
</diff>
|
|
464
473
|
</replace_in_file>
|
|
465
474
|
|
|
466
|
-
## Example 4: Another example of using an MCP tool (where the server name is a unique identifier
|
|
475
|
+
## Example 4: Another example of using an MCP tool (where the server name is a unique identifier listed in MCP_SERVER_LIST)
|
|
467
476
|
|
|
468
477
|
<use_mcp_tool>
|
|
469
|
-
<server_name>github
|
|
478
|
+
<server_name>github</server_name>
|
|
470
479
|
<tool_name>create_issue</tool_name>
|
|
471
|
-
<
|
|
472
|
-
|
|
473
|
-
"owner": "octocat",
|
|
474
|
-
"repo": "hello-world",
|
|
475
|
-
"title": "Found a bug",
|
|
476
|
-
"body": "I'm having a problem with this.",
|
|
477
|
-
"labels": ["bug", "help wanted"],
|
|
478
|
-
"assignees": ["octocat"]
|
|
479
|
-
}
|
|
480
|
-
</arguments>
|
|
481
|
-
</use_mcp_tool>`
|
|
482
|
-
: ""
|
|
483
|
-
}
|
|
480
|
+
<query>ower is octocat, repo is hello-world, title is Found a bug, body is I'm having a problem with this. labels is "bug" and "help wanted",assignees is "octocat"</query>
|
|
481
|
+
</use_mcp_tool>
|
|
484
482
|
|
|
485
483
|
# Tool Use Guidelines
|
|
486
484
|
|
|
@@ -660,12 +658,12 @@ class AgenticEdit:
|
|
|
660
658
|
4. Once you've completed the user's task, you must use the attempt_completion tool to present the result of the task to the user. You may also provide a CLI command to showcase the result of your task; this can be particularly useful for web development tasks, where you can run e.g. \`open index.html\` to show the website you've built.
|
|
661
659
|
5. The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.
|
|
662
660
|
|
|
663
|
-
{
|
|
661
|
+
{% if enable_active_context %}
|
|
664
662
|
**Very Important Notice**
|
|
665
663
|
Each directory has a description file stored separately. For example, the description for the directory `{{ current_project }}/src/abc/bbc` can be found in the file `{{ current_project }}/.auto-coder/active-context/src/abc/bbc/active.md`.
|
|
666
664
|
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
|
|
667
665
|
about the files that were recently changed.
|
|
668
|
-
{
|
|
666
|
+
{% endif %}
|
|
669
667
|
"""
|
|
670
668
|
env_info = detect_env()
|
|
671
669
|
shell_type = "bash"
|
|
@@ -735,11 +733,15 @@ class AgenticEdit:
|
|
|
735
733
|
Analyzes the user request, interacts with the LLM, parses responses,
|
|
736
734
|
executes tools, and yields structured events for visualization until completion or error.
|
|
737
735
|
"""
|
|
738
|
-
system_prompt = self._analyze.prompt(request)
|
|
736
|
+
system_prompt = self._analyze.prompt(request)
|
|
737
|
+
# print(system_prompt)
|
|
739
738
|
conversations = [
|
|
740
739
|
{"role": "system", "content": system_prompt},
|
|
741
|
-
|
|
742
|
-
|
|
740
|
+
] + self.conversation_manager.get_history()
|
|
741
|
+
conversations.append({
|
|
742
|
+
"role": "user", "content": request.user_input
|
|
743
|
+
})
|
|
744
|
+
self.conversation_manager.add_user_message(request.user_input)
|
|
743
745
|
logger.debug(
|
|
744
746
|
f"Initial conversation history size: {len(conversations)}")
|
|
745
747
|
|
|
@@ -749,17 +751,17 @@ class AgenticEdit:
|
|
|
749
751
|
f"Starting LLM interaction cycle. History size: {len(conversations)}")
|
|
750
752
|
tool_executed = False
|
|
751
753
|
assistant_buffer = ""
|
|
752
|
-
|
|
754
|
+
|
|
753
755
|
llm_response_gen = stream_chat_with_continue(
|
|
754
756
|
llm=self.llm,
|
|
755
757
|
conversations=conversations,
|
|
756
758
|
llm_config={}, # Placeholder for future LLM configs
|
|
757
759
|
args=self.args
|
|
758
760
|
)
|
|
759
|
-
|
|
761
|
+
|
|
760
762
|
meta_holder = byzerllm.MetaHolder()
|
|
761
763
|
parsed_events = self.stream_and_parse_llm_response(
|
|
762
|
-
llm_response_gen,meta_holder)
|
|
764
|
+
llm_response_gen, meta_holder)
|
|
763
765
|
|
|
764
766
|
for event in parsed_events:
|
|
765
767
|
global_cancel.check_and_raise()
|
|
@@ -777,6 +779,8 @@ class AgenticEdit:
|
|
|
777
779
|
"role": "assistant",
|
|
778
780
|
"content": assistant_buffer + tool_xml
|
|
779
781
|
})
|
|
782
|
+
self.conversation_manager.add_assistant_message(
|
|
783
|
+
assistant_buffer + tool_xml)
|
|
780
784
|
assistant_buffer = "" # Reset buffer after tool call
|
|
781
785
|
|
|
782
786
|
yield event # Yield the ToolCallEvent for display
|
|
@@ -790,6 +794,14 @@ class AgenticEdit:
|
|
|
790
794
|
"AgenticEdit analyze loop finished due to AttemptCompletion.")
|
|
791
795
|
return
|
|
792
796
|
|
|
797
|
+
if isinstance(tool_obj, PlanModeRespondTool):
|
|
798
|
+
logger.info(
|
|
799
|
+
"PlanModeRespondTool received. Finalizing session.")
|
|
800
|
+
yield PlanModeRespondEvent(completion=tool_obj, completion_xml=tool_xml)
|
|
801
|
+
logger.info(
|
|
802
|
+
"AgenticEdit analyze loop finished due to PlanModeRespond.")
|
|
803
|
+
return
|
|
804
|
+
|
|
793
805
|
# Resolve the tool
|
|
794
806
|
resolver_cls = TOOL_RESOLVER_MAP.get(type(tool_obj))
|
|
795
807
|
if not resolver_cls:
|
|
@@ -844,6 +856,7 @@ class AgenticEdit:
|
|
|
844
856
|
"role": "user", # Simulating the user providing the tool result
|
|
845
857
|
"content": error_xml
|
|
846
858
|
})
|
|
859
|
+
self.conversation_manager.add_user_message(error_xml)
|
|
847
860
|
logger.debug(
|
|
848
861
|
f"Added tool result to conversations for tool {type(tool_obj).__name__}")
|
|
849
862
|
break # After tool execution and result, break to start a new LLM cycle
|
|
@@ -862,6 +875,8 @@ class AgenticEdit:
|
|
|
862
875
|
if assistant_buffer:
|
|
863
876
|
conversations.append(
|
|
864
877
|
{"role": "assistant", "content": assistant_buffer})
|
|
878
|
+
self.conversation_manager.add_assistant_message(
|
|
879
|
+
assistant_buffer)
|
|
865
880
|
# If the loop ends without AttemptCompletion, it means the LLM finished talking
|
|
866
881
|
# without signaling completion. We might just stop or yield a final message.
|
|
867
882
|
# Let's assume it stops here.
|
|
@@ -870,7 +885,7 @@ class AgenticEdit:
|
|
|
870
885
|
logger.info("AgenticEdit analyze loop finished.")
|
|
871
886
|
|
|
872
887
|
def stream_and_parse_llm_response(
|
|
873
|
-
self, generator: Generator[Tuple[str, Any], None, None],meta_holder: byzerllm.MetaHolder
|
|
888
|
+
self, generator: Generator[Tuple[str, Any], None, None], meta_holder: byzerllm.MetaHolder
|
|
874
889
|
) -> Generator[Union[LLMOutputEvent, LLMThinkingEvent, ToolCallEvent, ErrorEvent], None, None]:
|
|
875
890
|
"""
|
|
876
891
|
Streamingly parses the LLM response generator, distinguishing between
|
|
@@ -920,19 +935,18 @@ class AgenticEdit:
|
|
|
920
935
|
params['requires_approval'] = params['requires_approval'].lower(
|
|
921
936
|
) == 'true'
|
|
922
937
|
# Attempt to handle JSON parsing for arguments in use_mcp_tool
|
|
923
|
-
if tool_tag == 'use_mcp_tool' and '
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
938
|
+
# if tool_tag == 'use_mcp_tool' and 'query' in params:
|
|
939
|
+
# try:
|
|
940
|
+
# params['arguments'] = json.loads(
|
|
941
|
+
# params['arguments'])
|
|
942
|
+
# except json.JSONDecodeError:
|
|
943
|
+
# logger.warning(
|
|
944
|
+
# f"Could not decode JSON arguments for use_mcp_tool: {params['arguments']}")
|
|
945
|
+
# # Keep as string or handle error? Let's keep as string for now.
|
|
946
|
+
# pass
|
|
932
947
|
# Handle recursive for list_files
|
|
933
948
|
if tool_tag == 'list_files' and 'recursive' in params:
|
|
934
|
-
params['recursive'] = params['recursive'].lower() == 'true'
|
|
935
|
-
|
|
949
|
+
params['recursive'] = params['recursive'].lower() == 'true'
|
|
936
950
|
return tool_cls(**params)
|
|
937
951
|
else:
|
|
938
952
|
logger.error(f"Tool class not found for tag: {tool_tag}")
|
|
@@ -970,7 +984,7 @@ class AgenticEdit:
|
|
|
970
984
|
break
|
|
971
985
|
|
|
972
986
|
# 2. Check for </tool_tag> if inside tool block
|
|
973
|
-
elif in_tool_block:
|
|
987
|
+
elif in_tool_block:
|
|
974
988
|
end_tag = f"</{current_tool_tag}>"
|
|
975
989
|
end_tool_pos = buffer.find(end_tag)
|
|
976
990
|
if end_tool_pos != -1:
|
|
@@ -1081,17 +1095,17 @@ class AgenticEdit:
|
|
|
1081
1095
|
Runs the agentic edit process, converting internal events to the
|
|
1082
1096
|
standard event system format and writing them using the event manager.
|
|
1083
1097
|
"""
|
|
1084
|
-
event_manager = get_event_manager(self.args.event_file)
|
|
1098
|
+
event_manager = get_event_manager(self.args.event_file)
|
|
1085
1099
|
|
|
1086
1100
|
try:
|
|
1087
1101
|
event_stream = self.analyze(request)
|
|
1088
1102
|
for agent_event in event_stream:
|
|
1089
1103
|
content = None
|
|
1090
1104
|
metadata = EventMetadata(
|
|
1091
|
-
action_file=self.args.event_file,
|
|
1092
|
-
is_streaming=False,
|
|
1105
|
+
action_file=self.args.event_file,
|
|
1106
|
+
is_streaming=False,
|
|
1093
1107
|
stream_out_type="/agent/edit")
|
|
1094
|
-
|
|
1108
|
+
|
|
1095
1109
|
if isinstance(agent_event, LLMThinkingEvent):
|
|
1096
1110
|
content = EventContentCreator.create_stream_thinking(
|
|
1097
1111
|
content=agent_event.text)
|
|
@@ -1134,7 +1148,8 @@ class AgenticEdit:
|
|
|
1134
1148
|
try:
|
|
1135
1149
|
self.apply_changes()
|
|
1136
1150
|
except Exception as e:
|
|
1137
|
-
logger.exception(
|
|
1151
|
+
logger.exception(
|
|
1152
|
+
f"Error merging shadow changes to project: {e}")
|
|
1138
1153
|
|
|
1139
1154
|
metadata.path = "/agent/edit/completion"
|
|
1140
1155
|
content = EventContentCreator.create_completion(
|
|
@@ -1144,7 +1159,8 @@ class AgenticEdit:
|
|
|
1144
1159
|
**agent_event.completion.model_dump()
|
|
1145
1160
|
}
|
|
1146
1161
|
)
|
|
1147
|
-
event_manager.write_completion(
|
|
1162
|
+
event_manager.write_completion(
|
|
1163
|
+
content=content.to_dict(), metadata=metadata.to_dict())
|
|
1148
1164
|
elif isinstance(agent_event, ErrorEvent):
|
|
1149
1165
|
metadata.path = "/agent/edit/error"
|
|
1150
1166
|
content = EventContentCreator.create_error(
|
|
@@ -1152,7 +1168,8 @@ class AgenticEdit:
|
|
|
1152
1168
|
error_message=agent_event.message,
|
|
1153
1169
|
details={"agent_event_type": "ErrorEvent"}
|
|
1154
1170
|
)
|
|
1155
|
-
event_manager.write_error(
|
|
1171
|
+
event_manager.write_error(
|
|
1172
|
+
content=content.to_dict(), metadata=metadata.to_dict())
|
|
1156
1173
|
else:
|
|
1157
1174
|
metadata.path = "/agent/edit/error"
|
|
1158
1175
|
logger.warning(
|
|
@@ -1163,7 +1180,8 @@ class AgenticEdit:
|
|
|
1163
1180
|
details={"agent_event_type": type(
|
|
1164
1181
|
agent_event).__name__}
|
|
1165
1182
|
)
|
|
1166
|
-
event_manager.write_error(
|
|
1183
|
+
event_manager.write_error(
|
|
1184
|
+
content=content.to_dict(), metadata=metadata.to_dict())
|
|
1167
1185
|
|
|
1168
1186
|
except Exception as e:
|
|
1169
1187
|
logger.exception(
|
|
@@ -1174,15 +1192,16 @@ class AgenticEdit:
|
|
|
1174
1192
|
error_message=f"An unexpected error occurred: {str(e)}",
|
|
1175
1193
|
details={"exception_type": type(e).__name__}
|
|
1176
1194
|
)
|
|
1177
|
-
event_manager.write_error(
|
|
1195
|
+
event_manager.write_error(
|
|
1196
|
+
content=error_content.to_dict(), metadata=metadata.to_dict())
|
|
1178
1197
|
# Re-raise the exception if needed, or handle appropriately
|
|
1179
1198
|
raise e
|
|
1180
1199
|
|
|
1181
1200
|
def apply_changes(self):
|
|
1182
1201
|
"""
|
|
1183
1202
|
Apply all tracked file changes to the original project directory.
|
|
1184
|
-
"""
|
|
1185
|
-
for (file_path,change) in self.get_all_file_changes().items():
|
|
1203
|
+
"""
|
|
1204
|
+
for (file_path, change) in self.get_all_file_changes().items():
|
|
1186
1205
|
with open(file_path, 'w', encoding='utf-8') as f:
|
|
1187
1206
|
f.write(change.content)
|
|
1188
1207
|
|
|
@@ -1194,33 +1213,40 @@ class AgenticEdit:
|
|
|
1194
1213
|
self.args.source_dir,
|
|
1195
1214
|
f"{self.args.query}\nauto_coder_{file_name}",
|
|
1196
1215
|
)
|
|
1197
|
-
|
|
1198
|
-
action_yml_file_manager = ActionYmlFileManager(
|
|
1216
|
+
|
|
1217
|
+
action_yml_file_manager = ActionYmlFileManager(
|
|
1218
|
+
self.args.source_dir)
|
|
1199
1219
|
action_file_name = os.path.basename(self.args.file)
|
|
1200
1220
|
add_updated_urls = []
|
|
1201
1221
|
commit_result.changed_files
|
|
1202
1222
|
for file in commit_result.changed_files:
|
|
1203
|
-
add_updated_urls.append(
|
|
1223
|
+
add_updated_urls.append(
|
|
1224
|
+
os.path.join(self.args.source_dir, file))
|
|
1204
1225
|
|
|
1205
1226
|
self.args.add_updated_urls = add_updated_urls
|
|
1206
|
-
update_yaml_success = action_yml_file_manager.update_yaml_field(
|
|
1207
|
-
|
|
1208
|
-
|
|
1227
|
+
update_yaml_success = action_yml_file_manager.update_yaml_field(
|
|
1228
|
+
action_file_name, "add_updated_urls", add_updated_urls)
|
|
1229
|
+
if not update_yaml_success:
|
|
1230
|
+
self.printer.print_in_terminal(
|
|
1231
|
+
"yaml_save_error", style="red", yaml_file=action_file_name)
|
|
1209
1232
|
|
|
1210
1233
|
if self.args.enable_active_context:
|
|
1211
|
-
active_context_manager = ActiveContextManager(
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1234
|
+
active_context_manager = ActiveContextManager(
|
|
1235
|
+
self.llm, self.args.source_dir)
|
|
1236
|
+
task_id = active_context_manager.process_changes(
|
|
1237
|
+
self.args)
|
|
1238
|
+
self.printer.print_in_terminal("active_context_background_task",
|
|
1239
|
+
style="blue",
|
|
1240
|
+
task_id=task_id)
|
|
1216
1241
|
git_utils.print_commit_info(commit_result=commit_result)
|
|
1217
1242
|
except Exception as e:
|
|
1218
1243
|
self.printer.print_str_in_terminal(
|
|
1219
|
-
self.git_require_msg(
|
|
1244
|
+
self.git_require_msg(
|
|
1245
|
+
source_dir=self.args.source_dir, error=str(e)),
|
|
1220
1246
|
style="red"
|
|
1221
|
-
)
|
|
1247
|
+
)
|
|
1222
1248
|
else:
|
|
1223
|
-
self.printer.print_in_terminal("no_changes_made")
|
|
1249
|
+
self.printer.print_in_terminal("no_changes_made")
|
|
1224
1250
|
|
|
1225
1251
|
def run_in_terminal(self, request: AgenticEditRequest):
|
|
1226
1252
|
"""
|
|
@@ -1258,6 +1284,9 @@ class AgenticEdit:
|
|
|
1258
1284
|
if event.tool_name == "AttemptCompletionTool":
|
|
1259
1285
|
continue # Do not display AttemptCompletionTool result
|
|
1260
1286
|
|
|
1287
|
+
if event.tool_name == "PlanModeRespondTool":
|
|
1288
|
+
continue
|
|
1289
|
+
|
|
1261
1290
|
result = event.result
|
|
1262
1291
|
title = f"✅ Tool Result: {event.tool_name}" if result.success else f"❌ Tool Result: {event.tool_name}"
|
|
1263
1292
|
border_style = "green" if result.success else "red"
|
|
@@ -1274,7 +1303,7 @@ class AgenticEdit:
|
|
|
1274
1303
|
panel_content = [base_content]
|
|
1275
1304
|
syntax_content = None
|
|
1276
1305
|
|
|
1277
|
-
if result.content is not None:
|
|
1306
|
+
if result.content is not None:
|
|
1278
1307
|
content_str = ""
|
|
1279
1308
|
try:
|
|
1280
1309
|
if isinstance(result.content, (dict, list)):
|
|
@@ -1316,7 +1345,8 @@ class AgenticEdit:
|
|
|
1316
1345
|
else:
|
|
1317
1346
|
content_str = str(result.content)
|
|
1318
1347
|
# Append simple string content directly
|
|
1319
|
-
panel_content.append(
|
|
1348
|
+
panel_content.append(
|
|
1349
|
+
_format_content(content_str))
|
|
1320
1350
|
except Exception as e:
|
|
1321
1351
|
logger.warning(
|
|
1322
1352
|
f"Error formatting tool result content: {e}")
|
|
@@ -1329,13 +1359,17 @@ class AgenticEdit:
|
|
|
1329
1359
|
# Print syntax highlighted content separately if it exists
|
|
1330
1360
|
if syntax_content:
|
|
1331
1361
|
console.print(syntax_content)
|
|
1362
|
+
elif isinstance(event, PlanModeRespondEvent):
|
|
1363
|
+
console.print(Panel(Markdown(event.completion.response),
|
|
1364
|
+
title="🏁 Task Completion", border_style="green", title_align="left"))
|
|
1332
1365
|
|
|
1333
1366
|
elif isinstance(event, CompletionEvent):
|
|
1334
1367
|
# 在这里完成实际合并
|
|
1335
1368
|
try:
|
|
1336
1369
|
self.apply_changes()
|
|
1337
1370
|
except Exception as e:
|
|
1338
|
-
logger.exception(
|
|
1371
|
+
logger.exception(
|
|
1372
|
+
f"Error merging shadow changes to project: {e}")
|
|
1339
1373
|
|
|
1340
1374
|
console.print(Panel(Markdown(event.completion.result),
|
|
1341
1375
|
title="🏁 Task Completion", border_style="green", title_align="left"))
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# src/autocoder/common/v2/agent/agentic_edit_conversation.py
|
|
2
2
|
import os
|
|
3
3
|
import json
|
|
4
|
+
import uuid
|
|
4
5
|
from typing import List, Dict, Any, Optional
|
|
5
6
|
from autocoder.common import AutoCoderArgs
|
|
6
7
|
|
|
@@ -15,16 +16,32 @@ class AgenticConversation:
|
|
|
15
16
|
and retrieving the history.
|
|
16
17
|
"""
|
|
17
18
|
|
|
18
|
-
def __init__(self, args: AutoCoderArgs, initial_history: Optional[List[MessageType]] = None):
|
|
19
|
+
def __init__(self, args: AutoCoderArgs, initial_history: Optional[List[MessageType]] = None, conversation_name: Optional[str] = None):
|
|
19
20
|
"""
|
|
20
21
|
Initializes the conversation history.
|
|
21
22
|
|
|
22
23
|
Args:
|
|
23
24
|
initial_history: An optional list of messages to start with.
|
|
25
|
+
conversation_name: Optional conversation identifier. If provided, history is saved/loaded from a file named after it.
|
|
24
26
|
"""
|
|
25
27
|
self.project_path = args.source_dir
|
|
26
28
|
self._history: List[MessageType] = initial_history if initial_history is not None else []
|
|
27
|
-
|
|
29
|
+
|
|
30
|
+
# Determine the memory directory
|
|
31
|
+
memory_dir = os.path.join(self.project_path, ".auto-coder", "memory", "agentic_edit_memory")
|
|
32
|
+
os.makedirs(memory_dir, exist_ok=True)
|
|
33
|
+
|
|
34
|
+
# Determine conversation file path
|
|
35
|
+
if conversation_name:
|
|
36
|
+
filename = f"{conversation_name}.json"
|
|
37
|
+
else:
|
|
38
|
+
conversation_name = str(uuid.uuid4())
|
|
39
|
+
filename = f"{conversation_name}.json"
|
|
40
|
+
|
|
41
|
+
self.conversation_name = conversation_name
|
|
42
|
+
self.memory_file_path = os.path.join(memory_dir, filename)
|
|
43
|
+
|
|
44
|
+
# Load existing history if file exists
|
|
28
45
|
self._load_memory()
|
|
29
46
|
|
|
30
47
|
def add_message(self, role: str, content: Any, **kwargs):
|
|
@@ -67,14 +84,62 @@ class AgenticConversation:
|
|
|
67
84
|
|
|
68
85
|
def get_history(self) -> List[MessageType]:
|
|
69
86
|
"""
|
|
70
|
-
Returns the
|
|
71
|
-
|
|
87
|
+
Returns the latest 20 pairs of (user, assistant) conversation history.
|
|
88
|
+
Merges adjacent same-role messages into one, concatenated by newline.
|
|
89
|
+
Ensures that each user message is paired with the subsequent assistant response,
|
|
90
|
+
skips other roles, and that the last message is always assistant (drops trailing user if unpaired).
|
|
91
|
+
|
|
72
92
|
Returns:
|
|
73
|
-
A list of message dictionaries.
|
|
93
|
+
A list of message dictionaries, ordered chronologically.
|
|
74
94
|
"""
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
paired_history = []
|
|
96
|
+
pair_count = 0
|
|
97
|
+
pending_assistant = None
|
|
98
|
+
pending_user = None
|
|
99
|
+
|
|
100
|
+
# Traverse history in reverse to collect latest pairs with merging
|
|
101
|
+
for msg in reversed(self._history):
|
|
102
|
+
role = msg.get("role")
|
|
103
|
+
if role == "assistant":
|
|
104
|
+
if pending_assistant is None:
|
|
105
|
+
pending_assistant = dict(msg)
|
|
106
|
+
else:
|
|
107
|
+
# Merge with previous assistant
|
|
108
|
+
prev_content = pending_assistant.get("content", "")
|
|
109
|
+
curr_content = msg.get("content", "")
|
|
110
|
+
merged_content = (curr_content.strip() + "\n" + prev_content.strip()).strip()
|
|
111
|
+
pending_assistant["content"] = merged_content
|
|
112
|
+
elif role == "user":
|
|
113
|
+
if pending_user is None:
|
|
114
|
+
pending_user = dict(msg)
|
|
115
|
+
else:
|
|
116
|
+
# Merge with previous user
|
|
117
|
+
prev_content = pending_user.get("content", "")
|
|
118
|
+
curr_content = msg.get("content", "")
|
|
119
|
+
merged_content = (curr_content.strip() + "\n" + prev_content.strip()).strip()
|
|
120
|
+
pending_user["content"] = merged_content
|
|
121
|
+
|
|
122
|
+
if pending_assistant is not None:
|
|
123
|
+
# Have a full pair, insert in order
|
|
124
|
+
paired_history.insert(0, pending_user)
|
|
125
|
+
paired_history.insert(1, pending_assistant)
|
|
126
|
+
pair_count += 1
|
|
127
|
+
pending_assistant = None
|
|
128
|
+
pending_user = None
|
|
129
|
+
if pair_count >= 20:
|
|
130
|
+
break
|
|
131
|
+
else:
|
|
132
|
+
# User without assistant yet, continue accumulating
|
|
133
|
+
continue
|
|
134
|
+
else:
|
|
135
|
+
# Ignore other roles
|
|
136
|
+
continue
|
|
137
|
+
|
|
138
|
+
# Ensure last message is assistant, drop trailing user if unpaired
|
|
139
|
+
if paired_history and paired_history[-1].get("role") == "user":
|
|
140
|
+
paired_history.pop()
|
|
141
|
+
|
|
142
|
+
return paired_history
|
|
78
143
|
|
|
79
144
|
def clear_history(self):
|
|
80
145
|
"""Clears the conversation history."""
|
|
@@ -6,6 +6,7 @@ from autocoder.common import AutoCoderArgs
|
|
|
6
6
|
from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver
|
|
7
7
|
from autocoder.common.v2.agent.agentic_edit_types import ReplaceInFileTool, ToolResult # Import ToolResult from types
|
|
8
8
|
from loguru import logger
|
|
9
|
+
from autocoder.common.auto_coder_lang import get_message_with_format
|
|
9
10
|
if typing.TYPE_CHECKING:
|
|
10
11
|
from autocoder.common.v2.agent.agentic_edit import AgenticEdit
|
|
11
12
|
|
|
@@ -66,7 +67,7 @@ class ReplaceInFileToolResolver(BaseToolResolver):
|
|
|
66
67
|
|
|
67
68
|
# Security check
|
|
68
69
|
if not abs_file_path.startswith(abs_project_dir):
|
|
69
|
-
return ToolResult(success=False, message=
|
|
70
|
+
return ToolResult(success=False, message=get_message_with_format("replace_in_file.access_denied", file_path=file_path))
|
|
70
71
|
|
|
71
72
|
# Determine target path: shadow file if shadow_manager exists
|
|
72
73
|
target_path = abs_file_path
|
|
@@ -90,14 +91,14 @@ class ReplaceInFileToolResolver(BaseToolResolver):
|
|
|
90
91
|
f.write(original_content)
|
|
91
92
|
logger.info(f"[Shadow] Initialized shadow file from original: {target_path}")
|
|
92
93
|
else:
|
|
93
|
-
return ToolResult(success=False, message=
|
|
94
|
+
return ToolResult(success=False, message=get_message_with_format("replace_in_file.file_not_found", file_path=file_path))
|
|
94
95
|
except Exception as e:
|
|
95
96
|
logger.error(f"Error reading file for replace '{file_path}': {str(e)}")
|
|
96
|
-
return ToolResult(success=False, message=
|
|
97
|
+
return ToolResult(success=False, message=get_message_with_format("replace_in_file.read_error", error=str(e)))
|
|
97
98
|
|
|
98
99
|
parsed_blocks = self.parse_diff(diff_content)
|
|
99
100
|
if not parsed_blocks:
|
|
100
|
-
return ToolResult(success=False, message="
|
|
101
|
+
return ToolResult(success=False, message=get_message_with_format("replace_in_file.no_valid_blocks"))
|
|
101
102
|
|
|
102
103
|
current_content = original_content
|
|
103
104
|
applied_count = 0
|
|
@@ -121,7 +122,7 @@ class ReplaceInFileToolResolver(BaseToolResolver):
|
|
|
121
122
|
# continue applying remaining blocks
|
|
122
123
|
|
|
123
124
|
if applied_count == 0 and errors:
|
|
124
|
-
return ToolResult(success=False, message=
|
|
125
|
+
return ToolResult(success=False, message=get_message_with_format("replace_in_file.apply_failed", errors="\n".join(errors)))
|
|
125
126
|
|
|
126
127
|
try:
|
|
127
128
|
os.makedirs(os.path.dirname(target_path), exist_ok=True)
|
|
@@ -129,9 +130,17 @@ class ReplaceInFileToolResolver(BaseToolResolver):
|
|
|
129
130
|
f.write(current_content)
|
|
130
131
|
logger.info(f"Successfully applied {applied_count}/{len(parsed_blocks)} changes to file: {file_path}")
|
|
131
132
|
|
|
132
|
-
message = f"Successfully applied {applied_count}/{len(parsed_blocks)} changes to file: {file_path}."
|
|
133
133
|
if errors:
|
|
134
|
-
message
|
|
134
|
+
message = get_message_with_format("replace_in_file.apply_success_with_warnings",
|
|
135
|
+
applied=applied_count,
|
|
136
|
+
total=len(parsed_blocks),
|
|
137
|
+
file_path=file_path,
|
|
138
|
+
errors="\n".join(errors))
|
|
139
|
+
else:
|
|
140
|
+
message = get_message_with_format("replace_in_file.apply_success",
|
|
141
|
+
applied=applied_count,
|
|
142
|
+
total=len(parsed_blocks),
|
|
143
|
+
file_path=file_path)
|
|
135
144
|
|
|
136
145
|
# 变更跟踪,回调AgenticEdit
|
|
137
146
|
if self.agent:
|
|
@@ -141,4 +150,4 @@ class ReplaceInFileToolResolver(BaseToolResolver):
|
|
|
141
150
|
return ToolResult(success=True, message=message, content=current_content)
|
|
142
151
|
except Exception as e:
|
|
143
152
|
logger.error(f"Error writing replaced content to file '{file_path}': {str(e)}")
|
|
144
|
-
return ToolResult(success=False, message=
|
|
153
|
+
return ToolResult(success=False, message=get_message_with_format("replace_in_file.write_error", error=str(e)))
|
|
@@ -80,6 +80,11 @@ class TokenUsageEvent(BaseModel):
|
|
|
80
80
|
"""Represents the result of executing a tool."""
|
|
81
81
|
usage: Any
|
|
82
82
|
|
|
83
|
+
class PlanModeRespondEvent(BaseModel):
|
|
84
|
+
"""Represents the LLM attempting to complete the task."""
|
|
85
|
+
completion: SkipValidation[PlanModeRespondTool] # Skip validation
|
|
86
|
+
completion_xml: str
|
|
87
|
+
|
|
83
88
|
class CompletionEvent(BaseModel):
|
|
84
89
|
"""Represents the LLM attempting to complete the task."""
|
|
85
90
|
completion: SkipValidation[AttemptCompletionTool] # Skip validation
|
|
@@ -93,7 +93,7 @@ TOOL_DISPLAY_MESSAGES: Dict[Type[BaseTool], Dict[str, str]] = {
|
|
|
93
93
|
"[dim]工具:[/dim] [blue]{{ tool_name }}[/]\n"
|
|
94
94
|
"[dim]参数:[/dim] {{ arguments_snippet }}{{ ellipsis }}"
|
|
95
95
|
)
|
|
96
|
-
}
|
|
96
|
+
}
|
|
97
97
|
# AttemptCompletionTool is handled separately in the display loop
|
|
98
98
|
}
|
|
99
99
|
|
|
@@ -165,7 +165,7 @@ def get_tool_display_message(tool: BaseTool) -> str:
|
|
|
165
165
|
"options_text": options_text_zh if lang == 'zh' else options_text_en
|
|
166
166
|
}
|
|
167
167
|
elif isinstance(tool, UseMcpTool):
|
|
168
|
-
args_str =
|
|
168
|
+
args_str = tool.query
|
|
169
169
|
snippet = args_str[:100]
|
|
170
170
|
context = {
|
|
171
171
|
"server_name": tool.server_name,
|
autocoder/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.339"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|