mindforge-memory 6.4.1__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 (64) hide show
  1. MindForge.py +20 -0
  2. mindforge/__init__.py +111 -0
  3. mindforge/adapters/__init__.py +18 -0
  4. mindforge/adapters/claude_adapter.py +84 -0
  5. mindforge/adapters/crewai_memory.py +76 -0
  6. mindforge/adapters/generic_api.py +183 -0
  7. mindforge/adapters/langgraph_store.py +463 -0
  8. mindforge/adapters/openclaw_adapter.py +91 -0
  9. mindforge/api/__init__.py +1 -0
  10. mindforge/api/server.py +1567 -0
  11. mindforge/cli/__init__.py +1 -0
  12. mindforge/cli/main.py +13607 -0
  13. mindforge/core/__init__.py +37 -0
  14. mindforge/core/embedding.py +847 -0
  15. mindforge/core/encryption.py +597 -0
  16. mindforge/core/indexer.py +363 -0
  17. mindforge/core/mindforge.py +6298 -0
  18. mindforge/core/paths.py +43 -0
  19. mindforge/core/query.py +501 -0
  20. mindforge/core/storage.py +18656 -0
  21. mindforge/core/types.py +314 -0
  22. mindforge/core/version.py +56 -0
  23. mindforge/daemon/__init__.py +0 -0
  24. mindforge/daemon/server.py +277 -0
  25. mindforge/mcp/__init__.py +4 -0
  26. mindforge/mcp/server.py +2559 -0
  27. mindforge/modules/__init__.py +138 -0
  28. mindforge/modules/categorizer.py +151 -0
  29. mindforge/modules/conflict_detector.py +356 -0
  30. mindforge/modules/connectors.py +331 -0
  31. mindforge/modules/context_window.py +337 -0
  32. mindforge/modules/event_bus.py +572 -0
  33. mindforge/modules/evolution.py +210 -0
  34. mindforge/modules/experience.py +402 -0
  35. mindforge/modules/federated.py +722 -0
  36. mindforge/modules/federated_acl.py +393 -0
  37. mindforge/modules/follow_up.py +315 -0
  38. mindforge/modules/hybrid_search.py +565 -0
  39. mindforge/modules/importer.py +374 -0
  40. mindforge/modules/integrator.py +196 -0
  41. mindforge/modules/intent_router.py +359 -0
  42. mindforge/modules/knowledge_graph.py +395 -0
  43. mindforge/modules/memory_compressor.py +592 -0
  44. mindforge/modules/memory_decay.py +474 -0
  45. mindforge/modules/memory_passport.py +250 -0
  46. mindforge/modules/multi_agent.py +534 -0
  47. mindforge/modules/multimodal.py +304 -0
  48. mindforge/modules/outcome_learning.py +186 -0
  49. mindforge/modules/personality.py +275 -0
  50. mindforge/modules/principled_forgetting.py +258 -0
  51. mindforge/modules/privacy.py +736 -0
  52. mindforge/modules/recall.py +263 -0
  53. mindforge/modules/session_focus.py +305 -0
  54. mindforge/modules/share_conflict.py +521 -0
  55. mindforge/modules/skill_extractor.py +340 -0
  56. mindforge/modules/skill_pack.py +205 -0
  57. mindforge/modules/timeline_viz.py +206 -0
  58. mindforge/modules/v6.py +931 -0
  59. mindforge_memory-6.4.1.dist-info/METADATA +281 -0
  60. mindforge_memory-6.4.1.dist-info/RECORD +64 -0
  61. mindforge_memory-6.4.1.dist-info/WHEEL +5 -0
  62. mindforge_memory-6.4.1.dist-info/entry_points.txt +3 -0
  63. mindforge_memory-6.4.1.dist-info/licenses/LICENSE +21 -0
  64. mindforge_memory-6.4.1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,206 @@
1
+ """
2
+ MindForge v6.0.0 功能 2:记忆时间线可视化
3
+
4
+ 把记忆按时间组织成可截图分享的 Web 可视化:记忆时间线、四层分布、
5
+ 偏好主题、偏好趋势。**只输出聚合统计,不含明文内容**(隐私安全)。
6
+
7
+ - build_timeline_visual(): 按日序列连续桶统计
8
+ - layers_distribution / categories / preferences 聚合
9
+ - preference_trend: 偏好标签随时间变化
10
+ - graph 规模:知识图谱节点/边(供可视化页展示)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import re
16
+ import time
17
+ from collections import OrderedDict
18
+ from datetime import datetime, timedelta
19
+ from typing import Any, Dict, List, Optional
20
+
21
+ # 偏好标签前缀(从记忆 tags 提取主题)
22
+ _PREFERENCE_PREFIXES = ("user_preference", "preference", "profile")
23
+ # 需要排除的通用标签(不算偏好主题)
24
+ _GENERIC_TAGS = {"imported", "general", "favorite", "important"}
25
+
26
+
27
+ def _day_key(ts: float) -> str:
28
+ """时间戳 -> YYYY-MM-DD"""
29
+ try:
30
+ return datetime.fromtimestamp(float(ts)).strftime("%Y-%m-%d")
31
+ except (TypeError, ValueError, OSError):
32
+ return datetime.fromtimestamp(0).strftime("%Y-%m-%d")
33
+
34
+
35
+ def _tag_theme(tags: List[str]) -> Optional[str]:
36
+ """从标签中提取偏好主题(去重、过滤通用标签)。"""
37
+ for t in tags or []:
38
+ t = (t or "").strip()
39
+ if not t or t in _GENERIC_TAGS:
40
+ continue
41
+ for p in _PREFERENCE_PREFIXES:
42
+ if t.startswith(p):
43
+ name = t[len(p):].strip(":_- ")
44
+ if name:
45
+ return name
46
+ return t
47
+ return None
48
+
49
+
50
+ def _build_daily_buckets(start_ts: float, end_ts: float,
51
+ days: int) -> "OrderedDict[str, Dict[str, Any]]":
52
+ """构建从 start 到 end 的 days 个连续日历日桶(含边界修正)。"""
53
+ if days <= 0:
54
+ days = 1
55
+ # 边界修正:保证 days 个日历日(start_dt = end_dt - (days-1))
56
+ end_dt = datetime.fromtimestamp(end_ts).replace(
57
+ hour=0, minute=0, second=0, microsecond=0)
58
+ start_dt = end_dt - timedelta(days=days - 1)
59
+ buckets: "OrderedDict[str, Dict[str, Any]]" = OrderedDict()
60
+ for i in range(days):
61
+ d = start_dt + timedelta(days=i)
62
+ key = d.strftime("%Y-%m-%d")
63
+ buckets[key] = {
64
+ "date": key,
65
+ "count": 0,
66
+ "layers": {},
67
+ "categories": {},
68
+ }
69
+ return buckets
70
+
71
+
72
+ def build_timeline_visual(storage, category: Optional[str] = None,
73
+ layer: Optional[str] = None,
74
+ days: int = 365,
75
+ limit: int = 20000) -> Dict[str, Any]:
76
+ """构建时间线可视化聚合数据(不含明文)。
77
+
78
+ 返回 dict:
79
+ {
80
+ "generated_at", "days", "start_date", "end_date",
81
+ "total_memories", "active_memories",
82
+ "timeline": [{"date","count","layers","categories"}...],
83
+ "layers_distribution": {layer: count},
84
+ "categories": {category: count},
85
+ "preferences": {"themes": [{name,count}...], "trend": [{date, themes:[{name,count}]}...]},
86
+ "graph": {"entities": n, "relations": n},
87
+ "four_tier_summary": {"sensory":n,"short_term":n,"long_term":n,"permanent":n},
88
+ "privacy": "aggregate-only"
89
+ }
90
+ """
91
+ from mindforge.core.types import MemoryLayer
92
+
93
+ now = time.time()
94
+ layer_names = [e.value for e in MemoryLayer]
95
+ buckets = _build_daily_buckets(now - 86400 * max(1, days), now, days)
96
+
97
+ layers_dist: Dict[str, int] = {}
98
+ categories: Dict[str, int] = {}
99
+ theme_count: Dict[str, int] = {}
100
+ total = 0
101
+ active = 0
102
+
103
+ # 解析 layer 参数(字符串 -> 枚举)
104
+ layer_enum = None
105
+ if layer:
106
+ try:
107
+ layer_enum = MemoryLayer.from_string(str(layer))
108
+ except Exception:
109
+ layer_enum = None
110
+
111
+ mems = storage.list_memories(
112
+ category=category, layer=layer_enum, limit=limit)
113
+ for m in mems or []:
114
+ total += 1
115
+ # v6.1.2 修复:原来读的是 `getattr(m, "is_archived", False)`,但
116
+ # MemoryEntry(core/storage.py)没有 is_archived 字段——本项目的「归档」
117
+ # 实为 category == 'trash',而 `list_memories` 早已把它排除。于是这里
118
+ # 恒不成立,`active_memories` 永远等于 `total_memories`,字段失去意义。
119
+ # 改用真实信号:过了 valid_to 的时刻不再计入活跃(valid_to == 0 表示无边界)。
120
+ valid_to = getattr(m, "valid_to", 0) or 0
121
+ if not valid_to or valid_to > now:
122
+ active += 1
123
+ ts = getattr(m, "created_at", 0) or 0
124
+ key = _day_key(ts)
125
+ bucket = buckets.get(key)
126
+ if bucket is None:
127
+ continue
128
+ bucket["count"] += 1
129
+ lname = getattr(m, "layer", "") or "short_term"
130
+ lname = getattr(lname, "value", lname) or "short_term"
131
+ bucket["layers"][lname] = bucket["layers"].get(lname, 0) + 1
132
+ cname = getattr(m, "category", "") or "general"
133
+ bucket["categories"][cname] = bucket["categories"].get(cname, 0) + 1
134
+ layers_dist[lname] = layers_dist.get(lname, 0) + 1
135
+ categories[cname] = categories.get(cname, 0) + 1
136
+ theme = _tag_theme(list(getattr(m, "tags", []) or []))
137
+ if theme:
138
+ theme_count[theme] = theme_count.get(theme, 0) + 1
139
+
140
+ # 偏好趋势:按月窗口聚合 top 主题
141
+ trend_buckets: "OrderedDict[str, Dict[str, int]]" = OrderedDict()
142
+ for m in mems or []:
143
+ theme = _tag_theme(list(getattr(m, "tags", []) or []))
144
+ if not theme:
145
+ continue
146
+ ts = getattr(m, "created_at", 0) or 0
147
+ try:
148
+ month = datetime.fromtimestamp(ts).strftime("%Y-%m")
149
+ except (TypeError, ValueError, OSError):
150
+ continue
151
+ tb = trend_buckets.setdefault(month, {})
152
+ tb[theme] = tb.get(theme, 0) + 1
153
+
154
+ preference_trend = []
155
+ for month, themes in trend_buckets.items():
156
+ top = sorted(themes.items(), key=lambda kv: (-kv[1], kv[0]))[:8]
157
+ preference_trend.append({"date": month, "themes": [
158
+ {"name": name, "count": cnt} for name, cnt in top]})
159
+
160
+ # 知识图谱规模(仅计数,不含实体明文)
161
+ entities = 0
162
+ relations = 0
163
+ try:
164
+ conn = storage._get_conn()
165
+ row = conn.execute("SELECT COUNT(*) FROM knowledge_graph").fetchone()
166
+ entities = int(row[0]) if row and row[0] else 0
167
+ row = conn.execute("SELECT COUNT(*) FROM graph_relations").fetchone()
168
+ relations = int(row[0]) if row and row[0] else 0
169
+ except Exception:
170
+ pass
171
+
172
+ timeline = []
173
+ for b in buckets.values():
174
+ timeline.append({
175
+ "date": b["date"], "count": b["count"],
176
+ "layers": b["layers"], "categories": b["categories"],
177
+ })
178
+
179
+ four_tier = {}
180
+ for ln in layer_names:
181
+ four_tier[ln] = layers_dist.get(ln, 0)
182
+
183
+ start_key = next(iter(buckets), "")
184
+ end_key = next(reversed(buckets), "") if buckets else ""
185
+
186
+ return {
187
+ "success": True,
188
+ "generated_at": round(now, 3),
189
+ "days": days,
190
+ "start_date": start_key,
191
+ "end_date": end_key,
192
+ "total_memories": total,
193
+ "active_memories": active,
194
+ "timeline": timeline,
195
+ "layers_distribution": layers_dist,
196
+ "categories": categories,
197
+ "preferences": {
198
+ "themes": [{"name": k, "count": v}
199
+ for k, v in sorted(
200
+ theme_count.items(), key=lambda kv: (-kv[1], kv[0]))[:20]],
201
+ "trend": preference_trend,
202
+ },
203
+ "graph": {"entities": entities, "relations": relations},
204
+ "four_tier_summary": four_tier,
205
+ "privacy": "aggregate-only",
206
+ }