auto-coder 0.1.259__py3-none-any.whl → 0.1.261__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.259.dist-info → auto_coder-0.1.261.dist-info}/METADATA +1 -1
- {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/RECORD +36 -27
- autocoder/agent/auto_review_commit.py +51 -24
- autocoder/auto_coder.py +24 -1
- autocoder/chat_auto_coder.py +377 -399
- autocoder/chat_auto_coder_lang.py +20 -0
- autocoder/commands/__init__.py +0 -0
- autocoder/commands/auto_command.py +1174 -0
- autocoder/commands/tools.py +533 -0
- autocoder/common/__init__.py +8 -0
- autocoder/common/auto_coder_lang.py +61 -8
- autocoder/common/auto_configure.py +304 -0
- autocoder/common/code_auto_merge.py +2 -2
- autocoder/common/code_auto_merge_diff.py +2 -2
- autocoder/common/code_auto_merge_editblock.py +2 -2
- autocoder/common/code_auto_merge_strict_diff.py +2 -2
- autocoder/common/code_modification_ranker.py +8 -7
- autocoder/common/command_completer.py +557 -0
- autocoder/common/conf_validator.py +245 -0
- autocoder/common/conversation_pruner.py +131 -0
- autocoder/common/git_utils.py +82 -1
- autocoder/common/index_import_export.py +101 -0
- autocoder/common/result_manager.py +115 -0
- autocoder/common/shells.py +22 -6
- autocoder/common/utils_code_auto_generate.py +2 -2
- autocoder/dispacher/actions/action.py +45 -4
- autocoder/dispacher/actions/plugins/action_regex_project.py +13 -1
- autocoder/index/filter/quick_filter.py +22 -7
- autocoder/utils/auto_coder_utils/chat_stream_out.py +13 -6
- autocoder/utils/project_structure.py +15 -0
- autocoder/utils/thread_utils.py +4 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1174 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
import byzerllm
|
|
6
|
+
from typing import List, Dict, Any, Union, Callable, Optional
|
|
7
|
+
from autocoder.common.printer import Printer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from pydantic import SkipValidation
|
|
11
|
+
|
|
12
|
+
from autocoder.common.result_manager import ResultManager
|
|
13
|
+
from autocoder.utils.auto_coder_utils.chat_stream_out import stream_out
|
|
14
|
+
from byzerllm.utils.str2model import to_model
|
|
15
|
+
from autocoder.common import git_utils
|
|
16
|
+
from autocoder.commands.tools import AutoCommandTools
|
|
17
|
+
from autocoder.auto_coder import AutoCoderArgs
|
|
18
|
+
from autocoder.common import detect_env
|
|
19
|
+
from autocoder.common import shells
|
|
20
|
+
from loguru import logger
|
|
21
|
+
from autocoder.utils import llms as llms_utils
|
|
22
|
+
from autocoder.rag.token_counter import count_tokens
|
|
23
|
+
from autocoder.common.global_cancel import global_cancel
|
|
24
|
+
|
|
25
|
+
class CommandMessage(BaseModel):
|
|
26
|
+
role: str
|
|
27
|
+
content: str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ExtendedCommandMessage(BaseModel):
|
|
31
|
+
message: CommandMessage
|
|
32
|
+
timestamp: str
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CommandConversation(BaseModel):
|
|
36
|
+
history: Dict[str, ExtendedCommandMessage]
|
|
37
|
+
current_conversation: List[CommandMessage]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def load_memory_file() -> CommandConversation:
|
|
41
|
+
"""Load command conversations from memory file"""
|
|
42
|
+
memory_dir = os.path.join(".auto-coder", "memory")
|
|
43
|
+
file_path = os.path.join(memory_dir, "command_chat_history.json")
|
|
44
|
+
if os.path.exists(file_path):
|
|
45
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
46
|
+
try:
|
|
47
|
+
return CommandConversation.model_validate_json(f.read())
|
|
48
|
+
except Exception:
|
|
49
|
+
return CommandConversation(history={}, current_conversation=[])
|
|
50
|
+
return CommandConversation(history={}, current_conversation=[])
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def save_to_memory_file(query: str, response: str):
|
|
54
|
+
"""Save command conversation to memory file using CommandConversation structure"""
|
|
55
|
+
memory_dir = os.path.join(".auto-coder", "memory")
|
|
56
|
+
os.makedirs(memory_dir, exist_ok=True)
|
|
57
|
+
file_path = os.path.join(memory_dir, "command_chat_history.json")
|
|
58
|
+
# Create new message objects
|
|
59
|
+
user_msg = CommandMessage(role="user", content=query)
|
|
60
|
+
assistant_msg = CommandMessage(role="assistant", content=response)
|
|
61
|
+
|
|
62
|
+
extended_user_msg = ExtendedCommandMessage(
|
|
63
|
+
message=user_msg,
|
|
64
|
+
timestamp=str(int(time.time()))
|
|
65
|
+
)
|
|
66
|
+
extended_assistant_msg = ExtendedCommandMessage(
|
|
67
|
+
message=assistant_msg,
|
|
68
|
+
timestamp=str(int(time.time()))
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Load existing conversation or create new
|
|
72
|
+
if os.path.exists(file_path):
|
|
73
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
74
|
+
try:
|
|
75
|
+
existing_conv = CommandConversation.model_validate_json(
|
|
76
|
+
f.read())
|
|
77
|
+
except Exception:
|
|
78
|
+
existing_conv = CommandConversation(
|
|
79
|
+
history={},
|
|
80
|
+
current_conversation=[]
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
existing_conv = CommandConversation(
|
|
84
|
+
history={},
|
|
85
|
+
current_conversation=[]
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
existing_conv.current_conversation.append(extended_user_msg)
|
|
89
|
+
existing_conv.current_conversation.append(extended_assistant_msg)
|
|
90
|
+
# Save updated conversation
|
|
91
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
92
|
+
f.write(existing_conv.model_dump_json(indent=2))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class CommandSuggestion(BaseModel):
|
|
96
|
+
command: str
|
|
97
|
+
parameters: Dict[str, Any]
|
|
98
|
+
confidence: float
|
|
99
|
+
reasoning: str
|
|
100
|
+
|
|
101
|
+
class AutoCommandResponse(BaseModel):
|
|
102
|
+
suggestions: List[CommandSuggestion]
|
|
103
|
+
reasoning: Optional[str] = None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class AutoCommandRequest(BaseModel):
|
|
107
|
+
user_input: str
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class MemoryConfig(BaseModel):
|
|
111
|
+
"""
|
|
112
|
+
A model to encapsulate memory configuration and operations.
|
|
113
|
+
"""
|
|
114
|
+
memory: Dict[str, Any]
|
|
115
|
+
save_memory_func: SkipValidation[Callable]
|
|
116
|
+
|
|
117
|
+
class Config:
|
|
118
|
+
arbitrary_types_allowed = True
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class CommandConfig(BaseModel):
|
|
122
|
+
coding: SkipValidation[Callable]
|
|
123
|
+
chat: SkipValidation[Callable]
|
|
124
|
+
add_files: SkipValidation[Callable]
|
|
125
|
+
remove_files: SkipValidation[Callable]
|
|
126
|
+
index_build: SkipValidation[Callable]
|
|
127
|
+
index_query: SkipValidation[Callable]
|
|
128
|
+
list_files: SkipValidation[Callable]
|
|
129
|
+
ask: SkipValidation[Callable]
|
|
130
|
+
revert: SkipValidation[Callable]
|
|
131
|
+
commit: SkipValidation[Callable]
|
|
132
|
+
help: SkipValidation[Callable]
|
|
133
|
+
exclude_dirs: SkipValidation[Callable]
|
|
134
|
+
summon: SkipValidation[Callable]
|
|
135
|
+
design: SkipValidation[Callable]
|
|
136
|
+
mcp: SkipValidation[Callable]
|
|
137
|
+
models: SkipValidation[Callable]
|
|
138
|
+
lib: SkipValidation[Callable]
|
|
139
|
+
execute_shell_command: SkipValidation[Callable]
|
|
140
|
+
generate_shell_command: SkipValidation[Callable]
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class CommandAutoTuner:
|
|
145
|
+
def __init__(self, llm: Union[byzerllm.ByzerLLM, byzerllm.SimpleByzerLLM],
|
|
146
|
+
args: AutoCoderArgs,
|
|
147
|
+
memory_config: MemoryConfig, command_config: CommandConfig):
|
|
148
|
+
self.llm = llm
|
|
149
|
+
self.args = args
|
|
150
|
+
self.printer = Printer()
|
|
151
|
+
self.memory_config = memory_config
|
|
152
|
+
self.command_config = command_config
|
|
153
|
+
self.tools = AutoCommandTools(args=args, llm=self.llm)
|
|
154
|
+
|
|
155
|
+
def get_conversations(self) -> List[CommandMessage]:
|
|
156
|
+
"""Get conversation history from memory file"""
|
|
157
|
+
conversation = load_memory_file()
|
|
158
|
+
return [extended_msg for extended_msg in conversation.current_conversation]
|
|
159
|
+
|
|
160
|
+
@byzerllm.prompt()
|
|
161
|
+
def _analyze(self, request: AutoCommandRequest) -> str:
|
|
162
|
+
"""
|
|
163
|
+
当前用户环境信息如下:
|
|
164
|
+
<os_info>
|
|
165
|
+
操作系统: {{ env_info.os_name }} {{ env_info.os_version }}
|
|
166
|
+
Python版本: {{ env_info.python_version }}
|
|
167
|
+
终端类型: {{ env_info.shell_type }}
|
|
168
|
+
终端编码: {{ env_info.shell_encoding }}
|
|
169
|
+
{%- if env_info.conda_env %}
|
|
170
|
+
Conda环境: {{ env_info.conda_env }}
|
|
171
|
+
{%- endif %}
|
|
172
|
+
{%- if env_info.virtualenv %}
|
|
173
|
+
虚拟环境: {{ env_info.virtualenv }}
|
|
174
|
+
{%- endif %}
|
|
175
|
+
</os_info>
|
|
176
|
+
|
|
177
|
+
我们的目标是根据用户输入和当前上下文,组合多个函数来完成用户的需求。
|
|
178
|
+
|
|
179
|
+
{% if current_files %}
|
|
180
|
+
当前活跃区文件列表:
|
|
181
|
+
<current_files>
|
|
182
|
+
{% for file in current_files %}
|
|
183
|
+
- {{ file }}
|
|
184
|
+
{% endfor %}
|
|
185
|
+
</current_files>
|
|
186
|
+
{% endif %}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
当前用户的配置选项如下:
|
|
190
|
+
<current_conf>
|
|
191
|
+
{{ current_conf }}
|
|
192
|
+
</current_conf>
|
|
193
|
+
|
|
194
|
+
可用函数列表:
|
|
195
|
+
{{ available_commands }}
|
|
196
|
+
|
|
197
|
+
函数组合说明:
|
|
198
|
+
<function_combination_readme>
|
|
199
|
+
如果用户是一个编码需求,你可以先简单观察当前活跃区文件列表:
|
|
200
|
+
0. 关注下当前软件的配置,诸如索引开启关闭。如果有觉得不合理的可以通过 help 函数来修改。
|
|
201
|
+
1. 如果你觉得这些文件不够满足用户的需求,而当前的索引配置关闭的,那么你可以通过help("将skip_filter_index 和 skip_build_index 设置为 false") 让
|
|
202
|
+
chat,coding 函数来获取更多文件,或者你也可以自己通过调用 get_project_structure 函数来获取项目结构,然后通过 get_project_map 函数来获取某个文件的用途,符号列表,以及
|
|
203
|
+
文件大小(tokens数),最后再通过 read_files/read_file_with_keyword_ranges 函数来读取文件内容, 最后通过 add_files 函数来添加文件到活跃区。
|
|
204
|
+
确保 chat,coding 函数能够正常使用。
|
|
205
|
+
2. 对于一个比较复杂的代码需求,你可以先通过 chat 函数来获得一些设计,根据chat返回的结果,你可以选择多次调用chat调整最后的设计。最后,当你满意后,可以通过 coding("/apply 根据历史对话实现代码,请不要有遗漏") 来完成最后的编码。
|
|
206
|
+
3. 注意,为了防止对话过长,你可以使用 chat("/new") 来创新新的会话。然后接着正常再次调用 chat 函数。 即可
|
|
207
|
+
4. 当用户询问项目,比如询问什么什么功能在哪里的时候,或者哪个文件实现了什么功能,推荐的工具组合是 get_project_map 和 get_project_structure。可以直通过 get_project_map 查看整个项目文件的索引(该索引包含了文件列表,每个文件的用途和符号列表),也可以
|
|
208
|
+
通过 get_project_structure 来获取项目结构,然后通过 get_project_map 来获取你想看的某个文件的用途,符号列表,最后再通过 read_files/read_file_with_keyword_ranges 函数来读取文件内容,确认对应的功能是否在相关的文件里。
|
|
209
|
+
5. 调用 coding 函数的时候,尽可能多的 @文件和@@符号,让需求更加清晰明了,建议多描述具体怎么完成对应的需求。
|
|
210
|
+
6. 对于代码需求设计,尽可能使用 chat 函数。
|
|
211
|
+
7. 如果成功执行了 coding 函数,最好再调用一次 chat("/review /commit")
|
|
212
|
+
</function_combination_readme>
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
{% if conversation_history %}
|
|
217
|
+
历史对话:
|
|
218
|
+
<conversation_history>
|
|
219
|
+
{% for conv in conversation_history %}
|
|
220
|
+
({{ conv.role }}): {{ conv.content }}
|
|
221
|
+
{% endfor %}
|
|
222
|
+
</conversation_history>
|
|
223
|
+
{% endif %}
|
|
224
|
+
|
|
225
|
+
用户需求:
|
|
226
|
+
<user_input>
|
|
227
|
+
{{ user_input }}
|
|
228
|
+
</user_input>
|
|
229
|
+
|
|
230
|
+
请分析用户意图,组合一个或者多个函数,帮助用户完成需求。
|
|
231
|
+
返回格式必须是严格的JSON格式:
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"suggestions": [
|
|
236
|
+
{
|
|
237
|
+
"command": "函数名称",
|
|
238
|
+
"parameters": {},
|
|
239
|
+
"confidence": 0.9,
|
|
240
|
+
"reasoning": "推荐理由"
|
|
241
|
+
}
|
|
242
|
+
],
|
|
243
|
+
"reasoning": "整体推理说明"
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
注意,现在,请返回第一个函数。我后续会把每个函数的执行结果告诉你。你根据执行结果继续确定下一步该执行什新的函数,直到
|
|
248
|
+
满足需求。
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
env_info = detect_env()
|
|
252
|
+
return {
|
|
253
|
+
"user_input": request.user_input,
|
|
254
|
+
"current_files": self.memory_config.memory["current_files"]["files"],
|
|
255
|
+
"conversation_history": self.get_conversations(),
|
|
256
|
+
"available_commands": self._command_readme.prompt(),
|
|
257
|
+
"current_conf": json.dumps(self.memory_config.memory["conf"], indent=2),
|
|
258
|
+
"env_info": env_info,
|
|
259
|
+
"shell_type": shells.get_terminal_name(),
|
|
260
|
+
"shell_encoding": shells.get_terminal_encoding()
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
@byzerllm.prompt()
|
|
264
|
+
def _execute_command_result(self, result:str) -> str:
|
|
265
|
+
'''
|
|
266
|
+
根据函数执行结果,返回下一个函数。
|
|
267
|
+
|
|
268
|
+
下面是我们上一个函数执行结果:
|
|
269
|
+
|
|
270
|
+
<function_result>
|
|
271
|
+
{{ result }}
|
|
272
|
+
</function_result>
|
|
273
|
+
|
|
274
|
+
请根据命令执行结果以及前面的对话,返回下一个函数。
|
|
275
|
+
|
|
276
|
+
*** 非常非常重要的提示 ***
|
|
277
|
+
1. 如果已经满足要求,则不要返回任何函数,确保 suggestions 为空。
|
|
278
|
+
2. 你最多尝试 {{ auto_command_max_iterations }} 次,如果 {{ auto_command_max_iterations }} 次都没有满足要求,则不要返回任何函数,确保 suggestions 为空。
|
|
279
|
+
'''
|
|
280
|
+
return {
|
|
281
|
+
"auto_command_max_iterations": self.args.auto_command_max_iterations
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
def analyze(self, request: AutoCommandRequest) -> AutoCommandResponse:
|
|
285
|
+
# 获取 prompt 内容
|
|
286
|
+
prompt = self._analyze.prompt(request)
|
|
287
|
+
|
|
288
|
+
# 构造对话上下文
|
|
289
|
+
conversations = [{"role": "user", "content": prompt}]
|
|
290
|
+
|
|
291
|
+
# 使用 stream_out 进行输出
|
|
292
|
+
printer = Printer()
|
|
293
|
+
title = printer.get_message_from_key("auto_command_analyzing")
|
|
294
|
+
final_title = printer.get_message_from_key("auto_command_analyzed")
|
|
295
|
+
|
|
296
|
+
def extract_command_response(content: str) -> str:
|
|
297
|
+
# 提取 JSON 并转换为 AutoCommandResponse
|
|
298
|
+
try:
|
|
299
|
+
response = to_model(content, AutoCommandResponse)
|
|
300
|
+
if response.suggestions:
|
|
301
|
+
command = response.suggestions[0].command
|
|
302
|
+
parameters = response.suggestions[0].parameters
|
|
303
|
+
if parameters:
|
|
304
|
+
params_str = ", ".join([f"{k}={v}" for k, v in parameters.items()])
|
|
305
|
+
else:
|
|
306
|
+
params_str = ""
|
|
307
|
+
return f"{command}({params_str})"
|
|
308
|
+
else:
|
|
309
|
+
return printer.get_message_from_key("satisfied_prompt", style="green")
|
|
310
|
+
except Exception as e:
|
|
311
|
+
logger.error(f"Error extracting command response: {e}")
|
|
312
|
+
return content
|
|
313
|
+
|
|
314
|
+
model_name = ",".join(llms_utils.get_llm_names(self.llm))
|
|
315
|
+
start_time = time.monotonic()
|
|
316
|
+
result, last_meta = stream_out(
|
|
317
|
+
self.llm.stream_chat_oai(conversations=conversations, delta_mode=True),
|
|
318
|
+
model_name=model_name,
|
|
319
|
+
title=title,
|
|
320
|
+
final_title=final_title,
|
|
321
|
+
display_func= extract_command_response
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
if last_meta:
|
|
325
|
+
elapsed_time = time.monotonic() - start_time
|
|
326
|
+
speed = last_meta.generated_tokens_count / elapsed_time
|
|
327
|
+
|
|
328
|
+
# Get model info for pricing
|
|
329
|
+
from autocoder.utils import llms as llm_utils
|
|
330
|
+
model_info = llm_utils.get_model_info(model_name, self.args.product_mode) or {}
|
|
331
|
+
input_price = model_info.get("input_price", 0.0) if model_info else 0.0
|
|
332
|
+
output_price = model_info.get("output_price", 0.0) if model_info else 0.0
|
|
333
|
+
|
|
334
|
+
# Calculate costs
|
|
335
|
+
input_cost = (last_meta.input_tokens_count * input_price) / 1000000 # Convert to millions
|
|
336
|
+
output_cost = (last_meta.generated_tokens_count * output_price) / 1000000 # Convert to millions
|
|
337
|
+
|
|
338
|
+
printer.print_in_terminal("stream_out_stats",
|
|
339
|
+
model_name=",".join(llms_utils.get_llm_names(self.llm)),
|
|
340
|
+
elapsed_time=elapsed_time,
|
|
341
|
+
first_token_time=last_meta.first_token_time,
|
|
342
|
+
input_tokens=last_meta.input_tokens_count,
|
|
343
|
+
output_tokens=last_meta.generated_tokens_count,
|
|
344
|
+
input_cost=round(input_cost, 4),
|
|
345
|
+
output_cost=round(output_cost, 4),
|
|
346
|
+
speed=round(speed, 2))
|
|
347
|
+
|
|
348
|
+
## 这里打印
|
|
349
|
+
|
|
350
|
+
conversations.append({"role": "assistant", "content": result})
|
|
351
|
+
# 提取 JSON 并转换为 AutoCommandResponse
|
|
352
|
+
response = to_model(result, AutoCommandResponse)
|
|
353
|
+
|
|
354
|
+
# 保存对话记录
|
|
355
|
+
save_to_memory_file(
|
|
356
|
+
query=request.user_input,
|
|
357
|
+
response=response.model_dump_json(indent=2)
|
|
358
|
+
)
|
|
359
|
+
result_manager = ResultManager()
|
|
360
|
+
|
|
361
|
+
while True:
|
|
362
|
+
if global_cancel.requested:
|
|
363
|
+
printer = Printer(console)
|
|
364
|
+
printer.print_in_terminal("generation_cancelled")
|
|
365
|
+
break
|
|
366
|
+
# 执行命令
|
|
367
|
+
command = response.suggestions[0].command
|
|
368
|
+
parameters = response.suggestions[0].parameters
|
|
369
|
+
|
|
370
|
+
# 打印正在执行的命令
|
|
371
|
+
self.printer.print_in_terminal(
|
|
372
|
+
"auto_command_executing",
|
|
373
|
+
style="blue",
|
|
374
|
+
command=command
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
self.execute_auto_command(command, parameters)
|
|
378
|
+
content = ""
|
|
379
|
+
last_result = result_manager.get_last()
|
|
380
|
+
if last_result:
|
|
381
|
+
action = last_result.meta["action"]
|
|
382
|
+
if action == "coding":
|
|
383
|
+
# 如果上一步是 coding,则需要把上一步的更改前和更改后的内容作为上下文
|
|
384
|
+
changes = git_utils.get_changes_by_commit_message("", last_result.meta["commit_message"])
|
|
385
|
+
if changes.success:
|
|
386
|
+
for file_path, change in changes.changes.items():
|
|
387
|
+
if change:
|
|
388
|
+
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"
|
|
389
|
+
else:
|
|
390
|
+
content = printer.get_message_from_key_with_format("no_changes_made")
|
|
391
|
+
else:
|
|
392
|
+
# 其他的直接获取执行结果
|
|
393
|
+
content = last_result.content
|
|
394
|
+
|
|
395
|
+
if action != command:
|
|
396
|
+
# command 和 action 不一致,则认为命令执行失败,退出
|
|
397
|
+
printer.print_in_terminal("auto_command_action_break", style="yellow", command=command, action=action)
|
|
398
|
+
break
|
|
399
|
+
|
|
400
|
+
# 打印执行结果
|
|
401
|
+
console = Console()
|
|
402
|
+
# 截取content前后200字符
|
|
403
|
+
truncated_content = content[:200] + "\n...\n" + content[-200:] if len(content) > 400 else content
|
|
404
|
+
title = printer.get_message_from_key_with_format(
|
|
405
|
+
"command_execution_result",
|
|
406
|
+
action=action
|
|
407
|
+
)
|
|
408
|
+
console.print(Panel(
|
|
409
|
+
truncated_content,
|
|
410
|
+
title=title,
|
|
411
|
+
border_style="blue",
|
|
412
|
+
padding=(1, 2)
|
|
413
|
+
))
|
|
414
|
+
# 保持原content不变,继续后续处理
|
|
415
|
+
|
|
416
|
+
# 添加新的对话内容
|
|
417
|
+
new_content = self._execute_command_result.prompt(content)
|
|
418
|
+
conversations.append({"role": "user", "content": new_content})
|
|
419
|
+
|
|
420
|
+
# 统计 token 数量
|
|
421
|
+
total_tokens = count_tokens(json.dumps(conversations,ensure_ascii=False))
|
|
422
|
+
|
|
423
|
+
# 如果对话过长,使用默认策略进行修剪
|
|
424
|
+
if total_tokens > self.args.conversation_prune_safe_zone_tokens:
|
|
425
|
+
self.printer.print_in_terminal(
|
|
426
|
+
"conversation_pruning_start",
|
|
427
|
+
style="yellow",
|
|
428
|
+
total_tokens=total_tokens,
|
|
429
|
+
safe_zone=self.args.conversation_prune_safe_zone_tokens
|
|
430
|
+
)
|
|
431
|
+
from autocoder.common.conversation_pruner import ConversationPruner
|
|
432
|
+
pruner = ConversationPruner(self.llm)
|
|
433
|
+
conversations = pruner.prune_conversations(conversations)
|
|
434
|
+
|
|
435
|
+
title = printer.get_message_from_key("auto_command_analyzing")
|
|
436
|
+
model_name = ",".join(llms_utils.get_llm_names(self.llm))
|
|
437
|
+
|
|
438
|
+
start_time = time.monotonic()
|
|
439
|
+
result, last_meta = stream_out(
|
|
440
|
+
self.llm.stream_chat_oai(conversations=conversations, delta_mode=True),
|
|
441
|
+
model_name=model_name,
|
|
442
|
+
title=title,
|
|
443
|
+
final_title=final_title,
|
|
444
|
+
display_func= extract_command_response
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
if last_meta:
|
|
448
|
+
elapsed_time = time.monotonic() - start_time
|
|
449
|
+
printer = Printer()
|
|
450
|
+
speed = last_meta.generated_tokens_count / elapsed_time
|
|
451
|
+
|
|
452
|
+
# Get model info for pricing
|
|
453
|
+
from autocoder.utils import llms as llm_utils
|
|
454
|
+
model_info = llm_utils.get_model_info(model_name, self.args.product_mode) or {}
|
|
455
|
+
input_price = model_info.get("input_price", 0.0) if model_info else 0.0
|
|
456
|
+
output_price = model_info.get("output_price", 0.0) if model_info else 0.0
|
|
457
|
+
|
|
458
|
+
# Calculate costs
|
|
459
|
+
input_cost = (last_meta.input_tokens_count * input_price) / 1000000 # Convert to millions
|
|
460
|
+
output_cost = (last_meta.generated_tokens_count * output_price) / 1000000 # Convert to millions
|
|
461
|
+
|
|
462
|
+
printer.print_in_terminal("stream_out_stats",
|
|
463
|
+
model_name=model_name,
|
|
464
|
+
elapsed_time=elapsed_time,
|
|
465
|
+
first_token_time=last_meta.first_token_time,
|
|
466
|
+
input_tokens=last_meta.input_tokens_count,
|
|
467
|
+
output_tokens=last_meta.generated_tokens_count,
|
|
468
|
+
input_cost=round(input_cost, 4),
|
|
469
|
+
output_cost=round(output_cost, 4),
|
|
470
|
+
speed=round(speed, 2))
|
|
471
|
+
|
|
472
|
+
conversations.append({"role": "assistant", "content": result})
|
|
473
|
+
# 提取 JSON 并转换为 AutoCommandResponse
|
|
474
|
+
response = to_model(result, AutoCommandResponse)
|
|
475
|
+
if not response or not response.suggestions:
|
|
476
|
+
break
|
|
477
|
+
# 保存对话记录
|
|
478
|
+
save_to_memory_file(
|
|
479
|
+
query=request.user_input,
|
|
480
|
+
response=response.model_dump_json(indent=2)
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
else:
|
|
484
|
+
self.printer.print_in_terminal("auto_command_break", style="yellow", command=command)
|
|
485
|
+
break
|
|
486
|
+
|
|
487
|
+
return response
|
|
488
|
+
|
|
489
|
+
@byzerllm.prompt()
|
|
490
|
+
def _command_readme(self) -> str:
|
|
491
|
+
'''
|
|
492
|
+
你有如下函数可供使用:
|
|
493
|
+
|
|
494
|
+
<commands>
|
|
495
|
+
|
|
496
|
+
<command>
|
|
497
|
+
<name>add_files</name>
|
|
498
|
+
<description>
|
|
499
|
+
添加文件到一个活跃区,活跃区当你使用 chat,coding 函数时,活跃区的文件一定会被他们使用。
|
|
500
|
+
支持通过模式匹配添加文件,支持 glob 语法,例如 *.py。可以使用相对路径或绝对路径。
|
|
501
|
+
如果你检测到用户的coding执行结果,缺少必要的文件修改,可以尝试使用该函数先添加文件再执行coding。
|
|
502
|
+
</description>
|
|
503
|
+
<usage>
|
|
504
|
+
该方法只有一个参数 args,args 是一个列表,列表的元素是字符串。会包含子指令,例如
|
|
505
|
+
|
|
506
|
+
add_files(args=["/refresh"])
|
|
507
|
+
|
|
508
|
+
会刷新文件列表。下面是常见的子指令:
|
|
509
|
+
|
|
510
|
+
## /refresh 刷新文件列表
|
|
511
|
+
刷新文件列表
|
|
512
|
+
|
|
513
|
+
## /group 文件分组管理
|
|
514
|
+
|
|
515
|
+
### /add
|
|
516
|
+
创建新组并将当前文件列表保存到该组。
|
|
517
|
+
使用例子:
|
|
518
|
+
|
|
519
|
+
/group /add my_group
|
|
520
|
+
|
|
521
|
+
### /drop
|
|
522
|
+
删除指定组及其文件列表
|
|
523
|
+
使用例子:
|
|
524
|
+
|
|
525
|
+
/group /drop my_group
|
|
526
|
+
|
|
527
|
+
### /set
|
|
528
|
+
设置组的描述信息,用于说明该组的用途
|
|
529
|
+
使用例子:
|
|
530
|
+
|
|
531
|
+
/group /set my_group "用于说明该组的用途"
|
|
532
|
+
|
|
533
|
+
### /list
|
|
534
|
+
列出所有已定义的组及其文件
|
|
535
|
+
使用例子:
|
|
536
|
+
|
|
537
|
+
/group /list
|
|
538
|
+
|
|
539
|
+
### /reset
|
|
540
|
+
重置当前活跃组,但保留文件列表
|
|
541
|
+
使用例子:
|
|
542
|
+
/group /reset
|
|
543
|
+
|
|
544
|
+
</usage>
|
|
545
|
+
</command>
|
|
546
|
+
|
|
547
|
+
<command>
|
|
548
|
+
<name>remove_files</name>
|
|
549
|
+
<description>从活跃区移除文件。可以指定多个文件,支持文件名或完整路径。</description>
|
|
550
|
+
<usage>
|
|
551
|
+
该方法接受一个参数 file_names,是一个列表,列表的元素是字符串。下面是常见的子指令:
|
|
552
|
+
|
|
553
|
+
## /all 移除所有文件
|
|
554
|
+
移除所有当前会话中的文件,同时清空活跃组列表。
|
|
555
|
+
使用例子:
|
|
556
|
+
|
|
557
|
+
remove_files(file_names=["/all"])
|
|
558
|
+
|
|
559
|
+
## 移除指定文件
|
|
560
|
+
可以指定一个或多个文件,文件名之间用逗号分隔。
|
|
561
|
+
使用例子:
|
|
562
|
+
|
|
563
|
+
remove_files(file_names=["file1.py,file2.py"])
|
|
564
|
+
remove_files(file_names=["/path/to/file1.py,file2.py"])
|
|
565
|
+
|
|
566
|
+
</usage>
|
|
567
|
+
</command>
|
|
568
|
+
|
|
569
|
+
<command>
|
|
570
|
+
<name>list_files</name>
|
|
571
|
+
<description>通过add_files 添加的文件</description>
|
|
572
|
+
<usage>
|
|
573
|
+
该命令不需要任何参数,直接使用即可。
|
|
574
|
+
使用例子:
|
|
575
|
+
|
|
576
|
+
list_files()
|
|
577
|
+
|
|
578
|
+
</usage>
|
|
579
|
+
</command>
|
|
580
|
+
|
|
581
|
+
<command>
|
|
582
|
+
<name>revert</name>
|
|
583
|
+
<description>
|
|
584
|
+
撤销最后一次代码修改,恢复到修改前的状态。同时会删除对应的操作记录文件,
|
|
585
|
+
如果很明显你对上一次coding函数执行后的效果觉得不满意,可以使用该函数来撤销上一次的代码修改。
|
|
586
|
+
</description>
|
|
587
|
+
<usage>
|
|
588
|
+
该命令不需要任何参数,直接使用即可。会撤销最近一次的代码修改操作。
|
|
589
|
+
使用例子:
|
|
590
|
+
|
|
591
|
+
revert()
|
|
592
|
+
|
|
593
|
+
注意:
|
|
594
|
+
- 只能撤销最后一次的修改
|
|
595
|
+
- 撤销后会同时删除对应的操作记录文件
|
|
596
|
+
- 如果没有可撤销的操作会提示错误
|
|
597
|
+
</usage>
|
|
598
|
+
</command>
|
|
599
|
+
|
|
600
|
+
<command>
|
|
601
|
+
<name>help</name>
|
|
602
|
+
<description>
|
|
603
|
+
显示帮助信息,也可以执行一些配置需求。
|
|
604
|
+
</description>
|
|
605
|
+
<usage>
|
|
606
|
+
该命令只有一个参数 query,query 为字符串,表示要执行的配置需求。
|
|
607
|
+
|
|
608
|
+
如果query 为空,则显示一个通用帮助信息。
|
|
609
|
+
|
|
610
|
+
## 显示通用帮助
|
|
611
|
+
不带参数显示所有可用命令的概览
|
|
612
|
+
使用例子:
|
|
613
|
+
|
|
614
|
+
help(query="")
|
|
615
|
+
|
|
616
|
+
## 帮助用户执行特定的配置
|
|
617
|
+
|
|
618
|
+
help(query="关闭索引")
|
|
619
|
+
|
|
620
|
+
这条命令会触发:
|
|
621
|
+
|
|
622
|
+
/conf skip_build_index:true
|
|
623
|
+
|
|
624
|
+
的执行。
|
|
625
|
+
|
|
626
|
+
常见的一些配置选项示例:
|
|
627
|
+
|
|
628
|
+
# 配置项说明
|
|
629
|
+
## auto_merge: 代码合并方式,可选值为editblock、diff、wholefile.
|
|
630
|
+
- editblock: 生成 SEARCH/REPLACE 块,然后根据 SEARCH块到对应的源码查找,如果相似度阈值大于 editblock_similarity, 那么则将
|
|
631
|
+
找到的代码块替换为 REPLACE 块。大部分情况都推荐使用 editblock。
|
|
632
|
+
- wholefile: 重新生成整个文件,然后替换原来的文件。对于重构场景,推荐使用 wholefile。
|
|
633
|
+
- diff: 生成标准 git diff 格式,适用于简单的代码修改。
|
|
634
|
+
|
|
635
|
+
## editblock_similarity: editblock相似度阈值
|
|
636
|
+
- editblock相似度阈值,取值范围为0-1,默认值为0.9。如果设置的太低,虽然能合并进去,但是会引入错误。推荐不要修改该值。
|
|
637
|
+
|
|
638
|
+
## generate_times_same_model: 相同模型生成次数
|
|
639
|
+
当进行生成代码时,大模型会对同一个需求生成多份代码,然后会使用 generate_rerank_model 模型对多份代码进行重排序,
|
|
640
|
+
然后选择得分最高的代码。一般次数越多,最终得到正确的代码概率越高。默认值为1,推荐设置为3。但是设置值越多,可能速度就越慢,消耗的token也越多。
|
|
641
|
+
|
|
642
|
+
## skip_filter_index: 是否跳过索引过滤
|
|
643
|
+
是否跳过根据用户的query 自动查找上下文。推荐设置为 false
|
|
644
|
+
|
|
645
|
+
## skip_build_index: 是否跳过索引构建
|
|
646
|
+
是否自动构建索引。推荐设置为 false。注意,如果该值设置为 true, 那么 skip_filter_index 设置不会生效。
|
|
647
|
+
|
|
648
|
+
## enable_global_memory: 是否开启全局记忆
|
|
649
|
+
是否开启全局记忆。
|
|
650
|
+
|
|
651
|
+
## rank_times_same_model: 相同模型重排序次数
|
|
652
|
+
默认值为1. 如果 generate_times_same_model 参数设置大于1,那么 coding 函数会自动对多份代码进行重排序。
|
|
653
|
+
rank_times_same_model 表示重拍的次数,次数越多,选择到最好的代码的可能性越高,但是也会显著增加消耗的token和时间。
|
|
654
|
+
建议保持默认,要修改也建议不要超过3。
|
|
655
|
+
|
|
656
|
+
比如你想开启索引,则可以执行:
|
|
657
|
+
|
|
658
|
+
help(query="开启索引")
|
|
659
|
+
|
|
660
|
+
其中 query 参数为 "开启索引"
|
|
661
|
+
|
|
662
|
+
** 特别注意,这些配置参数会影响 coding,chat 的执行效果或者结果 根据返回调用该函数做合理的配置**
|
|
663
|
+
|
|
664
|
+
</usage>
|
|
665
|
+
</command>
|
|
666
|
+
|
|
667
|
+
<command>
|
|
668
|
+
<name>chat</name>
|
|
669
|
+
<description>进入聊天模式,与AI进行交互对话。支持多轮对话和上下文理解。</description>
|
|
670
|
+
<usage>
|
|
671
|
+
该命令支持多种交互方式和特殊功能。
|
|
672
|
+
|
|
673
|
+
## 基础对话
|
|
674
|
+
直接输入对话内容
|
|
675
|
+
使用例子:
|
|
676
|
+
|
|
677
|
+
chat(query="这个项目使用了什么技术栈?")
|
|
678
|
+
|
|
679
|
+
## 新会话
|
|
680
|
+
使用 /new 开启新对话
|
|
681
|
+
使用例子:
|
|
682
|
+
|
|
683
|
+
chat(query="/new 让我们讨论新的话题")
|
|
684
|
+
|
|
685
|
+
## 代码审查
|
|
686
|
+
使用 /review 请求代码审查
|
|
687
|
+
使用例子:
|
|
688
|
+
|
|
689
|
+
chat(query="/review @main.py")
|
|
690
|
+
|
|
691
|
+
## 特殊功能
|
|
692
|
+
- /no_context:不使用当前文件上下文
|
|
693
|
+
- /mcp:获取 MCP 服务内容
|
|
694
|
+
- /rag:使用检索增强生成。 如果用户配置了 rag_url, 那可以设置query参数类似 `/rag 查询mcp该如何开发`
|
|
695
|
+
- /copy:chat 函数执行后的结果会被复制到黏贴版
|
|
696
|
+
- /save:chat 函数执行后的结果会被保存到全局记忆中,后续会自动加到 coding,chat 的上下文中
|
|
697
|
+
|
|
698
|
+
## 引用语法
|
|
699
|
+
- @文件名:引用特定文件
|
|
700
|
+
- @@符号:引用函数或类
|
|
701
|
+
- <img>图片路径</img>:引入图片
|
|
702
|
+
|
|
703
|
+
使用例子:
|
|
704
|
+
|
|
705
|
+
chat(query="@utils.py 这个文件的主要功能是什么?")
|
|
706
|
+
chat(query="@@process_data 这个函数的实现有什么问题?")
|
|
707
|
+
chat(query="<img>screenshots/error.png</img> 这个错误如何解决?")
|
|
708
|
+
|
|
709
|
+
## 对最后一次commit 进行review
|
|
710
|
+
使用例子:
|
|
711
|
+
chat(query="/review /commit")
|
|
712
|
+
|
|
713
|
+
</usage>
|
|
714
|
+
</command>
|
|
715
|
+
|
|
716
|
+
<command>
|
|
717
|
+
<name>coding</name>
|
|
718
|
+
<description>代码生成函数,用于生成、修改和重构代码。</description>
|
|
719
|
+
<usage>
|
|
720
|
+
该函数支持多种代码生成和修改场景。
|
|
721
|
+
|
|
722
|
+
该函数支持一个参数 query,query 为字符串,表示要生成的代码需求。
|
|
723
|
+
|
|
724
|
+
## 基础代码生成
|
|
725
|
+
直接描述需求
|
|
726
|
+
使用例子:
|
|
727
|
+
|
|
728
|
+
coding(query="创建一个处理用户登录的函数")
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
## 和 chat 搭配使用
|
|
732
|
+
当你用过 chat 之后,继续使用 coding 时,可以添加 /apply 来带上 chat 的对话内容。
|
|
733
|
+
使用例子:
|
|
734
|
+
|
|
735
|
+
coding(query="/apply 根据我们的历史对话实现代码,请不要遗漏任何细节。")
|
|
736
|
+
|
|
737
|
+
## 预测下一步
|
|
738
|
+
使用 /next 分析并建议后续步骤
|
|
739
|
+
使用例子:
|
|
740
|
+
|
|
741
|
+
coding(query="/next")
|
|
742
|
+
|
|
743
|
+
## 引用语法
|
|
744
|
+
- @文件名:引用特定文件
|
|
745
|
+
- @@符号:引用函数或类
|
|
746
|
+
- <img>图片路径</img>:引入图片
|
|
747
|
+
|
|
748
|
+
使用例子:
|
|
749
|
+
|
|
750
|
+
coding(query="@auth.py 添加JWT认证")
|
|
751
|
+
coding(query="@@login 优化错误处理")
|
|
752
|
+
coding(query="<img>design/flow.png</img> 实现这个流程图的功能")
|
|
753
|
+
</usage>
|
|
754
|
+
</command>
|
|
755
|
+
|
|
756
|
+
<command>
|
|
757
|
+
<name>lib</name>
|
|
758
|
+
<description>库管理命令,用于管理项目依赖和文档。</description>
|
|
759
|
+
<usage>
|
|
760
|
+
该命令用于管理项目的依赖库和相关文档。
|
|
761
|
+
参数为 args: List[str]
|
|
762
|
+
|
|
763
|
+
## 添加库
|
|
764
|
+
使用 /add 添加新库
|
|
765
|
+
使用例子:
|
|
766
|
+
|
|
767
|
+
lib(args=["/add", "byzer-llm"])
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
## 移除库
|
|
771
|
+
使用 /remove 移除库
|
|
772
|
+
使用例子:
|
|
773
|
+
|
|
774
|
+
lib(args=["/remove", "byzer-llm"])
|
|
775
|
+
|
|
776
|
+
## 查看库列表
|
|
777
|
+
使用 /list 查看已添加的库
|
|
778
|
+
使用例子:
|
|
779
|
+
|
|
780
|
+
lib(args=["/list"])
|
|
781
|
+
|
|
782
|
+
## 设置代理
|
|
783
|
+
使用 /set-proxy 设置下载代理
|
|
784
|
+
使用例子:
|
|
785
|
+
|
|
786
|
+
lib(args=["/set-proxy", "https://gitee.com/allwefantasy/llm_friendly_packages"])
|
|
787
|
+
|
|
788
|
+
## 刷新文档
|
|
789
|
+
使用 /refresh 更新文档
|
|
790
|
+
使用例子:
|
|
791
|
+
|
|
792
|
+
lib(args=["/refresh"])
|
|
793
|
+
|
|
794
|
+
## 获取文档
|
|
795
|
+
使用 /get 获取特定包的文档
|
|
796
|
+
使用例子:
|
|
797
|
+
|
|
798
|
+
lib(args=["/get", "byzer-llm"])
|
|
799
|
+
|
|
800
|
+
目前仅支持用于大模型的 byzer-llm 包,用于数据分析的 byzer-sql 包。
|
|
801
|
+
|
|
802
|
+
</usage>
|
|
803
|
+
</command>
|
|
804
|
+
|
|
805
|
+
<command>
|
|
806
|
+
<name>models</name>
|
|
807
|
+
<description>模型控制面板命令,用于管理和控制AI模型。</description>
|
|
808
|
+
<usage>
|
|
809
|
+
该命令用于管理和控制AI模型的配置和运行。 包含一个参数:query,字符串类型。
|
|
810
|
+
|
|
811
|
+
## 罗列模型模板
|
|
812
|
+
|
|
813
|
+
models(query="/list")
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
其中展示的结果中标注 * 好的模型表示目前已经激活(配置过api key)的。
|
|
817
|
+
|
|
818
|
+
##添加模型模板
|
|
819
|
+
|
|
820
|
+
比如我想添加 open router 或者硅基流动的模型,则可以通过如下方式:
|
|
821
|
+
|
|
822
|
+
models(query="/add_model name=openrouter-sonnet-3.5 base_url=https://openrouter.ai/api/v1")
|
|
823
|
+
|
|
824
|
+
这样就能添加自定义模型: openrouter-sonnet-3.5
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
如果你想添加添加硅基流动deepseek 模型的方式为:
|
|
828
|
+
|
|
829
|
+
models(query="/add_model name=siliconflow_ds_2.5 base_url=https://api.siliconflow.cn/v1 model_name=deepseek-ai/DeepSeek-V2.5")
|
|
830
|
+
|
|
831
|
+
name 为你取的一个名字,这意味着同一个模型,你可以添加多个,只要保证 name 不一样即可。
|
|
832
|
+
base_url 是 硅基流动的 API 地址
|
|
833
|
+
model_name 则为你在硅基流动选择的模型名
|
|
834
|
+
|
|
835
|
+
## 添加完模型后,你还需要能够激活模型:
|
|
836
|
+
|
|
837
|
+
models(query="/activate <模型名,/add_mdoel里的 name字段> <YOUR_API_KEY>")
|
|
838
|
+
|
|
839
|
+
之后你就可以这样配置来使用激活的模型:
|
|
840
|
+
|
|
841
|
+
conf(conf="model:openrouter-sonnet-3.5")
|
|
842
|
+
|
|
843
|
+
## 删除模型
|
|
844
|
+
|
|
845
|
+
models(query="/remove openrouter-sonnet-3.5")
|
|
846
|
+
|
|
847
|
+
常见的供应商模型模板(以 DeepSeek R1 和 V3 模型为例):
|
|
848
|
+
|
|
849
|
+
## openrouter
|
|
850
|
+
models(query="/add_model name=or_r1_chat base_url=https://openrouter.ai/api/v1 model_name=deepseek/deepseek-r1:nitro")
|
|
851
|
+
models(query="/add_model name=or_v3_chat base_url=https://openrouter.ai/api/v1 model_name=deepseek/deepseek-chat")
|
|
852
|
+
|
|
853
|
+
## 硅基流动
|
|
854
|
+
models(query="/add_model name=siliconflow_r1_chat base_url=https://api.siliconflow.cn/v1 model_name=Pro/deepseek-ai/DeepSeek-R1")
|
|
855
|
+
models(query="/add_model name=siliconflow_v3_chat base_url=https://api.siliconflow.cn/v1 model_name=Pro/deepseek-ai/DeepSeek-V3")
|
|
856
|
+
|
|
857
|
+
## 火山引擎/火山方舟
|
|
858
|
+
|
|
859
|
+
models(query="/add_model name=ark_v3_chat base_url=https://ark.cn-beijing.volces.com/api/v3 model_name=<你的推理点名称>")
|
|
860
|
+
models(query="/add_model name=ark_r1_chat base_url=https://ark.cn-beijing.volces.com/api/v3 model_name=<你的推理点名称> is_reasoning=true")
|
|
861
|
+
|
|
862
|
+
## 百度千帆
|
|
863
|
+
|
|
864
|
+
models(query="/add_model name=qianfan_r1_chat base_url=https://qianfan.baidubce.com/v2 model_name=deepseek-r1 is_reasoning=true")
|
|
865
|
+
models(query="/add_model name=qianfan_v3_chat base_url=https://qianfan.baidubce.com/v2 model_name=deepseek-v3")
|
|
866
|
+
|
|
867
|
+
## 阿里百炼
|
|
868
|
+
models(query="/add_model name=ali_r1_chat base_url=https://dashscope.aliyuncs.com/compatible-mode/v1 model_name=deepseek-r1 is_reasoning=true")
|
|
869
|
+
models(query="/add_model name=ali_deepseek_chat base_url=https://dashscope.aliyuncs.com/compatible-mode/v1 model_name=deepseek-v3")
|
|
870
|
+
|
|
871
|
+
## 腾讯混元
|
|
872
|
+
models(query="/add_model name=tencent_r1_chat base_url=https://tencent.ai.qq.com/v1 model_name=deepseek-r1 is_reasoning=true")
|
|
873
|
+
models(query="/add_model name=tencent_v3_chat base_url=https://tencent.ai.qq.com/v1 model_name=deepseek-v3")
|
|
874
|
+
|
|
875
|
+
*** 特别注意 ***
|
|
876
|
+
|
|
877
|
+
在使用本函数时,如果添加的模型用户在需求中没有提供像推理点名称,激活时的 api key,以及模型名称等,从而导致添加模型会发生不确定性,
|
|
878
|
+
你务必需要先通过函数 ask_user 来获取,之后得到完整信息再来执行 models 相关的操作。
|
|
879
|
+
|
|
880
|
+
比如用户说:帮我添加火山方舟的 R1 模型。你需要先问:火山方舟的 R1 模型推理点是什么?然后你再问:火山方舟的 API key 是什么?
|
|
881
|
+
收集到这两个信息后,你再执行:
|
|
882
|
+
|
|
883
|
+
models(query="/add_model name=ark_r1_chat base_url=https://ark.cn-beijing.volces.com/api/v3 model_name=<收集到的推理点名称> is_reasoning=true")
|
|
884
|
+
|
|
885
|
+
models(query="/activate ark_r1_chat <收集到的API key>")
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
</usage>
|
|
889
|
+
</command>
|
|
890
|
+
|
|
891
|
+
<command>
|
|
892
|
+
<name>ask_user</name>
|
|
893
|
+
<description>
|
|
894
|
+
如果你对用户的问题有什么疑问,或者你想从用户收集一些额外信息,可以调用此方法。
|
|
895
|
+
输入参数 question 是你对用户的提问。
|
|
896
|
+
返回值是 用户对你问题的回答。
|
|
897
|
+
** 如果你的问题比较多,建议一次就问一个,然后根据用户回答再问下一个。 **
|
|
898
|
+
</description>
|
|
899
|
+
<usage>
|
|
900
|
+
该命令接受一个参数 question,为需要向用户询问的问题字符串。
|
|
901
|
+
|
|
902
|
+
使用例子:
|
|
903
|
+
ask_user(question="请输入火山引擎的 R1 模型推理点")
|
|
904
|
+
|
|
905
|
+
</command>
|
|
906
|
+
|
|
907
|
+
<command>
|
|
908
|
+
<name>run_python</name>
|
|
909
|
+
<description>运行指定的Python代码。主要用于执行一些Python脚本或测试代码。</description>
|
|
910
|
+
<usage>
|
|
911
|
+
该命令接受一个参数 code,为要执行的Python代码字符串。
|
|
912
|
+
|
|
913
|
+
使用例子:
|
|
914
|
+
|
|
915
|
+
run_python(code="print('Hello World')")
|
|
916
|
+
|
|
917
|
+
注意:
|
|
918
|
+
- 代码将在项目根目录下执行
|
|
919
|
+
- 可以访问项目中的所有文件
|
|
920
|
+
- 输出结果会返回给用户
|
|
921
|
+
</usage>
|
|
922
|
+
</command>
|
|
923
|
+
|
|
924
|
+
<command>
|
|
925
|
+
<name>execute_shell_command</name>
|
|
926
|
+
<description>运行指定的Shell脚本。主要用于编译、运行、测试等任务。</description>
|
|
927
|
+
<usage>
|
|
928
|
+
该命令接受一个参数 command,为要执行的Shell脚本字符串。
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
使用例子:
|
|
932
|
+
|
|
933
|
+
execute_shell_command(command="ls -l")
|
|
934
|
+
|
|
935
|
+
注意:
|
|
936
|
+
- 脚本将在项目根目录下执行
|
|
937
|
+
- 禁止执行包含 rm 命令的脚本
|
|
938
|
+
- 输出结果会返回给用户
|
|
939
|
+
- 执行该命令的时候,需要通过 ask_user 询问用户是否同意执行,如果用户拒绝,则不再执行当前想执行的脚本呢。
|
|
940
|
+
</usage>
|
|
941
|
+
</command>
|
|
942
|
+
|
|
943
|
+
<command>
|
|
944
|
+
<name>generate_shell_command</name>
|
|
945
|
+
<description>
|
|
946
|
+
根据用户需求描述,生成shell脚本。
|
|
947
|
+
</description>
|
|
948
|
+
<usage>
|
|
949
|
+
支持的参数名为 input_text, 字符串类型,用户的需求,使用该函数,会打印生成结果,用户可以更加清晰
|
|
950
|
+
的看到生成的脚本。然后配合 ask_user, execute_shell_command 两个函数,最终完成
|
|
951
|
+
脚本执行。
|
|
952
|
+
</usage>
|
|
953
|
+
</command>
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
<command>
|
|
957
|
+
<name>get_project_structure</name>
|
|
958
|
+
<description>返回当前项目结构</description>
|
|
959
|
+
<usage>
|
|
960
|
+
该命令不需要参数。返回一个目录树结构(类似 tree 命令的输出)
|
|
961
|
+
|
|
962
|
+
使用例子:
|
|
963
|
+
|
|
964
|
+
get_project_structure()
|
|
965
|
+
|
|
966
|
+
该函数特别适合你通过目录结构来了解这个项目是什么类型的项目,有什么文件,如果你对一些文件
|
|
967
|
+
感兴趣,可以配合 read_files 函数来读取文件内容,从而帮你做更好的决策
|
|
968
|
+
|
|
969
|
+
</usage>
|
|
970
|
+
</command>
|
|
971
|
+
|
|
972
|
+
<command>
|
|
973
|
+
<name>get_related_files</name>
|
|
974
|
+
<description>根据类名、函数名或文件用途描述,返回项目中相关的文件。</description>
|
|
975
|
+
<usage>
|
|
976
|
+
该命令接受一个参数 query,为要查询的符号或描述字符串。
|
|
977
|
+
|
|
978
|
+
使用例子:
|
|
979
|
+
|
|
980
|
+
get_related_files(query="用户登录功能")
|
|
981
|
+
|
|
982
|
+
注意:
|
|
983
|
+
- 返回值为逗号分隔的文件路径列表
|
|
984
|
+
- 只能返回已被索引的文件
|
|
985
|
+
</usage>
|
|
986
|
+
</command>
|
|
987
|
+
|
|
988
|
+
<command>
|
|
989
|
+
<name>get_project_map</name>
|
|
990
|
+
<description>返回项目中指定文件包括文件用途、导入的包、定义的类、函数、变量等。</description>
|
|
991
|
+
<usage>
|
|
992
|
+
该命令接受一个参数 file_path,为文件路径(文件名或者文件路径的一部分)
|
|
993
|
+
|
|
994
|
+
使用例子:
|
|
995
|
+
|
|
996
|
+
get_project_map(file_path="main.py")
|
|
997
|
+
|
|
998
|
+
该函数特别适合你想要了解某个文件的用途,以及该文件的导入的包,定义的类,函数,变量等信息。
|
|
999
|
+
同时,你还能看到文件的大小(tokens数),以及索引的大小(tokens数),以及构建索引花费费用等信息。
|
|
1000
|
+
如果你觉得该文件确实是你关注的,你可以通过 read_files 函数来读取文件完整内容,从而帮你做更好的决策。
|
|
1001
|
+
|
|
1002
|
+
注意:
|
|
1003
|
+
- 返回值为JSON格式文本
|
|
1004
|
+
- 只能返回已被索引的文件
|
|
1005
|
+
</usage>
|
|
1006
|
+
</command>
|
|
1007
|
+
|
|
1008
|
+
<command>
|
|
1009
|
+
<name>read_files</name>
|
|
1010
|
+
<description>读取指定文件的内容(支持指定行范围),支持文件名或绝对路径。</description>
|
|
1011
|
+
<usage>
|
|
1012
|
+
该函数用于读取指定文件的内容。
|
|
1013
|
+
|
|
1014
|
+
参数说明:
|
|
1015
|
+
1. paths (str):
|
|
1016
|
+
- 以逗号分隔的文件路径列表
|
|
1017
|
+
- 支持两种格式:
|
|
1018
|
+
a) 文件名: 如果多个文件匹配该名称,将选择第一个匹配项
|
|
1019
|
+
b) 绝对路径: 直接指定文件的完整路径
|
|
1020
|
+
- 示例: "main.py,utils.py" 或 "/path/to/main.py,/path/to/utils.py"
|
|
1021
|
+
- 建议: 每次调用推荐一个文件,最多不要超过3个文件。
|
|
1022
|
+
|
|
1023
|
+
2. line_ranges (Optional[str]):
|
|
1024
|
+
- 可选参数,用于指定每个文件要读取的具体行范围
|
|
1025
|
+
- 格式说明:
|
|
1026
|
+
* 使用逗号分隔不同文件的行范围
|
|
1027
|
+
* 每个文件可以指定多个行范围,用/分隔
|
|
1028
|
+
* 每个行范围使用-连接起始行和结束行
|
|
1029
|
+
- 示例:
|
|
1030
|
+
* "1-100,2-50" (为两个文件分别指定一个行范围)
|
|
1031
|
+
* "1-100/200-300,50-100" (第一个文件指定两个行范围,第二个文件指定一个行范围)
|
|
1032
|
+
- 注意: line_ranges中的文件数量必须与paths中的文件数量一致,否则会抛出错误
|
|
1033
|
+
|
|
1034
|
+
返回值:
|
|
1035
|
+
- 返回str类型,包含所有请求文件的内容
|
|
1036
|
+
- 每个文件内容前会标注文件路径和行范围信息(如果指定了行范围)
|
|
1037
|
+
|
|
1038
|
+
使用例子:
|
|
1039
|
+
|
|
1040
|
+
read_files(paths="main.py,utils.py", line_ranges="1-100/200-300,50-100")
|
|
1041
|
+
|
|
1042
|
+
read_files(paths="main.py,utils.py")
|
|
1043
|
+
|
|
1044
|
+
你可以使用 get_project_structure 函数获取项目结构后,然后再通过 get_project_map 函数获取某个文件的用途,符号列表,以及
|
|
1045
|
+
文件大小(tokens数),最后再通过 read_files 函数来读取文件内容,从而帮你做更好的决策。如果需要读取的文件过大,
|
|
1046
|
+
你可以通过指定行范围来读取文件内容,从而避免读取过多的内容,你可以多次用行范围来调用 read_files,直到你看到满意的内容为止。
|
|
1047
|
+
|
|
1048
|
+
</usage>
|
|
1049
|
+
</command>
|
|
1050
|
+
|
|
1051
|
+
<command>
|
|
1052
|
+
<name>find_files_by_name</name>
|
|
1053
|
+
<description>根据文件名中的关键字搜索文件。</description>
|
|
1054
|
+
<usage>
|
|
1055
|
+
该命令接受一个参数 keyword,为要搜索的关键字字符串。
|
|
1056
|
+
|
|
1057
|
+
使用例子:
|
|
1058
|
+
|
|
1059
|
+
find_files_by_name(keyword="test")
|
|
1060
|
+
|
|
1061
|
+
注意:
|
|
1062
|
+
- 搜索不区分大小写
|
|
1063
|
+
- 返回所有匹配的文件路径,逗号分隔
|
|
1064
|
+
</usage>
|
|
1065
|
+
</command>
|
|
1066
|
+
|
|
1067
|
+
<command>
|
|
1068
|
+
<name>find_files_by_content</name>
|
|
1069
|
+
<description>根据文件内容中的关键字搜索文件。</description>
|
|
1070
|
+
<usage>
|
|
1071
|
+
该命令接受一个参数 keyword,为要搜索的关键字字符串。
|
|
1072
|
+
|
|
1073
|
+
使用例子:
|
|
1074
|
+
|
|
1075
|
+
find_files_by_content(keyword="TODO")
|
|
1076
|
+
|
|
1077
|
+
注意:
|
|
1078
|
+
- 搜索不区分大小写
|
|
1079
|
+
- 如果结果过多,只返回前10个匹配项
|
|
1080
|
+
</usage>
|
|
1081
|
+
</command>
|
|
1082
|
+
|
|
1083
|
+
<command>
|
|
1084
|
+
<name>read_file_with_keyword_ranges</name>
|
|
1085
|
+
<description>读取包含指定关键字的行及其前后指定范围的行。</description>
|
|
1086
|
+
<usage>
|
|
1087
|
+
该函数用于读取包含关键字的行及其前后指定范围的行。
|
|
1088
|
+
|
|
1089
|
+
参数说明:
|
|
1090
|
+
1. file_path (str): 文件路径,可以是相对路径或绝对路径
|
|
1091
|
+
2. keyword (str): 要搜索的关键字
|
|
1092
|
+
3. before_size (int): 关键字行之前要读取的行数,默认100
|
|
1093
|
+
4. after_size (int): 关键字行之后要读取的行数,默认100
|
|
1094
|
+
|
|
1095
|
+
返回值:
|
|
1096
|
+
- 返回str类型,包含关键字的行及其前后指定范围的行
|
|
1097
|
+
- 格式如下:
|
|
1098
|
+
```
|
|
1099
|
+
##File: /path/to/file.py
|
|
1100
|
+
##Line: 10-20
|
|
1101
|
+
|
|
1102
|
+
内容
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
使用例子:
|
|
1106
|
+
read_file_with_keyword_ranges(file_path="main.py", keyword="TODO", before_size=5, after_size=5)
|
|
1107
|
+
|
|
1108
|
+
注意:
|
|
1109
|
+
- 如果文件中有多个匹配的关键字,会返回多个内容块
|
|
1110
|
+
- 搜索不区分大小写
|
|
1111
|
+
</usage>
|
|
1112
|
+
</command>
|
|
1113
|
+
|
|
1114
|
+
</commands>
|
|
1115
|
+
'''
|
|
1116
|
+
|
|
1117
|
+
def execute_auto_command(self, command: str, parameters: Dict[str, Any]) -> None:
|
|
1118
|
+
"""
|
|
1119
|
+
执行自动生成的命令
|
|
1120
|
+
"""
|
|
1121
|
+
command_map = {
|
|
1122
|
+
"add_files": self.command_config.add_files,
|
|
1123
|
+
"remove_files": self.command_config.remove_files,
|
|
1124
|
+
"list_files": self.command_config.list_files,
|
|
1125
|
+
"revert": self.command_config.revert,
|
|
1126
|
+
"commit": self.command_config.commit,
|
|
1127
|
+
"help": self.command_config.help,
|
|
1128
|
+
"exclude_dirs": self.command_config.exclude_dirs,
|
|
1129
|
+
"ask": self.command_config.ask,
|
|
1130
|
+
"chat": self.command_config.chat,
|
|
1131
|
+
"coding": self.command_config.coding,
|
|
1132
|
+
"design": self.command_config.design,
|
|
1133
|
+
"summon": self.command_config.summon,
|
|
1134
|
+
"lib": self.command_config.lib,
|
|
1135
|
+
"models": self.command_config.models,
|
|
1136
|
+
"execute_shell_command": self.command_config.execute_shell_command,
|
|
1137
|
+
"generate_shell_command": self.command_config.generate_shell_command,
|
|
1138
|
+
|
|
1139
|
+
"run_python": self.tools.run_python_code,
|
|
1140
|
+
"get_related_files_by_symbols": self.tools.get_related_files_by_symbols,
|
|
1141
|
+
"get_project_map": self.tools.get_project_map,
|
|
1142
|
+
"get_project_structure": self.tools.get_project_structure,
|
|
1143
|
+
"read_files": self.tools.read_files,
|
|
1144
|
+
"find_files_by_name": self.tools.find_files_by_name,
|
|
1145
|
+
"find_files_by_content": self.tools.find_files_by_content,
|
|
1146
|
+
"get_project_related_files": self.tools.get_project_related_files,
|
|
1147
|
+
"ask_user":self.tools.ask_user,
|
|
1148
|
+
"read_file_with_keyword_ranges": self.tools.read_file_with_keyword_ranges,
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
if command not in command_map:
|
|
1154
|
+
self.printer.print_in_terminal(
|
|
1155
|
+
"auto_command_not_found", style="red", command=command)
|
|
1156
|
+
return
|
|
1157
|
+
|
|
1158
|
+
try:
|
|
1159
|
+
# 将参数字典转换为命令所需的格式
|
|
1160
|
+
if parameters:
|
|
1161
|
+
command_map[command](**parameters)
|
|
1162
|
+
else:
|
|
1163
|
+
command_map[command]()
|
|
1164
|
+
|
|
1165
|
+
except Exception as e:
|
|
1166
|
+
error_msg = str(e)
|
|
1167
|
+
self.printer.print_in_terminal(
|
|
1168
|
+
"auto_command_failed", style="red", command=command, error=error_msg)
|
|
1169
|
+
|
|
1170
|
+
# Save failed command execution
|
|
1171
|
+
save_to_memory_file(
|
|
1172
|
+
query=f"Command: {command} Parameters: {json.dumps(parameters) if parameters else 'None'}",
|
|
1173
|
+
response=f"Command execution failed: {error_msg}"
|
|
1174
|
+
)
|