auto-coder 0.1.362__py3-none-any.whl → 0.1.364__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.362.dist-info → auto_coder-0.1.364.dist-info}/METADATA +2 -2
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/RECORD +65 -22
- autocoder/agent/base_agentic/__init__.py +0 -0
- autocoder/agent/base_agentic/agent_hub.py +169 -0
- autocoder/agent/base_agentic/agentic_lang.py +112 -0
- autocoder/agent/base_agentic/agentic_tool_display.py +180 -0
- autocoder/agent/base_agentic/base_agent.py +1582 -0
- autocoder/agent/base_agentic/default_tools.py +683 -0
- autocoder/agent/base_agentic/test_base_agent.py +82 -0
- autocoder/agent/base_agentic/tool_registry.py +425 -0
- autocoder/agent/base_agentic/tools/__init__.py +12 -0
- autocoder/agent/base_agentic/tools/ask_followup_question_tool_resolver.py +72 -0
- autocoder/agent/base_agentic/tools/attempt_completion_tool_resolver.py +37 -0
- autocoder/agent/base_agentic/tools/base_tool_resolver.py +35 -0
- autocoder/agent/base_agentic/tools/example_tool_resolver.py +46 -0
- autocoder/agent/base_agentic/tools/execute_command_tool_resolver.py +72 -0
- autocoder/agent/base_agentic/tools/list_files_tool_resolver.py +110 -0
- autocoder/agent/base_agentic/tools/plan_mode_respond_tool_resolver.py +35 -0
- autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +54 -0
- autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +156 -0
- autocoder/agent/base_agentic/tools/search_files_tool_resolver.py +134 -0
- autocoder/agent/base_agentic/tools/talk_to_group_tool_resolver.py +96 -0
- autocoder/agent/base_agentic/tools/talk_to_tool_resolver.py +79 -0
- autocoder/agent/base_agentic/tools/use_mcp_tool_resolver.py +44 -0
- autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +58 -0
- autocoder/agent/base_agentic/types.py +189 -0
- autocoder/agent/base_agentic/utils.py +100 -0
- autocoder/auto_coder_runner.py +6 -4
- autocoder/chat/conf_command.py +11 -10
- autocoder/common/__init__.py +2 -0
- autocoder/common/file_checkpoint/__init__.py +21 -0
- autocoder/common/file_checkpoint/backup.py +264 -0
- autocoder/common/file_checkpoint/examples.py +217 -0
- autocoder/common/file_checkpoint/manager.py +404 -0
- autocoder/common/file_checkpoint/models.py +156 -0
- autocoder/common/file_checkpoint/store.py +383 -0
- autocoder/common/file_checkpoint/test_backup.py +242 -0
- autocoder/common/file_checkpoint/test_manager.py +570 -0
- autocoder/common/file_checkpoint/test_models.py +360 -0
- autocoder/common/file_checkpoint/test_store.py +327 -0
- autocoder/common/file_checkpoint/test_utils.py +297 -0
- autocoder/common/file_checkpoint/utils.py +119 -0
- autocoder/common/rulefiles/autocoderrules_utils.py +138 -55
- autocoder/common/save_formatted_log.py +76 -5
- autocoder/common/v2/agent/agentic_edit.py +339 -216
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +2 -2
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +100 -5
- autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +322 -0
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +160 -10
- autocoder/common/v2/agent/agentic_edit_types.py +1 -2
- autocoder/common/v2/agent/agentic_tool_display.py +2 -3
- autocoder/compilers/normal_compiler.py +64 -0
- autocoder/events/event_manager_singleton.py +133 -4
- autocoder/linters/normal_linter.py +373 -0
- autocoder/linters/python_linter.py +4 -2
- autocoder/rag/long_context_rag.py +424 -397
- autocoder/rag/test_doc_filter.py +393 -0
- autocoder/rag/test_long_context_rag.py +473 -0
- autocoder/rag/test_token_limiter.py +342 -0
- autocoder/shadows/shadow_manager.py +1 -3
- autocoder/version.py +1 -1
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from typing import Optional, Dict, Any
|
|
2
|
+
import os
|
|
3
|
+
from loguru import logger
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
from ..types import TalkToGroupTool, ToolResult
|
|
7
|
+
from ..tools.base_tool_resolver import BaseToolResolver
|
|
8
|
+
from ..agent_hub import AgentHub, Group
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TalkToGroupToolResolver(BaseToolResolver):
|
|
12
|
+
"""
|
|
13
|
+
处理talk_to_group工具的解析器
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def resolve(self) -> ToolResult:
|
|
17
|
+
"""
|
|
18
|
+
解析和执行talk_to_group工具
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
ToolResult: 工具执行结果
|
|
22
|
+
"""
|
|
23
|
+
try:
|
|
24
|
+
# 获取参数
|
|
25
|
+
group_name = self.tool.group_name
|
|
26
|
+
content = self.tool.content
|
|
27
|
+
mentions_names = self.tool.mentions or []
|
|
28
|
+
print_conversation = self.tool.print_conversation
|
|
29
|
+
|
|
30
|
+
# 获取目标群组
|
|
31
|
+
groups = AgentHub.get_all_groups()
|
|
32
|
+
target_group = None
|
|
33
|
+
for group in groups:
|
|
34
|
+
if group.name == group_name:
|
|
35
|
+
target_group = group
|
|
36
|
+
break
|
|
37
|
+
|
|
38
|
+
if not target_group:
|
|
39
|
+
return ToolResult(
|
|
40
|
+
success=False,
|
|
41
|
+
message=f"找不到名为 '{group_name}' 的群组",
|
|
42
|
+
content=None
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# 解析提及的代理
|
|
46
|
+
mentions = []
|
|
47
|
+
for name in mentions_names:
|
|
48
|
+
agent = AgentHub.get_agent(name)
|
|
49
|
+
if agent:
|
|
50
|
+
mentions.append(agent)
|
|
51
|
+
else:
|
|
52
|
+
logger.warning(f"找不到提及的代理: {name}")
|
|
53
|
+
|
|
54
|
+
# 执行talk_to_group操作
|
|
55
|
+
self.agent.talk_to_group(
|
|
56
|
+
group=target_group,
|
|
57
|
+
content=content,
|
|
58
|
+
mentions=mentions,
|
|
59
|
+
print_conversation=print_conversation
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# 获取群组消息历史
|
|
63
|
+
with target_group._history_lock:
|
|
64
|
+
group_history = target_group.history.copy()
|
|
65
|
+
|
|
66
|
+
# 获取群组成员
|
|
67
|
+
with target_group._members_lock:
|
|
68
|
+
members = [member.name for member in target_group.members]
|
|
69
|
+
|
|
70
|
+
# 格式化群组历史
|
|
71
|
+
formatted_history = f"群组:{group_name}\n"
|
|
72
|
+
formatted_history += f"成员:{', '.join(members)}\n\n"
|
|
73
|
+
formatted_history += "对话历史:\n\n"
|
|
74
|
+
|
|
75
|
+
for msg in group_history:
|
|
76
|
+
sender_display = "你" if msg.sender == self.agent.name else msg.sender
|
|
77
|
+
mention_text = ""
|
|
78
|
+
if msg.mentions:
|
|
79
|
+
mention_display = ["你" if m == self.agent.name else m for m in msg.mentions]
|
|
80
|
+
mention_text = f" @{' @'.join(mention_display)}"
|
|
81
|
+
formatted_history += f"[{sender_display}]{mention_text}: {msg.content}\n\n"
|
|
82
|
+
|
|
83
|
+
# 返回结果
|
|
84
|
+
return ToolResult(
|
|
85
|
+
success=True,
|
|
86
|
+
message=f"已向群组 '{group_name}' 探讨",
|
|
87
|
+
content=formatted_history
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logger.exception(f"执行talk_to_group工具时出错: {e}")
|
|
92
|
+
return ToolResult(
|
|
93
|
+
success=False,
|
|
94
|
+
message=f"执行talk_to_group工具时出错: {str(e)}",
|
|
95
|
+
content=None
|
|
96
|
+
)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from typing import Optional, Dict, Any
|
|
2
|
+
import os
|
|
3
|
+
from loguru import logger
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
from ..types import TalkToTool, ToolResult
|
|
7
|
+
from ..tools.base_tool_resolver import BaseToolResolver
|
|
8
|
+
from ..agent_hub import AgentHub
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TalkToToolResolver(BaseToolResolver):
|
|
12
|
+
"""
|
|
13
|
+
处理talk_to工具的解析器
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def resolve(self) -> ToolResult:
|
|
17
|
+
"""
|
|
18
|
+
解析和执行talk_to工具
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
ToolResult: 工具执行结果
|
|
22
|
+
"""
|
|
23
|
+
try:
|
|
24
|
+
# 获取参数
|
|
25
|
+
agent_name = self.tool.agent_name
|
|
26
|
+
content = self.tool.content
|
|
27
|
+
mentions_names = self.tool.mentions or []
|
|
28
|
+
print_conversation = self.tool.print_conversation
|
|
29
|
+
|
|
30
|
+
# 获取目标代理
|
|
31
|
+
target_agent = AgentHub.get_agent(agent_name)
|
|
32
|
+
if not target_agent:
|
|
33
|
+
return ToolResult(
|
|
34
|
+
success=False,
|
|
35
|
+
message=f"找不到名为 '{agent_name}' 的代理",
|
|
36
|
+
content=None
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# 解析提及的代理
|
|
40
|
+
mentions = []
|
|
41
|
+
for name in mentions_names:
|
|
42
|
+
agent = AgentHub.get_agent(name)
|
|
43
|
+
if agent:
|
|
44
|
+
mentions.append(agent)
|
|
45
|
+
else:
|
|
46
|
+
logger.warning(f"找不到提及的代理: {name}")
|
|
47
|
+
|
|
48
|
+
# 执行talk_to操作
|
|
49
|
+
self.agent.talk_to(
|
|
50
|
+
other=target_agent,
|
|
51
|
+
content=content,
|
|
52
|
+
mentions=mentions,
|
|
53
|
+
print_conversation=print_conversation
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# 获取对话历史记录,格式化为易读的形式
|
|
57
|
+
with self.agent._chat_lock:
|
|
58
|
+
chat_history = self.agent.private_chats.get(agent_name, [])
|
|
59
|
+
|
|
60
|
+
# 格式化聊天历史
|
|
61
|
+
formatted_history = "对话历史:\n\n"
|
|
62
|
+
for msg in chat_history:
|
|
63
|
+
sender_display = "你" if msg.sender == self.agent.name else msg.sender
|
|
64
|
+
formatted_history += f"[{sender_display}]: {msg.content}\n\n"
|
|
65
|
+
|
|
66
|
+
# 返回结果
|
|
67
|
+
return ToolResult(
|
|
68
|
+
success=True,
|
|
69
|
+
message=f"已向Agent '{agent_name}' 讨论",
|
|
70
|
+
content=formatted_history
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.exception(f"执行talk_to工具时出错: {e}")
|
|
75
|
+
return ToolResult(
|
|
76
|
+
success=False,
|
|
77
|
+
message=f"执行talk_to工具时出错: {str(e)}",
|
|
78
|
+
content=None
|
|
79
|
+
)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional
|
|
2
|
+
import typing
|
|
3
|
+
from autocoder.agent.base_agentic.tools.base_tool_resolver import BaseToolResolver
|
|
4
|
+
from autocoder.agent.base_agentic.types import UseMcpTool, ToolResult # Import ToolResult from types
|
|
5
|
+
from autocoder.common import AutoCoderArgs
|
|
6
|
+
from loguru import logger
|
|
7
|
+
|
|
8
|
+
if typing.TYPE_CHECKING:
|
|
9
|
+
from ..base_agent import BaseAgent
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class UseMcpToolResolver(BaseToolResolver):
|
|
13
|
+
def __init__(self, agent: Optional['BaseAgent'], tool: UseMcpTool, args: AutoCoderArgs):
|
|
14
|
+
super().__init__(agent, tool, args)
|
|
15
|
+
self.tool: UseMcpTool = tool # For type hinting
|
|
16
|
+
|
|
17
|
+
def resolve(self) -> ToolResult:
|
|
18
|
+
"""
|
|
19
|
+
Executes a tool via the Model Context Protocol (MCP) server.
|
|
20
|
+
"""
|
|
21
|
+
final_query = ""
|
|
22
|
+
server_name = self.tool.server_name
|
|
23
|
+
tool_name = self.tool.tool_name
|
|
24
|
+
|
|
25
|
+
if server_name:
|
|
26
|
+
final_query += f"{server_name}\n"
|
|
27
|
+
|
|
28
|
+
if tool_name:
|
|
29
|
+
final_query += f"{tool_name} is recommended for the following query:\n"
|
|
30
|
+
|
|
31
|
+
final_query += f"{self.tool.query}"
|
|
32
|
+
|
|
33
|
+
logger.info(f"Resolving UseMcpTool: Server='{server_name}', Tool='{tool_name}', Query='{final_query}'")
|
|
34
|
+
|
|
35
|
+
mcp_server = get_mcp_server()
|
|
36
|
+
response = mcp_server.send_request(
|
|
37
|
+
McpRequest(
|
|
38
|
+
query=final_query,
|
|
39
|
+
model=self.args.inference_model or self.args.model,
|
|
40
|
+
product_mode=self.args.product_mode
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
return ToolResult(success=True, message=response.result)
|
|
44
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Dict, Any, Optional
|
|
3
|
+
from autocoder.agent.base_agentic.types import WriteToFileTool, ToolResult # Import ToolResult from types
|
|
4
|
+
from autocoder.agent.base_agentic.tools.base_tool_resolver import BaseToolResolver
|
|
5
|
+
from loguru import logger
|
|
6
|
+
from autocoder.common import AutoCoderArgs
|
|
7
|
+
import typing
|
|
8
|
+
|
|
9
|
+
if typing.TYPE_CHECKING:
|
|
10
|
+
from ..base_agent import BaseAgent
|
|
11
|
+
|
|
12
|
+
class WriteToFileToolResolver(BaseToolResolver):
|
|
13
|
+
def __init__(self, agent: Optional['BaseAgent'], tool: WriteToFileTool, args: AutoCoderArgs):
|
|
14
|
+
super().__init__(agent, tool, args)
|
|
15
|
+
self.tool: WriteToFileTool = tool # For type hinting
|
|
16
|
+
self.shadow_manager = self.agent.shadow_manager if self.agent else None
|
|
17
|
+
|
|
18
|
+
def resolve(self) -> ToolResult:
|
|
19
|
+
file_path = self.tool.path
|
|
20
|
+
content = self.tool.content
|
|
21
|
+
source_dir = self.args.source_dir or "."
|
|
22
|
+
abs_project_dir = os.path.abspath(source_dir)
|
|
23
|
+
abs_file_path = os.path.abspath(os.path.join(source_dir, file_path))
|
|
24
|
+
|
|
25
|
+
# Security check: ensure the path is within the source directory
|
|
26
|
+
if not abs_file_path.startswith(abs_project_dir):
|
|
27
|
+
return ToolResult(success=False, message=f"Error: Access denied. Attempted to write file outside the project directory: {file_path}")
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
if self.shadow_manager:
|
|
31
|
+
shadow_path = self.shadow_manager.to_shadow_path(abs_file_path)
|
|
32
|
+
# Ensure shadow directory exists
|
|
33
|
+
os.makedirs(os.path.dirname(shadow_path), exist_ok=True)
|
|
34
|
+
with open(shadow_path, 'w', encoding='utf-8') as f:
|
|
35
|
+
f.write(content)
|
|
36
|
+
logger.info(f"[Shadow] Successfully wrote shadow file: {shadow_path}")
|
|
37
|
+
|
|
38
|
+
# 回调AgenticEdit,记录变更
|
|
39
|
+
if self.agent:
|
|
40
|
+
rel_path = os.path.relpath(abs_file_path, abs_project_dir)
|
|
41
|
+
self.agent.record_file_change(rel_path, "added", diff=None, content=content)
|
|
42
|
+
|
|
43
|
+
return ToolResult(success=True, message=f"Successfully wrote to file (shadow): {file_path}", content=content)
|
|
44
|
+
else:
|
|
45
|
+
# No shadow manager fallback to original file
|
|
46
|
+
os.makedirs(os.path.dirname(abs_file_path), exist_ok=True)
|
|
47
|
+
with open(abs_file_path, 'w', encoding='utf-8') as f:
|
|
48
|
+
f.write(content)
|
|
49
|
+
logger.info(f"Successfully wrote to file: {file_path}")
|
|
50
|
+
|
|
51
|
+
if self.agent:
|
|
52
|
+
rel_path = os.path.relpath(abs_file_path, abs_project_dir)
|
|
53
|
+
self.agent.record_file_change(rel_path, "added", diff=None, content=content)
|
|
54
|
+
|
|
55
|
+
return ToolResult(success=True, message=f"Successfully wrote to file: {file_path}", content=content)
|
|
56
|
+
except Exception as e:
|
|
57
|
+
logger.error(f"Error writing to file '{file_path}': {str(e)}")
|
|
58
|
+
return ToolResult(success=False, message=f"An error occurred while writing to the file: {str(e)}")
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
from pydantic import BaseModel, SkipValidation
|
|
2
|
+
from typing import List, Dict, Any, Optional, Type, Union, Tuple, Generator, Literal
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# 工具结果类,由工具解析器使用
|
|
6
|
+
class ToolResult(BaseModel):
|
|
7
|
+
"""
|
|
8
|
+
工具执行结果
|
|
9
|
+
"""
|
|
10
|
+
success: bool = False # 执行是否成功
|
|
11
|
+
message: str = "" # 结果消息
|
|
12
|
+
content: Any = None # 返回内容,可以是任何类型
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# 工具的基本Pydantic模型
|
|
16
|
+
class BaseTool(BaseModel):
|
|
17
|
+
"""
|
|
18
|
+
代理工具的基类,所有工具类都应继承此类
|
|
19
|
+
"""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ExecuteCommandTool(BaseTool):
|
|
24
|
+
command: str
|
|
25
|
+
requires_approval: bool
|
|
26
|
+
|
|
27
|
+
class ReadFileTool(BaseTool):
|
|
28
|
+
path: str
|
|
29
|
+
|
|
30
|
+
class WriteToFileTool(BaseTool):
|
|
31
|
+
path: str
|
|
32
|
+
content: str
|
|
33
|
+
|
|
34
|
+
class ReplaceInFileTool(BaseTool):
|
|
35
|
+
path: str
|
|
36
|
+
diff: str
|
|
37
|
+
|
|
38
|
+
class SearchFilesTool(BaseTool):
|
|
39
|
+
path: str
|
|
40
|
+
regex: str
|
|
41
|
+
file_pattern: Optional[str] = None
|
|
42
|
+
|
|
43
|
+
class ListFilesTool(BaseTool):
|
|
44
|
+
path: str
|
|
45
|
+
recursive: Optional[bool] = False
|
|
46
|
+
|
|
47
|
+
class AskFollowupQuestionTool(BaseTool):
|
|
48
|
+
question: str
|
|
49
|
+
options: Optional[List[str]] = None
|
|
50
|
+
|
|
51
|
+
class AttemptCompletionTool(BaseTool):
|
|
52
|
+
result: str
|
|
53
|
+
command: Optional[str] = None
|
|
54
|
+
|
|
55
|
+
class PlanModeRespondTool(BaseTool):
|
|
56
|
+
response: str
|
|
57
|
+
options: Optional[List[str]] = None
|
|
58
|
+
|
|
59
|
+
class UseMcpTool(BaseTool):
|
|
60
|
+
server_name: str
|
|
61
|
+
tool_name: str
|
|
62
|
+
query:str
|
|
63
|
+
|
|
64
|
+
class TalkToTool(BaseTool):
|
|
65
|
+
agent_name: str
|
|
66
|
+
content: str
|
|
67
|
+
mentions: List[str] = []
|
|
68
|
+
print_conversation: bool = False
|
|
69
|
+
|
|
70
|
+
class TalkToGroupTool(BaseTool):
|
|
71
|
+
group_name: str
|
|
72
|
+
content: str
|
|
73
|
+
mentions: List[str] = []
|
|
74
|
+
print_conversation: bool = False
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# 工具指南相关类型
|
|
78
|
+
class ToolDescription(BaseModel):
|
|
79
|
+
"""
|
|
80
|
+
工具描述
|
|
81
|
+
"""
|
|
82
|
+
description: str # 工具描述内容
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ToolExample(BaseModel):
|
|
86
|
+
"""
|
|
87
|
+
工具使用示例
|
|
88
|
+
"""
|
|
89
|
+
title: str # 示例标题
|
|
90
|
+
body: str # 示例内容
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class RoleDescription(BaseModel):
|
|
94
|
+
"""
|
|
95
|
+
代理角色描述
|
|
96
|
+
"""
|
|
97
|
+
agent_type: str # 代理类型
|
|
98
|
+
description: str # 角色描述
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# 事件类型,用于流式输出
|
|
102
|
+
class LLMOutputEvent(BaseModel):
|
|
103
|
+
"""表示来自LLM的纯文本输出"""
|
|
104
|
+
text: str
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class LLMThinkingEvent(BaseModel):
|
|
108
|
+
"""表示来自LLM的<thinking>标签内的文本"""
|
|
109
|
+
text: str
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class ToolCallEvent(BaseModel):
|
|
113
|
+
"""表示LLM决定调用工具"""
|
|
114
|
+
tool: SkipValidation[BaseTool] # 使用SkipValidation因为BaseTool本身很复杂
|
|
115
|
+
tool_xml: str
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class ToolResultEvent(BaseModel):
|
|
119
|
+
"""表示执行工具的结果"""
|
|
120
|
+
tool_name: str
|
|
121
|
+
result: ToolResult
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class TokenUsageEvent(BaseModel):
|
|
125
|
+
"""表示Token使用情况"""
|
|
126
|
+
usage: Any
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class CompletionEvent(BaseModel):
|
|
130
|
+
"""表示LLM尝试完成任务"""
|
|
131
|
+
completion: Any # 完成的工具,使用Any以避免循环导入
|
|
132
|
+
completion_xml: str
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class ErrorEvent(BaseModel):
|
|
136
|
+
"""表示过程中的错误"""
|
|
137
|
+
message: str
|
|
138
|
+
|
|
139
|
+
class PlanModeRespondEvent(BaseModel):
|
|
140
|
+
"""Represents the LLM attempting to complete the task."""
|
|
141
|
+
completion: SkipValidation[PlanModeRespondTool] # Skip validation
|
|
142
|
+
completion_xml: str
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# 工具执行中的事件类型联合
|
|
146
|
+
AgentEvent = Union[
|
|
147
|
+
LLMOutputEvent,
|
|
148
|
+
LLMThinkingEvent,
|
|
149
|
+
ToolCallEvent,
|
|
150
|
+
ToolResultEvent,
|
|
151
|
+
TokenUsageEvent,
|
|
152
|
+
CompletionEvent,
|
|
153
|
+
ErrorEvent,
|
|
154
|
+
PlanModeRespondEvent
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# 文件变更记录
|
|
159
|
+
class FileChangeEntry(BaseModel):
|
|
160
|
+
"""
|
|
161
|
+
文件变更条目,用于记录文件的变更信息
|
|
162
|
+
"""
|
|
163
|
+
type: str # 'added' 或 'modified'
|
|
164
|
+
diffs: List[str] = [] # 使用 replace_in_file 时,记录 diff 内容
|
|
165
|
+
content: Optional[str] = None # 使用 write_to_file 时,记录文件内容
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# 代理编辑请求
|
|
169
|
+
class AgentRequest(BaseModel):
|
|
170
|
+
"""
|
|
171
|
+
代理请求
|
|
172
|
+
"""
|
|
173
|
+
user_input: str
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class Message(BaseModel):
|
|
177
|
+
sender: str
|
|
178
|
+
content: str
|
|
179
|
+
mentions: List[str] = []
|
|
180
|
+
is_group: bool = False
|
|
181
|
+
group_name: Optional[str] = None
|
|
182
|
+
reply_config: Dict[str, Any] = {}
|
|
183
|
+
|
|
184
|
+
class ReplyDecision(BaseModel):
|
|
185
|
+
content: str
|
|
186
|
+
strategy: Literal["broadcast", "private", "ignore"]
|
|
187
|
+
mentions: List[str] = []
|
|
188
|
+
priority: int = 0
|
|
189
|
+
reason: str = ""
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
if typing.TYPE_CHECKING:
|
|
5
|
+
from .agent_hub import Group
|
|
6
|
+
import byzerllm
|
|
7
|
+
from typing import Union,List
|
|
8
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
import threading
|
|
11
|
+
from loguru import logger
|
|
12
|
+
class GroupMember(BaseModel):
|
|
13
|
+
member: str
|
|
14
|
+
reason: str
|
|
15
|
+
|
|
16
|
+
class GroupMemberResponse(BaseModel):
|
|
17
|
+
group_name: str
|
|
18
|
+
members: List[GroupMember]
|
|
19
|
+
|
|
20
|
+
class GroupUtils:
|
|
21
|
+
_executor = ThreadPoolExecutor(max_workers=8)
|
|
22
|
+
_executor_lock = threading.Lock()
|
|
23
|
+
|
|
24
|
+
def __init__(self, llm: Union[byzerllm.ByzerLLM, byzerllm.SimpleByzerLLM]):
|
|
25
|
+
self.llm = llm
|
|
26
|
+
|
|
27
|
+
def auto_select_group(self, content: str, groups: List['Group']) -> List[GroupMemberResponse]:
|
|
28
|
+
futures = []
|
|
29
|
+
with self._executor_lock:
|
|
30
|
+
for group in groups:
|
|
31
|
+
future = self._executor.submit(
|
|
32
|
+
self._safe_select_group,
|
|
33
|
+
content,
|
|
34
|
+
group
|
|
35
|
+
)
|
|
36
|
+
futures.append((group.name, future))
|
|
37
|
+
|
|
38
|
+
results = []
|
|
39
|
+
for group_name, future in futures:
|
|
40
|
+
try:
|
|
41
|
+
members = future.result()
|
|
42
|
+
if members and len(members) > 0:
|
|
43
|
+
results.append(GroupMemberResponse(
|
|
44
|
+
group_name=group_name,
|
|
45
|
+
members=members
|
|
46
|
+
))
|
|
47
|
+
except Exception as e:
|
|
48
|
+
import traceback
|
|
49
|
+
traceback.print_exc()
|
|
50
|
+
logger.error(f"Failed to select group {group_name}: {str(e)}")
|
|
51
|
+
|
|
52
|
+
return results
|
|
53
|
+
|
|
54
|
+
def _safe_select_group(self, content: str, group: 'Group') -> List[GroupMember]:
|
|
55
|
+
try:
|
|
56
|
+
return self.select_group.with_llm(self.llm).with_return_type(List[GroupMember]).run(content=content, group=group)
|
|
57
|
+
except Exception as e:
|
|
58
|
+
logger.error(f"Error in select_group: {str(e)}")
|
|
59
|
+
return []
|
|
60
|
+
|
|
61
|
+
@byzerllm.prompt()
|
|
62
|
+
def select_group(self, content: str, group: 'Group') -> List[GroupMember]:
|
|
63
|
+
'''
|
|
64
|
+
当前时间: {{ time }}
|
|
65
|
+
用户发来了如下问题:
|
|
66
|
+
<user_question>
|
|
67
|
+
{{ content }}
|
|
68
|
+
</user_question>
|
|
69
|
+
|
|
70
|
+
下面是群组 {{ group_name }} 所有成员及其对应的角色:
|
|
71
|
+
{% for member_info in members_info %}
|
|
72
|
+
===== {{ member_info[0] }} =====
|
|
73
|
+
<who_are_you>
|
|
74
|
+
{{ member_info[1] }}
|
|
75
|
+
</who_are_you>
|
|
76
|
+
{% endfor %}
|
|
77
|
+
|
|
78
|
+
请分析该群组中的成员是否有人可以回答用户的问题。
|
|
79
|
+
|
|
80
|
+
请生成 JSON 格式回复:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
[
|
|
84
|
+
{
|
|
85
|
+
"member": "可以回答用户问题的用户名",
|
|
86
|
+
"reason": "选择这些用户的原因"
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
```
|
|
90
|
+
如果群组中没有合适的成员可以回答问题,请返回空列表。
|
|
91
|
+
请严格按照 JSON 格式输出,不要有任何多余的内容。
|
|
92
|
+
'''
|
|
93
|
+
members_info = [(member.name, member.system_prompt) for member in group.members]
|
|
94
|
+
|
|
95
|
+
context = {
|
|
96
|
+
"time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
97
|
+
"group_name": group.name,
|
|
98
|
+
"members_info": members_info
|
|
99
|
+
}
|
|
100
|
+
return context
|
autocoder/auto_coder_runner.py
CHANGED
|
@@ -231,7 +231,7 @@ def get_all_extensions(directory: str = ".") -> str:
|
|
|
231
231
|
return ",".join(sorted(all_extensions))
|
|
232
232
|
|
|
233
233
|
def configure_logger():
|
|
234
|
-
# 设置日志目录和文件
|
|
234
|
+
# 设置日志目录和文件
|
|
235
235
|
log_dir = os.path.join(project_root, ".auto-coder", "logs")
|
|
236
236
|
os.makedirs(log_dir, exist_ok=True)
|
|
237
237
|
log_file = os.path.join(log_dir, "auto-coder.log")
|
|
@@ -258,8 +258,6 @@ def configure_logger():
|
|
|
258
258
|
]
|
|
259
259
|
)
|
|
260
260
|
|
|
261
|
-
configure_logger()
|
|
262
|
-
|
|
263
261
|
def init_singleton_instances():
|
|
264
262
|
# 初始化文件监控系统
|
|
265
263
|
try:
|
|
@@ -277,7 +275,9 @@ def init_singleton_instances():
|
|
|
277
275
|
_ = IgnoreFileManager(project_root=project_root)
|
|
278
276
|
|
|
279
277
|
|
|
280
|
-
|
|
278
|
+
if os.environ.get('autocoder_auto_init',"true") in ["true","True","True",True]:
|
|
279
|
+
configure_logger()
|
|
280
|
+
init_singleton_instances()
|
|
281
281
|
|
|
282
282
|
def initialize_system(args:InitializeSystemRequest):
|
|
283
283
|
from autocoder.utils.model_provider_selector import ModelProviderSelector
|
|
@@ -2850,6 +2850,7 @@ def auto_command(query: str,extra_args: Dict[str,Any]={}):
|
|
|
2850
2850
|
else:
|
|
2851
2851
|
agent.run_in_terminal(AgenticEditRequest(user_input=query))
|
|
2852
2852
|
|
|
2853
|
+
completer.refresh_files()
|
|
2853
2854
|
return
|
|
2854
2855
|
|
|
2855
2856
|
args = get_final_config()
|
|
@@ -2902,3 +2903,4 @@ def auto_command(query: str,extra_args: Dict[str,Any]={}):
|
|
|
2902
2903
|
border_style="blue",
|
|
2903
2904
|
padding=(1, 2)
|
|
2904
2905
|
))
|
|
2906
|
+
completer.refresh_files()
|
autocoder/chat/conf_command.py
CHANGED
|
@@ -183,14 +183,15 @@ def _handle_help(memory: Dict[str, Any], args: List[str]) -> str:
|
|
|
183
183
|
|
|
184
184
|
# Command dispatch table
|
|
185
185
|
COMMAND_HANDLERS: Dict[str, Callable[[Dict[str, Any], List[str]], str]] = {
|
|
186
|
-
"list": _handle_list_conf,
|
|
187
|
-
"show": _handle_list_conf, # Alias
|
|
188
|
-
"get": _handle_get_conf,
|
|
189
|
-
"set": _handle_set_conf,
|
|
190
|
-
"delete": _handle_delete_conf,
|
|
191
|
-
"del": _handle_delete_conf, # Alias
|
|
192
|
-
"rm": _handle_delete_conf, # Alias
|
|
193
|
-
"
|
|
186
|
+
"/list": _handle_list_conf,
|
|
187
|
+
"/show": _handle_list_conf, # Alias
|
|
188
|
+
"/get": _handle_get_conf,
|
|
189
|
+
"/set": _handle_set_conf,
|
|
190
|
+
"/delete": _handle_delete_conf,
|
|
191
|
+
"/del": _handle_delete_conf, # Alias
|
|
192
|
+
"/rm": _handle_delete_conf, # Alias
|
|
193
|
+
"/drop": _handle_delete_conf, # Add this line for /drop command
|
|
194
|
+
"/help": _handle_help,
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
def handle_conf_command(command_args: str, memory: Dict[str, Any]) -> str:
|
|
@@ -205,7 +206,7 @@ def handle_conf_command(command_args: str, memory: Dict[str, Any]) -> str:
|
|
|
205
206
|
Returns:
|
|
206
207
|
A string response to be displayed to the user.
|
|
207
208
|
"""
|
|
208
|
-
conf_str = command_args.strip()
|
|
209
|
+
conf_str = command_args.strip()
|
|
209
210
|
|
|
210
211
|
# Handle special subcommands first
|
|
211
212
|
if conf_str.startswith("/export"):
|
|
@@ -239,7 +240,7 @@ def handle_conf_command(command_args: str, memory: Dict[str, Any]) -> str:
|
|
|
239
240
|
return _handle_list_conf(memory, [])
|
|
240
241
|
else:
|
|
241
242
|
command = args[0].lower()
|
|
242
|
-
command_args_list = args[1:]
|
|
243
|
+
command_args_list = args[1:]
|
|
243
244
|
|
|
244
245
|
# Check if the first argument is a known command or potentially a pattern
|
|
245
246
|
handler = COMMAND_HANDLERS.get(command)
|
autocoder/common/__init__.py
CHANGED
|
@@ -432,6 +432,8 @@ class AutoCoderArgs(pydantic.BaseModel):
|
|
|
432
432
|
auto_fix_lint_max_attempts: Optional[int] = 5
|
|
433
433
|
auto_fix_compile_max_attempts: Optional[int] = 5
|
|
434
434
|
auto_fix_merge_max_attempts: Optional[int] = 5
|
|
435
|
+
|
|
436
|
+
enable_auto_select_rules: Optional[bool] = True
|
|
435
437
|
|
|
436
438
|
ignore_clean_shadows: Optional[bool] = False
|
|
437
439
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
文件变更管理模块
|
|
3
|
+
|
|
4
|
+
该模块提供了一种可靠的机制来应用、记录和撤销对项目文件的修改。
|
|
5
|
+
主要功能包括:
|
|
6
|
+
1. 将影子系统中的文件变更安全地应用到用户的实际项目中
|
|
7
|
+
2. 记录每次变更的历史,支持多版本撤销功能
|
|
8
|
+
3. 提供简单直观的API,便于集成到现有的编辑流程中
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from autocoder.common.file_checkpoint.models import (
|
|
12
|
+
FileChange, ChangeRecord, ApplyResult, UndoResult, DiffResult
|
|
13
|
+
)
|
|
14
|
+
from autocoder.common.file_checkpoint.manager import FileChangeManager
|
|
15
|
+
from autocoder.common.file_checkpoint.store import FileChangeStore
|
|
16
|
+
from autocoder.common.file_checkpoint.backup import FileBackupManager
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
'FileChange', 'ChangeRecord', 'ApplyResult', 'UndoResult', 'DiffResult',
|
|
20
|
+
'FileChangeManager', 'FileChangeStore', 'FileBackupManager'
|
|
21
|
+
]
|