dsh-layered-memory 0.7.1 → 0.8.1
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.en.md +165 -75
- package/README.md +174 -104
- package/assets/img/EmbeddingSource.png +0 -0
- package/assets/img/MemoryTools.png +0 -0
- package/assets/img/ToolTrajectory.png +0 -0
- package/assets/readme/bench-dialog.svg +87 -0
- package/assets/readme/bench-workflow.svg +79 -0
- package/assets/readme/flow.svg +74 -74
- package/assets/readme/storage.svg +56 -56
- package/dist/client.js +380 -2
- package/dist/config.d.ts +41 -1
- package/dist/config.js +10 -3
- package/dist/hooks/recall.d.ts +11 -8
- package/dist/hooks/recall.js +70 -51
- package/dist/index.d.ts +20 -0
- package/dist/index.js +15 -4
- package/dist/llm.d.ts +28 -0
- package/dist/llm.js +33 -0
- package/dist/pipeline/l1.js +5 -4
- package/dist/pipeline/l2.js +7 -2
- package/dist/pipeline/l3.js +7 -2
- package/dist/pipeline/rebuild.js +2 -2
- package/dist/pipeline/runner.d.ts +45 -8
- package/dist/pipeline/runner.js +185 -39
- package/dist/pipeline/trigger.d.ts +38 -0
- package/dist/pipeline/trigger.js +64 -0
- package/dist/settings.d.ts +14 -0
- package/dist/settings.js +41 -2
- package/dist/stats.js +113 -1
- package/dist/store/bm25.js +2 -1
- package/dist/store/download-queue.d.ts +35 -2
- package/dist/store/download-queue.js +102 -5
- package/dist/store/embedding.d.ts +12 -6
- package/dist/store/embedding.js +10 -7
- package/dist/store/l0.d.ts +2 -0
- package/dist/store/l0.js +4 -0
- package/dist/store/l1.d.ts +2 -0
- package/dist/store/l1.js +2 -2
- package/dist/store/model-catalog.js +1 -1
- package/dist/store/pending.d.ts +27 -6
- package/dist/store/pending.js +49 -9
- package/dist/store/search-utils.js +3 -2
- package/dist/store/session-modes.d.ts +4 -0
- package/dist/store/session-modes.js +16 -0
- package/dist/store/sqlite.d.ts +12 -0
- package/dist/store/sqlite.js +103 -5
- package/dist/util/recall-budget.d.ts +32 -0
- package/dist/util/recall-budget.js +85 -0
- package/dist/util/text.d.ts +8 -3
- package/dist/util/text.js +41 -15
- package/dist/util/tokenizer.d.ts +14 -0
- package/dist/util/tokenizer.js +53 -0
- package/package.json +6 -3
package/README.en.md
CHANGED
|
@@ -1,56 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
alt="DeepSeek Harness hero banner: conversations distilled into layered memories and recalled before every model step — chat bubbles dissolve into three progressively brighter light layers flowing into a frosted-glass capsule with a glowing orb and gradient track (tick labels: 日常·工作·智能·关闭), with light threads looping back to suggest recall">
|
|
6
|
-
</p>
|
|
3
|
+
<img src="./assets/img/Hero.png" width="100%"
|
|
4
|
+
alt="DeepSeek Harness hero banner: conversations distilled into layered memories and recalled before every model step — chat bubbles dissolve into three progressively brighter light layers flowing into a frosted-glass capsule with a glowing orb and gradient track (tick labels: 日常·工作·智能·关闭), with light threads looping back to suggest recall">
|
|
7
5
|
|
|
8
6
|
# dsh-layered-memory
|
|
9
7
|
|
|
10
|
-
A
|
|
11
|
-
plugin): conversations are processed in the background through L0 capture → L1 atomic
|
|
12
|
-
memories → L2 scene consolidation → L3 persona distillation, and relevant memories are
|
|
13
|
-
automatically injected into context before every model step — neither the user nor the
|
|
14
|
-
model needs to do anything.
|
|
8
|
+
**A layered distillation memory plugin for DeepSeek Harness: conversations are processed in the background through L0 capture → L1 atomic memories → L2 scene consolidation → L3 persona distillation, and relevant memories are automatically injected into context before every model step — neither the user nor the model needs to do anything.**
|
|
15
9
|
|
|
16
|
-
|
|
17
|
-
> prompts, and dual-write storage design) are modeled after **MemoryCore** from
|
|
18
|
-
> [TencentDB-Agent-Memory](https://github.com/TencentCloud/TencentDB-Agent-Memory):
|
|
19
|
-
> prompts are kept as-is; only the L2/L3 "LLM manipulates files" flow is adapted to
|
|
20
|
-
> "LLM outputs, engineering side executes".
|
|
10
|
+
[简体中文](README.md) · [Latest release](https://github.com/JunNanLYS/dsh-layered-memory/releases/latest) · [Report issues](https://github.com/JunNanLYS/dsh-layered-memory/issues)
|
|
21
11
|
|
|
22
|
-
|
|
12
|
+
[](https://www.npmjs.com/package/dsh-layered-memory)
|
|
13
|
+
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
14
|
+
[](LICENSE)
|
|
23
15
|
|
|
24
|
-
|
|
25
|
-
<img src="./assets/readme/flow.svg" width="100%"
|
|
26
|
-
alt="Runtime data flow: session events from User and Assistant (left) flow into the plugin (L0 capture, L1–L3 distillation, retrieval, memory tools), which injects relevant memories into the DeepSeek Harness core (right) at agent/pre-step; distillation reuses the core's ctx.llm and data is dual-written to ~/.dsh/memory/">
|
|
27
|
-
</p>
|
|
28
|
-
|
|
29
|
-
The plugin attaches to DSH-native event seams (`session/event` for capture,
|
|
30
|
-
`agent/pre-step` for injection), reuses the host's `ctx.llm` for distillation, and stays
|
|
31
|
-
fully transparent to both user and model. It also registers three model-callable memory
|
|
32
|
-
tools: `memory_search` / `conversation_search` / `memory_read_scene`.
|
|
33
|
-
|
|
34
|
-
## Layered Memory (L0–L3)
|
|
35
|
-
|
|
36
|
-
<p align="center">
|
|
37
|
-
<img src="./assets/img/Layers.png" width="100%"
|
|
38
|
-
alt="Four memory layers refining from top-left to bottom-right: L0 raw conversation (chat bubbles) → L1 atomic memories (glowing fact particles) → L2 scene blocks (glass document slabs) → L3 core persona (radiant crystal core); stages connected by LLM extract/consolidate/distill light beams, shrinking width shows progressive refinement">
|
|
39
|
-
</p>
|
|
40
|
-
|
|
41
|
-
## Per-Session Memory Modes
|
|
42
|
-
|
|
43
|
-
<p align="center">
|
|
44
|
-
<img src="./assets/img/Modes.png" width="100%"
|
|
45
|
-
alt="Per-session memory modes: a glass capsule track with four stops (日常 · 工作 · 智能 · 关闭), the glowing orb resting on 智能 (default); a vignette above each — personal chat bubbles for 日常, code/document panes for 工作, two streams merging brightest for 智能, a dim dashed ghost bubble for 关闭">
|
|
46
|
-
</p>
|
|
47
|
-
|
|
48
|
-
- **Control**: the pill next to the mode selector in the input bar (`Memory · Auto`);
|
|
49
|
-
clicking opens a macOS-style sliding picker above — release to snap to the nearest
|
|
50
|
-
mode; adapts to light/dark themes;
|
|
51
|
-
- Each session's choice is persisted by sessionId to `session-modes.json`, surviving
|
|
52
|
-
restarts/session restore; stacks with the global switches (global is the master gate);
|
|
53
|
-
L2/L3 are fully family-isolated — content never leaks across families.
|
|
16
|
+
</div>
|
|
54
17
|
|
|
55
18
|
## Getting Started
|
|
56
19
|
|
|
@@ -58,7 +21,7 @@ Requires Node ≥ 22.16. Two invocation styles — the `npx` prefix can replace
|
|
|
58
21
|
any command below:
|
|
59
22
|
|
|
60
23
|
```bash
|
|
61
|
-
# Option 1: run the official CLI directly via npx (no pre-installed dsh; version can be pinned, e.g. dsh-layered-memory@0.
|
|
24
|
+
# Option 1: run the official CLI directly via npx (no pre-installed dsh; version can be pinned, e.g. dsh-layered-memory@0.8.0)
|
|
62
25
|
npx -y @deepseek-ai/dsh plugin --profile web add dsh-layered-memory
|
|
63
26
|
|
|
64
27
|
# Option 2: with the dsh CLI installed (dsh is a pnpm forwarder; npm i -g pnpm first if missing)
|
|
@@ -69,6 +32,24 @@ dsh plugin --profile web add https://github.com/JunNanLYS/dsh-layered-memory
|
|
|
69
32
|
dsh plugin --profile web add /path/to/dsh-layered-memory
|
|
70
33
|
```
|
|
71
34
|
|
|
35
|
+
### Install via an AI Agent (Recommended)
|
|
36
|
+
|
|
37
|
+
If your current agent can run terminal commands, send it this message as-is:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
Please install the dsh-layered-memory plugin for the web profile of DeepSeek Harness.
|
|
41
|
+
|
|
42
|
+
Run only the two commands below and do not modify any other profile:
|
|
43
|
+
dsh plugin --profile web add dsh-layered-memory
|
|
44
|
+
dsh --profile web --dump-config
|
|
45
|
+
|
|
46
|
+
Confirm that dsh-layered-memory appears in the output, then report the result to me.
|
|
47
|
+
Do not close or restart my running DSH yourself; after installation, remind me to manually restart the DSH Web Host.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The agent should report the installation result and explicitly tell you whether
|
|
51
|
+
`dsh-layered-memory` has appeared in the configuration.
|
|
52
|
+
|
|
72
53
|
This package declares a `dsh.bundle` composition layer (`cordis.patch.yml`); after
|
|
73
54
|
installation the **plugin entry is mounted automatically** — no need to hand-edit
|
|
74
55
|
`$DSH_HOME/profiles/web/cordis.patch.yml`. Then restart DeepSeek Harness and verify:
|
|
@@ -95,6 +76,63 @@ npm run smoke # smoke test (rebuild first: see command b
|
|
|
95
76
|
npx tsc src/smoke.ts --outDir dist-smoke --module nodenext --moduleResolution nodenext --target es2022 --strict --skipLibCheck --esModuleInterop
|
|
96
77
|
```
|
|
97
78
|
|
|
79
|
+
## Runtime Data Flow
|
|
80
|
+
|
|
81
|
+
<p align="center">
|
|
82
|
+
<img src="./assets/readme/flow.svg" width="100%"
|
|
83
|
+
alt="Runtime data flow: session events from User and Assistant (left) flow into the plugin (L0 capture, L1–L3 distillation, retrieval, memory tools), which injects relevant memories into the DeepSeek Harness core (right) at agent/pre-step; distillation reuses the core's ctx.llm and data is dual-written to ~/.dsh/memory/">
|
|
84
|
+
</p>
|
|
85
|
+
|
|
86
|
+
The plugin attaches to DSH-native event seams (`session/event` for capture,
|
|
87
|
+
`agent/pre-step` for injection) and reuses the host's `ctx.llm` for distillation. Recall
|
|
88
|
+
is presented as **message-side injection**: relevant memories enter the conversation as a
|
|
89
|
+
synthetic message placed right before the user's new message, rendered as a
|
|
90
|
+
**"Context injection · memory"** row in the chat flow (expand to see the hits) — so you
|
|
91
|
+
can see "memory at work" directly. Injected content is bounded by length and
|
|
92
|
+
time budgets — oversized lines are truncated (pointing the model at the memory tools for
|
|
93
|
+
the full text) and a timed-out recall silently skips that turn, never slowing the chat. It
|
|
94
|
+
also registers three model-callable memory tools: `memory_search` /
|
|
95
|
+
`conversation_search` / `memory_read_scene`.
|
|
96
|
+
|
|
97
|
+
In action: the "Context injection · memory" row surfaces relevant memories first, and
|
|
98
|
+
the model then calls `memory_read_scene` directly to read scene blocks before answering
|
|
99
|
+
from memory:
|
|
100
|
+
|
|
101
|
+
<p align="center">
|
|
102
|
+
<img src="./assets/img/MemoryTools.png" width="60%"
|
|
103
|
+
alt="Real conversation UI (light theme): a "Context injection · memory" row sits above the user's message asking about recent plans; the assistant lists 4 memory_read_scene tool calls (with scene-block .md filenames as arguments) before answering from memory">
|
|
104
|
+
</p>
|
|
105
|
+
|
|
106
|
+
In restricted sessions where only the code-execution entry point is available, the model
|
|
107
|
+
reaches the memory tools indirectly through `run_code` (nested as SUBTOOL calls in the
|
|
108
|
+
trajectory view):
|
|
109
|
+
|
|
110
|
+
<p align="center">
|
|
111
|
+
<img src="./assets/img/ToolTrajectory.png" width="80%"
|
|
112
|
+
alt="Tool-call trajectory view: a colored timeline on top and a step list on the left (SYSTEM/CONTEXT/USER/ASSISTANT/TOOL/SUBTOOL tags); a run_code tool step nests 5 memory_read_scene sub-tool calls (SUBTOOL tags), with a detail panel for the selected step on the right">
|
|
113
|
+
</p>
|
|
114
|
+
|
|
115
|
+
## Layered Memory (L0–L3)
|
|
116
|
+
|
|
117
|
+
<p align="center">
|
|
118
|
+
<img src="./assets/img/Layers.png" width="100%"
|
|
119
|
+
alt="Four memory layers refining from top-left to bottom-right: L0 raw conversation (chat bubbles) → L1 atomic memories (glowing fact particles) → L2 scene blocks (glass document slabs) → L3 core persona (radiant crystal core); stages connected by LLM extract/consolidate/distill light beams, shrinking width shows progressive refinement">
|
|
120
|
+
</p>
|
|
121
|
+
|
|
122
|
+
## Per-Session Memory Modes
|
|
123
|
+
|
|
124
|
+
<p align="center">
|
|
125
|
+
<img src="./assets/img/Modes.png" width="100%"
|
|
126
|
+
alt="Per-session memory modes: a glass capsule track with four stops (日常 · 工作 · 智能 · 关闭), the glowing orb resting on 智能 (default); a vignette above each — personal chat bubbles for 日常, code/document panes for 工作, two streams merging brightest for 智能, a dim dashed ghost bubble for 关闭">
|
|
127
|
+
</p>
|
|
128
|
+
|
|
129
|
+
- **Control**: the pill next to the mode selector in the input bar (`Memory · Auto`);
|
|
130
|
+
clicking opens a macOS-style sliding picker above — release to snap to the nearest
|
|
131
|
+
mode; adapts to light/dark themes;
|
|
132
|
+
- Each session's choice is persisted by sessionId to `session-modes.json`, surviving
|
|
133
|
+
restarts/session restore; stacks with the global switches (global is the master gate);
|
|
134
|
+
L2/L3 are fully family-isolated — content never leaks across families.
|
|
135
|
+
|
|
98
136
|
## UI Preview
|
|
99
137
|
|
|
100
138
|
<p align="center">
|
|
@@ -104,6 +142,44 @@ npx tsc src/smoke.ts --outDir dist-smoke --module nodenext --moduleResolution no
|
|
|
104
142
|
alt="The same settings memory browser overview in light theme: identical layout and information on light card backgrounds with the same accent family, theme switch without reload">
|
|
105
143
|
</p>
|
|
106
144
|
|
|
145
|
+
## Measured Comparison (DSH-MemBench: Automated Benchmark)
|
|
146
|
+
|
|
147
|
+
Screenshots show what the plugin looks like — this section answers "**what does enabling it actually buy you?**" with measured numbers from an **automated benchmark** ([`bench/`](./bench/), one command to reproduce). Method: the same scenario bank with verbatim-identical inputs runs in **Group A (memory on)** and **Group B (memory off)**, 3 repetitions each, merged; environment DeepSeek official `deepseek-v4-flash`, plugin 0.8.0, Windows; taxonomy adapted from [LongMemEval](https://github.com/xiaowu0162/longmemeval) / [LoCoMo](https://snap-research.github.io/locomo/) / [AMB](https://github.com/vectorize-io/agent-memory-benchmark).
|
|
148
|
+
|
|
149
|
+
### Dialog track (15 scenarios × 6 probe types × 3 reps = 270 questions/group): does it remember correctly
|
|
150
|
+
|
|
151
|
+
<p align="center">
|
|
152
|
+
<img src="./assets/readme/bench-dialog.svg" width="100%"
|
|
153
|
+
alt="DSH-MemBench dialog track, Group A vs Group B bar chart: overall accuracy A (memory on) 92.6% (250/270) vs B (memory off) 17.8% (48/270); per probe type, 45 each — extraction A 45/45 vs B 3/45, multi-hop A 45/45 vs B 0/45, temporal A 43/45 vs B 0/45, knowledge updates A 31/45 vs B 0/45, scene recall A 41/45 vs B 0/45, abstention both 45/45 with 0 fabricated">
|
|
154
|
+
</p>
|
|
155
|
+
|
|
156
|
+
**Dual-channel recall** (Group A): passive injection hit rate **75.1%** (the answer's key points appear in the recall injection, 169/225); most of the rest the model recovered by **actively calling the memory tools** — 84 questions with active queries, **60 rescued by tools**. The end-to-end 92.6% is the composite of both channels plus model utilization. With the memory store accumulating across scenarios for the whole run, 144 probe injections carried other scenarios' memories (honestly counted) — yet overall accuracy held at 92.6%: interference resistance under a growing store, measured.
|
|
157
|
+
|
|
158
|
+
### Workflow track (4 scenarios × 3 reps, real tool sandbox): does it do it right, and cheaper
|
|
159
|
+
|
|
160
|
+
<p align="center">
|
|
161
|
+
<img src="./assets/readme/bench-workflow.svg" width="100%"
|
|
162
|
+
alt="DSH-MemBench workflow track, Group A vs Group B: task completion A 24/33 (72.7%) vs B 11/33 (33.3%); cost comparison (Group B as the full-bar baseline) — steps 125 vs 186 (B +49%), tool calls 184 vs 296 (B +61%), input tokens 1.30M vs 1.86M (B +43%); asks-user-for-help A 0 vs B 3; login scenario input tokens A 241k vs B 453k (+88%)">
|
|
163
|
+
</p>
|
|
164
|
+
|
|
165
|
+
**Login scenario close-up** (credentials exist only in memory; the site is a local service with unforgeable tokens): Group A completed all three runs **in a single turn each** (6/6, 241k input); Group B had to **ask the user for credentials every time** (3 asks, double the turns) and still finished only 5/6, at 453k input — **+88%**. This is one of memory's core values: **what it saves is not task difficulty, but pointless round-trips and re-teaching**.
|
|
166
|
+
|
|
167
|
+
### Methodology & reproduction
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
node bench/harness/run.mjs --arm A --repeats 3 --provider deepseek-official --model deepseek-v4-flash
|
|
171
|
+
node bench/harness/run.mjs --arm B --repeats 3 --provider deepseek-official --model deepseek-v4-flash # dialog track
|
|
172
|
+
node bench/harness/run.mjs --track workflow --arm A/B --repeats 3 ... # workflow track
|
|
173
|
+
node bench/harness/report.mjs --latest [dialog|workflow] # aggregate report
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- Scoring: programmatic `contains-all` plus an LLM judge against key points (every answer and verdict is preserved in `result.json` for human audit); workflow completion is verified programmatically from produced files and their contents;
|
|
177
|
+
- Metrics come from provider-reported usage (input with cache-hit split) and session-event folding; the **steady-state cache rate** excludes each session's first request (A 88.7% vs B 85.4% — memory injection does not hurt caching);
|
|
178
|
+
- Regression use: run before/after a plugin change and diff with `compare.mjs` (environment header check + Group-B control-drift warning);
|
|
179
|
+
- Limitations (stated honestly): single machine, 3 merged runs; the judge model is the same as the tested model; the scenario bank is author-built (biased toward memory-advantage scenarios — reproduce it yourself); the tool audit flags out-of-sandbox access (agents occasionally probed the user home dir in tests; this benchmark's answers never exist in the real memory store, so the numbers are unaffected).
|
|
180
|
+
|
|
181
|
+
Full reports and per-question data: [`bench/baseline/`](./bench/baseline/).
|
|
182
|
+
|
|
107
183
|
## Configuration
|
|
108
184
|
|
|
109
185
|
Override configs go into the profile's own `cordis.patch.yml` as a **top-level bare
|
|
@@ -115,9 +191,9 @@ the bundle layer appends and causes `duplicate loader entry id` startup failure)
|
|
|
115
191
|
name: dsh-layered-memory
|
|
116
192
|
config: # keys replace whole lines (no deep merge); write out all keys you want to keep
|
|
117
193
|
family: auto # default mode for new sessions: auto | chat | work
|
|
118
|
-
llm: # distillation
|
|
119
|
-
provider: ''
|
|
120
|
-
model: ''
|
|
194
|
+
llm: # static distillation route (both fields set = deployment pin,
|
|
195
|
+
provider: '' # which outranks the settings-page selection; when empty the route
|
|
196
|
+
model: '' # follows the settings-page "distillation model" picker or the default model)
|
|
121
197
|
```
|
|
122
198
|
|
|
123
199
|
| Field | Default | Description |
|
|
@@ -128,8 +204,9 @@ the bundle layer appends and causes `duplicate loader entry id` startup failure)
|
|
|
128
204
|
| `capture.stripCodeBlocks` | `true` | Strip code blocks from assistant messages |
|
|
129
205
|
| `capture.maxMessageChars` | `4000` | Max characters per message |
|
|
130
206
|
| `extract.enabled` | `true` | L1 extraction |
|
|
131
|
-
| `extract.minMessages` | `
|
|
132
|
-
| `extract.
|
|
207
|
+
| `extract.minMessages` | `6` | Steady-state trigger threshold: run L1 extraction once a session accumulates N new messages. The effective threshold ramps up 1→2→4→…→N (first turn yields memories immediately, then batches to save calls) |
|
|
208
|
+
| `extract.idleSeconds` | `300` | Idle flush: distill a session's pending slice after N seconds of silence (catches "user left before reaching the threshold"); `0` disables |
|
|
209
|
+
| `extract.backgroundMessages` | `10` | Background messages attached to extraction (fetched per session from L0 — no cross-session contamination) |
|
|
133
210
|
| `extract.candidatePool` | `5` | Dedup candidate pool size |
|
|
134
211
|
| `l2.enabled` | `true` | L2 scene consolidation |
|
|
135
212
|
| `l2.minNewMemories` | `5` | New-memory threshold since last L2 consolidation |
|
|
@@ -138,9 +215,12 @@ the bundle layer appends and causes `duplicate loader entry id` startup failure)
|
|
|
138
215
|
| `l3.enabled` | `true` | L3 persona distillation |
|
|
139
216
|
| `l3.interval` | `20` | L3 distillation interval (new-memory count) |
|
|
140
217
|
| `recall.enabled` | `true` | Auto recall |
|
|
141
|
-
| `recall.maxResults` | `5` | L1 records injected
|
|
142
|
-
| `recall.
|
|
143
|
-
| `recall.
|
|
218
|
+
| `recall.maxResults` | `5` | Max L1 records injected before each new user message |
|
|
219
|
+
| `recall.maxCharsPerMemory` | `500` | Per-memory character cap for injected recall (overlong lines truncated with a hint to use the memory tools for the full text); `0` disables |
|
|
220
|
+
| `recall.maxTotalRecallChars` | `2000` | Total character cap per injected recall batch (lowest-ranked tail dropped first); `0` disables |
|
|
221
|
+
| `recall.timeoutMs` | `5000` | Overall recall budget (ms): a timed-out recall skips that turn without blocking the chat; `0` disables |
|
|
222
|
+
| `recall.includePersona` | `true` | Inject persona context into the system prompt (`<user-persona>`, stable zone) |
|
|
223
|
+
| `recall.includeSceneNav` | `true` | Inject scene navigation into the system prompt (`<scene-navigation>`, stable zone) |
|
|
144
224
|
| `recall.strategy` | `hybrid` | Retrieval strategy: `keyword` / `embedding` / `hybrid` |
|
|
145
225
|
| `recall.scoreThreshold` | `0.3` | Recall score threshold (below is not injected; applies to keyword/embedding only, not pre-fusion hybrid; tool path unfiltered) |
|
|
146
226
|
| `embedding.enabled` | `false` | Vector retrieval switch; off = pure FTS |
|
|
@@ -152,11 +232,12 @@ the bundle layer appends and causes `duplicate loader entry id` startup failure)
|
|
|
152
232
|
| `embedding.timeoutMs` | `10000` | Per-call embedding timeout (ms) |
|
|
153
233
|
| `embedding.allowLocalModels` | `true` | Allow the local embedding tier (deployment ceiling; when off, no model downloads and no local tier in settings) |
|
|
154
234
|
| `embedding.mirror` | `https://hf-mirror.com` | Download mirror root for local models (can be changed back to `https://huggingface.co`) |
|
|
155
|
-
| `
|
|
156
|
-
| `llm.
|
|
235
|
+
| `embedding.proxy` | `''` | Three-state download proxy: `''` (default) = auto-detect proxy env vars (`HTTPS_PROXY`/`ALL_PROXY` etc., honoring `NO_PROXY`); `none` = disable, always direct; any other value = proxy URL (e.g. `http://127.0.0.1:7890`). Direct connections to the mirror are intermittently unreachable on some networks (connect timeouts and poisoned bytes have both been observed) — keep the default auto-detection on machines with a proxy |
|
|
236
|
+
| `llm.provider/model` | empty | Static distillation route (deployment pin): when **both** fields are set the route is locked, outranking the settings-page selection and the default model (deployments can force distillation onto a specific route); when empty the route follows "settings-page selection → default model". At runtime, switch among **configured providers** (including custom ones added in dsh Settings → Models) via the "distillation model" picker in Settings → Memory → Overview — effective immediately, no restart needed |
|
|
237
|
+
| `llm.maxTokens` | `65536` | Fallback output cap for non-layered calls. Each distillation stage has its own budget (extraction 16k / dedup 8k / L2 32k / L3 16k; auto ×4 when reasoning effort is high/max, so thinking can't starve the text budget); the per-layer budgets are runtime-adjustable in Settings → Memory → Overview → distillation parameters (empty/0 = built-in defaults) |
|
|
157
238
|
| `llm.reasoningEffort` | `off` | Distillation reasoning-effort tier (deployment default): `off` / `high` / `max`; empty string = don't send (follow model default). Distillation is structured extraction, so thinking is off by default — a reasoning model (e.g. v4-flash) at its default `high` tier can consume the entire output budget on thinking, leaving 0 chars of text; set to empty string for models that don't recognize the effort parameter. Switchable at runtime in Settings → Memory → Overview ("follow config" falls back to this value) |
|
|
158
239
|
| `llm.temperature` | `0.3` | Distillation temperature |
|
|
159
|
-
| `llm.maxInputChars` | `700000` | Input character budget per distillation call (over-budget L1 inputs are chunked automatically) |
|
|
240
|
+
| `llm.maxInputChars` | `700000` | Input character budget per distillation call (over-budget L1 inputs are chunked automatically); runtime-adjustable in Settings → distillation parameters → input budget (empty/0 = follow this value) |
|
|
160
241
|
| `llm.timeoutMs` | `120000` | Per-call distillation timeout (ms) |
|
|
161
242
|
| `tools` | `true` | Whether to register model-callable memory tools |
|
|
162
243
|
|
|
@@ -176,24 +257,25 @@ local), switchable at runtime in the settings page — see the next section.
|
|
|
176
257
|
Pick the embedding source in Settings → Memory → Overview → Semantic Retrieval;
|
|
177
258
|
it takes effect immediately, no config edit or restart:
|
|
178
259
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
| **Local** | Pick from a built-in model catalog, ONNX-quantized **CPU inference** — no API key, data never leaves the machine |
|
|
184
|
-
|
|
185
|
-
The local catalog is a built-in allowlist (each model pinned to a revision with
|
|
186
|
-
per-file sha256; arbitrary repos cannot be downloaded):
|
|
260
|
+
<p align="center">
|
|
261
|
+
<img src="./assets/img/EmbeddingSource.png" width="70%"
|
|
262
|
+
alt="Semantic retrieval (embedding source) panel in the settings page (light theme): a three-state selector (Off/Local/Remote, Local selected) showing the current source and the first-run runtime install hint; below it the local model catalog lists BGE small Chinese (in use/ready), EmbeddingGemma 300M (download 316MB) and BGE-M3 (download 560MB) with dims/context/size/notes and download buttons">
|
|
263
|
+
</p>
|
|
187
264
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
265
|
+
Three sources: **Off** (default; no vector embedding at all, pure BM25 keyword
|
|
266
|
+
retrieval), **Remote** (bring any OpenAI-compatible `/embeddings` service, selectable
|
|
267
|
+
only when the `embedding.*` quartet is configured), **Local** (pick from a built-in
|
|
268
|
+
model catalog, ONNX-quantized **CPU inference** — no API key, data never leaves the
|
|
269
|
+
machine). The local catalog is a built-in allowlist (each model pinned to a revision
|
|
270
|
+
with per-file sha256; arbitrary repos cannot be downloaded).
|
|
193
271
|
|
|
194
272
|
- **Download**: one click on the model card (default mirror `hf-mirror.com`, resumable
|
|
195
|
-
downloads + sha256 integrity checks
|
|
196
|
-
|
|
273
|
+
downloads + sha256 integrity checks; a proxy is used when direct access is
|
|
274
|
+
unreachable — proxy env vars like `HTTPS_PROXY`/`ALL_PROXY` are auto-detected by
|
|
275
|
+
default, see `embedding.proxy`). Per-file failures auto-retry with a rotated cache
|
|
276
|
+
key (`?dshmem-retry=N`, sidestepping occasionally bad CDN cache objects); hash
|
|
277
|
+
mismatches restart from zero, network errors resume from the checkpoint; stored
|
|
278
|
+
under `models/<id>/` in the data directory, deletable from the settings page at any time;
|
|
197
279
|
- **On-demand runtime**: the inference runtime (transformers.js, ~100–200MB) is
|
|
198
280
|
installed only on first switch to the local tier, into `runtime/` in the data
|
|
199
281
|
directory — never in the plugin's dependency tree or install directory;
|
|
@@ -221,8 +303,8 @@ of the raw model output; all failure warns carry the first stack frame.
|
|
|
221
303
|
|
|
222
304
|
- The full pipeline is embedded (no external Gateway); distillation reuses DSH's own LLM;
|
|
223
305
|
- L2/L3 changed from "LLM manipulates file tools" to "LLM outputs operation JSON / full documents, engineering side executes";
|
|
224
|
-
- Recall injection happens at `agent/pre-step`
|
|
225
|
-
- Storage/retrieval is a single-machine slimmed version of the official sqlite backend (drops multi-tenant isolation columns, TCVDB cloud backend, audit tables; tokenization uses
|
|
306
|
+
- Recall injection happens at `agent/pre-step` (message-side synthetic message, the official pre-step replacement semantics) plus agent-scoped `systemPrompt.context` (persona/navigation stable zone — DSH-native events/services);
|
|
307
|
+
- Storage/retrieval is a single-machine slimmed version of the official sqlite backend (drops multi-tenant isolation columns, TCVDB cloud backend, audit tables; tokenization uses jieba like the official one — @node-rs/jieba prebuilt binaries union CJK character bigrams: word tokens give BM25 exact-word hits while bigrams keep sub-word recall; on load failure it falls back to pure bigrams, and FTS indexes are rebuilt automatically via a tokenizer version stamp).
|
|
226
308
|
|
|
227
309
|
## Credits
|
|
228
310
|
|
|
@@ -231,6 +313,14 @@ dual-write storage architecture) are modeled after **MemoryCore** from
|
|
|
231
313
|
[TencentCloud/TencentDB-Agent-Memory](https://github.com/TencentCloud/TencentDB-Agent-Memory).
|
|
232
314
|
Thanks to the original project for open-sourcing its design and implementation.
|
|
233
315
|
|
|
316
|
+
## Roadmap
|
|
317
|
+
|
|
318
|
+
Features under planning — feedback and priorities welcome in the
|
|
319
|
+
[issue tracker](https://github.com/JunNanLYS/dsh-layered-memory/issues):
|
|
320
|
+
|
|
321
|
+
- [ ] **Git branch awareness**: associate memories with the current git branch; recall can filter/boost by branch (orthogonal to the existing memory modes)
|
|
322
|
+
- [ ] **Claude Code / Codex memory import**: one-click migration of existing memory assets (`CLAUDE.md`, Claude Code memory files, Codex `AGENTS.md`, etc.), fed into the layered distillation pipeline
|
|
323
|
+
|
|
234
324
|
## License
|
|
235
325
|
|
|
236
326
|
[MIT](LICENSE)
|