auto-coder 0.1.398__py3-none-any.whl → 0.1.400__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/METADATA +396 -0
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/RECORD +82 -29
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/WHEEL +1 -1
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/entry_points.txt +2 -0
- autocoder/agent/base_agentic/base_agent.py +2 -2
- autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +1 -1
- autocoder/agent/entry_command_agent/__init__.py +29 -0
- autocoder/agent/entry_command_agent/auto_tool.py +61 -0
- autocoder/agent/entry_command_agent/chat.py +475 -0
- autocoder/agent/entry_command_agent/designer.py +53 -0
- autocoder/agent/entry_command_agent/generate_command.py +50 -0
- autocoder/agent/entry_command_agent/project_reader.py +58 -0
- autocoder/agent/entry_command_agent/voice2text.py +71 -0
- autocoder/auto_coder.py +23 -548
- autocoder/auto_coder_runner.py +511 -8
- autocoder/chat/rules_command.py +1 -1
- autocoder/chat_auto_coder.py +6 -1
- autocoder/common/ac_style_command_parser/__init__.py +15 -0
- autocoder/common/ac_style_command_parser/example.py +7 -0
- autocoder/{command_parser.py → common/ac_style_command_parser/parser.py} +28 -45
- autocoder/common/ac_style_command_parser/test_parser.py +516 -0
- autocoder/common/auto_coder_lang.py +78 -0
- autocoder/common/command_completer_v2.py +1 -1
- autocoder/common/command_file_manager/examples.py +22 -8
- autocoder/common/command_file_manager/manager.py +37 -6
- autocoder/common/conversations/get_conversation_manager.py +143 -0
- autocoder/common/conversations/manager.py +122 -11
- autocoder/common/conversations/storage/index_manager.py +89 -0
- autocoder/common/pull_requests/__init__.py +256 -0
- autocoder/common/pull_requests/base_provider.py +191 -0
- autocoder/common/pull_requests/config.py +66 -0
- autocoder/common/pull_requests/example.py +1 -0
- autocoder/common/pull_requests/exceptions.py +46 -0
- autocoder/common/pull_requests/manager.py +201 -0
- autocoder/common/pull_requests/models.py +164 -0
- autocoder/common/pull_requests/providers/__init__.py +23 -0
- autocoder/common/pull_requests/providers/gitcode_provider.py +19 -0
- autocoder/common/pull_requests/providers/gitee_provider.py +20 -0
- autocoder/common/pull_requests/providers/github_provider.py +214 -0
- autocoder/common/pull_requests/providers/gitlab_provider.py +29 -0
- autocoder/common/pull_requests/test_module.py +1 -0
- autocoder/common/pull_requests/utils.py +344 -0
- autocoder/common/tokens/__init__.py +62 -0
- autocoder/common/tokens/counter.py +211 -0
- autocoder/common/tokens/file_detector.py +105 -0
- autocoder/common/tokens/filters.py +111 -0
- autocoder/common/tokens/models.py +28 -0
- autocoder/common/v2/agent/agentic_edit.py +312 -85
- autocoder/common/v2/agent/agentic_edit_types.py +11 -0
- autocoder/common/v2/code_auto_generate_editblock.py +10 -2
- autocoder/dispacher/__init__.py +10 -0
- autocoder/rags.py +0 -27
- autocoder/run_context.py +1 -0
- autocoder/sdk/__init__.py +188 -0
- autocoder/sdk/cli/__init__.py +15 -0
- autocoder/sdk/cli/__main__.py +26 -0
- autocoder/sdk/cli/completion_wrapper.py +38 -0
- autocoder/sdk/cli/formatters.py +211 -0
- autocoder/sdk/cli/handlers.py +175 -0
- autocoder/sdk/cli/install_completion.py +301 -0
- autocoder/sdk/cli/main.py +286 -0
- autocoder/sdk/cli/options.py +73 -0
- autocoder/sdk/constants.py +102 -0
- autocoder/sdk/core/__init__.py +20 -0
- autocoder/sdk/core/auto_coder_core.py +880 -0
- autocoder/sdk/core/bridge.py +500 -0
- autocoder/sdk/example.py +0 -0
- autocoder/sdk/exceptions.py +72 -0
- autocoder/sdk/models/__init__.py +19 -0
- autocoder/sdk/models/messages.py +209 -0
- autocoder/sdk/models/options.py +196 -0
- autocoder/sdk/models/responses.py +311 -0
- autocoder/sdk/session/__init__.py +32 -0
- autocoder/sdk/session/session.py +106 -0
- autocoder/sdk/session/session_manager.py +56 -0
- autocoder/sdk/utils/__init__.py +24 -0
- autocoder/sdk/utils/formatters.py +216 -0
- autocoder/sdk/utils/io_utils.py +302 -0
- autocoder/sdk/utils/validators.py +287 -0
- autocoder/version.py +2 -1
- auto_coder-0.1.398.dist-info/METADATA +0 -111
- autocoder/common/conversations/compatibility.py +0 -303
- autocoder/common/conversations/conversation_manager.py +0 -502
- autocoder/common/conversations/example.py +0 -152
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info/licenses}/LICENSE +0 -0
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,880 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auto-Coder SDK 核心封装类
|
|
3
|
+
|
|
4
|
+
提供统一的查询接口,处理同步和异步调用。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import AsyncIterator, Optional, Dict, Any, Iterator
|
|
8
|
+
import asyncio
|
|
9
|
+
import os
|
|
10
|
+
import time
|
|
11
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
12
|
+
|
|
13
|
+
# Rich 渲染相关导入
|
|
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
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
19
|
+
|
|
20
|
+
from ..models.options import AutoCodeOptions
|
|
21
|
+
from ..models.messages import Message
|
|
22
|
+
from ..models.responses import StreamEvent, CodeModificationResult
|
|
23
|
+
from ..exceptions import BridgeError
|
|
24
|
+
from .bridge import AutoCoderBridge
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AutoCoderCore:
|
|
28
|
+
"""AutoCoder核心封装类"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, options: AutoCodeOptions):
|
|
31
|
+
"""
|
|
32
|
+
初始化AutoCoderCore
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
options: 配置选项
|
|
36
|
+
"""
|
|
37
|
+
self.options = options
|
|
38
|
+
cwd_str = str(options.cwd) if options.cwd is not None else os.getcwd()
|
|
39
|
+
self.bridge = AutoCoderBridge(cwd_str,options)
|
|
40
|
+
self._executor = ThreadPoolExecutor(max_workers=1)
|
|
41
|
+
self._console = Console()
|
|
42
|
+
|
|
43
|
+
# 用于累计TokenUsageEvent数据
|
|
44
|
+
self._accumulated_token_usage = {
|
|
45
|
+
"model_name": "",
|
|
46
|
+
"input_tokens": 0,
|
|
47
|
+
"output_tokens": 0,
|
|
48
|
+
"input_cost": 0.0,
|
|
49
|
+
"output_cost": 0.0
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
def _render_stream_event(self, event: StreamEvent, show_terminal: bool = True) -> None:
|
|
53
|
+
"""
|
|
54
|
+
渲染流式事件到终端
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
event: 流式事件
|
|
58
|
+
show_terminal: 是否显示到终端
|
|
59
|
+
"""
|
|
60
|
+
if not show_terminal:
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
# 处理新的事件类型(动态检查以避免导入依赖)
|
|
65
|
+
event_class_name = type(event).__name__
|
|
66
|
+
|
|
67
|
+
# 处理 TokenUsageEvent 和 WindowLengthChangeEvent
|
|
68
|
+
if 'TokenUsageEvent' in event_class_name:
|
|
69
|
+
usage = getattr(event, 'usage', None)
|
|
70
|
+
if usage:
|
|
71
|
+
self._process_token_usage_event(usage)
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
elif 'WindowLengthChangeEvent' in event_class_name:
|
|
75
|
+
tokens_used = getattr(event, 'tokens_used', 0)
|
|
76
|
+
if tokens_used > 0:
|
|
77
|
+
self._console.print(f"[dim]当前会话总 tokens: {tokens_used}[/dim]")
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
elif 'LLMThinkingEvent' in event_class_name:
|
|
81
|
+
text = getattr(event, 'text', '')
|
|
82
|
+
if text.strip():
|
|
83
|
+
self._console.print(f"[grey50]{text}[/grey50]", end="")
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
elif 'LLMOutputEvent' in event_class_name:
|
|
87
|
+
text = getattr(event, 'text', '')
|
|
88
|
+
if text.strip():
|
|
89
|
+
self._console.print(text, end="")
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
elif 'ToolCallEvent' in event_class_name:
|
|
93
|
+
# 跳过 AttemptCompletionTool 的工具调用显示
|
|
94
|
+
tool = getattr(event, 'tool', None)
|
|
95
|
+
if tool and 'AttemptCompletionTool' in type(tool).__name__:
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
tool_name = type(tool).__name__ if tool else "Unknown Tool"
|
|
99
|
+
try:
|
|
100
|
+
# 尝试使用 get_tool_display_message 函数
|
|
101
|
+
from autocoder.common.v2.agent.agentic_edit_types import get_tool_display_message
|
|
102
|
+
display_content = get_tool_display_message(tool)
|
|
103
|
+
except:
|
|
104
|
+
# 如果导入失败,使用简单的显示
|
|
105
|
+
display_content = f"Tool: {tool_name}"
|
|
106
|
+
if hasattr(tool, '__dict__'):
|
|
107
|
+
for key, value in tool.__dict__.items():
|
|
108
|
+
if not key.startswith('_'):
|
|
109
|
+
display_content += f"\n{key}: {value}"
|
|
110
|
+
|
|
111
|
+
self._console.print(Panel(
|
|
112
|
+
display_content,
|
|
113
|
+
title=f"🛠️ Action: {tool_name}",
|
|
114
|
+
border_style="blue",
|
|
115
|
+
title_align="left"
|
|
116
|
+
))
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
elif 'ToolResultEvent' in event_class_name:
|
|
120
|
+
# 跳过 AttemptCompletionTool 和 PlanModeRespondTool 的结果显示
|
|
121
|
+
tool_name = getattr(event, 'tool_name', 'Unknown')
|
|
122
|
+
if tool_name in ["AttemptCompletionTool", "PlanModeRespondTool"]:
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
result = getattr(event, 'result', None)
|
|
126
|
+
if result:
|
|
127
|
+
success = getattr(result, 'success', True)
|
|
128
|
+
message = getattr(result, 'message', '')
|
|
129
|
+
content = getattr(result, 'content', None)
|
|
130
|
+
|
|
131
|
+
title = f"✅ Tool Result: {tool_name}" if success else f"❌ Tool Result: {tool_name}"
|
|
132
|
+
border_style = "green" if success else "red"
|
|
133
|
+
|
|
134
|
+
base_content = f"[bold]Status:[/bold] {'Success' if success else 'Failure'}\n"
|
|
135
|
+
base_content += f"[bold]Message:[/bold] {message}\n"
|
|
136
|
+
|
|
137
|
+
# 处理内容显示
|
|
138
|
+
if content is not None:
|
|
139
|
+
formatted_content = self._format_tool_result_content(content, tool_name)
|
|
140
|
+
if isinstance(formatted_content, Syntax):
|
|
141
|
+
self._console.print(Panel(base_content, title=title, border_style=border_style, title_align="left"))
|
|
142
|
+
self._console.print(formatted_content)
|
|
143
|
+
else:
|
|
144
|
+
base_content += f"\n{formatted_content}"
|
|
145
|
+
self._console.print(Panel(base_content, title=title, border_style=border_style, title_align="left"))
|
|
146
|
+
else:
|
|
147
|
+
self._console.print(Panel(base_content, title=title, border_style=border_style, title_align="left"))
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
elif 'CompletionEvent' in event_class_name:
|
|
151
|
+
completion = getattr(event, 'completion', None)
|
|
152
|
+
if completion:
|
|
153
|
+
result = getattr(completion, 'result', 'Task completed successfully')
|
|
154
|
+
command = getattr(completion, 'command', None)
|
|
155
|
+
|
|
156
|
+
self._console.print(Panel(
|
|
157
|
+
Markdown(result),
|
|
158
|
+
title="🏁 Task Completion",
|
|
159
|
+
border_style="green",
|
|
160
|
+
title_align="left"
|
|
161
|
+
))
|
|
162
|
+
if command:
|
|
163
|
+
self._console.print(f"[dim]Suggested command:[/dim] [bold cyan]{command}[/]")
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
elif 'PlanModeRespondEvent' in event_class_name:
|
|
167
|
+
completion = getattr(event, 'completion', None)
|
|
168
|
+
if completion:
|
|
169
|
+
response = getattr(completion, 'response', 'Plan completed')
|
|
170
|
+
self._console.print(Panel(
|
|
171
|
+
Markdown(response),
|
|
172
|
+
title="🏁 Plan Completion",
|
|
173
|
+
border_style="green",
|
|
174
|
+
title_align="left"
|
|
175
|
+
))
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
elif 'ErrorEvent' in event_class_name:
|
|
179
|
+
message = getattr(event, 'message', 'Unknown error')
|
|
180
|
+
self._console.print(Panel(
|
|
181
|
+
f"[bold red]Error:[/bold red] {message}",
|
|
182
|
+
title="🔥 Error",
|
|
183
|
+
border_style="red",
|
|
184
|
+
title_align="left"
|
|
185
|
+
))
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
elif 'ConversationIdEvent' in event_class_name:
|
|
189
|
+
conversation_id = getattr(event, 'conversation_id', '')
|
|
190
|
+
if conversation_id:
|
|
191
|
+
self._console.print(f"[dim]Conversation ID: {conversation_id}[/dim]")
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
# 处理旧格式的事件类型
|
|
195
|
+
if event.event_type == "start":
|
|
196
|
+
project_name = os.path.basename(os.path.abspath(self.options.cwd or os.getcwd()))
|
|
197
|
+
self._console.rule(f"[bold cyan]Starting Auto-Coder: {project_name}[/]")
|
|
198
|
+
query = event.data.get("query", "")
|
|
199
|
+
if query:
|
|
200
|
+
self._console.print(Panel(
|
|
201
|
+
f"[bold]Query:[/bold]\n{query}",
|
|
202
|
+
title="🎯 Objective",
|
|
203
|
+
border_style="blue"
|
|
204
|
+
))
|
|
205
|
+
|
|
206
|
+
elif event.event_type == "llm_thinking":
|
|
207
|
+
text = event.data.get("text", "")
|
|
208
|
+
if text.strip():
|
|
209
|
+
self._console.print(f"[grey50]{text}[/grey50]", end="")
|
|
210
|
+
|
|
211
|
+
elif event.event_type == "llm_output":
|
|
212
|
+
text = event.data.get("text", "")
|
|
213
|
+
if text.strip():
|
|
214
|
+
self._console.print(text, end="")
|
|
215
|
+
|
|
216
|
+
elif event.event_type == "tool_call":
|
|
217
|
+
tool_name = event.data.get("tool_name", "Unknown Tool")
|
|
218
|
+
tool_args = event.data.get("args", {})
|
|
219
|
+
display_content = self._format_tool_display(tool_name, tool_args)
|
|
220
|
+
self._console.print(Panel(
|
|
221
|
+
display_content,
|
|
222
|
+
title=f"🛠️ Action: {tool_name}",
|
|
223
|
+
border_style="blue",
|
|
224
|
+
title_align="left"
|
|
225
|
+
))
|
|
226
|
+
|
|
227
|
+
elif event.event_type == "tool_result":
|
|
228
|
+
tool_name = event.data.get("tool_name", "Unknown Tool")
|
|
229
|
+
success = event.data.get("success", True)
|
|
230
|
+
message = event.data.get("message", "")
|
|
231
|
+
content = event.data.get("content")
|
|
232
|
+
|
|
233
|
+
title = f"✅ Tool Result: {tool_name}" if success else f"❌ Tool Result: {tool_name}"
|
|
234
|
+
border_style = "green" if success else "red"
|
|
235
|
+
|
|
236
|
+
base_content = f"[bold]Status:[/bold] {'Success' if success else 'Failure'}\n"
|
|
237
|
+
base_content += f"[bold]Message:[/bold] {message}\n"
|
|
238
|
+
|
|
239
|
+
# 处理内容显示
|
|
240
|
+
if content is not None:
|
|
241
|
+
formatted_content = self._format_tool_result_content(content, tool_name)
|
|
242
|
+
if isinstance(formatted_content, Syntax):
|
|
243
|
+
self._console.print(Panel(base_content, title=title, border_style=border_style, title_align="left"))
|
|
244
|
+
self._console.print(formatted_content)
|
|
245
|
+
else:
|
|
246
|
+
base_content += f"\n{formatted_content}"
|
|
247
|
+
self._console.print(Panel(base_content, title=title, border_style=border_style, title_align="left"))
|
|
248
|
+
else:
|
|
249
|
+
self._console.print(Panel(base_content, title=title, border_style=border_style, title_align="left"))
|
|
250
|
+
|
|
251
|
+
elif event.event_type == "completion":
|
|
252
|
+
result = event.data.get("result", "Task completed successfully")
|
|
253
|
+
self._console.print(Panel(
|
|
254
|
+
Markdown(result),
|
|
255
|
+
title="🏁 Task Completion",
|
|
256
|
+
border_style="green",
|
|
257
|
+
title_align="left"
|
|
258
|
+
))
|
|
259
|
+
|
|
260
|
+
elif event.event_type == "plan_mode_respond":
|
|
261
|
+
result = event.data.get("result", "Plan completed")
|
|
262
|
+
self._console.print(Panel(
|
|
263
|
+
Markdown(result),
|
|
264
|
+
title="🏁 Plan Completion",
|
|
265
|
+
border_style="green",
|
|
266
|
+
title_align="left"
|
|
267
|
+
))
|
|
268
|
+
|
|
269
|
+
elif event.event_type == "token_usage":
|
|
270
|
+
usage = event.data.get("usage")
|
|
271
|
+
if usage:
|
|
272
|
+
self._process_token_usage_event(usage)
|
|
273
|
+
|
|
274
|
+
elif event.event_type == "window_change":
|
|
275
|
+
tokens_used = event.data.get("tokens_used", 0)
|
|
276
|
+
if tokens_used > 0:
|
|
277
|
+
self._console.print(f"[dim]当前会话总 tokens: {tokens_used}[/dim]")
|
|
278
|
+
|
|
279
|
+
elif event.event_type == "conversation_id":
|
|
280
|
+
conversation_id = event.data.get("conversation_id", "")
|
|
281
|
+
if conversation_id:
|
|
282
|
+
self._console.print(f"[dim]Conversation ID: {conversation_id}[/dim]")
|
|
283
|
+
|
|
284
|
+
elif event.event_type == "content":
|
|
285
|
+
content = event.data.get("content", "")
|
|
286
|
+
if content.strip():
|
|
287
|
+
# 检查是否是思考过程(通常包含特定标记)
|
|
288
|
+
if any(marker in content.lower() for marker in ["thinking", "analyzing", "考虑", "分析"]):
|
|
289
|
+
self._console.print(f"[grey50]{content}[/grey50]", end="")
|
|
290
|
+
else:
|
|
291
|
+
self._console.print(content, end="")
|
|
292
|
+
|
|
293
|
+
elif event.event_type == "file_modified":
|
|
294
|
+
files = event.data.get("files", [])
|
|
295
|
+
if files:
|
|
296
|
+
files_str = "\n".join([f" - {f}" for f in files])
|
|
297
|
+
self._console.print(Panel(
|
|
298
|
+
f"[bold]Modified Files:[/bold]\n{files_str}",
|
|
299
|
+
title="📝 File Changes",
|
|
300
|
+
border_style="yellow",
|
|
301
|
+
title_align="left"
|
|
302
|
+
))
|
|
303
|
+
|
|
304
|
+
elif event.event_type == "file_created":
|
|
305
|
+
files = event.data.get("files", [])
|
|
306
|
+
if files:
|
|
307
|
+
files_str = "\n".join([f" - {f}" for f in files])
|
|
308
|
+
self._console.print(Panel(
|
|
309
|
+
f"[bold]Created Files:[/bold]\n{files_str}",
|
|
310
|
+
title="📄 New Files",
|
|
311
|
+
border_style="green",
|
|
312
|
+
title_align="left"
|
|
313
|
+
))
|
|
314
|
+
|
|
315
|
+
elif event.event_type == "file_deleted":
|
|
316
|
+
files = event.data.get("files", [])
|
|
317
|
+
if files:
|
|
318
|
+
files_str = "\n".join([f" - {f}" for f in files])
|
|
319
|
+
self._console.print(Panel(
|
|
320
|
+
f"[bold]Deleted Files:[/bold]\n{files_str}",
|
|
321
|
+
title="🗑️ Removed Files",
|
|
322
|
+
border_style="red",
|
|
323
|
+
title_align="left"
|
|
324
|
+
))
|
|
325
|
+
|
|
326
|
+
elif event.event_type == "end":
|
|
327
|
+
status = event.data.get("status", "completed")
|
|
328
|
+
if status == "completed":
|
|
329
|
+
self._console.rule("[bold green]Auto-Coder Finished Successfully[/]")
|
|
330
|
+
else:
|
|
331
|
+
self._console.rule(f"[bold yellow]Auto-Coder Finished: {status}[/]")
|
|
332
|
+
|
|
333
|
+
elif event.event_type == "error":
|
|
334
|
+
error = event.data.get("error", "Unknown error")
|
|
335
|
+
error_type = event.data.get("error_type", "Error")
|
|
336
|
+
self._console.print(Panel(
|
|
337
|
+
f"[bold red]Error Type:[/bold red] {error_type}\n[bold red]Message:[/bold red] {error}",
|
|
338
|
+
title="🔥 Error",
|
|
339
|
+
border_style="red",
|
|
340
|
+
title_align="left"
|
|
341
|
+
))
|
|
342
|
+
|
|
343
|
+
except Exception as e:
|
|
344
|
+
# 渲染错误不应该影响主流程
|
|
345
|
+
self._console.print(f"[dim red]Render error: {str(e)}[/dim red]")
|
|
346
|
+
|
|
347
|
+
def _format_tool_display(self, tool_name: str, tool_args: Dict[str, Any]) -> str:
|
|
348
|
+
"""
|
|
349
|
+
格式化工具调用显示内容
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
tool_name: 工具名称
|
|
353
|
+
tool_args: 工具参数
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
str: 格式化后的显示内容
|
|
357
|
+
"""
|
|
358
|
+
if not tool_args:
|
|
359
|
+
return f"[bold]Tool:[/bold] {tool_name}"
|
|
360
|
+
|
|
361
|
+
content_parts = [f"[bold]Tool:[/bold] {tool_name}"]
|
|
362
|
+
|
|
363
|
+
for key, value in tool_args.items():
|
|
364
|
+
if isinstance(value, str) and len(value) > 100:
|
|
365
|
+
value = f"{value[:50]}...{value[-50:]}"
|
|
366
|
+
content_parts.append(f"[bold]{key}:[/bold] {value}")
|
|
367
|
+
|
|
368
|
+
return "\n".join(content_parts)
|
|
369
|
+
|
|
370
|
+
def _process_token_usage_event(self, usage):
|
|
371
|
+
"""
|
|
372
|
+
处理 TokenUsageEvent,累计 token 使用情况
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
usage: SingleOutputMeta 对象
|
|
376
|
+
"""
|
|
377
|
+
try:
|
|
378
|
+
# 正确提取 SingleOutputMeta 对象的属性
|
|
379
|
+
input_tokens = getattr(usage, 'input_tokens_count', 0)
|
|
380
|
+
output_tokens = getattr(usage, 'generated_tokens_count', 0)
|
|
381
|
+
|
|
382
|
+
# 获取模型信息用于定价
|
|
383
|
+
try:
|
|
384
|
+
from autocoder.utils import llms as llm_utils
|
|
385
|
+
# 这里需要获取 LLM 实例,但在 SDK 中可能不直接可用
|
|
386
|
+
# 暂时使用默认模型名称
|
|
387
|
+
model_name = self.options.model or "unknown"
|
|
388
|
+
model_info = llm_utils.get_model_info(model_name, "lite") or {}
|
|
389
|
+
input_price = model_info.get("input_price", 0.0)
|
|
390
|
+
output_price = model_info.get("output_price", 0.0)
|
|
391
|
+
except:
|
|
392
|
+
# 如果获取模型信息失败,使用默认值
|
|
393
|
+
model_name = self.options.model or "unknown"
|
|
394
|
+
input_price = 0.0
|
|
395
|
+
output_price = 0.0
|
|
396
|
+
|
|
397
|
+
# 计算成本
|
|
398
|
+
input_cost = (input_tokens * input_price) / 1000000 # 转换为百万token单位
|
|
399
|
+
output_cost = (output_tokens * output_price) / 1000000
|
|
400
|
+
|
|
401
|
+
# 累计token使用情况
|
|
402
|
+
self._accumulated_token_usage["model_name"] = model_name
|
|
403
|
+
self._accumulated_token_usage["input_tokens"] += input_tokens
|
|
404
|
+
self._accumulated_token_usage["output_tokens"] += output_tokens
|
|
405
|
+
self._accumulated_token_usage["input_cost"] += input_cost
|
|
406
|
+
self._accumulated_token_usage["output_cost"] += output_cost
|
|
407
|
+
|
|
408
|
+
# 显示当前的 token 使用情况
|
|
409
|
+
total_tokens = input_tokens + output_tokens
|
|
410
|
+
self._console.print(f"[dim]Token usage: Input={input_tokens}, Output={output_tokens}, Total={total_tokens}[/dim]")
|
|
411
|
+
|
|
412
|
+
except Exception as e:
|
|
413
|
+
self._console.print(f"[dim red]Error processing token usage: {str(e)}[/dim red]")
|
|
414
|
+
|
|
415
|
+
def _print_final_token_usage(self):
|
|
416
|
+
"""
|
|
417
|
+
打印最终的累计 token 使用情况
|
|
418
|
+
"""
|
|
419
|
+
try:
|
|
420
|
+
if self._accumulated_token_usage["input_tokens"] > 0:
|
|
421
|
+
from autocoder.common.printer import Printer
|
|
422
|
+
printer = Printer()
|
|
423
|
+
printer.print_in_terminal(
|
|
424
|
+
"code_generation_complete",
|
|
425
|
+
duration=0.0,
|
|
426
|
+
input_tokens=self._accumulated_token_usage["input_tokens"],
|
|
427
|
+
output_tokens=self._accumulated_token_usage["output_tokens"],
|
|
428
|
+
input_cost=self._accumulated_token_usage["input_cost"],
|
|
429
|
+
output_cost=self._accumulated_token_usage["output_cost"],
|
|
430
|
+
speed=0.0,
|
|
431
|
+
model_names=self._accumulated_token_usage["model_name"],
|
|
432
|
+
sampling_count=1
|
|
433
|
+
)
|
|
434
|
+
except Exception as e:
|
|
435
|
+
# 如果打印失败,使用简单的格式
|
|
436
|
+
total_tokens = self._accumulated_token_usage["input_tokens"] + self._accumulated_token_usage["output_tokens"]
|
|
437
|
+
total_cost = self._accumulated_token_usage["input_cost"] + self._accumulated_token_usage["output_cost"]
|
|
438
|
+
self._console.print(Panel(
|
|
439
|
+
f"总计 Token 使用: {total_tokens} (输入: {self._accumulated_token_usage['input_tokens']}, 输出: {self._accumulated_token_usage['output_tokens']})\n"
|
|
440
|
+
f"总计成本: ${total_cost:.6f}",
|
|
441
|
+
title="📊 Token 使用统计",
|
|
442
|
+
border_style="cyan"
|
|
443
|
+
))
|
|
444
|
+
|
|
445
|
+
def _reset_token_usage(self):
|
|
446
|
+
"""
|
|
447
|
+
重置累计的 token 使用情况
|
|
448
|
+
"""
|
|
449
|
+
self._accumulated_token_usage = {
|
|
450
|
+
"model_name": "",
|
|
451
|
+
"input_tokens": 0,
|
|
452
|
+
"output_tokens": 0,
|
|
453
|
+
"input_cost": 0.0,
|
|
454
|
+
"output_cost": 0.0
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
def _format_tool_result_content(self, content: Any, tool_name: str = "") -> str | Syntax:
|
|
458
|
+
"""
|
|
459
|
+
格式化工具结果内容
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
content: 结果内容
|
|
463
|
+
tool_name: 工具名称(用于推断语法类型)
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
str | Syntax: 格式化后的内容或语法高亮对象
|
|
467
|
+
"""
|
|
468
|
+
def _truncate_content(content_str: str) -> str:
|
|
469
|
+
if len(content_str) > 500:
|
|
470
|
+
return f"{content_str[:200]}\n...\n{content_str[-200:]}"
|
|
471
|
+
return content_str
|
|
472
|
+
|
|
473
|
+
try:
|
|
474
|
+
if isinstance(content, (dict, list)):
|
|
475
|
+
import json
|
|
476
|
+
content_str = json.dumps(content, indent=2, ensure_ascii=False)
|
|
477
|
+
return Syntax(_truncate_content(content_str), "json", theme="default", line_numbers=False)
|
|
478
|
+
|
|
479
|
+
elif isinstance(content, str):
|
|
480
|
+
# 检查是否是多行内容或代码
|
|
481
|
+
if '\n' in content or content.strip().startswith('<') or len(content) > 200:
|
|
482
|
+
# 推断语法类型
|
|
483
|
+
lexer = "text"
|
|
484
|
+
if "ReadFile" in tool_name:
|
|
485
|
+
if any(ext in content for ext in [".py", "python"]):
|
|
486
|
+
lexer = "python"
|
|
487
|
+
elif any(ext in content for ext in [".js", "javascript"]):
|
|
488
|
+
lexer = "javascript"
|
|
489
|
+
elif any(ext in content for ext in [".ts", "typescript"]):
|
|
490
|
+
lexer = "typescript"
|
|
491
|
+
elif any(ext in content for ext in [".html", "<!DOCTYPE", "<html"]):
|
|
492
|
+
lexer = "html"
|
|
493
|
+
elif any(ext in content for ext in [".css", "{"]):
|
|
494
|
+
lexer = "css"
|
|
495
|
+
elif any(ext in content for ext in [".json", "{"]):
|
|
496
|
+
lexer = "json"
|
|
497
|
+
elif any(ext in content for ext in [".xml", "<?xml"]):
|
|
498
|
+
lexer = "xml"
|
|
499
|
+
elif any(ext in content for ext in [".md", "#"]):
|
|
500
|
+
lexer = "markdown"
|
|
501
|
+
elif "ExecuteCommand" in tool_name or "Shell" in tool_name:
|
|
502
|
+
lexer = "shell"
|
|
503
|
+
elif content.strip().startswith('{') or content.strip().startswith('['):
|
|
504
|
+
lexer = "json"
|
|
505
|
+
|
|
506
|
+
return Syntax(_truncate_content(content), lexer, theme="default", line_numbers=True)
|
|
507
|
+
else:
|
|
508
|
+
return _truncate_content(content)
|
|
509
|
+
else:
|
|
510
|
+
return _truncate_content(str(content))
|
|
511
|
+
|
|
512
|
+
except Exception:
|
|
513
|
+
return _truncate_content(str(content))
|
|
514
|
+
|
|
515
|
+
async def query_stream(self, prompt: str, show_terminal: bool = True) -> AsyncIterator[Message]:
|
|
516
|
+
"""
|
|
517
|
+
异步流式查询 - 使用 run_auto_command
|
|
518
|
+
|
|
519
|
+
Args:
|
|
520
|
+
prompt: 查询提示
|
|
521
|
+
show_terminal: 是否显示到终端
|
|
522
|
+
|
|
523
|
+
Yields:
|
|
524
|
+
Message: 响应消息流
|
|
525
|
+
|
|
526
|
+
Raises:
|
|
527
|
+
BridgeError: 桥接层错误
|
|
528
|
+
"""
|
|
529
|
+
try:
|
|
530
|
+
# 重置累计的 token 使用情况
|
|
531
|
+
self._reset_token_usage()
|
|
532
|
+
|
|
533
|
+
# 先返回用户消息
|
|
534
|
+
user_message = Message(role="user", content=prompt)
|
|
535
|
+
yield user_message
|
|
536
|
+
|
|
537
|
+
# 在线程池中执行同步调用
|
|
538
|
+
loop = asyncio.get_event_loop()
|
|
539
|
+
|
|
540
|
+
# 使用 run_auto_command 进行代码修改
|
|
541
|
+
event_stream = await loop.run_in_executor(
|
|
542
|
+
self._executor,
|
|
543
|
+
self._sync_run_auto_command,
|
|
544
|
+
prompt
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
# 处理事件流并转换为消息
|
|
548
|
+
assistant_content = ""
|
|
549
|
+
for event in event_stream:
|
|
550
|
+
# 渲染事件到终端
|
|
551
|
+
self._render_stream_event(event, show_terminal)
|
|
552
|
+
|
|
553
|
+
if event.event_type == "content":
|
|
554
|
+
content = event.data.get("content", "")
|
|
555
|
+
assistant_content += content
|
|
556
|
+
|
|
557
|
+
# 返回增量消息
|
|
558
|
+
yield Message(
|
|
559
|
+
role="assistant",
|
|
560
|
+
content=content,
|
|
561
|
+
metadata={
|
|
562
|
+
"event_type": event.event_type,
|
|
563
|
+
"model": self.options.model,
|
|
564
|
+
"temperature": self.options.temperature,
|
|
565
|
+
"is_incremental": True
|
|
566
|
+
}
|
|
567
|
+
)
|
|
568
|
+
elif event.event_type == "end":
|
|
569
|
+
# 返回最终完整消息
|
|
570
|
+
yield Message(
|
|
571
|
+
role="assistant",
|
|
572
|
+
content=assistant_content,
|
|
573
|
+
metadata={
|
|
574
|
+
"event_type": event.event_type,
|
|
575
|
+
"model": self.options.model,
|
|
576
|
+
"temperature": self.options.temperature,
|
|
577
|
+
"is_final": True,
|
|
578
|
+
"status": event.data.get("status", "completed")
|
|
579
|
+
}
|
|
580
|
+
)
|
|
581
|
+
elif event.event_type == "error":
|
|
582
|
+
# 返回错误消息
|
|
583
|
+
yield Message(
|
|
584
|
+
role="assistant",
|
|
585
|
+
content=f"Error: {event.data.get('error', 'Unknown error')}",
|
|
586
|
+
metadata={
|
|
587
|
+
"event_type": event.event_type,
|
|
588
|
+
"error_type": event.data.get("error_type", "Unknown"),
|
|
589
|
+
"is_error": True
|
|
590
|
+
}
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
# 添加小延迟以改善视觉效果
|
|
594
|
+
if show_terminal:
|
|
595
|
+
time.sleep(0.05)
|
|
596
|
+
|
|
597
|
+
# 打印最终的累计 token 使用情况
|
|
598
|
+
if show_terminal:
|
|
599
|
+
self._print_final_token_usage()
|
|
600
|
+
|
|
601
|
+
except Exception as e:
|
|
602
|
+
# 在异常时也打印累计的 token 使用情况
|
|
603
|
+
if show_terminal:
|
|
604
|
+
self._print_final_token_usage()
|
|
605
|
+
raise e
|
|
606
|
+
|
|
607
|
+
def query_sync(self, prompt: str, show_terminal: bool = True) -> str:
|
|
608
|
+
"""
|
|
609
|
+
同步查询 - 使用 run_auto_command
|
|
610
|
+
|
|
611
|
+
Args:
|
|
612
|
+
prompt: 查询提示
|
|
613
|
+
show_terminal: 是否显示到终端
|
|
614
|
+
|
|
615
|
+
Returns:
|
|
616
|
+
str: 响应内容
|
|
617
|
+
|
|
618
|
+
Raises:
|
|
619
|
+
BridgeError: 桥接层错误
|
|
620
|
+
"""
|
|
621
|
+
try:
|
|
622
|
+
# 重置累计的 token 使用情况
|
|
623
|
+
self._reset_token_usage()
|
|
624
|
+
|
|
625
|
+
event_stream = self._sync_run_auto_command(prompt)
|
|
626
|
+
|
|
627
|
+
# 收集所有内容
|
|
628
|
+
content_parts = []
|
|
629
|
+
for event in event_stream:
|
|
630
|
+
# 渲染事件到终端
|
|
631
|
+
self._render_stream_event(event, show_terminal)
|
|
632
|
+
|
|
633
|
+
if event.event_type == "content":
|
|
634
|
+
content_parts.append(event.data.get("content", ""))
|
|
635
|
+
elif event.event_type == "error":
|
|
636
|
+
raise BridgeError(f"Query failed: {event.data.get('error', 'Unknown error')}")
|
|
637
|
+
|
|
638
|
+
# 添加小延迟以改善视觉效果
|
|
639
|
+
if show_terminal:
|
|
640
|
+
time.sleep(0.05)
|
|
641
|
+
|
|
642
|
+
# 打印最终的累计 token 使用情况
|
|
643
|
+
if show_terminal:
|
|
644
|
+
self._print_final_token_usage()
|
|
645
|
+
|
|
646
|
+
return "".join(content_parts)
|
|
647
|
+
|
|
648
|
+
except Exception as e:
|
|
649
|
+
# 在异常时也打印累计的 token 使用情况
|
|
650
|
+
if show_terminal:
|
|
651
|
+
self._print_final_token_usage()
|
|
652
|
+
|
|
653
|
+
raise e
|
|
654
|
+
|
|
655
|
+
def modify_code(
|
|
656
|
+
self,
|
|
657
|
+
prompt: str,
|
|
658
|
+
pre_commit: bool = False,
|
|
659
|
+
pr: Optional[bool] = None,
|
|
660
|
+
extra_args: Optional[Dict[str, Any]] = None,
|
|
661
|
+
show_terminal: bool = True
|
|
662
|
+
) -> CodeModificationResult:
|
|
663
|
+
"""
|
|
664
|
+
代码修改接口 - 直接使用 run_auto_command
|
|
665
|
+
|
|
666
|
+
Args:
|
|
667
|
+
prompt: 修改提示
|
|
668
|
+
pre_commit: 是否预提交
|
|
669
|
+
pr: 是否创建 PR,如果为None则使用配置中的值
|
|
670
|
+
extra_args: 额外参数
|
|
671
|
+
show_terminal: 是否显示到终端
|
|
672
|
+
|
|
673
|
+
Returns:
|
|
674
|
+
CodeModificationResult: 修改结果
|
|
675
|
+
"""
|
|
676
|
+
try:
|
|
677
|
+
# 重置累计的 token 使用情况
|
|
678
|
+
self._reset_token_usage()
|
|
679
|
+
|
|
680
|
+
event_stream = self._sync_run_auto_command(
|
|
681
|
+
prompt,
|
|
682
|
+
pre_commit=pre_commit,
|
|
683
|
+
pr=pr,
|
|
684
|
+
extra_args=extra_args
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
# 分析事件流,提取修改结果
|
|
688
|
+
modified_files = []
|
|
689
|
+
created_files = []
|
|
690
|
+
deleted_files = []
|
|
691
|
+
messages = []
|
|
692
|
+
success = True
|
|
693
|
+
error_details = None
|
|
694
|
+
|
|
695
|
+
for event in event_stream:
|
|
696
|
+
# 渲染事件到终端
|
|
697
|
+
self._render_stream_event(event, show_terminal)
|
|
698
|
+
|
|
699
|
+
if event.event_type == "content":
|
|
700
|
+
messages.append(event.data.get("content", ""))
|
|
701
|
+
elif event.event_type == "error":
|
|
702
|
+
success = False
|
|
703
|
+
error_details = event.data.get("error", "Unknown error")
|
|
704
|
+
elif event.event_type == "file_modified":
|
|
705
|
+
modified_files.extend(event.data.get("files", []))
|
|
706
|
+
elif event.event_type == "file_created":
|
|
707
|
+
created_files.extend(event.data.get("files", []))
|
|
708
|
+
elif event.event_type == "file_deleted":
|
|
709
|
+
deleted_files.extend(event.data.get("files", []))
|
|
710
|
+
|
|
711
|
+
# 添加小延迟以改善视觉效果
|
|
712
|
+
if show_terminal:
|
|
713
|
+
time.sleep(0.05)
|
|
714
|
+
|
|
715
|
+
# 打印最终的累计 token 使用情况
|
|
716
|
+
if show_terminal:
|
|
717
|
+
self._print_final_token_usage()
|
|
718
|
+
|
|
719
|
+
return CodeModificationResult(
|
|
720
|
+
success=success,
|
|
721
|
+
message="".join(messages),
|
|
722
|
+
modified_files=modified_files,
|
|
723
|
+
created_files=created_files,
|
|
724
|
+
deleted_files=deleted_files,
|
|
725
|
+
error_details=error_details,
|
|
726
|
+
metadata={
|
|
727
|
+
"pre_commit": pre_commit,
|
|
728
|
+
"extra_args": extra_args or {}
|
|
729
|
+
}
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
except Exception as e:
|
|
733
|
+
# 在异常时也打印累计的 token 使用情况
|
|
734
|
+
if show_terminal:
|
|
735
|
+
self._print_final_token_usage()
|
|
736
|
+
self._console.print(Panel(
|
|
737
|
+
f"[bold red]FATAL ERROR:[/bold red]\n{str(e)}",
|
|
738
|
+
title="🔥 System Error",
|
|
739
|
+
border_style="red"
|
|
740
|
+
))
|
|
741
|
+
return CodeModificationResult(
|
|
742
|
+
success=False,
|
|
743
|
+
message="",
|
|
744
|
+
error_details=str(e),
|
|
745
|
+
metadata={"exception_type": type(e).__name__}
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
async def modify_code_stream(
|
|
749
|
+
self,
|
|
750
|
+
prompt: str,
|
|
751
|
+
pre_commit: bool = False,
|
|
752
|
+
pr: Optional[bool] = None,
|
|
753
|
+
extra_args: Optional[Dict[str, Any]] = None,
|
|
754
|
+
show_terminal: bool = True
|
|
755
|
+
) -> AsyncIterator[StreamEvent]:
|
|
756
|
+
"""
|
|
757
|
+
异步流式代码修改接口
|
|
758
|
+
|
|
759
|
+
Args:
|
|
760
|
+
prompt: 修改提示
|
|
761
|
+
pre_commit: 是否预提交
|
|
762
|
+
pr: 是否创建 PR,如果为None则使用配置中的值
|
|
763
|
+
extra_args: 额外参数
|
|
764
|
+
show_terminal: 是否显示到终端
|
|
765
|
+
|
|
766
|
+
Yields:
|
|
767
|
+
StreamEvent: 修改事件流
|
|
768
|
+
"""
|
|
769
|
+
try:
|
|
770
|
+
# 重置累计的 token 使用情况
|
|
771
|
+
self._reset_token_usage()
|
|
772
|
+
|
|
773
|
+
loop = asyncio.get_event_loop()
|
|
774
|
+
|
|
775
|
+
# 在线程池中执行同步调用
|
|
776
|
+
event_stream = await loop.run_in_executor(
|
|
777
|
+
self._executor,
|
|
778
|
+
self._sync_run_auto_command,
|
|
779
|
+
prompt,
|
|
780
|
+
pre_commit,
|
|
781
|
+
pr,
|
|
782
|
+
extra_args
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
# 处理并转发事件流
|
|
786
|
+
for event in event_stream:
|
|
787
|
+
# 渲染事件到终端
|
|
788
|
+
self._render_stream_event(event, show_terminal)
|
|
789
|
+
|
|
790
|
+
# 转发事件
|
|
791
|
+
yield event
|
|
792
|
+
|
|
793
|
+
# 添加小延迟以改善视觉效果
|
|
794
|
+
if show_terminal:
|
|
795
|
+
time.sleep(0.05)
|
|
796
|
+
|
|
797
|
+
# 打印最终的累计 token 使用情况
|
|
798
|
+
if show_terminal:
|
|
799
|
+
self._print_final_token_usage()
|
|
800
|
+
|
|
801
|
+
except Exception as e:
|
|
802
|
+
# 在异常时也打印累计的 token 使用情况
|
|
803
|
+
if show_terminal:
|
|
804
|
+
self._print_final_token_usage()
|
|
805
|
+
|
|
806
|
+
error_event = StreamEvent(
|
|
807
|
+
event_type="error",
|
|
808
|
+
data={"error": str(e), "error_type": type(e).__name__}
|
|
809
|
+
)
|
|
810
|
+
self._render_stream_event(error_event, show_terminal)
|
|
811
|
+
yield error_event
|
|
812
|
+
|
|
813
|
+
def _sync_run_auto_command(
|
|
814
|
+
self,
|
|
815
|
+
prompt: str,
|
|
816
|
+
pre_commit: bool = False,
|
|
817
|
+
pr: Optional[bool] = None,
|
|
818
|
+
extra_args: Optional[Dict[str, Any]] = None
|
|
819
|
+
) -> Iterator[StreamEvent]:
|
|
820
|
+
"""
|
|
821
|
+
内部同步调用 run_auto_command
|
|
822
|
+
|
|
823
|
+
Args:
|
|
824
|
+
prompt: 查询提示
|
|
825
|
+
pre_commit: 是否预提交
|
|
826
|
+
pr: 是否创建 PR,如果为None则使用配置中的值
|
|
827
|
+
extra_args: 额外参数
|
|
828
|
+
|
|
829
|
+
Returns:
|
|
830
|
+
Iterator[StreamEvent]: 事件流
|
|
831
|
+
"""
|
|
832
|
+
# 如果没有明确指定pr参数,使用配置中的值
|
|
833
|
+
if pr is None:
|
|
834
|
+
pr = self.options.pr
|
|
835
|
+
|
|
836
|
+
return self.bridge.call_run_auto_command(
|
|
837
|
+
query=prompt,
|
|
838
|
+
pre_commit=pre_commit,
|
|
839
|
+
pr=pr,
|
|
840
|
+
extra_args=extra_args or {},
|
|
841
|
+
stream=True
|
|
842
|
+
)
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
def get_project_memory(self) -> Dict[str, Any]:
|
|
846
|
+
"""
|
|
847
|
+
获取项目内存状态
|
|
848
|
+
|
|
849
|
+
Returns:
|
|
850
|
+
Dict[str, Any]: 项目内存数据
|
|
851
|
+
"""
|
|
852
|
+
return self.bridge.get_memory()
|
|
853
|
+
|
|
854
|
+
def save_project_memory(self, memory_data: Dict[str, Any]) -> None:
|
|
855
|
+
"""
|
|
856
|
+
保存项目内存状态
|
|
857
|
+
|
|
858
|
+
Args:
|
|
859
|
+
memory_data: 内存数据
|
|
860
|
+
"""
|
|
861
|
+
self.bridge.save_memory(memory_data)
|
|
862
|
+
|
|
863
|
+
def get_project_config(self) -> Dict[str, Any]:
|
|
864
|
+
"""
|
|
865
|
+
获取项目配置
|
|
866
|
+
|
|
867
|
+
Returns:
|
|
868
|
+
Dict[str, Any]: 项目配置
|
|
869
|
+
"""
|
|
870
|
+
return self.bridge.get_project_config()
|
|
871
|
+
|
|
872
|
+
def __del__(self):
|
|
873
|
+
"""清理资源"""
|
|
874
|
+
if hasattr(self, '_executor'):
|
|
875
|
+
self._executor.shutdown(wait=False)
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
|