auto-coder 0.1.397__py3-none-any.whl → 0.1.399__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.399.dist-info/METADATA +396 -0
- {auto_coder-0.1.397.dist-info → auto_coder-0.1.399.dist-info}/RECORD +81 -28
- {auto_coder-0.1.397.dist-info → auto_coder-0.1.399.dist-info}/WHEEL +1 -1
- {auto_coder-0.1.397.dist-info → auto_coder-0.1.399.dist-info}/entry_points.txt +2 -0
- autocoder/agent/base_agentic/base_agent.py +2 -2
- autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +1 -1
- autocoder/agent/entry_command_agent/__init__.py +29 -0
- autocoder/agent/entry_command_agent/auto_tool.py +61 -0
- autocoder/agent/entry_command_agent/chat.py +475 -0
- autocoder/agent/entry_command_agent/designer.py +53 -0
- autocoder/agent/entry_command_agent/generate_command.py +50 -0
- autocoder/agent/entry_command_agent/project_reader.py +58 -0
- autocoder/agent/entry_command_agent/voice2text.py +71 -0
- autocoder/auto_coder.py +23 -548
- autocoder/auto_coder_rag.py +1 -0
- autocoder/auto_coder_runner.py +510 -8
- autocoder/chat/rules_command.py +1 -1
- autocoder/chat_auto_coder.py +8 -0
- autocoder/common/ac_style_command_parser/__init__.py +15 -0
- autocoder/common/ac_style_command_parser/example.py +7 -0
- autocoder/{command_parser.py → common/ac_style_command_parser/parser.py} +1 -33
- autocoder/common/ac_style_command_parser/test_parser.py +516 -0
- autocoder/common/command_completer_v2.py +1 -1
- autocoder/common/command_file_manager/examples.py +22 -8
- autocoder/common/command_file_manager/manager.py +37 -6
- autocoder/common/conversations/__init__.py +84 -39
- autocoder/common/conversations/backup/__init__.py +14 -0
- autocoder/common/conversations/backup/backup_manager.py +564 -0
- autocoder/common/conversations/backup/restore_manager.py +546 -0
- autocoder/common/conversations/cache/__init__.py +16 -0
- autocoder/common/conversations/cache/base_cache.py +89 -0
- autocoder/common/conversations/cache/cache_manager.py +368 -0
- autocoder/common/conversations/cache/memory_cache.py +224 -0
- autocoder/common/conversations/config.py +195 -0
- autocoder/common/conversations/exceptions.py +72 -0
- autocoder/common/conversations/file_locker.py +145 -0
- autocoder/common/conversations/get_conversation_manager.py +143 -0
- autocoder/common/conversations/manager.py +1028 -0
- autocoder/common/conversations/models.py +154 -0
- autocoder/common/conversations/search/__init__.py +15 -0
- autocoder/common/conversations/search/filter_manager.py +431 -0
- autocoder/common/conversations/search/text_searcher.py +366 -0
- autocoder/common/conversations/storage/__init__.py +16 -0
- autocoder/common/conversations/storage/base_storage.py +82 -0
- autocoder/common/conversations/storage/file_storage.py +267 -0
- autocoder/common/conversations/storage/index_manager.py +406 -0
- autocoder/common/v2/agent/agentic_edit.py +131 -18
- autocoder/common/v2/agent/agentic_edit_types.py +10 -0
- autocoder/common/v2/code_auto_generate_editblock.py +10 -2
- autocoder/dispacher/__init__.py +10 -0
- autocoder/rags.py +73 -50
- autocoder/run_context.py +1 -0
- autocoder/sdk/__init__.py +188 -0
- autocoder/sdk/cli/__init__.py +15 -0
- autocoder/sdk/cli/__main__.py +26 -0
- autocoder/sdk/cli/completion_wrapper.py +38 -0
- autocoder/sdk/cli/formatters.py +211 -0
- autocoder/sdk/cli/handlers.py +174 -0
- autocoder/sdk/cli/install_completion.py +301 -0
- autocoder/sdk/cli/main.py +284 -0
- autocoder/sdk/cli/options.py +72 -0
- autocoder/sdk/constants.py +102 -0
- autocoder/sdk/core/__init__.py +20 -0
- autocoder/sdk/core/auto_coder_core.py +867 -0
- autocoder/sdk/core/bridge.py +497 -0
- autocoder/sdk/example.py +0 -0
- autocoder/sdk/exceptions.py +72 -0
- autocoder/sdk/models/__init__.py +19 -0
- autocoder/sdk/models/messages.py +209 -0
- autocoder/sdk/models/options.py +194 -0
- autocoder/sdk/models/responses.py +311 -0
- autocoder/sdk/session/__init__.py +32 -0
- autocoder/sdk/session/session.py +106 -0
- autocoder/sdk/session/session_manager.py +56 -0
- autocoder/sdk/utils/__init__.py +24 -0
- autocoder/sdk/utils/formatters.py +216 -0
- autocoder/sdk/utils/io_utils.py +302 -0
- autocoder/sdk/utils/validators.py +287 -0
- autocoder/version.py +2 -1
- auto_coder-0.1.397.dist-info/METADATA +0 -111
- autocoder/common/conversations/compatibility.py +0 -303
- autocoder/common/conversations/conversation_manager.py +0 -502
- autocoder/common/conversations/example.py +0 -152
- {auto_coder-0.1.397.dist-info → auto_coder-0.1.399.dist-info/licenses}/LICENSE +0 -0
- {auto_coder-0.1.397.dist-info → auto_coder-0.1.399.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""
|
|
2
|
+
文件存储实现
|
|
3
|
+
|
|
4
|
+
基于JSON文件的对话存储实现,支持原子写入和数据完整性检查。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import json
|
|
9
|
+
import tempfile
|
|
10
|
+
import re
|
|
11
|
+
from typing import Optional, List, Dict, Any
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from .base_storage import BaseStorage
|
|
15
|
+
from ..exceptions import DataIntegrityError
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FileStorage(BaseStorage):
|
|
19
|
+
"""基于文件的存储实现"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, storage_path: str):
|
|
22
|
+
"""
|
|
23
|
+
初始化文件存储
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
storage_path: 存储目录路径
|
|
27
|
+
"""
|
|
28
|
+
self.storage_path = Path(storage_path)
|
|
29
|
+
self._ensure_storage_directory()
|
|
30
|
+
|
|
31
|
+
def _ensure_storage_directory(self):
|
|
32
|
+
"""确保存储目录存在"""
|
|
33
|
+
self.storage_path.mkdir(parents=True, exist_ok=True)
|
|
34
|
+
|
|
35
|
+
def _get_conversation_file_path(self, conversation_id: str) -> Path:
|
|
36
|
+
"""
|
|
37
|
+
获取对话文件路径
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
conversation_id: 对话ID
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Path: 对话文件路径
|
|
44
|
+
"""
|
|
45
|
+
# 清理文件名中的特殊字符
|
|
46
|
+
safe_filename = self._sanitize_filename(conversation_id)
|
|
47
|
+
return self.storage_path / f"{safe_filename}.json"
|
|
48
|
+
|
|
49
|
+
def _sanitize_filename(self, filename: str) -> str:
|
|
50
|
+
"""
|
|
51
|
+
清理文件名,移除或替换特殊字符
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
filename: 原始文件名
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
str: 安全的文件名
|
|
58
|
+
"""
|
|
59
|
+
# 移除或替换不安全的字符
|
|
60
|
+
safe_filename = re.sub(r'[<>:"/\\|?*]', '_', filename)
|
|
61
|
+
# 确保文件名不为空
|
|
62
|
+
if not safe_filename or safe_filename.isspace():
|
|
63
|
+
safe_filename = 'unnamed'
|
|
64
|
+
return safe_filename
|
|
65
|
+
|
|
66
|
+
def _validate_conversation_data(self, conversation_data: Dict[str, Any]) -> bool:
|
|
67
|
+
"""
|
|
68
|
+
验证对话数据的完整性
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
conversation_data: 对话数据
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
bool: 数据有效返回True
|
|
75
|
+
"""
|
|
76
|
+
if not isinstance(conversation_data, dict):
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
# 检查必需字段
|
|
80
|
+
required_fields = ['conversation_id']
|
|
81
|
+
for field in required_fields:
|
|
82
|
+
if field not in conversation_data:
|
|
83
|
+
return False
|
|
84
|
+
if not conversation_data[field]:
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
def _atomic_write_file(self, file_path: Path, data: Dict[str, Any]) -> bool:
|
|
90
|
+
"""
|
|
91
|
+
原子写入文件
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
file_path: 目标文件路径
|
|
95
|
+
data: 要写入的数据
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
bool: 写入成功返回True
|
|
99
|
+
"""
|
|
100
|
+
temp_fd = None
|
|
101
|
+
temp_path = None
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
# 创建临时文件
|
|
105
|
+
temp_fd, temp_path = tempfile.mkstemp(
|
|
106
|
+
suffix='.tmp',
|
|
107
|
+
prefix=file_path.name + '.',
|
|
108
|
+
dir=file_path.parent
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# 写入数据到临时文件
|
|
112
|
+
with os.fdopen(temp_fd, 'w', encoding='utf-8') as temp_file:
|
|
113
|
+
temp_fd = None # 文件已关闭,避免重复关闭
|
|
114
|
+
json.dump(data, temp_file, ensure_ascii=False, indent=2)
|
|
115
|
+
|
|
116
|
+
# 原子重命名
|
|
117
|
+
os.rename(temp_path, file_path)
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
except (OSError, IOError, PermissionError, TypeError, ValueError):
|
|
121
|
+
# 清理临时文件
|
|
122
|
+
if temp_fd is not None:
|
|
123
|
+
try:
|
|
124
|
+
os.close(temp_fd)
|
|
125
|
+
except OSError:
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
if temp_path and os.path.exists(temp_path):
|
|
129
|
+
try:
|
|
130
|
+
os.unlink(temp_path)
|
|
131
|
+
except OSError:
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
def save_conversation(self, conversation_data: Dict[str, Any]) -> bool:
|
|
137
|
+
"""
|
|
138
|
+
保存对话数据
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
conversation_data: 对话数据字典
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
bool: 保存成功返回True
|
|
145
|
+
"""
|
|
146
|
+
if not self._validate_conversation_data(conversation_data):
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
conversation_id = conversation_data['conversation_id']
|
|
150
|
+
file_path = self._get_conversation_file_path(conversation_id)
|
|
151
|
+
|
|
152
|
+
return self._atomic_write_file(file_path, conversation_data)
|
|
153
|
+
|
|
154
|
+
def load_conversation(self, conversation_id: str) -> Optional[Dict[str, Any]]:
|
|
155
|
+
"""
|
|
156
|
+
加载对话数据
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
conversation_id: 对话ID
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Optional[Dict[str, Any]]: 对话数据,不存在返回None
|
|
163
|
+
"""
|
|
164
|
+
file_path = self._get_conversation_file_path(conversation_id)
|
|
165
|
+
|
|
166
|
+
if not file_path.exists():
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
171
|
+
data = json.load(f)
|
|
172
|
+
|
|
173
|
+
# 验证加载的数据
|
|
174
|
+
if not self._validate_conversation_data(data):
|
|
175
|
+
raise DataIntegrityError(f"对话数据无效: {conversation_id}")
|
|
176
|
+
|
|
177
|
+
return data
|
|
178
|
+
|
|
179
|
+
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
|
180
|
+
raise DataIntegrityError(f"对话文件损坏: {conversation_id}, 错误: {str(e)}")
|
|
181
|
+
except (OSError, IOError) as e:
|
|
182
|
+
# 文件读取错误,返回None
|
|
183
|
+
return None
|
|
184
|
+
|
|
185
|
+
def delete_conversation(self, conversation_id: str) -> bool:
|
|
186
|
+
"""
|
|
187
|
+
删除对话数据
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
conversation_id: 对话ID
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
bool: 删除成功返回True
|
|
194
|
+
"""
|
|
195
|
+
file_path = self._get_conversation_file_path(conversation_id)
|
|
196
|
+
|
|
197
|
+
if not file_path.exists():
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
file_path.unlink()
|
|
202
|
+
return True
|
|
203
|
+
except (OSError, IOError):
|
|
204
|
+
return False
|
|
205
|
+
|
|
206
|
+
def conversation_exists(self, conversation_id: str) -> bool:
|
|
207
|
+
"""
|
|
208
|
+
检查对话是否存在
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
conversation_id: 对话ID
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
bool: 存在返回True
|
|
215
|
+
"""
|
|
216
|
+
file_path = self._get_conversation_file_path(conversation_id)
|
|
217
|
+
return file_path.exists()
|
|
218
|
+
|
|
219
|
+
def list_conversations(
|
|
220
|
+
self,
|
|
221
|
+
limit: Optional[int] = None,
|
|
222
|
+
offset: int = 0
|
|
223
|
+
) -> List[Dict[str, Any]]:
|
|
224
|
+
"""
|
|
225
|
+
列出对话
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
limit: 限制返回数量
|
|
229
|
+
offset: 偏移量
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
List[Dict[str, Any]]: 对话数据列表
|
|
233
|
+
"""
|
|
234
|
+
conversations = []
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
# 获取所有JSON文件
|
|
238
|
+
json_files = list(self.storage_path.glob("*.json"))
|
|
239
|
+
|
|
240
|
+
# 按修改时间排序(最新的在前)
|
|
241
|
+
json_files.sort(key=lambda x: x.stat().st_mtime, reverse=True)
|
|
242
|
+
|
|
243
|
+
# 应用偏移量和限制
|
|
244
|
+
if limit is not None:
|
|
245
|
+
json_files = json_files[offset:offset + limit]
|
|
246
|
+
else:
|
|
247
|
+
json_files = json_files[offset:]
|
|
248
|
+
|
|
249
|
+
# 加载对话数据
|
|
250
|
+
for file_path in json_files:
|
|
251
|
+
try:
|
|
252
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
253
|
+
data = json.load(f)
|
|
254
|
+
|
|
255
|
+
# 验证数据
|
|
256
|
+
if self._validate_conversation_data(data):
|
|
257
|
+
conversations.append(data)
|
|
258
|
+
|
|
259
|
+
except (json.JSONDecodeError, UnicodeDecodeError, OSError, IOError):
|
|
260
|
+
# 跳过损坏的文件
|
|
261
|
+
continue
|
|
262
|
+
|
|
263
|
+
except OSError:
|
|
264
|
+
# 目录访问错误,返回空列表
|
|
265
|
+
pass
|
|
266
|
+
|
|
267
|
+
return conversations
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
"""
|
|
2
|
+
索引管理器实现
|
|
3
|
+
|
|
4
|
+
提供对话索引管理功能,支持快速查询、搜索和过滤。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import json
|
|
9
|
+
import time
|
|
10
|
+
from typing import Optional, List, Dict, Any
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from ..file_locker import FileLocker
|
|
14
|
+
from ..exceptions import DataIntegrityError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class IndexManager:
|
|
18
|
+
"""索引管理器,用于管理对话索引"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, index_path: str):
|
|
21
|
+
"""
|
|
22
|
+
初始化索引管理器
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
index_path: 索引目录路径
|
|
26
|
+
"""
|
|
27
|
+
self.index_path = Path(index_path)
|
|
28
|
+
self.index_file = self.index_path / "conversations.idx"
|
|
29
|
+
self.config_file = self.index_path / "config.json"
|
|
30
|
+
self.lock_file = self.index_path / "index.lock"
|
|
31
|
+
|
|
32
|
+
self._ensure_index_directory()
|
|
33
|
+
self._load_index()
|
|
34
|
+
self._load_config()
|
|
35
|
+
|
|
36
|
+
def _ensure_index_directory(self):
|
|
37
|
+
"""确保索引目录存在"""
|
|
38
|
+
self.index_path.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
|
|
40
|
+
def _load_index(self):
|
|
41
|
+
"""加载索引数据"""
|
|
42
|
+
try:
|
|
43
|
+
if self.index_file.exists():
|
|
44
|
+
with open(self.index_file, 'r', encoding='utf-8') as f:
|
|
45
|
+
self._index_data = json.load(f)
|
|
46
|
+
else:
|
|
47
|
+
self._index_data = {}
|
|
48
|
+
except (json.JSONDecodeError, OSError, IOError):
|
|
49
|
+
# 如果索引损坏,重建空索引
|
|
50
|
+
self._index_data = {}
|
|
51
|
+
|
|
52
|
+
def _load_config(self):
|
|
53
|
+
"""加载配置数据"""
|
|
54
|
+
try:
|
|
55
|
+
if self.config_file.exists():
|
|
56
|
+
with open(self.config_file, 'r', encoding='utf-8') as f:
|
|
57
|
+
self._config_data = json.load(f)
|
|
58
|
+
else:
|
|
59
|
+
self._config_data = {}
|
|
60
|
+
except (json.JSONDecodeError, OSError, IOError):
|
|
61
|
+
# 如果配置损坏,重建空配置
|
|
62
|
+
self._config_data = {}
|
|
63
|
+
|
|
64
|
+
def _save_index(self) -> bool:
|
|
65
|
+
"""
|
|
66
|
+
保存索引数据
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
bool: 保存成功返回True
|
|
70
|
+
"""
|
|
71
|
+
try:
|
|
72
|
+
# 使用临时文件进行原子写入
|
|
73
|
+
temp_file = self.index_file.with_suffix('.tmp')
|
|
74
|
+
|
|
75
|
+
with open(temp_file, 'w', encoding='utf-8') as f:
|
|
76
|
+
json.dump(self._index_data, f, ensure_ascii=False, indent=2)
|
|
77
|
+
|
|
78
|
+
# 原子重命名
|
|
79
|
+
temp_file.replace(self.index_file)
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
except (OSError, IOError):
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
def _save_config(self) -> bool:
|
|
86
|
+
"""
|
|
87
|
+
保存配置数据
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
bool: 保存成功返回True
|
|
91
|
+
"""
|
|
92
|
+
try:
|
|
93
|
+
# 使用临时文件进行原子写入
|
|
94
|
+
temp_file = self.config_file.with_suffix('.tmp')
|
|
95
|
+
|
|
96
|
+
with open(temp_file, 'w', encoding='utf-8') as f:
|
|
97
|
+
json.dump(self._config_data, f, ensure_ascii=False, indent=2)
|
|
98
|
+
|
|
99
|
+
# 原子重命名
|
|
100
|
+
temp_file.replace(self.config_file)
|
|
101
|
+
return True
|
|
102
|
+
|
|
103
|
+
except (OSError, IOError):
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
def set_current_conversation(self, conversation_id: Optional[str]) -> bool:
|
|
107
|
+
"""
|
|
108
|
+
设置当前对话ID
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
conversation_id: 对话ID,None表示清除当前对话
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
bool: 设置成功返回True
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
# 重新加载配置以获取最新数据
|
|
118
|
+
self._load_config()
|
|
119
|
+
|
|
120
|
+
# 设置或清除当前对话ID
|
|
121
|
+
if conversation_id is None:
|
|
122
|
+
self._config_data.pop('current_conversation_id', None)
|
|
123
|
+
else:
|
|
124
|
+
self._config_data['current_conversation_id'] = conversation_id
|
|
125
|
+
|
|
126
|
+
# 更新时间戳
|
|
127
|
+
self._config_data['last_updated'] = time.time()
|
|
128
|
+
|
|
129
|
+
# 保存配置
|
|
130
|
+
return self._save_config()
|
|
131
|
+
|
|
132
|
+
except Exception:
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
def get_current_conversation_id(self) -> Optional[str]:
|
|
136
|
+
"""
|
|
137
|
+
获取当前对话ID
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Optional[str]: 当前对话ID,未设置返回None
|
|
141
|
+
"""
|
|
142
|
+
try:
|
|
143
|
+
# 重新加载配置以获取最新数据
|
|
144
|
+
self._load_config()
|
|
145
|
+
|
|
146
|
+
return self._config_data.get('current_conversation_id')
|
|
147
|
+
|
|
148
|
+
except Exception:
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
def clear_current_conversation(self) -> bool:
|
|
152
|
+
"""
|
|
153
|
+
清除当前对话设置
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
bool: 清除成功返回True
|
|
157
|
+
"""
|
|
158
|
+
return self.set_current_conversation(None)
|
|
159
|
+
|
|
160
|
+
def add_conversation(self, conversation_metadata: Dict[str, Any]) -> bool:
|
|
161
|
+
"""
|
|
162
|
+
添加对话到索引
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
conversation_metadata: 对话元数据
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
bool: 添加成功返回True
|
|
169
|
+
"""
|
|
170
|
+
if not conversation_metadata.get('conversation_id'):
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
conversation_id = conversation_metadata['conversation_id']
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
# 重新加载索引以获取最新数据
|
|
177
|
+
self._load_index()
|
|
178
|
+
|
|
179
|
+
# 添加或更新对话元数据
|
|
180
|
+
self._index_data[conversation_id] = conversation_metadata.copy()
|
|
181
|
+
|
|
182
|
+
# 保存索引
|
|
183
|
+
return self._save_index()
|
|
184
|
+
|
|
185
|
+
except Exception:
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
def update_conversation(self, conversation_metadata: Dict[str, Any]) -> bool:
|
|
189
|
+
"""
|
|
190
|
+
更新索引中的对话
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
conversation_metadata: 对话元数据
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
bool: 更新成功返回True
|
|
197
|
+
"""
|
|
198
|
+
if not conversation_metadata.get('conversation_id'):
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
conversation_id = conversation_metadata['conversation_id']
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
# 重新加载索引以获取最新数据
|
|
205
|
+
self._load_index()
|
|
206
|
+
|
|
207
|
+
# 检查对话是否存在
|
|
208
|
+
if conversation_id not in self._index_data:
|
|
209
|
+
return False
|
|
210
|
+
|
|
211
|
+
# 更新对话元数据
|
|
212
|
+
self._index_data[conversation_id] = conversation_metadata.copy()
|
|
213
|
+
|
|
214
|
+
# 保存索引
|
|
215
|
+
return self._save_index()
|
|
216
|
+
|
|
217
|
+
except Exception:
|
|
218
|
+
return False
|
|
219
|
+
|
|
220
|
+
def remove_conversation(self, conversation_id: str) -> bool:
|
|
221
|
+
"""
|
|
222
|
+
从索引中删除对话
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
conversation_id: 对话ID
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
bool: 删除成功返回True
|
|
229
|
+
"""
|
|
230
|
+
try:
|
|
231
|
+
# 重新加载索引以获取最新数据
|
|
232
|
+
self._load_index()
|
|
233
|
+
|
|
234
|
+
# 检查对话是否存在
|
|
235
|
+
if conversation_id not in self._index_data:
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
# 删除对话
|
|
239
|
+
del self._index_data[conversation_id]
|
|
240
|
+
|
|
241
|
+
# 保存索引
|
|
242
|
+
return self._save_index()
|
|
243
|
+
|
|
244
|
+
except Exception:
|
|
245
|
+
return False
|
|
246
|
+
|
|
247
|
+
def get_conversation(self, conversation_id: str) -> Optional[Dict[str, Any]]:
|
|
248
|
+
"""
|
|
249
|
+
从索引获取对话信息
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
conversation_id: 对话ID
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
Optional[Dict[str, Any]]: 对话元数据,不存在返回None
|
|
256
|
+
"""
|
|
257
|
+
try:
|
|
258
|
+
# 重新加载索引以获取最新数据
|
|
259
|
+
self._load_index()
|
|
260
|
+
|
|
261
|
+
return self._index_data.get(conversation_id)
|
|
262
|
+
|
|
263
|
+
except Exception:
|
|
264
|
+
return None
|
|
265
|
+
|
|
266
|
+
def conversation_exists(self, conversation_id: str) -> bool:
|
|
267
|
+
"""
|
|
268
|
+
检查对话是否在索引中存在
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
conversation_id: 对话ID
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
bool: 存在返回True
|
|
275
|
+
"""
|
|
276
|
+
return self.get_conversation(conversation_id) is not None
|
|
277
|
+
|
|
278
|
+
def list_conversations(
|
|
279
|
+
self,
|
|
280
|
+
limit: Optional[int] = None,
|
|
281
|
+
offset: int = 0,
|
|
282
|
+
sort_by: str = 'updated_at',
|
|
283
|
+
sort_order: str = 'desc'
|
|
284
|
+
) -> List[Dict[str, Any]]:
|
|
285
|
+
"""
|
|
286
|
+
列出对话
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
limit: 限制返回数量
|
|
290
|
+
offset: 偏移量
|
|
291
|
+
sort_by: 排序字段
|
|
292
|
+
sort_order: 排序顺序 ('asc' 或 'desc')
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
List[Dict[str, Any]]: 对话元数据列表
|
|
296
|
+
"""
|
|
297
|
+
try:
|
|
298
|
+
# 重新加载索引以获取最新数据
|
|
299
|
+
self._load_index()
|
|
300
|
+
|
|
301
|
+
# 获取所有对话
|
|
302
|
+
conversations = list(self._index_data.values())
|
|
303
|
+
|
|
304
|
+
# 排序
|
|
305
|
+
reverse = (sort_order.lower() == 'desc')
|
|
306
|
+
|
|
307
|
+
if sort_by == 'name':
|
|
308
|
+
conversations.sort(
|
|
309
|
+
key=lambda x: x.get('name', ''),
|
|
310
|
+
reverse=reverse
|
|
311
|
+
)
|
|
312
|
+
elif sort_by == 'created_at':
|
|
313
|
+
conversations.sort(
|
|
314
|
+
key=lambda x: x.get('created_at', 0),
|
|
315
|
+
reverse=reverse
|
|
316
|
+
)
|
|
317
|
+
elif sort_by == 'updated_at':
|
|
318
|
+
conversations.sort(
|
|
319
|
+
key=lambda x: x.get('updated_at', 0),
|
|
320
|
+
reverse=reverse
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# 应用分页
|
|
324
|
+
if limit is not None:
|
|
325
|
+
return conversations[offset:offset + limit]
|
|
326
|
+
else:
|
|
327
|
+
return conversations[offset:]
|
|
328
|
+
|
|
329
|
+
except Exception:
|
|
330
|
+
return []
|
|
331
|
+
|
|
332
|
+
def search_conversations(
|
|
333
|
+
self,
|
|
334
|
+
query: Optional[str] = None,
|
|
335
|
+
filters: Optional[Dict[str, Any]] = None
|
|
336
|
+
) -> List[Dict[str, Any]]:
|
|
337
|
+
"""
|
|
338
|
+
搜索对话
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
query: 搜索查询字符串
|
|
342
|
+
filters: 过滤条件
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
List[Dict[str, Any]]: 匹配的对话元数据列表
|
|
346
|
+
"""
|
|
347
|
+
try:
|
|
348
|
+
# 重新加载索引以获取最新数据
|
|
349
|
+
self._load_index()
|
|
350
|
+
|
|
351
|
+
conversations = list(self._index_data.values())
|
|
352
|
+
results = []
|
|
353
|
+
|
|
354
|
+
for conv in conversations:
|
|
355
|
+
# 文本搜索
|
|
356
|
+
if query:
|
|
357
|
+
query_lower = query.lower()
|
|
358
|
+
name_match = query_lower in conv.get('name', '').lower()
|
|
359
|
+
desc_match = query_lower in conv.get('description', '').lower()
|
|
360
|
+
|
|
361
|
+
if not (name_match or desc_match):
|
|
362
|
+
continue
|
|
363
|
+
|
|
364
|
+
# 应用过滤器
|
|
365
|
+
if filters:
|
|
366
|
+
match = True
|
|
367
|
+
|
|
368
|
+
# 时间范围过滤
|
|
369
|
+
# created_after: 大于等于这个时间的记录
|
|
370
|
+
if 'created_after' in filters:
|
|
371
|
+
created_at = conv.get('created_at', 0)
|
|
372
|
+
if created_at < filters['created_after']:
|
|
373
|
+
match = False
|
|
374
|
+
|
|
375
|
+
# created_before: 小于这个时间的记录(不包含边界)
|
|
376
|
+
if 'created_before' in filters:
|
|
377
|
+
created_at = conv.get('created_at', float('inf'))
|
|
378
|
+
if created_at >= filters['created_before']:
|
|
379
|
+
match = False
|
|
380
|
+
|
|
381
|
+
# 消息数量过滤
|
|
382
|
+
if 'min_message_count' in filters:
|
|
383
|
+
message_count = conv.get('message_count', 0)
|
|
384
|
+
if message_count < filters['min_message_count']:
|
|
385
|
+
match = False
|
|
386
|
+
|
|
387
|
+
if 'max_message_count' in filters:
|
|
388
|
+
message_count = conv.get('message_count', float('inf'))
|
|
389
|
+
if message_count > filters['max_message_count']:
|
|
390
|
+
match = False
|
|
391
|
+
|
|
392
|
+
if not match:
|
|
393
|
+
continue
|
|
394
|
+
|
|
395
|
+
results.append(conv)
|
|
396
|
+
|
|
397
|
+
# 按相关性或更新时间排序
|
|
398
|
+
results.sort(
|
|
399
|
+
key=lambda x: x.get('updated_at', 0),
|
|
400
|
+
reverse=True
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
return results
|
|
404
|
+
|
|
405
|
+
except Exception:
|
|
406
|
+
return []
|