@zvec/zvec-grep 0.1.5 → 0.1.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/README.md +88 -249
- package/README_CN.md +82 -246
- package/dist/authorization/index.d.ts +7 -0
- package/dist/authorization/index.js +7 -0
- package/dist/authorization/index.js.map +1 -0
- package/dist/authorization/manager.d.ts +8 -0
- package/dist/authorization/manager.js +19 -0
- package/dist/authorization/manager.js.map +1 -0
- package/dist/authorization/operation.d.ts +7 -0
- package/dist/authorization/operation.js +50 -0
- package/dist/authorization/operation.js.map +1 -0
- package/dist/authorization/planner.d.ts +21 -0
- package/dist/authorization/planner.js +97 -0
- package/dist/authorization/planner.js.map +1 -0
- package/dist/authorization/prompt.d.ts +11 -0
- package/dist/authorization/prompt.js +62 -0
- package/dist/authorization/prompt.js.map +1 -0
- package/dist/authorization/store.d.ts +20 -0
- package/dist/authorization/store.js +251 -0
- package/dist/authorization/store.js.map +1 -0
- package/dist/authorization/target.d.ts +15 -0
- package/dist/authorization/target.js +49 -0
- package/dist/authorization/target.js.map +1 -0
- package/dist/authorization/types.d.ts +61 -0
- package/dist/authorization/types.js +2 -0
- package/dist/authorization/types.js.map +1 -0
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.js +304 -54
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/auth.d.ts +21 -0
- package/dist/cli/auth.js +212 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/commands.js +433 -618
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/errors.js +2 -2
- package/dist/cli/errors.js.map +1 -1
- package/dist/cli/format/context.d.ts +1 -0
- package/dist/cli/format/context.js +241 -45
- package/dist/cli/format/context.js.map +1 -1
- package/dist/cli/format/debug.js +2 -2
- package/dist/cli/format/debug.js.map +1 -1
- package/dist/cli/format/progress.d.ts +4 -0
- package/dist/cli/format/progress.js +219 -46
- package/dist/cli/format/progress.js.map +1 -1
- package/dist/cli/format/status.d.ts +77 -6
- package/dist/cli/format/status.js +302 -106
- package/dist/cli/format/status.js.map +1 -1
- package/dist/cli/help.js +258 -59
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/index.js +0 -0
- package/dist/cli/install.d.ts +4 -0
- package/dist/cli/install.js +845 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/cli/managed-rg.d.ts +10 -0
- package/dist/cli/managed-rg.js +329 -0
- package/dist/cli/managed-rg.js.map +1 -0
- package/dist/cli/types.d.ts +20 -6
- package/dist/cli/types.js.map +1 -1
- package/dist/client/daemon-client.d.ts +19 -0
- package/dist/client/daemon-client.js +180 -0
- package/dist/client/daemon-client.js.map +1 -0
- package/dist/client/mode-router.d.ts +9 -0
- package/dist/client/mode-router.js +28 -0
- package/dist/client/mode-router.js.map +1 -0
- package/dist/client/search-policy.d.ts +11 -0
- package/dist/client/search-policy.js +15 -0
- package/dist/client/search-policy.js.map +1 -0
- package/dist/daemon/backend.d.ts +76 -0
- package/dist/daemon/backend.js +1049 -0
- package/dist/daemon/backend.js.map +1 -0
- package/dist/daemon/change-set.d.ts +25 -0
- package/dist/daemon/change-set.js +101 -0
- package/dist/daemon/change-set.js.map +1 -0
- package/dist/daemon/config.d.ts +24 -0
- package/dist/daemon/config.js +88 -0
- package/dist/daemon/config.js.map +1 -0
- package/dist/daemon/errors.d.ts +5 -0
- package/dist/daemon/errors.js +11 -0
- package/dist/daemon/errors.js.map +1 -0
- package/dist/daemon/http-server.d.ts +30 -0
- package/dist/daemon/http-server.js +287 -0
- package/dist/daemon/http-server.js.map +1 -0
- package/dist/daemon/index-coordinator.d.ts +23 -0
- package/dist/daemon/index-coordinator.js +68 -0
- package/dist/daemon/index-coordinator.js.map +1 -0
- package/dist/daemon/job-scheduler.d.ts +70 -0
- package/dist/daemon/job-scheduler.js +366 -0
- package/dist/daemon/job-scheduler.js.map +1 -0
- package/dist/daemon/logger.d.ts +9 -0
- package/dist/daemon/logger.js +53 -0
- package/dist/daemon/logger.js.map +1 -0
- package/dist/daemon/model-pool.d.ts +45 -0
- package/dist/daemon/model-pool.js +187 -0
- package/dist/daemon/model-pool.js.map +1 -0
- package/dist/daemon/root-lease.d.ts +14 -0
- package/dist/daemon/root-lease.js +188 -0
- package/dist/daemon/root-lease.js.map +1 -0
- package/dist/daemon/root-runtime.d.ts +78 -0
- package/dist/daemon/root-runtime.js +333 -0
- package/dist/daemon/root-runtime.js.map +1 -0
- package/dist/daemon/runtime-manager.d.ts +45 -0
- package/dist/daemon/runtime-manager.js +243 -0
- package/dist/daemon/runtime-manager.js.map +1 -0
- package/dist/daemon/runtime.d.ts +12 -0
- package/dist/daemon/runtime.js +102 -0
- package/dist/daemon/runtime.js.map +1 -0
- package/dist/daemon/server-controller.d.ts +39 -0
- package/dist/daemon/server-controller.js +203 -0
- package/dist/daemon/server-controller.js.map +1 -0
- package/dist/daemon/watch-manager.d.ts +50 -0
- package/dist/daemon/watch-manager.js +293 -0
- package/dist/daemon/watch-manager.js.map +1 -0
- package/dist/daemon/workspace-read-session-cache.d.ts +31 -0
- package/dist/daemon/workspace-read-session-cache.js +115 -0
- package/dist/daemon/workspace-read-session-cache.js.map +1 -0
- package/dist/engine/config.d.ts +30 -8
- package/dist/engine/config.js +171 -45
- package/dist/engine/config.js.map +1 -1
- package/dist/engine/{errors/index.d.ts → errors.d.ts} +6 -1
- package/dist/engine/{errors/details.js → errors.js} +17 -4
- package/dist/engine/errors.js.map +1 -0
- package/dist/engine/extraction/code/adapter.d.ts +1 -1
- package/dist/engine/extraction/code/extractor.d.ts +5 -11
- package/dist/engine/extraction/code/extractor.js +105 -44
- package/dist/engine/extraction/code/extractor.js.map +1 -1
- package/dist/engine/extraction/code/families/c-family.js +1 -1
- package/dist/engine/extraction/code/families/c-family.js.map +1 -1
- package/dist/engine/extraction/code/families/js-ts.d.ts +1 -1
- package/dist/engine/extraction/code/families/metadata.d.ts +1 -1
- package/dist/engine/extraction/code/tree-sitter/grammar.js.map +1 -0
- package/dist/engine/extraction/code/tree-sitter/nodes.js.map +1 -0
- package/dist/engine/extraction/code/tree-sitter/parser.js.map +1 -0
- package/dist/engine/extraction/image/extractor.d.ts +5 -0
- package/dist/engine/extraction/image/extractor.js +35 -0
- package/dist/engine/extraction/image/extractor.js.map +1 -0
- package/dist/engine/extraction/index.d.ts +3 -44
- package/dist/engine/extraction/index.js +1 -280
- package/dist/engine/extraction/index.js.map +1 -1
- package/dist/engine/extraction/markdown/extractor.d.ts +5 -11
- package/dist/engine/extraction/markdown/extractor.js +35 -16
- package/dist/engine/extraction/markdown/extractor.js.map +1 -1
- package/dist/engine/extraction/runtime.d.ts +4 -0
- package/dist/engine/extraction/runtime.js +26 -0
- package/dist/engine/extraction/runtime.js.map +1 -0
- package/dist/engine/extraction/source.d.ts +18 -0
- package/dist/engine/extraction/source.js +22 -0
- package/dist/engine/extraction/source.js.map +1 -0
- package/dist/engine/extraction/text/extractor.d.ts +7 -0
- package/dist/engine/extraction/text/extractor.js +168 -0
- package/dist/engine/extraction/text/extractor.js.map +1 -0
- package/dist/engine/extraction/types.d.ts +4 -0
- package/dist/engine/{models → extraction}/types.js.map +1 -1
- package/dist/engine/{files/file-type.d.ts → file-type.d.ts} +1 -1
- package/dist/engine/file-type.js.map +1 -0
- package/dist/engine/index-status.d.ts +9 -0
- package/dist/engine/index-status.js +38 -0
- package/dist/engine/index-status.js.map +1 -0
- package/dist/engine/manifest.d.ts +12 -0
- package/dist/engine/manifest.js +120 -0
- package/dist/engine/manifest.js.map +1 -0
- package/dist/engine/models/{providers/llama-cpp/embedding.d.ts → backends/llama-cpp.d.ts} +25 -20
- package/dist/engine/models/{providers/llama-cpp/embedding.js → backends/llama-cpp.js} +127 -106
- package/dist/engine/models/backends/llama-cpp.js.map +1 -0
- package/dist/engine/models/backends/model2vec.d.ts +57 -0
- package/dist/engine/models/backends/model2vec.js +332 -0
- package/dist/engine/models/backends/model2vec.js.map +1 -0
- package/dist/engine/models/backends/qwen.d.ts +44 -0
- package/dist/engine/models/{providers/qwen/embedding.js → backends/qwen.js} +155 -94
- package/dist/engine/models/backends/qwen.js.map +1 -0
- package/dist/engine/models/backends/transformers-js.d.ts +87 -0
- package/dist/engine/models/backends/transformers-js.js +316 -0
- package/dist/engine/models/backends/transformers-js.js.map +1 -0
- package/dist/engine/models/catalog.d.ts +188 -23
- package/dist/engine/models/catalog.js +172 -25
- package/dist/engine/models/catalog.js.map +1 -1
- package/dist/engine/models/download-progress.d.ts +15 -0
- package/dist/engine/models/download-progress.js +48 -0
- package/dist/engine/models/download-progress.js.map +1 -0
- package/dist/engine/models/embeddings.d.ts +58 -18
- package/dist/engine/models/embeddings.js +52 -28
- package/dist/engine/models/embeddings.js.map +1 -1
- package/dist/engine/models/factory.d.ts +2 -5
- package/dist/engine/models/factory.js +41 -56
- package/dist/engine/models/factory.js.map +1 -1
- package/dist/engine/models/index.d.ts +4 -6
- package/dist/engine/models/index.js +4 -4
- package/dist/engine/models/index.js.map +1 -1
- package/dist/engine/models/ranking.d.ts +7 -3
- package/dist/engine/models/resolution.d.ts +8 -0
- package/dist/engine/models/resolution.js +22 -0
- package/dist/engine/models/resolution.js.map +1 -0
- package/dist/engine/pipeline/indexing/index.d.ts +9 -7
- package/dist/engine/pipeline/indexing/index.js +360 -121
- package/dist/engine/pipeline/indexing/index.js.map +1 -1
- package/dist/engine/pipeline/indexing/root-paths.js +6 -6
- package/dist/engine/pipeline/indexing/root-paths.js.map +1 -1
- package/dist/engine/pipeline/indexing/scanner/index.d.ts +6 -1
- package/dist/engine/pipeline/indexing/scanner/index.js +233 -29
- package/dist/engine/pipeline/indexing/scanner/index.js.map +1 -1
- package/dist/engine/pipeline/search/index.d.ts +6 -6
- package/dist/engine/pipeline/search/index.js +8 -10
- package/dist/engine/pipeline/search/index.js.map +1 -1
- package/dist/engine/service/index.d.ts +3 -2
- package/dist/engine/service/index.js +1 -1
- package/dist/engine/service/index.js.map +1 -1
- package/dist/engine/service/root.d.ts +13 -12
- package/dist/engine/service/root.js +35 -24
- package/dist/engine/service/root.js.map +1 -1
- package/dist/engine/service/structure-enrichment.js +12 -14
- package/dist/engine/service/structure-enrichment.js.map +1 -1
- package/dist/engine/service/types.d.ts +32 -37
- package/dist/engine/service/workspace-index.d.ts +24 -0
- package/dist/engine/service/workspace-index.js +151 -0
- package/dist/engine/service/workspace-index.js.map +1 -0
- package/dist/engine/service/zvec-grep.d.ts +12 -1
- package/dist/engine/service/zvec-grep.js +677 -486
- package/dist/engine/service/zvec-grep.js.map +1 -1
- package/dist/engine/storage/index.d.ts +8 -4
- package/dist/engine/storage/index.js +1 -1
- package/dist/engine/storage/index.js.map +1 -1
- package/dist/engine/storage/zvec.d.ts +10 -33
- package/dist/engine/storage/zvec.js +95 -38
- package/dist/engine/storage/zvec.js.map +1 -1
- package/dist/engine/types.d.ts +15 -23
- package/dist/engine/types.js +1 -1
- package/dist/engine/types.js.map +1 -1
- package/dist/engine/utils/daemon-lease.d.ts +17 -0
- package/dist/engine/utils/daemon-lease.js +175 -0
- package/dist/engine/utils/daemon-lease.js.map +1 -0
- package/dist/engine/utils/file-selection.js +38 -1
- package/dist/engine/utils/file-selection.js.map +1 -1
- package/dist/engine/utils/lock.js +1 -1
- package/dist/engine/utils/lock.js.map +1 -1
- package/dist/index-progress.d.ts +10 -0
- package/dist/index-progress.js +159 -0
- package/dist/index-progress.js.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/http-transport.d.ts +27 -0
- package/dist/mcp/http-transport.js +182 -0
- package/dist/mcp/http-transport.js.map +1 -0
- package/dist/mcp/input-normalization.d.ts +38 -0
- package/dist/mcp/input-normalization.js +95 -0
- package/dist/mcp/input-normalization.js.map +1 -0
- package/dist/mcp/progress-heartbeat.d.ts +17 -0
- package/dist/mcp/progress-heartbeat.js +46 -0
- package/dist/mcp/progress-heartbeat.js.map +1 -0
- package/dist/mcp/request-metadata.d.ts +2 -0
- package/dist/mcp/request-metadata.js +11 -0
- package/dist/mcp/request-metadata.js.map +1 -0
- package/dist/mcp/request-state.d.ts +40 -0
- package/dist/mcp/request-state.js +210 -0
- package/dist/mcp/request-state.js.map +1 -0
- package/dist/mcp/result-format.d.ts +13 -0
- package/dist/mcp/result-format.js +75 -0
- package/dist/mcp/result-format.js.map +1 -0
- package/dist/mcp/schemas.d.ts +430 -0
- package/dist/mcp/schemas.js +447 -0
- package/dist/mcp/schemas.js.map +1 -0
- package/dist/mcp/tools.d.ts +150 -0
- package/dist/mcp/tools.js +448 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/toolset.d.ts +5 -0
- package/dist/mcp/toolset.js +12 -0
- package/dist/mcp/toolset.js.map +1 -0
- package/dist/observability/trace-context.d.ts +11 -0
- package/dist/observability/trace-context.js +138 -0
- package/dist/observability/trace-context.js.map +1 -0
- package/docs/01-agents.md +111 -0
- package/docs/02-cli.md +229 -0
- package/docs/03-mcp.md +167 -0
- package/docs/04-pipeline.md +218 -0
- package/docs/05-architecture.md +86 -0
- package/docs/06-server.md +210 -0
- package/docs/07-embedding.md +179 -0
- package/docs/08-roadmap.md +85 -0
- package/docs/README.md +53 -0
- package/package.json +17 -8
- package/skills/zvec-grep/SKILL.md +43 -133
- package/skills/zvec-grep/agents/openai.yaml +2 -2
- package/skills/zvec-grep/references/cli-fallback.md +85 -0
- package/dist/cli/mcp.d.ts +0 -2
- package/dist/cli/mcp.js +0 -354
- package/dist/cli/mcp.js.map +0 -1
- package/dist/engine/collection/index.d.ts +0 -55
- package/dist/engine/collection/index.js +0 -710
- package/dist/engine/collection/index.js.map +0 -1
- package/dist/engine/errors/details.d.ts +0 -6
- package/dist/engine/errors/details.js.map +0 -1
- package/dist/engine/errors/index.js +0 -16
- package/dist/engine/errors/index.js.map +0 -1
- package/dist/engine/extraction/tree-sitter/grammar.js.map +0 -1
- package/dist/engine/extraction/tree-sitter/nodes.js.map +0 -1
- package/dist/engine/extraction/tree-sitter/parser.js.map +0 -1
- package/dist/engine/files/file-type.js.map +0 -1
- package/dist/engine/models/providers/index.d.ts +0 -2
- package/dist/engine/models/providers/index.js +0 -3
- package/dist/engine/models/providers/index.js.map +0 -1
- package/dist/engine/models/providers/llama-cpp/embedding.js.map +0 -1
- package/dist/engine/models/providers/llama-cpp/index.d.ts +0 -1
- package/dist/engine/models/providers/llama-cpp/index.js +0 -2
- package/dist/engine/models/providers/llama-cpp/index.js.map +0 -1
- package/dist/engine/models/providers/qwen/embedding.d.ts +0 -42
- package/dist/engine/models/providers/qwen/embedding.js.map +0 -1
- package/dist/engine/models/providers/qwen/index.d.ts +0 -1
- package/dist/engine/models/providers/qwen/index.js +0 -2
- package/dist/engine/models/providers/qwen/index.js.map +0 -1
- package/dist/engine/models/types.d.ts +0 -30
- /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/grammar.d.ts +0 -0
- /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/grammar.js +0 -0
- /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/nodes.d.ts +0 -0
- /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/nodes.js +0 -0
- /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/parser.d.ts +0 -0
- /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/parser.js +0 -0
- /package/dist/engine/{models → extraction}/types.js +0 -0
- /package/dist/engine/{files/file-type.js → file-type.js} +0 -0
|
@@ -1,27 +1,87 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
|
-
import { dirname, join, relative, resolve } from "node:path";
|
|
4
|
-
import { globalConfigPath, readGlobalConfig, } from "../config.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { createEmbeddingModel,
|
|
8
|
-
import {
|
|
1
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { createHash, createHmac, randomBytes, randomUUID } from "node:crypto";
|
|
3
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
4
|
+
import { globalConfigPath, readGlobalConfig, resolveEmbeddingRuntimeOptions, } from "../config.js";
|
|
5
|
+
import { isWorkspaceIndexed, WorkspaceIndex } from "./workspace-index.js";
|
|
6
|
+
import { workspaceIndexDetail, detail, EngineError, errorDetails, } from "../errors.js";
|
|
7
|
+
import { createEmbeddingModel, EmbeddingPurpose, resolveEmbeddingReference, } from "../models/index.js";
|
|
8
|
+
import { CURRENT_INDEX_VERSION } from "../types.js";
|
|
9
|
+
import { indexStatusNeedsRefresh } from "../index-status.js";
|
|
10
|
+
import { workspaceIndexInfoFromManifest, CURRENT_MANIFEST_VERSION, readWorkspaceManifest, writeWorkspaceManifest, } from "../manifest.js";
|
|
11
|
+
import { validateRootPaths } from "../pipeline/indexing/root-paths.js";
|
|
12
|
+
import { findNearestWorkspace, hasWorkspaceIndex, resetWorkspaceIndexStorage, resolveZvecGrepRoot, workspaceHome, workspaceIndexLocation, } from "./root.js";
|
|
9
13
|
import { runRgSearch } from "./lexical.js";
|
|
10
14
|
import { defaultHome, normalizePath } from "../utils/path.js";
|
|
11
15
|
import { acquireReadWriteLock, assertNoWriteLock, } from "../utils/lock.js";
|
|
16
|
+
import { assertDaemonWriteAllowed } from "../utils/daemon-lease.js";
|
|
12
17
|
import { TimingCollector } from "../utils/timing.js";
|
|
13
18
|
import { enrichLexicalItemsWithStructure } from "./structure-enrichment.js";
|
|
19
|
+
import { remoteEmbeddingAuthorizationGuard } from "../../authorization/operation.js";
|
|
20
|
+
import { RemoteEmbeddingAuthorizationStore } from "../../authorization/store.js";
|
|
14
21
|
const DEFAULT_CONTEXT_LIMIT = 10;
|
|
15
22
|
const DEFAULT_CONTEXT_TOTAL_LIMIT = 30;
|
|
16
23
|
const DEFAULT_LOCAL_EMBEDDING = "local/embeddinggemma-300m";
|
|
24
|
+
const PROVIDER_API_KEY_IDENTITY_SECRET = randomBytes(32);
|
|
17
25
|
const MAX_RECOVERED_EMBEDDING_MODELS = 4;
|
|
18
26
|
export async function createZvecGrep(options = {}) {
|
|
19
27
|
return new ZvecGrepService(options);
|
|
20
28
|
}
|
|
29
|
+
export function openWorkspaceReadSession(startRoot, embeddingModel) {
|
|
30
|
+
const start = resolveZvecGrepRoot(startRoot);
|
|
31
|
+
assertNearestWorkspaceHomeUnlocked(start, "daemon.context.open");
|
|
32
|
+
const nearest = findNearestWorkspaceIndex(start);
|
|
33
|
+
if (!nearest) {
|
|
34
|
+
throw workspaceIndexMissingError(start, "undecided");
|
|
35
|
+
}
|
|
36
|
+
const { location, info } = nearest;
|
|
37
|
+
if (info.indexPolicy === "disabled") {
|
|
38
|
+
throw workspaceIndexDisabledError(location.root);
|
|
39
|
+
}
|
|
40
|
+
if (!isWorkspaceIndexed(info) || !hasWorkspaceIndex(location)) {
|
|
41
|
+
throw workspaceIndexMissingError(location.root, info.indexPolicy ?? "enabled");
|
|
42
|
+
}
|
|
43
|
+
const workspaceIndex = new WorkspaceIndex(info, embeddingModel, true, location.filesPath);
|
|
44
|
+
let closed = false;
|
|
45
|
+
return {
|
|
46
|
+
root: location.root,
|
|
47
|
+
async context(options) {
|
|
48
|
+
if (closed) {
|
|
49
|
+
throw new EngineError("Workspace read session is already closed", {
|
|
50
|
+
code: "ZVEC_GREP.ENGINE.SERVICE.READ_SESSION_CLOSED",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const timings = new TimingCollector();
|
|
54
|
+
const request = normalizeContextRequest(options);
|
|
55
|
+
const result = await timings.time("total", () => withHomeReadLock(location.home, "daemon.context", () => contextFromOpenWorkspaceIndex({
|
|
56
|
+
root: location.root,
|
|
57
|
+
request,
|
|
58
|
+
workspaceIndex,
|
|
59
|
+
options: { ...options, autoUpdate: false },
|
|
60
|
+
timings,
|
|
61
|
+
})));
|
|
62
|
+
return withContextTimings(result, timings);
|
|
63
|
+
},
|
|
64
|
+
async close() {
|
|
65
|
+
if (closed) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
workspaceIndex.close();
|
|
69
|
+
closed = true;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function createEmbeddingModelForIdentity(identity, options = {}, workspaceRuntime = {}) {
|
|
74
|
+
const reference = embeddingModelReference(identity);
|
|
75
|
+
return createServiceEmbeddingModel(reference, providerOptions(options, identity, readGlobalConfig(), workspaceRuntime), options);
|
|
76
|
+
}
|
|
77
|
+
export function embeddingModelPoolKeyForIdentity(identity, options = {}, workspaceRuntime = {}) {
|
|
78
|
+
const reference = embeddingModelReference(identity);
|
|
79
|
+
const fingerprint = providerOptionsFingerprint(providerOptions(options, identity, readGlobalConfig(), workspaceRuntime));
|
|
80
|
+
return [reference, fingerprint].join("/");
|
|
81
|
+
}
|
|
21
82
|
class ZvecGrepService {
|
|
22
83
|
options;
|
|
23
84
|
root;
|
|
24
|
-
collections;
|
|
25
85
|
embeddingModel;
|
|
26
86
|
recoveredEmbeddingModels = new Map();
|
|
27
87
|
retiredEmbeddingModels = new Set();
|
|
@@ -31,23 +91,21 @@ class ZvecGrepService {
|
|
|
31
91
|
this.options = options;
|
|
32
92
|
this.root = resolveZvecGrepRoot(options.root);
|
|
33
93
|
this.embeddingModel = options.embeddingModel;
|
|
34
|
-
this.collections = {
|
|
35
|
-
list: () => this.listCollections(),
|
|
36
|
-
info: (name) => this.collectionInfo(name),
|
|
37
|
-
status: (name) => this.collectionStatus(name),
|
|
38
|
-
index: (name, paths, indexOptions) => this.indexCollection(name, paths, indexOptions),
|
|
39
|
-
remove: (name) => this.removeCollection(name),
|
|
40
|
-
};
|
|
41
94
|
}
|
|
42
95
|
async index(options = {}) {
|
|
43
96
|
this.ensureOpen();
|
|
44
97
|
const root = resolveZvecGrepRoot(options.root ?? this.root);
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
98
|
+
const daemonWritePermit = assertDaemonWriteAllowed(root, this.options.daemonInstanceToken);
|
|
99
|
+
const location = workspaceIndexLocation(root);
|
|
100
|
+
try {
|
|
101
|
+
return await this.withEmbeddingModelOperation(() => withHomeWriteLock(location.home, options.rebuild ? "index.rebuild" : "index", async () => {
|
|
102
|
+
const existing = readWorkspaceManifest(location.home);
|
|
103
|
+
const existingRuntime = existing?.embeddingRuntime ?? {};
|
|
104
|
+
const embeddingModel = this.embeddingModelForIndex(existing, "index", existingRuntime);
|
|
105
|
+
const effectiveRuntime = effectiveEmbeddingRuntime(this.options, embeddingModel, runtimeForModelProvider(existing, embeddingModel, existingRuntime));
|
|
106
|
+
if (!options.rebuild) {
|
|
107
|
+
assertWorkspaceEndpointMatchesCurrentRuntime(existing, existingRuntime, effectiveRuntime, "zg index --endpoint <url> --rebuild");
|
|
108
|
+
}
|
|
51
109
|
const rootPaths = resolveIndexRootPaths(existing, options.rootPaths, root, {
|
|
52
110
|
resetPaths: options.resetPaths === true,
|
|
53
111
|
includePaths: options.includePaths,
|
|
@@ -63,56 +121,100 @@ class ZvecGrepService {
|
|
|
63
121
|
maxFileSizeBytes: options.maxFileSizeBytes,
|
|
64
122
|
follow: options.follow,
|
|
65
123
|
});
|
|
66
|
-
if (options.rebuild) {
|
|
67
|
-
|
|
124
|
+
if (options.rebuild || !isWorkspaceIndexed(existing)) {
|
|
125
|
+
resetWorkspaceIndexStorage(location);
|
|
68
126
|
}
|
|
69
|
-
const existingAfterRebuild =
|
|
70
|
-
if (
|
|
71
|
-
|
|
127
|
+
const existingAfterRebuild = options.rebuild ? null : existing;
|
|
128
|
+
if (isWorkspaceIndexed(existingAfterRebuild)) {
|
|
129
|
+
assertWorkspaceEmbeddingMatchesCurrentModel(existingAfterRebuild, embeddingModel, "zg index --rebuild");
|
|
72
130
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
131
|
+
const manifest = prepareWorkspaceManifest(location, existingAfterRebuild, rootPaths, embeddingModel, existingRuntime);
|
|
132
|
+
const workspaceIndex = new WorkspaceIndex(manifest, embeddingModel, false, location.filesPath);
|
|
133
|
+
writeWorkspaceManifest(location.home, manifest);
|
|
134
|
+
try {
|
|
135
|
+
const releaseWriterContext = options.onWriterContext?.((contextOptions) => this.contextFromWriterWorkspaceIndex(root, workspaceIndex, contextOptions));
|
|
136
|
+
try {
|
|
137
|
+
const result = await workspaceIndex.index({
|
|
138
|
+
rebuild: false,
|
|
139
|
+
embeddingConcurrency: options.embeddingConcurrency,
|
|
140
|
+
onProgress: options.onProgress,
|
|
141
|
+
changedPaths: options.changedPaths,
|
|
142
|
+
signal: options.signal,
|
|
143
|
+
});
|
|
144
|
+
writeWorkspaceManifest(location.home, {
|
|
145
|
+
...manifest,
|
|
146
|
+
embeddingRuntime: embeddingRuntimeAfterSuccessfulIndex(existing, existingRuntime, embeddingModel, effectiveRuntime, this.options),
|
|
147
|
+
updatedTime: Date.now(),
|
|
148
|
+
});
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
writeWorkspaceManifest(location.home, {
|
|
153
|
+
...manifest,
|
|
154
|
+
embeddingRuntime: existingRuntime,
|
|
155
|
+
updatedTime: Date.now(),
|
|
156
|
+
});
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
await releaseWriterContext?.();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
workspaceIndex.close();
|
|
165
|
+
}
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
daemonWritePermit?.release();
|
|
170
|
+
}
|
|
84
171
|
}
|
|
85
172
|
async dropIndex(options = {}) {
|
|
86
173
|
this.ensureOpen();
|
|
87
174
|
const root = resolveZvecGrepRoot(options.root ?? this.root);
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const registry = new CollectionRegistry(location.home, undefined);
|
|
94
|
-
try {
|
|
95
|
-
return registry.remove(ANONYMOUS_COLLECTION_NAME);
|
|
96
|
-
}
|
|
97
|
-
finally {
|
|
98
|
-
registry.close();
|
|
175
|
+
const daemonWritePermit = assertDaemonWriteAllowed(root, this.options.daemonInstanceToken);
|
|
176
|
+
const location = workspaceIndexLocation(root);
|
|
177
|
+
try {
|
|
178
|
+
if (!readWorkspaceManifest(location.home)) {
|
|
179
|
+
return false;
|
|
99
180
|
}
|
|
100
|
-
|
|
181
|
+
return await withHomeWriteLock(location.home, "index.drop", async () => {
|
|
182
|
+
resetWorkspaceIndexStorage(location);
|
|
183
|
+
return true;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
daemonWritePermit?.release();
|
|
188
|
+
}
|
|
101
189
|
}
|
|
102
190
|
async disableIndex(options = {}) {
|
|
103
191
|
this.ensureOpen();
|
|
104
192
|
const root = resolveZvecGrepRoot(options.root ?? this.root);
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
193
|
+
const daemonWritePermit = assertDaemonWriteAllowed(root, this.options.daemonInstanceToken);
|
|
194
|
+
const location = workspaceIndexLocation(root);
|
|
195
|
+
try {
|
|
196
|
+
await withHomeWriteLock(location.home, "index.disable", async () => {
|
|
197
|
+
const existing = readWorkspaceManifest(location.home);
|
|
198
|
+
const now = Date.now();
|
|
199
|
+
writeWorkspaceManifest(location.home, {
|
|
200
|
+
manifestVersion: CURRENT_MANIFEST_VERSION,
|
|
201
|
+
id: existing?.id ?? randomUUID(),
|
|
202
|
+
name: existing?.name ?? workspaceDisplayName(root),
|
|
203
|
+
path: location.home,
|
|
204
|
+
rootPaths: existing?.rootPaths ?? validateRootPaths([root]),
|
|
205
|
+
indexPolicy: "disabled",
|
|
206
|
+
embedding: null,
|
|
207
|
+
indexVersion: null,
|
|
208
|
+
createdTime: existing?.createdTime ?? now,
|
|
209
|
+
updatedTime: now,
|
|
210
|
+
embeddingRuntime: existing?.embeddingRuntime ?? {},
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
return this.info({ root });
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
daemonWritePermit?.release();
|
|
217
|
+
}
|
|
116
218
|
}
|
|
117
219
|
async context(options) {
|
|
118
220
|
this.ensureOpen();
|
|
@@ -124,76 +226,73 @@ class ZvecGrepService {
|
|
|
124
226
|
}
|
|
125
227
|
async contextWithTimings(options, timings) {
|
|
126
228
|
const request = normalizeContextRequest(options);
|
|
127
|
-
if (options.collection) {
|
|
128
|
-
return this.contextFromNamedCollection(request, options, timings);
|
|
129
|
-
}
|
|
130
229
|
const startRoot = resolveZvecGrepRoot(options.root ?? this.root);
|
|
131
230
|
if (options.rg) {
|
|
132
231
|
return this.contextFromRg(startRoot, request, options, timings);
|
|
133
232
|
}
|
|
134
|
-
|
|
135
|
-
const nearest =
|
|
233
|
+
assertNearestWorkspaceHomeUnlocked(startRoot, "context");
|
|
234
|
+
const nearest = findNearestWorkspaceIndex(startRoot);
|
|
136
235
|
if (nearest) {
|
|
137
236
|
const { location, info } = nearest;
|
|
138
237
|
if (info.indexPolicy === "disabled") {
|
|
139
|
-
throw
|
|
238
|
+
throw workspaceIndexDisabledError(location.root);
|
|
140
239
|
}
|
|
141
|
-
if (!
|
|
142
|
-
throw
|
|
240
|
+
if (!isWorkspaceIndexed(info) || !hasWorkspaceIndex(location)) {
|
|
241
|
+
throw workspaceIndexMissingError(location.root, info.indexPolicy ?? "enabled");
|
|
143
242
|
}
|
|
144
243
|
if (options.autoUpdate !== false) {
|
|
145
|
-
await this.
|
|
244
|
+
await this.refreshWorkspaceIndexForContext(location, options, timings);
|
|
146
245
|
}
|
|
147
|
-
return await withHomeReadLock(location.home, "context", () => this.
|
|
246
|
+
return await withHomeReadLock(location.home, "context", () => this.contextFromWorkspaceIndex(location, request, options, timings));
|
|
148
247
|
}
|
|
149
|
-
throw
|
|
248
|
+
throw workspaceIndexMissingError(startRoot, "undecided");
|
|
150
249
|
}
|
|
151
250
|
async info(options = {}) {
|
|
152
251
|
this.ensureOpen();
|
|
153
252
|
const startRoot = resolveZvecGrepRoot(options.root ?? this.root);
|
|
154
|
-
|
|
155
|
-
const nearest =
|
|
253
|
+
assertNearestWorkspaceHomeUnlocked(startRoot, "info");
|
|
254
|
+
const nearest = findNearestWorkspaceIndex(startRoot);
|
|
156
255
|
if (!nearest) {
|
|
157
|
-
const location =
|
|
256
|
+
const location = workspaceIndexLocation(startRoot);
|
|
158
257
|
return {
|
|
159
258
|
root: startRoot,
|
|
160
259
|
indexed: false,
|
|
161
260
|
indexPolicy: "undecided",
|
|
162
261
|
home: location.home,
|
|
163
|
-
indexPath: location.
|
|
262
|
+
indexPath: location.indexPath,
|
|
164
263
|
source: "unindexed",
|
|
165
264
|
suggestion: "zg index or zg query --rg",
|
|
166
265
|
};
|
|
167
266
|
}
|
|
168
267
|
return await withHomeReadLock(nearest.location.home, "info", async () => {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
finally {
|
|
190
|
-
registry.close();
|
|
191
|
-
}
|
|
268
|
+
const workspaceIndex = readWorkspaceManifest(nearest.location.home);
|
|
269
|
+
const indexed = workspaceIndex !== null &&
|
|
270
|
+
workspaceIndex.indexPolicy !== "disabled" &&
|
|
271
|
+
isWorkspaceIndexed(workspaceIndex) &&
|
|
272
|
+
hasWorkspaceIndex(nearest.location);
|
|
273
|
+
return {
|
|
274
|
+
root: nearest.location.root,
|
|
275
|
+
indexed,
|
|
276
|
+
indexPolicy: workspaceIndex?.indexPolicy ?? "undecided",
|
|
277
|
+
home: nearest.location.home,
|
|
278
|
+
indexPath: nearest.location.indexPath,
|
|
279
|
+
source: indexed ? "index" : "unindexed",
|
|
280
|
+
workspaceIndex: workspaceIndex
|
|
281
|
+
? workspaceIndexInfoFromManifest(workspaceIndex)
|
|
282
|
+
: undefined,
|
|
283
|
+
status: indexed && options.includeStatus !== false
|
|
284
|
+
? await workspaceIndexStatus(workspaceIndex, nearest.location)
|
|
285
|
+
: null,
|
|
286
|
+
suggestion: workspaceInfoSuggestion(workspaceIndex),
|
|
287
|
+
};
|
|
192
288
|
});
|
|
193
289
|
}
|
|
194
290
|
async close() {
|
|
195
291
|
const models = new Set([
|
|
196
|
-
...(this.embeddingModel
|
|
292
|
+
...(this.embeddingModel &&
|
|
293
|
+
this.options.embeddingModelOwnership !== "borrowed"
|
|
294
|
+
? [this.embeddingModel]
|
|
295
|
+
: []),
|
|
197
296
|
...this.recoveredEmbeddingModels.values(),
|
|
198
297
|
...this.retiredEmbeddingModels,
|
|
199
298
|
]);
|
|
@@ -204,248 +303,83 @@ class ZvecGrepService {
|
|
|
204
303
|
}
|
|
205
304
|
this.closed = true;
|
|
206
305
|
}
|
|
207
|
-
async
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
registry.close();
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
async collectionInfo(name) {
|
|
220
|
-
this.ensureOpen();
|
|
221
|
-
return await withHomeReadLock(serviceHome(this.options), "collections.info", async () => {
|
|
222
|
-
const registry = this.createRegistry(true);
|
|
223
|
-
try {
|
|
224
|
-
return registry.get(name);
|
|
225
|
-
}
|
|
226
|
-
finally {
|
|
227
|
-
registry.close();
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
async collectionStatus(name) {
|
|
232
|
-
this.ensureOpen();
|
|
233
|
-
return await withHomeReadLock(serviceHome(this.options), "collections.status", async () => {
|
|
234
|
-
const registry = this.createRegistry(true);
|
|
235
|
-
try {
|
|
236
|
-
return await registry.status(name);
|
|
237
|
-
}
|
|
238
|
-
finally {
|
|
239
|
-
registry.close();
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
async indexCollection(name, paths, options = {}) {
|
|
244
|
-
this.ensureOpen();
|
|
245
|
-
const home = serviceHome(this.options);
|
|
246
|
-
return await this.withEmbeddingModelOperation(() => withHomeWriteLock(home, options.rebuild ? "collections.index.rebuild" : "collections.index", async () => {
|
|
247
|
-
const existing = readCollectionInfo(home, name);
|
|
248
|
-
const embeddingModel = this.embeddingModelForIndex(existing, home, "collections.index");
|
|
249
|
-
const registry = this.createRegistry(false, embeddingModel);
|
|
250
|
-
try {
|
|
251
|
-
const requestedRootPaths = paths === undefined
|
|
252
|
-
? undefined
|
|
253
|
-
: Array.isArray(paths)
|
|
254
|
-
? paths
|
|
255
|
-
: [paths];
|
|
256
|
-
const rootPaths = resolveIndexRootPaths(existing, requestedRootPaths, this.root, {
|
|
257
|
-
resetPaths: options.resetPaths === true,
|
|
258
|
-
includePaths: options.includePaths,
|
|
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,
|
|
270
|
-
});
|
|
271
|
-
if (options.rebuild) {
|
|
272
|
-
registry.remove(name);
|
|
273
|
-
}
|
|
274
|
-
const existingAfterRebuild = registry.get(name);
|
|
275
|
-
if (isCollectionIndexed(existingAfterRebuild)) {
|
|
276
|
-
assertCollectionEmbeddingMatchesCurrentModel(existingAfterRebuild, embeddingModel, "zg collections index <name> --rebuild");
|
|
277
|
-
}
|
|
278
|
-
registry.prepareIndex(name, rootPaths);
|
|
279
|
-
return await registry.open(name).index({
|
|
280
|
-
embeddingConcurrency: options.embeddingConcurrency,
|
|
281
|
-
onProgress: options.onProgress,
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
finally {
|
|
285
|
-
registry.close();
|
|
286
|
-
}
|
|
287
|
-
}));
|
|
288
|
-
}
|
|
289
|
-
async removeCollection(name) {
|
|
290
|
-
this.ensureOpen();
|
|
291
|
-
return await withHomeWriteLock(serviceHome(this.options), "collections.remove", async () => {
|
|
292
|
-
const registry = this.createRegistry(false);
|
|
293
|
-
try {
|
|
294
|
-
return registry.remove(name);
|
|
295
|
-
}
|
|
296
|
-
finally {
|
|
297
|
-
registry.close();
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
async contextFromAnonymousIndex(location, request, options, timings) {
|
|
302
|
-
const registry = new CollectionRegistry(location.home, undefined, true);
|
|
306
|
+
async contextFromWorkspaceIndex(location, request, options, timings) {
|
|
307
|
+
const info = readWorkspaceManifest(location.home);
|
|
308
|
+
if (!info) {
|
|
309
|
+
throw new EngineError("Workspace index manifest not found", {
|
|
310
|
+
code: "ZVEC_GREP.ENGINE.MANIFEST.NOT_FOUND",
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
const workspaceIndex = this.openWorkspaceIndexForSearch(info, request, location, info.embeddingRuntime);
|
|
303
314
|
try {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
try {
|
|
312
|
-
return await this.contextFromCollection({
|
|
313
|
-
root: location.root,
|
|
314
|
-
request,
|
|
315
|
-
collection,
|
|
316
|
-
anonymous: true,
|
|
317
|
-
options,
|
|
318
|
-
timings,
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
finally {
|
|
322
|
-
collection.close();
|
|
323
|
-
}
|
|
315
|
+
return await contextFromOpenWorkspaceIndex({
|
|
316
|
+
root: location.root,
|
|
317
|
+
request,
|
|
318
|
+
workspaceIndex,
|
|
319
|
+
options,
|
|
320
|
+
timings,
|
|
321
|
+
});
|
|
324
322
|
}
|
|
325
323
|
finally {
|
|
326
|
-
|
|
324
|
+
workspaceIndex.close();
|
|
327
325
|
}
|
|
328
326
|
}
|
|
329
|
-
async
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (!needsRefresh) {
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
await timings.time("auto_update", () => withHomeWriteLock(location.home, "context.refresh", async () => {
|
|
344
|
-
const existing = readCollectionInfo(location.home, ANONYMOUS_COLLECTION_NAME);
|
|
345
|
-
if (!existing) {
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
const stillNeedsRefresh = await timings.time("refresh_status_scan", () => collectionNeedsRefresh(location.home, ANONYMOUS_COLLECTION_NAME));
|
|
349
|
-
if (!stillNeedsRefresh) {
|
|
327
|
+
async refreshWorkspaceIndexForContext(location, options, timings) {
|
|
328
|
+
const daemonWritePermit = assertDaemonWriteAllowed(location.root, this.options.daemonInstanceToken);
|
|
329
|
+
try {
|
|
330
|
+
const needsRefresh = await timings.time("status_scan", () => withHomeReadLock(location.home, "context.status", async () => {
|
|
331
|
+
const manifest = readWorkspaceManifest(location.home);
|
|
332
|
+
const status = manifest
|
|
333
|
+
? await workspaceIndexStatus(manifest, location)
|
|
334
|
+
: null;
|
|
335
|
+
return indexStatusNeedsRefresh(status);
|
|
336
|
+
}));
|
|
337
|
+
if (!needsRefresh) {
|
|
350
338
|
return;
|
|
351
339
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
timings.addEntries(result.timings, "auto_update_");
|
|
361
|
-
}
|
|
362
|
-
finally {
|
|
363
|
-
registry.close();
|
|
364
|
-
}
|
|
365
|
-
}));
|
|
366
|
-
}
|
|
367
|
-
async contextFromNamedCollection(request, options, timings) {
|
|
368
|
-
const collectionName = options.collection;
|
|
369
|
-
return await withHomeReadLock(serviceHome(this.options), "collection.context", async () => {
|
|
370
|
-
const registry = this.createRegistry(true, undefined);
|
|
371
|
-
try {
|
|
372
|
-
const info = registry.get(collectionName);
|
|
373
|
-
if (!info) {
|
|
374
|
-
throw new EngineError("Collection not found", {
|
|
375
|
-
code: "ZVEC_GREP.ENGINE.COLLECTION.NOT_FOUND",
|
|
376
|
-
context: errorDetails([collectionDetail(collectionName)]),
|
|
377
|
-
});
|
|
340
|
+
await timings.time("auto_update", () => withHomeWriteLock(location.home, "context.refresh", async () => {
|
|
341
|
+
const existing = readWorkspaceManifest(location.home);
|
|
342
|
+
if (!existing) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const stillNeedsRefresh = await timings.time("refresh_status_scan", () => workspaceIndexNeedsRefresh(location));
|
|
346
|
+
if (!stillNeedsRefresh) {
|
|
347
|
+
return;
|
|
378
348
|
}
|
|
379
|
-
const
|
|
380
|
-
const
|
|
381
|
-
|
|
349
|
+
const workspaceRuntime = existing.embeddingRuntime;
|
|
350
|
+
const embeddingModel = this.embeddingModelForIndex(existing, "context.refresh", workspaceRuntime);
|
|
351
|
+
assertWorkspaceEndpointMatchesCurrentRuntime(existing, workspaceRuntime, effectiveEmbeddingRuntime(this.options, embeddingModel, workspaceRuntime), "zg index --endpoint <url> --rebuild");
|
|
352
|
+
assertWorkspaceEmbeddingMatchesCurrentModel(existing, embeddingModel, "zg index --rebuild");
|
|
353
|
+
const workspaceIndex = new WorkspaceIndex(existing, embeddingModel, false, location.filesPath);
|
|
382
354
|
try {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
collection,
|
|
387
|
-
anonymous: false,
|
|
388
|
-
options: {
|
|
389
|
-
...options,
|
|
390
|
-
},
|
|
391
|
-
timings,
|
|
355
|
+
const result = await workspaceIndex.index({
|
|
356
|
+
embeddingConcurrency: options.embeddingConcurrency,
|
|
357
|
+
onProgress: options.onAutoUpdateProgress,
|
|
392
358
|
});
|
|
359
|
+
timings.addEntries(result.timings, "auto_update_");
|
|
393
360
|
}
|
|
394
361
|
finally {
|
|
395
|
-
|
|
362
|
+
workspaceIndex.close();
|
|
396
363
|
}
|
|
397
|
-
}
|
|
398
|
-
finally {
|
|
399
|
-
registry.close();
|
|
400
|
-
}
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
async contextFromCollection(input) {
|
|
404
|
-
const searches = [];
|
|
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) {
|
|
410
|
-
const search = await input.collection.searchPlan({
|
|
411
|
-
routes: group.routes,
|
|
412
|
-
limit,
|
|
413
|
-
trace: input.options.trace,
|
|
414
|
-
preferSymbol: input.options.preferSymbol,
|
|
415
|
-
symbolTypes: input.options.symbolTypes,
|
|
416
|
-
includePaths: input.options.includePaths,
|
|
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,
|
|
422
|
-
modifiedAfter: input.options.modifiedAfter,
|
|
423
|
-
modifiedBefore: input.options.modifiedBefore,
|
|
424
|
-
});
|
|
425
|
-
input.timings.addEntries(search.timings);
|
|
426
|
-
searches.push(search);
|
|
364
|
+
}));
|
|
427
365
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
routes: searches.flatMap((search) => search.plan.routes),
|
|
446
|
-
},
|
|
447
|
-
},
|
|
448
|
-
};
|
|
366
|
+
finally {
|
|
367
|
+
daemonWritePermit?.release();
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async contextFromWriterWorkspaceIndex(root, workspaceIndex, options) {
|
|
371
|
+
return await this.withEmbeddingModelOperation(async () => {
|
|
372
|
+
const timings = new TimingCollector();
|
|
373
|
+
const request = normalizeContextRequest(options);
|
|
374
|
+
const result = await timings.time("total", () => contextFromOpenWorkspaceIndex({
|
|
375
|
+
root,
|
|
376
|
+
request,
|
|
377
|
+
workspaceIndex,
|
|
378
|
+
options: { ...options, autoUpdate: false },
|
|
379
|
+
timings,
|
|
380
|
+
}));
|
|
381
|
+
return withContextTimings(result, timings);
|
|
382
|
+
});
|
|
449
383
|
}
|
|
450
384
|
async contextFromRg(root, request, options, timings) {
|
|
451
385
|
let rgResult;
|
|
@@ -497,60 +431,68 @@ class ZvecGrepService {
|
|
|
497
431
|
},
|
|
498
432
|
};
|
|
499
433
|
}
|
|
500
|
-
|
|
501
|
-
return new
|
|
502
|
-
}
|
|
503
|
-
openCollectionForSearch(info, request, registryHome) {
|
|
504
|
-
return new Collection(info, this.embeddingModelForSearch(indexedEmbeddingSchema(info), request, registryHome), true, join(registryHome, "files.zvec"));
|
|
434
|
+
openWorkspaceIndexForSearch(info, request, location, workspaceRuntime) {
|
|
435
|
+
return new WorkspaceIndex(info, this.embeddingModelForSearch(indexedEmbeddingSchema(info), request, workspaceRuntime, info), true, location.filesPath);
|
|
505
436
|
}
|
|
506
|
-
embeddingModelForSearch(schema, request,
|
|
437
|
+
embeddingModelForSearch(schema, request, workspaceRuntime, info) {
|
|
507
438
|
if (!request.routes.some((route) => route.mode === "vector")) {
|
|
508
439
|
return undefined;
|
|
509
440
|
}
|
|
441
|
+
const identity = {
|
|
442
|
+
provider: schema.provider,
|
|
443
|
+
name: schema.model,
|
|
444
|
+
};
|
|
445
|
+
const effectiveRuntime = resolveEmbeddingRuntimeOptions(embeddingModelReference(identity), this.options, workspaceRuntime, readGlobalConfig());
|
|
446
|
+
assertSearchEndpointMatchesWorkspace(info, workspaceRuntime, effectiveRuntime);
|
|
510
447
|
if (this.embeddingModel) {
|
|
511
448
|
return this.embeddingModel;
|
|
512
449
|
}
|
|
513
|
-
return this.recoverEmbeddingModel(schema,
|
|
450
|
+
return this.recoverEmbeddingModel(schema, workspaceRuntime);
|
|
514
451
|
}
|
|
515
|
-
embeddingModelForIndex(existing,
|
|
516
|
-
if (
|
|
452
|
+
embeddingModelForIndex(existing, operation, workspaceRuntime = {}) {
|
|
453
|
+
if (isWorkspaceIndexed(existing) &&
|
|
517
454
|
!this.embeddingModel &&
|
|
518
455
|
!this.options.embedding) {
|
|
519
|
-
return this.recoverEmbeddingModel(existing.embedding,
|
|
456
|
+
return this.recoverEmbeddingModel(existing.embedding, workspaceRuntime);
|
|
520
457
|
}
|
|
521
|
-
const
|
|
522
|
-
|
|
458
|
+
const config = readGlobalConfig();
|
|
459
|
+
const reference = resolveEmbeddingReference({
|
|
460
|
+
explicit: this.options.embedding,
|
|
461
|
+
globalDefault: config.defaults?.embedding,
|
|
462
|
+
fallback: this.options.defaultEmbedding === true
|
|
523
463
|
? DEFAULT_LOCAL_EMBEDDING
|
|
524
|
-
: undefined
|
|
464
|
+
: undefined,
|
|
465
|
+
});
|
|
466
|
+
const referenceIdentity = reference
|
|
467
|
+
? parseEmbeddingModelReference(reference)
|
|
468
|
+
: undefined;
|
|
469
|
+
const selectedWorkspaceRuntime = isWorkspaceIndexed(existing) &&
|
|
470
|
+
referenceIdentity &&
|
|
471
|
+
existing.embedding.provider !== referenceIdentity.provider
|
|
472
|
+
? {}
|
|
473
|
+
: workspaceRuntime;
|
|
525
474
|
return (this.embeddingModel ??
|
|
526
475
|
(reference
|
|
527
|
-
? this.embeddingModelFromReference(reference,
|
|
476
|
+
? this.embeddingModelFromReference(reference, config, selectedWorkspaceRuntime)
|
|
528
477
|
: undefined) ??
|
|
529
|
-
this.configuredEmbeddingModel(registryHome) ??
|
|
530
478
|
this.requireEmbeddingModel(operation));
|
|
531
479
|
}
|
|
532
|
-
recoverEmbeddingModel(schema,
|
|
480
|
+
recoverEmbeddingModel(schema, workspaceRuntime = {}) {
|
|
533
481
|
const config = readGlobalConfig();
|
|
534
|
-
const
|
|
535
|
-
const key = `${schema.provider}/${schema.model}/${providerOptionsFingerprint(options)}`;
|
|
536
|
-
return this.cachedEmbeddingModel(key, () => createEmbeddingModel({
|
|
482
|
+
const identity = {
|
|
537
483
|
provider: schema.provider,
|
|
538
|
-
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
}
|
|
549
|
-
embeddingModelFromReference(reference, registryHome, config = readGlobalConfig()) {
|
|
550
|
-
const provider = providerFromReference(reference);
|
|
551
|
-
const options = providerOptions(this.options, this.root, registryHome, provider, config);
|
|
484
|
+
name: schema.model,
|
|
485
|
+
};
|
|
486
|
+
const reference = embeddingModelReference(identity);
|
|
487
|
+
const options = providerOptions(this.options, identity, config, workspaceRuntime);
|
|
488
|
+
const key = `${reference}/${providerOptionsFingerprint(options)}`;
|
|
489
|
+
return this.cachedEmbeddingModel(key, () => createServiceEmbeddingModel(reference, options, this.options));
|
|
490
|
+
}
|
|
491
|
+
embeddingModelFromReference(reference, config = readGlobalConfig(), workspaceRuntime = {}) {
|
|
492
|
+
const identity = parseEmbeddingModelReference(reference);
|
|
493
|
+
const options = providerOptions(this.options, identity, config, workspaceRuntime);
|
|
552
494
|
const key = `configured/${reference}/${providerOptionsFingerprint(options)}`;
|
|
553
|
-
return this.cachedEmbeddingModel(key, () =>
|
|
495
|
+
return this.cachedEmbeddingModel(key, () => createServiceEmbeddingModel(reference, options, this.options));
|
|
554
496
|
}
|
|
555
497
|
cachedEmbeddingModel(key, create) {
|
|
556
498
|
const cached = this.recoveredEmbeddingModels.get(key);
|
|
@@ -617,6 +559,66 @@ class ZvecGrepService {
|
|
|
617
559
|
}
|
|
618
560
|
}
|
|
619
561
|
}
|
|
562
|
+
async function contextFromOpenWorkspaceIndex(input) {
|
|
563
|
+
const searches = [];
|
|
564
|
+
const groups = input.options.fuse
|
|
565
|
+
? [
|
|
566
|
+
{
|
|
567
|
+
id: "Q1",
|
|
568
|
+
query: input.request.displayQuery,
|
|
569
|
+
role: input.request.groups.some((group) => group.role === "primary")
|
|
570
|
+
? "primary"
|
|
571
|
+
: "supplemental",
|
|
572
|
+
routes: input.request.routes,
|
|
573
|
+
},
|
|
574
|
+
]
|
|
575
|
+
: input.request.groups;
|
|
576
|
+
const limit = contextGroupLimit(input.options.limit, groups.length);
|
|
577
|
+
for (const group of groups) {
|
|
578
|
+
const search = await input.workspaceIndex.searchPlan({
|
|
579
|
+
routes: group.routes,
|
|
580
|
+
limit,
|
|
581
|
+
trace: input.options.trace,
|
|
582
|
+
preferSymbol: input.options.preferSymbol,
|
|
583
|
+
symbolTypes: input.options.symbolTypes,
|
|
584
|
+
includePaths: input.options.includePaths,
|
|
585
|
+
excludePaths: input.options.excludePaths,
|
|
586
|
+
globs: input.options.globs,
|
|
587
|
+
insensitiveGlobs: input.options.insensitiveGlobs,
|
|
588
|
+
fileTypes: input.options.fileTypes,
|
|
589
|
+
excludedFileTypes: input.options.excludedFileTypes,
|
|
590
|
+
modifiedAfter: input.options.modifiedAfter,
|
|
591
|
+
modifiedBefore: input.options.modifiedBefore,
|
|
592
|
+
});
|
|
593
|
+
input.timings.addEntries(search.timings);
|
|
594
|
+
searches.push(search);
|
|
595
|
+
}
|
|
596
|
+
const items = selectAndRankContextItems(searches.flatMap((search, index) => searchPlanToContextItems(search, input.root, groups[index])), groups.filter((group) => group.role === "primary").map((group) => group.id));
|
|
597
|
+
return {
|
|
598
|
+
query: input.request.displayQuery,
|
|
599
|
+
root: input.root,
|
|
600
|
+
source: "index",
|
|
601
|
+
coverage: "ranked_sample",
|
|
602
|
+
workspaceIndex: {
|
|
603
|
+
id: input.workspaceIndex.info.id,
|
|
604
|
+
name: input.workspaceIndex.info.name,
|
|
605
|
+
path: input.workspaceIndex.info.path,
|
|
606
|
+
},
|
|
607
|
+
items,
|
|
608
|
+
diagnostics: {
|
|
609
|
+
emptyReason: items.length === 0 ? "no_matches" : undefined,
|
|
610
|
+
index: {
|
|
611
|
+
hitsReturned: items.length,
|
|
612
|
+
queryGroups: groups.map((group) => ({
|
|
613
|
+
id: group.id,
|
|
614
|
+
query: group.query,
|
|
615
|
+
role: group.role,
|
|
616
|
+
})),
|
|
617
|
+
routes: searches.flatMap((search) => search.plan.routes),
|
|
618
|
+
},
|
|
619
|
+
},
|
|
620
|
+
};
|
|
621
|
+
}
|
|
620
622
|
async function withHomeReadLock(home, operation, task) {
|
|
621
623
|
const lock = acquireHomeLock(home, "read", operation);
|
|
622
624
|
try {
|
|
@@ -635,22 +637,12 @@ async function withHomeWriteLock(home, operation, task) {
|
|
|
635
637
|
lock.release();
|
|
636
638
|
}
|
|
637
639
|
}
|
|
638
|
-
async function
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
finally {
|
|
645
|
-
registry.close();
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
function collectionIndexStatusNeedsRefresh(status) {
|
|
649
|
-
return (status.filesAdded > 0 ||
|
|
650
|
-
status.filesModified > 0 ||
|
|
651
|
-
status.filesDeleted > 0 ||
|
|
652
|
-
status.filesPending > 0 ||
|
|
653
|
-
status.filesFailed > 0);
|
|
640
|
+
async function workspaceIndexNeedsRefresh(location) {
|
|
641
|
+
const manifest = readWorkspaceManifest(location.home);
|
|
642
|
+
const status = manifest
|
|
643
|
+
? await workspaceIndexStatus(manifest, location)
|
|
644
|
+
: null;
|
|
645
|
+
return indexStatusNeedsRefresh(status);
|
|
654
646
|
}
|
|
655
647
|
function acquireHomeLock(home, mode, operation) {
|
|
656
648
|
return acquireReadWriteLock(homeLockPath(home), mode, { operation });
|
|
@@ -658,10 +650,10 @@ function acquireHomeLock(home, mode, operation) {
|
|
|
658
650
|
function assertHomeUnlocked(home, operation) {
|
|
659
651
|
assertNoWriteLock(homeLockPath(home), operation);
|
|
660
652
|
}
|
|
661
|
-
function
|
|
653
|
+
function assertNearestWorkspaceHomeUnlocked(start, operation) {
|
|
662
654
|
let current = resolve(start);
|
|
663
655
|
while (true) {
|
|
664
|
-
assertHomeUnlocked(
|
|
656
|
+
assertHomeUnlocked(workspaceHome(current), operation);
|
|
665
657
|
const parent = dirname(current);
|
|
666
658
|
if (parent === current) {
|
|
667
659
|
return;
|
|
@@ -672,9 +664,6 @@ function assertNearestAnonymousHomeUnlocked(start, operation) {
|
|
|
672
664
|
function homeLockPath(home) {
|
|
673
665
|
return join(home, "locks", "home");
|
|
674
666
|
}
|
|
675
|
-
function serviceHome(options) {
|
|
676
|
-
return options.home ?? defaultHome();
|
|
677
|
-
}
|
|
678
667
|
function rgEmptyReason(diagnostics) {
|
|
679
668
|
return diagnostics.missingPaths &&
|
|
680
669
|
diagnostics.missingPaths.length > 0 &&
|
|
@@ -696,36 +685,28 @@ function withContextTimings(result, timings) {
|
|
|
696
685
|
},
|
|
697
686
|
};
|
|
698
687
|
}
|
|
699
|
-
function
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
return { location, info };
|
|
706
|
-
}
|
|
707
|
-
const parent = dirname(current);
|
|
708
|
-
if (parent === current) {
|
|
709
|
-
return null;
|
|
710
|
-
}
|
|
711
|
-
current = parent;
|
|
712
|
-
}
|
|
688
|
+
function findNearestWorkspaceIndex(start) {
|
|
689
|
+
const location = findNearestWorkspace(start);
|
|
690
|
+
if (!location)
|
|
691
|
+
return null;
|
|
692
|
+
const info = readWorkspaceManifest(location.home);
|
|
693
|
+
return info ? { location, info } : null;
|
|
713
694
|
}
|
|
714
|
-
function
|
|
715
|
-
if (!
|
|
695
|
+
function workspaceInfoSuggestion(workspaceIndex) {
|
|
696
|
+
if (!workspaceIndex) {
|
|
716
697
|
return "zg index or zg query --rg";
|
|
717
698
|
}
|
|
718
|
-
if (
|
|
699
|
+
if (workspaceIndex.indexPolicy === "disabled") {
|
|
719
700
|
return "zg query --rg";
|
|
720
701
|
}
|
|
721
|
-
if (!
|
|
702
|
+
if (!isWorkspaceIndexed(workspaceIndex)) {
|
|
722
703
|
return "zg index";
|
|
723
704
|
}
|
|
724
705
|
return undefined;
|
|
725
706
|
}
|
|
726
|
-
function
|
|
727
|
-
return new EngineError("No
|
|
728
|
-
code: "ZVEC_GREP.ENGINE.SERVICE.
|
|
707
|
+
function workspaceIndexMissingError(root, policy) {
|
|
708
|
+
return new EngineError("No zvec-grep index found for this workspace", {
|
|
709
|
+
code: "ZVEC_GREP.ENGINE.SERVICE.WORKSPACE_INDEX_NOT_FOUND",
|
|
729
710
|
context: errorDetails([
|
|
730
711
|
detail("root", root),
|
|
731
712
|
detail("policy", policy),
|
|
@@ -738,9 +719,9 @@ function anonymousIndexMissingError(root, policy) {
|
|
|
738
719
|
]),
|
|
739
720
|
});
|
|
740
721
|
}
|
|
741
|
-
function
|
|
742
|
-
return new EngineError("
|
|
743
|
-
code: "ZVEC_GREP.ENGINE.SERVICE.
|
|
722
|
+
function workspaceIndexDisabledError(root) {
|
|
723
|
+
return new EngineError("The zvec-grep index is disabled for this workspace", {
|
|
724
|
+
code: "ZVEC_GREP.ENGINE.SERVICE.WORKSPACE_INDEX_DISABLED",
|
|
744
725
|
context: errorDetails([
|
|
745
726
|
detail("root", root),
|
|
746
727
|
detail("policy", "disabled"),
|
|
@@ -836,117 +817,216 @@ function applyRootPathOverrides(rootPath, options) {
|
|
|
836
817
|
follow: options.follow ?? normalized.follow,
|
|
837
818
|
};
|
|
838
819
|
}
|
|
839
|
-
function
|
|
840
|
-
const
|
|
820
|
+
function prepareWorkspaceManifest(location, existing, rootPaths, embeddingModel, embeddingRuntime) {
|
|
821
|
+
const now = Date.now();
|
|
822
|
+
return {
|
|
823
|
+
manifestVersion: CURRENT_MANIFEST_VERSION,
|
|
824
|
+
id: existing?.id ?? randomUUID(),
|
|
825
|
+
name: existing?.name ?? workspaceDisplayName(location.root),
|
|
826
|
+
path: location.home,
|
|
827
|
+
rootPaths: validateRootPaths(rootPaths),
|
|
828
|
+
indexPolicy: "enabled",
|
|
829
|
+
embedding: currentEmbeddingSchema(embeddingModel),
|
|
830
|
+
indexVersion: CURRENT_INDEX_VERSION,
|
|
831
|
+
createdTime: existing?.createdTime ?? now,
|
|
832
|
+
updatedTime: now,
|
|
833
|
+
embeddingRuntime,
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
function currentEmbeddingSchema(embeddingModel) {
|
|
837
|
+
return {
|
|
838
|
+
provider: embeddingModel.info.provider,
|
|
839
|
+
model: embeddingModel.info.name,
|
|
840
|
+
dimension: embeddingModel.info.dimension,
|
|
841
|
+
metric: embeddingModel.info.metric,
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
function workspaceDisplayName(root) {
|
|
845
|
+
return basename(normalizePath(root)) || "workspace";
|
|
846
|
+
}
|
|
847
|
+
async function workspaceIndexStatus(info, location) {
|
|
848
|
+
if (info.indexPolicy === "disabled" ||
|
|
849
|
+
!isWorkspaceIndexed(info) ||
|
|
850
|
+
!hasWorkspaceIndex(location)) {
|
|
851
|
+
return null;
|
|
852
|
+
}
|
|
853
|
+
const workspaceIndex = new WorkspaceIndex(info, undefined, true, location.filesPath);
|
|
841
854
|
try {
|
|
842
|
-
return
|
|
855
|
+
return await workspaceIndex.status();
|
|
843
856
|
}
|
|
844
857
|
finally {
|
|
845
|
-
|
|
858
|
+
workspaceIndex.close();
|
|
846
859
|
}
|
|
847
860
|
}
|
|
848
861
|
function indexedEmbeddingSchema(info) {
|
|
849
|
-
if (
|
|
862
|
+
if (isWorkspaceIndexed(info)) {
|
|
850
863
|
return info.embedding;
|
|
851
864
|
}
|
|
852
865
|
throw new EngineError("zvec-grep index has not been built", {
|
|
853
866
|
code: "ZVEC_GREP.ENGINE.SERVICE.INDEX_MISSING",
|
|
854
867
|
context: errorDetails([
|
|
855
|
-
|
|
868
|
+
workspaceIndexDetail(info.name),
|
|
856
869
|
detail("hint", "Run zg index to build this index."),
|
|
857
870
|
]),
|
|
858
871
|
});
|
|
859
872
|
}
|
|
860
|
-
function providerOptions(options,
|
|
861
|
-
const
|
|
873
|
+
function providerOptions(options, identity, config = readGlobalConfig(), workspaceRuntime = {}) {
|
|
874
|
+
const reference = embeddingModelReference(identity);
|
|
875
|
+
const runtime = resolveEmbeddingRuntimeOptions(reference, options, workspaceRuntime, config);
|
|
862
876
|
return {
|
|
863
|
-
apiKey:
|
|
864
|
-
endpoint:
|
|
877
|
+
apiKey: runtime.apiKey,
|
|
878
|
+
endpoint: runtime.endpoint,
|
|
865
879
|
modelCacheDir: options.modelCacheDir ??
|
|
866
|
-
process.env.ZVEC_GREP_MODEL_CACHE ??
|
|
867
880
|
config.defaults?.modelCacheDir ??
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
881
|
+
process.env.ZVEC_GREP_MODEL_CACHE ??
|
|
882
|
+
join(options.home ?? defaultHome(), "models"),
|
|
883
|
+
device: runtime.device,
|
|
871
884
|
};
|
|
872
885
|
}
|
|
873
|
-
function
|
|
886
|
+
function createServiceEmbeddingModel(reference, modelOptions, serviceOptions) {
|
|
887
|
+
const model = createEmbeddingModel(reference, modelOptions);
|
|
888
|
+
if (model.info.provider !== "qwen") {
|
|
889
|
+
return model;
|
|
890
|
+
}
|
|
891
|
+
const endpoint = model.info.endpoint;
|
|
892
|
+
if (endpoint === undefined) {
|
|
893
|
+
throw new EngineError("Remote embedding model did not provide an endpoint", {
|
|
894
|
+
code: "ZVEC_GREP.ENGINE.MODELS.REMOTE_ENDPOINT_NOT_IMPLEMENTED",
|
|
895
|
+
context: `model=${model.info.reference}`,
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
const authorize = remoteEmbeddingAuthorizationGuard({
|
|
899
|
+
store: new RemoteEmbeddingAuthorizationStore({
|
|
900
|
+
signingKeyPath: serviceOptions.authorizationSigningKeyPath,
|
|
901
|
+
}),
|
|
902
|
+
});
|
|
903
|
+
const authorizedModel = {
|
|
904
|
+
info: model.info,
|
|
905
|
+
async embed(contents, embedOptions) {
|
|
906
|
+
await authorize({
|
|
907
|
+
provider: model.info.provider,
|
|
908
|
+
model: model.info.name,
|
|
909
|
+
endpoint,
|
|
910
|
+
purpose: embedOptions?.purpose ?? EmbeddingPurpose.Document,
|
|
911
|
+
contentKinds: contents.map((content) => content.kind),
|
|
912
|
+
contentCount: contents.length,
|
|
913
|
+
});
|
|
914
|
+
return await model.embed(contents, embedOptions);
|
|
915
|
+
},
|
|
916
|
+
dispose: () => model.dispose(),
|
|
917
|
+
};
|
|
918
|
+
return authorizedModel;
|
|
919
|
+
}
|
|
920
|
+
function parseEmbeddingModelReference(reference) {
|
|
874
921
|
const separator = reference.indexOf("/");
|
|
875
|
-
|
|
922
|
+
if (separator <= 0 || separator === reference.length - 1) {
|
|
923
|
+
throw new EngineError("Embedding model reference is invalid", {
|
|
924
|
+
code: "ZVEC_GREP.ENGINE.MODELS.EMBEDDING_INVALID_REFERENCE",
|
|
925
|
+
context: `reference=${reference}`,
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
return {
|
|
929
|
+
provider: reference.slice(0, separator),
|
|
930
|
+
name: reference.slice(separator + 1),
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
function embeddingModelReference(identity) {
|
|
934
|
+
return `${identity.provider}/${identity.name}`;
|
|
876
935
|
}
|
|
877
936
|
function providerOptionsFingerprint(options) {
|
|
878
|
-
|
|
879
|
-
.update(JSON.stringify([
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
937
|
+
const publicOptionsFingerprint = createHash("sha256")
|
|
938
|
+
.update(JSON.stringify([options.endpoint, options.modelCacheDir, options.device]))
|
|
939
|
+
.digest("hex");
|
|
940
|
+
return `${providerApiKeyIdentity(options.apiKey)}:${publicOptionsFingerprint}`;
|
|
941
|
+
}
|
|
942
|
+
function providerApiKeyIdentity(apiKey) {
|
|
943
|
+
return createHmac("sha256", PROVIDER_API_KEY_IDENTITY_SECRET)
|
|
944
|
+
.update(apiKey)
|
|
886
945
|
.digest("hex");
|
|
887
946
|
}
|
|
888
|
-
function
|
|
889
|
-
|
|
947
|
+
function effectiveEmbeddingRuntime(options, model, workspaceRuntime) {
|
|
948
|
+
const resolved = resolveEmbeddingRuntimeOptions(model.info.reference, options, workspaceRuntime, readGlobalConfig());
|
|
949
|
+
return {
|
|
950
|
+
apiKey: resolved.apiKey,
|
|
951
|
+
...((model.info.endpoint ?? resolved.endpoint)
|
|
952
|
+
? { endpoint: model.info.endpoint ?? resolved.endpoint }
|
|
953
|
+
: {}),
|
|
954
|
+
...(model.info.provider === "local"
|
|
955
|
+
? { device: resolved.device ?? "auto" }
|
|
956
|
+
: {}),
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
function runtimeForModelProvider(existing, model, workspaceRuntime) {
|
|
960
|
+
return isWorkspaceIndexed(existing) &&
|
|
961
|
+
existing.embedding.provider !== model.info.provider
|
|
962
|
+
? {}
|
|
963
|
+
: workspaceRuntime;
|
|
964
|
+
}
|
|
965
|
+
function embeddingRuntimeAfterSuccessfulIndex(existing, existingRuntime, model, effectiveRuntime, explicit) {
|
|
966
|
+
const sameProvider = isWorkspaceIndexed(existing) &&
|
|
967
|
+
existing.embedding.provider === model.info.provider;
|
|
968
|
+
const apiKey = model.info.provider === "local"
|
|
969
|
+
? undefined
|
|
970
|
+
: (explicit.apiKey ??
|
|
971
|
+
(sameProvider ? existingRuntime.apiKey : undefined));
|
|
972
|
+
return {
|
|
973
|
+
...(apiKey !== undefined ? { apiKey } : {}),
|
|
974
|
+
...(model.info.provider !== "local" &&
|
|
975
|
+
effectiveRuntime.endpoint !== undefined
|
|
976
|
+
? { endpoint: effectiveRuntime.endpoint }
|
|
977
|
+
: {}),
|
|
978
|
+
...(model.info.provider === "local"
|
|
979
|
+
? { device: effectiveRuntime.device ?? "auto" }
|
|
980
|
+
: {}),
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
function assertWorkspaceEndpointMatchesCurrentRuntime(info, workspaceRuntime, effectiveRuntime, rebuildCommand) {
|
|
984
|
+
if (!isWorkspaceIndexed(info) ||
|
|
985
|
+
workspaceRuntime.endpoint === effectiveRuntime.endpoint) {
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
throw new EngineError("Existing zvec-grep index uses a different embedding endpoint", {
|
|
989
|
+
code: "ZVEC_GREP.ENGINE.SERVICE.EMBEDDING_ENDPOINT_CHANGE_REQUIRES_REBUILD",
|
|
990
|
+
context: errorDetails([
|
|
991
|
+
workspaceIndexDetail(info.name),
|
|
992
|
+
detail("hint", `Run "${rebuildCommand}" to rebuild this index with the requested endpoint.`),
|
|
993
|
+
]),
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
function assertSearchEndpointMatchesWorkspace(info, workspaceRuntime, effectiveRuntime) {
|
|
997
|
+
if (workspaceRuntime.endpoint === effectiveRuntime.endpoint) {
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
throw new EngineError("Search cannot override the embedding endpoint of an existing index", {
|
|
1001
|
+
code: "ZVEC_GREP.ENGINE.SERVICE.SEARCH_ENDPOINT_CHANGE_REQUIRES_REBUILD",
|
|
1002
|
+
context: errorDetails([
|
|
1003
|
+
workspaceIndexDetail(info.name),
|
|
1004
|
+
detail("hint", 'Run "zg index --endpoint <url> --rebuild" before searching with this endpoint.'),
|
|
1005
|
+
]),
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
function assertWorkspaceEmbeddingMatchesCurrentModel(info, model, rebuildCommand) {
|
|
1009
|
+
if (!isWorkspaceIndexed(info)) {
|
|
890
1010
|
return;
|
|
891
1011
|
}
|
|
892
1012
|
const expected = info.embedding;
|
|
893
|
-
const changed = expected.provider !== model.
|
|
894
|
-
expected.model !== model.
|
|
895
|
-
expected.dimension !== model.dimension ||
|
|
896
|
-
expected.metric !== model.metric;
|
|
1013
|
+
const changed = expected.provider !== model.info.provider ||
|
|
1014
|
+
expected.model !== model.info.name ||
|
|
1015
|
+
expected.dimension !== model.info.dimension ||
|
|
1016
|
+
expected.metric !== model.info.metric;
|
|
897
1017
|
if (!changed) {
|
|
898
1018
|
return;
|
|
899
1019
|
}
|
|
900
1020
|
throw new EngineError("Existing zvec-grep index uses a different embedding model", {
|
|
901
1021
|
code: "ZVEC_GREP.ENGINE.SERVICE.EMBEDDING_SCHEMA_CHANGE_REQUIRES_REBUILD",
|
|
902
1022
|
context: errorDetails([
|
|
903
|
-
|
|
1023
|
+
workspaceIndexDetail(info.name),
|
|
904
1024
|
detail("existing", `${expected.provider}/${expected.model}`),
|
|
905
|
-
detail("requested",
|
|
1025
|
+
detail("requested", model.info.reference),
|
|
906
1026
|
detail("hint", `Run "${rebuildCommand}" to rebuild this index with the requested embedding model.`),
|
|
907
1027
|
]),
|
|
908
1028
|
});
|
|
909
1029
|
}
|
|
910
|
-
function modelCacheDir(options, root, registryHome) {
|
|
911
|
-
if (options.modelCacheDir || process.env.ZVEC_GREP_MODEL_CACHE) {
|
|
912
|
-
return options.modelCacheDir ?? process.env.ZVEC_GREP_MODEL_CACHE;
|
|
913
|
-
}
|
|
914
|
-
const globalCache = join(options.home ?? defaultHome(), "models");
|
|
915
|
-
const legacyCaches = [
|
|
916
|
-
registryHome ? join(registryHome, "models") : undefined,
|
|
917
|
-
join(anonymousHome(root), "models"),
|
|
918
|
-
];
|
|
919
|
-
const globalHasModel = directoryHasGguf(globalCache);
|
|
920
|
-
for (const legacyCache of legacyCaches) {
|
|
921
|
-
if (legacyCache && directoryHasGguf(legacyCache) && !globalHasModel) {
|
|
922
|
-
return legacyCache;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
if (globalHasModel || canWriteCacheDirectory(globalCache)) {
|
|
926
|
-
return globalCache;
|
|
927
|
-
}
|
|
928
|
-
return join(anonymousHome(root), "models");
|
|
929
|
-
}
|
|
930
|
-
function directoryHasGguf(path) {
|
|
931
|
-
try {
|
|
932
|
-
return readdirSync(path, { withFileTypes: true }).some((entry) => entry.isFile() && entry.name.endsWith(".gguf"));
|
|
933
|
-
}
|
|
934
|
-
catch {
|
|
935
|
-
return false;
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
function canWriteCacheDirectory(path) {
|
|
939
|
-
const probe = join(path, `.zvec-grep-write-test-${Date.now()}-${Math.random().toString(16).slice(2)}`);
|
|
940
|
-
try {
|
|
941
|
-
mkdirSync(path, { recursive: true });
|
|
942
|
-
writeFileSync(probe, "");
|
|
943
|
-
unlinkSync(probe);
|
|
944
|
-
return true;
|
|
945
|
-
}
|
|
946
|
-
catch {
|
|
947
|
-
return false;
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
1030
|
function contextGroupLimit(limit, groupCount) {
|
|
951
1031
|
if (limit !== undefined) {
|
|
952
1032
|
return limit;
|
|
@@ -1021,18 +1101,24 @@ function normalizeContextRoutes(routes) {
|
|
|
1021
1101
|
}
|
|
1022
1102
|
function contextGroups(primaryQueries, extraRoutes) {
|
|
1023
1103
|
return [
|
|
1024
|
-
...primaryQueries.map((query) => ({
|
|
1104
|
+
...primaryQueries.map((query, index) => ({
|
|
1105
|
+
id: `Q${index + 1}`,
|
|
1106
|
+
query,
|
|
1107
|
+
role: "primary",
|
|
1025
1108
|
routes: [
|
|
1026
1109
|
{ mode: "fts", query },
|
|
1027
1110
|
{ mode: "vector", query },
|
|
1028
1111
|
],
|
|
1029
1112
|
})),
|
|
1030
|
-
...extraRoutes.map((route) => ({
|
|
1113
|
+
...extraRoutes.map((route, index) => ({
|
|
1114
|
+
id: `Q${primaryQueries.length + index + 1}`,
|
|
1115
|
+
query: route.query,
|
|
1116
|
+
role: "supplemental",
|
|
1031
1117
|
routes: [route],
|
|
1032
1118
|
})),
|
|
1033
1119
|
];
|
|
1034
1120
|
}
|
|
1035
|
-
function searchPlanToContextItems(result, root) {
|
|
1121
|
+
function searchPlanToContextItems(result, root, group) {
|
|
1036
1122
|
return result.hits.map((hit) => {
|
|
1037
1123
|
const target = contextItemTarget(hit);
|
|
1038
1124
|
return {
|
|
@@ -1054,9 +1140,75 @@ function searchPlanToContextItems(result, root) {
|
|
|
1054
1140
|
metadata: hit.entity.metadata,
|
|
1055
1141
|
entityId: hit.entity.id,
|
|
1056
1142
|
trace: hit.trace,
|
|
1143
|
+
queryGroups: [
|
|
1144
|
+
{
|
|
1145
|
+
id: group.id,
|
|
1146
|
+
query: group.query,
|
|
1147
|
+
role: group.role,
|
|
1148
|
+
rank: hit.rank,
|
|
1149
|
+
matchedBy: hit.matchedBy,
|
|
1150
|
+
},
|
|
1151
|
+
],
|
|
1057
1152
|
};
|
|
1058
1153
|
});
|
|
1059
1154
|
}
|
|
1155
|
+
const DEFAULT_CONTEXT_DETAIL_LIMIT = 6;
|
|
1156
|
+
const CONTEXT_GROUP_RRF_K = 60;
|
|
1157
|
+
function selectAndRankContextItems(items, coverageGroupIds) {
|
|
1158
|
+
const deduped = new Map();
|
|
1159
|
+
for (const item of items) {
|
|
1160
|
+
const key = contextItemDedupeKey(item);
|
|
1161
|
+
const existing = deduped.get(key);
|
|
1162
|
+
if (existing) {
|
|
1163
|
+
existing.queryGroups = mergeContextQueryGroupMatches(existing.queryGroups ?? [], item.queryGroups ?? []);
|
|
1164
|
+
existing.matchedBy = mergedContextMatchedBy(existing.queryGroups);
|
|
1165
|
+
continue;
|
|
1166
|
+
}
|
|
1167
|
+
deduped.set(key, { ...item });
|
|
1168
|
+
}
|
|
1169
|
+
const candidates = [...deduped.values()];
|
|
1170
|
+
const globallyRanked = [...candidates].sort(compareContextGlobalRank);
|
|
1171
|
+
const selected = new Set();
|
|
1172
|
+
const detailed = [];
|
|
1173
|
+
for (const groupId of coverageGroupIds) {
|
|
1174
|
+
if (detailed.length >= DEFAULT_CONTEXT_DETAIL_LIMIT) {
|
|
1175
|
+
break;
|
|
1176
|
+
}
|
|
1177
|
+
const candidate = candidates
|
|
1178
|
+
.filter((item) => !selected.has(contextItemDedupeKey(item)))
|
|
1179
|
+
.filter((item) => contextQueryGroupMatch(item, groupId) !== undefined)
|
|
1180
|
+
.sort((left, right) => compareContextGroupRank(left, right, groupId))[0];
|
|
1181
|
+
if (!candidate) {
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1184
|
+
selected.add(contextItemDedupeKey(candidate));
|
|
1185
|
+
detailed.push({
|
|
1186
|
+
...candidate,
|
|
1187
|
+
selectionReason: "coverage",
|
|
1188
|
+
coverageGroup: groupId,
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
for (const candidate of globallyRanked) {
|
|
1192
|
+
if (detailed.length >= DEFAULT_CONTEXT_DETAIL_LIMIT) {
|
|
1193
|
+
break;
|
|
1194
|
+
}
|
|
1195
|
+
const key = contextItemDedupeKey(candidate);
|
|
1196
|
+
if (selected.has(key)) {
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
selected.add(key);
|
|
1200
|
+
detailed.push({
|
|
1201
|
+
...candidate,
|
|
1202
|
+
selectionReason: "global_fill",
|
|
1203
|
+
coverageGroup: undefined,
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
const additional = globallyRanked.filter((item) => !selected.has(contextItemDedupeKey(item)));
|
|
1207
|
+
return [...detailed, ...additional].map((item, index) => ({
|
|
1208
|
+
...item,
|
|
1209
|
+
rank: index + 1,
|
|
1210
|
+
}));
|
|
1211
|
+
}
|
|
1060
1212
|
function dedupeAndRerankContextItems(items) {
|
|
1061
1213
|
const seen = new Set();
|
|
1062
1214
|
const deduped = [];
|
|
@@ -1066,13 +1218,52 @@ function dedupeAndRerankContextItems(items) {
|
|
|
1066
1218
|
continue;
|
|
1067
1219
|
}
|
|
1068
1220
|
seen.add(key);
|
|
1069
|
-
deduped.push({
|
|
1070
|
-
...item,
|
|
1071
|
-
rank: deduped.length + 1,
|
|
1072
|
-
});
|
|
1221
|
+
deduped.push({ ...item, rank: deduped.length + 1 });
|
|
1073
1222
|
}
|
|
1074
1223
|
return deduped;
|
|
1075
1224
|
}
|
|
1225
|
+
function mergeContextQueryGroupMatches(left, right) {
|
|
1226
|
+
const matches = new Map(left.map((match) => [match.id, match]));
|
|
1227
|
+
for (const match of right) {
|
|
1228
|
+
const existing = matches.get(match.id);
|
|
1229
|
+
if (!existing || match.rank < existing.rank) {
|
|
1230
|
+
matches.set(match.id, match);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
return [...matches.values()].sort((a, b) => contextGroupNumber(a.id) - contextGroupNumber(b.id));
|
|
1234
|
+
}
|
|
1235
|
+
function mergedContextMatchedBy(matches) {
|
|
1236
|
+
const hasFts = matches.some((match) => match.matchedBy === "fts" || match.matchedBy === "fts+vector");
|
|
1237
|
+
const hasVector = matches.some((match) => match.matchedBy === "vector" || match.matchedBy === "fts+vector");
|
|
1238
|
+
return hasFts && hasVector ? "fts+vector" : hasFts ? "fts" : "vector";
|
|
1239
|
+
}
|
|
1240
|
+
function compareContextGlobalRank(left, right) {
|
|
1241
|
+
return (contextGlobalRrfScore(right) - contextGlobalRrfScore(left) ||
|
|
1242
|
+
contextBestGroupRank(left) - contextBestGroupRank(right) ||
|
|
1243
|
+
contextFirstGroupNumber(left) - contextFirstGroupNumber(right) ||
|
|
1244
|
+
contextItemDedupeKey(left).localeCompare(contextItemDedupeKey(right)));
|
|
1245
|
+
}
|
|
1246
|
+
function contextGlobalRrfScore(item) {
|
|
1247
|
+
return (item.queryGroups ?? []).reduce((score, match) => score + 1 / (CONTEXT_GROUP_RRF_K + match.rank), 0);
|
|
1248
|
+
}
|
|
1249
|
+
function compareContextGroupRank(left, right, groupId) {
|
|
1250
|
+
return (contextQueryGroupMatch(left, groupId).rank -
|
|
1251
|
+
contextQueryGroupMatch(right, groupId).rank ||
|
|
1252
|
+
compareContextGlobalRank(left, right));
|
|
1253
|
+
}
|
|
1254
|
+
function contextQueryGroupMatch(item, groupId) {
|
|
1255
|
+
return item.queryGroups?.find((match) => match.id === groupId);
|
|
1256
|
+
}
|
|
1257
|
+
function contextBestGroupRank(item) {
|
|
1258
|
+
return Math.min(...(item.queryGroups ?? []).map((match) => match.rank));
|
|
1259
|
+
}
|
|
1260
|
+
function contextFirstGroupNumber(item) {
|
|
1261
|
+
return Math.min(...(item.queryGroups ?? []).map((match) => contextGroupNumber(match.id)));
|
|
1262
|
+
}
|
|
1263
|
+
function contextGroupNumber(id) {
|
|
1264
|
+
const value = Number.parseInt(id.slice(1), 10);
|
|
1265
|
+
return Number.isFinite(value) ? value : Number.MAX_SAFE_INTEGER;
|
|
1266
|
+
}
|
|
1076
1267
|
function contextItemDedupeKey(item) {
|
|
1077
1268
|
if (item.entityId) {
|
|
1078
1269
|
return `entity:${item.entityId}`;
|