ultra-memory 3.1.0 → 4.0.0

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.
@@ -0,0 +1 @@
1
+ # ultra-memory integrations package
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ ultra-memory: LangChain Memory 集成
4
+ 提供 UltraMemoryMemory 类,实现 LangChain BaseMemory 接口,
5
+ 可直接用于 LC agents。
6
+
7
+ 用法:
8
+ from integrations.langchain_memory import UltraMemoryMemory
9
+ memory = UltraMemoryMemory(session_id="sess_langchain_test", project="my-agent")
10
+ agent = OpenAIAgent(..., memory=memory)
11
+ """
12
+
13
+ import json
14
+ import os
15
+ import sys
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ try:
20
+ from langchain.schema import BaseMemory
21
+ from langchain.schema import HumanMessage, AIMessage
22
+ HAS_LANGCHAIN = True
23
+ except ImportError:
24
+ HAS_LANGCHAIN = False
25
+
26
+ ULTRA_MEMORY_HOME = Path(os.environ.get("ULTRA_MEMORY_HOME", Path.home() / ".ultra-memory"))
27
+ _SCRIPTS_DIR = Path(__file__).parent.parent / "scripts"
28
+
29
+
30
+ class UltraMemoryMemory:
31
+ """
32
+ LangChain memory backed by ultra-memory's 5-layer system.
33
+ Implements BaseMemory-compatible interface.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ session_id: str,
39
+ project: str = "langchain",
40
+ top_k: int = 5,
41
+ ):
42
+ self.session_id = session_id
43
+ self.project = project
44
+ self.top_k = top_k
45
+
46
+ @property
47
+ def memory_variables(self) -> list[str]:
48
+ return ["ultra_memory_context"]
49
+
50
+ def load_memory_variables(self, inputs: dict) -> dict:
51
+ """加载与当前上下文相关的记忆"""
52
+ query = inputs.get("query", "")
53
+
54
+ if not self.session_id:
55
+ return {"ultra_memory_context": ""}
56
+
57
+ if query:
58
+ # 使用 recall 获取相关记忆
59
+ import subprocess, io
60
+ recall_script = _SCRIPTS_DIR / "recall.py"
61
+ old_stdout = sys.stdout
62
+ sys.stdout = io.StringIO()
63
+ try:
64
+ subprocess.run(
65
+ [sys.executable, str(recall_script),
66
+ "--session", self.session_id,
67
+ "--query", query,
68
+ "--top-k", str(self.top_k)],
69
+ capture_output=True,
70
+ timeout=30,
71
+ )
72
+ context = sys.stdout.getvalue()
73
+ except Exception:
74
+ context = ""
75
+ finally:
76
+ sys.stdout = old_stdout
77
+ else:
78
+ # 加载最新摘要
79
+ summary_file = ULTRA_MEMORY_HOME / "sessions" / self.session_id / "summary.md"
80
+ if summary_file.exists():
81
+ context = summary_file.read_text(encoding="utf-8")
82
+ else:
83
+ context = ""
84
+
85
+ return {"ultra_memory_context": context}
86
+
87
+ def save_context(self, inputs: dict, outputs: dict) -> None:
88
+ """保存一轮对话到 ultra-memory"""
89
+ import subprocess
90
+
91
+ input_text = inputs.get("input", "")[:200]
92
+ output_text = outputs.get("output", "")[:200]
93
+
94
+ detail = {
95
+ "input": inputs.get("input", ""),
96
+ "output": outputs.get("output", ""),
97
+ }
98
+
99
+ try:
100
+ subprocess.run(
101
+ [
102
+ sys.executable,
103
+ str(_SCRIPTS_DIR / "log_op.py"),
104
+ "--session", self.session_id,
105
+ "--type", "tool_call",
106
+ "--summary", f"LC: {input_text[:60]}",
107
+ "--detail", json.dumps(detail, ensure_ascii=False),
108
+ "--tags", "langchain",
109
+ ],
110
+ capture_output=True,
111
+ timeout=5,
112
+ )
113
+ except Exception:
114
+ pass
115
+
116
+ def clear(self) -> None:
117
+ """清除当前记忆(不删除 session)"""
118
+ self.session_id = None
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ ultra-memory: LangGraph Checkpointer 集成
4
+ 提供 UltraMemoryCheckpointer 类,作为 LangGraph 的状态持久化后端。
5
+
6
+ 用法:
7
+ from integrations.langgraph_checkpointer import UltraMemoryCheckpointer
8
+ checkpointer = UltraMemoryCheckpointer(session_id="sess_langgraph_proj")
9
+ compiled = graph.compile(checkpointer=checkpointer)
10
+ """
11
+
12
+ import json
13
+ import os
14
+ from pathlib import Path
15
+ from typing import Any, Optional
16
+
17
+ ULTRA_MEMORY_HOME = Path(os.environ.get("ULTRA_MEMORY_HOME", Path.home() / ".ultra-memory"))
18
+
19
+
20
+ class UltraMemoryCheckpointer:
21
+ """
22
+ LangGraph checkpointer backed by ultra-memory。
23
+ 在每个节点执行后保存/恢复 agent graph 状态。
24
+ """
25
+
26
+ def __init__(self, session_id: str):
27
+ self.session_id = session_id
28
+ self.checkpoint_dir = ULTRA_MEMORY_HOME / "sessions" / session_id / "checkpoints"
29
+ self.checkpoint_dir.mkdir(parents=True, exist_ok=True)
30
+
31
+ def _checkpoint_file(self, thread_id: str, step: int) -> Path:
32
+ """获取检查点文件路径"""
33
+ return self.checkpoint_dir / f"thread_{thread_id}_step_{step:04d}.json"
34
+
35
+ def get(self, thread_id: str, step: int) -> Optional[dict[str, Any]]:
36
+ """获取指定 thread 和 step 的检查点状态"""
37
+ checkpoint_file = self._checkpoint_file(thread_id, step)
38
+ if not checkpoint_file.exists():
39
+ return None
40
+ try:
41
+ with open(checkpoint_file, encoding="utf-8") as f:
42
+ data = json.load(f)
43
+ return data.get("state")
44
+ except (json.JSONDecodeError, IOError):
45
+ return None
46
+
47
+ def put(self, thread_id: str, step: int, state: dict[str, Any]) -> None:
48
+ """保存检查点状态"""
49
+ checkpoint_file = self._checkpoint_file(thread_id, step)
50
+ data = {
51
+ "step": step,
52
+ "state": state,
53
+ "session_id": self.session_id,
54
+ "thread_id": thread_id,
55
+ }
56
+ with open(checkpoint_file, "w", encoding="utf-8") as f:
57
+ json.dump(data, f, ensure_ascii=False, indent=2)
58
+
59
+ def get_latest(self, thread_id: str) -> Optional[dict[str, Any]]:
60
+ """获取指定 thread 的最新检查点"""
61
+ checkpoints = sorted(
62
+ self.checkpoint_dir.glob(f"thread_{thread_id}_step_*.json"),
63
+ key=lambda p: int(p.stem.split("_")[-1]),
64
+ )
65
+ if not checkpoints:
66
+ return None
67
+ return self.get(thread_id, int(checkpoints[-1].stem.split("_")[-1]))
68
+
69
+ def list_threads(self) -> list[str]:
70
+ """列出所有已有 thread ID"""
71
+ threads = set()
72
+ for f in self.checkpoint_dir.glob("thread_*_step_*.json"):
73
+ parts = f.stem.split("_")
74
+ if len(parts) >= 2:
75
+ threads.add(parts[1])
76
+ return sorted(threads)
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ ultra-memory: n8n 集成节点
4
+ 作为 n8n "Execute Command" 节点的 Python 脚本后端,
5
+ 支持 init / log / recall 三种操作。
6
+
7
+ n8n 配置示例:
8
+ Execute Command 节点
9
+ 命令: python3
10
+ 参数: /path/to/ultra-memory/integrations/n8n_nodes.py <operation> <args>
11
+
12
+ Operations:
13
+ init --project <proj> → 返回 session_id
14
+ log --session <id> --summary "..." --type <type> --detail '{}'
15
+ recall --session <id> --query "..."
16
+ profile --action read|update --field <field> --value <value>
17
+ """
18
+
19
+ import json
20
+ import os
21
+ import sys
22
+ import re
23
+ from pathlib import Path
24
+
25
+ ULTRA_MEMORY_HOME = Path(os.environ.get("ULTRA_MEMORY_HOME", Path.home() / ".ultra-memory"))
26
+ _SCRIPTS_DIR = Path(__file__).parent.parent / "scripts"
27
+
28
+
29
+ def _run_script(script_name: str, args: list[str]) -> str:
30
+ """运行脚本并返回输出"""
31
+ import subprocess
32
+
33
+ script_path = _SCRIPTS_DIR / script_name
34
+ result = subprocess.run(
35
+ [sys.executable, str(script_path)] + args,
36
+ capture_output=True, text=True, timeout=30,
37
+ )
38
+ return result.stdout + result.stderr
39
+
40
+
41
+ def cmd_init(project: str) -> dict:
42
+ """初始化会话"""
43
+ output = _run_script("init.py", ["--project", project, "--resume"])
44
+
45
+ session_id = None
46
+ memory_ready = False
47
+
48
+ for line in output.split("\n"):
49
+ if "session_id:" in line:
50
+ match = re.search(r"session_id:\s*(sess_\w+)", line)
51
+ if match:
52
+ session_id = match.group(1)
53
+ if "MEMORY_READY" in line:
54
+ memory_ready = True
55
+
56
+ return {
57
+ "success": memory_ready,
58
+ "session_id": session_id,
59
+ "output": output,
60
+ }
61
+
62
+
63
+ def cmd_log(session_id: str, summary: str, op_type: str, detail: str = "{}") -> dict:
64
+ """记录操作"""
65
+ output = _run_script("log_op.py", [
66
+ "--session", session_id,
67
+ "--type", op_type,
68
+ "--summary", summary,
69
+ "--detail", detail,
70
+ ])
71
+ return {"success": True, "output": output}
72
+
73
+
74
+ def cmd_recall(session_id: str, query: str, top_k: int = 5) -> dict:
75
+ """检索记忆"""
76
+ output = _run_script("recall.py", [
77
+ "--session", session_id,
78
+ "--query", query,
79
+ "--top-k", str(top_k),
80
+ ])
81
+ return {"success": True, "output": output}
82
+
83
+
84
+ def cmd_profile(action: str, field: str = None, value: str = None) -> dict:
85
+ """读取或更新用户画像"""
86
+ if action == "read":
87
+ output = _run_script("evolve_profile.py", [])
88
+ return {"success": True, "output": output}
89
+ elif action == "update" and field and value:
90
+ output = _run_script("evolve_profile.py", [
91
+ "--field", field, "--value", value,
92
+ ])
93
+ return {"success": True, "output": output}
94
+ return {"success": False, "error": "invalid profile command"}
95
+
96
+
97
+ # ── CLI ─────────────────────────────────────────────────────────────────────
98
+
99
+
100
+ if __name__ == "__main__":
101
+ if len(sys.argv) < 2:
102
+ print("Usage: n8n_nodes.py <init|log|recall|profile> [args...]")
103
+ sys.exit(1)
104
+
105
+ operation = sys.argv[1].lower()
106
+ args = sys.argv[2:]
107
+
108
+ result = {}
109
+ try:
110
+ if operation == "init":
111
+ project = next((a for a in args if a.startswith("--project=")),
112
+ "--project=default").split("=", 1)[1]
113
+ result = cmd_init(project)
114
+
115
+ elif operation == "log":
116
+ session_id = next((a for a in args if a.startswith("--session=")),
117
+ None).split("=", 1)[1]
118
+ summary = next((a for a in args if a.startswith("--summary=")),
119
+ "").split("=", 1)[1]
120
+ op_type = next((a for a in args if a.startswith("--type=")),
121
+ "tool_call").split("=", 1)[1]
122
+ detail = next((a for a in args if a.startswith("--detail=")),
123
+ "{}").split("=", 1)[1]
124
+ result = cmd_log(session_id, summary, op_type, detail)
125
+
126
+ elif operation == "recall":
127
+ session_id = next((a for a in args if a.startswith("--session=")),
128
+ None).split("=", 1)[1]
129
+ query = next((a for a in args if a.startswith("--query=")),
130
+ "").split("=", 1)[1]
131
+ result = cmd_recall(session_id, query)
132
+
133
+ elif operation == "profile":
134
+ action = next((a for a in args if a.startswith("--action=")),
135
+ "read").split("=", 1)[1]
136
+ field = next((a for a in args if a.startswith("--field=")),
137
+ None)
138
+ field = field.split("=", 1)[1] if field else None
139
+ value = next((a for a in args if a.startswith("--value=")),
140
+ None)
141
+ value = value.split("=", 1)[1] if value else None
142
+ result = cmd_profile(action, field, value)
143
+
144
+ else:
145
+ result = {"success": False, "error": f"unknown operation: {operation}"}
146
+
147
+ except Exception as e:
148
+ result = {"success": False, "error": str(e)}
149
+
150
+ print(json.dumps(result, ensure_ascii=False, indent=2))
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ultra-memory",
3
- "version": "3.1.0",
4
- "description": "超长会话记忆系统 — 5层记忆架构,零外部依赖,支持所有LLM平台(Claude/GPT/Gemini/Qwen等)",
3
+ "version": "4.0.0",
4
+ "description": "超长会话记忆系统 — 5层记忆架构+自我进化引擎+时序矛盾检测,支持所有LLM平台",
5
5
  "keywords": [
6
6
  "ai",
7
7
  "memory",
@@ -46,7 +46,9 @@
46
46
  "ultra-memory-recall": "scripts/recall.py",
47
47
  "ultra-memory-summarize": "scripts/summarize.py",
48
48
  "ultra-memory-restore": "scripts/restore.py",
49
- "ultra-memory-knowledge": "scripts/log_knowledge.py"
49
+ "ultra-memory-knowledge": "scripts/log_knowledge.py",
50
+ "ultra-memory-extract-facts": "scripts/extract_facts.py",
51
+ "ultra-memory-evolve": "scripts/evolve_profile.py"
50
52
  },
51
53
  "scripts": {
52
54
  "test": "python3 test_e2e.py",
@@ -57,6 +59,14 @@
57
59
  "node": ">=18.0.0",
58
60
  "python": ">=3.8.0"
59
61
  },
62
+ "optionalDependencies": {
63
+ "pdfminer.six": "*",
64
+ "pytesseract": "*",
65
+ "whisper": "*"
66
+ },
67
+ "peerDependencies": {
68
+ "langchain": ">=0.1.0"
69
+ },
60
70
  "os": [
61
71
  "darwin",
62
72
  "linux",
@@ -64,7 +74,9 @@
64
74
  ],
65
75
  "files": [
66
76
  "scripts/",
77
+ "scripts/multimodal/",
67
78
  "platform/",
79
+ "integrations/",
68
80
  "SKILL.md",
69
81
  "README.md",
70
82
  "CLAWHUB.md"
@@ -102,7 +114,8 @@
102
114
  "2": "summary.md — 会话摘要层(里程碑压缩)",
103
115
  "3": "semantic/ — 跨会话语义层(知识库+实体索引)",
104
116
  "4": "entities.jsonl — 结构化实体索引(7类实体)",
105
- "5": "tfidf_cache.json — 向量语义层(TF-IDF/sentence-transformers)"
117
+ "5": "tfidf_cache.json — 向量语义层(TF-IDF/sentence-transformers)",
118
+ "6": "evolution/ — 自我进化层(事实提取+矛盾检测+遗忘)"
106
119
  }
107
120
  }
108
121
  }