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.
- {auto_coder-0.1.400.dist-info → auto_coder-1.0.0.dist-info}/METADATA +1 -1
- {auto_coder-0.1.400.dist-info → auto_coder-1.0.0.dist-info}/RECORD +48 -31
- autocoder/agent/agentic_filter.py +1 -1
- autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +1 -1
- autocoder/auto_coder_runner.py +120 -26
- autocoder/chat_auto_coder.py +81 -22
- autocoder/commands/auto_command.py +1 -1
- autocoder/common/__init__.py +2 -2
- autocoder/common/file_monitor/test_file_monitor.py +307 -0
- autocoder/common/git_utils.py +7 -2
- autocoder/common/pruner/__init__.py +0 -0
- autocoder/common/pruner/agentic_conversation_pruner.py +197 -0
- autocoder/common/pruner/context_pruner.py +574 -0
- autocoder/common/pruner/conversation_pruner.py +132 -0
- autocoder/common/pruner/test_agentic_conversation_pruner.py +342 -0
- autocoder/common/pruner/test_context_pruner.py +546 -0
- autocoder/common/tokens/__init__.py +15 -0
- autocoder/common/tokens/counter.py +20 -0
- autocoder/common/v2/agent/agentic_edit.py +372 -538
- autocoder/common/v2/agent/agentic_edit_tools/__init__.py +8 -1
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_read_tool_resolver.py +40 -0
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +43 -0
- autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +8 -0
- autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +1 -1
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +1 -1
- autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +33 -88
- autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +8 -8
- autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +118 -0
- autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +324 -0
- autocoder/common/v2/agent/agentic_edit_types.py +46 -4
- autocoder/common/v2/agent/runner/__init__.py +31 -0
- autocoder/common/v2/agent/runner/base_runner.py +106 -0
- autocoder/common/v2/agent/runner/event_runner.py +216 -0
- autocoder/common/v2/agent/runner/sdk_runner.py +40 -0
- autocoder/common/v2/agent/runner/terminal_runner.py +283 -0
- autocoder/common/v2/agent/runner/tool_display.py +191 -0
- autocoder/index/entry.py +1 -1
- autocoder/plugins/token_helper_plugin.py +107 -7
- autocoder/run_context.py +9 -0
- autocoder/sdk/__init__.py +114 -81
- autocoder/sdk/cli/main.py +5 -0
- autocoder/sdk/core/auto_coder_core.py +0 -158
- autocoder/sdk/core/bridge.py +2 -4
- autocoder/version.py +1 -1
- {auto_coder-0.1.400.dist-info → auto_coder-1.0.0.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.400.dist-info → auto_coder-1.0.0.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.400.dist-info → auto_coder-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {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[/]")
|