auto-coder 0.1.361__py3-none-any.whl → 0.1.363__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.361.dist-info → auto_coder-0.1.363.dist-info}/METADATA +2 -1
- {auto_coder-0.1.361.dist-info → auto_coder-0.1.363.dist-info}/RECORD +57 -29
- autocoder/agent/auto_learn.py +249 -262
- 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.py +1 -1
- autocoder/auto_coder_runner.py +36 -14
- autocoder/chat/conf_command.py +11 -10
- autocoder/commands/auto_command.py +227 -159
- autocoder/common/__init__.py +2 -2
- autocoder/common/ignorefiles/ignore_file_utils.py +12 -8
- autocoder/common/result_manager.py +10 -2
- autocoder/common/rulefiles/autocoderrules_utils.py +169 -0
- autocoder/common/save_formatted_log.py +1 -1
- autocoder/common/v2/agent/agentic_edit.py +53 -41
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +15 -12
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +73 -1
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +132 -4
- autocoder/common/v2/agent/agentic_edit_types.py +1 -2
- autocoder/common/v2/agent/agentic_tool_display.py +2 -3
- autocoder/common/v2/code_auto_generate_editblock.py +3 -1
- autocoder/index/index.py +14 -8
- autocoder/privacy/model_filter.py +297 -35
- 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/utils/_markitdown.py +22 -3
- autocoder/version.py +1 -1
- {auto_coder-0.1.361.dist-info → auto_coder-0.1.363.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.361.dist-info → auto_coder-0.1.363.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.361.dist-info → auto_coder-0.1.363.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.361.dist-info → auto_coder-0.1.363.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.py
CHANGED
|
@@ -1069,7 +1069,7 @@ def main(input_args: Optional[List[str]] = None):
|
|
|
1069
1069
|
border_style="green",
|
|
1070
1070
|
)
|
|
1071
1071
|
)
|
|
1072
|
-
if not args.query:
|
|
1072
|
+
if not args.query or (args.query_prefix and args.query == args.query_prefix) or (args.query_suffix and args.query == args.query_suffix):
|
|
1073
1073
|
return
|
|
1074
1074
|
|
|
1075
1075
|
if os.path.exists(memory_file):
|
autocoder/auto_coder_runner.py
CHANGED
|
@@ -65,6 +65,7 @@ from loguru import logger as global_logger
|
|
|
65
65
|
from autocoder.utils.project_structure import EnhancedFileAnalyzer
|
|
66
66
|
from autocoder.common import SourceCodeList,SourceCode
|
|
67
67
|
from autocoder.common.file_monitor import FileMonitor
|
|
68
|
+
from filelock import FileLock
|
|
68
69
|
|
|
69
70
|
|
|
70
71
|
## 对外API,用于第三方集成 auto-coder 使用。
|
|
@@ -230,7 +231,7 @@ def get_all_extensions(directory: str = ".") -> str:
|
|
|
230
231
|
return ",".join(sorted(all_extensions))
|
|
231
232
|
|
|
232
233
|
def configure_logger():
|
|
233
|
-
# 设置日志目录和文件
|
|
234
|
+
# 设置日志目录和文件
|
|
234
235
|
log_dir = os.path.join(project_root, ".auto-coder", "logs")
|
|
235
236
|
os.makedirs(log_dir, exist_ok=True)
|
|
236
237
|
log_file = os.path.join(log_dir, "auto-coder.log")
|
|
@@ -257,13 +258,26 @@ def configure_logger():
|
|
|
257
258
|
]
|
|
258
259
|
)
|
|
259
260
|
|
|
260
|
-
|
|
261
|
+
def init_singleton_instances():
|
|
262
|
+
# 初始化文件监控系统
|
|
263
|
+
try:
|
|
264
|
+
FileMonitor(project_root).start()
|
|
265
|
+
except Exception as e:
|
|
266
|
+
global_logger.error(f"Failed to start file monitor: {e}")
|
|
267
|
+
global_logger.exception(e)
|
|
268
|
+
|
|
269
|
+
# 初始化规则文件管理器
|
|
270
|
+
from autocoder.common.rulefiles.autocoderrules_utils import get_rules
|
|
271
|
+
get_rules(project_root=project_root)
|
|
272
|
+
|
|
273
|
+
# 初始化忽略文件管理器
|
|
274
|
+
from autocoder.common.ignorefiles.ignore_file_utils import IgnoreFileManager
|
|
275
|
+
_ = IgnoreFileManager(project_root=project_root)
|
|
276
|
+
|
|
261
277
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
global_logger.error(f"Failed to start file monitor: {e}")
|
|
266
|
-
global_logger.exception(e)
|
|
278
|
+
if os.environ.get('autocoder_auto_init',"true") in ["true","True","True",True]:
|
|
279
|
+
configure_logger()
|
|
280
|
+
init_singleton_instances()
|
|
267
281
|
|
|
268
282
|
def initialize_system(args:InitializeSystemRequest):
|
|
269
283
|
from autocoder.utils.model_provider_selector import ModelProviderSelector
|
|
@@ -633,20 +647,28 @@ def get_symbol_list() -> List[SymbolItem]:
|
|
|
633
647
|
|
|
634
648
|
|
|
635
649
|
def save_memory():
|
|
636
|
-
|
|
637
|
-
|
|
650
|
+
memory_path = os.path.join(base_persist_dir, "memory.json")
|
|
651
|
+
lock_path = memory_path + ".lock"
|
|
652
|
+
|
|
653
|
+
with FileLock(lock_path, timeout=30):
|
|
654
|
+
with open(memory_path, "w", encoding="utf-8") as f:
|
|
655
|
+
json.dump(memory, f, indent=2, ensure_ascii=False)
|
|
656
|
+
|
|
638
657
|
load_memory()
|
|
639
658
|
|
|
640
659
|
|
|
641
660
|
def load_memory():
|
|
642
661
|
global memory
|
|
643
662
|
memory_path = os.path.join(base_persist_dir, "memory.json")
|
|
663
|
+
lock_path = memory_path + ".lock"
|
|
664
|
+
|
|
644
665
|
if os.path.exists(memory_path):
|
|
645
|
-
with
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
666
|
+
with FileLock(lock_path, timeout=30):
|
|
667
|
+
with open(memory_path, "r", encoding="utf-8") as f:
|
|
668
|
+
_memory = json.load(f)
|
|
669
|
+
# clear memory
|
|
670
|
+
memory.clear()
|
|
671
|
+
memory.update(_memory)
|
|
650
672
|
return memory
|
|
651
673
|
|
|
652
674
|
def get_memory():
|