auto-coder 0.1.330__py3-none-any.whl → 0.1.332__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.330.dist-info → auto_coder-0.1.332.dist-info}/METADATA +1 -1
- {auto_coder-0.1.330.dist-info → auto_coder-0.1.332.dist-info}/RECORD +47 -45
- autocoder/agent/agentic_filter.py +928 -0
- autocoder/agent/project_reader.py +1 -14
- autocoder/auto_coder.py +6 -47
- autocoder/auto_coder_runner.py +2 -0
- autocoder/command_args.py +1 -6
- autocoder/commands/auto_command.py +1 -1
- autocoder/commands/tools.py +68 -16
- autocoder/common/__init__.py +8 -3
- autocoder/common/auto_coder_lang.py +21 -1
- autocoder/common/code_auto_generate.py +6 -160
- autocoder/common/code_auto_generate_diff.py +5 -111
- autocoder/common/code_auto_generate_editblock.py +5 -95
- autocoder/common/code_auto_generate_strict_diff.py +6 -112
- autocoder/common/code_auto_merge_editblock.py +1 -45
- autocoder/common/code_modification_ranker.py +6 -2
- autocoder/common/command_templates.py +2 -9
- autocoder/common/conf_utils.py +36 -0
- autocoder/common/stream_out_type.py +7 -2
- autocoder/common/types.py +3 -2
- autocoder/common/v2/code_auto_generate.py +6 -4
- autocoder/common/v2/code_auto_generate_diff.py +4 -3
- autocoder/common/v2/code_auto_generate_editblock.py +9 -4
- autocoder/common/v2/code_auto_generate_strict_diff.py +182 -14
- autocoder/common/v2/code_auto_merge_diff.py +560 -306
- autocoder/common/v2/code_auto_merge_editblock.py +12 -45
- autocoder/common/v2/code_auto_merge_strict_diff.py +76 -7
- autocoder/common/v2/code_diff_manager.py +73 -6
- autocoder/common/v2/code_editblock_manager.py +534 -82
- autocoder/dispacher/actions/action.py +15 -28
- autocoder/dispacher/actions/plugins/action_regex_project.py +5 -9
- autocoder/helper/project_creator.py +0 -1
- autocoder/index/entry.py +35 -53
- autocoder/index/filter/normal_filter.py +0 -16
- autocoder/lang.py +2 -4
- autocoder/linters/shadow_linter.py +4 -0
- autocoder/pyproject/__init__.py +2 -19
- autocoder/rag/cache/simple_cache.py +31 -6
- autocoder/regexproject/__init__.py +4 -22
- autocoder/suffixproject/__init__.py +6 -24
- autocoder/tsproject/__init__.py +5 -22
- autocoder/version.py +1 -1
- {auto_coder-0.1.330.dist-info → auto_coder-0.1.332.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.330.dist-info → auto_coder-0.1.332.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.330.dist-info → auto_coder-0.1.332.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.330.dist-info → auto_coder-0.1.332.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,928 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
import byzerllm
|
|
7
|
+
from typing import List, Dict, Any, Union, Callable, Optional
|
|
8
|
+
from autocoder.common.printer import Printer
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from pydantic import SkipValidation
|
|
12
|
+
|
|
13
|
+
from autocoder.common.result_manager import ResultManager
|
|
14
|
+
from autocoder.utils.auto_coder_utils.chat_stream_out import stream_out
|
|
15
|
+
from byzerllm.utils.str2model import to_model
|
|
16
|
+
from autocoder.common import git_utils
|
|
17
|
+
from autocoder.commands.tools import AutoCommandTools
|
|
18
|
+
from autocoder.auto_coder import AutoCoderArgs
|
|
19
|
+
from autocoder.common import detect_env
|
|
20
|
+
from autocoder.common import shells
|
|
21
|
+
from loguru import logger
|
|
22
|
+
from autocoder.utils import llms as llms_utils
|
|
23
|
+
from autocoder.rag.token_counter import count_tokens
|
|
24
|
+
from autocoder.common.global_cancel import global_cancel
|
|
25
|
+
from autocoder.common.auto_configure import config_readme
|
|
26
|
+
from autocoder.utils.auto_project_type import ProjectTypeAnalyzer
|
|
27
|
+
from rich.text import Text
|
|
28
|
+
from autocoder.common.mcp_server import get_mcp_server, McpServerInfoRequest
|
|
29
|
+
from autocoder.common.action_yml_file_manager import ActionYmlFileManager
|
|
30
|
+
from autocoder.events.event_manager_singleton import get_event_manager
|
|
31
|
+
from autocoder.events import event_content as EventContentCreator
|
|
32
|
+
from autocoder.run_context import get_run_context
|
|
33
|
+
from autocoder.common.stream_out_type import AgenticFilterStreamOutType
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AgenticFilterRequest(BaseModel):
|
|
37
|
+
user_input: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class FileOperation(BaseModel):
|
|
41
|
+
path: str
|
|
42
|
+
operation: str # e.g., "MODIFY", "REFERENCE", "ADD", "REMOVE"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class AgenticFilterResponse(BaseModel):
|
|
46
|
+
files: List[FileOperation] # 文件列表,包含path和operation字段
|
|
47
|
+
reasoning: str # 决策过程说明
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CommandSuggestion(BaseModel):
|
|
51
|
+
command: str
|
|
52
|
+
parameters: Dict[str, Any]
|
|
53
|
+
confidence: float
|
|
54
|
+
reasoning: str
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class AutoCommandResponse(BaseModel):
|
|
58
|
+
suggestions: List[CommandSuggestion]
|
|
59
|
+
reasoning: Optional[str] = None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class AutoCommandRequest(BaseModel):
|
|
63
|
+
user_input: str
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class MemoryConfig(BaseModel):
|
|
67
|
+
"""
|
|
68
|
+
A model to encapsulate memory configuration and operations.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
memory: Dict[str, Any]
|
|
72
|
+
save_memory_func: SkipValidation[Callable]
|
|
73
|
+
|
|
74
|
+
class Config:
|
|
75
|
+
arbitrary_types_allowed = True
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class CommandConfig(BaseModel):
|
|
79
|
+
coding: SkipValidation[Callable]
|
|
80
|
+
chat: SkipValidation[Callable]
|
|
81
|
+
add_files: SkipValidation[Callable]
|
|
82
|
+
remove_files: SkipValidation[Callable]
|
|
83
|
+
index_build: SkipValidation[Callable]
|
|
84
|
+
index_query: SkipValidation[Callable]
|
|
85
|
+
list_files: SkipValidation[Callable]
|
|
86
|
+
ask: SkipValidation[Callable]
|
|
87
|
+
revert: SkipValidation[Callable]
|
|
88
|
+
commit: SkipValidation[Callable]
|
|
89
|
+
help: SkipValidation[Callable]
|
|
90
|
+
exclude_dirs: SkipValidation[Callable]
|
|
91
|
+
summon: SkipValidation[Callable]
|
|
92
|
+
design: SkipValidation[Callable]
|
|
93
|
+
mcp: SkipValidation[Callable]
|
|
94
|
+
models: SkipValidation[Callable]
|
|
95
|
+
lib: SkipValidation[Callable]
|
|
96
|
+
execute_shell_command: SkipValidation[Callable]
|
|
97
|
+
generate_shell_command: SkipValidation[Callable]
|
|
98
|
+
conf_export: SkipValidation[Callable]
|
|
99
|
+
conf_import: SkipValidation[Callable]
|
|
100
|
+
index_export: SkipValidation[Callable]
|
|
101
|
+
index_import: SkipValidation[Callable]
|
|
102
|
+
exclude_files: SkipValidation[Callable]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class AgenticFilter:
|
|
106
|
+
def __init__(
|
|
107
|
+
self,
|
|
108
|
+
llm: Union[byzerllm.ByzerLLM, byzerllm.SimpleByzerLLM],
|
|
109
|
+
conversation_history: List[Dict[str, Any]],
|
|
110
|
+
args: AutoCoderArgs,
|
|
111
|
+
memory_config: MemoryConfig,
|
|
112
|
+
command_config: Optional[CommandConfig] = None,
|
|
113
|
+
):
|
|
114
|
+
self.llm = llm
|
|
115
|
+
self.args = args
|
|
116
|
+
self.printer = Printer()
|
|
117
|
+
self.tools = AutoCommandTools(args=args, llm=self.llm)
|
|
118
|
+
self.result_manager = ResultManager(source_dir=args.source_dir)
|
|
119
|
+
# Use existing args for max iterations
|
|
120
|
+
self.max_iterations = args.auto_command_max_iterations
|
|
121
|
+
self.conversation_history = conversation_history
|
|
122
|
+
self.memory_config = memory_config
|
|
123
|
+
self.command_config = command_config
|
|
124
|
+
self.project_type_analyzer = ProjectTypeAnalyzer(args=args, llm=self.llm)
|
|
125
|
+
try:
|
|
126
|
+
self.mcp_server = get_mcp_server()
|
|
127
|
+
mcp_server_info_response = self.mcp_server.send_request(
|
|
128
|
+
McpServerInfoRequest(
|
|
129
|
+
model=args.inference_model or args.model,
|
|
130
|
+
product_mode=args.product_mode,
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
self.mcp_server_info = mcp_server_info_response.result
|
|
134
|
+
except Exception as e:
|
|
135
|
+
logger.error(f"Error getting MCP server info: {str(e)}")
|
|
136
|
+
self.mcp_server_info = ""
|
|
137
|
+
|
|
138
|
+
@byzerllm.prompt()
|
|
139
|
+
def _analyze(self, request: AgenticFilterRequest) -> str:
|
|
140
|
+
"""
|
|
141
|
+
## 当前用户环境信息如下:
|
|
142
|
+
<os_info>
|
|
143
|
+
操作系统: {{ env_info.os_name }} {{ env_info.os_version }}
|
|
144
|
+
操作系统发行版: {{ os_distribution }}
|
|
145
|
+
Python版本: {{ env_info.python_version }}
|
|
146
|
+
终端类型: {{ env_info.shell_type }}
|
|
147
|
+
终端编码: {{ env_info.shell_encoding }}
|
|
148
|
+
当前用户: {{ current_user }}
|
|
149
|
+
|
|
150
|
+
{%- if shell_type %}
|
|
151
|
+
脚本类型:{{ shell_type }}
|
|
152
|
+
{%- endif %}
|
|
153
|
+
|
|
154
|
+
{%- if env_info.conda_env %}
|
|
155
|
+
Conda环境: {{ env_info.conda_env }}
|
|
156
|
+
{%- endif %}
|
|
157
|
+
{%- if env_info.virtualenv %}
|
|
158
|
+
虚拟环境: {{ env_info.virtualenv }}
|
|
159
|
+
{%- endif %}
|
|
160
|
+
</os_info>
|
|
161
|
+
|
|
162
|
+
当前项目根目录:
|
|
163
|
+
{{ current_project }}
|
|
164
|
+
|
|
165
|
+
{% if current_files %}
|
|
166
|
+
## 当前用户手动添加关注的文件列表:
|
|
167
|
+
<current_files>
|
|
168
|
+
{% for file in current_files %}
|
|
169
|
+
- {{ file }}
|
|
170
|
+
{% endfor %}
|
|
171
|
+
</current_files>
|
|
172
|
+
{% endif %}
|
|
173
|
+
|
|
174
|
+
## 可用函数列表:
|
|
175
|
+
{{ available_commands }}
|
|
176
|
+
|
|
177
|
+
## 当前大模型窗口安全值
|
|
178
|
+
{{ conversation_safe_zone_tokens }}
|
|
179
|
+
|
|
180
|
+
## Token 安全区
|
|
181
|
+
对话和文件内容的总Token数不应超过 {{ conversation_safe_zone_tokens }}。请谨慎读取大文件。
|
|
182
|
+
|
|
183
|
+
## 对话历史
|
|
184
|
+
<conversation_history>
|
|
185
|
+
{% for msg in conversation_history %}
|
|
186
|
+
**{{ msg.role }}**: {{ msg.content }}
|
|
187
|
+
{% endfor %}
|
|
188
|
+
</conversation_history>
|
|
189
|
+
|
|
190
|
+
## 完成任务的一些实践指导
|
|
191
|
+
{{ command_combination_readme }}
|
|
192
|
+
|
|
193
|
+
## 你的任务以及要求
|
|
194
|
+
你是一个代码分析专家,需要根据用户的需求分析项目中相关的文件。你的工作是确定:
|
|
195
|
+
|
|
196
|
+
1. **需要修改的文件**(标记为"MODIFY"):直接需要更改的文件
|
|
197
|
+
2. **需要参考的文件**(标记为"REFERENCE"):理解需求或实现修改所需参考的文件
|
|
198
|
+
3. **需要新增的文件**(标记为"ADD"):实现需求可能需要创建的新文件
|
|
199
|
+
4. **需要删除的文件**(标记为"REMOVE"):实现需求可能需要删除的文件
|
|
200
|
+
|
|
201
|
+
请通过以下步骤进行分析:
|
|
202
|
+
1. 理解用户需求的核心目标
|
|
203
|
+
2. 使用提供的工具函数探索项目结构
|
|
204
|
+
3. 分析相关文件的内容和依赖关系
|
|
205
|
+
4. 确定需要修改、参考、新增或删除的文件列表
|
|
206
|
+
|
|
207
|
+
## 返回格式要求
|
|
208
|
+
返回格式必须是严格的JSON格式:
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"suggestions": [
|
|
213
|
+
{
|
|
214
|
+
"command": "函数名称",
|
|
215
|
+
"parameters": {},
|
|
216
|
+
"confidence": 0.9,
|
|
217
|
+
"reasoning": "推荐理由"
|
|
218
|
+
}
|
|
219
|
+
],
|
|
220
|
+
"reasoning": "整体推理说明"
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
请返回第一个建议的函数调用。我会将每个函数的执行结果提供给你,然后你可以根据这些结果确定下一步要执行的函数,直到完成分析。
|
|
225
|
+
"""
|
|
226
|
+
env_info = detect_env()
|
|
227
|
+
shell_type = "bash"
|
|
228
|
+
if shells.is_running_in_cmd():
|
|
229
|
+
shell_type = "cmd"
|
|
230
|
+
elif shells.is_running_in_powershell():
|
|
231
|
+
shell_type = "powershell"
|
|
232
|
+
return {
|
|
233
|
+
"user_input": request.user_input,
|
|
234
|
+
"current_files": self.memory_config.memory["current_files"]["files"],
|
|
235
|
+
"conversation_history": self.conversation_history,
|
|
236
|
+
"available_commands": self._command_readme.prompt(),
|
|
237
|
+
"current_conf": json.dumps(self.memory_config.memory["conf"], indent=2),
|
|
238
|
+
"env_info": env_info,
|
|
239
|
+
"shell_type": shell_type,
|
|
240
|
+
"shell_encoding": shells.get_terminal_encoding(),
|
|
241
|
+
"conversation_safe_zone_tokens": self.args.conversation_prune_safe_zone_tokens,
|
|
242
|
+
"os_distribution": shells.get_os_distribution(),
|
|
243
|
+
"current_user": shells.get_current_username(),
|
|
244
|
+
"command_combination_readme": self._command_combination_readme.prompt(
|
|
245
|
+
user_input=request.user_input
|
|
246
|
+
),
|
|
247
|
+
"current_project": os.path.abspath(self.args.source_dir),
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
@byzerllm.prompt()
|
|
251
|
+
def _command_combination_readme(self, user_input: str) -> str:
|
|
252
|
+
"""
|
|
253
|
+
## 操作流程建议
|
|
254
|
+
1. **理解需求**: 分析用户输入 `{{ user_input }}`。
|
|
255
|
+
2. **探索项目**:
|
|
256
|
+
* 使用 `list_files` 层层递进了解项目结构,用来确定需要关注的文件。
|
|
257
|
+
* 如果用户提及文件名等,则可以使用 `find_files_by_name` 或,如果用户提到关键字则可以使用 `find_files_by_content` 定位可能相关的文件。
|
|
258
|
+
* 如果用户提到了具体的符号(函数,类名等)则可以使用 `get_project_map` 获取候选文件的详细信息(如符号)。
|
|
259
|
+
3. **深入分析**:
|
|
260
|
+
* 使用 `read_files` 读取关键文件的内容进行确认。如果文件过大,使用 `line_ranges` 参数分段读取。
|
|
261
|
+
* 如有必要,使用 `run_python` 或 `execute_shell_command` 执行代码或命令进行更复杂的分析。
|
|
262
|
+
4. **迭代决策**: 根据工具的返回结果,你可能需要多次调用不同的工具来逐步缩小范围或获取更多信息。
|
|
263
|
+
5. **最终响应**: 当你确定了所有需要参考和修改的文件后,**必须**调用 `output_result` 工具,并提供符合其要求格式的JSON字符串作为其 `response` 参数。
|
|
264
|
+
该json格式要求为:
|
|
265
|
+
```json
|
|
266
|
+
{
|
|
267
|
+
"files": [
|
|
268
|
+
{"path": "/path/to/file1.py", "operation": "MODIFY"},
|
|
269
|
+
{"path": "/path/to/file2.md", "operation": "REFERENCE"},
|
|
270
|
+
{"path": "/path/to/new_file.txt", "operation": "ADD"},
|
|
271
|
+
{"path": "/path/to/old_file.log", "operation": "REMOVE"}
|
|
272
|
+
],
|
|
273
|
+
"reasoning": "详细说明你是如何通过分析和使用工具得出这个文件列表的。"
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
@byzerllm.prompt()
|
|
281
|
+
def _execute_command_result(self, result: str) -> str:
|
|
282
|
+
"""
|
|
283
|
+
根据函数执行结果,返回下一个函数。
|
|
284
|
+
|
|
285
|
+
下面是我们上一个函数执行结果:
|
|
286
|
+
|
|
287
|
+
<function_result>
|
|
288
|
+
{{ result }}
|
|
289
|
+
</function_result>
|
|
290
|
+
|
|
291
|
+
请根据命令执行结果以及前面的对话,返回下一个函数。
|
|
292
|
+
|
|
293
|
+
*** 非常非常重要的提示 ***
|
|
294
|
+
1. 如果你认为已经收集到足够信息来确定最终的文件列表,请务必调用 `output_result` 并以如下格式要求的JSON字符串作为 `response` 参数。最多允许 {{ max_iterations }} 次工具调用。
|
|
295
|
+
```json
|
|
296
|
+
{
|
|
297
|
+
"files": [
|
|
298
|
+
{"path": "/path/to/file1.py", "operation": "MODIFY"},
|
|
299
|
+
{"path": "/path/to/file2.md", "operation": "REFERENCE"},
|
|
300
|
+
{"path": "/path/to/new_file.txt", "operation": "ADD"},
|
|
301
|
+
{"path": "/path/to/old_file.log", "operation": "REMOVE"}
|
|
302
|
+
],
|
|
303
|
+
"reasoning": "详细说明你是如何通过分析和使用工具得出这个文件列表的。"
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
2. 你最多尝试 {{ auto_command_max_iterations }} 次,如果 {{ auto_command_max_iterations }} 次都没有满足要求,则不要返回任何函数,确保 suggestions 为空。
|
|
307
|
+
"""
|
|
308
|
+
return {
|
|
309
|
+
"auto_command_max_iterations": self.args.auto_command_max_iterations,
|
|
310
|
+
"conversation_safe_zone_tokens": self.args.conversation_prune_safe_zone_tokens,
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
@byzerllm.prompt()
|
|
314
|
+
def _command_readme(self) -> str:
|
|
315
|
+
"""
|
|
316
|
+
你有如下函数可供使用:
|
|
317
|
+
<commands>
|
|
318
|
+
<name>ask_user</name>
|
|
319
|
+
<description>
|
|
320
|
+
如果你对用户的问题有什么疑问,或者你想从用户收集一些额外信息,可以调用此方法。
|
|
321
|
+
输入参数 question 是你对用户的提问。
|
|
322
|
+
返回值是 用户对你问题的回答。
|
|
323
|
+
** 如果你的问题比较多,建议一次就问一个,然后根据用户回答再问下一个。 **
|
|
324
|
+
</description>
|
|
325
|
+
<usage>
|
|
326
|
+
该命令接受一个参数 question,为需要向用户询问的问题字符串。
|
|
327
|
+
|
|
328
|
+
使用例子:
|
|
329
|
+
ask_user(question="请输入火山引擎的 R1 模型推理点")
|
|
330
|
+
</command>
|
|
331
|
+
|
|
332
|
+
<command>
|
|
333
|
+
<name>run_python</name>
|
|
334
|
+
<description>运行指定的Python代码。主要用于执行一些Python脚本或测试代码。</description>
|
|
335
|
+
<usage>
|
|
336
|
+
该命令接受一个参数 code,为要执行的Python代码字符串。
|
|
337
|
+
|
|
338
|
+
使用例子:
|
|
339
|
+
|
|
340
|
+
run_python(code="print('Hello World')")
|
|
341
|
+
|
|
342
|
+
注意:
|
|
343
|
+
- 代码将在项目根目录下执行
|
|
344
|
+
- 可以访问项目中的所有文件
|
|
345
|
+
- 输出结果会返回给用户
|
|
346
|
+
</usage>
|
|
347
|
+
</command>
|
|
348
|
+
|
|
349
|
+
<command>
|
|
350
|
+
<name>execute_shell_command</name>
|
|
351
|
+
<description>运行指定的Shell脚本。主要用于编译、运行、测试等任务。</description>
|
|
352
|
+
<usage>
|
|
353
|
+
该命令接受一个参数 command,为要执行的Shell脚本字符串。
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
使用例子:
|
|
357
|
+
|
|
358
|
+
execute_shell_command(command="ls -l")
|
|
359
|
+
|
|
360
|
+
注意:
|
|
361
|
+
- 脚本将在项目根目录下执行
|
|
362
|
+
- 禁止执行包含 rm 命令的脚本
|
|
363
|
+
- 输出结果会返回给用户
|
|
364
|
+
- 执行该命令的时候,需要通过 ask_user 询问用户是否同意执行,如果用户拒绝,则不再执行当前想执行的脚本呢。
|
|
365
|
+
</usage>
|
|
366
|
+
</command>
|
|
367
|
+
|
|
368
|
+
<command>
|
|
369
|
+
<name>generate_shell_command</name>
|
|
370
|
+
<description>
|
|
371
|
+
根据用户需求描述,生成shell脚本。
|
|
372
|
+
</description>
|
|
373
|
+
<usage>
|
|
374
|
+
支持的参数名为 input_text, 字符串类型,用户的需求,使用该函数,会打印生成结果,用户可以更加清晰
|
|
375
|
+
的看到生成的脚本。然后配合 ask_user, execute_shell_command 两个函数,最终完成
|
|
376
|
+
脚本执行。
|
|
377
|
+
</usage>
|
|
378
|
+
</command>
|
|
379
|
+
|
|
380
|
+
<command>
|
|
381
|
+
<name>get_project_map</name>
|
|
382
|
+
<description>返回项目中指定文件包括文件用途、导入的包、定义的类、函数、变量等。</description>
|
|
383
|
+
<usage>
|
|
384
|
+
该命令接受一个参数 file_paths,路径list,或者是以逗号分割的多个文件路径。
|
|
385
|
+
路径支持相对路径和绝对路径。
|
|
386
|
+
|
|
387
|
+
使用例子:
|
|
388
|
+
|
|
389
|
+
get_project_map(file_paths=["full/path/to/main.py","partial/path/to/utils.py"]),
|
|
390
|
+
|
|
391
|
+
或者:
|
|
392
|
+
|
|
393
|
+
get_project_map(file_paths="full/path/to/main.py,partial/path/to/utils.py")
|
|
394
|
+
|
|
395
|
+
该函数特别适合你想要了解某个文件的用途,以及该文件的导入的包,定义的类,函数,变量等信息。
|
|
396
|
+
同时,你还能看到文件的大小(tokens数),以及索引的大小(tokens数),以及构建索引花费费用等信息。
|
|
397
|
+
如果你觉得该文件确实是你关注的,你可以通过 read_files 函数来读取文件完整内容,从而帮你做更好的决策。
|
|
398
|
+
|
|
399
|
+
注意:
|
|
400
|
+
- 返回值为JSON格式文本
|
|
401
|
+
- 只能返回已被索引的文件
|
|
402
|
+
</usage>
|
|
403
|
+
</command>
|
|
404
|
+
|
|
405
|
+
<command>
|
|
406
|
+
<name>list_files</name>
|
|
407
|
+
<description>list_files 查看某个目录下的所有文件</description>
|
|
408
|
+
<usage>
|
|
409
|
+
该命令接受一个参数 path, 为要查看的目录路径。
|
|
410
|
+
使用例子:
|
|
411
|
+
list_files(path="path/to/dir")
|
|
412
|
+
|
|
413
|
+
</usage>
|
|
414
|
+
</command>
|
|
415
|
+
|
|
416
|
+
<command>
|
|
417
|
+
<name>read_files</name>
|
|
418
|
+
<description>读取指定文件的内容(支持指定行范围),支持文件名或绝对路径。</description>
|
|
419
|
+
<usage>
|
|
420
|
+
该函数用于读取指定文件的内容。
|
|
421
|
+
|
|
422
|
+
参数说明:
|
|
423
|
+
1. paths (str):
|
|
424
|
+
- 以逗号分隔的文件路径列表
|
|
425
|
+
- 支持两种格式:
|
|
426
|
+
a) 文件名: 如果多个文件匹配该名称,将选择第一个匹配项
|
|
427
|
+
b) 绝对路径: 直接指定文件的完整路径
|
|
428
|
+
- 示例: "main.py,utils.py" 或 "/path/to/main.py,/path/to/utils.py"
|
|
429
|
+
- 建议: 每次调用推荐一个文件,最多不要超过3个文件。
|
|
430
|
+
|
|
431
|
+
2. line_ranges (Optional[str]):
|
|
432
|
+
- 可选参数,用于指定每个文件要读取的具体行范围
|
|
433
|
+
- 格式说明:
|
|
434
|
+
* 使用逗号分隔不同文件的行范围
|
|
435
|
+
* 每个文件可以指定多个行范围,用/分隔
|
|
436
|
+
* 每个行范围使用-连接起始行和结束行
|
|
437
|
+
- 示例:
|
|
438
|
+
* "1-100,2-50" (为两个文件分别指定一个行范围)
|
|
439
|
+
* "1-100/200-300,50-100" (第一个文件指定两个行范围,第二个文件指定一个行范围)
|
|
440
|
+
- 注意: line_ranges中的文件数量必须与paths中的文件数量一致,否则会抛出错误
|
|
441
|
+
|
|
442
|
+
返回值:
|
|
443
|
+
- 返回str类型,包含所有请求文件的内容
|
|
444
|
+
- 每个文件内容前会标注文件路径和行范围信息(如果指定了行范围)
|
|
445
|
+
|
|
446
|
+
使用例子:
|
|
447
|
+
|
|
448
|
+
read_files(paths="main.py,utils.py", line_ranges="1-100/200-300,50-100")
|
|
449
|
+
|
|
450
|
+
read_files(paths="main.py,utils.py")
|
|
451
|
+
|
|
452
|
+
你可以使用 get_project_structure 函数获取项目结构后,然后再通过 get_project_map 函数获取某个文件的用途,符号列表,以及
|
|
453
|
+
文件大小(tokens数),最后再通过 read_files 函数来读取文件内容,从而帮你做更好的决策。如果需要读取的文件过大,
|
|
454
|
+
|
|
455
|
+
特别注意:使用 read_files 时,一次性读取文件数量不要超过1个,每次只读取200行。如果发现读取的内容不够,则继续读取下面200行。
|
|
456
|
+
|
|
457
|
+
</usage>
|
|
458
|
+
</command>
|
|
459
|
+
|
|
460
|
+
<command>
|
|
461
|
+
<name>find_files_by_name</name>
|
|
462
|
+
<description>根据文件名中的关键字搜索文件。</description>
|
|
463
|
+
<usage>
|
|
464
|
+
该命令接受一个参数 keyword,为要搜索的关键字字符串。
|
|
465
|
+
|
|
466
|
+
使用例子:
|
|
467
|
+
|
|
468
|
+
find_files_by_name(keyword="test")
|
|
469
|
+
|
|
470
|
+
注意:
|
|
471
|
+
- 搜索不区分大小写
|
|
472
|
+
- 返回所有匹配的文件路径,逗号分隔
|
|
473
|
+
</usage>
|
|
474
|
+
</command>
|
|
475
|
+
|
|
476
|
+
<command>
|
|
477
|
+
<name>find_files_by_content</name>
|
|
478
|
+
<description>根据文件内容中的关键字搜索文件。</description>
|
|
479
|
+
<usage>
|
|
480
|
+
该命令接受一个参数 keyword,为要搜索的关键字字符串。
|
|
481
|
+
|
|
482
|
+
使用例子:
|
|
483
|
+
|
|
484
|
+
find_files_by_content(keyword="TODO")
|
|
485
|
+
|
|
486
|
+
注意:
|
|
487
|
+
- 搜索不区分大小写
|
|
488
|
+
- 如果结果过多,只返回前10个匹配项
|
|
489
|
+
</usage>
|
|
490
|
+
</command>
|
|
491
|
+
|
|
492
|
+
<command>
|
|
493
|
+
<name>read_file_with_keyword_ranges</name>
|
|
494
|
+
<description>读取包含指定关键字的行及其前后指定范围的行。</description>
|
|
495
|
+
<usage>
|
|
496
|
+
该函数用于读取包含关键字的行及其前后指定范围的行。
|
|
497
|
+
|
|
498
|
+
参数说明:
|
|
499
|
+
1. file_path (str): 文件路径,可以是相对路径或绝对路径
|
|
500
|
+
2. keyword (str): 要搜索的关键字
|
|
501
|
+
3. before_size (int): 关键字行之前要读取的行数,默认100
|
|
502
|
+
4. after_size (int): 关键字行之后要读取的行数,默认100
|
|
503
|
+
|
|
504
|
+
返回值:
|
|
505
|
+
- 返回str类型,包含关键字的行及其前后指定范围的行
|
|
506
|
+
- 格式如下:
|
|
507
|
+
```
|
|
508
|
+
##File: /path/to/file.py
|
|
509
|
+
##Line: 10-20
|
|
510
|
+
|
|
511
|
+
内容
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
使用例子:
|
|
515
|
+
read_file_with_keyword_ranges(file_path="main.py", keyword="TODO", before_size=5, after_size=5)
|
|
516
|
+
|
|
517
|
+
注意:
|
|
518
|
+
- 如果文件中有多个匹配的关键字,会返回多个内容块
|
|
519
|
+
- 搜索不区分大小写
|
|
520
|
+
</usage>
|
|
521
|
+
</command>
|
|
522
|
+
|
|
523
|
+
<command>
|
|
524
|
+
<name>output_result</name>
|
|
525
|
+
<description>输出最后需要的结果</description>
|
|
526
|
+
<usage>
|
|
527
|
+
只有一个参数:
|
|
528
|
+
response: 字符串类型,需要返回给用户的内容。 response 必须满足如下Json格式:
|
|
529
|
+
|
|
530
|
+
```json
|
|
531
|
+
{
|
|
532
|
+
"files": [
|
|
533
|
+
{"path": "/path/to/file1.py", "operation": "MODIFY"},
|
|
534
|
+
{"path": "/path/to/file2.md", "operation": "REFERENCE"},
|
|
535
|
+
{"path": "/path/to/new_file.txt", "operation": "ADD"},
|
|
536
|
+
{"path": "/path/to/old_file.log", "operation": "REMOVE"}
|
|
537
|
+
],
|
|
538
|
+
"reasoning": "详细说明你是如何通过分析和使用工具得出这个文件列表的。"
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
使用例子:
|
|
543
|
+
output_result(response='{"files": [{"path": "/path/to/file1.py", "operation": "MODIFY"}, {"path": "/path/to/file2.md", "operation": "REFERENCE"}, {"path": "/path/to/new_file.txt", "operation": "ADD"}, {"path": "/path/to/old_file.log", "operation": "REMOVE"}], "reasoning": "详细说明你是如何通过分析和使用工具得出这个文件列表的。"}')
|
|
544
|
+
</usage>
|
|
545
|
+
</command>
|
|
546
|
+
|
|
547
|
+
<command>
|
|
548
|
+
<name>count_file_tokens</name>
|
|
549
|
+
<description>计算指定文件的token数量。</description>
|
|
550
|
+
<usage>
|
|
551
|
+
该函数接受一个参数 file_path, 为要计算的文件路径。
|
|
552
|
+
|
|
553
|
+
使用例子:
|
|
554
|
+
count_file_tokens(file_path="full")
|
|
555
|
+
|
|
556
|
+
注意:
|
|
557
|
+
- 返回值为int类型,表示文件的token数量。
|
|
558
|
+
|
|
559
|
+
</usage>
|
|
560
|
+
</command>
|
|
561
|
+
|
|
562
|
+
<command>
|
|
563
|
+
<name>count_string_tokens</name>
|
|
564
|
+
<description>计算指定字符串的token数量。</description>
|
|
565
|
+
<usage>
|
|
566
|
+
该函数接受一个参数 text, 为要计算的文本。
|
|
567
|
+
|
|
568
|
+
使用例子:
|
|
569
|
+
count_string_tokens(text="你好,世界")
|
|
570
|
+
|
|
571
|
+
注意:
|
|
572
|
+
- 返回值为int类型,表示文本的token数量。
|
|
573
|
+
|
|
574
|
+
</usage>
|
|
575
|
+
</command>
|
|
576
|
+
|
|
577
|
+
<command>
|
|
578
|
+
<n>find_symbol_definition</n>
|
|
579
|
+
<description>查找指定符号的定义所在的文件路径。</description>
|
|
580
|
+
<usage>
|
|
581
|
+
该函数接受一个参数 symbol, 为要查找的符号名称。
|
|
582
|
+
|
|
583
|
+
使用例子:
|
|
584
|
+
find_symbol_definition(symbol="MyClass")
|
|
585
|
+
find_symbol_definition(symbol="process_data")
|
|
586
|
+
|
|
587
|
+
注意:
|
|
588
|
+
- 返回值为字符串,包含符号定义所在的文件路径列表,以逗号分隔
|
|
589
|
+
- 支持精确匹配和模糊匹配(不区分大小写)
|
|
590
|
+
- 如果未找到匹配项,会返回提示信息
|
|
591
|
+
|
|
592
|
+
</usage>
|
|
593
|
+
</command>
|
|
594
|
+
|
|
595
|
+
<command>
|
|
596
|
+
<n>execute_mcp_server</n>
|
|
597
|
+
<description>执行MCP服务器</description>
|
|
598
|
+
<usage>
|
|
599
|
+
该函数接受一个参数 query, 为要执行的MCP服务器查询字符串。
|
|
600
|
+
|
|
601
|
+
你可以根据下面已经连接的 mcp server 信息,来决定个是否调用该函数,注意该函数会更具你的 query
|
|
602
|
+
自动选择合适的 mcp server 来执行。如果你想某个特定的 server 来执行,你可以在 query 中说明你想哪个 server 执行。
|
|
603
|
+
|
|
604
|
+
<mcp_server_info>
|
|
605
|
+
{{ mcp_server_info }}
|
|
606
|
+
</mcp_server_info>
|
|
607
|
+
|
|
608
|
+
</usage>
|
|
609
|
+
</command>
|
|
610
|
+
"""
|
|
611
|
+
return {
|
|
612
|
+
"config_readme": config_readme.prompt(),
|
|
613
|
+
"mcp_server_info": self.mcp_server_info,
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
def analyze(self, request: AgenticFilterRequest) -> Optional[AgenticFilterResponse]:
|
|
617
|
+
# 获取 prompt 内容
|
|
618
|
+
prompt = self._analyze.prompt(request)
|
|
619
|
+
|
|
620
|
+
# 获取对当前项目变更的最近8条历史人物
|
|
621
|
+
action_yml_file_manager = ActionYmlFileManager(self.args.source_dir)
|
|
622
|
+
history_tasks = action_yml_file_manager.to_tasks_prompt(limit=8)
|
|
623
|
+
new_messages = []
|
|
624
|
+
if self.args.enable_task_history:
|
|
625
|
+
new_messages.append({"role": "user", "content": history_tasks})
|
|
626
|
+
new_messages.append(
|
|
627
|
+
{
|
|
628
|
+
"role": "assistant",
|
|
629
|
+
"content": "好的,我知道最近的任务对项目的变更了,我会参考这些来更好的理解你的需求。",
|
|
630
|
+
}
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
# 构造对话上下文
|
|
634
|
+
conversations = new_messages + [{"role": "user", "content": prompt}]
|
|
635
|
+
|
|
636
|
+
# 使用 stream_out 进行输出
|
|
637
|
+
printer = Printer()
|
|
638
|
+
title = printer.get_message_from_key("auto_command_analyzing")
|
|
639
|
+
final_title = printer.get_message_from_key("auto_command_analyzed")
|
|
640
|
+
|
|
641
|
+
def extract_command_response(content: str) -> str:
|
|
642
|
+
# 提取 JSON 并转换为 AutoCommandResponse
|
|
643
|
+
try:
|
|
644
|
+
response = to_model(content, AutoCommandResponse)
|
|
645
|
+
if response.suggestions:
|
|
646
|
+
command = response.suggestions[0].command
|
|
647
|
+
parameters = response.suggestions[0].parameters
|
|
648
|
+
if parameters:
|
|
649
|
+
params_str = ", ".join(
|
|
650
|
+
[f"{k}={v}" for k, v in parameters.items()]
|
|
651
|
+
)
|
|
652
|
+
else:
|
|
653
|
+
params_str = ""
|
|
654
|
+
return f"{command}({params_str})"
|
|
655
|
+
else:
|
|
656
|
+
return printer.get_message_from_key("satisfied_prompt")
|
|
657
|
+
except Exception as e:
|
|
658
|
+
logger.error(f"Error extracting command response: {str(e)}")
|
|
659
|
+
return content
|
|
660
|
+
|
|
661
|
+
result_manager = ResultManager()
|
|
662
|
+
success_flag = False
|
|
663
|
+
|
|
664
|
+
get_event_manager(self.args.event_file).write_result(
|
|
665
|
+
EventContentCreator.create_result(content=printer.get_message_from_key("agenticFilterContext")),
|
|
666
|
+
metadata={
|
|
667
|
+
"stream_out_type": AgenticFilterStreamOutType.AGENTIC_FILTER.value
|
|
668
|
+
}
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
while True:
|
|
672
|
+
global_cancel.check_and_raise()
|
|
673
|
+
# print(json.dumps(conversations, ensure_ascii=False, indent=4))
|
|
674
|
+
model_name = ",".join(llms_utils.get_llm_names(self.llm))
|
|
675
|
+
start_time = time.monotonic()
|
|
676
|
+
result, last_meta = stream_out(
|
|
677
|
+
self.llm.stream_chat_oai(conversations=conversations, delta_mode=True),
|
|
678
|
+
model_name=model_name,
|
|
679
|
+
title=title,
|
|
680
|
+
final_title=final_title,
|
|
681
|
+
display_func=extract_command_response,
|
|
682
|
+
args=self.args,
|
|
683
|
+
extra_meta={
|
|
684
|
+
"stream_out_type": AgenticFilterStreamOutType.AGENTIC_FILTER.value
|
|
685
|
+
},
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
if last_meta:
|
|
689
|
+
elapsed_time = time.monotonic() - start_time
|
|
690
|
+
speed = last_meta.generated_tokens_count / elapsed_time
|
|
691
|
+
|
|
692
|
+
# Get model info for pricing
|
|
693
|
+
from autocoder.utils import llms as llm_utils
|
|
694
|
+
|
|
695
|
+
model_info = (
|
|
696
|
+
llm_utils.get_model_info(model_name, self.args.product_mode) or {}
|
|
697
|
+
)
|
|
698
|
+
input_price = model_info.get("input_price", 0.0) if model_info else 0.0
|
|
699
|
+
output_price = (
|
|
700
|
+
model_info.get("output_price", 0.0) if model_info else 0.0
|
|
701
|
+
)
|
|
702
|
+
|
|
703
|
+
# Calculate costs
|
|
704
|
+
input_cost = (
|
|
705
|
+
last_meta.input_tokens_count * input_price
|
|
706
|
+
) / 1000000 # Convert to millions
|
|
707
|
+
output_cost = (
|
|
708
|
+
last_meta.generated_tokens_count * output_price
|
|
709
|
+
) / 1000000 # Convert to millions
|
|
710
|
+
|
|
711
|
+
temp_content = printer.get_message_from_key_with_format(
|
|
712
|
+
"stream_out_stats",
|
|
713
|
+
model_name=",".join(llms_utils.get_llm_names(self.llm)),
|
|
714
|
+
elapsed_time=elapsed_time,
|
|
715
|
+
first_token_time=last_meta.first_token_time,
|
|
716
|
+
input_tokens=last_meta.input_tokens_count,
|
|
717
|
+
output_tokens=last_meta.generated_tokens_count,
|
|
718
|
+
input_cost=round(input_cost, 4),
|
|
719
|
+
output_cost=round(output_cost, 4),
|
|
720
|
+
speed=round(speed, 2),
|
|
721
|
+
)
|
|
722
|
+
printer.print_str_in_terminal(temp_content)
|
|
723
|
+
get_event_manager(self.args.event_file).write_result(
|
|
724
|
+
EventContentCreator.create_result(
|
|
725
|
+
content=EventContentCreator.ResultTokenStatContent(
|
|
726
|
+
model_name=model_name,
|
|
727
|
+
elapsed_time=elapsed_time,
|
|
728
|
+
first_token_time=last_meta.first_token_time,
|
|
729
|
+
input_tokens=last_meta.input_tokens_count,
|
|
730
|
+
output_tokens=last_meta.generated_tokens_count,
|
|
731
|
+
input_cost=round(input_cost, 4),
|
|
732
|
+
output_cost=round(output_cost, 4),
|
|
733
|
+
speed=round(speed, 2),
|
|
734
|
+
)
|
|
735
|
+
).to_dict()
|
|
736
|
+
)
|
|
737
|
+
|
|
738
|
+
conversations.append({"role": "assistant", "content": result})
|
|
739
|
+
# 提取 JSON 并转换为 AutoCommandResponse
|
|
740
|
+
response = to_model(result, AutoCommandResponse)
|
|
741
|
+
|
|
742
|
+
if not response or not response.suggestions:
|
|
743
|
+
break
|
|
744
|
+
|
|
745
|
+
# 执行命令
|
|
746
|
+
command = response.suggestions[0].command
|
|
747
|
+
parameters = response.suggestions[0].parameters
|
|
748
|
+
|
|
749
|
+
# 打印正在执行的命令
|
|
750
|
+
temp_content = printer.get_message_from_key_with_format(
|
|
751
|
+
"auto_command_executing", command=command
|
|
752
|
+
)
|
|
753
|
+
printer.print_str_in_terminal(temp_content, style="blue")
|
|
754
|
+
|
|
755
|
+
get_event_manager(self.args.event_file).write_result(
|
|
756
|
+
EventContentCreator.create_result(
|
|
757
|
+
content=EventContentCreator.ResultCommandPrepareStatContent(
|
|
758
|
+
command=command, parameters=parameters
|
|
759
|
+
).to_dict()
|
|
760
|
+
),metadata={
|
|
761
|
+
"stream_out_type": AgenticFilterStreamOutType.AGENTIC_FILTER.value
|
|
762
|
+
}
|
|
763
|
+
)
|
|
764
|
+
try:
|
|
765
|
+
self.execute_auto_command(command, parameters)
|
|
766
|
+
except Exception as e:
|
|
767
|
+
error_content = f"执行命令失败,错误信息:{e}"
|
|
768
|
+
conversations.append({"role": "user", "content": error_content})
|
|
769
|
+
continue
|
|
770
|
+
|
|
771
|
+
content = ""
|
|
772
|
+
last_result = result_manager.get_last()
|
|
773
|
+
if last_result:
|
|
774
|
+
action = last_result.meta["action"]
|
|
775
|
+
if action == "coding":
|
|
776
|
+
# 如果上一步是 coding,则需要把上一步的更改前和更改后的内容作为上下文
|
|
777
|
+
changes = git_utils.get_changes_by_commit_message(
|
|
778
|
+
"", last_result.meta["commit_message"]
|
|
779
|
+
)
|
|
780
|
+
if changes.success:
|
|
781
|
+
for file_path, change in changes.changes.items():
|
|
782
|
+
if change:
|
|
783
|
+
content += f"## File: {file_path}[更改前]\n{change.before or 'New File'}\n\nFile: {file_path}\n\n[更改后]\n{change.after or 'Deleted File'}\n\n"
|
|
784
|
+
else:
|
|
785
|
+
content = printer.get_message_from_key("no_changes_made")
|
|
786
|
+
else:
|
|
787
|
+
# 其他的直接获取执行结果
|
|
788
|
+
content = last_result.content
|
|
789
|
+
|
|
790
|
+
if action != command:
|
|
791
|
+
# command 和 action 不一致,则认为命令执行失败,退出
|
|
792
|
+
temp_content = printer.get_message_from_key_with_format(
|
|
793
|
+
"auto_command_action_break", command=command, action=action
|
|
794
|
+
)
|
|
795
|
+
printer.print_str_in_terminal(temp_content, style="yellow")
|
|
796
|
+
get_event_manager(self.args.event_file).write_result(
|
|
797
|
+
EventContentCreator.create_result(content=temp_content),
|
|
798
|
+
metadata={
|
|
799
|
+
"stream_out_type": AgenticFilterStreamOutType.AGENTIC_FILTER.value
|
|
800
|
+
}
|
|
801
|
+
)
|
|
802
|
+
break
|
|
803
|
+
|
|
804
|
+
if command == "output_result":
|
|
805
|
+
success_flag = True
|
|
806
|
+
break
|
|
807
|
+
|
|
808
|
+
get_event_manager(self.args.event_file).write_result(
|
|
809
|
+
EventContentCreator.create_result(
|
|
810
|
+
content=EventContentCreator.ResultCommandExecuteStatContent(
|
|
811
|
+
command=command, content=content
|
|
812
|
+
).to_dict(),
|
|
813
|
+
metadata={
|
|
814
|
+
"stream_out_type": AgenticFilterStreamOutType.AGENTIC_FILTER.value
|
|
815
|
+
}
|
|
816
|
+
)
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
# 打印执行结果
|
|
820
|
+
console = Console()
|
|
821
|
+
# 截取content前后200字符
|
|
822
|
+
truncated_content = (
|
|
823
|
+
content[:200] + "\n...\n" + content[-200:]
|
|
824
|
+
if len(content) > 400
|
|
825
|
+
else content
|
|
826
|
+
)
|
|
827
|
+
title = printer.get_message_from_key_with_format(
|
|
828
|
+
"command_execution_result", action=action
|
|
829
|
+
)
|
|
830
|
+
# 转义内容,避免Rich将内容中的[]解释为markup语法
|
|
831
|
+
text_content = Text(truncated_content)
|
|
832
|
+
console.print(
|
|
833
|
+
Panel(
|
|
834
|
+
text_content, title=title, border_style="blue", padding=(1, 2)
|
|
835
|
+
)
|
|
836
|
+
)
|
|
837
|
+
|
|
838
|
+
# 添加新的对话内容
|
|
839
|
+
new_content = self._execute_command_result.prompt(content)
|
|
840
|
+
conversations.append({"role": "user", "content": new_content})
|
|
841
|
+
|
|
842
|
+
# 统计 token 数量
|
|
843
|
+
total_tokens = count_tokens(
|
|
844
|
+
json.dumps(conversations, ensure_ascii=False)
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
# 如果对话过长,使用默认策略进行修剪
|
|
848
|
+
if total_tokens > self.args.conversation_prune_safe_zone_tokens:
|
|
849
|
+
self.printer.print_in_terminal(
|
|
850
|
+
"conversation_pruning_start",
|
|
851
|
+
style="yellow",
|
|
852
|
+
total_tokens=total_tokens,
|
|
853
|
+
safe_zone=self.args.conversation_prune_safe_zone_tokens,
|
|
854
|
+
)
|
|
855
|
+
from autocoder.common.conversation_pruner import ConversationPruner
|
|
856
|
+
|
|
857
|
+
pruner = ConversationPruner(self.args, self.llm)
|
|
858
|
+
conversations = pruner.prune_conversations(conversations)
|
|
859
|
+
|
|
860
|
+
else:
|
|
861
|
+
temp_content = printer.get_message_from_key_with_format(
|
|
862
|
+
"auto_command_break", command=command
|
|
863
|
+
)
|
|
864
|
+
printer.print_str_in_terminal(temp_content, style="yellow")
|
|
865
|
+
get_event_manager(self.args.event_file).write_result(
|
|
866
|
+
EventContentCreator.create_result(content=temp_content),
|
|
867
|
+
metadata={
|
|
868
|
+
"stream_out_type": AgenticFilterStreamOutType.AGENTIC_FILTER.value
|
|
869
|
+
}
|
|
870
|
+
)
|
|
871
|
+
break
|
|
872
|
+
|
|
873
|
+
get_event_manager(self.args.event_file).write_result(
|
|
874
|
+
EventContentCreator.create_result(content=printer.get_message_from_key("agenticFilterCommandResult")),
|
|
875
|
+
metadata={
|
|
876
|
+
"stream_out_type": AgenticFilterStreamOutType.AGENTIC_FILTER.value
|
|
877
|
+
}
|
|
878
|
+
)
|
|
879
|
+
|
|
880
|
+
if success_flag:
|
|
881
|
+
return AgenticFilterResponse(**json.loads(content))
|
|
882
|
+
else:
|
|
883
|
+
return None
|
|
884
|
+
|
|
885
|
+
def execute_auto_command(self, command: str, parameters: Dict[str, Any]) -> None:
|
|
886
|
+
"""
|
|
887
|
+
执行自动生成的命令
|
|
888
|
+
"""
|
|
889
|
+
command_map = {
|
|
890
|
+
"run_python": self.tools.run_python_code,
|
|
891
|
+
"get_related_files_by_symbols": self.tools.get_related_files_by_symbols,
|
|
892
|
+
"get_project_map": self.tools.get_project_map,
|
|
893
|
+
"get_project_structure": self.tools.get_project_structure,
|
|
894
|
+
"list_files": self.tools.list_files,
|
|
895
|
+
"read_files": self.tools.read_files,
|
|
896
|
+
"find_files_by_name": self.tools.find_files_by_name,
|
|
897
|
+
"find_files_by_content": self.tools.find_files_by_content,
|
|
898
|
+
"get_project_related_files": self.tools.get_project_related_files,
|
|
899
|
+
"ask_user": self.tools.ask_user,
|
|
900
|
+
"read_file_with_keyword_ranges": self.tools.read_file_with_keyword_ranges,
|
|
901
|
+
"get_project_type": self.project_type_analyzer.analyze,
|
|
902
|
+
"output_result": self.tools.output_result,
|
|
903
|
+
"execute_mcp_server": self.tools.execute_mcp_server,
|
|
904
|
+
"count_file_tokens": self.tools.count_file_tokens,
|
|
905
|
+
"count_string_tokens": self.tools.count_string_tokens,
|
|
906
|
+
"find_symbol_definition": self.tools.find_symbol_definition,
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
if command not in command_map:
|
|
910
|
+
v = self.printer.get_message_from_key_with_format(
|
|
911
|
+
"auto_command_not_found", style="red", command=command
|
|
912
|
+
)
|
|
913
|
+
raise Exception(v)
|
|
914
|
+
return
|
|
915
|
+
|
|
916
|
+
try:
|
|
917
|
+
# 将参数字典转换为命令所需的格式
|
|
918
|
+
if parameters:
|
|
919
|
+
command_map[command](**parameters)
|
|
920
|
+
else:
|
|
921
|
+
command_map[command]()
|
|
922
|
+
|
|
923
|
+
except Exception as e:
|
|
924
|
+
error_msg = str(e)
|
|
925
|
+
v = self.printer.get_message_from_key_with_format(
|
|
926
|
+
"auto_command_failed", style="red", command=command, error=error_msg
|
|
927
|
+
)
|
|
928
|
+
raise Exception(v)
|