dsh-nebula-mind 0.3.3 → 0.3.4

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.
package/README.md CHANGED
@@ -94,6 +94,34 @@ set NEBULA_HOME=E:\nebula # 换根目录,数据在 E:\nebula\memory
94
94
 
95
95
  ---
96
96
 
97
+ ## 三步跑起来
98
+
99
+ ```bash
100
+ # 1) 装(装完彻底退出 DSH 再重开)
101
+ dsh plugin --profile desktop add dsh-nebula-mind
102
+
103
+ # 2) 记下第一条(它同时会把这条算进向量索引)
104
+ python python/xin.py add "今天他提到周五要交画册"
105
+
106
+ # 3) 自检:哪几路检索开着、差什么、下一步干什么
107
+ python python/doctor.py
108
+ ```
109
+
110
+ `doctor.py` 会逐项报告,并明确告诉你「缺了也能用」的部分。典型输出:
111
+
112
+ ```
113
+ ✅ Python 3.14.6
114
+ ✅ 数据目录可写 ~/.dsh/nebula-mind/memory(2 本账)
115
+ ✅ 记忆 事实 20 / 画像 4
116
+ ⚠️ 语义检索 没有 torch/transformers(ModuleNotFoundError)
117
+ ↳ 缺它也能用:关键词 / 图扩散 / 情绪共振照常
118
+ ✅ 心情联动 ~/.dsh/feiyu-mood/state.json
119
+ ✅ 星图生成 nebula-doctor.html(47 KB)
120
+ 结论:5 项可用,1 项降级,0 项不可用
121
+ ```
122
+
123
+ **没有 torch 也能用**:四路召回里只少「语义向量」这一路,另外三路(关键词、图扩散、情绪共振)照样跑;装了 torch 再跑一次 `python python/embed.py --build` 就有了。
124
+
97
125
  ## 记忆是怎么长出来的
98
126
 
99
127
  | 账本 | 内容 | 谁写 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-nebula-mind",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "lib/host.js",
@@ -46,7 +46,8 @@
46
46
  "node": ">=20"
47
47
  },
48
48
  "scripts": {
49
- "check": "node --check lib/host.js"
49
+ "check": "node --check lib/host.js",
50
+ "doctor": "python python/doctor.py"
50
51
  },
51
52
  "repository": {
52
53
  "type": "git",
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """自检:告诉用户这台机器上,哪些能力开了、哪些还差什么。
4
+
5
+ 用法:
6
+ python python/doctor.py
7
+ NEBULA_PYTHON=... python python/doctor.py
8
+
9
+ 输出三项:✅ 可用 / ⚠️ 降级可用 / ❌ 不可用(并给一句下一步)。
10
+ """
11
+ import importlib
12
+ import json
13
+ import os
14
+ import subprocess
15
+ import sys
16
+ import tempfile
17
+ from pathlib import Path
18
+
19
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
20
+ import paths as P # noqa: E402
21
+
22
+ OK, WARN, BAD = "✅", "⚠️", "❌"
23
+ rows = []
24
+
25
+
26
+ def add(level, name, detail, hint=""):
27
+ rows.append((level, name, detail, hint))
28
+
29
+
30
+ def check_python():
31
+ v = sys.version_info
32
+ if v >= (3, 9):
33
+ add(OK, "Python", f"{v.major}.{v.minor}.{v.micro}")
34
+ else:
35
+ add(BAD, "Python", f"{v.major}.{v.minor}(需要 3.9+)", "装一个新的 Python 再跑")
36
+
37
+
38
+ def check_data_dir():
39
+ try:
40
+ P.MIND_DIR = Path(P.MIND)
41
+ P.MIND_DIR.mkdir(parents=True, exist_ok=True)
42
+ t = P.MIND_DIR / ".doctor-probe"
43
+ t.write_text("ok", encoding="utf-8")
44
+ t.unlink()
45
+ n = len(list(P.MIND_DIR.glob("*.jsonl")))
46
+ add(OK, "数据目录可写", f"{P.MIND}({n} 本账)")
47
+ except Exception as e:
48
+ add(BAD, "数据目录不可写", f"{P.MIND}: {e}", "检查权限,或用 NEBULA_HOME 换一个目录")
49
+
50
+
51
+ def check_accounts():
52
+ books = {
53
+ "notes.jsonl": "事实",
54
+ "profile.jsonl": "画像",
55
+ "promises.jsonl": "约定",
56
+ "thoughts.jsonl": "念想",
57
+ }
58
+ have = {name: 0 for name in books}
59
+ for f in books:
60
+ p = Path(P.MIND) / f
61
+ if p.exists():
62
+ have[f] = sum(1 for line in p.read_text(encoding="utf-8").splitlines() if line.strip())
63
+ total = sum(have.values())
64
+ if total == 0:
65
+ add(WARN, "记忆", "还是空的", '先记一条:python python/xin.py add "一句话"')
66
+ else:
67
+ detail = " / ".join(f"{books[k]} {v}" for k, v in have.items() if v)
68
+ add(OK, "记忆", detail)
69
+
70
+
71
+ def check_vector():
72
+ try:
73
+ importlib.import_module("torch")
74
+ importlib.import_module("transformers")
75
+ except Exception as e:
76
+ add(WARN, "语义检索", f"没有 torch/transformers({type(e).__name__})",
77
+ "缺它也能用:关键词 / 图扩散 / 情绪共振照常;要开语义检索请看 README「向量检索」")
78
+ return
79
+ model = Path(P.BGE_DIR)
80
+ if not model.exists():
81
+ add(WARN, "语义检索", f"模型没下({model})", "见 README「向量检索」的下载步骤,或设 NEBULA_BGE_DIR")
82
+ return
83
+ vec = Path(P.MIND) / "vectors.json"
84
+ if vec.exists():
85
+ try:
86
+ n = len(json.loads(vec.read_text(encoding="utf-8")).get("items", []))
87
+ add(OK, "语义检索", f"模型在 / 索引 {n} 条")
88
+ except Exception:
89
+ add(WARN, "语义检索", "索引坏了", "重建:python python/embed.py --build")
90
+ else:
91
+ add(WARN, "语义检索", "模型在,还没建索引", "python python/embed.py --build")
92
+
93
+
94
+ def check_mood():
95
+ p = Path(P.MOOD_STATE)
96
+ if p.exists():
97
+ add(OK, "心情联动", f"{p}")
98
+ else:
99
+ add(WARN, "心情联动", "没找到 dsh-feiyu-mood 的状态文件", "不装心情插件也行,心情按中性算")
100
+
101
+
102
+ def check_starmap():
103
+ try:
104
+ out = Path(tempfile.gettempdir()) / "nebula-doctor.html"
105
+ r = subprocess.run([sys.executable, os.path.join(os.path.dirname(os.path.abspath(__file__)), "star_map.py"),
106
+ "--out", str(out)], capture_output=True, text=True,
107
+ encoding="utf-8", errors="replace", timeout=60)
108
+ if r.returncode == 0 and out.exists():
109
+ add(OK, "星图生成", f"{out.name}({out.stat().st_size // 1024} KB)")
110
+ else:
111
+ add(BAD, "星图生成失败", (r.stderr or r.stdout or "").strip()[:120])
112
+ except Exception as e:
113
+ add(BAD, "星图生成失败", str(e)[:120])
114
+
115
+
116
+ def main():
117
+ check_python()
118
+ check_data_dir()
119
+ check_accounts()
120
+ check_vector()
121
+ check_mood()
122
+ check_starmap()
123
+
124
+ print("记忆星图 · 自检\n" + "=" * 46)
125
+ for level, name, detail, hint in rows:
126
+ print(f"{level} {name:<12} {detail}")
127
+ if hint:
128
+ print(f" ↳ {hint}")
129
+ bad = sum(1 for r in rows if r[0] == BAD)
130
+ warn = sum(1 for r in rows if r[0] == WARN)
131
+ print("=" * 46)
132
+ print(f"结论:{len(rows) - bad - warn} 项可用,{warn} 项降级,{bad} 项不可用")
133
+ return 1 if bad else 0
134
+
135
+
136
+ if __name__ == "__main__":
137
+ sys.exit(main())
package/python/xin.py CHANGED
@@ -314,7 +314,7 @@ def tokenize(s):
314
314
  return en + zh + bigrams
315
315
 
316
316
 
317
- EMBED_PY = r"D:\indextts\.venv\Scripts\python.exe"
317
+ EMBED_PY = os.environ.get("NEBULA_EMBED_PYTHON") or r"D:\indextts\.venv\Scripts\python.exe"
318
318
  EMBED_SH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "embed.py")
319
319
  VEC_JSON = os.path.join(ROOT, "vectors.json")
320
320