kb-memory 2.1.1__tar.gz → 2.1.2__tar.gz

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 (92) hide show
  1. {kb_memory-2.1.1 → kb_memory-2.1.2}/PKG-INFO +6 -10
  2. {kb_memory-2.1.1 → kb_memory-2.1.2}/README.md +5 -9
  3. kb_memory-2.1.2/kb/__init__.py +2 -0
  4. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/api.py +3 -5
  5. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/watcher.py +22 -2
  6. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb_memory.egg-info/PKG-INFO +6 -10
  7. {kb_memory-2.1.1 → kb_memory-2.1.2}/pyproject.toml +1 -1
  8. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n10_ask.py +3 -1
  9. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n15_watcher.py +35 -0
  10. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_v21_bootstrap.py +1 -1
  11. kb_memory-2.1.1/kb/__init__.py +0 -2
  12. {kb_memory-2.1.1 → kb_memory-2.1.2}/LICENSE +0 -0
  13. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/__main__.py +0 -0
  14. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/audit.py +0 -0
  15. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/bm25.py +0 -0
  16. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/cli.py +0 -0
  17. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/config.py +0 -0
  18. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/consolidation.py +0 -0
  19. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/discovery.py +0 -0
  20. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/embedder.py +0 -0
  21. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/eval.py +0 -0
  22. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/governance.py +0 -0
  23. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/i18n.py +0 -0
  24. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/idle.py +0 -0
  25. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/ingest.py +0 -0
  26. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/llm.py +0 -0
  27. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/logging_setup.py +0 -0
  28. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/mcp.py +0 -0
  29. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/models.py +0 -0
  30. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/monitor.py +0 -0
  31. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/reranker.py +0 -0
  32. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/retriever.py +0 -0
  33. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/scopes.py +0 -0
  34. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/service.py +0 -0
  35. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/sparse.py +0 -0
  36. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb/storage.py +0 -0
  37. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb_memory.egg-info/SOURCES.txt +0 -0
  38. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb_memory.egg-info/dependency_links.txt +0 -0
  39. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb_memory.egg-info/entry_points.txt +0 -0
  40. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb_memory.egg-info/requires.txt +0 -0
  41. {kb_memory-2.1.1 → kb_memory-2.1.2}/kb_memory.egg-info/top_level.txt +0 -0
  42. {kb_memory-2.1.1 → kb_memory-2.1.2}/setup.cfg +0 -0
  43. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_agent_isolation.py +0 -0
  44. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_api_auth.py +0 -0
  45. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_charset_json.py +0 -0
  46. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_consolidation.py +0 -0
  47. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_embed_model_resolution.py +0 -0
  48. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_hotfix_validation.py +0 -0
  49. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_improvements_bm25_batch.py +0 -0
  50. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_improvements_concurrency.py +0 -0
  51. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_improvements_isolation_recall.py +0 -0
  52. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n01_config.py +0 -0
  53. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n02_models.py +0 -0
  54. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n03_embedder.py +0 -0
  55. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n04_storage.py +0 -0
  56. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n05_bm25.py +0 -0
  57. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n06_retriever_service.py +0 -0
  58. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n07_api.py +0 -0
  59. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n08_api_ext.py +0 -0
  60. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n09_llm.py +0 -0
  61. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n11_routing.py +0 -0
  62. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n12_mcp.py +0 -0
  63. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n13_ingest.py +0 -0
  64. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n13_ingest_extra.py +0 -0
  65. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n14_web.py +0 -0
  66. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n16_regression.py +0 -0
  67. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n17_logging.py +0 -0
  68. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n18_logs_api.py +0 -0
  69. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n18_logs_doc.py +0 -0
  70. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n18_monitor.py +0 -0
  71. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n19_nav.py +0 -0
  72. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n21_decay.py +0 -0
  73. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n21_metadata.py +0 -0
  74. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n22_dedup.py +0 -0
  75. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n22_governance.py +0 -0
  76. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n23_audit.py +0 -0
  77. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n23_cli.py +0 -0
  78. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n24_reranker.py +0 -0
  79. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n25_sparse.py +0 -0
  80. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n26_eval.py +0 -0
  81. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n27_perf.py +0 -0
  82. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n28_cli.py +0 -0
  83. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n29_security.py +0 -0
  84. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n30_mcp_stdio.py +0 -0
  85. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n31_serve_probe.py +0 -0
  86. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_n32_hybrid_fusion_recall.py +0 -0
  87. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_skill_package.py +0 -0
  88. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_user_experience_fixes.py +0 -0
  89. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_v21_i18n.py +0 -0
  90. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_v21_packaging.py +0 -0
  91. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_v21_runtime.py +0 -0
  92. {kb_memory-2.1.1 → kb_memory-2.1.2}/tests/test_watcher_missing_dir.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kb-memory
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Local-first agent memory & knowledge service: hybrid retrieval + memory governance (decay / dedup / freshness), REST + MCP dual protocol, fully usable offline without an LLM
5
5
  Author: fish827-08
6
6
  License-Expression: Apache-2.0
@@ -60,7 +60,7 @@ Dynamic: license-file
60
60
 
61
61
  <!-- GitHub Topics: mcp, memory-service, rag, ai-agent, knowledge-base, local-first, llm, claude-code, hybrid-search, embedding -->
62
62
 
63
- **kb** 是本地优先、完全免费的 Agent 记忆与知识服务(v2.1.1 · 自动化回归全绿,待人工验收)。
63
+ **kb** 是本地优先、完全免费的 Agent 记忆与知识服务(v2.1.2)。
64
64
 
65
65
  > 开源协议:[Apache-2.0](LICENSE)(含专利授权,可商用)。
66
66
 
@@ -150,7 +150,7 @@ curl http://127.0.0.1:8000/api/v1/healthz
150
150
  >
151
151
  > **CPU 即可运行,不占显卡**:全新安装默认拿到 CPU 版 torch(`device=cpu`),
152
152
  > 不占用 GPU 显存,无独立显卡的机器也能完整使用。有显卡想加速(大库、批量入库)
153
- > 可另装 CUDA 版 torch,见 [AGENTS.md](AGENTS.md) 第 7 节;也可显式 `KB_DEVICE=cuda`。
153
+ > 可另装 CUDA 版 torch 后显式 `KB_DEVICE=cuda` 启用 GPU 加速。
154
154
  >
155
155
  > **LLM 默认关闭(`KB_LLM_MODE=off`)**:服务启动不会探测/加载/调用任何大模型,
156
156
  > 零显存、零成本、纯离线;记忆写入、文档入库、混合检索完整可用。
@@ -398,18 +398,14 @@ python -m kb dedup --threshold 0.92 # 语义去重
398
398
  ```
399
399
  kb/ kb 服务源码(config / models / embedder / storage / bm25 / retriever /
400
400
  service / llm / ingest / watcher / api / mcp / cli + reranker / sparse / eval)
401
- tests/ kb 验收测试(417 项通过 / 1 跳过,含 eval_zh_50.jsonl 检索评测数据集)
402
- docs/ 核心文档、当前设计(历史 spec/plan 已归档本地,不上传)
401
+ tests/ kb 验收测试(425 项通过 / 1 跳过,含 eval_zh_50.jsonl 检索评测数据集)
402
+ docs/ 用户指南、开发者手册与设计文档
403
403
  kb_data/ kb 运行数据(gitignore)
404
- _archive/ 旧学习项目 + 历史 spec/plan 归档(仅本地保留,禁止参考)
405
404
  ```
406
405
 
407
406
  ## 更多文档
408
407
 
409
- - **用户使用手册(人类用户入口)**:[docs/USER_GUIDE.md](docs/USER_GUIDE.md)
410
- - **AI 接力文档(AI 助手入口)**:[PROJECT.md](PROJECT.md)(项目状态 / 进度看板 / 接手指南)
408
+ - **用户使用手册**:[docs/USER_GUIDE.md](docs/USER_GUIDE.md)
411
409
  - 设计文档(需求、架构、API、里程碑):`docs/superpowers/specs/2026-08-23-kb-memory-service-design.md`
412
410
  - **v2.1 设计(当前)**:`docs/superpowers/specs/2026-10-20-kb-v2.1-design.md`
413
411
  - 开发者维护手册:`docs/DEV_HANDBOOK.md`
414
- - AI 协作规范:`AGENTS.md`
415
- - 历史 spec / 节点计划已归档到本地 `_archive/docs/`(不上传,不再作为开发依据)
@@ -10,7 +10,7 @@
10
10
 
11
11
  <!-- GitHub Topics: mcp, memory-service, rag, ai-agent, knowledge-base, local-first, llm, claude-code, hybrid-search, embedding -->
12
12
 
13
- **kb** 是本地优先、完全免费的 Agent 记忆与知识服务(v2.1.1 · 自动化回归全绿,待人工验收)。
13
+ **kb** 是本地优先、完全免费的 Agent 记忆与知识服务(v2.1.2)。
14
14
 
15
15
  > 开源协议:[Apache-2.0](LICENSE)(含专利授权,可商用)。
16
16
 
@@ -100,7 +100,7 @@ curl http://127.0.0.1:8000/api/v1/healthz
100
100
  >
101
101
  > **CPU 即可运行,不占显卡**:全新安装默认拿到 CPU 版 torch(`device=cpu`),
102
102
  > 不占用 GPU 显存,无独立显卡的机器也能完整使用。有显卡想加速(大库、批量入库)
103
- > 可另装 CUDA 版 torch,见 [AGENTS.md](AGENTS.md) 第 7 节;也可显式 `KB_DEVICE=cuda`。
103
+ > 可另装 CUDA 版 torch 后显式 `KB_DEVICE=cuda` 启用 GPU 加速。
104
104
  >
105
105
  > **LLM 默认关闭(`KB_LLM_MODE=off`)**:服务启动不会探测/加载/调用任何大模型,
106
106
  > 零显存、零成本、纯离线;记忆写入、文档入库、混合检索完整可用。
@@ -348,18 +348,14 @@ python -m kb dedup --threshold 0.92 # 语义去重
348
348
  ```
349
349
  kb/ kb 服务源码(config / models / embedder / storage / bm25 / retriever /
350
350
  service / llm / ingest / watcher / api / mcp / cli + reranker / sparse / eval)
351
- tests/ kb 验收测试(417 项通过 / 1 跳过,含 eval_zh_50.jsonl 检索评测数据集)
352
- docs/ 核心文档、当前设计(历史 spec/plan 已归档本地,不上传)
351
+ tests/ kb 验收测试(425 项通过 / 1 跳过,含 eval_zh_50.jsonl 检索评测数据集)
352
+ docs/ 用户指南、开发者手册与设计文档
353
353
  kb_data/ kb 运行数据(gitignore)
354
- _archive/ 旧学习项目 + 历史 spec/plan 归档(仅本地保留,禁止参考)
355
354
  ```
356
355
 
357
356
  ## 更多文档
358
357
 
359
- - **用户使用手册(人类用户入口)**:[docs/USER_GUIDE.md](docs/USER_GUIDE.md)
360
- - **AI 接力文档(AI 助手入口)**:[PROJECT.md](PROJECT.md)(项目状态 / 进度看板 / 接手指南)
358
+ - **用户使用手册**:[docs/USER_GUIDE.md](docs/USER_GUIDE.md)
361
359
  - 设计文档(需求、架构、API、里程碑):`docs/superpowers/specs/2026-08-23-kb-memory-service-design.md`
362
360
  - **v2.1 设计(当前)**:`docs/superpowers/specs/2026-10-20-kb-v2.1-design.md`
363
361
  - 开发者维护手册:`docs/DEV_HANDBOOK.md`
364
- - AI 协作规范:`AGENTS.md`
365
- - 历史 spec / 节点计划已归档到本地 `_archive/docs/`(不上传,不再作为开发依据)
@@ -0,0 +1,2 @@
1
+ """kb:本地 Agent 记忆与知识服务。"""
2
+ __version__ = "2.1.2" # 对应 git tag v2.1.2(生命周期日志 version= 字段用)
@@ -562,7 +562,7 @@ def create_app(settings: Settings | None = None,
562
562
  "未安装 watchdog,目录监听已禁用"
563
563
  "(pip install \"kb-memory[ingest]\" 可启用)")
564
564
  else:
565
- watcher = KBWatcher(kb, wd)
565
+ watcher = KBWatcher(kb, wd, ignore_dir=kb.settings.data_dir)
566
566
  watcher.start()
567
567
  # 本地监控 Agent(TASK-0017):serve 模式默认启用,仅读配置决定间隔;
568
568
  # TestClient 测试默认不启动(monitor_enabled 默认 False,测试按需开)
@@ -811,11 +811,9 @@ def create_app(settings: Settings | None = None,
811
811
  try:
812
812
  return kb.ask(body.question, agent_id=body.agent_id,
813
813
  client=body.client, project=body.project)
814
- except LLMDisabledError:
814
+ except LLMDisabledError as exc:
815
815
  raise HTTPException(status_code=503, detail={
816
- "error": "LLM_DISABLED",
817
- "message": "未检测到可用的 LLM:请在 .env 配置 KB_LLM_MODE=local "
818
- "(Ollama)或 KB_LLM_API_KEY 启用云端(任意 OpenAI 兼容服务商)"})
816
+ "error": "LLM_DISABLED", "message": str(exc)})
819
817
 
820
818
  @app.get("/api/v1/audit")
821
819
  def audit(client: str | None = None, project: str | None = None,
@@ -47,10 +47,16 @@ def _is_supported(path: Path) -> bool:
47
47
  class KBWatcher:
48
48
  """目录监听器:watchdog Observer 采集事件 + 后台 worker 去抖处理。"""
49
49
 
50
- def __init__(self, service, watch_dir: Path, debounce_seconds: float = 2.0):
51
- """service 为 KBService;watch_dir 为监听目录(仅顶层,不递归)。"""
50
+ def __init__(self, service, watch_dir: Path, debounce_seconds: float = 2.0,
51
+ ignore_dir: Path | None = None):
52
+ """service 为 KBService;watch_dir 为监听目录(仅顶层,不递归)。
53
+
54
+ ignore_dir 为服务数据目录:与 watch_dir 重合时 start() 跳过监听,
55
+ 避免把 bm25_corpus.json 等数据文件反复入库(自我入库死循环)。
56
+ """
52
57
  self.service = service
53
58
  self.watch_dir = Path(watch_dir)
59
+ self.ignore_dir = Path(ignore_dir) if ignore_dir is not None else None
54
60
  self.debounce_seconds = debounce_seconds
55
61
  self._pending: dict[Path, float] = {} # 待入库路径 → 最近一次事件时间
56
62
  self._deletes: queue.Queue[str] = queue.Queue() # 待删除 source(文件名)
@@ -60,6 +66,15 @@ class KBWatcher:
60
66
  self._worker: threading.Thread | None = None
61
67
 
62
68
  # ---- 生命周期 ----
69
+ def _overlaps_data_dir(self) -> bool:
70
+ """监听目录是否就是数据目录(相对路径/符号链接解析后比较)。"""
71
+ if self.ignore_dir is None:
72
+ return False
73
+ try:
74
+ return self.watch_dir.resolve() == self.ignore_dir.resolve()
75
+ except OSError:
76
+ return False
77
+
63
78
  def start(self) -> None:
64
79
  """启动 watchdog Observer 与后台处理线程。
65
80
 
@@ -75,6 +90,11 @@ class KBWatcher:
75
90
  logger.info("监听目录不存在或不可用,跳过目录监听:%s",
76
91
  self.watch_dir)
77
92
  return
93
+ if self._overlaps_data_dir():
94
+ logger.warning(
95
+ "监听目录与数据目录相同(%s),已跳过目录监听以避免自我入库;"
96
+ "请把 KB_WATCH_DIR 指向数据目录之外", self.watch_dir)
97
+ return
78
98
  self._observer = Observer()
79
99
  self._observer.schedule(_EventHandler(self), str(self.watch_dir),
80
100
  recursive=False)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kb-memory
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Local-first agent memory & knowledge service: hybrid retrieval + memory governance (decay / dedup / freshness), REST + MCP dual protocol, fully usable offline without an LLM
5
5
  Author: fish827-08
6
6
  License-Expression: Apache-2.0
@@ -60,7 +60,7 @@ Dynamic: license-file
60
60
 
61
61
  <!-- GitHub Topics: mcp, memory-service, rag, ai-agent, knowledge-base, local-first, llm, claude-code, hybrid-search, embedding -->
62
62
 
63
- **kb** 是本地优先、完全免费的 Agent 记忆与知识服务(v2.1.1 · 自动化回归全绿,待人工验收)。
63
+ **kb** 是本地优先、完全免费的 Agent 记忆与知识服务(v2.1.2)。
64
64
 
65
65
  > 开源协议:[Apache-2.0](LICENSE)(含专利授权,可商用)。
66
66
 
@@ -150,7 +150,7 @@ curl http://127.0.0.1:8000/api/v1/healthz
150
150
  >
151
151
  > **CPU 即可运行,不占显卡**:全新安装默认拿到 CPU 版 torch(`device=cpu`),
152
152
  > 不占用 GPU 显存,无独立显卡的机器也能完整使用。有显卡想加速(大库、批量入库)
153
- > 可另装 CUDA 版 torch,见 [AGENTS.md](AGENTS.md) 第 7 节;也可显式 `KB_DEVICE=cuda`。
153
+ > 可另装 CUDA 版 torch 后显式 `KB_DEVICE=cuda` 启用 GPU 加速。
154
154
  >
155
155
  > **LLM 默认关闭(`KB_LLM_MODE=off`)**:服务启动不会探测/加载/调用任何大模型,
156
156
  > 零显存、零成本、纯离线;记忆写入、文档入库、混合检索完整可用。
@@ -398,18 +398,14 @@ python -m kb dedup --threshold 0.92 # 语义去重
398
398
  ```
399
399
  kb/ kb 服务源码(config / models / embedder / storage / bm25 / retriever /
400
400
  service / llm / ingest / watcher / api / mcp / cli + reranker / sparse / eval)
401
- tests/ kb 验收测试(417 项通过 / 1 跳过,含 eval_zh_50.jsonl 检索评测数据集)
402
- docs/ 核心文档、当前设计(历史 spec/plan 已归档本地,不上传)
401
+ tests/ kb 验收测试(425 项通过 / 1 跳过,含 eval_zh_50.jsonl 检索评测数据集)
402
+ docs/ 用户指南、开发者手册与设计文档
403
403
  kb_data/ kb 运行数据(gitignore)
404
- _archive/ 旧学习项目 + 历史 spec/plan 归档(仅本地保留,禁止参考)
405
404
  ```
406
405
 
407
406
  ## 更多文档
408
407
 
409
- - **用户使用手册(人类用户入口)**:[docs/USER_GUIDE.md](docs/USER_GUIDE.md)
410
- - **AI 接力文档(AI 助手入口)**:[PROJECT.md](PROJECT.md)(项目状态 / 进度看板 / 接手指南)
408
+ - **用户使用手册**:[docs/USER_GUIDE.md](docs/USER_GUIDE.md)
411
409
  - 设计文档(需求、架构、API、里程碑):`docs/superpowers/specs/2026-08-23-kb-memory-service-design.md`
412
410
  - **v2.1 设计(当前)**:`docs/superpowers/specs/2026-10-20-kb-v2.1-design.md`
413
411
  - 开发者维护手册:`docs/DEV_HANDBOOK.md`
414
- - AI 协作规范:`AGENTS.md`
415
- - 历史 spec / 节点计划已归档到本地 `_archive/docs/`(不上传,不再作为开发依据)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "kb-memory"
7
- version = "2.1.1"
7
+ version = "2.1.2"
8
8
  description = "Local-first agent memory & knowledge service: hybrid retrieval + memory governance (decay / dedup / freshness), REST + MCP dual protocol, fully usable offline without an LLM"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -51,4 +51,6 @@ def test_禁用转503(env_isolated):
51
51
  r = c.post("/api/v1/ask", json={"question": "测试"})
52
52
  assert r.status_code == 503
53
53
  assert r.json()["error"] == "LLM_DISABLED"
54
- assert "Ollama" in r.json()["message"]
54
+ # 默认 KB_LLM_MODE=off:必须说明是主动关闭,而不是误报 Ollama 不可用
55
+ assert "已关闭" in r.json()["message"]
56
+ assert "KB_LLM_MODE=off" in r.json()["message"]
@@ -39,3 +39,38 @@ def test_文件删除级联清理(env_isolated):
39
39
  assert s.list_records(q="将被删除")[1] == 0
40
40
  finally:
41
41
  w.stop()
42
+
43
+
44
+ def test_监听目录与数据目录相同时跳过(tmp_path, caplog):
45
+ """watch_dir == data_dir 时 listener 会把自己的 bm25 语料反复入库,必须跳过。"""
46
+ from kb.watcher import KBWatcher
47
+
48
+ class FakeService:
49
+ def add_document(self, path): # pragma: no cover - 不应被调用
50
+ raise AssertionError("不应入库")
51
+
52
+ data = tmp_path / "data"
53
+ data.mkdir()
54
+ w = KBWatcher(FakeService(), data, ignore_dir=data)
55
+ with caplog.at_level("WARNING", logger="kb.watcher"):
56
+ w.start()
57
+ try:
58
+ assert w._observer is None
59
+ assert w._worker is None
60
+ assert "自我入库" in caplog.text
61
+ finally:
62
+ w.stop()
63
+
64
+
65
+ def test_监听目录与数据目录不同仍正常监听(tmp_path):
66
+ """ignore_dir 只拦截与监听目录重合的数据目录,不影响正常监听。"""
67
+ from kb.watcher import KBWatcher
68
+
69
+ watch = tmp_path / "watch"; watch.mkdir()
70
+ data = tmp_path / "data"; data.mkdir()
71
+ w = KBWatcher(object(), watch, ignore_dir=data)
72
+ w.start()
73
+ try:
74
+ assert w._observer is not None and w._worker is not None
75
+ finally:
76
+ w.stop()
@@ -107,7 +107,7 @@ class FailingFirstPipRunner(FakeRunner):
107
107
  if "pip install kb-memory" in joined \
108
108
  and "--extra-index-url" not in joined:
109
109
  return SimpleNamespace(returncode=1, stdout="")
110
- return SimpleNamespace(returncode=0, stdout="2.1.1\n")
110
+ return SimpleNamespace(returncode=0, stdout="2.1.2\n")
111
111
 
112
112
 
113
113
  def test_pip源缺包时自动官方兜底(boot, tmp_path, capsys):
@@ -1,2 +0,0 @@
1
- """kb:本地 Agent 记忆与知识服务。"""
2
- __version__ = "2.1.1" # 对应 git tag v2.1.1(生命周期日志 version= 字段用)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes