@zvec/zvec-grep 0.1.4 → 0.1.5
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 +127 -42
- package/README_CN.md +124 -42
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.js +490 -192
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/commands.d.ts +3 -1
- package/dist/cli/commands.js +590 -56
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/errors.js +6 -5
- package/dist/cli/errors.js.map +1 -1
- package/dist/cli/format/context.js +33 -29
- package/dist/cli/format/context.js.map +1 -1
- package/dist/cli/format/debug.js +12 -14
- package/dist/cli/format/debug.js.map +1 -1
- package/dist/cli/format/highlight.js.map +1 -1
- package/dist/cli/format/progress.js +8 -3
- package/dist/cli/format/progress.js.map +1 -1
- package/dist/cli/format/range.js.map +1 -1
- package/dist/cli/format/status.js +59 -26
- package/dist/cli/format/status.js.map +1 -1
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +170 -85
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/index.js +4 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts +2 -0
- package/dist/cli/mcp.js +354 -0
- package/dist/cli/mcp.js.map +1 -0
- package/dist/cli/types.d.ts +22 -9
- package/dist/cli/types.js.map +1 -1
- package/dist/cli/version.js +3 -1
- package/dist/cli/version.js.map +1 -1
- package/dist/engine/collection/index.js +15 -15
- package/dist/engine/collection/index.js.map +1 -1
- package/dist/engine/config.d.ts +28 -0
- package/dist/engine/config.js +195 -0
- package/dist/engine/config.js.map +1 -0
- package/dist/engine/errors/details.js.map +1 -1
- package/dist/engine/errors/index.js.map +1 -1
- package/dist/engine/extraction/code/adapter.js.map +1 -1
- package/dist/engine/extraction/code/extractor.js +87 -73
- package/dist/engine/extraction/code/extractor.js.map +1 -1
- package/dist/engine/extraction/code/families/c-family.js +12 -6
- package/dist/engine/extraction/code/families/c-family.js.map +1 -1
- package/dist/engine/extraction/code/families/js-ts.js +24 -18
- package/dist/engine/extraction/code/families/js-ts.js.map +1 -1
- package/dist/engine/extraction/code/families/metadata.js +11 -8
- package/dist/engine/extraction/code/families/metadata.js.map +1 -1
- package/dist/engine/extraction/code/families/name-field.js +5 -5
- package/dist/engine/extraction/code/families/name-field.js.map +1 -1
- package/dist/engine/extraction/code/languages/c.js +1 -4
- package/dist/engine/extraction/code/languages/c.js.map +1 -1
- package/dist/engine/extraction/code/languages/cpp.js.map +1 -1
- package/dist/engine/extraction/code/languages/go.js +1 -3
- package/dist/engine/extraction/code/languages/go.js.map +1 -1
- package/dist/engine/extraction/code/languages/java.js.map +1 -1
- package/dist/engine/extraction/code/languages/javascript.js +1 -3
- package/dist/engine/extraction/code/languages/javascript.js.map +1 -1
- package/dist/engine/extraction/code/languages/python.js +5 -6
- package/dist/engine/extraction/code/languages/python.js.map +1 -1
- package/dist/engine/extraction/code/languages/rust.js +1 -5
- package/dist/engine/extraction/code/languages/rust.js.map +1 -1
- package/dist/engine/extraction/code/languages/typescript.js.map +1 -1
- package/dist/engine/extraction/ids.js.map +1 -1
- package/dist/engine/extraction/index.js +3 -3
- package/dist/engine/extraction/index.js.map +1 -1
- package/dist/engine/extraction/markdown/extractor.js +4 -2
- package/dist/engine/extraction/markdown/extractor.js.map +1 -1
- package/dist/engine/extraction/tree-sitter/grammar.js.map +1 -1
- package/dist/engine/extraction/tree-sitter/nodes.js +2 -1
- package/dist/engine/extraction/tree-sitter/nodes.js.map +1 -1
- package/dist/engine/extraction/tree-sitter/parser.js.map +1 -1
- package/dist/engine/files/file-type.js.map +1 -1
- package/dist/engine/models/catalog.js +7 -23
- package/dist/engine/models/catalog.js.map +1 -1
- package/dist/engine/models/embeddings.js +3 -3
- package/dist/engine/models/embeddings.js.map +1 -1
- package/dist/engine/models/factory.js.map +1 -1
- package/dist/engine/models/index.d.ts +1 -1
- package/dist/engine/models/index.js.map +1 -1
- package/dist/engine/models/providers/index.d.ts +1 -1
- package/dist/engine/models/providers/index.js +1 -1
- package/dist/engine/models/providers/index.js.map +1 -1
- package/dist/engine/models/providers/llama-cpp/embedding.js +49 -18
- package/dist/engine/models/providers/llama-cpp/embedding.js.map +1 -1
- package/dist/engine/models/providers/qwen/embedding.js +16 -11
- package/dist/engine/models/providers/qwen/embedding.js.map +1 -1
- package/dist/engine/models/providers/qwen/index.d.ts +1 -1
- package/dist/engine/models/providers/qwen/index.js +1 -1
- package/dist/engine/models/providers/qwen/index.js.map +1 -1
- package/dist/engine/pipeline/indexing/index.js +46 -33
- package/dist/engine/pipeline/indexing/index.js.map +1 -1
- package/dist/engine/pipeline/indexing/root-paths.js +10 -13
- package/dist/engine/pipeline/indexing/root-paths.js.map +1 -1
- package/dist/engine/pipeline/indexing/scanner/index.js +91 -43
- package/dist/engine/pipeline/indexing/scanner/index.js.map +1 -1
- package/dist/engine/pipeline/search/index.js +68 -24
- package/dist/engine/pipeline/search/index.js.map +1 -1
- package/dist/engine/service/index.d.ts +1 -1
- package/dist/engine/service/lexical.d.ts +14 -15
- package/dist/engine/service/lexical.js +78 -119
- package/dist/engine/service/lexical.js.map +1 -1
- package/dist/engine/service/root.js +2 -2
- package/dist/engine/service/root.js.map +1 -1
- package/dist/engine/service/structure-enrichment.js +17 -11
- package/dist/engine/service/structure-enrichment.js.map +1 -1
- package/dist/engine/service/types.d.ts +39 -8
- package/dist/engine/service/zvec-grep.js +303 -160
- package/dist/engine/service/zvec-grep.js.map +1 -1
- package/dist/engine/storage/index.js.map +1 -1
- package/dist/engine/storage/zvec.js +15 -11
- package/dist/engine/storage/zvec.js.map +1 -1
- package/dist/engine/types.d.ts +14 -0
- package/dist/engine/types.js.map +1 -1
- package/dist/engine/utils/file-selection.d.ts +10 -0
- package/dist/engine/utils/file-selection.js +120 -0
- package/dist/engine/utils/file-selection.js.map +1 -0
- package/dist/engine/utils/glob.d.ts +3 -0
- package/dist/engine/utils/glob.js +116 -18
- package/dist/engine/utils/glob.js.map +1 -1
- package/dist/engine/utils/hash.js.map +1 -1
- package/dist/engine/utils/json.d.ts +6 -2
- package/dist/engine/utils/json.js +50 -12
- package/dist/engine/utils/json.js.map +1 -1
- package/dist/engine/utils/lock.js +6 -6
- package/dist/engine/utils/lock.js.map +1 -1
- package/dist/engine/utils/path.js +2 -2
- package/dist/engine/utils/path.js.map +1 -1
- package/dist/engine/utils/timing.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +34 -7
- package/skills/zvec-grep/SKILL.md +58 -48
- package/skills/zvec-grep/agents/openai.yaml +1 -1
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { dirname, join, relative, resolve } from "node:path";
|
|
4
|
+
import { globalConfigPath, readGlobalConfig, } from "../config.js";
|
|
4
5
|
import { Collection, CollectionRegistry, isCollectionIndexed, } from "../collection/index.js";
|
|
5
6
|
import { collectionDetail, detail, EngineError, errorDetails, } from "../errors/index.js";
|
|
6
7
|
import { createEmbeddingModel, createEmbeddingModelFromReference, } from "../models/index.js";
|
|
7
8
|
import { ANONYMOUS_COLLECTION_NAME, anonymousCollectionPath, anonymousHome, anonymousIndexLocation, resolveZvecGrepRoot, } from "./root.js";
|
|
8
|
-
import {
|
|
9
|
-
import { defaultHome } from "../utils/path.js";
|
|
10
|
-
import { acquireReadWriteLock, assertNoWriteLock } from "../utils/lock.js";
|
|
9
|
+
import { runRgSearch } from "./lexical.js";
|
|
10
|
+
import { defaultHome, normalizePath } from "../utils/path.js";
|
|
11
|
+
import { acquireReadWriteLock, assertNoWriteLock, } from "../utils/lock.js";
|
|
11
12
|
import { TimingCollector } from "../utils/timing.js";
|
|
12
13
|
import { enrichLexicalItemsWithStructure } from "./structure-enrichment.js";
|
|
13
14
|
const DEFAULT_CONTEXT_LIMIT = 10;
|
|
14
15
|
const DEFAULT_CONTEXT_TOTAL_LIMIT = 30;
|
|
15
16
|
const DEFAULT_LOCAL_EMBEDDING = "local/embeddinggemma-300m";
|
|
17
|
+
const MAX_RECOVERED_EMBEDDING_MODELS = 4;
|
|
16
18
|
export async function createZvecGrep(options = {}) {
|
|
17
19
|
return new ZvecGrepService(options);
|
|
18
20
|
}
|
|
@@ -21,14 +23,14 @@ class ZvecGrepService {
|
|
|
21
23
|
root;
|
|
22
24
|
collections;
|
|
23
25
|
embeddingModel;
|
|
24
|
-
embeddingModelExplicit;
|
|
25
26
|
recoveredEmbeddingModels = new Map();
|
|
27
|
+
retiredEmbeddingModels = new Set();
|
|
28
|
+
activeEmbeddingModelOperations = 0;
|
|
26
29
|
closed = false;
|
|
27
30
|
constructor(options) {
|
|
28
31
|
this.options = options;
|
|
29
32
|
this.root = resolveZvecGrepRoot(options.root);
|
|
30
|
-
this.
|
|
31
|
-
this.embeddingModel = resolveEmbeddingModel(options, this.root);
|
|
33
|
+
this.embeddingModel = options.embeddingModel;
|
|
32
34
|
this.collections = {
|
|
33
35
|
list: () => this.listCollections(),
|
|
34
36
|
info: (name) => this.collectionInfo(name),
|
|
@@ -41,7 +43,7 @@ class ZvecGrepService {
|
|
|
41
43
|
this.ensureOpen();
|
|
42
44
|
const root = resolveZvecGrepRoot(options.root ?? this.root);
|
|
43
45
|
const location = anonymousIndexLocation(root);
|
|
44
|
-
return await withHomeWriteLock(location.home, options.rebuild ? "index.rebuild" : "index", async () => {
|
|
46
|
+
return await this.withEmbeddingModelOperation(() => withHomeWriteLock(location.home, options.rebuild ? "index.rebuild" : "index", async () => {
|
|
45
47
|
const existing = readCollectionInfo(location.home, ANONYMOUS_COLLECTION_NAME);
|
|
46
48
|
const embeddingModel = this.embeddingModelForIndex(existing, location.home, "index");
|
|
47
49
|
const registry = new CollectionRegistry(location.home, embeddingModel);
|
|
@@ -50,13 +52,23 @@ class ZvecGrepService {
|
|
|
50
52
|
resetPaths: options.resetPaths === true,
|
|
51
53
|
includePaths: options.includePaths,
|
|
52
54
|
excludePaths: options.excludePaths,
|
|
55
|
+
globs: options.globs,
|
|
56
|
+
insensitiveGlobs: options.insensitiveGlobs,
|
|
57
|
+
fileTypes: options.fileTypes,
|
|
58
|
+
excludedFileTypes: options.excludedFileTypes,
|
|
59
|
+
hidden: options.hidden,
|
|
60
|
+
noIgnore: options.noIgnore,
|
|
61
|
+
ignoreFiles: options.ignoreFiles,
|
|
62
|
+
maxDepth: options.maxDepth,
|
|
63
|
+
maxFileSizeBytes: options.maxFileSizeBytes,
|
|
64
|
+
follow: options.follow,
|
|
53
65
|
});
|
|
54
66
|
if (options.rebuild) {
|
|
55
67
|
registry.remove(ANONYMOUS_COLLECTION_NAME);
|
|
56
68
|
}
|
|
57
69
|
const existingAfterRebuild = registry.get(ANONYMOUS_COLLECTION_NAME);
|
|
58
70
|
if (isCollectionIndexed(existingAfterRebuild)) {
|
|
59
|
-
assertCollectionEmbeddingMatchesCurrentModel(existingAfterRebuild, embeddingModel, "zg
|
|
71
|
+
assertCollectionEmbeddingMatchesCurrentModel(existingAfterRebuild, embeddingModel, "zg index --rebuild");
|
|
60
72
|
}
|
|
61
73
|
registry.prepareIndex(ANONYMOUS_COLLECTION_NAME, rootPaths, anonymousCollectionPath(root));
|
|
62
74
|
return await registry.open(ANONYMOUS_COLLECTION_NAME).index({
|
|
@@ -68,6 +80,23 @@ class ZvecGrepService {
|
|
|
68
80
|
finally {
|
|
69
81
|
registry.close();
|
|
70
82
|
}
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
async dropIndex(options = {}) {
|
|
86
|
+
this.ensureOpen();
|
|
87
|
+
const root = resolveZvecGrepRoot(options.root ?? this.root);
|
|
88
|
+
const location = anonymousIndexLocation(root);
|
|
89
|
+
if (!readCollectionInfo(location.home, ANONYMOUS_COLLECTION_NAME)) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return await withHomeWriteLock(location.home, "index.drop", async () => {
|
|
93
|
+
const registry = new CollectionRegistry(location.home, undefined);
|
|
94
|
+
try {
|
|
95
|
+
return registry.remove(ANONYMOUS_COLLECTION_NAME);
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
registry.close();
|
|
99
|
+
}
|
|
71
100
|
});
|
|
72
101
|
}
|
|
73
102
|
async disableIndex(options = {}) {
|
|
@@ -87,9 +116,11 @@ class ZvecGrepService {
|
|
|
87
116
|
}
|
|
88
117
|
async context(options) {
|
|
89
118
|
this.ensureOpen();
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
119
|
+
return await this.withEmbeddingModelOperation(async () => {
|
|
120
|
+
const timings = new TimingCollector();
|
|
121
|
+
const result = await timings.time("total", () => this.contextWithTimings(options, timings));
|
|
122
|
+
return withContextTimings(result, timings);
|
|
123
|
+
});
|
|
93
124
|
}
|
|
94
125
|
async contextWithTimings(options, timings) {
|
|
95
126
|
const request = normalizeContextRequest(options);
|
|
@@ -131,16 +162,16 @@ class ZvecGrepService {
|
|
|
131
162
|
home: location.home,
|
|
132
163
|
indexPath: location.collectionPath,
|
|
133
164
|
source: "unindexed",
|
|
134
|
-
suggestion: "zg
|
|
165
|
+
suggestion: "zg index or zg query --rg",
|
|
135
166
|
};
|
|
136
167
|
}
|
|
137
168
|
return await withHomeReadLock(nearest.location.home, "info", async () => {
|
|
138
169
|
const registry = new CollectionRegistry(nearest.location.home, undefined, true);
|
|
139
170
|
try {
|
|
140
171
|
const collection = registry.get(ANONYMOUS_COLLECTION_NAME);
|
|
141
|
-
const indexed = collection !== null
|
|
142
|
-
|
|
143
|
-
|
|
172
|
+
const indexed = collection !== null &&
|
|
173
|
+
collection.indexPolicy !== "disabled" &&
|
|
174
|
+
isCollectionIndexed(collection);
|
|
144
175
|
return {
|
|
145
176
|
root: nearest.location.root,
|
|
146
177
|
indexed,
|
|
@@ -149,7 +180,9 @@ class ZvecGrepService {
|
|
|
149
180
|
indexPath: nearest.location.collectionPath,
|
|
150
181
|
source: indexed ? "index" : "unindexed",
|
|
151
182
|
collection: collection ?? undefined,
|
|
152
|
-
status: indexed
|
|
183
|
+
status: indexed
|
|
184
|
+
? await registry.status(ANONYMOUS_COLLECTION_NAME)
|
|
185
|
+
: null,
|
|
153
186
|
suggestion: anonymousInfoSuggestion(collection),
|
|
154
187
|
};
|
|
155
188
|
}
|
|
@@ -162,8 +195,10 @@ class ZvecGrepService {
|
|
|
162
195
|
const models = new Set([
|
|
163
196
|
...(this.embeddingModel ? [this.embeddingModel] : []),
|
|
164
197
|
...this.recoveredEmbeddingModels.values(),
|
|
198
|
+
...this.retiredEmbeddingModels,
|
|
165
199
|
]);
|
|
166
200
|
this.recoveredEmbeddingModels.clear();
|
|
201
|
+
this.retiredEmbeddingModels.clear();
|
|
167
202
|
for (const model of models) {
|
|
168
203
|
await model.dispose();
|
|
169
204
|
}
|
|
@@ -208,7 +243,7 @@ class ZvecGrepService {
|
|
|
208
243
|
async indexCollection(name, paths, options = {}) {
|
|
209
244
|
this.ensureOpen();
|
|
210
245
|
const home = serviceHome(this.options);
|
|
211
|
-
return await withHomeWriteLock(home, options.rebuild ? "collections.index.rebuild" : "collections.index", async () => {
|
|
246
|
+
return await this.withEmbeddingModelOperation(() => withHomeWriteLock(home, options.rebuild ? "collections.index.rebuild" : "collections.index", async () => {
|
|
212
247
|
const existing = readCollectionInfo(home, name);
|
|
213
248
|
const embeddingModel = this.embeddingModelForIndex(existing, home, "collections.index");
|
|
214
249
|
const registry = this.createRegistry(false, embeddingModel);
|
|
@@ -222,13 +257,23 @@ class ZvecGrepService {
|
|
|
222
257
|
resetPaths: options.resetPaths === true,
|
|
223
258
|
includePaths: options.includePaths,
|
|
224
259
|
excludePaths: options.excludePaths,
|
|
260
|
+
globs: options.globs,
|
|
261
|
+
insensitiveGlobs: options.insensitiveGlobs,
|
|
262
|
+
fileTypes: options.fileTypes,
|
|
263
|
+
excludedFileTypes: options.excludedFileTypes,
|
|
264
|
+
hidden: options.hidden,
|
|
265
|
+
noIgnore: options.noIgnore,
|
|
266
|
+
ignoreFiles: options.ignoreFiles,
|
|
267
|
+
maxDepth: options.maxDepth,
|
|
268
|
+
maxFileSizeBytes: options.maxFileSizeBytes,
|
|
269
|
+
follow: options.follow,
|
|
225
270
|
});
|
|
226
271
|
if (options.rebuild) {
|
|
227
272
|
registry.remove(name);
|
|
228
273
|
}
|
|
229
274
|
const existingAfterRebuild = registry.get(name);
|
|
230
275
|
if (isCollectionIndexed(existingAfterRebuild)) {
|
|
231
|
-
assertCollectionEmbeddingMatchesCurrentModel(existingAfterRebuild, embeddingModel, "zg
|
|
276
|
+
assertCollectionEmbeddingMatchesCurrentModel(existingAfterRebuild, embeddingModel, "zg collections index <name> --rebuild");
|
|
232
277
|
}
|
|
233
278
|
registry.prepareIndex(name, rootPaths);
|
|
234
279
|
return await registry.open(name).index({
|
|
@@ -239,7 +284,7 @@ class ZvecGrepService {
|
|
|
239
284
|
finally {
|
|
240
285
|
registry.close();
|
|
241
286
|
}
|
|
242
|
-
});
|
|
287
|
+
}));
|
|
243
288
|
}
|
|
244
289
|
async removeCollection(name) {
|
|
245
290
|
this.ensureOpen();
|
|
@@ -305,7 +350,7 @@ class ZvecGrepService {
|
|
|
305
350
|
return;
|
|
306
351
|
}
|
|
307
352
|
const embeddingModel = this.embeddingModelForIndex(existing, location.home, "context.refresh");
|
|
308
|
-
assertCollectionEmbeddingMatchesCurrentModel(existing, embeddingModel, "zg
|
|
353
|
+
assertCollectionEmbeddingMatchesCurrentModel(existing, embeddingModel, "zg index --rebuild");
|
|
309
354
|
const registry = new CollectionRegistry(location.home, embeddingModel);
|
|
310
355
|
try {
|
|
311
356
|
const result = await registry.open(ANONYMOUS_COLLECTION_NAME).index({
|
|
@@ -332,7 +377,8 @@ class ZvecGrepService {
|
|
|
332
377
|
});
|
|
333
378
|
}
|
|
334
379
|
const collection = this.openCollectionForSearch(info, request, registry.home);
|
|
335
|
-
const root = collection.info.rootPaths[0]?.absolutePath ??
|
|
380
|
+
const root = collection.info.rootPaths[0]?.absolutePath ??
|
|
381
|
+
resolveZvecGrepRoot(options.root ?? this.root);
|
|
336
382
|
try {
|
|
337
383
|
return await this.contextFromCollection({
|
|
338
384
|
root,
|
|
@@ -341,7 +387,6 @@ class ZvecGrepService {
|
|
|
341
387
|
anonymous: false,
|
|
342
388
|
options: {
|
|
343
389
|
...options,
|
|
344
|
-
fallback: "disabled",
|
|
345
390
|
},
|
|
346
391
|
timings,
|
|
347
392
|
});
|
|
@@ -357,8 +402,11 @@ class ZvecGrepService {
|
|
|
357
402
|
}
|
|
358
403
|
async contextFromCollection(input) {
|
|
359
404
|
const searches = [];
|
|
360
|
-
const
|
|
361
|
-
|
|
405
|
+
const groups = input.options.fuse
|
|
406
|
+
? [{ routes: input.request.routes }]
|
|
407
|
+
: input.request.groups;
|
|
408
|
+
const limit = contextGroupLimit(input.options.limit, groups.length);
|
|
409
|
+
for (const group of groups) {
|
|
362
410
|
const search = await input.collection.searchPlan({
|
|
363
411
|
routes: group.routes,
|
|
364
412
|
limit,
|
|
@@ -367,6 +415,10 @@ class ZvecGrepService {
|
|
|
367
415
|
symbolTypes: input.options.symbolTypes,
|
|
368
416
|
includePaths: input.options.includePaths,
|
|
369
417
|
excludePaths: input.options.excludePaths,
|
|
418
|
+
globs: input.options.globs,
|
|
419
|
+
insensitiveGlobs: input.options.insensitiveGlobs,
|
|
420
|
+
fileTypes: input.options.fileTypes,
|
|
421
|
+
excludedFileTypes: input.options.excludedFileTypes,
|
|
370
422
|
modifiedAfter: input.options.modifiedAfter,
|
|
371
423
|
modifiedBefore: input.options.modifiedBefore,
|
|
372
424
|
});
|
|
@@ -395,45 +447,26 @@ class ZvecGrepService {
|
|
|
395
447
|
},
|
|
396
448
|
};
|
|
397
449
|
}
|
|
398
|
-
async contextFromLexicalFallback(root, request, options, timings) {
|
|
399
|
-
const fallbackResults = [];
|
|
400
|
-
const limit = contextGroupLimit(options.limit, request.groups.length);
|
|
401
|
-
for (const query of request.fallbackQueries) {
|
|
402
|
-
fallbackResults.push(await timings.time("fallback_search", () => runLexicalFallback({
|
|
403
|
-
root,
|
|
404
|
-
query,
|
|
405
|
-
limit,
|
|
406
|
-
includePaths: options.includePaths,
|
|
407
|
-
excludePaths: options.excludePaths,
|
|
408
|
-
modifiedAfter: options.modifiedAfter,
|
|
409
|
-
modifiedBefore: options.modifiedBefore,
|
|
410
|
-
rgOptions: options.rgOptions,
|
|
411
|
-
})));
|
|
412
|
-
}
|
|
413
|
-
const items = dedupeAndRerankContextItems(fallbackResults.flatMap((fallback) => fallback.items));
|
|
414
|
-
const diagnostics = mergeFallbackDiagnostics(fallbackResults);
|
|
415
|
-
return {
|
|
416
|
-
query: request.displayQuery,
|
|
417
|
-
root,
|
|
418
|
-
source: "lexical_fallback",
|
|
419
|
-
coverage: diagnostics.truncated ? "lexical_truncated" : "lexical_exhaustive",
|
|
420
|
-
items,
|
|
421
|
-
diagnostics: {
|
|
422
|
-
emptyReason: items.length === 0 ? "no_matches" : undefined,
|
|
423
|
-
fallback: diagnostics,
|
|
424
|
-
},
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
450
|
async contextFromRg(root, request, options, timings) {
|
|
428
451
|
let rgResult;
|
|
429
452
|
try {
|
|
430
453
|
rgResult = await timings.time("rg_search", () => runRgSearch({
|
|
431
454
|
root,
|
|
432
|
-
patterns: request.
|
|
455
|
+
patterns: request.rgPatterns,
|
|
433
456
|
paths: options.rgPaths,
|
|
434
457
|
limit: options.limit,
|
|
435
458
|
includePaths: options.includePaths,
|
|
436
459
|
excludePaths: options.excludePaths,
|
|
460
|
+
globs: options.globs,
|
|
461
|
+
insensitiveGlobs: options.insensitiveGlobs,
|
|
462
|
+
fileTypes: options.fileTypes,
|
|
463
|
+
excludedFileTypes: options.excludedFileTypes,
|
|
464
|
+
hidden: options.hidden,
|
|
465
|
+
noIgnore: options.noIgnore,
|
|
466
|
+
ignoreFiles: options.ignoreFiles,
|
|
467
|
+
maxDepth: options.maxDepth,
|
|
468
|
+
maxFileSizeBytes: options.maxFileSizeBytes,
|
|
469
|
+
follow: options.follow,
|
|
437
470
|
modifiedAfter: options.modifiedAfter,
|
|
438
471
|
modifiedBefore: options.modifiedBefore,
|
|
439
472
|
rgOptions: options.rgOptions,
|
|
@@ -442,27 +475,24 @@ class ZvecGrepService {
|
|
|
442
475
|
catch (cause) {
|
|
443
476
|
throw new EngineError("Search failed", {
|
|
444
477
|
code: "ZVEC_GREP.ENGINE.SEARCH.FAILED",
|
|
445
|
-
context: errorDetails([
|
|
446
|
-
detail("source", "rg"),
|
|
447
|
-
detail("root", root),
|
|
448
|
-
]),
|
|
478
|
+
context: errorDetails([detail("source", "rg"), detail("root", root)]),
|
|
449
479
|
cause,
|
|
450
480
|
});
|
|
451
481
|
}
|
|
452
482
|
const structuralEnrichment = await timings.time("structure_enrichment", () => enrichLexicalItemsWithStructure(root, rgResult.items));
|
|
453
483
|
const items = dedupeAndRerankContextItems(structuralEnrichment.items);
|
|
454
|
-
const emptyReason = items.length === 0
|
|
455
|
-
? rgEmptyReason(rgResult.diagnostics)
|
|
456
|
-
: undefined;
|
|
484
|
+
const emptyReason = items.length === 0 ? rgEmptyReason(rgResult.diagnostics) : undefined;
|
|
457
485
|
return {
|
|
458
486
|
query: request.displayQuery,
|
|
459
487
|
root,
|
|
460
488
|
source: "rg",
|
|
461
|
-
coverage: rgResult.diagnostics.truncated
|
|
489
|
+
coverage: rgResult.diagnostics.truncated
|
|
490
|
+
? "rg_truncated"
|
|
491
|
+
: "rg_exhaustive",
|
|
462
492
|
items,
|
|
463
493
|
diagnostics: {
|
|
464
494
|
emptyReason,
|
|
465
|
-
|
|
495
|
+
rg: rgResult.diagnostics,
|
|
466
496
|
structure: structuralEnrichment.diagnostics,
|
|
467
497
|
},
|
|
468
498
|
};
|
|
@@ -483,31 +513,96 @@ class ZvecGrepService {
|
|
|
483
513
|
return this.recoverEmbeddingModel(schema, registryHome);
|
|
484
514
|
}
|
|
485
515
|
embeddingModelForIndex(existing, registryHome, operation) {
|
|
486
|
-
if (isCollectionIndexed(existing) &&
|
|
516
|
+
if (isCollectionIndexed(existing) &&
|
|
517
|
+
!this.embeddingModel &&
|
|
518
|
+
!this.options.embedding) {
|
|
487
519
|
return this.recoverEmbeddingModel(existing.embedding, registryHome);
|
|
488
520
|
}
|
|
489
|
-
|
|
521
|
+
const reference = this.options.embedding ??
|
|
522
|
+
(this.options.defaultEmbedding === true
|
|
523
|
+
? DEFAULT_LOCAL_EMBEDDING
|
|
524
|
+
: undefined);
|
|
525
|
+
return (this.embeddingModel ??
|
|
526
|
+
(reference
|
|
527
|
+
? this.embeddingModelFromReference(reference, registryHome)
|
|
528
|
+
: undefined) ??
|
|
529
|
+
this.configuredEmbeddingModel(registryHome) ??
|
|
530
|
+
this.requireEmbeddingModel(operation));
|
|
490
531
|
}
|
|
491
532
|
recoverEmbeddingModel(schema, registryHome) {
|
|
492
|
-
const
|
|
533
|
+
const config = readGlobalConfig();
|
|
534
|
+
const options = providerOptions(this.options, this.root, registryHome, schema.provider, config);
|
|
535
|
+
const key = `${schema.provider}/${schema.model}/${providerOptionsFingerprint(options)}`;
|
|
536
|
+
return this.cachedEmbeddingModel(key, () => createEmbeddingModel({
|
|
537
|
+
provider: schema.provider,
|
|
538
|
+
model: schema.model,
|
|
539
|
+
}, options));
|
|
540
|
+
}
|
|
541
|
+
configuredEmbeddingModel(registryHome) {
|
|
542
|
+
const config = readGlobalConfig();
|
|
543
|
+
const reference = config.defaults?.embedding;
|
|
544
|
+
if (!reference) {
|
|
545
|
+
return undefined;
|
|
546
|
+
}
|
|
547
|
+
return this.embeddingModelFromReference(reference, registryHome, config);
|
|
548
|
+
}
|
|
549
|
+
embeddingModelFromReference(reference, registryHome, config = readGlobalConfig()) {
|
|
550
|
+
const provider = providerFromReference(reference);
|
|
551
|
+
const options = providerOptions(this.options, this.root, registryHome, provider, config);
|
|
552
|
+
const key = `configured/${reference}/${providerOptionsFingerprint(options)}`;
|
|
553
|
+
return this.cachedEmbeddingModel(key, () => createEmbeddingModelFromReference(reference, options));
|
|
554
|
+
}
|
|
555
|
+
cachedEmbeddingModel(key, create) {
|
|
493
556
|
const cached = this.recoveredEmbeddingModels.get(key);
|
|
494
557
|
if (cached) {
|
|
558
|
+
this.recoveredEmbeddingModels.delete(key);
|
|
559
|
+
this.recoveredEmbeddingModels.set(key, cached);
|
|
495
560
|
return cached;
|
|
496
561
|
}
|
|
497
|
-
const model =
|
|
498
|
-
provider: schema.provider,
|
|
499
|
-
model: schema.model,
|
|
500
|
-
}, providerOptions(this.options, this.root, registryHome));
|
|
562
|
+
const model = create();
|
|
501
563
|
this.recoveredEmbeddingModels.set(key, model);
|
|
564
|
+
this.trimRecoveredEmbeddingModels();
|
|
502
565
|
return model;
|
|
503
566
|
}
|
|
567
|
+
trimRecoveredEmbeddingModels() {
|
|
568
|
+
while (this.recoveredEmbeddingModels.size > MAX_RECOVERED_EMBEDDING_MODELS) {
|
|
569
|
+
const oldestKey = this.recoveredEmbeddingModels.keys().next().value;
|
|
570
|
+
if (oldestKey === undefined) {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const model = this.recoveredEmbeddingModels.get(oldestKey);
|
|
574
|
+
this.recoveredEmbeddingModels.delete(oldestKey);
|
|
575
|
+
if (model) {
|
|
576
|
+
this.retiredEmbeddingModels.add(model);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
async withEmbeddingModelOperation(operation) {
|
|
581
|
+
this.activeEmbeddingModelOperations += 1;
|
|
582
|
+
try {
|
|
583
|
+
return await operation();
|
|
584
|
+
}
|
|
585
|
+
finally {
|
|
586
|
+
this.activeEmbeddingModelOperations -= 1;
|
|
587
|
+
if (this.activeEmbeddingModelOperations === 0) {
|
|
588
|
+
await this.disposeRetiredEmbeddingModels();
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
async disposeRetiredEmbeddingModels() {
|
|
593
|
+
const models = [...this.retiredEmbeddingModels];
|
|
594
|
+
this.retiredEmbeddingModels.clear();
|
|
595
|
+
for (const model of models) {
|
|
596
|
+
await model.dispose();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
504
599
|
requireEmbeddingModel(operation) {
|
|
505
600
|
if (!this.embeddingModel) {
|
|
506
601
|
throw new EngineError("zvec-grep operation requires an embedding model", {
|
|
507
602
|
code: "ZVEC_GREP.ENGINE.SERVICE.EMBEDDING_MODEL_REQUIRED",
|
|
508
603
|
context: errorDetails([
|
|
509
604
|
detail("operation", operation),
|
|
510
|
-
detail("hint",
|
|
605
|
+
detail("hint", `Pass "--embedding <model>", set ZVEC_GREP_EMBEDDING, or configure defaults.embedding in ${globalConfigPath()}. Existing indexes can run "zg index" without --embedding to reuse the stored schema.`),
|
|
511
606
|
detail("examples", "local/embeddinggemma-300m, qwen/text-embedding-v4"),
|
|
512
607
|
]),
|
|
513
608
|
});
|
|
@@ -551,11 +646,11 @@ async function collectionNeedsRefresh(home, collectionName) {
|
|
|
551
646
|
}
|
|
552
647
|
}
|
|
553
648
|
function collectionIndexStatusNeedsRefresh(status) {
|
|
554
|
-
return status.filesAdded > 0
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
649
|
+
return (status.filesAdded > 0 ||
|
|
650
|
+
status.filesModified > 0 ||
|
|
651
|
+
status.filesDeleted > 0 ||
|
|
652
|
+
status.filesPending > 0 ||
|
|
653
|
+
status.filesFailed > 0);
|
|
559
654
|
}
|
|
560
655
|
function acquireHomeLock(home, mode, operation) {
|
|
561
656
|
return acquireReadWriteLock(homeLockPath(home), mode, { operation });
|
|
@@ -581,9 +676,10 @@ function serviceHome(options) {
|
|
|
581
676
|
return options.home ?? defaultHome();
|
|
582
677
|
}
|
|
583
678
|
function rgEmptyReason(diagnostics) {
|
|
584
|
-
return diagnostics.missingPaths &&
|
|
585
|
-
|
|
586
|
-
|
|
679
|
+
return diagnostics.missingPaths &&
|
|
680
|
+
diagnostics.missingPaths.length > 0 &&
|
|
681
|
+
diagnostics.searchedPaths &&
|
|
682
|
+
diagnostics.searchedPaths.length === 0
|
|
587
683
|
? "no_searchable_files"
|
|
588
684
|
: "no_matches";
|
|
589
685
|
}
|
|
@@ -617,13 +713,13 @@ function findNearestAnonymousCollection(start) {
|
|
|
617
713
|
}
|
|
618
714
|
function anonymousInfoSuggestion(collection) {
|
|
619
715
|
if (!collection) {
|
|
620
|
-
return "zg
|
|
716
|
+
return "zg index or zg query --rg";
|
|
621
717
|
}
|
|
622
718
|
if (collection.indexPolicy === "disabled") {
|
|
623
|
-
return "zg --rg";
|
|
719
|
+
return "zg query --rg";
|
|
624
720
|
}
|
|
625
721
|
if (!isCollectionIndexed(collection)) {
|
|
626
|
-
return "zg
|
|
722
|
+
return "zg index";
|
|
627
723
|
}
|
|
628
724
|
return undefined;
|
|
629
725
|
}
|
|
@@ -634,11 +730,11 @@ function anonymousIndexMissingError(root, policy) {
|
|
|
634
730
|
detail("root", root),
|
|
635
731
|
detail("policy", policy),
|
|
636
732
|
detail("hint", policy === "undecided"
|
|
637
|
-
? "Ask the user whether to build an index with zg
|
|
638
|
-
: "Run zg
|
|
733
|
+
? "Ask the user whether to build an index with zg index, or use zg query --rg for no-index search."
|
|
734
|
+
: "Run zg index to build the enabled workspace index, or use zg query --rg for no-index search."),
|
|
639
735
|
detail("agent_prompt", policy === "undecided"
|
|
640
|
-
? "Ask the user whether this workspace should be indexed. If yes, run zg
|
|
641
|
-
: "This workspace is marked index-enabled but has no built index. Ask before running zg
|
|
736
|
+
? "Ask the user whether this workspace should be indexed. If yes, run zg index --embedding <model> with appropriate -g/--glob and -t/--type filters; otherwise use zg query --rg for immediate no-index search."
|
|
737
|
+
: "This workspace is marked index-enabled but has no built index. Ask before running zg index if an embedding model or cost is involved; otherwise use zg query --rg for immediate no-index search."),
|
|
642
738
|
]),
|
|
643
739
|
});
|
|
644
740
|
}
|
|
@@ -648,23 +744,66 @@ function anonymousIndexDisabledError(root) {
|
|
|
648
744
|
context: errorDetails([
|
|
649
745
|
detail("root", root),
|
|
650
746
|
detail("policy", "disabled"),
|
|
651
|
-
detail("hint", "Use zg --rg for no-index search. Run zg
|
|
747
|
+
detail("hint", "Use zg query --rg for no-index search. Run zg index only if the user explicitly decides to index this workspace."),
|
|
652
748
|
detail("agent_action", "do_not_build_index"),
|
|
653
749
|
]),
|
|
654
750
|
});
|
|
655
751
|
}
|
|
656
752
|
function resolveIndexRootPaths(existing, requested, fallbackRoot, options) {
|
|
657
753
|
let rootPaths = requested
|
|
658
|
-
|
|
659
|
-
?? [fallbackRoot];
|
|
754
|
+
? inheritRequestedRootPathSettings(requested, existing?.rootPaths)
|
|
755
|
+
: (existing?.rootPaths ?? [fallbackRoot]);
|
|
660
756
|
if (options.resetPaths) {
|
|
661
757
|
rootPaths = rootPaths.map(resetRootPathFilters);
|
|
662
758
|
}
|
|
663
|
-
if (options.includePaths !== undefined ||
|
|
664
|
-
|
|
759
|
+
if (options.includePaths !== undefined ||
|
|
760
|
+
options.excludePaths !== undefined ||
|
|
761
|
+
options.globs !== undefined ||
|
|
762
|
+
options.insensitiveGlobs !== undefined ||
|
|
763
|
+
options.fileTypes !== undefined ||
|
|
764
|
+
options.excludedFileTypes !== undefined ||
|
|
765
|
+
options.hidden !== undefined ||
|
|
766
|
+
options.noIgnore !== undefined ||
|
|
767
|
+
options.ignoreFiles !== undefined ||
|
|
768
|
+
options.maxDepth !== undefined ||
|
|
769
|
+
options.maxFileSizeBytes !== undefined ||
|
|
770
|
+
options.follow !== undefined) {
|
|
771
|
+
rootPaths = rootPaths.map((rootPath) => applyRootPathOverrides(rootPath, options));
|
|
665
772
|
}
|
|
666
773
|
return rootPaths;
|
|
667
774
|
}
|
|
775
|
+
function inheritRequestedRootPathSettings(requested, existing) {
|
|
776
|
+
if (!existing?.length) {
|
|
777
|
+
return requested;
|
|
778
|
+
}
|
|
779
|
+
return requested.map((rootPath) => {
|
|
780
|
+
const absolutePath = normalizePath(typeof rootPath === "string" ? rootPath : rootPath.absolutePath);
|
|
781
|
+
const inherited = existing.find((candidate) => normalizePath(candidate.absolutePath) === absolutePath);
|
|
782
|
+
if (!inherited) {
|
|
783
|
+
return rootPath;
|
|
784
|
+
}
|
|
785
|
+
if (typeof rootPath === "string") {
|
|
786
|
+
return { ...inherited, absolutePath };
|
|
787
|
+
}
|
|
788
|
+
return {
|
|
789
|
+
...inherited,
|
|
790
|
+
...rootPath,
|
|
791
|
+
absolutePath,
|
|
792
|
+
include: rootPath.include ?? inherited.include,
|
|
793
|
+
exclude: rootPath.exclude ?? inherited.exclude,
|
|
794
|
+
globs: rootPath.globs ?? inherited.globs,
|
|
795
|
+
insensitiveGlobs: rootPath.insensitiveGlobs ?? inherited.insensitiveGlobs,
|
|
796
|
+
fileTypes: rootPath.fileTypes ?? inherited.fileTypes,
|
|
797
|
+
excludedFileTypes: rootPath.excludedFileTypes ?? inherited.excludedFileTypes,
|
|
798
|
+
hidden: rootPath.hidden ?? inherited.hidden,
|
|
799
|
+
noIgnore: rootPath.noIgnore ?? inherited.noIgnore,
|
|
800
|
+
ignoreFiles: rootPath.ignoreFiles ?? inherited.ignoreFiles,
|
|
801
|
+
maxDepth: rootPath.maxDepth ?? inherited.maxDepth,
|
|
802
|
+
maxFileSizeBytes: rootPath.maxFileSizeBytes ?? inherited.maxFileSizeBytes,
|
|
803
|
+
follow: rootPath.follow ?? inherited.follow,
|
|
804
|
+
};
|
|
805
|
+
});
|
|
806
|
+
}
|
|
668
807
|
function resetRootPathFilters(rootPath) {
|
|
669
808
|
if (typeof rootPath === "string") {
|
|
670
809
|
return rootPath;
|
|
@@ -674,7 +813,7 @@ function resetRootPathFilters(rootPath) {
|
|
|
674
813
|
recursive: rootPath.recursive,
|
|
675
814
|
};
|
|
676
815
|
}
|
|
677
|
-
function
|
|
816
|
+
function applyRootPathOverrides(rootPath, options) {
|
|
678
817
|
const normalized = typeof rootPath === "string"
|
|
679
818
|
? {
|
|
680
819
|
absolutePath: rootPath,
|
|
@@ -683,26 +822,20 @@ function applyRootPathFilterOverrides(rootPath, include, exclude) {
|
|
|
683
822
|
: rootPath;
|
|
684
823
|
return {
|
|
685
824
|
...normalized,
|
|
686
|
-
include:
|
|
687
|
-
exclude:
|
|
825
|
+
include: options.includePaths ?? normalized.include,
|
|
826
|
+
exclude: options.excludePaths ?? normalized.exclude,
|
|
827
|
+
globs: options.globs ?? normalized.globs,
|
|
828
|
+
insensitiveGlobs: options.insensitiveGlobs ?? normalized.insensitiveGlobs,
|
|
829
|
+
fileTypes: options.fileTypes ?? normalized.fileTypes,
|
|
830
|
+
excludedFileTypes: options.excludedFileTypes ?? normalized.excludedFileTypes,
|
|
831
|
+
hidden: options.hidden ?? normalized.hidden,
|
|
832
|
+
noIgnore: options.noIgnore ?? normalized.noIgnore,
|
|
833
|
+
ignoreFiles: options.ignoreFiles ?? normalized.ignoreFiles,
|
|
834
|
+
maxDepth: options.maxDepth ?? normalized.maxDepth,
|
|
835
|
+
maxFileSizeBytes: options.maxFileSizeBytes ?? normalized.maxFileSizeBytes,
|
|
836
|
+
follow: options.follow ?? normalized.follow,
|
|
688
837
|
};
|
|
689
838
|
}
|
|
690
|
-
function resolveEmbeddingModel(options, root) {
|
|
691
|
-
if (options.embeddingModel) {
|
|
692
|
-
return options.embeddingModel;
|
|
693
|
-
}
|
|
694
|
-
if (options.embedding) {
|
|
695
|
-
return createEmbeddingModelFromReference(options.embedding, providerOptions(options, root));
|
|
696
|
-
}
|
|
697
|
-
if (options.defaultEmbedding === true) {
|
|
698
|
-
return createEmbeddingModelFromReference(DEFAULT_LOCAL_EMBEDDING, providerOptions(options, root));
|
|
699
|
-
}
|
|
700
|
-
return undefined;
|
|
701
|
-
}
|
|
702
|
-
function hasExplicitEmbeddingModel(options) {
|
|
703
|
-
return options.embeddingModel !== undefined
|
|
704
|
-
|| options.embedding !== undefined;
|
|
705
|
-
}
|
|
706
839
|
function readCollectionInfo(home, name) {
|
|
707
840
|
const registry = new CollectionRegistry(home, undefined, true);
|
|
708
841
|
try {
|
|
@@ -720,28 +853,47 @@ function indexedEmbeddingSchema(info) {
|
|
|
720
853
|
code: "ZVEC_GREP.ENGINE.SERVICE.INDEX_MISSING",
|
|
721
854
|
context: errorDetails([
|
|
722
855
|
collectionDetail(info.name),
|
|
723
|
-
detail("hint", "Run zg
|
|
856
|
+
detail("hint", "Run zg index to build this index."),
|
|
724
857
|
]),
|
|
725
858
|
});
|
|
726
859
|
}
|
|
727
|
-
function providerOptions(options, root, registryHome) {
|
|
860
|
+
function providerOptions(options, root, registryHome, provider, config = readGlobalConfig()) {
|
|
861
|
+
const providerConfig = provider ? config.providers?.[provider] : undefined;
|
|
728
862
|
return {
|
|
729
|
-
apiKey: options.apiKey ?? "",
|
|
730
|
-
endpoint: options.endpoint,
|
|
731
|
-
modelCacheDir: modelCacheDir
|
|
732
|
-
|
|
733
|
-
|
|
863
|
+
apiKey: options.apiKey ?? providerConfig?.apiKey ?? "",
|
|
864
|
+
endpoint: options.endpoint ?? providerConfig?.endpoint,
|
|
865
|
+
modelCacheDir: options.modelCacheDir ??
|
|
866
|
+
process.env.ZVEC_GREP_MODEL_CACHE ??
|
|
867
|
+
config.defaults?.modelCacheDir ??
|
|
868
|
+
modelCacheDir(options, root, registryHome),
|
|
869
|
+
llamaGpu: options.llamaGpu ?? config.defaults?.llamaGpu,
|
|
870
|
+
embeddingParallelism: options.embeddingParallelism ?? config.defaults?.embeddingParallelism,
|
|
734
871
|
};
|
|
735
872
|
}
|
|
873
|
+
function providerFromReference(reference) {
|
|
874
|
+
const separator = reference.indexOf("/");
|
|
875
|
+
return separator > 0 ? reference.slice(0, separator) : undefined;
|
|
876
|
+
}
|
|
877
|
+
function providerOptionsFingerprint(options) {
|
|
878
|
+
return createHash("sha256")
|
|
879
|
+
.update(JSON.stringify([
|
|
880
|
+
options.apiKey,
|
|
881
|
+
options.endpoint,
|
|
882
|
+
options.modelCacheDir,
|
|
883
|
+
options.llamaGpu,
|
|
884
|
+
options.embeddingParallelism,
|
|
885
|
+
]))
|
|
886
|
+
.digest("hex");
|
|
887
|
+
}
|
|
736
888
|
function assertCollectionEmbeddingMatchesCurrentModel(info, model, rebuildCommand) {
|
|
737
889
|
if (!isCollectionIndexed(info)) {
|
|
738
890
|
return;
|
|
739
891
|
}
|
|
740
892
|
const expected = info.embedding;
|
|
741
|
-
const changed = expected.provider !== model.ref.provider
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
893
|
+
const changed = expected.provider !== model.ref.provider ||
|
|
894
|
+
expected.model !== model.ref.model ||
|
|
895
|
+
expected.dimension !== model.dimension ||
|
|
896
|
+
expected.metric !== model.metric;
|
|
745
897
|
if (!changed) {
|
|
746
898
|
return;
|
|
747
899
|
}
|
|
@@ -808,29 +960,36 @@ function contextGroupLimit(limit, groupCount) {
|
|
|
808
960
|
function normalizeContextRequest(options) {
|
|
809
961
|
const primaryQueries = normalizePrimaryQueries(options.query, options.queries);
|
|
810
962
|
const extraRoutes = normalizeContextRoutes(options.routes ?? []);
|
|
811
|
-
|
|
963
|
+
const hasPatternFiles = options.rg === true && (options.rgOptions?.patternFiles?.length ?? 0) > 0;
|
|
964
|
+
if (primaryQueries.length === 0 &&
|
|
965
|
+
extraRoutes.length === 0 &&
|
|
966
|
+
!hasPatternFiles) {
|
|
812
967
|
throw new EngineError("zvec-grep context requires a non-empty query or route", {
|
|
813
968
|
code: "ZVEC_GREP.ENGINE.SERVICE.EMPTY_QUERY",
|
|
814
969
|
});
|
|
815
970
|
}
|
|
816
971
|
const groups = contextGroups(primaryQueries, extraRoutes);
|
|
817
972
|
const routes = groups.flatMap((group) => group.routes);
|
|
818
|
-
const
|
|
973
|
+
const rgPatterns = [
|
|
974
|
+
...primaryQueries,
|
|
975
|
+
...extraRoutes.map((route) => route.query),
|
|
976
|
+
];
|
|
819
977
|
const displayQuery = primaryQueries.length > 0
|
|
820
978
|
? primaryQueries.join(" | ")
|
|
821
|
-
: extraRoutes.
|
|
979
|
+
: extraRoutes.length > 0
|
|
980
|
+
? extraRoutes.map((route) => route.query).join(" | ")
|
|
981
|
+
: options
|
|
982
|
+
.rgOptions.patternFiles.map((path) => `@${path}`)
|
|
983
|
+
.join(" | ");
|
|
822
984
|
return {
|
|
823
985
|
displayQuery,
|
|
824
|
-
|
|
986
|
+
rgPatterns,
|
|
825
987
|
routes,
|
|
826
988
|
groups,
|
|
827
989
|
};
|
|
828
990
|
}
|
|
829
991
|
function normalizePrimaryQueries(query, queries) {
|
|
830
|
-
return [
|
|
831
|
-
query,
|
|
832
|
-
...(queries ?? []),
|
|
833
|
-
].flatMap((value) => {
|
|
992
|
+
return [query, ...(queries ?? [])].flatMap((value) => {
|
|
834
993
|
const normalized = normalizeOptionalQuery(value);
|
|
835
994
|
return normalized ? [normalized] : [];
|
|
836
995
|
});
|
|
@@ -867,11 +1026,9 @@ function contextGroups(primaryQueries, extraRoutes) {
|
|
|
867
1026
|
{ mode: "fts", query },
|
|
868
1027
|
{ mode: "vector", query },
|
|
869
1028
|
],
|
|
870
|
-
fallbackQuery: query,
|
|
871
1029
|
})),
|
|
872
1030
|
...extraRoutes.map((route) => ({
|
|
873
1031
|
routes: [route],
|
|
874
|
-
fallbackQuery: route.query,
|
|
875
1032
|
})),
|
|
876
1033
|
];
|
|
877
1034
|
}
|
|
@@ -920,23 +1077,7 @@ function contextItemDedupeKey(item) {
|
|
|
920
1077
|
if (item.entityId) {
|
|
921
1078
|
return `entity:${item.entityId}`;
|
|
922
1079
|
}
|
|
923
|
-
return [
|
|
924
|
-
"range",
|
|
925
|
-
item.file.absolutePath,
|
|
926
|
-
JSON.stringify(item.range),
|
|
927
|
-
].join(":");
|
|
928
|
-
}
|
|
929
|
-
function mergeFallbackDiagnostics(results) {
|
|
930
|
-
const [first] = results;
|
|
931
|
-
if (!first) {
|
|
932
|
-
throw new EngineError("zvec-grep lexical fallback requires at least one query", {
|
|
933
|
-
code: "ZVEC_GREP.ENGINE.SERVICE.EMPTY_QUERY",
|
|
934
|
-
});
|
|
935
|
-
}
|
|
936
|
-
return {
|
|
937
|
-
...first.diagnostics,
|
|
938
|
-
truncated: results.some((result) => result.diagnostics.truncated),
|
|
939
|
-
};
|
|
1080
|
+
return ["range", item.file.absolutePath, JSON.stringify(item.range)].join(":");
|
|
940
1081
|
}
|
|
941
1082
|
function contextItemTarget(hit) {
|
|
942
1083
|
const evidence = hit.evidence.find((item) => !item.isEntity);
|
|
@@ -979,7 +1120,8 @@ function contextItemOutline(hit, evidence) {
|
|
|
979
1120
|
if (!isUsefulOutline(hit.entity.metadata, outline)) {
|
|
980
1121
|
return undefined;
|
|
981
1122
|
}
|
|
982
|
-
if (evidence?.content.kind === "text" &&
|
|
1123
|
+
if (evidence?.content.kind === "text" &&
|
|
1124
|
+
evidence.content.text.trim() === outline) {
|
|
983
1125
|
return undefined;
|
|
984
1126
|
}
|
|
985
1127
|
return outline;
|
|
@@ -988,23 +1130,23 @@ function isUsefulOutline(metadata, outline) {
|
|
|
988
1130
|
if (!metadata || metadata.kind !== "code") {
|
|
989
1131
|
return false;
|
|
990
1132
|
}
|
|
991
|
-
return metadata.symbolType === "class"
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1133
|
+
return (metadata.symbolType === "class" ||
|
|
1134
|
+
metadata.symbolType === "interface" ||
|
|
1135
|
+
metadata.symbolType === "module" ||
|
|
1136
|
+
outline.includes("\ncalls:"));
|
|
995
1137
|
}
|
|
996
1138
|
function sameDisplayedContent(entity, evidence) {
|
|
997
|
-
return JSON.stringify(entity.range) === JSON.stringify(evidence.range)
|
|
998
|
-
|
|
1139
|
+
return (JSON.stringify(entity.range) === JSON.stringify(evidence.range) &&
|
|
1140
|
+
contentEquals(entity.content, evidence.content));
|
|
999
1141
|
}
|
|
1000
1142
|
function contentEquals(left, right) {
|
|
1001
1143
|
if (left.kind !== right.kind) {
|
|
1002
1144
|
return false;
|
|
1003
1145
|
}
|
|
1004
1146
|
if (left.kind === "image") {
|
|
1005
|
-
return right.kind === "image"
|
|
1006
|
-
|
|
1007
|
-
|
|
1147
|
+
return (right.kind === "image" &&
|
|
1148
|
+
left.format === right.format &&
|
|
1149
|
+
left.data.byteLength === right.data.byteLength);
|
|
1008
1150
|
}
|
|
1009
1151
|
return right.kind === "text" && left.text === right.text;
|
|
1010
1152
|
}
|
|
@@ -1020,7 +1162,8 @@ function fileFreshnessStatus(file) {
|
|
|
1020
1162
|
if (file.indexStatus.indexedTime >= info.mtimeMs) {
|
|
1021
1163
|
return "fresh";
|
|
1022
1164
|
}
|
|
1023
|
-
if (file.contentHash &&
|
|
1165
|
+
if (file.contentHash &&
|
|
1166
|
+
sha256File(file.absolutePath) === file.contentHash) {
|
|
1024
1167
|
return "fresh";
|
|
1025
1168
|
}
|
|
1026
1169
|
}
|