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,191 @@
|
|
|
1
|
+
"""
|
|
2
|
+
工具显示模块,提供格式化工具调用信息的功能。
|
|
3
|
+
|
|
4
|
+
这个模块负责生成用户友好的、国际化的工具调用显示信息,
|
|
5
|
+
主要用于终端运行器中展示工具调用的详细信息。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from typing import Dict, Callable, Type
|
|
10
|
+
|
|
11
|
+
from autocoder.common.auto_coder_lang import get_system_language, format_str_jinja2
|
|
12
|
+
from autocoder.common.v2.agent.agentic_edit_types import (
|
|
13
|
+
BaseTool,
|
|
14
|
+
ReadFileTool, WriteToFileTool, ReplaceInFileTool, ExecuteCommandTool,
|
|
15
|
+
ListFilesTool, SearchFilesTool, ListCodeDefinitionNamesTool,
|
|
16
|
+
AskFollowupQuestionTool, UseMcpTool, AttemptCompletionTool
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# Define message templates for each tool in English and Chinese
|
|
20
|
+
TOOL_DISPLAY_MESSAGES: Dict[Type[BaseTool], Dict[str, str]] = {
|
|
21
|
+
ReadFileTool: {
|
|
22
|
+
"en": "AutoCoder wants to read this file:\n[bold cyan]{{ path }}[/]",
|
|
23
|
+
"zh": "AutoCoder 想要读取此文件:\n[bold cyan]{{ path }}[/]"
|
|
24
|
+
},
|
|
25
|
+
WriteToFileTool: {
|
|
26
|
+
"en": (
|
|
27
|
+
"AutoCoder wants to write to this file:\n[bold cyan]{{ path }}[/]\n\n"
|
|
28
|
+
"[dim]Content Snippet:[/dim]\n{{ content_snippet }}{{ ellipsis }}"
|
|
29
|
+
),
|
|
30
|
+
"zh": (
|
|
31
|
+
"AutoCoder 想要写入此文件:\n[bold cyan]{{ path }}[/]\n\n"
|
|
32
|
+
"[dim]内容片段:[/dim]\n{{ content_snippet }}{{ ellipsis }}"
|
|
33
|
+
)
|
|
34
|
+
},
|
|
35
|
+
ReplaceInFileTool: {
|
|
36
|
+
"en": (
|
|
37
|
+
"AutoCoder wants to replace content in this file:\n[bold cyan]{{ path }}[/]\n\n"
|
|
38
|
+
"[dim]Diff Snippet:[/dim]\n{{ diff_snippet }}{{ ellipsis }}"
|
|
39
|
+
),
|
|
40
|
+
"zh": (
|
|
41
|
+
"AutoCoder 想要替换此文件中的内容:\n[bold cyan]{{ path }}[/]\n\n"
|
|
42
|
+
"[dim]差异片段:[/dim]\n{{ diff_snippet }}{{ ellipsis }}"
|
|
43
|
+
)
|
|
44
|
+
},
|
|
45
|
+
ExecuteCommandTool: {
|
|
46
|
+
"en": (
|
|
47
|
+
"AutoCoder wants to execute this command:\n[bold yellow]{{ command }}[/]\n"
|
|
48
|
+
"[dim](Requires Approval: {{ requires_approval }})[/]"
|
|
49
|
+
),
|
|
50
|
+
"zh": (
|
|
51
|
+
"AutoCoder 想要执行此命令:\n[bold yellow]{{ command }}[/]\n"
|
|
52
|
+
"[dim](需要批准:{{ requires_approval }})[/]"
|
|
53
|
+
)
|
|
54
|
+
},
|
|
55
|
+
ListFilesTool: {
|
|
56
|
+
"en": (
|
|
57
|
+
"AutoCoder wants to list files in:\n[bold green]{{ path }}[/] "
|
|
58
|
+
"{{ recursive_text }}"
|
|
59
|
+
),
|
|
60
|
+
"zh": (
|
|
61
|
+
"AutoCoder 想要列出此目录中的文件:\n[bold green]{{ path }}[/] "
|
|
62
|
+
"{{ recursive_text }}"
|
|
63
|
+
)
|
|
64
|
+
},
|
|
65
|
+
SearchFilesTool: {
|
|
66
|
+
"en": (
|
|
67
|
+
"AutoCoder wants to search files in:\n[bold green]{{ path }}[/]\n"
|
|
68
|
+
"[dim]File Pattern:[/dim] [yellow]{{ file_pattern }}[/]\n"
|
|
69
|
+
"[dim]Regex:[/dim] [yellow]{{ regex }}[/]"
|
|
70
|
+
),
|
|
71
|
+
"zh": (
|
|
72
|
+
"AutoCoder 想要在此目录中搜索文件:\n[bold green]{{ path }}[/]\n"
|
|
73
|
+
"[dim]文件模式:[/dim] [yellow]{{ file_pattern }}[/]\n"
|
|
74
|
+
"[dim]正则表达式:[/dim] [yellow]{{ regex }}[/]"
|
|
75
|
+
)
|
|
76
|
+
},
|
|
77
|
+
ListCodeDefinitionNamesTool: {
|
|
78
|
+
"en": "AutoCoder wants to list definitions in:\n[bold green]{{ path }}[/]",
|
|
79
|
+
"zh": "AutoCoder 想要列出此路径中的定义:\n[bold green]{{ path }}[/]"
|
|
80
|
+
},
|
|
81
|
+
AskFollowupQuestionTool: {
|
|
82
|
+
"en": (
|
|
83
|
+
"AutoCoder is asking a question:\n[bold magenta]{{ question }}[/]\n"
|
|
84
|
+
"{{ options_text }}"
|
|
85
|
+
),
|
|
86
|
+
"zh": (
|
|
87
|
+
"AutoCoder 正在提问:\n[bold magenta]{{ question }}[/]\n"
|
|
88
|
+
"{{ options_text }}"
|
|
89
|
+
)
|
|
90
|
+
},
|
|
91
|
+
UseMcpTool: {
|
|
92
|
+
"en": (
|
|
93
|
+
"AutoCoder wants to use an MCP tool:\n"
|
|
94
|
+
"[dim]Server:[/dim] [blue]{{ server_name }}[/]\n"
|
|
95
|
+
"[dim]Tool:[/dim] [blue]{{ tool_name }}[/]\n"
|
|
96
|
+
"[dim]Args:[/dim] {{ arguments_snippet }}{{ ellipsis }}"
|
|
97
|
+
),
|
|
98
|
+
"zh": (
|
|
99
|
+
"AutoCoder 想要使用 MCP 工具:\n"
|
|
100
|
+
"[dim]服务器:[/dim] [blue]{{ server_name }}[/]\n"
|
|
101
|
+
"[dim]工具:[/dim] [blue]{{ tool_name }}[/]\n"
|
|
102
|
+
"[dim]参数:[/dim] {{ arguments_snippet }}{{ ellipsis }}"
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
def get_tool_display_message(tool: BaseTool) -> str:
|
|
108
|
+
"""
|
|
109
|
+
生成工具调用的用户友好、国际化的字符串表示。
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
tool: 工具实例(Pydantic模型)。
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
用于在终端中显示的格式化字符串。
|
|
116
|
+
"""
|
|
117
|
+
lang = get_system_language()
|
|
118
|
+
tool_type = type(tool)
|
|
119
|
+
|
|
120
|
+
if tool_type not in TOOL_DISPLAY_MESSAGES:
|
|
121
|
+
# 未知工具的回退处理
|
|
122
|
+
return f"未知工具类型: {tool_type.__name__}\n数据: {tool.model_dump_json(indent=2)}"
|
|
123
|
+
|
|
124
|
+
templates = TOOL_DISPLAY_MESSAGES[tool_type]
|
|
125
|
+
template = templates.get(lang, templates.get("en", "工具显示模板未找到")) # 回退到英文
|
|
126
|
+
|
|
127
|
+
# 准备特定于每种工具类型的上下文
|
|
128
|
+
context = {}
|
|
129
|
+
if isinstance(tool, ReadFileTool):
|
|
130
|
+
context = {"path": tool.path}
|
|
131
|
+
elif isinstance(tool, WriteToFileTool):
|
|
132
|
+
snippet = tool.content[:150]
|
|
133
|
+
context = {
|
|
134
|
+
"path": tool.path,
|
|
135
|
+
"content_snippet": snippet,
|
|
136
|
+
"ellipsis": '...' if len(tool.content) > 150 else ''
|
|
137
|
+
}
|
|
138
|
+
elif isinstance(tool, ReplaceInFileTool):
|
|
139
|
+
snippet = tool.diff
|
|
140
|
+
context = {
|
|
141
|
+
"path": tool.path,
|
|
142
|
+
"diff_snippet": snippet,
|
|
143
|
+
"ellipsis": ''
|
|
144
|
+
}
|
|
145
|
+
elif isinstance(tool, ExecuteCommandTool):
|
|
146
|
+
context = {"command": tool.command, "requires_approval": tool.requires_approval}
|
|
147
|
+
elif isinstance(tool, ListFilesTool):
|
|
148
|
+
rec_text_en = '(Recursively)' if tool.recursive else '(Top Level)'
|
|
149
|
+
rec_text_zh = '(递归)' if tool.recursive else '(顶层)'
|
|
150
|
+
context = {
|
|
151
|
+
"path": tool.path,
|
|
152
|
+
"recursive_text": rec_text_zh if lang == 'zh' else rec_text_en
|
|
153
|
+
}
|
|
154
|
+
elif isinstance(tool, SearchFilesTool):
|
|
155
|
+
context = {
|
|
156
|
+
"path": tool.path,
|
|
157
|
+
"file_pattern": tool.file_pattern or '*',
|
|
158
|
+
"regex": tool.regex
|
|
159
|
+
}
|
|
160
|
+
elif isinstance(tool, ListCodeDefinitionNamesTool):
|
|
161
|
+
context = {"path": tool.path}
|
|
162
|
+
elif isinstance(tool, AskFollowupQuestionTool):
|
|
163
|
+
options_text_en = ""
|
|
164
|
+
options_text_zh = ""
|
|
165
|
+
if tool.options:
|
|
166
|
+
options_list_en = "\n".join([f"- {opt}" for opt in tool.options])
|
|
167
|
+
options_list_zh = "\n".join([f"- {opt}" for opt in tool.options]) # 假设选项足够简单,不需要翻译
|
|
168
|
+
options_text_en = f"[dim]Options:[/dim]\n{options_list_en}"
|
|
169
|
+
options_text_zh = f"[dim]选项:[/dim]\n{options_list_zh}"
|
|
170
|
+
context = {
|
|
171
|
+
"question": tool.question,
|
|
172
|
+
"options_text": options_text_zh if lang == 'zh' else options_text_en
|
|
173
|
+
}
|
|
174
|
+
elif isinstance(tool, UseMcpTool):
|
|
175
|
+
args_str = tool.query
|
|
176
|
+
snippet = args_str[:100]
|
|
177
|
+
context = {
|
|
178
|
+
"server_name": tool.server_name,
|
|
179
|
+
"tool_name": tool.tool_name,
|
|
180
|
+
"arguments_snippet": snippet,
|
|
181
|
+
"ellipsis": '...' if len(args_str) > 100 else ''
|
|
182
|
+
}
|
|
183
|
+
else:
|
|
184
|
+
# 未专门处理的工具的通用上下文
|
|
185
|
+
context = tool.model_dump()
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
return format_str_jinja2(template, **context)
|
|
189
|
+
except Exception as e:
|
|
190
|
+
# 格式化错误时的回退处理
|
|
191
|
+
return f"格式化 {tool_type.__name__} 的显示时出错: {e}\n模板: {template}\n上下文: {context}"
|
autocoder/index/entry.py
CHANGED
|
@@ -25,7 +25,7 @@ from autocoder.index.filter.normal_filter import NormalFilter
|
|
|
25
25
|
from autocoder.index.index import IndexManager
|
|
26
26
|
from loguru import logger
|
|
27
27
|
from autocoder.common import SourceCodeList
|
|
28
|
-
from autocoder.common.context_pruner import PruneContext
|
|
28
|
+
from autocoder.common.pruner.context_pruner import PruneContext
|
|
29
29
|
from autocoder.common.action_yml_file_manager import ActionYmlFileManager
|
|
30
30
|
|
|
31
31
|
from autocoder.events.event_manager_singleton import get_event_manager
|
|
@@ -68,7 +68,7 @@ class TokenHelperPlugin(Plugin):
|
|
|
68
68
|
return {
|
|
69
69
|
"token/count": (self.count_tokens_in_project, "Count tokens in all project files"),
|
|
70
70
|
"token/top": (self.show_top_token_files, "Show top N files by token count"),
|
|
71
|
-
"token/file": (self.count_tokens_in_file, "Count tokens in a specific file"),
|
|
71
|
+
"token/file": (self.count_tokens_in_file, "Count tokens in a specific file or directory"),
|
|
72
72
|
"token/summary": (self.show_token_summary, "Show token count summary for the project"),
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -274,17 +274,46 @@ class TokenHelperPlugin(Plugin):
|
|
|
274
274
|
print(f"{token_count.tokens:<10,} {token_count.file_size:<15,} {relative_path}")
|
|
275
275
|
|
|
276
276
|
def count_tokens_in_file(self, args: str) -> None:
|
|
277
|
-
"""Count tokens in a specific file.
|
|
277
|
+
"""Count tokens in a specific file or directory.
|
|
278
278
|
|
|
279
279
|
Args:
|
|
280
|
-
args: Path to the file
|
|
280
|
+
args: Path to the file or directory. If starts with @, remove @ and treat as path.
|
|
281
281
|
"""
|
|
282
282
|
if not args:
|
|
283
|
-
print("Please specify a file path.")
|
|
283
|
+
print("Please specify a file or directory path.")
|
|
284
284
|
return
|
|
285
285
|
|
|
286
|
-
|
|
286
|
+
# Handle @ prefix - remove it and treat as path
|
|
287
|
+
path = args.strip()
|
|
288
|
+
if path.startswith('@'):
|
|
289
|
+
path = path[1:]
|
|
287
290
|
|
|
291
|
+
if not os.path.exists(path):
|
|
292
|
+
print(f"Error: Path '{path}' does not exist.")
|
|
293
|
+
return
|
|
294
|
+
|
|
295
|
+
try:
|
|
296
|
+
if os.path.isfile(path):
|
|
297
|
+
# Handle single file
|
|
298
|
+
self._count_tokens_single_file(path)
|
|
299
|
+
elif os.path.isdir(path):
|
|
300
|
+
# Handle directory recursively
|
|
301
|
+
self._count_tokens_directory(path)
|
|
302
|
+
else:
|
|
303
|
+
print(f"Error: '{path}' is neither a file nor a directory.")
|
|
304
|
+
|
|
305
|
+
except Exception as e:
|
|
306
|
+
print(f"Error counting tokens: {str(e)}")
|
|
307
|
+
|
|
308
|
+
def _count_tokens_single_file(self, file_path: str) -> int:
|
|
309
|
+
"""Count tokens in a single file and display results.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
file_path: Path to the file
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
Number of tokens in the file
|
|
316
|
+
"""
|
|
288
317
|
try:
|
|
289
318
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|
290
319
|
content = f.read()
|
|
@@ -293,10 +322,81 @@ class TokenHelperPlugin(Plugin):
|
|
|
293
322
|
print(f"\nFile: {file_path}")
|
|
294
323
|
print(f"Tokens: {tokens:,}")
|
|
295
324
|
print(f"File size: {len(content):,} bytes")
|
|
296
|
-
|
|
325
|
+
if tokens > 0:
|
|
326
|
+
print(f"Avg bytes per token: {len(content)/tokens:.2f}")
|
|
327
|
+
|
|
328
|
+
return tokens
|
|
297
329
|
|
|
330
|
+
except UnicodeDecodeError:
|
|
331
|
+
print(f"Warning: Skipping binary file '{file_path}'")
|
|
332
|
+
return 0
|
|
298
333
|
except Exception as e:
|
|
299
|
-
print(f"Error
|
|
334
|
+
print(f"Error reading file '{file_path}': {str(e)}")
|
|
335
|
+
return 0
|
|
336
|
+
|
|
337
|
+
def _count_tokens_directory(self, dir_path: str) -> None:
|
|
338
|
+
"""Count tokens in all files within a directory recursively.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
dir_path: Path to the directory
|
|
342
|
+
"""
|
|
343
|
+
total_tokens = 0
|
|
344
|
+
file_count = 0
|
|
345
|
+
processed_files = []
|
|
346
|
+
|
|
347
|
+
print(f"\nScanning directory: {dir_path}")
|
|
348
|
+
|
|
349
|
+
for root, dirs, files in os.walk(dir_path):
|
|
350
|
+
# Skip common ignore directories
|
|
351
|
+
dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ['__pycache__', 'node_modules', 'dist', 'build']]
|
|
352
|
+
|
|
353
|
+
for file in files:
|
|
354
|
+
# Skip hidden files and common binary/generated files
|
|
355
|
+
if file.startswith('.') or file.endswith(('.pyc', '.pyo', '.so', '.dll', '.exe', '.bin')):
|
|
356
|
+
continue
|
|
357
|
+
|
|
358
|
+
file_path = os.path.join(root, file)
|
|
359
|
+
relative_path = os.path.relpath(file_path, dir_path)
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
363
|
+
content = f.read()
|
|
364
|
+
|
|
365
|
+
tokens = count_tokens(content)
|
|
366
|
+
total_tokens += tokens
|
|
367
|
+
file_count += 1
|
|
368
|
+
|
|
369
|
+
processed_files.append({
|
|
370
|
+
'path': relative_path,
|
|
371
|
+
'tokens': tokens,
|
|
372
|
+
'size': len(content)
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
except (UnicodeDecodeError, PermissionError):
|
|
376
|
+
# Skip binary files and files without permission
|
|
377
|
+
continue
|
|
378
|
+
except Exception as e:
|
|
379
|
+
print(f"Warning: Error processing '{relative_path}': {str(e)}")
|
|
380
|
+
continue
|
|
381
|
+
|
|
382
|
+
# Display results
|
|
383
|
+
print(f"\nDirectory scan complete!")
|
|
384
|
+
print(f"Total files processed: {file_count}")
|
|
385
|
+
print(f"Total tokens: {total_tokens:,}")
|
|
386
|
+
|
|
387
|
+
if file_count > 0:
|
|
388
|
+
avg_tokens = total_tokens / file_count
|
|
389
|
+
print(f"Average tokens per file: {avg_tokens:.2f}")
|
|
390
|
+
|
|
391
|
+
# Show top 10 files by token count
|
|
392
|
+
if len(processed_files) > 1:
|
|
393
|
+
print(f"\nTop files by token count:")
|
|
394
|
+
sorted_files = sorted(processed_files, key=lambda x: x['tokens'], reverse=True)
|
|
395
|
+
print(f"{'Tokens':>8} {'Size':>8} {'File'}")
|
|
396
|
+
print(f"{'-'*8} {'-'*8} {'-'*50}")
|
|
397
|
+
|
|
398
|
+
for file_info in sorted_files[:10]:
|
|
399
|
+
print(f"{file_info['tokens']:>8,} {file_info['size']:>8} {file_info['path']}")
|
|
300
400
|
|
|
301
401
|
def show_token_summary(self, args: str) -> None:
|
|
302
402
|
"""Show token count summary by file type.
|
autocoder/run_context.py
CHANGED
|
@@ -10,6 +10,7 @@ class RunMode(Enum):
|
|
|
10
10
|
TERMINAL = auto()
|
|
11
11
|
WEB = auto()
|
|
12
12
|
API = auto()
|
|
13
|
+
CLI = auto()
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class RunContext:
|
|
@@ -52,6 +53,14 @@ class RunContext:
|
|
|
52
53
|
"""Check if running in web mode."""
|
|
53
54
|
return self._mode == RunMode.WEB
|
|
54
55
|
|
|
56
|
+
def is_cli(self) -> bool:
|
|
57
|
+
"""Check if running in cli mode."""
|
|
58
|
+
return self._mode == RunMode.CLI
|
|
59
|
+
|
|
60
|
+
def is_api(self) -> bool:
|
|
61
|
+
"""Check if running in api mode."""
|
|
62
|
+
return self._mode == RunMode.API
|
|
63
|
+
|
|
55
64
|
|
|
56
65
|
def get_run_context() -> RunContext:
|
|
57
66
|
"""
|
autocoder/sdk/__init__.py
CHANGED
|
@@ -4,8 +4,7 @@ Auto-Coder SDK
|
|
|
4
4
|
为第三方开发者提供的 Python SDK,允许通过命令行工具和 Python API 两种方式使用 Auto-Coder 的核心功能。
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from typing import AsyncIterator,
|
|
8
|
-
|
|
7
|
+
from typing import AsyncIterator, Iterator, Optional
|
|
9
8
|
from .core.auto_coder_core import AutoCoderCore
|
|
10
9
|
from .models.options import AutoCodeOptions
|
|
11
10
|
from .models.messages import Message
|
|
@@ -19,33 +18,15 @@ from .exceptions import (
|
|
|
19
18
|
BridgeError,
|
|
20
19
|
ValidationError
|
|
21
20
|
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
# 数据模型
|
|
32
|
-
"AutoCodeOptions",
|
|
33
|
-
"Message",
|
|
34
|
-
"StreamEvent",
|
|
35
|
-
"CodeModificationResult",
|
|
36
|
-
|
|
37
|
-
# 会话管理
|
|
38
|
-
"Session",
|
|
39
|
-
"SessionManager",
|
|
40
|
-
|
|
41
|
-
# 异常
|
|
42
|
-
"AutoCoderSDKError",
|
|
43
|
-
"SessionNotFoundError",
|
|
44
|
-
"InvalidOptionsError",
|
|
45
|
-
"BridgeError",
|
|
46
|
-
"ValidationError",
|
|
47
|
-
]
|
|
48
|
-
|
|
21
|
+
from autocoder.auto_coder_runner import init_project_if_required as init_project_if_required_buildin
|
|
22
|
+
from autocoder.auto_coder_runner import (
|
|
23
|
+
configure as configure_buildin,
|
|
24
|
+
load_tokenizer,
|
|
25
|
+
start as start_engine,
|
|
26
|
+
commit as commit_buildin
|
|
27
|
+
)
|
|
28
|
+
from autocoder.rag.variable_holder import VariableHolder
|
|
29
|
+
from autocoder.utils.llms import get_single_llm
|
|
49
30
|
|
|
50
31
|
async def query(
|
|
51
32
|
prompt: str,
|
|
@@ -110,79 +91,131 @@ def query_sync(
|
|
|
110
91
|
return core.query_sync(f"/new {prompt}", show_terminal)
|
|
111
92
|
|
|
112
93
|
|
|
113
|
-
def
|
|
114
|
-
prompt: str,
|
|
115
|
-
pre_commit: bool = False,
|
|
116
|
-
extra_args: Optional[Dict[str, Any]] = None,
|
|
94
|
+
async def query_with_events(
|
|
95
|
+
prompt: str,
|
|
117
96
|
options: Optional[AutoCodeOptions] = None,
|
|
118
|
-
show_terminal: bool =
|
|
119
|
-
) ->
|
|
97
|
+
show_terminal: bool = False
|
|
98
|
+
) -> AsyncIterator[StreamEvent]:
|
|
120
99
|
"""
|
|
121
|
-
|
|
100
|
+
异步流式查询接口,返回原始事件流
|
|
122
101
|
|
|
123
102
|
Args:
|
|
124
|
-
prompt:
|
|
125
|
-
pre_commit: 是否预提交
|
|
126
|
-
extra_args: 额外参数
|
|
103
|
+
prompt: 查询提示
|
|
127
104
|
options: 配置选项
|
|
128
|
-
show_terminal:
|
|
105
|
+
show_terminal: 是否在终端显示友好的渲染输出(默认为False,避免重复显示)
|
|
129
106
|
|
|
130
|
-
|
|
131
|
-
|
|
107
|
+
Yields:
|
|
108
|
+
StreamEvent: 原始事件流,包含详细的执行过程信息
|
|
132
109
|
|
|
133
110
|
Example:
|
|
134
|
-
>>>
|
|
111
|
+
>>> import asyncio
|
|
112
|
+
>>> from autocoder.sdk import query_with_events, AutoCodeOptions
|
|
135
113
|
>>>
|
|
136
|
-
>>>
|
|
137
|
-
|
|
138
|
-
...
|
|
139
|
-
...
|
|
140
|
-
... options=options
|
|
141
|
-
... )
|
|
114
|
+
>>> async def main():
|
|
115
|
+
... options = AutoCodeOptions(max_turns=3)
|
|
116
|
+
... async for event in query_with_events("Write a hello world function", options):
|
|
117
|
+
... print(f"[{event.event_type}] {event.data}")
|
|
142
118
|
>>>
|
|
143
|
-
>>>
|
|
144
|
-
... print(f"Modified files: {result.modified_files}")
|
|
145
|
-
... else:
|
|
146
|
-
... print(f"Error: {result.error_details}")
|
|
119
|
+
>>> asyncio.run(main())
|
|
147
120
|
"""
|
|
148
|
-
|
|
149
|
-
|
|
121
|
+
if options is None:
|
|
122
|
+
options = AutoCodeOptions()
|
|
123
|
+
core = AutoCoderCore(options)
|
|
124
|
+
|
|
125
|
+
# 使用桥接层直接获取事件流
|
|
126
|
+
import asyncio
|
|
127
|
+
loop = asyncio.get_event_loop()
|
|
128
|
+
|
|
129
|
+
# 在线程池中执行同步调用
|
|
130
|
+
event_stream = await loop.run_in_executor(
|
|
131
|
+
core._executor,
|
|
132
|
+
core._sync_run_auto_command,
|
|
133
|
+
f"/new {prompt}"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# 直接返回事件流
|
|
137
|
+
for event in event_stream:
|
|
138
|
+
# 可选择性地渲染到终端
|
|
139
|
+
if show_terminal:
|
|
140
|
+
core._render_stream_event(event, show_terminal)
|
|
141
|
+
yield event
|
|
150
142
|
|
|
151
143
|
|
|
152
|
-
|
|
153
|
-
prompt: str,
|
|
154
|
-
pre_commit: bool = False,
|
|
155
|
-
extra_args: Optional[Dict[str, Any]] = None,
|
|
144
|
+
def query_with_events_sync(
|
|
145
|
+
prompt: str,
|
|
156
146
|
options: Optional[AutoCodeOptions] = None,
|
|
157
|
-
show_terminal: bool =
|
|
158
|
-
) ->
|
|
147
|
+
show_terminal: bool = False
|
|
148
|
+
) -> Iterator[StreamEvent]:
|
|
159
149
|
"""
|
|
160
|
-
|
|
150
|
+
同步查询接口,返回原始事件流生成器
|
|
161
151
|
|
|
162
152
|
Args:
|
|
163
|
-
prompt:
|
|
164
|
-
pre_commit: 是否预提交
|
|
165
|
-
extra_args: 额外参数
|
|
153
|
+
prompt: 查询提示
|
|
166
154
|
options: 配置选项
|
|
167
|
-
show_terminal:
|
|
155
|
+
show_terminal: 是否在终端显示友好的渲染输出(默认为False,避免重复显示)
|
|
168
156
|
|
|
169
|
-
|
|
170
|
-
StreamEvent:
|
|
157
|
+
Returns:
|
|
158
|
+
Iterator[StreamEvent]: 原始事件流生成器,包含详细的执行过程信息
|
|
171
159
|
|
|
172
160
|
Example:
|
|
173
|
-
>>> import
|
|
174
|
-
>>> from autocoder.sdk import modify_code_stream, AutoCodeOptions
|
|
161
|
+
>>> from autocoder.sdk import query_with_events_sync, AutoCodeOptions
|
|
175
162
|
>>>
|
|
176
|
-
>>>
|
|
177
|
-
|
|
178
|
-
...
|
|
179
|
-
... "Refactor the user authentication module",
|
|
180
|
-
... options=options
|
|
181
|
-
... ):
|
|
182
|
-
... print(f"[{event.event_type}] {event.data}")
|
|
183
|
-
>>>
|
|
184
|
-
>>> asyncio.run(main())
|
|
163
|
+
>>> options = AutoCodeOptions(max_turns=1)
|
|
164
|
+
>>> for event in query_with_events_sync("Write a simple calculator function", options):
|
|
165
|
+
... print(f"[{event.event_type}] {event.data}")
|
|
185
166
|
"""
|
|
186
|
-
|
|
187
|
-
|
|
167
|
+
if options is None:
|
|
168
|
+
options = AutoCodeOptions()
|
|
169
|
+
core = AutoCoderCore(options)
|
|
170
|
+
|
|
171
|
+
# 直接调用同步方法获取事件流
|
|
172
|
+
event_stream = core._sync_run_auto_command(f"/new {prompt}")
|
|
173
|
+
|
|
174
|
+
# 直接返回事件流
|
|
175
|
+
for event in event_stream:
|
|
176
|
+
# 可选择性地渲染到终端
|
|
177
|
+
if show_terminal:
|
|
178
|
+
core._render_stream_event(event, show_terminal)
|
|
188
179
|
yield event
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def init_project_if_required(target_dir: str,project_type = ".py,.ts"):
|
|
183
|
+
init_project_if_required_buildin(target_dir,project_type)
|
|
184
|
+
if not VariableHolder.TOKENIZER_MODEL:
|
|
185
|
+
load_tokenizer()
|
|
186
|
+
start_engine()
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def configure(key:str,value:str):
|
|
190
|
+
configure_buildin(f"{key}:{value}")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def get_llm(model:str,product_mode:str="lite"):
|
|
194
|
+
return get_single_llm(model,product_mode)
|
|
195
|
+
|
|
196
|
+
def commit(query: Optional[str] = None):
|
|
197
|
+
commit_buildin(query)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
__version__ = "1.0.0"
|
|
201
|
+
__all__ = [
|
|
202
|
+
"query",
|
|
203
|
+
"query_sync",
|
|
204
|
+
"query_with_events",
|
|
205
|
+
"query_with_events_sync",
|
|
206
|
+
"get_llm",
|
|
207
|
+
"configure",
|
|
208
|
+
"init_project_if_required",
|
|
209
|
+
"AutoCodeOptions",
|
|
210
|
+
"Message",
|
|
211
|
+
"StreamEvent",
|
|
212
|
+
"CodeModificationResult",
|
|
213
|
+
"Session",
|
|
214
|
+
"SessionManager",
|
|
215
|
+
"AutoCoderSDKError",
|
|
216
|
+
"SessionNotFoundError",
|
|
217
|
+
"InvalidOptionsError",
|
|
218
|
+
"BridgeError",
|
|
219
|
+
"ValidationError",
|
|
220
|
+
"commit",
|
|
221
|
+
]
|
autocoder/sdk/cli/main.py
CHANGED
|
@@ -18,6 +18,11 @@ except ImportError:
|
|
|
18
18
|
from .options import CLIOptions, CLIResult
|
|
19
19
|
from .handlers import PrintModeHandler
|
|
20
20
|
from ..exceptions import AutoCoderSDKError
|
|
21
|
+
from autocoder.run_context import get_run_context,RunMode
|
|
22
|
+
|
|
23
|
+
# 设置运行模式为终端模式
|
|
24
|
+
get_run_context().set_mode(RunMode.CLI)
|
|
25
|
+
|
|
21
26
|
|
|
22
27
|
|
|
23
28
|
class AutoCoderCLI:
|