iflow-mcp_hanw39_reasoning-bank-mcp 0.2.0__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.
Files changed (55) hide show
  1. iflow_mcp_hanw39_reasoning_bank_mcp-0.2.0.dist-info/METADATA +599 -0
  2. iflow_mcp_hanw39_reasoning_bank_mcp-0.2.0.dist-info/RECORD +55 -0
  3. iflow_mcp_hanw39_reasoning_bank_mcp-0.2.0.dist-info/WHEEL +4 -0
  4. iflow_mcp_hanw39_reasoning_bank_mcp-0.2.0.dist-info/entry_points.txt +2 -0
  5. iflow_mcp_hanw39_reasoning_bank_mcp-0.2.0.dist-info/licenses/LICENSE +21 -0
  6. src/__init__.py +16 -0
  7. src/__main__.py +6 -0
  8. src/config.py +266 -0
  9. src/deduplication/__init__.py +19 -0
  10. src/deduplication/base.py +88 -0
  11. src/deduplication/factory.py +60 -0
  12. src/deduplication/strategies/__init__.py +1 -0
  13. src/deduplication/strategies/semantic_dedup.py +187 -0
  14. src/default_config.yaml +121 -0
  15. src/initializers/__init__.py +50 -0
  16. src/initializers/base.py +196 -0
  17. src/initializers/embedding_initializer.py +22 -0
  18. src/initializers/llm_initializer.py +22 -0
  19. src/initializers/memory_manager_initializer.py +55 -0
  20. src/initializers/retrieval_initializer.py +32 -0
  21. src/initializers/storage_initializer.py +22 -0
  22. src/initializers/tools_initializer.py +48 -0
  23. src/llm/__init__.py +10 -0
  24. src/llm/base.py +61 -0
  25. src/llm/factory.py +75 -0
  26. src/llm/providers/__init__.py +12 -0
  27. src/llm/providers/anthropic.py +62 -0
  28. src/llm/providers/dashscope.py +76 -0
  29. src/llm/providers/openai.py +76 -0
  30. src/merge/__init__.py +22 -0
  31. src/merge/base.py +89 -0
  32. src/merge/factory.py +60 -0
  33. src/merge/strategies/__init__.py +1 -0
  34. src/merge/strategies/llm_merge.py +170 -0
  35. src/merge/strategies/voting_merge.py +108 -0
  36. src/prompts/__init__.py +21 -0
  37. src/prompts/formatters.py +74 -0
  38. src/prompts/templates.py +184 -0
  39. src/retrieval/__init__.py +8 -0
  40. src/retrieval/base.py +37 -0
  41. src/retrieval/factory.py +55 -0
  42. src/retrieval/strategies/__init__.py +8 -0
  43. src/retrieval/strategies/cosine_retrieval.py +47 -0
  44. src/retrieval/strategies/hybrid_retrieval.py +155 -0
  45. src/server.py +306 -0
  46. src/services/__init__.py +5 -0
  47. src/services/memory_manager.py +403 -0
  48. src/storage/__init__.py +45 -0
  49. src/storage/backends/json_backend.py +290 -0
  50. src/storage/base.py +150 -0
  51. src/tools/__init__.py +8 -0
  52. src/tools/extract_memory.py +285 -0
  53. src/tools/retrieve_memory.py +139 -0
  54. src/utils/__init__.py +7 -0
  55. src/utils/similarity.py +54 -0
@@ -0,0 +1,290 @@
1
+ """JSON 存储后端实现"""
2
+ import json
3
+ import asyncio
4
+ from pathlib import Path
5
+ from typing import List, Dict, Optional
6
+ from datetime import datetime
7
+ import numpy as np
8
+ from threading import Lock
9
+
10
+ from ..base import StorageBackend
11
+
12
+
13
+ class JSONStorageBackend(StorageBackend):
14
+ """JSON 文件存储后端"""
15
+
16
+ def __init__(self, config: Dict):
17
+ self.memories_path = Path(config.get("memories_path", "data/memories.json")).expanduser()
18
+ self.embeddings_path = Path(config.get("embeddings_path", "data/embeddings.json")).expanduser()
19
+ self.archived_path = Path(config.get("archived_path", "data/archived_memories.json")).expanduser()
20
+ self._lock = Lock()
21
+
22
+ # Store retrieval strategy reference (injected later)
23
+ self.retrieval_strategy = None
24
+
25
+ # 初始化文件
26
+ self._initialize_files()
27
+
28
+ def _initialize_files(self):
29
+ """初始化 JSON 文件"""
30
+ # 创建所有必要的目录
31
+ self.memories_path.parent.mkdir(parents=True, exist_ok=True)
32
+ self.embeddings_path.parent.mkdir(parents=True, exist_ok=True)
33
+ self.archived_path.parent.mkdir(parents=True, exist_ok=True)
34
+
35
+ # 初始化 memories 文件
36
+ if not self.memories_path.exists():
37
+ self._save_memories({
38
+ "version": "1.0",
39
+ "total_count": 0,
40
+ "memories": []
41
+ })
42
+
43
+ # 初始化 embeddings 文件
44
+ if not self.embeddings_path.exists():
45
+ self._save_embeddings({
46
+ "version": "1.0",
47
+ "embedding_model": "unknown",
48
+ "embedding_dim": 0,
49
+ "embeddings": {}
50
+ })
51
+
52
+ # 注意:archived 文件是可选的,只在需要时创建(在 archive_memories 方法中)
53
+ # 这里只确保父目录存在
54
+
55
+ def _load_memories(self) -> Dict:
56
+ """加载记忆数据"""
57
+ with self._lock:
58
+ with open(self.memories_path, 'r', encoding='utf-8') as f:
59
+ return json.load(f)
60
+
61
+ def _save_memories(self, data: Dict):
62
+ """保存记忆数据"""
63
+ with self._lock:
64
+ with open(self.memories_path, 'w', encoding='utf-8') as f:
65
+ json.dump(data, f, ensure_ascii=False, indent=2)
66
+
67
+ def _load_embeddings(self) -> Dict:
68
+ """加载嵌入数据"""
69
+ with self._lock:
70
+ with open(self.embeddings_path, 'r', encoding='utf-8') as f:
71
+ return json.load(f)
72
+
73
+ def _save_embeddings(self, data: Dict):
74
+ """保存嵌入数据"""
75
+ with self._lock:
76
+ with open(self.embeddings_path, 'w', encoding='utf-8') as f:
77
+ json.dump(data, f, ensure_ascii=False, indent=2)
78
+
79
+ async def add_memory(self, memory: Dict, embedding: List[float]):
80
+ """添加新记忆"""
81
+ # 添加到 memories.json
82
+ memories_data = self._load_memories()
83
+ memories_data["memories"].append(memory)
84
+ memories_data["total_count"] = len(memories_data["memories"])
85
+ memories_data["last_updated"] = memory["timestamp"]
86
+ self._save_memories(memories_data)
87
+
88
+ # 添加到 embeddings.json
89
+ embeddings_data = self._load_embeddings()
90
+ embeddings_data["embeddings"][memory["memory_id"]] = {
91
+ "query_text": memory["query"],
92
+ "vector": embedding,
93
+ "created_at": memory["timestamp"]
94
+ }
95
+ embeddings_data["last_updated"] = memory["timestamp"]
96
+ self._save_embeddings(embeddings_data)
97
+
98
+ async def get_memory_by_id(self, memory_id: str) -> Optional[Dict]:
99
+ """根据 ID 获取记忆"""
100
+ memories_data = self._load_memories()
101
+ for mem in memories_data["memories"]:
102
+ if mem["memory_id"] == memory_id:
103
+ return mem
104
+ return None
105
+
106
+ async def get_all_memories(self, agent_id: str = None) -> List[Dict]:
107
+ """
108
+ 获取所有记忆
109
+
110
+ Args:
111
+ agent_id: Agent ID,用于过滤。None 表示获取所有记忆
112
+ """
113
+ memories_data = self._load_memories()
114
+ all_memories = memories_data["memories"]
115
+
116
+ # 如果指定了 agent_id,只返回匹配的记忆
117
+ if agent_id is not None:
118
+ return [m for m in all_memories if m.get("agent_id") == agent_id]
119
+
120
+ # 否则返回所有记忆
121
+ return all_memories
122
+
123
+ async def get_all_embeddings(self, agent_id: str = None) -> Dict[str, np.ndarray]:
124
+ """
125
+ 获取所有嵌入向量
126
+
127
+ Args:
128
+ agent_id: Agent ID,用于过滤。None 表示获取所有嵌入
129
+ """
130
+ embeddings_data = self._load_embeddings()
131
+
132
+ if agent_id is None:
133
+ # 返回所有
134
+ return {
135
+ mem_id: np.array(data["vector"])
136
+ for mem_id, data in embeddings_data["embeddings"].items()
137
+ }
138
+
139
+ # 需要先获取记忆列表,找出属于该 agent 的 memory_id
140
+ memories = await self.get_all_memories(agent_id)
141
+ memory_ids = {m["memory_id"] for m in memories}
142
+
143
+ return {
144
+ mem_id: np.array(data["vector"])
145
+ for mem_id, data in embeddings_data["embeddings"].items()
146
+ if mem_id in memory_ids
147
+ }
148
+
149
+ async def update_retrieval_stats(self, memory_id: str, timestamp: str):
150
+ """更新检索统计"""
151
+ memories_data = self._load_memories()
152
+ for mem in memories_data["memories"]:
153
+ if mem["memory_id"] == memory_id:
154
+ mem["retrieval_count"] = mem.get("retrieval_count", 0) + 1
155
+ mem["last_retrieved"] = timestamp
156
+ break
157
+ self._save_memories(memories_data)
158
+
159
+ async def get_stats(self) -> Dict:
160
+ """获取统计信息"""
161
+ memories_data = self._load_memories()
162
+ memories = memories_data["memories"]
163
+
164
+ success_count = sum(1 for m in memories if m.get("success", True))
165
+ failure_count = len(memories) - success_count
166
+
167
+ return {
168
+ "total_count": len(memories),
169
+ "success_count": success_count,
170
+ "failure_count": failure_count,
171
+ "last_updated": memories_data.get("last_updated")
172
+ }
173
+
174
+ async def save_memories(self, memories: List[Dict], embeddings: Dict[str, np.ndarray]):
175
+ """批量保存记忆(用于合并后保存)"""
176
+ memories_data = self._load_memories()
177
+ embeddings_data = self._load_embeddings()
178
+
179
+ for mem in memories:
180
+ # 添加记忆
181
+ memories_data["memories"].append(mem)
182
+
183
+ # 添加嵌入
184
+ if mem["memory_id"] in embeddings:
185
+ embedding_vector = embeddings[mem["memory_id"]]
186
+ embeddings_data["embeddings"][mem["memory_id"]] = {
187
+ "query_text": mem.get("query", ""),
188
+ "vector": embedding_vector.tolist() if isinstance(embedding_vector, np.ndarray) else embedding_vector,
189
+ "created_at": mem.get("timestamp", datetime.now().isoformat())
190
+ }
191
+
192
+ # 更新元数据
193
+ memories_data["total_count"] = len(memories_data["memories"])
194
+ memories_data["last_updated"] = datetime.now().isoformat()
195
+ embeddings_data["last_updated"] = datetime.now().isoformat()
196
+
197
+ # 保存
198
+ self._save_memories(memories_data)
199
+ self._save_embeddings(embeddings_data)
200
+
201
+ async def get_memory(self, memory_id: str) -> Optional[Dict]:
202
+ """获取单个记忆(别名)"""
203
+ return await self.get_memory_by_id(memory_id)
204
+
205
+ async def get_embeddings(self, memory_ids: List[str]) -> Dict[str, np.ndarray]:
206
+ """获取指定记忆的嵌入向量"""
207
+ embeddings_data = self._load_embeddings()
208
+ result = {}
209
+
210
+ for mem_id in memory_ids:
211
+ if mem_id in embeddings_data["embeddings"]:
212
+ result[mem_id] = np.array(embeddings_data["embeddings"][mem_id]["vector"])
213
+
214
+ return result
215
+
216
+ async def archive_memories(self, memories: List[Dict]):
217
+ """归档记忆到 archived_memories.json"""
218
+ # 加载或创建归档文件
219
+ if self.archived_path.exists():
220
+ with self._lock:
221
+ with open(self.archived_path, 'r', encoding='utf-8') as f:
222
+ archived_data = json.load(f)
223
+ else:
224
+ archived_data = {
225
+ "version": "1.0",
226
+ "description": "已归档的原始经验,不参与检索但可追溯",
227
+ "memories": []
228
+ }
229
+
230
+ # 添加新归档
231
+ archived_data["memories"].extend(memories)
232
+ archived_data["last_updated"] = datetime.now().isoformat()
233
+
234
+ # 保存
235
+ with self._lock:
236
+ with open(self.archived_path, 'w', encoding='utf-8') as f:
237
+ json.dump(archived_data, f, ensure_ascii=False, indent=2)
238
+
239
+ async def get_archived_memory(self, memory_id: str) -> Optional[Dict]:
240
+ """获取已归档的记忆"""
241
+ if not self.archived_path.exists():
242
+ return None
243
+
244
+ with self._lock:
245
+ with open(self.archived_path, 'r', encoding='utf-8') as f:
246
+ archived_data = json.load(f)
247
+
248
+ for mem in archived_data.get("memories", []):
249
+ if mem["memory_id"] == memory_id:
250
+ return mem
251
+
252
+ return None
253
+
254
+ async def delete_memories(self, memory_ids: List[str], agent_id: Optional[str] = None):
255
+ """删除记忆(带 agent_id 安全验证)"""
256
+ memories_data = self._load_memories()
257
+ embeddings_data = self._load_embeddings()
258
+
259
+ # 过滤掉要删除的记忆
260
+ deleted_count = 0
261
+ new_memories = []
262
+
263
+ for mem in memories_data["memories"]:
264
+ should_delete = False
265
+
266
+ if mem["memory_id"] in memory_ids:
267
+ # 如果指定了 agent_id,验证权限
268
+ if agent_id is None or mem.get("agent_id") == agent_id:
269
+ should_delete = True
270
+ deleted_count += 1
271
+
272
+ # 同时删除对应的 embedding
273
+ if mem["memory_id"] in embeddings_data["embeddings"]:
274
+ del embeddings_data["embeddings"][mem["memory_id"]]
275
+
276
+ if not should_delete:
277
+ new_memories.append(mem)
278
+
279
+ # 更新数据
280
+ memories_data["memories"] = new_memories
281
+ memories_data["total_count"] = len(new_memories)
282
+ memories_data["last_updated"] = datetime.now().isoformat()
283
+ embeddings_data["last_updated"] = datetime.now().isoformat()
284
+
285
+ # 保存
286
+ self._save_memories(memories_data)
287
+ self._save_embeddings(embeddings_data)
288
+
289
+ return deleted_count
290
+
src/storage/base.py ADDED
@@ -0,0 +1,150 @@
1
+ """存储后端抽象基类"""
2
+ from abc import ABC, abstractmethod
3
+ from typing import List, Dict, Optional
4
+ import numpy as np
5
+
6
+
7
+ class StorageBackend(ABC):
8
+ """存储后端抽���接口"""
9
+
10
+ @abstractmethod
11
+ async def add_memory(self, memory: Dict, embedding: List[float]):
12
+ """
13
+ 添加记忆
14
+
15
+ Args:
16
+ memory: 记忆项字典
17
+ embedding: 嵌入向量
18
+ """
19
+ pass
20
+
21
+ @abstractmethod
22
+ async def get_memory_by_id(self, memory_id: str) -> Optional[Dict]:
23
+ """
24
+ 获取单个记忆
25
+
26
+ Args:
27
+ memory_id: 记忆 ID
28
+
29
+ Returns:
30
+ 记忆项字典,不存在返回 None
31
+ """
32
+ pass
33
+
34
+ @abstractmethod
35
+ async def get_all_memories(self, agent_id: str = None) -> List[Dict]:
36
+ """
37
+ 获取所有记忆
38
+
39
+ Args:
40
+ agent_id: Agent ID,用于过滤。None 表示获取所有记忆
41
+
42
+ Returns:
43
+ 记忆项列表
44
+ """
45
+ pass
46
+
47
+ @abstractmethod
48
+ async def get_all_embeddings(self, agent_id: str = None) -> Dict[str, np.ndarray]:
49
+ """
50
+ 获取所有嵌入向量
51
+
52
+ Args:
53
+ agent_id: Agent ID,用于过滤。None 表示获取所有嵌入
54
+
55
+ Returns:
56
+ {memory_id: embedding_vector} 字典
57
+ """
58
+ pass
59
+
60
+ @abstractmethod
61
+ async def update_retrieval_stats(self, memory_id: str, timestamp: str):
62
+ """
63
+ 更新检索统计
64
+
65
+ Args:
66
+ memory_id: 记忆 ID
67
+ timestamp: 检索时间戳
68
+ """
69
+ pass
70
+
71
+ @abstractmethod
72
+ async def get_stats(self) -> Dict:
73
+ """
74
+ 获取统计信息
75
+
76
+ Returns:
77
+ 统计信息字典
78
+ """
79
+ pass
80
+
81
+ @abstractmethod
82
+ async def save_memories(self, memories: List[Dict], embeddings: Dict[str, np.ndarray]):
83
+ """
84
+ 批量保存记忆(用于合并后保存)
85
+
86
+ Args:
87
+ memories: 记忆项列表
88
+ embeddings: {memory_id: embedding_vector} 字典
89
+ """
90
+ pass
91
+
92
+ @abstractmethod
93
+ async def get_memory(self, memory_id: str) -> Optional[Dict]:
94
+ """
95
+ 获取单个记忆(别名,与 get_memory_by_id 相同)
96
+
97
+ Args:
98
+ memory_id: 记忆 ID
99
+
100
+ Returns:
101
+ 记忆项字典,不存在返回 None
102
+ """
103
+ pass
104
+
105
+ @abstractmethod
106
+ async def get_embeddings(self, memory_ids: List[str]) -> Dict[str, np.ndarray]:
107
+ """
108
+ 获取指定记忆的嵌入向量
109
+
110
+ Args:
111
+ memory_ids: 记忆 ID 列表
112
+
113
+ Returns:
114
+ {memory_id: embedding_vector} 字典
115
+ """
116
+ pass
117
+
118
+ @abstractmethod
119
+ async def archive_memories(self, memories: List[Dict]):
120
+ """
121
+ 归档记忆到 archived_memories.json
122
+
123
+ Args:
124
+ memories: 要归档的记忆项列表(包含 archived=True 等字段)
125
+ """
126
+ pass
127
+
128
+ @abstractmethod
129
+ async def get_archived_memory(self, memory_id: str) -> Optional[Dict]:
130
+ """
131
+ 获取已归档的记忆
132
+
133
+ Args:
134
+ memory_id: 记忆 ID
135
+
136
+ Returns:
137
+ 归档的记忆项,不存在返回 None
138
+ """
139
+ pass
140
+
141
+ @abstractmethod
142
+ async def delete_memories(self, memory_ids: List[str], agent_id: Optional[str] = None):
143
+ """
144
+ 删除记忆
145
+
146
+ Args:
147
+ memory_ids: 要删除的记忆 ID 列表
148
+ agent_id: Agent ID,用于安全验证(只能删除该 agent 的记忆)
149
+ """
150
+ pass
src/tools/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """MCP 工具模块"""
2
+ from .retrieve_memory import RetrieveMemoryTool
3
+ from .extract_memory import ExtractMemoryTool
4
+
5
+ __all__ = [
6
+ "RetrieveMemoryTool",
7
+ "ExtractMemoryTool",
8
+ ]