grepmax 0.26.4 → 0.26.6
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/.claude-plugin/marketplace.json +1 -1
- package/README.md +6 -2
- package/dist/commands/doctor.js +20 -5
- package/dist/commands/mcp.js +60 -15
- package/dist/commands/search-run.js +4 -3
- package/dist/commands/similar.js +2 -2
- package/dist/eval-graph-recovery-probe.js +2 -2
- package/dist/eval-graph-sanity.js +2 -2
- package/dist/lib/analysis/surprising-connections.js +2 -2
- package/dist/lib/daemon/daemon.js +1 -0
- package/dist/lib/index/batch-processor.js +49 -22
- package/dist/lib/index/chunker.js +633 -602
- package/dist/lib/index/syncer.js +1 -1
- package/dist/lib/index/watcher.js +14 -3
- package/dist/lib/search/searcher.js +2 -2
- package/dist/lib/skeleton/skeletonizer.js +9 -1
- package/dist/lib/store/ann-config.js +34 -0
- package/dist/lib/store/vector-db.js +184 -33
- package/dist/lib/workers/orchestrator.js +5 -35
- package/dist/lib/workers/pool.js +15 -32
- package/mlx-embed-server/server.py +11 -0
- package/package.json +3 -8
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
package/dist/lib/workers/pool.js
CHANGED
|
@@ -58,26 +58,6 @@ const config_1 = require("../../config");
|
|
|
58
58
|
const embedding_generation_1 = require("../index/embedding-generation");
|
|
59
59
|
const index_config_1 = require("../index/index-config");
|
|
60
60
|
const logger_1 = require("../utils/logger");
|
|
61
|
-
function reviveBufferLike(input) {
|
|
62
|
-
if (input &&
|
|
63
|
-
typeof input === "object" &&
|
|
64
|
-
"type" in input &&
|
|
65
|
-
input.type === "Buffer" &&
|
|
66
|
-
Array.isArray(input.data)) {
|
|
67
|
-
return Buffer.from(input.data);
|
|
68
|
-
}
|
|
69
|
-
return input;
|
|
70
|
-
}
|
|
71
|
-
function reviveProcessFileResult(result) {
|
|
72
|
-
if (!result || !Array.isArray(result.vectors))
|
|
73
|
-
return result;
|
|
74
|
-
const vectors = result.vectors.map((v) => {
|
|
75
|
-
const revived = reviveBufferLike(v.colbert);
|
|
76
|
-
return revived && (Buffer.isBuffer(revived) || revived instanceof Int8Array)
|
|
77
|
-
? Object.assign(Object.assign({}, v), { colbert: revived }) : v;
|
|
78
|
-
});
|
|
79
|
-
return Object.assign(Object.assign({}, result), { vectors });
|
|
80
|
-
}
|
|
81
61
|
const TASK_TIMEOUT_MS = (() => {
|
|
82
62
|
var _a;
|
|
83
63
|
const fromEnv = Number.parseInt((_a = process.env.GMAX_WORKER_TASK_TIMEOUT_MS) !== null && _a !== void 0 ? _a : "", 10);
|
|
@@ -155,6 +135,7 @@ class ProcessWorker {
|
|
|
155
135
|
this.child = childProcess.fork(modulePath, {
|
|
156
136
|
execArgv: [...memArgs, ...execArgv],
|
|
157
137
|
env: Object.assign(Object.assign({}, process.env), embeddingEnvironment),
|
|
138
|
+
serialization: "advanced",
|
|
158
139
|
});
|
|
159
140
|
}
|
|
160
141
|
}
|
|
@@ -182,13 +163,13 @@ const MIN_KEEP_WORKERS = 1;
|
|
|
182
163
|
// Recycle a worker whose RSS has grown past this. ONNX native memory lives
|
|
183
164
|
// outside V8, so --max-old-space-size can't bound it; replacing the worker is
|
|
184
165
|
// the only reclaim. The threshold must sit ABOVE the healthy working set or
|
|
185
|
-
// the pool executes warm workers for normal behavior
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
//
|
|
166
|
+
// the pool executes warm workers for normal behavior. A post-tree-cleanup
|
|
167
|
+
// soak on 2026-08-04 measured MLX p50/p95/max at 1056/1110/1122 MB (900 tasks)
|
|
168
|
+
// and ONNX at 1030/1182/1195 MB (600 tasks). Applying the 1.25x ONNX-p95 rule
|
|
169
|
+
// and rounding to a 128 MB boundary retains 1536 MB. The old 800 default sat
|
|
170
|
+
// inside the healthy range and recycled warm workers after normal tasks; the
|
|
171
|
+
// previously proposed 1024 MLX-only override is also below measured p50. 0
|
|
172
|
+
// (or negative) disables the check.
|
|
192
173
|
const WORKER_RSS_RECYCLE_MB = (() => {
|
|
193
174
|
var _a;
|
|
194
175
|
const fromEnv = Number.parseInt((_a = process.env.GMAX_WORKER_RSS_RECYCLE_MB) !== null && _a !== void 0 ? _a : "", 10);
|
|
@@ -255,6 +236,12 @@ class WorkerPool {
|
|
|
255
236
|
.map((w) => w.child.pid)
|
|
256
237
|
.filter((pid) => pid !== undefined);
|
|
257
238
|
}
|
|
239
|
+
/** Latest worker-reported RSS, sampled after each IPC message. */
|
|
240
|
+
getWorkerMemoryStats() {
|
|
241
|
+
return this.workers.flatMap((worker) => worker.child.pid === undefined
|
|
242
|
+
? []
|
|
243
|
+
: [{ pid: worker.child.pid, rssBytes: worker.lastRssBytes }]);
|
|
244
|
+
}
|
|
258
245
|
clearTaskTimeout(task) {
|
|
259
246
|
if (task.timeout) {
|
|
260
247
|
clearTimeout(task.timeout);
|
|
@@ -378,16 +365,12 @@ class WorkerPool {
|
|
|
378
365
|
task.reject(new Error(msg.error));
|
|
379
366
|
}
|
|
380
367
|
else {
|
|
381
|
-
let result = msg.result;
|
|
382
|
-
if (task.method === "processFile") {
|
|
383
|
-
result = reviveProcessFileResult(result);
|
|
384
|
-
}
|
|
385
368
|
const elapsed = task.startTime
|
|
386
369
|
? `${Date.now() - task.startTime}ms`
|
|
387
370
|
: "?ms";
|
|
388
371
|
const filePath = (_h = (_f = (_e = task.payload) === null || _e === void 0 ? void 0 : _e.path) !== null && _f !== void 0 ? _f : (_g = task.payload) === null || _g === void 0 ? void 0 : _g.absolutePath) !== null && _h !== void 0 ? _h : "";
|
|
389
372
|
(0, logger_1.debug)("pool", `complete task=${task.id} method=${task.method} ${elapsed}${filePath ? ` file=${filePath}` : ""}`);
|
|
390
|
-
task.resolve(result);
|
|
373
|
+
task.resolve(msg.result);
|
|
391
374
|
}
|
|
392
375
|
this.completeTask(task, worker);
|
|
393
376
|
this.consecutiveRespawns = 0;
|
|
@@ -13,6 +13,7 @@ GPU operations.
|
|
|
13
13
|
import asyncio
|
|
14
14
|
import logging
|
|
15
15
|
import os
|
|
16
|
+
import re
|
|
16
17
|
import signal
|
|
17
18
|
import socket
|
|
18
19
|
import time
|
|
@@ -90,12 +91,22 @@ def is_port_in_use(port: int) -> bool:
|
|
|
90
91
|
return s.connect_ex(("127.0.0.1", port)) == 0
|
|
91
92
|
|
|
92
93
|
|
|
94
|
+
# JSON can carry lone UTF-16 surrogates (a client slicing mid-codepoint);
|
|
95
|
+
# the HF fast tokenizer rejects them with a TypeError that fails the whole
|
|
96
|
+
# batch. Replace with U+FFFD so one bad text can't take down a batch.
|
|
97
|
+
_LONE_SURROGATE = re.compile("[\ud800-\udfff]")
|
|
98
|
+
|
|
99
|
+
|
|
93
100
|
def embed_texts(texts: list[str]) -> mx.array:
|
|
94
101
|
"""Tokenize, forward pass, L2 normalize.
|
|
95
102
|
|
|
96
103
|
mlx_embeddings model already does mean pooling internally —
|
|
97
104
|
last_hidden_state is (batch, dim), not (batch, seq, dim).
|
|
98
105
|
"""
|
|
106
|
+
texts = [
|
|
107
|
+
_LONE_SURROGATE.sub("�", t) if _LONE_SURROGATE.search(t) else t
|
|
108
|
+
for t in texts
|
|
109
|
+
]
|
|
99
110
|
encoded = tokenizer(
|
|
100
111
|
texts, padding=True, truncation=True, max_length=256, return_tensors="np"
|
|
101
112
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grepmax",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.6",
|
|
4
4
|
"author": "Robert Owens <78518764+reowens@users.noreply.github.com>",
|
|
5
5
|
"homepage": "https://github.com/reowens/grepmax",
|
|
6
6
|
"bugs": {
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
"dependencies": {
|
|
80
80
|
"@clack/prompts": "^1.6.0",
|
|
81
81
|
"@huggingface/transformers": "^4.2.0",
|
|
82
|
-
"@lancedb/lancedb": "
|
|
83
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
82
|
+
"@lancedb/lancedb": "0.31.0",
|
|
83
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
84
84
|
"@parcel/watcher": "^2.5.6",
|
|
85
85
|
"apache-arrow": "^18.1.0",
|
|
86
86
|
"chalk": "^5.6.2",
|
|
@@ -99,11 +99,6 @@
|
|
|
99
99
|
"web-tree-sitter": "^0.26.9",
|
|
100
100
|
"zod": "^4.4.3"
|
|
101
101
|
},
|
|
102
|
-
"pnpm": {
|
|
103
|
-
"overrides": {
|
|
104
|
-
"mathjs": "15.2.0"
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
102
|
"devDependencies": {
|
|
108
103
|
"@biomejs/biome": "2.4.10",
|
|
109
104
|
"@types/node": "^26.0.1",
|