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,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
- file_path = args.strip()
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
- print(f"Avg bytes per token: {len(content)/tokens:.2f}")
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 counting tokens in file: {str(e)}")
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, Optional, Dict, Any
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
- __version__ = "1.0.0"
24
- __all__ = [
25
- # 核心功能
26
- "query",
27
- "query_sync",
28
- "modify_code",
29
- "modify_code_stream",
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 modify_code(
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 = True
119
- ) -> CodeModificationResult:
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
- Returns:
131
- CodeModificationResult: 修改结果
107
+ Yields:
108
+ StreamEvent: 原始事件流,包含详细的执行过程信息
132
109
 
133
110
  Example:
134
- >>> from autocoder.sdk import modify_code, AutoCodeOptions
111
+ >>> import asyncio
112
+ >>> from autocoder.sdk import query_with_events, AutoCodeOptions
135
113
  >>>
136
- >>> options = AutoCodeOptions(cwd="/path/to/project")
137
- >>> result = modify_code(
138
- ... "Add error handling to the main function",
139
- ... pre_commit=False,
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
- >>> if result.success:
144
- ... print(f"Modified files: {result.modified_files}")
145
- ... else:
146
- ... print(f"Error: {result.error_details}")
119
+ >>> asyncio.run(main())
147
120
  """
148
- core = AutoCoderCore(options or AutoCodeOptions())
149
- return core.modify_code(prompt, pre_commit, extra_args, show_terminal)
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
- async def modify_code_stream(
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 = True
158
- ) -> AsyncIterator[StreamEvent]:
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
- Yields:
170
- StreamEvent: 修改事件流
157
+ Returns:
158
+ Iterator[StreamEvent]: 原始事件流生成器,包含详细的执行过程信息
171
159
 
172
160
  Example:
173
- >>> import asyncio
174
- >>> from autocoder.sdk import modify_code_stream, AutoCodeOptions
161
+ >>> from autocoder.sdk import query_with_events_sync, AutoCodeOptions
175
162
  >>>
176
- >>> async def main():
177
- ... options = AutoCodeOptions(cwd="/path/to/project")
178
- ... async for event in modify_code_stream(
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
- core = AutoCoderCore(options or AutoCodeOptions())
187
- async for event in core.modify_code_stream(prompt, pre_commit, extra_args, show_terminal):
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: