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.

Files changed (57) hide show
  1. {auto_coder-0.1.361.dist-info → auto_coder-0.1.363.dist-info}/METADATA +2 -1
  2. {auto_coder-0.1.361.dist-info → auto_coder-0.1.363.dist-info}/RECORD +57 -29
  3. autocoder/agent/auto_learn.py +249 -262
  4. autocoder/agent/base_agentic/__init__.py +0 -0
  5. autocoder/agent/base_agentic/agent_hub.py +169 -0
  6. autocoder/agent/base_agentic/agentic_lang.py +112 -0
  7. autocoder/agent/base_agentic/agentic_tool_display.py +180 -0
  8. autocoder/agent/base_agentic/base_agent.py +1582 -0
  9. autocoder/agent/base_agentic/default_tools.py +683 -0
  10. autocoder/agent/base_agentic/test_base_agent.py +82 -0
  11. autocoder/agent/base_agentic/tool_registry.py +425 -0
  12. autocoder/agent/base_agentic/tools/__init__.py +12 -0
  13. autocoder/agent/base_agentic/tools/ask_followup_question_tool_resolver.py +72 -0
  14. autocoder/agent/base_agentic/tools/attempt_completion_tool_resolver.py +37 -0
  15. autocoder/agent/base_agentic/tools/base_tool_resolver.py +35 -0
  16. autocoder/agent/base_agentic/tools/example_tool_resolver.py +46 -0
  17. autocoder/agent/base_agentic/tools/execute_command_tool_resolver.py +72 -0
  18. autocoder/agent/base_agentic/tools/list_files_tool_resolver.py +110 -0
  19. autocoder/agent/base_agentic/tools/plan_mode_respond_tool_resolver.py +35 -0
  20. autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +54 -0
  21. autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +156 -0
  22. autocoder/agent/base_agentic/tools/search_files_tool_resolver.py +134 -0
  23. autocoder/agent/base_agentic/tools/talk_to_group_tool_resolver.py +96 -0
  24. autocoder/agent/base_agentic/tools/talk_to_tool_resolver.py +79 -0
  25. autocoder/agent/base_agentic/tools/use_mcp_tool_resolver.py +44 -0
  26. autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +58 -0
  27. autocoder/agent/base_agentic/types.py +189 -0
  28. autocoder/agent/base_agentic/utils.py +100 -0
  29. autocoder/auto_coder.py +1 -1
  30. autocoder/auto_coder_runner.py +36 -14
  31. autocoder/chat/conf_command.py +11 -10
  32. autocoder/commands/auto_command.py +227 -159
  33. autocoder/common/__init__.py +2 -2
  34. autocoder/common/ignorefiles/ignore_file_utils.py +12 -8
  35. autocoder/common/result_manager.py +10 -2
  36. autocoder/common/rulefiles/autocoderrules_utils.py +169 -0
  37. autocoder/common/save_formatted_log.py +1 -1
  38. autocoder/common/v2/agent/agentic_edit.py +53 -41
  39. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +15 -12
  40. autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +73 -1
  41. autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +132 -4
  42. autocoder/common/v2/agent/agentic_edit_types.py +1 -2
  43. autocoder/common/v2/agent/agentic_tool_display.py +2 -3
  44. autocoder/common/v2/code_auto_generate_editblock.py +3 -1
  45. autocoder/index/index.py +14 -8
  46. autocoder/privacy/model_filter.py +297 -35
  47. autocoder/rag/long_context_rag.py +424 -397
  48. autocoder/rag/test_doc_filter.py +393 -0
  49. autocoder/rag/test_long_context_rag.py +473 -0
  50. autocoder/rag/test_token_limiter.py +342 -0
  51. autocoder/shadows/shadow_manager.py +1 -3
  52. autocoder/utils/_markitdown.py +22 -3
  53. autocoder/version.py +1 -1
  54. {auto_coder-0.1.361.dist-info → auto_coder-0.1.363.dist-info}/LICENSE +0 -0
  55. {auto_coder-0.1.361.dist-info → auto_coder-0.1.363.dist-info}/WHEEL +0 -0
  56. {auto_coder-0.1.361.dist-info → auto_coder-0.1.363.dist-info}/entry_points.txt +0 -0
  57. {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):
@@ -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
- configure_logger()
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
- try:
263
- FileMonitor(project_root).start()
264
- except Exception as e:
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
- with open(os.path.join(base_persist_dir, "memory.json"), "w",encoding="utf-8") as f:
637
- json.dump(memory, f, indent=2, ensure_ascii=False)
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 open(memory_path, "r", encoding="utf-8") as f:
646
- _memory = json.load(f)
647
- # clear memory
648
- memory.clear()
649
- memory.update(_memory)
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():