auto-coder 0.1.364__py3-none-any.whl → 0.1.365__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of auto-coder might be problematic. Click here for more details.
- {auto_coder-0.1.364.dist-info → auto_coder-0.1.365.dist-info}/METADATA +1 -1
- {auto_coder-0.1.364.dist-info → auto_coder-0.1.365.dist-info}/RECORD +19 -18
- autocoder/auto_coder.py +46 -2
- autocoder/common/__init__.py +3 -0
- autocoder/common/file_checkpoint/conversation_checkpoint.py +182 -0
- autocoder/common/file_checkpoint/manager.py +208 -1
- autocoder/common/utils_code_auto_generate.py +2 -1
- autocoder/common/v2/agent/agentic_edit.py +291 -92
- autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +83 -43
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +115 -28
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +169 -61
- autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +101 -56
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +95 -76
- autocoder/common/v2/agent/agentic_edit_types.py +4 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.364.dist-info → auto_coder-0.1.365.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.364.dist-info → auto_coder-0.1.365.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.364.dist-info → auto_coder-0.1.365.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.364.dist-info → auto_coder-0.1.365.dist-info}/top_level.txt +0 -0
|
@@ -44,6 +44,7 @@ from autocoder.common.save_formatted_log import save_formatted_log
|
|
|
44
44
|
# Import the new display function
|
|
45
45
|
from autocoder.common.v2.agent.agentic_tool_display import get_tool_display_message
|
|
46
46
|
from autocoder.common.v2.agent.agentic_edit_types import FileChangeEntry
|
|
47
|
+
from autocoder.utils.llms import get_single_llm
|
|
47
48
|
|
|
48
49
|
from autocoder.common.file_checkpoint.models import FileChange as CheckpointFileChange
|
|
49
50
|
from autocoder.common.file_checkpoint.manager import FileChangeManager as CheckpointFileChangeManager
|
|
@@ -72,12 +73,15 @@ from autocoder.common.v2.agent.agentic_edit_types import (AgenticEditRequest, To
|
|
|
72
73
|
# Event Types
|
|
73
74
|
LLMOutputEvent, LLMThinkingEvent, ToolCallEvent,
|
|
74
75
|
ToolResultEvent, CompletionEvent, PlanModeRespondEvent, ErrorEvent, TokenUsageEvent,
|
|
76
|
+
WindowLengthChangeEvent,
|
|
75
77
|
# Import specific tool types for display mapping
|
|
76
78
|
ReadFileTool, WriteToFileTool, ReplaceInFileTool, ExecuteCommandTool,
|
|
77
79
|
ListFilesTool, SearchFilesTool, ListCodeDefinitionNamesTool,
|
|
78
80
|
AskFollowupQuestionTool, UseMcpTool, AttemptCompletionTool
|
|
79
81
|
)
|
|
80
82
|
|
|
83
|
+
from autocoder.rag.token_counter import count_tokens
|
|
84
|
+
|
|
81
85
|
# Map Pydantic Tool Models to their Resolver Classes
|
|
82
86
|
TOOL_RESOLVER_MAP: Dict[Type[BaseTool], Type[BaseToolResolver]] = {
|
|
83
87
|
ExecuteCommandTool: ExecuteCommandToolResolver,
|
|
@@ -110,6 +114,7 @@ class AgenticEdit:
|
|
|
110
114
|
conversation_name:Optional[str] = "current"
|
|
111
115
|
):
|
|
112
116
|
self.llm = llm
|
|
117
|
+
self.context_prune_llm = get_single_llm(args.context_prune_model or args.model,product_mode=args.product_mode)
|
|
113
118
|
self.args = args
|
|
114
119
|
self.printer = Printer()
|
|
115
120
|
# Removed self.tools and self.result_manager
|
|
@@ -117,6 +122,8 @@ class AgenticEdit:
|
|
|
117
122
|
# Removed self.max_iterations
|
|
118
123
|
# Note: This might need updating based on the new flow
|
|
119
124
|
self.conversation_history = conversation_history
|
|
125
|
+
|
|
126
|
+
self.current_conversations = []
|
|
120
127
|
self.memory_config = memory_config
|
|
121
128
|
self.command_config = command_config # Note: command_config might be unused now
|
|
122
129
|
self.project_type_analyzer = ProjectTypeAnalyzer(
|
|
@@ -610,30 +617,43 @@ class AgenticEdit:
|
|
|
610
617
|
- If at any point a mermaid diagram would make your plan clearer to help the user quickly see the structure, you are encouraged to include a Mermaid code block in the response. (Note: if you use colors in your mermaid diagrams, be sure to use high contrast colors so the text is readable.)
|
|
611
618
|
- Finally once it seems like you've reached a good plan, ask the user to switch you back to ACT MODE to implement the solution.
|
|
612
619
|
|
|
613
|
-
{% if enable_active_context_in_generate %}
|
|
614
620
|
====
|
|
615
621
|
|
|
616
|
-
|
|
622
|
+
PACKAGE CONTEXT INFORMATION
|
|
623
|
+
|
|
624
|
+
# Understanding Directory Context
|
|
625
|
+
|
|
626
|
+
## Purpose
|
|
617
627
|
|
|
618
|
-
Each directory
|
|
619
|
-
|
|
628
|
+
- Each directory in the project (especially source code directories) has implicit context information
|
|
629
|
+
- This includes recent changes, important files, and their purposes
|
|
630
|
+
- This contextual information helps you understand the role of the directory and the files in the directory
|
|
631
|
+
|
|
632
|
+
## Accessing Directory Context
|
|
633
|
+
|
|
634
|
+
- Use the **list_package_info** tool to view this information for a specific directory
|
|
635
|
+
- Do NOT use other tools like list_files to view this specialized context information
|
|
636
|
+
|
|
637
|
+
## When to Use
|
|
620
638
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
639
|
+
- When you need to understand what has recently changed in a directory
|
|
640
|
+
- When you need insight into the purpose and organization of a directory
|
|
641
|
+
- Before diving into detailed file exploration with other tools
|
|
624
642
|
|
|
625
|
-
|
|
643
|
+
## Example
|
|
626
644
|
|
|
627
645
|
```xml
|
|
628
|
-
<
|
|
629
|
-
<path
|
|
630
|
-
</
|
|
646
|
+
<list_package_info>
|
|
647
|
+
<path>src/some/directory</path>
|
|
648
|
+
</list_package_info>
|
|
631
649
|
```
|
|
632
650
|
|
|
633
|
-
|
|
634
|
-
|
|
651
|
+
# Benefits
|
|
652
|
+
|
|
653
|
+
- Quickly identifies recently modified files that may be relevant to your task
|
|
654
|
+
- Provides high-level understanding of directory contents and purpose
|
|
655
|
+
- Helps prioritize which files to examine in detail with tools like read_file, search_files, or list_code_definition_names
|
|
635
656
|
|
|
636
|
-
{% endif %}
|
|
637
657
|
====
|
|
638
658
|
|
|
639
659
|
CAPABILITIES
|
|
@@ -675,18 +695,34 @@ class AgenticEdit:
|
|
|
675
695
|
{% if extra_docs %}
|
|
676
696
|
====
|
|
677
697
|
|
|
678
|
-
RULES PROVIDED BY USER
|
|
698
|
+
RULES OR DOCUMENTS PROVIDED BY USER
|
|
679
699
|
|
|
680
700
|
The following rules are provided by the user, and you must follow them strictly.
|
|
681
701
|
|
|
702
|
+
<user_rule_or_document_files>
|
|
682
703
|
{% for key, value in extra_docs.items() %}
|
|
683
|
-
<
|
|
704
|
+
<user_rule_or_document_file>
|
|
684
705
|
##File: {{ key }}
|
|
685
706
|
{{ value }}
|
|
686
|
-
</
|
|
687
|
-
{% endfor %}
|
|
707
|
+
</user_rule_or_document_file>
|
|
708
|
+
{% endfor %}
|
|
709
|
+
</user_rule_or_document_files>
|
|
710
|
+
|
|
688
711
|
Make sure you always start your task by using the read_file tool to get the relevant RULE files listed in index.md based on the user's specific requirements.
|
|
689
712
|
{% endif %}
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
{% if file_paths_str %}
|
|
716
|
+
====
|
|
717
|
+
|
|
718
|
+
FILES MENTIONED BY USER
|
|
719
|
+
|
|
720
|
+
The following are files or directories that the user mentioned.
|
|
721
|
+
Make sure you always start your task by using the read_file tool to get the content of the files or list_files tool to list the files contained in the mentioned directories. If it is a directory, please use list_files to see what files it contains, and read the files as needed using read_file. If it is a file, please use read_file to read the file.
|
|
722
|
+
<files>
|
|
723
|
+
{{file_paths_str}}
|
|
724
|
+
</files>
|
|
725
|
+
{% endif %}
|
|
690
726
|
|
|
691
727
|
|
|
692
728
|
====
|
|
@@ -709,15 +745,7 @@ class AgenticEdit:
|
|
|
709
745
|
3. Remember, you have extensive capabilities with access to a wide range of tools that can be used in powerful and clever ways as necessary to accomplish each goal. Before calling a tool, do some analysis within <thinking></thinking> tags. First, analyze the file structure provided in environment_details to gain context and insights for proceeding effectively. Then, think about which of the provided tools is the most relevant tool to accomplish the user's task. Next, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool use. BUT, if one of the values for a required parameter is missing, DO NOT invoke the tool (not even with fillers for the missing params) and instead, ask the user to provide the missing parameters using the ask_followup_question tool. DO NOT ask for more information on optional parameters if it is not provided.
|
|
710
746
|
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.
|
|
711
747
|
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.
|
|
712
|
-
|
|
713
|
-
{% if file_paths_str %}
|
|
714
|
-
====
|
|
715
|
-
The following are files that the user is currently focusing on.
|
|
716
|
-
Make sure you always start your task by using the read_file tool to get the content of the files.
|
|
717
|
-
<files>
|
|
718
|
-
{{file_paths_str}}
|
|
719
|
-
</files>
|
|
720
|
-
{% endif %}
|
|
748
|
+
|
|
721
749
|
"""
|
|
722
750
|
## auto_select_rules(context=request.user_input, llm=self.llm,args=self.args) rules =
|
|
723
751
|
# extra_docs = get_rules()
|
|
@@ -790,7 +818,7 @@ class AgenticEdit:
|
|
|
790
818
|
# Join with newline for readability, matching prompt examples
|
|
791
819
|
return "\n".join(xml_parts)
|
|
792
820
|
|
|
793
|
-
def analyze(self, request: AgenticEditRequest) -> Generator[Union[LLMOutputEvent, LLMThinkingEvent, ToolCallEvent, ToolResultEvent, CompletionEvent, ErrorEvent], None, None]:
|
|
821
|
+
def analyze(self, request: AgenticEditRequest) -> Generator[Union[LLMOutputEvent, LLMThinkingEvent, ToolCallEvent, ToolResultEvent, CompletionEvent, ErrorEvent, WindowLengthChangeEvent], None, None]:
|
|
794
822
|
"""
|
|
795
823
|
Analyzes the user request, interacts with the LLM, parses responses,
|
|
796
824
|
executes tools, and yields structured events for visualization until completion or error.
|
|
@@ -800,6 +828,7 @@ class AgenticEdit:
|
|
|
800
828
|
logger.info(f"Generated system prompt with length: {len(system_prompt)}")
|
|
801
829
|
|
|
802
830
|
# print(system_prompt)
|
|
831
|
+
|
|
803
832
|
conversations = [
|
|
804
833
|
{"role": "system", "content": system_prompt},
|
|
805
834
|
]
|
|
@@ -808,8 +837,16 @@ class AgenticEdit:
|
|
|
808
837
|
"role": "user", "content": request.user_input
|
|
809
838
|
})
|
|
810
839
|
|
|
840
|
+
|
|
841
|
+
self.current_conversations = conversations
|
|
842
|
+
|
|
843
|
+
# 计算初始对话窗口长度并触发事件
|
|
844
|
+
conversation_str = json.dumps(conversations, ensure_ascii=False)
|
|
845
|
+
current_tokens = count_tokens(conversation_str)
|
|
846
|
+
yield WindowLengthChangeEvent(tokens_used=current_tokens)
|
|
847
|
+
|
|
811
848
|
logger.info(
|
|
812
|
-
f"Initial conversation history size: {len(conversations)}")
|
|
849
|
+
f"Initial conversation history size: {len(conversations)}, tokens: {current_tokens}")
|
|
813
850
|
|
|
814
851
|
iteration_count = 0
|
|
815
852
|
tool_executed = False
|
|
@@ -862,11 +899,18 @@ class AgenticEdit:
|
|
|
862
899
|
|
|
863
900
|
# Append assistant's thoughts and the tool call to history
|
|
864
901
|
logger.info(f"Adding assistant message with tool call to conversation history")
|
|
902
|
+
|
|
903
|
+
# 记录当前对话的token数量
|
|
865
904
|
conversations.append({
|
|
866
905
|
"role": "assistant",
|
|
867
906
|
"content": assistant_buffer + tool_xml
|
|
868
907
|
})
|
|
869
908
|
assistant_buffer = "" # Reset buffer after tool call
|
|
909
|
+
|
|
910
|
+
# 计算当前对话的总 token 数量并触发事件
|
|
911
|
+
current_conversation_str = json.dumps(conversations, ensure_ascii=False)
|
|
912
|
+
total_tokens = count_tokens(current_conversation_str)
|
|
913
|
+
yield WindowLengthChangeEvent(tokens_used=total_tokens)
|
|
870
914
|
|
|
871
915
|
yield event # Yield the ToolCallEvent for display
|
|
872
916
|
logger.info("Yielded ToolCallEvent")
|
|
@@ -946,10 +990,18 @@ class AgenticEdit:
|
|
|
946
990
|
|
|
947
991
|
# Append the tool result (as user message) to history
|
|
948
992
|
logger.info("Adding tool result to conversation history")
|
|
993
|
+
|
|
994
|
+
# 添加工具结果到对话历史
|
|
949
995
|
conversations.append({
|
|
950
996
|
"role": "user", # Simulating the user providing the tool result
|
|
951
997
|
"content": error_xml
|
|
952
998
|
})
|
|
999
|
+
|
|
1000
|
+
# 计算当前对话的总 token 数量并触发事件
|
|
1001
|
+
current_conversation_str = json.dumps(conversations, ensure_ascii=False)
|
|
1002
|
+
total_tokens = count_tokens(current_conversation_str)
|
|
1003
|
+
yield WindowLengthChangeEvent(tokens_used=total_tokens)
|
|
1004
|
+
|
|
953
1005
|
logger.info(
|
|
954
1006
|
f"Added tool result to conversations for tool {type(tool_obj).__name__}")
|
|
955
1007
|
logger.info(f"Breaking LLM cycle after executing tool: {tool_name}")
|
|
@@ -972,6 +1024,7 @@ class AgenticEdit:
|
|
|
972
1024
|
# Append any remaining assistant buffer to history if it wasn't followed by a tool
|
|
973
1025
|
if assistant_buffer:
|
|
974
1026
|
logger.info(f"Appending assistant buffer to history: {len(assistant_buffer)} chars")
|
|
1027
|
+
|
|
975
1028
|
last_message = conversations[-1]
|
|
976
1029
|
if last_message["role"] != "assistant":
|
|
977
1030
|
logger.info("Adding new assistant message")
|
|
@@ -980,13 +1033,24 @@ class AgenticEdit:
|
|
|
980
1033
|
elif last_message["role"] == "assistant":
|
|
981
1034
|
logger.info("Appending to existing assistant message")
|
|
982
1035
|
last_message["content"] += assistant_buffer
|
|
1036
|
+
|
|
1037
|
+
# 计算当前对话的总 token 数量并触发事件
|
|
1038
|
+
current_conversation_str = json.dumps(conversations, ensure_ascii=False)
|
|
1039
|
+
total_tokens = count_tokens(current_conversation_str)
|
|
1040
|
+
yield WindowLengthChangeEvent(tokens_used=total_tokens)
|
|
983
1041
|
|
|
984
1042
|
# 添加系统提示,要求LLM必须使用工具或明确结束,而不是直接退出
|
|
985
1043
|
logger.info("Adding system reminder to use tools or attempt completion")
|
|
1044
|
+
|
|
986
1045
|
conversations.append({
|
|
987
1046
|
"role": "user",
|
|
988
1047
|
"content": "NOTE: You must use an appropriate tool (such as read_file, write_to_file, execute_command, etc.) or explicitly complete the task (using attempt_completion). Do not provide text responses without taking concrete actions. Please select a suitable tool to continue based on the user's task."
|
|
989
1048
|
})
|
|
1049
|
+
|
|
1050
|
+
# 计算当前对话的总 token 数量并触发事件
|
|
1051
|
+
current_conversation_str = json.dumps(conversations, ensure_ascii=False)
|
|
1052
|
+
total_tokens = count_tokens(current_conversation_str)
|
|
1053
|
+
yield WindowLengthChangeEvent(tokens_used=total_tokens)
|
|
990
1054
|
# 继续循环,让 LLM 再思考,而不是 break
|
|
991
1055
|
logger.info("Continuing the LLM interaction loop without breaking")
|
|
992
1056
|
continue
|
|
@@ -1189,9 +1253,7 @@ class AgenticEdit:
|
|
|
1189
1253
|
|
|
1190
1254
|
# If no event was processed in this iteration, break inner loop
|
|
1191
1255
|
if not found_event:
|
|
1192
|
-
break
|
|
1193
|
-
|
|
1194
|
-
yield TokenUsageEvent(usage=meta_holder.meta)
|
|
1256
|
+
break
|
|
1195
1257
|
|
|
1196
1258
|
# After generator exhausted, yield any remaining content
|
|
1197
1259
|
if in_thinking_block:
|
|
@@ -1208,6 +1270,9 @@ class AgenticEdit:
|
|
|
1208
1270
|
elif buffer:
|
|
1209
1271
|
# Yield remaining plain text
|
|
1210
1272
|
yield LLMOutputEvent(text=buffer)
|
|
1273
|
+
|
|
1274
|
+
# 这个要放在最后,防止其他关联的多个事件的信息中断
|
|
1275
|
+
yield TokenUsageEvent(usage=meta_holder.meta)
|
|
1211
1276
|
|
|
1212
1277
|
|
|
1213
1278
|
def apply_pre_changes(self):
|
|
@@ -1235,6 +1300,162 @@ class AgenticEdit:
|
|
|
1235
1300
|
source_dir=self.args.source_dir, error=str(e))
|
|
1236
1301
|
return
|
|
1237
1302
|
|
|
1303
|
+
def get_available_checkpoints(self) -> List[Dict[str, Any]]:
|
|
1304
|
+
"""
|
|
1305
|
+
获取可用的检查点列表
|
|
1306
|
+
|
|
1307
|
+
Returns:
|
|
1308
|
+
List[Dict[str, Any]]: 检查点信息列表
|
|
1309
|
+
"""
|
|
1310
|
+
if not self.checkpoint_manager:
|
|
1311
|
+
return []
|
|
1312
|
+
|
|
1313
|
+
return self.checkpoint_manager.get_available_checkpoints()
|
|
1314
|
+
|
|
1315
|
+
def rollback_to_checkpoint(self, checkpoint_id: str) -> bool:
|
|
1316
|
+
"""
|
|
1317
|
+
回滚到指定的检查点,恢复文件状态和对话状态
|
|
1318
|
+
|
|
1319
|
+
Args:
|
|
1320
|
+
checkpoint_id: 检查点ID
|
|
1321
|
+
|
|
1322
|
+
Returns:
|
|
1323
|
+
bool: 是否成功回滚
|
|
1324
|
+
"""
|
|
1325
|
+
if not self.checkpoint_manager:
|
|
1326
|
+
logger.error("无法回滚:检查点管理器未初始化")
|
|
1327
|
+
return False
|
|
1328
|
+
|
|
1329
|
+
try:
|
|
1330
|
+
# 回滚文件变更
|
|
1331
|
+
undo_result, checkpoint = self.checkpoint_manager.undo_change_group_with_conversation(checkpoint_id)
|
|
1332
|
+
if not undo_result.success:
|
|
1333
|
+
logger.error(f"回滚文件变更失败: {undo_result.errors}")
|
|
1334
|
+
return False
|
|
1335
|
+
|
|
1336
|
+
# 恢复对话状态
|
|
1337
|
+
if checkpoint:
|
|
1338
|
+
self.current_conversations = checkpoint.conversations
|
|
1339
|
+
logger.info(f"已恢复对话状态,包含 {len(checkpoint.conversations)} 条消息")
|
|
1340
|
+
return True
|
|
1341
|
+
else:
|
|
1342
|
+
logger.warning(f"未找到关联的对话检查点: {checkpoint_id},只回滚了文件变更")
|
|
1343
|
+
return undo_result.success
|
|
1344
|
+
except Exception as e:
|
|
1345
|
+
logger.exception(f"回滚到检查点 {checkpoint_id} 失败: {str(e)}")
|
|
1346
|
+
return False
|
|
1347
|
+
|
|
1348
|
+
def handle_rollback_command(self, command: str) -> str:
|
|
1349
|
+
"""
|
|
1350
|
+
处理回滚相关的命令
|
|
1351
|
+
|
|
1352
|
+
Args:
|
|
1353
|
+
command: 命令字符串,如 "rollback list", "rollback to <id>", "rollback info <id>"
|
|
1354
|
+
|
|
1355
|
+
Returns:
|
|
1356
|
+
str: 命令执行结果
|
|
1357
|
+
"""
|
|
1358
|
+
if command == "rollback list":
|
|
1359
|
+
# 列出可用的检查点
|
|
1360
|
+
checkpoints = self.get_available_checkpoints()
|
|
1361
|
+
if not checkpoints:
|
|
1362
|
+
return "没有可用的检查点。"
|
|
1363
|
+
|
|
1364
|
+
result = "可用的检查点列表:\n"
|
|
1365
|
+
for i, cp in enumerate(checkpoints):
|
|
1366
|
+
time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(cp["timestamp"]))
|
|
1367
|
+
result += f"{i+1}. ID: {cp['id'][:8]}... | 时间: {time_str} | 变更文件数: {cp['changes_count']}"
|
|
1368
|
+
result += f" | {'包含对话状态' if cp['has_conversation'] else '不包含对话状态'}\n"
|
|
1369
|
+
|
|
1370
|
+
return result
|
|
1371
|
+
|
|
1372
|
+
elif command.startswith("rollback info "):
|
|
1373
|
+
# 显示检查点详情
|
|
1374
|
+
cp_id = command[len("rollback info "):].strip()
|
|
1375
|
+
|
|
1376
|
+
# 查找检查点
|
|
1377
|
+
checkpoints = self.get_available_checkpoints()
|
|
1378
|
+
target_cp = None
|
|
1379
|
+
|
|
1380
|
+
# 支持通过序号或ID查询
|
|
1381
|
+
if cp_id.isdigit() and 1 <= int(cp_id) <= len(checkpoints):
|
|
1382
|
+
target_cp = checkpoints[int(cp_id) - 1]
|
|
1383
|
+
else:
|
|
1384
|
+
for cp in checkpoints:
|
|
1385
|
+
if cp["id"].startswith(cp_id):
|
|
1386
|
+
target_cp = cp
|
|
1387
|
+
break
|
|
1388
|
+
|
|
1389
|
+
if not target_cp:
|
|
1390
|
+
return f"未找到ID为 {cp_id} 的检查点。"
|
|
1391
|
+
|
|
1392
|
+
# 获取检查点详细信息
|
|
1393
|
+
time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(target_cp["timestamp"]))
|
|
1394
|
+
|
|
1395
|
+
# 获取变更文件列表
|
|
1396
|
+
changes = self.checkpoint_manager.get_changes_by_group(target_cp["id"])
|
|
1397
|
+
changed_files = [change.file_path for change in changes]
|
|
1398
|
+
|
|
1399
|
+
# 获取对话状态信息
|
|
1400
|
+
conversation_info = "无对话状态信息"
|
|
1401
|
+
if target_cp["has_conversation"] and hasattr(self.checkpoint_manager, 'conversation_store'):
|
|
1402
|
+
checkpoint = self.checkpoint_manager.conversation_store.get_checkpoint(target_cp["id"])
|
|
1403
|
+
if checkpoint and checkpoint.conversations:
|
|
1404
|
+
conversation_info = f"包含 {len(checkpoint.conversations)} 条对话消息"
|
|
1405
|
+
|
|
1406
|
+
result = f"检查点详情:\n"
|
|
1407
|
+
result += f"ID: {target_cp['id']}\n"
|
|
1408
|
+
result += f"创建时间: {time_str}\n"
|
|
1409
|
+
result += f"变更文件数: {target_cp['changes_count']}\n"
|
|
1410
|
+
result += f"对话状态: {conversation_info}\n\n"
|
|
1411
|
+
|
|
1412
|
+
if changed_files:
|
|
1413
|
+
result += "变更文件列表:\n"
|
|
1414
|
+
for i, file_path in enumerate(changed_files):
|
|
1415
|
+
result += f"{i+1}. {file_path}\n"
|
|
1416
|
+
|
|
1417
|
+
return result
|
|
1418
|
+
|
|
1419
|
+
elif command.startswith("rollback to "):
|
|
1420
|
+
# 回滚到指定检查点
|
|
1421
|
+
cp_id = command[len("rollback to "):].strip()
|
|
1422
|
+
|
|
1423
|
+
# 查找检查点
|
|
1424
|
+
checkpoints = self.get_available_checkpoints()
|
|
1425
|
+
target_cp = None
|
|
1426
|
+
|
|
1427
|
+
# 支持通过序号或ID回滚
|
|
1428
|
+
if cp_id.isdigit() and 1 <= int(cp_id) <= len(checkpoints):
|
|
1429
|
+
target_cp = checkpoints[int(cp_id) - 1]
|
|
1430
|
+
else:
|
|
1431
|
+
for cp in checkpoints:
|
|
1432
|
+
if cp["id"].startswith(cp_id):
|
|
1433
|
+
target_cp = cp
|
|
1434
|
+
break
|
|
1435
|
+
|
|
1436
|
+
if not target_cp:
|
|
1437
|
+
return f"未找到ID为 {cp_id} 的检查点。"
|
|
1438
|
+
|
|
1439
|
+
# 执行回滚
|
|
1440
|
+
success = self.rollback_to_checkpoint(target_cp["id"])
|
|
1441
|
+
|
|
1442
|
+
if success:
|
|
1443
|
+
# 获取变更文件列表
|
|
1444
|
+
changes = self.checkpoint_manager.get_changes_by_group(target_cp["id"])
|
|
1445
|
+
changed_files = [change.file_path for change in changes]
|
|
1446
|
+
|
|
1447
|
+
result = f"成功回滚到检查点 {target_cp['id'][:8]}...\n"
|
|
1448
|
+
result += f"恢复了 {len(changed_files)} 个文件的状态"
|
|
1449
|
+
|
|
1450
|
+
if target_cp["has_conversation"]:
|
|
1451
|
+
result += f"\n同时恢复了对话状态"
|
|
1452
|
+
|
|
1453
|
+
return result
|
|
1454
|
+
else:
|
|
1455
|
+
return f"回滚到检查点 {target_cp['id'][:8]}... 失败。"
|
|
1456
|
+
|
|
1457
|
+
return "未知命令。可用命令:rollback list, rollback info <id>, rollback to <id>"
|
|
1458
|
+
|
|
1238
1459
|
def apply_changes(self):
|
|
1239
1460
|
"""
|
|
1240
1461
|
Apply all tracked file changes to the original project directory.
|
|
@@ -1363,7 +1584,12 @@ class AgenticEdit:
|
|
|
1363
1584
|
accumulated_token_usage["input_cost"] += input_cost
|
|
1364
1585
|
accumulated_token_usage["output_cost"] += output_cost
|
|
1365
1586
|
|
|
1366
|
-
|
|
1587
|
+
elif isinstance(event, WindowLengthChangeEvent):
|
|
1588
|
+
# 显示当前会话的token数量
|
|
1589
|
+
logger.info(f"当前会话总 tokens: {event.tokens_used}")
|
|
1590
|
+
console.print(f"[dim]当前会话总 tokens: {event.tokens_used}[/dim]")
|
|
1591
|
+
|
|
1592
|
+
elif isinstance(event, LLMThinkingEvent):
|
|
1367
1593
|
# Render thinking within a less prominent style, maybe grey?
|
|
1368
1594
|
console.print(f"[grey50]{event.text}[/grey50]", end="")
|
|
1369
1595
|
elif isinstance(event, LLMOutputEvent):
|
|
@@ -1527,18 +1753,7 @@ class AgenticEdit:
|
|
|
1527
1753
|
standard event system format and writing them using the event manager.
|
|
1528
1754
|
"""
|
|
1529
1755
|
event_manager = get_event_manager(self.args.event_file)
|
|
1530
|
-
self.apply_pre_changes()
|
|
1531
|
-
|
|
1532
|
-
# 用于累计TokenUsageEvent数据
|
|
1533
|
-
accumulated_token_usage = {
|
|
1534
|
-
"model_name": "",
|
|
1535
|
-
"elapsed_time": 0.0,
|
|
1536
|
-
"first_token_time": 0.0,
|
|
1537
|
-
"input_tokens": 0,
|
|
1538
|
-
"output_tokens": 0,
|
|
1539
|
-
"input_cost": 0.0,
|
|
1540
|
-
"output_cost": 0.0
|
|
1541
|
-
}
|
|
1756
|
+
self.apply_pre_changes()
|
|
1542
1757
|
|
|
1543
1758
|
try:
|
|
1544
1759
|
event_stream = self.analyze(request)
|
|
@@ -1616,15 +1831,20 @@ class AgenticEdit:
|
|
|
1616
1831
|
|
|
1617
1832
|
# 添加日志记录
|
|
1618
1833
|
logger.info(f"Token Usage Details: Model={model_name}, Input Tokens={last_meta.input_tokens_count}, Output Tokens={last_meta.generated_tokens_count}, Input Cost=${input_cost:.6f}, Output Cost=${output_cost:.6f}")
|
|
1619
|
-
|
|
1620
|
-
#
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1834
|
+
|
|
1835
|
+
# 直接将每次的 TokenUsageEvent 写入到事件中
|
|
1836
|
+
metadata.path = "/agent/edit/token_usage"
|
|
1837
|
+
content = EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
|
|
1838
|
+
model_name=model_name,
|
|
1839
|
+
elapsed_time=0.0,
|
|
1840
|
+
first_token_time=last_meta.first_token_time,
|
|
1841
|
+
input_tokens=last_meta.input_tokens_count,
|
|
1842
|
+
output_tokens=last_meta.generated_tokens_count,
|
|
1843
|
+
input_cost=input_cost,
|
|
1844
|
+
output_cost=output_cost
|
|
1845
|
+
).to_dict())
|
|
1846
|
+
event_manager.write_result(content=content.to_dict(), metadata=metadata.to_dict())
|
|
1847
|
+
|
|
1628
1848
|
|
|
1629
1849
|
elif isinstance(agent_event, CompletionEvent):
|
|
1630
1850
|
# 在这里完成实际合并
|
|
@@ -1632,19 +1852,7 @@ class AgenticEdit:
|
|
|
1632
1852
|
self.apply_changes()
|
|
1633
1853
|
except Exception as e:
|
|
1634
1854
|
logger.exception(
|
|
1635
|
-
f"Error merging shadow changes to project: {e}")
|
|
1636
|
-
|
|
1637
|
-
# 发送累计的TokenUsageEvent数据
|
|
1638
|
-
get_event_manager(self.args.event_file).write_result(
|
|
1639
|
-
EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
|
|
1640
|
-
model_name=accumulated_token_usage["model_name"],
|
|
1641
|
-
elapsed_time=0.0,
|
|
1642
|
-
first_token_time=accumulated_token_usage["first_token_time"],
|
|
1643
|
-
input_tokens=accumulated_token_usage["input_tokens"],
|
|
1644
|
-
output_tokens=accumulated_token_usage["output_tokens"],
|
|
1645
|
-
input_cost=accumulated_token_usage["input_cost"],
|
|
1646
|
-
output_cost=accumulated_token_usage["output_cost"]
|
|
1647
|
-
).to_dict()), metadata=metadata.to_dict())
|
|
1855
|
+
f"Error merging shadow changes to project: {e}")
|
|
1648
1856
|
|
|
1649
1857
|
metadata.path = "/agent/edit/completion"
|
|
1650
1858
|
content = EventContentCreator.create_completion(
|
|
@@ -1656,20 +1864,22 @@ class AgenticEdit:
|
|
|
1656
1864
|
)
|
|
1657
1865
|
event_manager.write_completion(
|
|
1658
1866
|
content=content.to_dict(), metadata=metadata.to_dict())
|
|
1659
|
-
elif isinstance(agent_event,
|
|
1660
|
-
#
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
output_cost=accumulated_token_usage["output_cost"]
|
|
1671
|
-
).to_dict()), metadata=metadata.to_dict())
|
|
1867
|
+
elif isinstance(agent_event, WindowLengthChangeEvent):
|
|
1868
|
+
# 处理窗口长度变化事件
|
|
1869
|
+
metadata.path = "/agent/edit/window_length_change"
|
|
1870
|
+
content = EventContentCreator.create_result(
|
|
1871
|
+
content={
|
|
1872
|
+
"tokens_used": agent_event.tokens_used
|
|
1873
|
+
},
|
|
1874
|
+
metadata={}
|
|
1875
|
+
)
|
|
1876
|
+
event_manager.write_result(
|
|
1877
|
+
content=content.to_dict(), metadata=metadata.to_dict())
|
|
1672
1878
|
|
|
1879
|
+
# 记录日志
|
|
1880
|
+
logger.info(f"当前会话总 tokens: {agent_event.tokens_used}")
|
|
1881
|
+
|
|
1882
|
+
elif isinstance(agent_event, ErrorEvent):
|
|
1673
1883
|
metadata.path = "/agent/edit/error"
|
|
1674
1884
|
content = EventContentCreator.create_error(
|
|
1675
1885
|
error_code="AGENT_ERROR",
|
|
@@ -1699,18 +1909,7 @@ class AgenticEdit:
|
|
|
1699
1909
|
is_streaming=False,
|
|
1700
1910
|
stream_out_type="/agent/edit/error")
|
|
1701
1911
|
|
|
1702
|
-
# 发送累计的TokenUsageEvent数据(在错误情况下也需要发送)
|
|
1703
|
-
if accumulated_token_usage["input_tokens"] > 0:
|
|
1704
|
-
get_event_manager(self.args.event_file).write_result(
|
|
1705
|
-
EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
|
|
1706
|
-
model_name=accumulated_token_usage["model_name"],
|
|
1707
|
-
elapsed_time=0.0,
|
|
1708
|
-
first_token_time=accumulated_token_usage["first_token_time"],
|
|
1709
|
-
input_tokens=accumulated_token_usage["input_tokens"],
|
|
1710
|
-
output_tokens=accumulated_token_usage["output_tokens"],
|
|
1711
|
-
input_cost=accumulated_token_usage["input_cost"],
|
|
1712
|
-
output_cost=accumulated_token_usage["output_cost"]
|
|
1713
|
-
).to_dict()), metadata=metadata.to_dict())
|
|
1912
|
+
# 发送累计的TokenUsageEvent数据(在错误情况下也需要发送)
|
|
1714
1913
|
|
|
1715
1914
|
error_content = EventContentCreator.create_error(
|
|
1716
1915
|
error_code="AGENT_FATAL_ERROR",
|