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.

Files changed (65) hide show
  1. {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/METADATA +2 -2
  2. {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/RECORD +65 -22
  3. autocoder/agent/base_agentic/__init__.py +0 -0
  4. autocoder/agent/base_agentic/agent_hub.py +169 -0
  5. autocoder/agent/base_agentic/agentic_lang.py +112 -0
  6. autocoder/agent/base_agentic/agentic_tool_display.py +180 -0
  7. autocoder/agent/base_agentic/base_agent.py +1582 -0
  8. autocoder/agent/base_agentic/default_tools.py +683 -0
  9. autocoder/agent/base_agentic/test_base_agent.py +82 -0
  10. autocoder/agent/base_agentic/tool_registry.py +425 -0
  11. autocoder/agent/base_agentic/tools/__init__.py +12 -0
  12. autocoder/agent/base_agentic/tools/ask_followup_question_tool_resolver.py +72 -0
  13. autocoder/agent/base_agentic/tools/attempt_completion_tool_resolver.py +37 -0
  14. autocoder/agent/base_agentic/tools/base_tool_resolver.py +35 -0
  15. autocoder/agent/base_agentic/tools/example_tool_resolver.py +46 -0
  16. autocoder/agent/base_agentic/tools/execute_command_tool_resolver.py +72 -0
  17. autocoder/agent/base_agentic/tools/list_files_tool_resolver.py +110 -0
  18. autocoder/agent/base_agentic/tools/plan_mode_respond_tool_resolver.py +35 -0
  19. autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +54 -0
  20. autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +156 -0
  21. autocoder/agent/base_agentic/tools/search_files_tool_resolver.py +134 -0
  22. autocoder/agent/base_agentic/tools/talk_to_group_tool_resolver.py +96 -0
  23. autocoder/agent/base_agentic/tools/talk_to_tool_resolver.py +79 -0
  24. autocoder/agent/base_agentic/tools/use_mcp_tool_resolver.py +44 -0
  25. autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +58 -0
  26. autocoder/agent/base_agentic/types.py +189 -0
  27. autocoder/agent/base_agentic/utils.py +100 -0
  28. autocoder/auto_coder_runner.py +6 -4
  29. autocoder/chat/conf_command.py +11 -10
  30. autocoder/common/__init__.py +2 -0
  31. autocoder/common/file_checkpoint/__init__.py +21 -0
  32. autocoder/common/file_checkpoint/backup.py +264 -0
  33. autocoder/common/file_checkpoint/examples.py +217 -0
  34. autocoder/common/file_checkpoint/manager.py +404 -0
  35. autocoder/common/file_checkpoint/models.py +156 -0
  36. autocoder/common/file_checkpoint/store.py +383 -0
  37. autocoder/common/file_checkpoint/test_backup.py +242 -0
  38. autocoder/common/file_checkpoint/test_manager.py +570 -0
  39. autocoder/common/file_checkpoint/test_models.py +360 -0
  40. autocoder/common/file_checkpoint/test_store.py +327 -0
  41. autocoder/common/file_checkpoint/test_utils.py +297 -0
  42. autocoder/common/file_checkpoint/utils.py +119 -0
  43. autocoder/common/rulefiles/autocoderrules_utils.py +138 -55
  44. autocoder/common/save_formatted_log.py +76 -5
  45. autocoder/common/v2/agent/agentic_edit.py +339 -216
  46. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +2 -2
  47. autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +100 -5
  48. autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +322 -0
  49. autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +160 -10
  50. autocoder/common/v2/agent/agentic_edit_types.py +1 -2
  51. autocoder/common/v2/agent/agentic_tool_display.py +2 -3
  52. autocoder/compilers/normal_compiler.py +64 -0
  53. autocoder/events/event_manager_singleton.py +133 -4
  54. autocoder/linters/normal_linter.py +373 -0
  55. autocoder/linters/python_linter.py +4 -2
  56. autocoder/rag/long_context_rag.py +424 -397
  57. autocoder/rag/test_doc_filter.py +393 -0
  58. autocoder/rag/test_long_context_rag.py +473 -0
  59. autocoder/rag/test_token_limiter.py +342 -0
  60. autocoder/shadows/shadow_manager.py +1 -3
  61. autocoder/version.py +1 -1
  62. {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/LICENSE +0 -0
  63. {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/WHEEL +0 -0
  64. {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/entry_points.txt +0 -0
  65. {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,9 @@ from autocoder.common.v2.agent.agentic_edit_types import WriteToFileTool, ToolRe
4
4
  from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver
5
5
  from loguru import logger
6
6
  from autocoder.common import AutoCoderArgs
7
+ from autocoder.common.auto_coder_lang import get_message_with_format
8
+ from autocoder.common.file_checkpoint.models import FileChange as CheckpointFileChange
9
+ from autocoder.common.file_checkpoint.manager import FileChangeManager as CheckpointFileChangeManager
7
10
  import typing
8
11
 
9
12
  if typing.TYPE_CHECKING:
@@ -13,7 +16,33 @@ class WriteToFileToolResolver(BaseToolResolver):
13
16
  def __init__(self, agent: Optional['AgenticEdit'], tool: WriteToFileTool, args: AutoCoderArgs):
14
17
  super().__init__(agent, tool, args)
15
18
  self.tool: WriteToFileTool = tool # For type hinting
19
+ self.args = args
16
20
  self.shadow_manager = self.agent.shadow_manager if self.agent else None
21
+ self.shadow_linter = self.agent.shadow_linter if self.agent else None
22
+
23
+ def _format_lint_issues(self, lint_result):
24
+ """
25
+ 将 lint 结果格式化为可读的文本格式
26
+
27
+ 参数:
28
+ lint_result: 单个文件的 lint 结果对象
29
+
30
+ 返回:
31
+ str: 格式化的问题描述
32
+ """
33
+ formatted_issues = []
34
+
35
+ for issue in lint_result.issues:
36
+ severity = "错误" if issue.severity.value == 3 else "警告" if issue.severity.value == 2 else "信息"
37
+ line_info = f"第{issue.position.line}行"
38
+ if issue.position.column:
39
+ line_info += f", 第{issue.position.column}列"
40
+
41
+ formatted_issues.append(
42
+ f" - [{severity}] {line_info}: {issue.message} (规则: {issue.code})"
43
+ )
44
+
45
+ return "\n".join(formatted_issues)
17
46
 
18
47
  def resolve(self) -> ToolResult:
19
48
  file_path = self.tool.path
@@ -39,20 +68,141 @@ class WriteToFileToolResolver(BaseToolResolver):
39
68
  if self.agent:
40
69
  rel_path = os.path.relpath(abs_file_path, abs_project_dir)
41
70
  self.agent.record_file_change(rel_path, "added", diff=None, content=content)
71
+
72
+ # 新增:执行代码质量检查
73
+ lint_results = None
74
+ lint_message = ""
75
+ formatted_issues = ""
76
+ has_lint_issues = False
77
+
78
+ # 检查是否启用了Lint功能
79
+ enable_lint = self.args.enable_auto_fix_lint
80
+
81
+ if enable_lint:
82
+ try:
83
+ if self.shadow_linter and self.shadow_manager:
84
+ # 对新创建的文件进行 lint 检查
85
+ shadow_path = self.shadow_manager.to_shadow_path(abs_file_path)
86
+ lint_results = self.shadow_linter.lint_shadow_file(shadow_path)
87
+
88
+ if lint_results and lint_results.issues:
89
+ has_lint_issues = True
90
+ # 格式化 lint 问题
91
+ formatted_issues = self._format_lint_issues(lint_results)
92
+ lint_message = f"\n\n代码质量检查发现 {len(lint_results.issues)} 个问题:\n{formatted_issues}"
93
+ else:
94
+ lint_message = "\n\n代码质量检查通过,未发现问题。"
95
+ except Exception as e:
96
+ logger.error(f"Lint 检查失败: {str(e)}")
97
+ lint_message = "\n\n尝试进行代码质量检查时出错。"
98
+ else:
99
+ logger.info("代码质量检查已禁用")
100
+
101
+ # 构建包含 lint 结果的返回消息
102
+ message = f"Successfully wrote to file (shadow): {file_path}"
103
+
104
+ # 将 lint 消息添加到结果中,如果启用了Lint
105
+ if enable_lint:
106
+ message += lint_message
107
+
108
+ # 附加 lint 结果到返回内容
109
+ result_content = {
110
+ "content": content,
111
+ }
112
+
113
+ # 只有在启用Lint时才添加Lint结果
114
+ if enable_lint:
115
+ result_content["lint_results"] = {
116
+ "has_issues": has_lint_issues,
117
+ "issues": formatted_issues if has_lint_issues else None
118
+ }
119
+
120
+ return ToolResult(success=True, message=message, content=result_content)
121
+
122
+ # No shadow manager fallback to original file
123
+ os.makedirs(os.path.dirname(abs_file_path), exist_ok=True)
42
124
 
43
- return ToolResult(success=True, message=f"Successfully wrote to file (shadow): {file_path}", content=content)
125
+ if self.agent:
126
+ rel_path = os.path.relpath(abs_file_path, abs_project_dir)
127
+ self.agent.record_file_change(rel_path, "added", diff=None, content=content)
128
+
129
+ if self.agent and self.agent.checkpoint_manager:
130
+ changes = {
131
+ file_path: CheckpointFileChange(
132
+ file_path=file_path,
133
+ content=content,
134
+ is_deletion=False,
135
+ is_new=True
136
+ )
137
+ }
138
+ change_group_id = self.args.event_file
139
+ self.agent.checkpoint_manager.apply_changes(changes,change_group_id)
44
140
  else:
45
- # No shadow manager fallback to original file
46
- os.makedirs(os.path.dirname(abs_file_path), exist_ok=True)
47
141
  with open(abs_file_path, 'w', encoding='utf-8') as f:
48
142
  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)
143
+ logger.info(f"Successfully wrote to file: {file_path}")
144
+
145
+ # 新增:执行代码质量检查
146
+ lint_results = None
147
+ lint_message = ""
148
+ formatted_issues = ""
149
+ has_lint_issues = False
150
+
151
+ # 检查是否启用了Lint功能
152
+ enable_lint = self.args.enable_auto_fix_lint
153
+
154
+ if enable_lint:
155
+ try:
156
+ if self.shadow_linter and self.shadow_manager:
157
+ # 对新创建的文件进行 lint 检查
158
+ # 由于没有shadow系统,需要先创建shadow文件
159
+ shadow_path = self.shadow_manager.to_shadow_path(abs_file_path)
160
+ os.makedirs(os.path.dirname(shadow_path), exist_ok=True)
161
+ with open(shadow_path, 'w', encoding='utf-8') as f:
162
+ f.write(content)
163
+
164
+ lint_results = self.shadow_linter.lint_shadow_file(shadow_path)
165
+
166
+ if lint_results and lint_results.issues:
167
+ has_lint_issues = True
168
+ # 格式化 lint 问题
169
+ formatted_issues = self._format_lint_issues(lint_results)
170
+ lint_message = f"\n\n代码质量检查发现 {len(lint_results.issues)} 个问题:\n{formatted_issues}"
171
+ else:
172
+ lint_message = "\n\n代码质量检查通过,未发现问题。"
173
+ if self.agent.linter:
174
+ lint_results = self.agent.linter.lint_file(file_path)
175
+ if lint_results and lint_results.issues:
176
+ has_lint_issues = True
177
+ # 格式化 lint 问题
178
+ formatted_issues = self._format_lint_issues(lint_results)
179
+ lint_message = f"\n\n代码质量检查发现 {len(lint_results.issues)} 个问题:\n{formatted_issues}"
180
+ except Exception as e:
181
+ logger.error(f"Lint 检查失败: {str(e)}")
182
+ lint_message = "\n\n尝试进行代码质量检查时出错。"
183
+ else:
184
+ logger.info("代码质量检查已禁用")
185
+
186
+ # 构建包含 lint 结果的返回消息
187
+ message = f"{file_path}"
188
+
189
+ # 将 lint 消息添加到结果中,如果启用了Lint
190
+ if enable_lint:
191
+ message += lint_message
192
+
193
+ # 附加 lint 结果到返回内容
194
+ result_content = {
195
+ "content": content,
196
+ }
197
+
198
+ # 只有在启用Lint时才添加Lint结果
199
+ if enable_lint:
200
+ result_content["lint_results"] = {
201
+ "has_issues": has_lint_issues,
202
+ "issues": formatted_issues if has_lint_issues else None
203
+ }
204
+
205
+ return ToolResult(success=True, message=message, content=result_content)
56
206
  except Exception as e:
57
207
  logger.error(f"Error writing to file '{file_path}': {str(e)}")
58
208
  return ToolResult(success=False, message=f"An error occurred while writing to the file: {str(e)}")
@@ -2,7 +2,6 @@ from pydantic import BaseModel
2
2
  from typing import List, Dict, Any, Callable, Optional, Type
3
3
  from pydantic import SkipValidation
4
4
 
5
-
6
5
  # Result class used by Tool Resolvers
7
6
  class ToolResult(BaseModel):
8
7
  success: bool
@@ -115,7 +114,7 @@ TOOL_MODEL_MAP: Dict[str, Type[BaseTool]] = {
115
114
  "attempt_completion": AttemptCompletionTool,
116
115
  "plan_mode_respond": PlanModeRespondTool,
117
116
  "use_mcp_tool": UseMcpTool,
118
- "list_package_info": ListPackageInfoTool,
117
+ "list_package_info": ListPackageInfoTool,
119
118
  }
120
119
 
121
120
  class FileChangeEntry(BaseModel):
@@ -93,8 +93,7 @@ TOOL_DISPLAY_MESSAGES: Dict[Type[BaseTool], Dict[str, str]] = {
93
93
  "[dim]工具:[/dim] [blue]{{ tool_name }}[/]\n"
94
94
  "[dim]参数:[/dim] {{ arguments_snippet }}{{ ellipsis }}"
95
95
  )
96
- }
97
- # AttemptCompletionTool is handled separately in the display loop
96
+ }
98
97
  }
99
98
 
100
99
  def get_tool_display_message(tool: BaseTool) -> str:
@@ -172,7 +171,7 @@ def get_tool_display_message(tool: BaseTool) -> str:
172
171
  "tool_name": tool.tool_name,
173
172
  "arguments_snippet": snippet,
174
173
  "ellipsis": '...' if len(args_str) > 100 else ''
175
- }
174
+ }
176
175
  else:
177
176
  # Generic context for tools not specifically handled above
178
177
  context = tool.model_dump()
@@ -0,0 +1,64 @@
1
+ """
2
+ 用于对文件进行编译的模块。
3
+ """
4
+
5
+ import os
6
+ from typing import Optional
7
+ from autocoder.compilers.compiler_factory import CompilerFactory
8
+ from autocoder.compilers.models import ProjectCompilationResult, FileCompilationResult
9
+
10
+ class NormalCompiler:
11
+ """
12
+ 用于对文件进行编译的类。
13
+ """
14
+
15
+ def __init__(self, project_dir: str, verbose: bool = False):
16
+ """
17
+ 初始化编译器。
18
+
19
+ 参数:
20
+ project_dir (str): 项目根目录路径
21
+ verbose (bool): 是否启用详细输出
22
+ """
23
+ self.project_dir = project_dir
24
+ self.compiler = CompilerFactory.create_compiler(
25
+ language="provided",
26
+ config_path=os.path.join(self.project_dir, ".auto-coder", "projects", "compiler.yml")
27
+ )
28
+
29
+ def compile_file(self, file_path: str) -> FileCompilationResult:
30
+ """
31
+ 编译单个文件。
32
+
33
+ 参数:
34
+ file_path (str): 文件路径
35
+
36
+ 返回:
37
+ FileCompilationResult: 编译结果
38
+ """
39
+ return FileCompilationResult(
40
+ file_path=file_path,
41
+ success=True,
42
+ language="provided",
43
+ errors=[],
44
+ error_message=None,
45
+ warning_count=0,
46
+ error_count=0,
47
+ info_count=0,
48
+ execution_time_ms=0,
49
+ output_file=None
50
+ )
51
+
52
+ def compile_all_files(self, target_compiler_name: Optional[str] = None) -> ProjectCompilationResult:
53
+ """
54
+ 编译项目中的所有文件。
55
+
56
+ 参数:
57
+ target_compiler_name (Optional[str]): 目标编译器名称
58
+
59
+ 返回:
60
+ ProjectCompilationResult: 项目编译结果
61
+ """
62
+ result = self.compiler.compile_project(self.project_dir, target_compiler_name)
63
+ result.project_path = self.project_dir
64
+ return result
@@ -6,6 +6,7 @@ from datetime import datetime
6
6
  import glob
7
7
  import json
8
8
  import re
9
+ import time
9
10
  from pathlib import Path
10
11
  import byzerllm
11
12
 
@@ -22,6 +23,10 @@ class EventManagerSingleton:
22
23
  _default_instance: Optional[EventManager] = None
23
24
  _instances: Dict[str, EventManager] = {}
24
25
  _lock = threading.RLock() # 用于线程安全操作的锁
26
+ _cleanup_thread: Optional[threading.Thread] = None
27
+ _stop_cleanup = threading.Event()
28
+ _max_event_files = 100 # 最大保留的事件文件数量
29
+ _cleanup_interval = 60 # 清理线程执行间隔,单位秒
25
30
 
26
31
  @classmethod
27
32
  def get_instance(cls, event_file: Optional[str] = None) -> EventManager:
@@ -34,15 +39,26 @@ class EventManagerSingleton:
34
39
  Returns:
35
40
  EventManager: The appropriate EventManager instance
36
41
  """
42
+ # 确保清理线程已启动
43
+ cls._ensure_cleanup_thread_started()
44
+
37
45
  if event_file is None:
38
46
  # Use default instance logic
39
47
  if cls._default_instance is None:
40
- cls._default_instance = EventManager(JsonlEventStore(os.path.join(".auto-coder", "events", "events.jsonl")))
48
+ logger.info("Creating new default EventManager instance.")
49
+ event_store_path = os.path.join(".auto-coder", "events", "events.jsonl")
50
+ logger.debug(f"Default EventManager using event store: {event_store_path}")
51
+ cls._default_instance = EventManager(JsonlEventStore(event_store_path))
52
+ else:
53
+ logger.debug("Returning existing default EventManager instance.")
41
54
  return cls._default_instance
42
55
 
43
56
  # If event_file is provided, use it as a key to store/retrieve EventManager instances
44
57
  if event_file not in cls._instances:
58
+ logger.info(f"Creating new EventManager instance for event file: {event_file}")
45
59
  cls._instances[event_file] = EventManager(JsonlEventStore(event_file))
60
+ else:
61
+ logger.debug(f"Returning existing EventManager instance for event file: {event_file}")
46
62
 
47
63
  return cls._instances[event_file]
48
64
 
@@ -52,7 +68,11 @@ class EventManagerSingleton:
52
68
  重置单例实例。主要用于测试或需要更改事件文件时。
53
69
  """
54
70
  with cls._lock:
55
- cls._default_instance = None
71
+ if cls._default_instance is not None:
72
+ logger.info("Resetting default EventManager instance.")
73
+ cls._default_instance = None
74
+ else:
75
+ logger.debug("Default EventManager instance was already None. No reset needed.")
56
76
 
57
77
  @classmethod
58
78
  def set_default_event_file(cls, event_file: str) -> None:
@@ -68,6 +88,110 @@ class EventManagerSingleton:
68
88
  cls._default_event_file = event_file
69
89
  else:
70
90
  logger.warning("尝试更改默认事件文件,但实例已存在。请先调用reset_instance()。")
91
+
92
+ @classmethod
93
+ def _ensure_cleanup_thread_started(cls) -> None:
94
+ """
95
+ 确保清理线程已启动。如果尚未启动,则启动线程。
96
+ """
97
+ with cls._lock:
98
+ if cls._cleanup_thread is None or not cls._cleanup_thread.is_alive():
99
+ logger.info("启动事件文件清理线程")
100
+ cls._stop_cleanup.clear()
101
+ cls._cleanup_thread = threading.Thread(
102
+ target=cls._cleanup_event_files_thread,
103
+ daemon=True,
104
+ name="EventFilesCleanupThread"
105
+ )
106
+ cls._cleanup_thread.start()
107
+
108
+ @classmethod
109
+ def _cleanup_event_files_thread(cls) -> None:
110
+ """
111
+ 定时清理线程的主函数。定期执行清理操作,确保事件目录只保留最新的指定数量文件。
112
+ """
113
+ logger.info(f"事件文件清理线程已启动,将保留最新的{cls._max_event_files}个文件")
114
+ while not cls._stop_cleanup.is_set():
115
+ try:
116
+ cls._cleanup_event_files()
117
+ # 等待指定时间或直到停止信号
118
+ cls._stop_cleanup.wait(cls._cleanup_interval)
119
+ except Exception as e:
120
+ logger.error(f"事件文件清理过程中发生错误: {e}")
121
+ # 出错后等待一段时间再重试
122
+ time.sleep(60)
123
+
124
+ @classmethod
125
+ def _cleanup_event_files(cls) -> None:
126
+ """
127
+ 清理事件文件,只保留最新的指定数量文件。
128
+ """
129
+ logger.info("开始清理事件文件...")
130
+
131
+ # 确定事件文件所在目录
132
+ events_dir = os.path.join(os.getcwd(),".auto-coder", "events")
133
+
134
+ # 确保目录存在
135
+ if not os.path.exists(events_dir):
136
+ logger.warning(f"事件目录不存在: {events_dir}")
137
+ return
138
+
139
+ # 获取所有事件文件
140
+ event_file_pattern = os.path.join(events_dir, "*.jsonl")
141
+ event_files = glob.glob(event_file_pattern)
142
+
143
+ if not event_files:
144
+ logger.info(f"未找到任何事件文件: {event_file_pattern}")
145
+ return
146
+
147
+ # 排除默认的events.jsonl文件
148
+ event_files = [f for f in event_files if os.path.basename(f) != "events.jsonl"]
149
+
150
+ if len(event_files) <= cls._max_event_files:
151
+ logger.info(f"事件文件数量({len(event_files)})未超过最大保留数量({cls._max_event_files}),无需清理")
152
+ return
153
+
154
+ # 解析文件名中的时间戳,格式为 uuid_YYYYMMDD-HHMMSS.jsonl
155
+ def extract_timestamp(file_path):
156
+ file_name = os.path.basename(file_path)
157
+ match = re.search(r'_(\d{8}-\d{6})\.jsonl$', file_name)
158
+ if match:
159
+ timestamp_str = match.group(1)
160
+ try:
161
+ return datetime.strptime(timestamp_str, "%Y%m%d-%H%M%S")
162
+ except ValueError:
163
+ return datetime.fromtimestamp(0) # 默认最早时间
164
+ return datetime.fromtimestamp(0) # 默认最早时间
165
+
166
+ # 按时间戳从新到旧排序
167
+ sorted_files = sorted(event_files, key=extract_timestamp, reverse=True)
168
+
169
+ # 保留最新的文件,删除其余文件
170
+ files_to_keep = sorted_files[:cls._max_event_files]
171
+ files_to_delete = sorted_files[cls._max_event_files:]
172
+
173
+ for file_path in files_to_delete:
174
+ try:
175
+ os.remove(file_path)
176
+ logger.info(f"已删除旧事件文件: {file_path}")
177
+ except Exception as e:
178
+ logger.error(f"删除事件文件失败 {file_path}: {e}")
179
+
180
+ logger.info(f"事件文件清理完成,删除了{len(files_to_delete)}个旧文件,保留了{len(files_to_keep)}个最新文件")
181
+
182
+ @classmethod
183
+ def stop_cleanup_thread(cls) -> None:
184
+ """
185
+ 停止清理线程,通常在应用程序退出时调用。
186
+ """
187
+ logger.info("正在停止事件文件清理线程...")
188
+ cls._stop_cleanup.set()
189
+ if cls._cleanup_thread and cls._cleanup_thread.is_alive():
190
+ cls._cleanup_thread.join(timeout=5)
191
+ if cls._cleanup_thread.is_alive():
192
+ logger.warning("清理线程未在指定时间内停止")
193
+ else:
194
+ logger.info("清理线程已成功停止")
71
195
 
72
196
  def get_event_file_path(file_id:str,project_path: Optional[str] = None) -> str:
73
197
  if project_path is None:
@@ -95,9 +219,12 @@ def gengerate_event_file_path(project_path: Optional[str] = None) -> Tuple[str,s
95
219
 
96
220
  # 要使用绝对路径,否则会被认为产生两个event_manager
97
221
  if project_path is None:
98
- return os.path.join(os.getcwd(),".auto-coder", "events", file_name),file_id
222
+ full_path = os.path.join(os.getcwd(),".auto-coder", "events", file_name)
99
223
  else:
100
- return os.path.join(project_path, ".auto-coder", "events", file_name),file_id
224
+ full_path = os.path.join(project_path, ".auto-coder", "events", file_name)
225
+
226
+ logger.info(f"Generated event file path: {full_path}, file_id: {file_id}")
227
+ return full_path, file_id
101
228
 
102
229
  @byzerllm.prompt()
103
230
  def _format_events_prompt(event_files: List[Dict]) -> str:
@@ -148,6 +275,7 @@ def to_events_prompt(limit: int = 5, project_path: Optional[str] = None) -> str:
148
275
  Returns:
149
276
  格式化后的事件提示文本
150
277
  """
278
+ logger.info(f"Generating events prompt with limit={limit}, project_path={project_path}")
151
279
  # 确定事件文件所在目录
152
280
  if project_path is None:
153
281
  events_dir = os.path.join(".auto-coder", "events")
@@ -164,6 +292,7 @@ def to_events_prompt(limit: int = 5, project_path: Optional[str] = None) -> str:
164
292
  event_files = glob.glob(event_file_pattern)
165
293
 
166
294
  if not event_files:
295
+ logger.info(f"No event files found in pattern: {event_file_pattern}")
167
296
  logger.warning(f"未找到任何事件文件: {event_file_pattern}")
168
297
  return "未找到任何事件记录。"
169
298