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
autocoder/rags.py
CHANGED
|
@@ -369,30 +369,3 @@ def update_rag_status(name: str, status: str, **kwargs) -> bool:
|
|
|
369
369
|
return rag_manager.update_status(name, status, **kwargs)
|
|
370
370
|
|
|
371
371
|
|
|
372
|
-
# 使用示例
|
|
373
|
-
if __name__ == "__main__":
|
|
374
|
-
# 创建配置
|
|
375
|
-
config = {
|
|
376
|
-
"model": "deepseek_chat",
|
|
377
|
-
"tokenizer_path": "/Users/allwefantasy/Downloads/tokenizer.json",
|
|
378
|
-
"doc_dir": "/path/to/docs",
|
|
379
|
-
"rag_doc_filter_relevance": 2.0,
|
|
380
|
-
"host": "0.0.0.0",
|
|
381
|
-
"port": 8000,
|
|
382
|
-
"enable_hybrid_index": False,
|
|
383
|
-
"required_exts": ".md,.rst",
|
|
384
|
-
"disable_inference_enhance": True
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
# 创建
|
|
388
|
-
create_rag_config("test_rag", config)
|
|
389
|
-
|
|
390
|
-
# 读取
|
|
391
|
-
all_configs = get_rag_config()
|
|
392
|
-
specific_config = get_rag_config("test_rag")
|
|
393
|
-
|
|
394
|
-
# 更新
|
|
395
|
-
update_rag_config("test_rag", {"status": "running", "port": 8001})
|
|
396
|
-
|
|
397
|
-
# 删除
|
|
398
|
-
delete_rag_config("test_rag")
|
autocoder/run_context.py
CHANGED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auto-Coder SDK
|
|
3
|
+
|
|
4
|
+
为第三方开发者提供的 Python SDK,允许通过命令行工具和 Python API 两种方式使用 Auto-Coder 的核心功能。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import AsyncIterator, Optional, Dict, Any
|
|
8
|
+
|
|
9
|
+
from .core.auto_coder_core import AutoCoderCore
|
|
10
|
+
from .models.options import AutoCodeOptions
|
|
11
|
+
from .models.messages import Message
|
|
12
|
+
from .models.responses import StreamEvent, CodeModificationResult
|
|
13
|
+
from .session.session import Session
|
|
14
|
+
from .session.session_manager import SessionManager
|
|
15
|
+
from .exceptions import (
|
|
16
|
+
AutoCoderSDKError,
|
|
17
|
+
SessionNotFoundError,
|
|
18
|
+
InvalidOptionsError,
|
|
19
|
+
BridgeError,
|
|
20
|
+
ValidationError
|
|
21
|
+
)
|
|
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
|
+
|
|
49
|
+
|
|
50
|
+
async def query(
|
|
51
|
+
prompt: str,
|
|
52
|
+
options: Optional[AutoCodeOptions] = None,
|
|
53
|
+
show_terminal: bool = True
|
|
54
|
+
) -> AsyncIterator[Message]:
|
|
55
|
+
"""
|
|
56
|
+
异步流式查询接口
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
prompt: 查询提示
|
|
60
|
+
options: 配置选项
|
|
61
|
+
show_terminal: 是否在终端显示友好的渲染输出
|
|
62
|
+
|
|
63
|
+
Yields:
|
|
64
|
+
Message: 响应消息流
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
>>> import asyncio
|
|
68
|
+
>>> from autocoder.sdk import query, AutoCodeOptions
|
|
69
|
+
>>>
|
|
70
|
+
>>> async def main():
|
|
71
|
+
... options = AutoCodeOptions(max_turns=3)
|
|
72
|
+
... async for message in query("Write a hello world function", options):
|
|
73
|
+
... print(f"[{message.role}] {message.content}")
|
|
74
|
+
>>>
|
|
75
|
+
>>> asyncio.run(main())
|
|
76
|
+
"""
|
|
77
|
+
if options is None:
|
|
78
|
+
options = AutoCodeOptions()
|
|
79
|
+
core = AutoCoderCore(options)
|
|
80
|
+
async for message in core.query_stream(f"/new {prompt}", show_terminal):
|
|
81
|
+
yield message
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def query_sync(
|
|
85
|
+
prompt: str,
|
|
86
|
+
options: Optional[AutoCodeOptions] = None,
|
|
87
|
+
show_terminal: bool = True
|
|
88
|
+
) -> str:
|
|
89
|
+
"""
|
|
90
|
+
同步查询接口
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
prompt: 查询提示
|
|
94
|
+
options: 配置选项
|
|
95
|
+
show_terminal: 是否在终端显示友好的渲染输出
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
str: 响应内容
|
|
99
|
+
|
|
100
|
+
Example:
|
|
101
|
+
>>> from autocoder.sdk import query_sync, AutoCodeOptions
|
|
102
|
+
>>>
|
|
103
|
+
>>> options = AutoCodeOptions(max_turns=1)
|
|
104
|
+
>>> response = query_sync("Write a simple calculator function", options)
|
|
105
|
+
>>> print(response)
|
|
106
|
+
"""
|
|
107
|
+
if options is None:
|
|
108
|
+
options = AutoCodeOptions()
|
|
109
|
+
core = AutoCoderCore(options)
|
|
110
|
+
return core.query_sync(f"/new {prompt}", show_terminal)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def modify_code(
|
|
114
|
+
prompt: str,
|
|
115
|
+
pre_commit: bool = False,
|
|
116
|
+
extra_args: Optional[Dict[str, Any]] = None,
|
|
117
|
+
options: Optional[AutoCodeOptions] = None,
|
|
118
|
+
show_terminal: bool = True
|
|
119
|
+
) -> CodeModificationResult:
|
|
120
|
+
"""
|
|
121
|
+
代码修改接口
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
prompt: 修改提示
|
|
125
|
+
pre_commit: 是否预提交
|
|
126
|
+
extra_args: 额外参数
|
|
127
|
+
options: 配置选项
|
|
128
|
+
show_terminal: 是否在终端显示友好的渲染输出
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
CodeModificationResult: 修改结果
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
>>> from autocoder.sdk import modify_code, AutoCodeOptions
|
|
135
|
+
>>>
|
|
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
|
+
... )
|
|
142
|
+
>>>
|
|
143
|
+
>>> if result.success:
|
|
144
|
+
... print(f"Modified files: {result.modified_files}")
|
|
145
|
+
... else:
|
|
146
|
+
... print(f"Error: {result.error_details}")
|
|
147
|
+
"""
|
|
148
|
+
core = AutoCoderCore(options or AutoCodeOptions())
|
|
149
|
+
return core.modify_code(prompt, pre_commit, extra_args, show_terminal)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
async def modify_code_stream(
|
|
153
|
+
prompt: str,
|
|
154
|
+
pre_commit: bool = False,
|
|
155
|
+
extra_args: Optional[Dict[str, Any]] = None,
|
|
156
|
+
options: Optional[AutoCodeOptions] = None,
|
|
157
|
+
show_terminal: bool = True
|
|
158
|
+
) -> AsyncIterator[StreamEvent]:
|
|
159
|
+
"""
|
|
160
|
+
异步流式代码修改接口
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
prompt: 修改提示
|
|
164
|
+
pre_commit: 是否预提交
|
|
165
|
+
extra_args: 额外参数
|
|
166
|
+
options: 配置选项
|
|
167
|
+
show_terminal: 是否在终端显示友好的渲染输出
|
|
168
|
+
|
|
169
|
+
Yields:
|
|
170
|
+
StreamEvent: 修改事件流
|
|
171
|
+
|
|
172
|
+
Example:
|
|
173
|
+
>>> import asyncio
|
|
174
|
+
>>> from autocoder.sdk import modify_code_stream, AutoCodeOptions
|
|
175
|
+
>>>
|
|
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())
|
|
185
|
+
"""
|
|
186
|
+
core = AutoCoderCore(options or AutoCodeOptions())
|
|
187
|
+
async for event in core.modify_code_stream(prompt, pre_commit, extra_args, show_terminal):
|
|
188
|
+
yield event
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auto-Coder CLI 模块
|
|
3
|
+
|
|
4
|
+
提供命令行接口,允许用户通过终端使用 Auto-Coder 的核心功能。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .main import AutoCoderCLI
|
|
8
|
+
from .options import CLIOptions, CLIResult
|
|
9
|
+
|
|
10
|
+
# 导出main函数作为入口点
|
|
11
|
+
def main():
|
|
12
|
+
"""CLI主入口点函数"""
|
|
13
|
+
return AutoCoderCLI.main()
|
|
14
|
+
|
|
15
|
+
__all__ = ["AutoCoderCLI", "CLIOptions", "CLIResult", "main"]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
CLI 模块主入口点
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
import argparse
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
def main():
|
|
11
|
+
"""主入口点,路由到不同的 CLI 功能"""
|
|
12
|
+
|
|
13
|
+
# 检查是否是自动补全相关的命令
|
|
14
|
+
if len(sys.argv) > 1 and sys.argv[1] in ["install", "uninstall", "test"]:
|
|
15
|
+
# 路由到自动补全安装工具
|
|
16
|
+
from .install_completion import main as install_main
|
|
17
|
+
install_main()
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
# 默认显示自动补全工具的帮助
|
|
21
|
+
from .install_completion import main as install_main
|
|
22
|
+
install_main()
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
main()
|
|
26
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
#!/usr/bin/env python3
|
|
4
|
+
"""
|
|
5
|
+
Auto-Coder CLI 自动补全包装器
|
|
6
|
+
|
|
7
|
+
提供一个独立的补全脚本,不依赖于特定的 shell 环境
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
def main():
|
|
14
|
+
"""主函数,处理自动补全请求"""
|
|
15
|
+
try:
|
|
16
|
+
# 设置环境变量以启用 argcomplete
|
|
17
|
+
os.environ.setdefault('_ARGCOMPLETE_COMPLETE', 'complete')
|
|
18
|
+
|
|
19
|
+
# 导入并运行 CLI
|
|
20
|
+
from autocoder.sdk.cli.main import AutoCoderCLI
|
|
21
|
+
|
|
22
|
+
# 模拟 auto-coder.run 命令
|
|
23
|
+
sys.argv[0] = 'auto-coder.run'
|
|
24
|
+
|
|
25
|
+
# 解析参数(这会触发自动补全)
|
|
26
|
+
AutoCoderCLI.parse_args()
|
|
27
|
+
|
|
28
|
+
except SystemExit:
|
|
29
|
+
# argcomplete 会调用 sys.exit,这是正常的
|
|
30
|
+
pass
|
|
31
|
+
except Exception as e:
|
|
32
|
+
# 在补全过程中出现错误,静默处理
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
if __name__ == '__main__':
|
|
36
|
+
main()
|
|
37
|
+
|
|
38
|
+
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""
|
|
2
|
+
输出格式化器模块
|
|
3
|
+
|
|
4
|
+
提供不同格式的输出处理,支持文本、JSON和流式JSON格式。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import asyncio
|
|
9
|
+
from typing import Any, Dict, AsyncIterator, Union, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class OutputFormatter:
|
|
13
|
+
"""输出格式化器,处理不同格式的输出。"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, verbose: bool = False):
|
|
16
|
+
"""
|
|
17
|
+
初始化输出格式化器。
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
verbose: 是否输出详细信息
|
|
21
|
+
"""
|
|
22
|
+
self.verbose = verbose
|
|
23
|
+
|
|
24
|
+
def format_text(self, content: Union[str, Dict[str, Any]]) -> str:
|
|
25
|
+
"""
|
|
26
|
+
格式化为文本输出。
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
content: 要格式化的内容,可以是字符串或字典
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
格式化后的文本
|
|
33
|
+
"""
|
|
34
|
+
if isinstance(content, dict):
|
|
35
|
+
# 如果是字典,提取主要内容
|
|
36
|
+
if "content" in content:
|
|
37
|
+
result = content["content"]
|
|
38
|
+
else:
|
|
39
|
+
result = str(content)
|
|
40
|
+
else:
|
|
41
|
+
result = str(content)
|
|
42
|
+
|
|
43
|
+
if self.verbose:
|
|
44
|
+
# 在详细模式下添加调试信息
|
|
45
|
+
debug_info = self._get_debug_info(content)
|
|
46
|
+
if debug_info:
|
|
47
|
+
result += f"\n\n[DEBUG]\n{debug_info}"
|
|
48
|
+
|
|
49
|
+
return result
|
|
50
|
+
|
|
51
|
+
def format_json(self, content: Any) -> str:
|
|
52
|
+
"""
|
|
53
|
+
格式化为JSON输出。
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
content: 要格式化的内容
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
JSON格式的字符串
|
|
60
|
+
"""
|
|
61
|
+
# 确保内容可以被序列化为JSON
|
|
62
|
+
if isinstance(content, str):
|
|
63
|
+
try:
|
|
64
|
+
# 尝试解析为JSON
|
|
65
|
+
parsed = json.loads(content)
|
|
66
|
+
content = parsed
|
|
67
|
+
except json.JSONDecodeError:
|
|
68
|
+
# 如果不是有效的JSON,则包装为字典
|
|
69
|
+
content = {"content": content}
|
|
70
|
+
|
|
71
|
+
# 添加调试信息(如果启用详细模式)
|
|
72
|
+
if self.verbose and isinstance(content, dict):
|
|
73
|
+
content["debug"] = self._get_debug_info_dict(content)
|
|
74
|
+
|
|
75
|
+
return json.dumps(content, ensure_ascii=False, indent=2)
|
|
76
|
+
|
|
77
|
+
async def format_stream_json(self, stream: AsyncIterator[Any]) -> AsyncIterator[str]:
|
|
78
|
+
"""
|
|
79
|
+
格式化为流式JSON输出。
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
stream: 异步迭代器,提供流式内容
|
|
83
|
+
|
|
84
|
+
Yields:
|
|
85
|
+
JSON格式的字符串,每行一个JSON对象
|
|
86
|
+
"""
|
|
87
|
+
async for item in stream:
|
|
88
|
+
# 处理不同类型的项
|
|
89
|
+
if isinstance(item, dict):
|
|
90
|
+
output = item
|
|
91
|
+
elif isinstance(item, str):
|
|
92
|
+
try:
|
|
93
|
+
output = json.loads(item)
|
|
94
|
+
except json.JSONDecodeError:
|
|
95
|
+
output = {"content": item, "type": "text"}
|
|
96
|
+
else:
|
|
97
|
+
output = {"content": str(item), "type": "text"}
|
|
98
|
+
|
|
99
|
+
# 添加调试信息(如果启用详细模式)
|
|
100
|
+
if self.verbose:
|
|
101
|
+
output["debug"] = self._get_debug_info_dict(output)
|
|
102
|
+
|
|
103
|
+
yield json.dumps(output, ensure_ascii=False)
|
|
104
|
+
|
|
105
|
+
def _get_debug_info(self, content: Any) -> str:
|
|
106
|
+
"""
|
|
107
|
+
获取调试信息的文本表示。
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
content: 内容对象
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
调试信息文本
|
|
114
|
+
"""
|
|
115
|
+
debug_dict = self._get_debug_info_dict(content)
|
|
116
|
+
if debug_dict:
|
|
117
|
+
return json.dumps(debug_dict, ensure_ascii=False, indent=2)
|
|
118
|
+
return ""
|
|
119
|
+
|
|
120
|
+
def _get_debug_info_dict(self, content: Any) -> Dict[str, Any]:
|
|
121
|
+
"""
|
|
122
|
+
获取调试信息的字典表示。
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
content: 内容对象
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
调试信息字典
|
|
129
|
+
"""
|
|
130
|
+
debug_info = {}
|
|
131
|
+
|
|
132
|
+
# 从内容中提取元数据
|
|
133
|
+
if isinstance(content, dict):
|
|
134
|
+
if "metadata" in content:
|
|
135
|
+
debug_info["metadata"] = content["metadata"]
|
|
136
|
+
|
|
137
|
+
# 提取令牌计数信息
|
|
138
|
+
if "tokens" in content:
|
|
139
|
+
debug_info["tokens"] = content["tokens"]
|
|
140
|
+
elif "metadata" in content and "tokens" in content["metadata"]:
|
|
141
|
+
debug_info["tokens"] = content["metadata"]["tokens"]
|
|
142
|
+
|
|
143
|
+
# 提取模型信息
|
|
144
|
+
if "model" in content:
|
|
145
|
+
debug_info["model"] = content["model"]
|
|
146
|
+
elif "metadata" in content and "model" in content["metadata"]:
|
|
147
|
+
debug_info["model"] = content["metadata"]["model"]
|
|
148
|
+
|
|
149
|
+
# 提取时间信息
|
|
150
|
+
if "timestamp" in content:
|
|
151
|
+
debug_info["timestamp"] = content["timestamp"]
|
|
152
|
+
elif "metadata" in content and "timestamp" in content["metadata"]:
|
|
153
|
+
debug_info["timestamp"] = content["metadata"]["timestamp"]
|
|
154
|
+
|
|
155
|
+
return debug_info
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class InputFormatter:
|
|
159
|
+
"""输入格式化器,处理不同格式的输入。"""
|
|
160
|
+
|
|
161
|
+
def format_text(self, content: str) -> str:
|
|
162
|
+
"""
|
|
163
|
+
格式化文本输入。
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
content: 输入文本
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
格式化后的文本
|
|
170
|
+
"""
|
|
171
|
+
return content.strip()
|
|
172
|
+
|
|
173
|
+
def format_json(self, content: str) -> Dict[str, Any]:
|
|
174
|
+
"""
|
|
175
|
+
格式化JSON输入。
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
content: JSON格式的输入字符串
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
解析后的字典
|
|
182
|
+
|
|
183
|
+
Raises:
|
|
184
|
+
ValueError: 如果输入不是有效的JSON
|
|
185
|
+
"""
|
|
186
|
+
try:
|
|
187
|
+
return json.loads(content)
|
|
188
|
+
except json.JSONDecodeError as e:
|
|
189
|
+
raise ValueError(f"无效的JSON输入: {str(e)}")
|
|
190
|
+
|
|
191
|
+
async def format_stream_json(self, content: str) -> AsyncIterator[Dict[str, Any]]:
|
|
192
|
+
"""
|
|
193
|
+
格式化流式JSON输入。
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
content: 包含多行JSON对象的字符串
|
|
197
|
+
|
|
198
|
+
Yields:
|
|
199
|
+
解析后的字典
|
|
200
|
+
|
|
201
|
+
Raises:
|
|
202
|
+
ValueError: 如果某行不是有效的JSON
|
|
203
|
+
"""
|
|
204
|
+
for line in content.strip().split("\n"):
|
|
205
|
+
if not line.strip():
|
|
206
|
+
continue
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
yield json.loads(line)
|
|
210
|
+
except json.JSONDecodeError as e:
|
|
211
|
+
raise ValueError(f"无效的JSON行: {line}, 错误: {str(e)}")
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""
|
|
2
|
+
命令处理器模块
|
|
3
|
+
|
|
4
|
+
提供统一的命令处理器,支持单次运行和会话复用功能。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import asyncio
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional, Dict, Any, List, Union
|
|
11
|
+
|
|
12
|
+
from ..core import AutoCoderCore
|
|
13
|
+
from ..models import AutoCodeOptions, Message
|
|
14
|
+
from ..exceptions import AutoCoderSDKError
|
|
15
|
+
from .options import CLIOptions, CLIResult
|
|
16
|
+
from .formatters import OutputFormatter, InputFormatter
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CommandHandler:
|
|
20
|
+
"""命令处理器基类,提供通用功能。"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, options: CLIOptions, cwd: Optional[str] = None):
|
|
23
|
+
"""
|
|
24
|
+
初始化命令处理器。
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
options: CLI选项
|
|
28
|
+
cwd: 当前工作目录,如果为None则使用系统当前目录
|
|
29
|
+
"""
|
|
30
|
+
self.options = options
|
|
31
|
+
self.cwd = Path(cwd) if cwd else Path.cwd()
|
|
32
|
+
self.output_formatter = OutputFormatter(verbose=options.verbose)
|
|
33
|
+
self.input_formatter = InputFormatter()
|
|
34
|
+
|
|
35
|
+
def _create_core_options(self) -> AutoCodeOptions:
|
|
36
|
+
"""
|
|
37
|
+
创建核心选项。
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
AutoCodeOptions实例
|
|
41
|
+
"""
|
|
42
|
+
return AutoCodeOptions(
|
|
43
|
+
max_turns=self.options.max_turns,
|
|
44
|
+
system_prompt=self.options.system_prompt,
|
|
45
|
+
cwd=str(self.cwd),
|
|
46
|
+
allowed_tools=self.options.allowed_tools,
|
|
47
|
+
permission_mode=self.options.permission_mode,
|
|
48
|
+
output_format=self.options.output_format,
|
|
49
|
+
stream=self.options.output_format.startswith("stream"),
|
|
50
|
+
session_id=self.options.resume_session,
|
|
51
|
+
continue_session=self.options.continue_session,
|
|
52
|
+
model=self.options.model,
|
|
53
|
+
pr=self.options.pr
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def _get_prompt(self) -> str:
|
|
57
|
+
"""
|
|
58
|
+
获取提示内容,如果未提供则从stdin读取。
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
提示内容
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
ValueError: 如果未提供提示且stdin为空
|
|
65
|
+
"""
|
|
66
|
+
if self.options.prompt:
|
|
67
|
+
return self.options.prompt
|
|
68
|
+
|
|
69
|
+
# 从stdin读取
|
|
70
|
+
if not sys.stdin.isatty():
|
|
71
|
+
content = sys.stdin.read()
|
|
72
|
+
if not content.strip():
|
|
73
|
+
raise ValueError("未提供提示内容且标准输入为空")
|
|
74
|
+
|
|
75
|
+
# 根据输入格式处理
|
|
76
|
+
if self.options.input_format == "text":
|
|
77
|
+
return self.input_formatter.format_text(content)
|
|
78
|
+
elif self.options.input_format == "json":
|
|
79
|
+
result = self.input_formatter.format_json(content)
|
|
80
|
+
# 尝试提取提示内容
|
|
81
|
+
if isinstance(result, dict):
|
|
82
|
+
if "prompt" in result:
|
|
83
|
+
return result["prompt"]
|
|
84
|
+
elif "message" in result:
|
|
85
|
+
message = result["message"]
|
|
86
|
+
if isinstance(message, dict) and "content" in message:
|
|
87
|
+
return message["content"]
|
|
88
|
+
elif isinstance(message, str):
|
|
89
|
+
return message
|
|
90
|
+
return content # 如果无法提取,则返回原始内容
|
|
91
|
+
else:
|
|
92
|
+
# 对于流式输入,暂时只支持直接传递
|
|
93
|
+
return content
|
|
94
|
+
else:
|
|
95
|
+
raise ValueError("未提供提示内容且没有标准输入")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class PrintModeHandler(CommandHandler):
|
|
99
|
+
"""统一的命令处理器,支持单次运行和会话复用。"""
|
|
100
|
+
|
|
101
|
+
def handle(self) -> CLIResult:
|
|
102
|
+
"""
|
|
103
|
+
处理命令,支持会话复用。
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
命令执行结果
|
|
107
|
+
"""
|
|
108
|
+
try:
|
|
109
|
+
prompt = self._get_prompt()
|
|
110
|
+
core_options = self._create_core_options()
|
|
111
|
+
core = AutoCoderCore(core_options)
|
|
112
|
+
|
|
113
|
+
# 根据会话参数构建完整的 prompt
|
|
114
|
+
final_prompt = self._build_prompt_with_session_context(prompt)
|
|
115
|
+
|
|
116
|
+
# 根据输出格式选择不同的处理方式
|
|
117
|
+
if self.options.output_format == "stream-json":
|
|
118
|
+
# 流式JSON输出
|
|
119
|
+
result = asyncio.run(self._handle_stream(core, final_prompt))
|
|
120
|
+
else:
|
|
121
|
+
# 同步查询
|
|
122
|
+
response = core.query_sync(final_prompt)
|
|
123
|
+
|
|
124
|
+
# 格式化输出
|
|
125
|
+
if self.options.output_format == "json":
|
|
126
|
+
result = self.output_formatter.format_json(response)
|
|
127
|
+
else:
|
|
128
|
+
result = self.output_formatter.format_text(response)
|
|
129
|
+
|
|
130
|
+
return CLIResult(success=True, output=result)
|
|
131
|
+
|
|
132
|
+
except Exception as e:
|
|
133
|
+
return CLIResult(success=False, error=str(e))
|
|
134
|
+
|
|
135
|
+
def _build_prompt_with_session_context(self, prompt: str) -> str:
|
|
136
|
+
"""
|
|
137
|
+
根据会话参数构建完整的 prompt。
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
prompt: 原始提示内容
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
str: 构建后的完整提示
|
|
144
|
+
"""
|
|
145
|
+
if self.options.continue_session:
|
|
146
|
+
# 继续当前对话
|
|
147
|
+
return f"{prompt}" if prompt else ""
|
|
148
|
+
elif self.options.resume_session:
|
|
149
|
+
# 恢复特定会话
|
|
150
|
+
return f"/resume {self.options.resume_session} {prompt}" if prompt else f"/resume {self.options.resume_session}"
|
|
151
|
+
else:
|
|
152
|
+
# 创建新对话
|
|
153
|
+
return f"/new {prompt}"
|
|
154
|
+
|
|
155
|
+
async def _handle_stream(self, core: AutoCoderCore, prompt: str) -> str:
|
|
156
|
+
"""
|
|
157
|
+
处理流式输出。
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
core: AutoCoderCore实例
|
|
161
|
+
prompt: 提示内容
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
处理结果
|
|
165
|
+
"""
|
|
166
|
+
result = []
|
|
167
|
+
async for message in core.query_stream(prompt):
|
|
168
|
+
formatted = await anext(self.output_formatter.format_stream_json([message]))
|
|
169
|
+
result.append(formatted)
|
|
170
|
+
# 实时输出到stdout
|
|
171
|
+
print(formatted, flush=True)
|
|
172
|
+
|
|
173
|
+
return "\n".join(result)
|
|
174
|
+
|
|
175
|
+
|