@unblocklabs/unblock-memory 0.3.25 → 0.3.26
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.
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
|
+
import { setImmediate as yieldToEventLoop } from "node:timers/promises";
|
|
5
6
|
import { readSessionManifest } from "./session-sync.js";
|
|
6
7
|
import { resolveSessionSource } from "./sources.js";
|
|
7
8
|
import { trainingCandidates } from "./training-candidates.js";
|
|
@@ -95,7 +96,15 @@ export async function historicalTrainingSearch(stateDir, chatTypes, cutoff, open
|
|
|
95
96
|
FROM content_vectors cv JOIN vectors_vec v ON v.hash_seq=cv.hash||'_'||cv.seq
|
|
96
97
|
WHERE cv.hash=? ORDER BY cv.seq`);
|
|
97
98
|
let dimensions;
|
|
99
|
+
// Copy cooperatively: concurrent snapshots must not starve lease timers,
|
|
100
|
+
// provider responses or native-model disposal. Keep the source transaction
|
|
101
|
+
// open across yields so every row still comes from the same read snapshot.
|
|
102
|
+
let yieldAt = performance.now() + 25;
|
|
98
103
|
for (const session of Object.values(manifest.sessions).sort((a, b) => a.documentPath.localeCompare(b.documentPath))) {
|
|
104
|
+
if (performance.now() >= yieldAt) {
|
|
105
|
+
await yieldToEventLoop();
|
|
106
|
+
yieldAt = performance.now() + 25;
|
|
107
|
+
}
|
|
99
108
|
// Loggie projections can retroactively annotate old text using later revisions.
|
|
100
109
|
// Workspace files and meetings lack immutable historical content proof here.
|
|
101
110
|
if (!chatTypes.includes(session.chatType) || session.provider === "loggie" || session.startedAt >= cutoff) {
|
|
@@ -120,7 +129,11 @@ export async function historicalTrainingSearch(stateDir, chatTypes, cutoff, open
|
|
|
120
129
|
report.sessions++;
|
|
121
130
|
if (prefix.spans.length < (session.messages?.length ?? 0))
|
|
122
131
|
report.truncated++;
|
|
123
|
-
for (const chunk of chunks.
|
|
132
|
+
for (const chunk of chunks.iterate(row.hash)) {
|
|
133
|
+
if (performance.now() >= yieldAt) {
|
|
134
|
+
await yieldToEventLoop();
|
|
135
|
+
yieldAt = performance.now() + 25;
|
|
136
|
+
}
|
|
124
137
|
if (!Number.isSafeInteger(chunk.pos) || !Number.isSafeInteger(chunk.chunk_len) || chunk.pos < 0 || chunk.chunk_len <= 0 ||
|
|
125
138
|
chunk.pos + chunk.chunk_len > prefix.body.length) {
|
|
126
139
|
report.excludedChunks++;
|
|
@@ -43,6 +43,8 @@ export class TrainingStore {
|
|
|
43
43
|
status TEXT NOT NULL CHECK(status IN ('pending','attempted','complete','failed','ambiguous')),
|
|
44
44
|
result_json TEXT, error TEXT, completed_at INTEGER
|
|
45
45
|
) STRICT;
|
|
46
|
+
CREATE INDEX IF NOT EXISTS training_judgment_exclusions ON training_steps(json_extract(request_json,'$.identity'))
|
|
47
|
+
WHERE stage='judge' AND status='complete' AND json_extract(result_json,'$.excluded')=1;
|
|
46
48
|
CREATE TABLE IF NOT EXISTS training_step_attempts (
|
|
47
49
|
id INTEGER PRIMARY KEY, step_id TEXT NOT NULL REFERENCES training_steps(id), started_at INTEGER NOT NULL,
|
|
48
50
|
finished_at INTEGER, status TEXT NOT NULL, error TEXT, result_json TEXT
|
package/docs/memory-training.md
CHANGED
|
@@ -128,8 +128,13 @@ No blanket failure skipping.
|
|
|
128
128
|
|
|
129
129
|
The host must support isolated completion and grant
|
|
130
130
|
`plugins.entries.unblock-memory.llm.allowModelOverride: true` with
|
|
131
|
-
`allowedModels: ["openai/gpt-6-luna"]`.
|
|
132
|
-
|
|
131
|
+
`allowedModels: ["openai/gpt-6-luna"]`. The operator CLI explicitly selects an
|
|
132
|
+
agent, so an unbound CLI runtime also requires
|
|
133
|
+
`plugins.entries.unblock-memory.llm.allowAgentIdOverride: true`. Preserve other
|
|
134
|
+
grants. The host's model catalog and selected runtime must support that exact
|
|
135
|
+
model with usable host-managed authentication; a model permission grant alone
|
|
136
|
+
does not establish model availability. The plugin does not change its own
|
|
137
|
+
permissions. Credentials stay with the host, never in provenance.
|
|
133
138
|
|
|
134
139
|
## Export and consolidation
|
|
135
140
|
|
|
@@ -145,3 +150,16 @@ identical-input groups together across nodes for train/validation. Use the actua
|
|
|
145
150
|
**LFM2.5-230M-Base** tokenizer and an explicit causal-LM input/target format before
|
|
146
151
|
fine-tuning; the interim byte limit is not a token count. Runtime abstention remains
|
|
147
152
|
a separate TypeSafe gate; positive-only query training does not teach abstention.
|
|
153
|
+
|
|
154
|
+
The repository helper uses a pinned official tokenizer revision and chat template,
|
|
155
|
+
with assistant-only loss labels, a conservative 32,768-token training ceiling
|
|
156
|
+
(the model config supports 128,000 positions), and deterministic grouped splits:
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
python scripts/prepare-query-training.py /private/node1-queries.jsonl /private/node2-queries.jsonl \
|
|
160
|
+
--output /private/new-prepared-directory --cache-dir /private/tokenizer-cache
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Install `transformers` and `jinja2` in a separate environment first. It downloads
|
|
164
|
+
tokenizer files only, never model weights. Regex secret screening is not an
|
|
165
|
+
exhaustive privacy audit; inspect quarantine references before including those rows.
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "unblock-memory",
|
|
3
3
|
"name": "Unblock Memory",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.26",
|
|
5
5
|
"description": "Indexes, retrieves, and analyzes configured workspace memory with existing QMD vectors.",
|
|
6
6
|
"kind": "memory",
|
|
7
7
|
"activation": { "onStartup": true },
|