auto-coder 0.1.400__py3-none-any.whl → 1.0.0__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.

Files changed (48) hide show
  1. {auto_coder-0.1.400.dist-info → auto_coder-1.0.0.dist-info}/METADATA +1 -1
  2. {auto_coder-0.1.400.dist-info → auto_coder-1.0.0.dist-info}/RECORD +48 -31
  3. autocoder/agent/agentic_filter.py +1 -1
  4. autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +1 -1
  5. autocoder/auto_coder_runner.py +120 -26
  6. autocoder/chat_auto_coder.py +81 -22
  7. autocoder/commands/auto_command.py +1 -1
  8. autocoder/common/__init__.py +2 -2
  9. autocoder/common/file_monitor/test_file_monitor.py +307 -0
  10. autocoder/common/git_utils.py +7 -2
  11. autocoder/common/pruner/__init__.py +0 -0
  12. autocoder/common/pruner/agentic_conversation_pruner.py +197 -0
  13. autocoder/common/pruner/context_pruner.py +574 -0
  14. autocoder/common/pruner/conversation_pruner.py +132 -0
  15. autocoder/common/pruner/test_agentic_conversation_pruner.py +342 -0
  16. autocoder/common/pruner/test_context_pruner.py +546 -0
  17. autocoder/common/tokens/__init__.py +15 -0
  18. autocoder/common/tokens/counter.py +20 -0
  19. autocoder/common/v2/agent/agentic_edit.py +372 -538
  20. autocoder/common/v2/agent/agentic_edit_tools/__init__.py +8 -1
  21. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_read_tool_resolver.py +40 -0
  22. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +43 -0
  23. autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +8 -0
  24. autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +1 -1
  25. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +1 -1
  26. autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +33 -88
  27. autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +8 -8
  28. autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +118 -0
  29. autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +324 -0
  30. autocoder/common/v2/agent/agentic_edit_types.py +46 -4
  31. autocoder/common/v2/agent/runner/__init__.py +31 -0
  32. autocoder/common/v2/agent/runner/base_runner.py +106 -0
  33. autocoder/common/v2/agent/runner/event_runner.py +216 -0
  34. autocoder/common/v2/agent/runner/sdk_runner.py +40 -0
  35. autocoder/common/v2/agent/runner/terminal_runner.py +283 -0
  36. autocoder/common/v2/agent/runner/tool_display.py +191 -0
  37. autocoder/index/entry.py +1 -1
  38. autocoder/plugins/token_helper_plugin.py +107 -7
  39. autocoder/run_context.py +9 -0
  40. autocoder/sdk/__init__.py +114 -81
  41. autocoder/sdk/cli/main.py +5 -0
  42. autocoder/sdk/core/auto_coder_core.py +0 -158
  43. autocoder/sdk/core/bridge.py +2 -4
  44. autocoder/version.py +1 -1
  45. {auto_coder-0.1.400.dist-info → auto_coder-1.0.0.dist-info}/WHEEL +0 -0
  46. {auto_coder-0.1.400.dist-info → auto_coder-1.0.0.dist-info}/entry_points.txt +0 -0
  47. {auto_coder-0.1.400.dist-info → auto_coder-1.0.0.dist-info}/licenses/LICENSE +0 -0
  48. {auto_coder-0.1.400.dist-info → auto_coder-1.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,216 @@
1
+ """
2
+ EventRunner 提供将代理事件转换为标准事件系统格式的功能。
3
+
4
+ 这个模块负责将内部代理事件转换为标准事件系统格式,并通过事件管理器写入。
5
+ 它支持流式处理事件和结果事件的写入。
6
+ """
7
+
8
+ import logging
9
+ from typing import Any, Dict, Optional
10
+
11
+ from autocoder.common.v2.agent.agentic_edit_types import (
12
+ AgenticEditRequest, AgentEvent, CompletionEvent,
13
+ LLMOutputEvent, LLMThinkingEvent, ToolCallEvent,
14
+ ToolResultEvent, TokenUsageEvent, ErrorEvent,PlanModeRespondEvent,
15
+ WindowLengthChangeEvent,ConversationIdEvent
16
+ )
17
+ from autocoder.events.event_manager_singleton import get_event_manager
18
+ from autocoder.events.event_types import EventMetadata
19
+ from autocoder.events import event_content as EventContentCreator
20
+ from byzerllm.utils.types import SingleOutputMeta
21
+ from .base_runner import BaseRunner
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ class EventRunner(BaseRunner):
26
+ """
27
+ 将代理事件转换为标准事件系统格式并写入事件管理器。
28
+
29
+ 这个运行器负责将内部代理事件转换为标准事件系统格式,
30
+ 并通过事件管理器写入,支持流式处理和结果事件。
31
+ """
32
+
33
+ def run(self, request: AgenticEditRequest) -> None:
34
+ """
35
+ Runs the agentic edit process, converting internal events to the
36
+ standard event system format and writing them using the event manager.
37
+ """
38
+ event_manager = get_event_manager(self.args.event_file)
39
+ self.apply_pre_changes()
40
+
41
+ try:
42
+ event_stream = self.analyze(request)
43
+ for agent_event in event_stream:
44
+ content = None
45
+ metadata = EventMetadata(
46
+ action_file=self.args.file,
47
+ is_streaming=False,
48
+ stream_out_type="/agent/edit")
49
+
50
+ if isinstance(agent_event, LLMThinkingEvent):
51
+ content = EventContentCreator.create_stream_thinking(
52
+ content=agent_event.text)
53
+ metadata.is_streaming = True
54
+ metadata.path = "/agent/edit/thinking"
55
+ event_manager.write_stream(
56
+ content=content.to_dict(), metadata=metadata.to_dict())
57
+ elif isinstance(agent_event, LLMOutputEvent):
58
+ content = EventContentCreator.create_stream_content(
59
+ content=agent_event.text)
60
+ metadata.is_streaming = True
61
+ metadata.path = "/agent/edit/output"
62
+ event_manager.write_stream(content=content.to_dict(),
63
+ metadata=metadata.to_dict())
64
+
65
+ elif isinstance(agent_event, ToolCallEvent):
66
+ tool_name = type(agent_event.tool).__name__
67
+ metadata.path = "/agent/edit/tool/call"
68
+ content = EventContentCreator.create_result(
69
+ content={
70
+ "tool_name": tool_name,
71
+ **agent_event.tool.model_dump()
72
+ },
73
+ metadata={}
74
+ )
75
+ event_manager.write_result(
76
+ content=content.to_dict(), metadata=metadata.to_dict())
77
+ elif isinstance(agent_event, ToolResultEvent):
78
+ metadata.path = "/agent/edit/tool/result"
79
+ content = EventContentCreator.create_result(
80
+ content={
81
+ "tool_name": agent_event.tool_name,
82
+ **agent_event.result.model_dump()
83
+ },
84
+ metadata={}
85
+ )
86
+ event_manager.write_result(
87
+ content=content.to_dict(), metadata=metadata.to_dict())
88
+
89
+ elif isinstance(agent_event, PlanModeRespondEvent):
90
+ metadata.path = "/agent/edit/plan_mode_respond"
91
+ content = EventContentCreator.create_markdown_result(
92
+ content=agent_event.completion.response,
93
+ metadata={}
94
+ )
95
+ event_manager.write_result(
96
+ content=content.to_dict(), metadata=metadata.to_dict())
97
+
98
+ elif isinstance(agent_event, TokenUsageEvent):
99
+ last_meta: SingleOutputMeta = agent_event.usage
100
+ # Get model info for pricing
101
+ from autocoder.utils import llms as llm_utils
102
+ model_name = ",".join(llm_utils.get_llm_names(self.llm))
103
+ model_info = llm_utils.get_model_info(
104
+ model_name, self.args.product_mode) or {}
105
+ input_price = model_info.get(
106
+ "input_price", 0.0) if model_info else 0.0
107
+ output_price = model_info.get(
108
+ "output_price", 0.0) if model_info else 0.0
109
+
110
+ # Calculate costs
111
+ input_cost = (last_meta.input_tokens_count *
112
+ input_price) / 1000000 # Convert to millions
113
+ # Convert to millions
114
+ output_cost = (
115
+ last_meta.generated_tokens_count * output_price) / 1000000
116
+
117
+ # 添加日志记录
118
+ 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}")
119
+
120
+ # 直接将每次的 TokenUsageEvent 写入到事件中
121
+ metadata.path = "/agent/edit/token_usage"
122
+ content = EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
123
+ model_name=model_name,
124
+ elapsed_time=0.0,
125
+ first_token_time=last_meta.first_token_time,
126
+ input_tokens=last_meta.input_tokens_count,
127
+ output_tokens=last_meta.generated_tokens_count,
128
+ input_cost=input_cost,
129
+ output_cost=output_cost
130
+ ).to_dict())
131
+ event_manager.write_result(content=content.to_dict(), metadata=metadata.to_dict())
132
+
133
+ elif isinstance(agent_event, CompletionEvent):
134
+ # 在这里完成实际合并
135
+ try:
136
+ self.apply_changes()
137
+ except Exception as e:
138
+ logger.exception(
139
+ f"Error merging shadow changes to project: {e}")
140
+
141
+ metadata.path = "/agent/edit/completion"
142
+ content = EventContentCreator.create_completion(
143
+ success_code="AGENT_COMPLETE",
144
+ success_message="Agent attempted task completion.",
145
+ result={
146
+ "response": agent_event.completion.result
147
+ }
148
+ )
149
+ event_manager.write_completion(
150
+ content=content.to_dict(), metadata=metadata.to_dict())
151
+ elif isinstance(agent_event, WindowLengthChangeEvent):
152
+ # 处理窗口长度变化事件
153
+ metadata.path = "/agent/edit/window_length_change"
154
+ content = EventContentCreator.create_result(
155
+ content={
156
+ "tokens_used": agent_event.tokens_used
157
+ },
158
+ metadata={}
159
+ )
160
+ event_manager.write_result(
161
+ content=content.to_dict(), metadata=metadata.to_dict())
162
+
163
+ # 记录日志
164
+ logger.info(f"当前会话总 tokens: {agent_event.tokens_used}")
165
+
166
+ elif isinstance(agent_event, ConversationIdEvent):
167
+ metadata.path = "/agent/edit/conversation_id"
168
+ content = EventContentCreator.create_result(
169
+ content={
170
+ "conversation_id": agent_event.conversation_id
171
+ },
172
+ metadata={}
173
+ )
174
+ event_manager.write_result(content=content.to_dict(), metadata=metadata.to_dict())
175
+
176
+ elif isinstance(agent_event, ErrorEvent):
177
+ metadata.path = "/agent/edit/error"
178
+ content = EventContentCreator.create_error(
179
+ error_code="AGENT_ERROR",
180
+ error_message=agent_event.message,
181
+ details={"agent_event_type": "ErrorEvent"}
182
+ )
183
+ event_manager.write_error(
184
+ content=content.to_dict(), metadata=metadata.to_dict())
185
+ else:
186
+ metadata.path = "/agent/edit/error"
187
+ logger.warning(
188
+ f"Unhandled agent event type: {type(agent_event)}")
189
+ content = EventContentCreator.create_error(
190
+ error_code="AGENT_ERROR",
191
+ error_message=f"Unhandled agent event type: {type(agent_event)}",
192
+ details={"agent_event_type": type(
193
+ agent_event).__name__}
194
+ )
195
+ event_manager.write_error(
196
+ content=content.to_dict(), metadata=metadata.to_dict())
197
+
198
+ except Exception as e:
199
+ logger.exception(
200
+ "An unexpected error occurred during agent execution:")
201
+ metadata = EventMetadata(
202
+ action_file=self.args.file,
203
+ is_streaming=False,
204
+ stream_out_type="/agent/edit/error")
205
+
206
+ # 发送累计的TokenUsageEvent数据(在错误情况下也需要发送)
207
+
208
+ error_content = EventContentCreator.create_error(
209
+ error_code="AGENT_FATAL_ERROR",
210
+ error_message=f"An unexpected error occurred: {str(e)}",
211
+ details={"exception_type": type(e).__name__}
212
+ )
213
+ event_manager.write_error(
214
+ content=error_content.to_dict(), metadata=metadata.to_dict())
215
+ # Re-raise the exception if needed, or handle appropriately
216
+ raise e
@@ -0,0 +1,40 @@
1
+ """
2
+ SdkRunner 提供生成器接口,适用于SDK环境下的代理运行。
3
+
4
+ 这个模块提供了一个简单的生成器接口,允许外部代码迭代处理代理事件。
5
+ 它是三种运行模式中最轻量级的一种,适合集成到其他应用程序中。
6
+ """
7
+
8
+ import logging
9
+ from typing import Generator, Any
10
+
11
+ from autocoder.common.v2.agent.agentic_edit_types import (
12
+ AgenticEditRequest, AgentEvent, CompletionEvent
13
+ )
14
+ from .base_runner import BaseRunner
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ class SdkRunner(BaseRunner):
19
+ """
20
+ 提供生成器接口的代理运行器,适用于SDK环境。
21
+
22
+ 这个运行器返回一个事件生成器,允许外部代码迭代处理代理事件。
23
+ 它是三种运行模式中最轻量级的一种,适合集成到其他应用程序中。
24
+ """
25
+
26
+ def run(self, request: AgenticEditRequest) -> Generator[AgentEvent, None, None]:
27
+ """
28
+ Runs the agentic edit process and yields events for external processing.
29
+ """
30
+ try:
31
+ event_stream = self.analyze(request)
32
+ for agent_event in event_stream:
33
+ if isinstance(agent_event, CompletionEvent):
34
+ self.apply_changes()
35
+ yield agent_event
36
+
37
+ except Exception as e:
38
+ logger.exception(
39
+ "An unexpected error occurred during agent execution: {e}")
40
+ raise e
@@ -0,0 +1,283 @@
1
+ """
2
+ TerminalRunner 提供在终端环境中运行代理的功能,支持格式化输出和交互式显示。
3
+
4
+ 这个模块使用 Rich 库来提供格式化的终端输出,包括颜色、样式和布局。
5
+ 它处理各种代理事件并以用户友好的方式在终端中显示。
6
+ """
7
+
8
+ import os
9
+ import json
10
+ import time
11
+ import logging
12
+ from typing import Any, Dict, Optional, List
13
+
14
+ from rich.console import Console
15
+ from rich.panel import Panel
16
+ from rich.markdown import Markdown
17
+ from rich.syntax import Syntax
18
+
19
+ from autocoder.common.auto_coder_lang import get_message
20
+ from autocoder.utils import llms as llm_utils
21
+ from autocoder.common.v2.agent.agentic_edit_types import (
22
+ AgenticEditRequest, AgentEvent, CompletionEvent,
23
+ LLMOutputEvent, LLMThinkingEvent, ToolCallEvent,
24
+ ToolResultEvent, TokenUsageEvent, ErrorEvent,
25
+ WindowLengthChangeEvent, ConversationIdEvent,
26
+ PlanModeRespondEvent, SingleOutputMeta, AttemptCompletionTool
27
+ )
28
+ from .tool_display import get_tool_display_message
29
+ from .base_runner import BaseRunner
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+ class TerminalRunner(BaseRunner):
34
+ """
35
+ 在终端环境中运行代理,提供格式化输出和交互式显示。
36
+
37
+ 这个运行器使用 Rich 库来格式化终端输出,处理各种代理事件,
38
+ 并以用户友好的方式在终端中显示。
39
+ """
40
+
41
+ def run(self, request: AgenticEditRequest) -> None:
42
+ """
43
+ Runs the agentic edit process based on the request and displays
44
+ the interaction streamingly in the terminal using Rich.
45
+ """
46
+ import json
47
+ console = Console()
48
+ project_name = os.path.basename(os.path.abspath(self.args.source_dir))
49
+
50
+ if self.conversation_config.action == "list":
51
+ conversations = self.agent.conversation_manager.list_conversations()
52
+ # 只保留 conversation_id 和 name 字段
53
+ filtered_conversations = []
54
+ for conv in conversations:
55
+ filtered_conv = {
56
+ "conversation_id": conv.get("conversation_id"),
57
+ "name": conv.get("name")
58
+ }
59
+ filtered_conversations.append(filtered_conv)
60
+
61
+ # 格式化 JSON 输出,使用 JSON 格式渲染而不是 Markdown
62
+ json_str = json.dumps(filtered_conversations, ensure_ascii=False, indent=4)
63
+ console.print(Panel(json_str,
64
+ title="🏁 Task Completion", border_style="green", title_align="left"))
65
+ return
66
+
67
+
68
+ if self.conversation_config.action == "new" and not request.user_input.strip():
69
+ console.print(Panel(Markdown(f"New conversation created: {self.agent.conversation_manager.get_current_conversation_id()}"),
70
+ title="🏁 Task Completion", border_style="green", title_align="left"))
71
+ return
72
+
73
+ console.rule(f"[bold cyan]Starting Agentic Edit: {project_name}[/]")
74
+ console.print(Panel(
75
+ f"[bold]{get_message('/agent/edit/user_query')}:[/bold]\n{request.user_input}", title=get_message("/agent/edit/objective"), border_style="blue"))
76
+
77
+ # 用于累计TokenUsageEvent数据
78
+ accumulated_token_usage = {
79
+ "model_name": "",
80
+ "input_tokens": 0,
81
+ "output_tokens": 0,
82
+ "input_cost": 0.0,
83
+ "output_cost": 0.0
84
+ }
85
+
86
+ try:
87
+ self.apply_pre_changes()
88
+ event_stream = self.analyze(request)
89
+ for event in event_stream:
90
+ if isinstance(event, ConversationIdEvent):
91
+ console.print(f"[dim]Conversation ID: {event.conversation_id}[/dim]")
92
+ continue
93
+ if isinstance(event, TokenUsageEvent):
94
+ last_meta: SingleOutputMeta = event.usage
95
+ # Get model info for pricing
96
+ model_name = ",".join(llm_utils.get_llm_names(self.llm))
97
+ model_info = llm_utils.get_model_info(
98
+ model_name, self.args.product_mode) or {}
99
+ input_price = model_info.get(
100
+ "input_price", 0.0) if model_info else 0.0
101
+ output_price = model_info.get(
102
+ "output_price", 0.0) if model_info else 0.0
103
+
104
+ # Calculate costs
105
+ input_cost = (last_meta.input_tokens_count *
106
+ input_price) / 1000000 # Convert to millions
107
+ # Convert to millions
108
+ output_cost = (
109
+ last_meta.generated_tokens_count * output_price) / 1000000
110
+
111
+ # 添加日志记录
112
+ logger.info(f"Token Usage: 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}")
113
+
114
+ # 累计token使用情况
115
+ accumulated_token_usage["model_name"] = model_name
116
+ accumulated_token_usage["input_tokens"] += last_meta.input_tokens_count
117
+ accumulated_token_usage["output_tokens"] += last_meta.generated_tokens_count
118
+ accumulated_token_usage["input_cost"] += input_cost
119
+ accumulated_token_usage["output_cost"] += output_cost
120
+
121
+ elif isinstance(event, WindowLengthChangeEvent):
122
+ # 显示当前会话的token数量
123
+ logger.info(f"当前会话总 tokens: {event.tokens_used}")
124
+ console.print(f"[dim]当前会话总 tokens: {event.tokens_used}[/dim]")
125
+
126
+ elif isinstance(event, LLMThinkingEvent):
127
+ # Render thinking within a less prominent style, maybe grey?
128
+ console.print(f"[grey50]{event.text}[/grey50]", end="")
129
+ elif isinstance(event, LLMOutputEvent):
130
+ # Print regular LLM output, potentially as markdown if needed later
131
+ console.print(event.text, end="")
132
+ elif isinstance(event, ToolCallEvent):
133
+ # Skip displaying AttemptCompletionTool's tool call
134
+ if isinstance(event.tool, AttemptCompletionTool):
135
+ continue # Do not display AttemptCompletionTool tool call
136
+
137
+ tool_name = type(event.tool).__name__
138
+ # Use the new internationalized display function
139
+ display_content = get_tool_display_message(event.tool)
140
+ console.print(Panel(
141
+ display_content, title=f"🔧 Action: {tool_name}", border_style="blue", title_align="left"))
142
+
143
+ elif isinstance(event, ToolResultEvent):
144
+ # Skip displaying AttemptCompletionTool's result
145
+ if event.tool_name == "AttemptCompletionTool":
146
+ continue # Do not display AttemptCompletionTool result
147
+
148
+ if event.tool_name == "PlanModeRespondTool":
149
+ continue
150
+
151
+ result = event.result
152
+ title = f"✅ Tool Result: {event.tool_name}" if result.success else f"❌ Tool Result: {event.tool_name}"
153
+ border_style = "green" if result.success else "red"
154
+ base_content = f"[bold]Status:[/bold] {'Success' if result.success else 'Failure'}\n"
155
+ base_content += f"[bold]Message:[/bold] {result.message}\n"
156
+
157
+ def _format_content(content):
158
+ if len(content) > 200:
159
+ return f"{content[:100]}\n...\n{content[-100:]}"
160
+ else:
161
+ return content
162
+
163
+ # Prepare panel for base info first
164
+ panel_content = [base_content]
165
+ syntax_content = None
166
+
167
+ if result.content is not None:
168
+ content_str = ""
169
+ try:
170
+ if isinstance(result.content, (dict, list)):
171
+ import json
172
+ content_str = json.dumps(
173
+ result.content, indent=2, ensure_ascii=False)
174
+ syntax_content = Syntax(
175
+ content_str, "json", theme="default", line_numbers=False)
176
+ elif isinstance(result.content, str) and ('\n' in result.content or result.content.strip().startswith('<')):
177
+ # Heuristic for code or XML/HTML
178
+ lexer = "python" # Default guess
179
+ if event.tool_name == "ReadFileTool" and isinstance(event.result.message, str):
180
+ # Try to guess lexer from file extension in message
181
+ if ".py" in event.result.message:
182
+ lexer = "python"
183
+ elif ".js" in event.result.message:
184
+ lexer = "javascript"
185
+ elif ".ts" in event.result.message:
186
+ lexer = "typescript"
187
+ elif ".html" in event.result.message:
188
+ lexer = "html"
189
+ elif ".css" in event.result.message:
190
+ lexer = "css"
191
+ elif ".json" in event.result.message:
192
+ lexer = "json"
193
+ elif ".xml" in event.result.message:
194
+ lexer = "xml"
195
+ elif ".md" in event.result.message:
196
+ lexer = "markdown"
197
+ else:
198
+ lexer = "text" # Fallback lexer
199
+ elif event.tool_name == "ExecuteCommandTool":
200
+ lexer = "shell"
201
+ else:
202
+ lexer = "text"
203
+
204
+ syntax_content = Syntax(
205
+ _format_content(result.content), lexer, theme="default", line_numbers=True)
206
+ else:
207
+ content_str = str(result.content)
208
+ # Append simple string content directly
209
+ panel_content.append(
210
+ _format_content(content_str))
211
+ except Exception as e:
212
+ logger.warning(
213
+ f"Error formatting tool result content: {e}")
214
+ panel_content.append(
215
+ # Fallback
216
+ _format_content(str(result.content)))
217
+
218
+ # Print the base info panel
219
+ console.print(Panel("\n".join(
220
+ panel_content), title=title, border_style=border_style, title_align="left"))
221
+ # Print syntax highlighted content separately if it exists
222
+ if syntax_content:
223
+ console.print(syntax_content)
224
+
225
+ elif isinstance(event, PlanModeRespondEvent):
226
+ console.print(Panel(Markdown(event.completion.response),
227
+ title="🏁 Task Completion", border_style="green", title_align="left"))
228
+
229
+ elif isinstance(event, CompletionEvent):
230
+ # 在这里完成实际合并
231
+ try:
232
+ self.apply_changes()
233
+ except Exception as e:
234
+ logger.exception(
235
+ f"Error merging shadow changes to project: {e}")
236
+
237
+ console.print(Panel(Markdown(event.completion.result),
238
+ title="🏁 Task Completion", border_style="green", title_align="left"))
239
+ if event.completion.command:
240
+ console.print(
241
+ f"[dim]Suggested command:[/dim] [bold cyan]{event.completion.command}[/]")
242
+ elif isinstance(event, ErrorEvent):
243
+ console.print(Panel(
244
+ f"[bold red]Error:[/bold red] {event.message}", title="🔥 Error", border_style="red", title_align="left"))
245
+
246
+ time.sleep(0.1) # Small delay for better visual flow
247
+
248
+ # 在处理完所有事件后打印累计的token使用情况
249
+ if accumulated_token_usage["input_tokens"] > 0:
250
+ self.printer.print_in_terminal(
251
+ "code_generation_complete",
252
+ duration=0.0,
253
+ input_tokens=accumulated_token_usage["input_tokens"],
254
+ output_tokens=accumulated_token_usage["output_tokens"],
255
+ input_cost=accumulated_token_usage["input_cost"],
256
+ output_cost=accumulated_token_usage["output_cost"],
257
+ speed=0.0,
258
+ model_names=accumulated_token_usage["model_name"],
259
+ sampling_count=1
260
+ )
261
+
262
+ except Exception as e:
263
+ # 在处理异常时也打印累计的token使用情况
264
+ if accumulated_token_usage["input_tokens"] > 0:
265
+ self.printer.print_in_terminal(
266
+ "code_generation_complete",
267
+ duration=0.0,
268
+ input_tokens=accumulated_token_usage["input_tokens"],
269
+ output_tokens=accumulated_token_usage["output_tokens"],
270
+ input_cost=accumulated_token_usage["input_cost"],
271
+ output_cost=accumulated_token_usage["output_cost"],
272
+ speed=0.0,
273
+ model_names=accumulated_token_usage["model_name"],
274
+ sampling_count=1
275
+ )
276
+
277
+ logger.exception(
278
+ "An unexpected error occurred during agent execution:")
279
+ console.print(Panel(
280
+ f"[bold red]FATAL ERROR:[/bold red]\n{str(e)}", title="🔥 System Error", border_style="red"))
281
+ raise e
282
+ finally:
283
+ console.rule("[bold cyan]Agentic Edit Finished[/]")