auto-coder 0.1.396__py3-none-any.whl → 0.1.398__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 (31) hide show
  1. {auto_coder-0.1.396.dist-info → auto_coder-0.1.398.dist-info}/METADATA +2 -2
  2. {auto_coder-0.1.396.dist-info → auto_coder-0.1.398.dist-info}/RECORD +31 -12
  3. autocoder/auto_coder_rag.py +1 -0
  4. autocoder/chat_auto_coder.py +3 -0
  5. autocoder/common/conversations/__init__.py +84 -39
  6. autocoder/common/conversations/backup/__init__.py +14 -0
  7. autocoder/common/conversations/backup/backup_manager.py +564 -0
  8. autocoder/common/conversations/backup/restore_manager.py +546 -0
  9. autocoder/common/conversations/cache/__init__.py +16 -0
  10. autocoder/common/conversations/cache/base_cache.py +89 -0
  11. autocoder/common/conversations/cache/cache_manager.py +368 -0
  12. autocoder/common/conversations/cache/memory_cache.py +224 -0
  13. autocoder/common/conversations/config.py +195 -0
  14. autocoder/common/conversations/exceptions.py +72 -0
  15. autocoder/common/conversations/file_locker.py +145 -0
  16. autocoder/common/conversations/manager.py +917 -0
  17. autocoder/common/conversations/models.py +154 -0
  18. autocoder/common/conversations/search/__init__.py +15 -0
  19. autocoder/common/conversations/search/filter_manager.py +431 -0
  20. autocoder/common/conversations/search/text_searcher.py +366 -0
  21. autocoder/common/conversations/storage/__init__.py +16 -0
  22. autocoder/common/conversations/storage/base_storage.py +82 -0
  23. autocoder/common/conversations/storage/file_storage.py +267 -0
  24. autocoder/common/conversations/storage/index_manager.py +317 -0
  25. autocoder/common/rag_manager/rag_manager.py +16 -18
  26. autocoder/rags.py +74 -24
  27. autocoder/version.py +1 -1
  28. {auto_coder-0.1.396.dist-info → auto_coder-0.1.398.dist-info}/LICENSE +0 -0
  29. {auto_coder-0.1.396.dist-info → auto_coder-0.1.398.dist-info}/WHEEL +0 -0
  30. {auto_coder-0.1.396.dist-info → auto_coder-0.1.398.dist-info}/entry_points.txt +0 -0
  31. {auto_coder-0.1.396.dist-info → auto_coder-0.1.398.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,195 @@
1
+ """
2
+ PersistConversationManager 配置类定义
3
+ """
4
+
5
+ import os
6
+ import json
7
+ from dataclasses import dataclass, field
8
+ from typing import Optional, Dict, Any
9
+ from copy import deepcopy
10
+
11
+
12
+ @dataclass
13
+ class ConversationManagerConfig:
14
+ """对话管理器配置类"""
15
+
16
+ storage_path: str = "./.auto-coder/conversations"
17
+ max_cache_size: int = 100
18
+ cache_ttl: float = 300.0
19
+ lock_timeout: float = 10.0
20
+ backup_enabled: bool = True
21
+ backup_interval: float = 3600.0
22
+ max_backups: int = 10
23
+ enable_compression: bool = False
24
+ log_level: str = "INFO"
25
+
26
+ def __post_init__(self):
27
+ """配置验证"""
28
+ self._validate()
29
+
30
+ def _validate(self):
31
+ """验证配置数据"""
32
+ # 验证存储路径
33
+ if not self.storage_path or not isinstance(self.storage_path, str):
34
+ raise ValueError("存储路径不能为空")
35
+
36
+ # 验证缓存大小
37
+ if not isinstance(self.max_cache_size, int) or self.max_cache_size <= 0:
38
+ raise ValueError("缓存大小必须是正整数")
39
+
40
+ # 验证缓存TTL
41
+ if not isinstance(self.cache_ttl, (int, float)) or self.cache_ttl <= 0:
42
+ raise ValueError("缓存TTL必须是正数")
43
+
44
+ # 验证锁超时
45
+ if not isinstance(self.lock_timeout, (int, float)) or self.lock_timeout <= 0:
46
+ raise ValueError("锁超时时间必须是正数")
47
+
48
+ # 验证备份间隔
49
+ if not isinstance(self.backup_interval, (int, float)) or self.backup_interval <= 0:
50
+ raise ValueError("备份间隔必须是正数")
51
+
52
+ # 验证最大备份数
53
+ if not isinstance(self.max_backups, int) or self.max_backups <= 0:
54
+ raise ValueError("最大备份数必须是正整数")
55
+
56
+ # 验证日志级别
57
+ valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
58
+ if self.log_level not in valid_levels:
59
+ raise ValueError(f"无效的日志级别: {self.log_level},有效级别: {valid_levels}")
60
+
61
+ def to_dict(self) -> Dict[str, Any]:
62
+ """转换为字典"""
63
+ return {
64
+ "storage_path": self.storage_path,
65
+ "max_cache_size": self.max_cache_size,
66
+ "cache_ttl": self.cache_ttl,
67
+ "lock_timeout": self.lock_timeout,
68
+ "backup_enabled": self.backup_enabled,
69
+ "backup_interval": self.backup_interval,
70
+ "max_backups": self.max_backups,
71
+ "enable_compression": self.enable_compression,
72
+ "log_level": self.log_level
73
+ }
74
+
75
+ @classmethod
76
+ def from_dict(cls, data: Dict[str, Any]) -> "ConversationManagerConfig":
77
+ """从字典创建配置"""
78
+ # 创建默认配置
79
+ config = cls()
80
+
81
+ # 更新配置字段
82
+ for key, value in data.items():
83
+ if hasattr(config, key):
84
+ setattr(config, key, value)
85
+
86
+ # 重新验证
87
+ config._validate()
88
+
89
+ return config
90
+
91
+ @classmethod
92
+ def from_env(cls, prefix: str = "CONVERSATION_") -> "ConversationManagerConfig":
93
+ """从环境变量创建配置"""
94
+ config = cls()
95
+
96
+ # 环境变量映射
97
+ env_mapping = {
98
+ f"{prefix}STORAGE_PATH": "storage_path",
99
+ f"{prefix}MAX_CACHE_SIZE": "max_cache_size",
100
+ f"{prefix}CACHE_TTL": "cache_ttl",
101
+ f"{prefix}LOCK_TIMEOUT": "lock_timeout",
102
+ f"{prefix}BACKUP_ENABLED": "backup_enabled",
103
+ f"{prefix}BACKUP_INTERVAL": "backup_interval",
104
+ f"{prefix}MAX_BACKUPS": "max_backups",
105
+ f"{prefix}ENABLE_COMPRESSION": "enable_compression",
106
+ f"{prefix}LOG_LEVEL": "log_level"
107
+ }
108
+
109
+ for env_key, attr_name in env_mapping.items():
110
+ env_value = os.environ.get(env_key)
111
+ if env_value is not None:
112
+ # 类型转换
113
+ try:
114
+ if attr_name in ["max_cache_size", "max_backups"]:
115
+ value = int(env_value)
116
+ elif attr_name in ["cache_ttl", "lock_timeout", "backup_interval"]:
117
+ value = float(env_value)
118
+ elif attr_name in ["backup_enabled", "enable_compression"]:
119
+ value = env_value.lower() in ["true", "1", "yes", "on"]
120
+ else:
121
+ value = env_value
122
+
123
+ setattr(config, attr_name, value)
124
+ except (ValueError, TypeError) as e:
125
+ raise ValueError(f"环境变量 {env_key} 的值 '{env_value}' 无效: {e}")
126
+
127
+ # 重新验证
128
+ config._validate()
129
+
130
+ return config
131
+
132
+ def save_to_file(self, file_path: str):
133
+ """保存配置到文件"""
134
+ # 确保目录存在
135
+ os.makedirs(os.path.dirname(file_path), exist_ok=True)
136
+
137
+ with open(file_path, 'w', encoding='utf-8') as f:
138
+ json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
139
+
140
+ @classmethod
141
+ def load_from_file(cls, file_path: str) -> "ConversationManagerConfig":
142
+ """从文件加载配置"""
143
+ if not os.path.exists(file_path):
144
+ raise FileNotFoundError(f"配置文件不存在: {file_path}")
145
+
146
+ try:
147
+ with open(file_path, 'r', encoding='utf-8') as f:
148
+ data = json.load(f)
149
+
150
+ return cls.from_dict(data)
151
+ except json.JSONDecodeError as e:
152
+ raise ValueError(f"配置文件格式错误: {e}")
153
+
154
+ def copy(self) -> "ConversationManagerConfig":
155
+ """创建配置的深拷贝"""
156
+ return ConversationManagerConfig.from_dict(self.to_dict())
157
+
158
+ def update(self, **kwargs):
159
+ """更新配置字段"""
160
+ # 先备份当前配置
161
+ backup_values = {}
162
+ for key in kwargs.keys():
163
+ if hasattr(self, key):
164
+ backup_values[key] = getattr(self, key)
165
+ else:
166
+ raise AttributeError(f"配置类没有属性: {key}")
167
+
168
+ # 尝试更新
169
+ try:
170
+ for key, value in kwargs.items():
171
+ setattr(self, key, value)
172
+ # 重新验证
173
+ self._validate()
174
+ except Exception:
175
+ # 如果验证失败,恢复原值
176
+ for key, value in backup_values.items():
177
+ setattr(self, key, value)
178
+ raise
179
+
180
+ def __repr__(self) -> str:
181
+ """字符串表示"""
182
+ return (f"ConversationManagerConfig("
183
+ f"storage_path='{self.storage_path}', "
184
+ f"max_cache_size={self.max_cache_size}, "
185
+ f"cache_ttl={self.cache_ttl}, "
186
+ f"lock_timeout={self.lock_timeout}, "
187
+ f"backup_enabled={self.backup_enabled}, "
188
+ f"log_level='{self.log_level}')")
189
+
190
+ def __eq__(self, other) -> bool:
191
+ """相等性比较"""
192
+ if not isinstance(other, ConversationManagerConfig):
193
+ return False
194
+
195
+ return self.to_dict() == other.to_dict()
@@ -0,0 +1,72 @@
1
+ """
2
+ PersistConversationManager 异常类定义
3
+ """
4
+
5
+
6
+ class ConversationManagerError(Exception):
7
+ """对话管理器基础异常类"""
8
+
9
+ def __init__(self, message="对话管理器发生错误", error_code="GENERAL_ERROR"):
10
+ super().__init__(message)
11
+ self.error_code = error_code
12
+
13
+
14
+ class ConversationNotFoundError(ConversationManagerError):
15
+ """对话不存在异常"""
16
+
17
+ def __init__(self, conversation_id):
18
+ # 检查是否是类似ID的字符串(不包含中文等)
19
+ if (isinstance(conversation_id, str) and len(conversation_id) > 0 and
20
+ not any(ord(c) > 127 or c.isspace() for c in conversation_id)):
21
+ message = f"对话未找到: {conversation_id}"
22
+ else:
23
+ message = conversation_id # 自定义消息
24
+ super().__init__(message, error_code="CONVERSATION_NOT_FOUND")
25
+
26
+
27
+ class MessageNotFoundError(ConversationManagerError):
28
+ """消息不存在异常"""
29
+
30
+ def __init__(self, message_id):
31
+ # 检查是否是类似ID的字符串(不包含中文等)
32
+ if (isinstance(message_id, str) and len(message_id) > 0 and
33
+ not any(ord(c) > 127 or c.isspace() for c in message_id)):
34
+ message = f"消息未找到: {message_id}"
35
+ else:
36
+ message = message_id # 自定义消息
37
+ super().__init__(message, error_code="MESSAGE_NOT_FOUND")
38
+
39
+
40
+ class ConcurrencyError(ConversationManagerError):
41
+ """并发访问异常"""
42
+
43
+ def __init__(self, message="并发访问冲突"):
44
+ super().__init__(message, error_code="CONCURRENCY_ERROR")
45
+
46
+
47
+ class DataIntegrityError(ConversationManagerError):
48
+ """数据完整性异常"""
49
+
50
+ def __init__(self, message="数据完整性检查失败"):
51
+ super().__init__(message, error_code="DATA_INTEGRITY_ERROR")
52
+
53
+
54
+ class LockTimeoutError(ConversationManagerError):
55
+ """锁超时异常"""
56
+
57
+ def __init__(self, message="锁获取超时"):
58
+ super().__init__(message, error_code="LOCK_TIMEOUT_ERROR")
59
+
60
+
61
+ class BackupError(ConversationManagerError):
62
+ """备份操作异常"""
63
+
64
+ def __init__(self, message="备份操作失败"):
65
+ super().__init__(message, error_code="BACKUP_ERROR")
66
+
67
+
68
+ class RestoreError(ConversationManagerError):
69
+ """恢复操作异常"""
70
+
71
+ def __init__(self, message="恢复操作失败"):
72
+ super().__init__(message, error_code="RESTORE_ERROR")
@@ -0,0 +1,145 @@
1
+ """
2
+ PersistConversationManager 跨平台文件锁实现
3
+ """
4
+
5
+ import os
6
+ import sys
7
+ import time
8
+ import contextlib
9
+ from typing import Generator
10
+ from .exceptions import LockTimeoutError
11
+
12
+
13
+ # 跨平台文件锁实现
14
+ if sys.platform == "win32":
15
+ import msvcrt
16
+
17
+ class FileLocker:
18
+ """Windows 平台文件锁实现"""
19
+
20
+ def __init__(self, lock_file: str, timeout: float = 10.0):
21
+ self.lock_file = lock_file
22
+ self.timeout = timeout
23
+ self.lock_fd = None
24
+
25
+ @contextlib.contextmanager
26
+ def acquire_read_lock(self) -> Generator[None, None, None]:
27
+ """获取读锁(共享锁)- Windows 实现"""
28
+ self._acquire_lock(shared=True)
29
+ try:
30
+ yield
31
+ finally:
32
+ self._release_lock()
33
+
34
+ @contextlib.contextmanager
35
+ def acquire_write_lock(self) -> Generator[None, None, None]:
36
+ """获取写锁(排他锁)- Windows 实现"""
37
+ self._acquire_lock(shared=False)
38
+ try:
39
+ yield
40
+ finally:
41
+ self._release_lock()
42
+
43
+ def _acquire_lock(self, shared: bool = False):
44
+ """Windows 文件锁实现"""
45
+ start_time = time.time()
46
+ while True:
47
+ try:
48
+ # 确保锁文件目录存在
49
+ os.makedirs(os.path.dirname(self.lock_file), exist_ok=True)
50
+
51
+ # 打开文件用于锁定
52
+ self.lock_fd = open(self.lock_file, 'w+')
53
+
54
+ # Windows 下使用 msvcrt.locking
55
+ # 注意:Windows 不直接支持共享锁,这里简化处理
56
+ msvcrt.locking(self.lock_fd.fileno(), msvcrt.LK_NBLCK, 1)
57
+ return
58
+
59
+ except (IOError, OSError):
60
+ if self.lock_fd:
61
+ self.lock_fd.close()
62
+ self.lock_fd = None
63
+
64
+ if time.time() - start_time > self.timeout:
65
+ raise LockTimeoutError(f"Failed to acquire lock on {self.lock_file} within {self.timeout}s")
66
+
67
+ time.sleep(0.1)
68
+
69
+ def _release_lock(self):
70
+ """释放锁"""
71
+ if self.lock_fd:
72
+ try:
73
+ msvcrt.locking(self.lock_fd.fileno(), msvcrt.LK_UNLCK, 1)
74
+ self.lock_fd.close()
75
+ except:
76
+ # 忽略释放时的错误
77
+ pass
78
+ finally:
79
+ self.lock_fd = None
80
+
81
+ else:
82
+ # Unix/Linux/Mac 系统使用 fcntl
83
+ import fcntl
84
+
85
+ class FileLocker:
86
+ """Unix/Linux/Mac 平台文件锁实现"""
87
+
88
+ def __init__(self, lock_file: str, timeout: float = 10.0):
89
+ self.lock_file = lock_file
90
+ self.timeout = timeout
91
+ self.lock_fd = None
92
+
93
+ @contextlib.contextmanager
94
+ def acquire_read_lock(self) -> Generator[None, None, None]:
95
+ """获取读锁(共享锁)- Unix/Linux/Mac 实现"""
96
+ self._acquire_lock(fcntl.LOCK_SH)
97
+ try:
98
+ yield
99
+ finally:
100
+ self._release_lock()
101
+
102
+ @contextlib.contextmanager
103
+ def acquire_write_lock(self) -> Generator[None, None, None]:
104
+ """获取写锁(排他锁)- Unix/Linux/Mac 实现"""
105
+ self._acquire_lock(fcntl.LOCK_EX)
106
+ try:
107
+ yield
108
+ finally:
109
+ self._release_lock()
110
+
111
+ def _acquire_lock(self, lock_type: int):
112
+ """Unix/Linux/Mac 文件锁实现"""
113
+ start_time = time.time()
114
+
115
+ # 确保锁文件目录存在
116
+ os.makedirs(os.path.dirname(self.lock_file), exist_ok=True)
117
+
118
+ # 打开锁文件
119
+ self.lock_fd = open(self.lock_file, 'w+')
120
+
121
+ while True:
122
+ try:
123
+ # 尝试获取非阻塞锁
124
+ fcntl.flock(self.lock_fd.fileno(), lock_type | fcntl.LOCK_NB)
125
+ return
126
+
127
+ except (IOError, OSError):
128
+ if time.time() - start_time > self.timeout:
129
+ self.lock_fd.close()
130
+ self.lock_fd = None
131
+ raise LockTimeoutError(f"Failed to acquire lock on {self.lock_file} within {self.timeout}s")
132
+
133
+ time.sleep(0.1)
134
+
135
+ def _release_lock(self):
136
+ """释放锁"""
137
+ if self.lock_fd:
138
+ try:
139
+ fcntl.flock(self.lock_fd.fileno(), fcntl.LOCK_UN)
140
+ self.lock_fd.close()
141
+ except:
142
+ # 忽略释放时的错误
143
+ pass
144
+ finally:
145
+ self.lock_fd = None