@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,12 +1,12 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
1
|
+
import { lstat, readFile } from "node:fs/promises";
|
|
2
|
+
import { workspaceIndexDetail, detail, EngineError, errorDetails, isEngineError, } from "../../errors.js";
|
|
3
3
|
import { sha256Bytes } from "../../utils/hash.js";
|
|
4
|
+
import { normalizePath } from "../../utils/path.js";
|
|
4
5
|
import { ConcurrentTiming, TimingCollector } from "../../utils/timing.js";
|
|
5
|
-
import {
|
|
6
|
-
import { scanRootPaths } from "./scanner/index.js";
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const DEFAULT_EMBEDDING_MAX_CONCURRENCY = 12;
|
|
6
|
+
import { extract, } from "../../extraction/index.js";
|
|
7
|
+
import { scanDirectoryPath, scanFilePath, scanRootPaths, } from "./scanner/index.js";
|
|
8
|
+
const APPROXIMATE_CHARS_PER_TOKEN = 2;
|
|
9
|
+
const CHUNK_OVERLAP_RATIO = 0.15;
|
|
10
10
|
const EMBEDDING_TRANSIENT_MAX_RETRIES = 3;
|
|
11
11
|
const EMBEDDING_RATE_LIMIT_MAX_RETRIES = 6;
|
|
12
12
|
const EMBEDDING_TRANSIENT_RETRY_BASE_DELAY_MS = 500;
|
|
@@ -15,33 +15,44 @@ const EMBEDDING_TRANSIENT_RETRY_MAX_DELAY_MS = 8000;
|
|
|
15
15
|
const EMBEDDING_RATE_LIMIT_RETRY_MAX_DELAY_MS = 30000;
|
|
16
16
|
const EMBEDDING_RETRY_JITTER_MS = 500;
|
|
17
17
|
const EMBEDDING_SUCCESS_STREAK_MIN = 4;
|
|
18
|
-
export async function
|
|
18
|
+
export async function indexWorkspace(ctx) {
|
|
19
19
|
try {
|
|
20
|
-
return await
|
|
20
|
+
return await indexWorkspaceUnchecked(ctx);
|
|
21
21
|
}
|
|
22
22
|
catch (error) {
|
|
23
|
-
throw toEngineError(error, "Indexing
|
|
24
|
-
code: "ZVEC_GREP.ENGINE.INDEXING.
|
|
25
|
-
context:
|
|
23
|
+
throw toEngineError(error, "Indexing workspace failed", {
|
|
24
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.WORKSPACE_FAILED",
|
|
25
|
+
context: workspaceIndexContext(ctx.workspaceIndex),
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
export async function
|
|
29
|
+
export async function indexWorkspacePaths(ctx, changedPaths) {
|
|
30
30
|
try {
|
|
31
|
-
|
|
31
|
+
return await indexWorkspacePathsUnchecked(ctx, changedPaths);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
throw toEngineError(error, "Indexing changed paths failed", {
|
|
35
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.WORKSPACE_FAILED",
|
|
36
|
+
context: workspaceIndexContext(ctx.workspaceIndex),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function getWorkspaceIndexStatus(workspaceIndex, storedFiles) {
|
|
41
|
+
try {
|
|
42
|
+
const scan = await scanRootPaths(workspaceIndex.id, workspaceIndex.rootPaths);
|
|
32
43
|
const diff = await computeDiffFromFiles(scan.files, storedFiles);
|
|
33
44
|
const pendingFiles = storedFiles.filter((file) => file.indexStatus?.indexedTime === null);
|
|
34
45
|
const failedFiles = pendingFiles.filter((file) => file.indexStatus?.error !== undefined);
|
|
35
46
|
const indexedFiles = storedFiles.filter((file) => file.indexStatus?.indexedTime !== undefined &&
|
|
36
47
|
file.indexStatus.indexedTime !== null);
|
|
37
48
|
const entitiesIndexed = indexedFiles.reduce((count, file) => count + (file.indexStatus?.entityCount ?? 0), 0);
|
|
49
|
+
const fragmentsTruncated = indexedFiles.reduce((count, file) => count + (file.indexStatus?.truncatedFragmentCount ?? 0), 0);
|
|
38
50
|
return {
|
|
39
|
-
collectionId: collection.id,
|
|
40
|
-
collectionName: collection.name,
|
|
41
51
|
filesScanned: scan.files.length,
|
|
42
52
|
filesStored: storedFiles.length,
|
|
43
53
|
filesIndexed: indexedFiles.length,
|
|
44
54
|
entitiesIndexed,
|
|
55
|
+
fragmentsTruncated,
|
|
45
56
|
filesPending: pendingFiles.length,
|
|
46
57
|
filesFailed: failedFiles.length,
|
|
47
58
|
filesAdded: diff.added.length,
|
|
@@ -56,36 +67,42 @@ export async function getCollectionIndexStatus(collection, storedFiles) {
|
|
|
56
67
|
};
|
|
57
68
|
}
|
|
58
69
|
catch (error) {
|
|
59
|
-
throw toEngineError(error, "Inspecting
|
|
70
|
+
throw toEngineError(error, "Inspecting workspace index status failed", {
|
|
60
71
|
code: "ZVEC_GREP.ENGINE.INDEXING.STATUS_FAILED",
|
|
61
|
-
context:
|
|
72
|
+
context: workspaceIndexContext(workspaceIndex),
|
|
62
73
|
});
|
|
63
74
|
}
|
|
64
75
|
}
|
|
65
|
-
async function
|
|
76
|
+
async function indexWorkspaceUnchecked(ctx) {
|
|
66
77
|
const start = Date.now();
|
|
67
78
|
const report = ctx.onProgress ?? (() => undefined);
|
|
68
79
|
const timings = new TimingCollector();
|
|
80
|
+
throwIfIndexCancelled(ctx);
|
|
69
81
|
const firstPass = await runIndexPass(ctx, report, "Scanning files...", timings);
|
|
70
82
|
const passes = [firstPass];
|
|
83
|
+
let progressBase;
|
|
71
84
|
if (firstPass.stats.filesFailed > 0) {
|
|
72
85
|
const failed = firstPass.stats.filesFailed;
|
|
86
|
+
progressBase = retryProgressBase(firstPass);
|
|
73
87
|
report({
|
|
74
88
|
phase: "scanning",
|
|
89
|
+
filesTotal: progressBase.filesTotal,
|
|
90
|
+
filesIndexed: progressBase.filesSucceeded,
|
|
75
91
|
detail: `Retrying ${failed} failed ${failed === 1 ? "file" : "files"}...`,
|
|
76
92
|
});
|
|
77
|
-
passes.push(await runIndexPass(ctx, report, "Scanning retry candidates...", timings));
|
|
93
|
+
passes.push(await runIndexPass(ctx, report, "Scanning retry candidates...", timings, progressBase));
|
|
78
94
|
}
|
|
79
95
|
const finalPass = passes[passes.length - 1];
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
throwIfIndexCancelled(ctx);
|
|
97
|
+
reportIndexFinalizing(ctx, report, finalPass, progressBase);
|
|
98
|
+
await timings.time("index_optimize", () => optimizeStorage(ctx));
|
|
82
99
|
const result = buildIndexResult(ctx, passes, Date.now() - start, timings);
|
|
83
100
|
if (result.filesFailed > 0) {
|
|
84
101
|
report({ phase: "done", detail: "Indexing completed with failed files" });
|
|
85
102
|
throw new EngineError(`Indexing completed with ${result.filesFailed} failed ${result.filesFailed === 1 ? "file" : "files"}`, {
|
|
86
103
|
code: "ZVEC_GREP.ENGINE.INDEXING.FILES_FAILED",
|
|
87
104
|
context: errorDetails([
|
|
88
|
-
|
|
105
|
+
workspaceIndexDetail(ctx.workspaceIndex.name),
|
|
89
106
|
detail("filesFailed", result.filesFailed),
|
|
90
107
|
detail("filesScanned", result.filesScanned),
|
|
91
108
|
detail("failedFiles", summarizeFailedFiles(finalPass.stats.failedFiles)),
|
|
@@ -99,18 +116,78 @@ async function indexCollectionUnchecked(ctx) {
|
|
|
99
116
|
report({ phase: "done", detail: "Indexing complete" });
|
|
100
117
|
return result;
|
|
101
118
|
}
|
|
102
|
-
async function
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
const
|
|
119
|
+
async function indexWorkspacePathsUnchecked(ctx, changedPaths) {
|
|
120
|
+
const start = Date.now();
|
|
121
|
+
const report = ctx.onProgress ?? (() => undefined);
|
|
122
|
+
const timings = new TimingCollector();
|
|
123
|
+
const normalizedPaths = [...new Set(changedPaths.map(normalizePath))];
|
|
124
|
+
throwIfIndexCancelled(ctx);
|
|
125
|
+
const firstPass = await runPathIndexPass(ctx, report, normalizedPaths, timings);
|
|
126
|
+
const passes = [firstPass];
|
|
127
|
+
let progressBase;
|
|
128
|
+
if (firstPass.stats.filesFailed > 0) {
|
|
129
|
+
progressBase = retryProgressBase(firstPass);
|
|
130
|
+
passes.push(await runPathIndexPass(ctx, report, normalizedPaths, timings, progressBase));
|
|
131
|
+
}
|
|
132
|
+
const finalPass = passes[passes.length - 1];
|
|
133
|
+
throwIfIndexCancelled(ctx);
|
|
134
|
+
reportIndexFinalizing(ctx, report, finalPass, progressBase);
|
|
135
|
+
await timings.time("index_optimize", () => optimizeStorage(ctx));
|
|
136
|
+
const result = buildIndexResult(ctx, passes, Date.now() - start, timings);
|
|
137
|
+
if (result.filesFailed > 0) {
|
|
138
|
+
throw new EngineError(`Indexing completed with ${result.filesFailed} failed files`, {
|
|
139
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.FILES_FAILED",
|
|
140
|
+
context: workspaceIndexContext(ctx.workspaceIndex),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
report({ phase: "done", detail: "Indexing complete" });
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
async function runPathIndexPass(ctx, report, changedPaths, timings, progressBase) {
|
|
147
|
+
reportScanning(report, "Scanning changed paths...", progressBase);
|
|
148
|
+
const scanned = await timings.time("index_scan_paths", async () => {
|
|
149
|
+
const files = [];
|
|
150
|
+
for (const path of changedPaths) {
|
|
151
|
+
throwIfIndexCancelled(ctx);
|
|
152
|
+
const info = await lstat(path).catch(() => null);
|
|
153
|
+
const scan = info?.isDirectory()
|
|
154
|
+
? await scanDirectoryPath(ctx.workspaceIndex.id, ctx.workspaceIndex.rootPaths, path, { signal: ctx.signal })
|
|
155
|
+
: await scanFilePath(ctx.workspaceIndex.id, ctx.workspaceIndex.rootPaths, path, {
|
|
156
|
+
signal: ctx.signal,
|
|
157
|
+
});
|
|
158
|
+
files.push(...scan.files);
|
|
159
|
+
}
|
|
160
|
+
return [...new Map(files.map((file) => [file.id, file])).values()];
|
|
161
|
+
});
|
|
162
|
+
throwIfIndexCancelled(ctx);
|
|
163
|
+
const existing = [
|
|
164
|
+
...new Map(changedPaths
|
|
165
|
+
.flatMap((path) => ctx.storage.listFilesByPathPrefix(path))
|
|
166
|
+
.map((file) => [file.id, file])).values(),
|
|
167
|
+
];
|
|
168
|
+
return runDiffPass(ctx, report, scanned, existing, timings, progressBase);
|
|
169
|
+
}
|
|
170
|
+
async function runIndexPass(ctx, report, scanningDetail, timings, progressBase) {
|
|
171
|
+
reportScanning(report, scanningDetail, progressBase);
|
|
172
|
+
const scan = await timings.time("index_scan", () => scanRootPaths(ctx.workspaceIndex.id, ctx.workspaceIndex.rootPaths, {
|
|
173
|
+
signal: ctx.signal,
|
|
174
|
+
}));
|
|
175
|
+
throwIfIndexCancelled(ctx);
|
|
176
|
+
return runDiffPass(ctx, report, scan.files, ctx.storage.listFiles(), timings, progressBase);
|
|
177
|
+
}
|
|
178
|
+
async function runDiffPass(ctx, report, scannedFiles, existingFiles, timings, progressBase) {
|
|
179
|
+
const diff = await timings.time("index_diff", () => computeDiffFromFiles(scannedFiles, existingFiles));
|
|
180
|
+
throwIfIndexCancelled(ctx);
|
|
106
181
|
const pending = [...diff.added, ...diff.modified, ...diff.pending];
|
|
107
182
|
report({
|
|
108
183
|
phase: "scanning",
|
|
109
|
-
filesTotal:
|
|
184
|
+
filesTotal: progressBase?.filesTotal ?? scannedFiles.length,
|
|
185
|
+
filesIndexed: progressBase?.filesSucceeded ?? 0,
|
|
110
186
|
detail: `${diff.added.length} added, ${diff.modified.length} modified, ${diff.pending.length} pending, ${diff.deleted.length} deleted, ${diff.unchanged.length} unchanged`,
|
|
111
187
|
});
|
|
112
188
|
timings.timeSync("index_delete_stale", () => {
|
|
113
189
|
for (const file of diff.deleted) {
|
|
190
|
+
throwIfIndexCancelled(ctx);
|
|
114
191
|
try {
|
|
115
192
|
ctx.storage.deleteFile(file.id);
|
|
116
193
|
}
|
|
@@ -125,8 +202,10 @@ async function runIndexPass(ctx, report, scanningDetail, timings) {
|
|
|
125
202
|
const reportIndexing = (currentStats, detail, embedding) => {
|
|
126
203
|
report({
|
|
127
204
|
phase: "indexing",
|
|
128
|
-
filesTotal: pending.length,
|
|
129
|
-
filesIndexed:
|
|
205
|
+
filesTotal: progressBase?.filesTotal ?? pending.length,
|
|
206
|
+
filesIndexed: (progressBase?.filesSucceeded ?? 0) +
|
|
207
|
+
currentStats.filesIndexed +
|
|
208
|
+
currentStats.filesFailed,
|
|
130
209
|
filesFailed: currentStats.filesFailed,
|
|
131
210
|
detail,
|
|
132
211
|
embedding,
|
|
@@ -140,21 +219,45 @@ async function runIndexPass(ctx, report, scanningDetail, timings) {
|
|
|
140
219
|
entitiesCreated: 0,
|
|
141
220
|
});
|
|
142
221
|
const stats = await indexFiles(pending, ctx, reportIndexing, timings);
|
|
222
|
+
throwIfIndexCancelled(ctx);
|
|
143
223
|
return {
|
|
144
|
-
filesScanned:
|
|
224
|
+
filesScanned: scannedFiles.length,
|
|
145
225
|
diff,
|
|
146
226
|
stats,
|
|
147
227
|
};
|
|
148
228
|
}
|
|
149
|
-
function reportIndexFinalizing(ctx, report, pass) {
|
|
229
|
+
function reportIndexFinalizing(ctx, report, pass, progressBase) {
|
|
150
230
|
report({
|
|
151
231
|
phase: "indexing",
|
|
232
|
+
filesTotal: progressBase?.filesTotal ??
|
|
233
|
+
pass.diff.added.length +
|
|
234
|
+
pass.diff.modified.length +
|
|
235
|
+
pass.diff.pending.length,
|
|
236
|
+
filesIndexed: (progressBase?.filesSucceeded ?? 0) +
|
|
237
|
+
pass.stats.filesIndexed +
|
|
238
|
+
pass.stats.filesFailed,
|
|
239
|
+
filesFailed: pass.stats.filesFailed,
|
|
240
|
+
detail: "finalizing index",
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
function retryProgressBase(pass) {
|
|
244
|
+
return {
|
|
245
|
+
filesSucceeded: pass.stats.filesIndexed,
|
|
152
246
|
filesTotal: pass.diff.added.length +
|
|
153
247
|
pass.diff.modified.length +
|
|
154
248
|
pass.diff.pending.length,
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function reportScanning(report, detail, progressBase) {
|
|
252
|
+
report({
|
|
253
|
+
phase: "scanning",
|
|
254
|
+
...(progressBase
|
|
255
|
+
? {
|
|
256
|
+
filesTotal: progressBase.filesTotal,
|
|
257
|
+
filesIndexed: progressBase.filesSucceeded,
|
|
258
|
+
}
|
|
259
|
+
: {}),
|
|
260
|
+
detail,
|
|
158
261
|
});
|
|
159
262
|
}
|
|
160
263
|
function buildIndexResult(ctx, passes, durationMs, timings) {
|
|
@@ -162,8 +265,6 @@ function buildIndexResult(ctx, passes, durationMs, timings) {
|
|
|
162
265
|
const retryPasses = passes.slice(1);
|
|
163
266
|
const finalPass = passes[passes.length - 1];
|
|
164
267
|
return {
|
|
165
|
-
collectionId: ctx.collection.id,
|
|
166
|
-
collectionName: ctx.collection.name,
|
|
167
268
|
filesScanned: finalPass.filesScanned,
|
|
168
269
|
filesAdded: firstPass.diff.added.length +
|
|
169
270
|
retryPasses.reduce((count, pass) => count + pass.diff.added.length, 0),
|
|
@@ -180,20 +281,17 @@ function buildIndexResult(ctx, passes, durationMs, timings) {
|
|
|
180
281
|
timings: timings.entries(),
|
|
181
282
|
};
|
|
182
283
|
}
|
|
183
|
-
function optimizeStorage(ctx) {
|
|
284
|
+
async function optimizeStorage(ctx) {
|
|
184
285
|
try {
|
|
185
|
-
ctx.storage.optimize();
|
|
286
|
+
await ctx.storage.optimize();
|
|
186
287
|
}
|
|
187
288
|
catch (error) {
|
|
188
289
|
throw toEngineError(error, "Indexing failed to finalize storage", {
|
|
189
290
|
code: "ZVEC_GREP.ENGINE.INDEXING.OPTIMIZE_FAILED",
|
|
190
|
-
context:
|
|
291
|
+
context: workspaceIndexContext(ctx.workspaceIndex),
|
|
191
292
|
});
|
|
192
293
|
}
|
|
193
294
|
}
|
|
194
|
-
async function computeDiff(scannedFiles, storage) {
|
|
195
|
-
return computeDiffFromFiles(scannedFiles, storage.listFiles());
|
|
196
|
-
}
|
|
197
295
|
async function computeDiffFromFiles(scannedFiles, existingFiles) {
|
|
198
296
|
const existingById = new Map(existingFiles.map((file) => [file.id, file]));
|
|
199
297
|
const seen = new Set();
|
|
@@ -242,10 +340,12 @@ async function indexFiles(files, ctx, onProgress, timings) {
|
|
|
242
340
|
const embeddingScheduler = createEmbeddingScheduler(ctx.embeddingConcurrency, ctx.embeddingModel);
|
|
243
341
|
const embeddingTiming = new ConcurrentTiming(timings, "index_embedding");
|
|
244
342
|
const runningEmbeddings = new Set();
|
|
343
|
+
const chunkOptions = indexChunkOptions(ctx);
|
|
245
344
|
const reportEmbeddingProgress = (currentStats, detail) => {
|
|
246
345
|
onProgress(currentStats, detail, embeddingScheduler.snapshot());
|
|
247
346
|
};
|
|
248
347
|
const flushBatch = async () => {
|
|
348
|
+
throwIfIndexCancelled(ctx);
|
|
249
349
|
if (batchFiles.length === 0) {
|
|
250
350
|
return;
|
|
251
351
|
}
|
|
@@ -254,6 +354,7 @@ async function indexFiles(files, ctx, onProgress, timings) {
|
|
|
254
354
|
await scheduleEmbeddingTask(() => embedAndCommitBatch(filesToEmbed, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming));
|
|
255
355
|
};
|
|
256
356
|
const scheduleEmbeddingTask = async (task) => {
|
|
357
|
+
throwIfIndexCancelled(ctx);
|
|
257
358
|
const promise = task().finally(() => {
|
|
258
359
|
runningEmbeddings.delete(promise);
|
|
259
360
|
});
|
|
@@ -262,71 +363,109 @@ async function indexFiles(files, ctx, onProgress, timings) {
|
|
|
262
363
|
await Promise.race(runningEmbeddings);
|
|
263
364
|
}
|
|
264
365
|
};
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
await
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
366
|
+
try {
|
|
367
|
+
for (const file of files) {
|
|
368
|
+
throwIfIndexCancelled(ctx);
|
|
369
|
+
onProgress(stats, `reading ${file.relativePath}`);
|
|
370
|
+
const prepared = await timings.time("index_prepare", () => prepareFile(file, ctx, chunkOptions));
|
|
371
|
+
throwIfIndexCancelled(ctx);
|
|
372
|
+
if ("failedReason" in prepared) {
|
|
373
|
+
recordFileFailed(stats, file, prepared.failedReason);
|
|
374
|
+
onProgress(stats, `failed ${file.relativePath}`);
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (prepared.fragments.length === 0) {
|
|
378
|
+
const committed = commitFile(prepared, [], ctx, stats);
|
|
379
|
+
onProgress(stats, finishedFileDetail(committed, file.relativePath));
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (prepared.fragments.length > ctx.embeddingModel.info.limits.maxBatchSize) {
|
|
383
|
+
await flushBatch();
|
|
384
|
+
await scheduleEmbeddingTask(async () => {
|
|
385
|
+
throwIfIndexCancelled(ctx);
|
|
386
|
+
reportEmbeddingProgress(stats, `embedding ${file.relativePath}`);
|
|
387
|
+
await embedAndCommitFile(prepared, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming);
|
|
388
|
+
});
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (batchFragmentCount > 0 &&
|
|
392
|
+
batchFragmentCount + prepared.fragments.length >
|
|
393
|
+
ctx.embeddingModel.info.limits.maxBatchSize) {
|
|
394
|
+
await flushBatch();
|
|
395
|
+
}
|
|
396
|
+
batchFiles.push(prepared);
|
|
397
|
+
batchFragmentCount += prepared.fragments.length;
|
|
398
|
+
if (batchFragmentCount === ctx.embeddingModel.info.limits.maxBatchSize) {
|
|
399
|
+
await flushBatch();
|
|
400
|
+
}
|
|
295
401
|
}
|
|
402
|
+
await flushBatch();
|
|
403
|
+
await Promise.all(runningEmbeddings);
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
await Promise.allSettled(runningEmbeddings);
|
|
407
|
+
throw error;
|
|
296
408
|
}
|
|
297
|
-
|
|
298
|
-
await Promise.all(runningEmbeddings);
|
|
409
|
+
throwIfIndexCancelled(ctx);
|
|
299
410
|
return stats;
|
|
300
411
|
}
|
|
301
|
-
async function prepareFile(file, ctx) {
|
|
412
|
+
async function prepareFile(file, ctx, chunkOptions) {
|
|
302
413
|
try {
|
|
414
|
+
throwIfIndexCancelled(ctx);
|
|
303
415
|
const source = await readSource(file);
|
|
304
|
-
const extracted = await
|
|
305
|
-
|
|
416
|
+
const extracted = await extract(source, chunkOptions);
|
|
417
|
+
throwIfIndexCancelled(ctx);
|
|
418
|
+
const fragments = extracted.filter((fragment) => ctx.embeddingModel.info.inputKinds.includes(fragment.content.kind));
|
|
306
419
|
return { file, fragments };
|
|
307
420
|
}
|
|
308
421
|
catch (error) {
|
|
422
|
+
if (indexIsCancelled(ctx)) {
|
|
423
|
+
throw indexCancellationError(ctx);
|
|
424
|
+
}
|
|
309
425
|
return {
|
|
310
426
|
file,
|
|
311
427
|
failedReason: markFileFailed(ctx, file, error, "prepare"),
|
|
312
428
|
};
|
|
313
429
|
}
|
|
314
430
|
}
|
|
431
|
+
function indexChunkOptions(ctx) {
|
|
432
|
+
const maxInputTokens = ctx.embeddingModel.info.limits.maxInputTokens;
|
|
433
|
+
if (maxInputTokens === undefined) {
|
|
434
|
+
return {};
|
|
435
|
+
}
|
|
436
|
+
const maxChunkChars = Math.floor(maxInputTokens * APPROXIMATE_CHARS_PER_TOKEN);
|
|
437
|
+
const chunkOverlapChars = Math.floor(maxChunkChars * CHUNK_OVERLAP_RATIO);
|
|
438
|
+
return { maxChunkChars, chunkOverlapChars };
|
|
439
|
+
}
|
|
315
440
|
async function embedAndCommitBatch(files, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming) {
|
|
316
441
|
const fragments = files.flatMap((file) => file.fragments);
|
|
317
442
|
try {
|
|
443
|
+
throwIfIndexCancelled(ctx);
|
|
318
444
|
const contents = fragments.map(vectorContentForFragment);
|
|
319
445
|
onProgress(stats, `embedding ${describePreparedFiles(files)}`, embeddingScheduler.snapshot());
|
|
320
|
-
const
|
|
446
|
+
const embedding = await embeddingTiming.time(() => embedContentsWithRetry(contents, ctx.embeddingModel, embeddingScheduler, ctx.signal, (progress) => reportModelDownloadProgress(stats, progress, onProgress, embeddingScheduler)));
|
|
447
|
+
const truncatedInputIndexes = new Set(embedding.truncated);
|
|
448
|
+
throwIfIndexCancelled(ctx);
|
|
321
449
|
let offset = 0;
|
|
322
450
|
for (const file of files) {
|
|
323
|
-
|
|
451
|
+
throwIfIndexCancelled(ctx);
|
|
452
|
+
const end = offset + file.fragments.length;
|
|
453
|
+
const fileVectors = embedding.vectors.slice(offset, end);
|
|
454
|
+
let truncatedFragmentCount = 0;
|
|
455
|
+
for (let index = offset; index < end; index++) {
|
|
456
|
+
if (truncatedInputIndexes.has(index)) {
|
|
457
|
+
truncatedFragmentCount++;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
324
460
|
offset += file.fragments.length;
|
|
325
|
-
const committed = timings.timeSync("index_commit", () => commitFile(file, fileVectors, ctx, stats));
|
|
461
|
+
const committed = timings.timeSync("index_commit", () => commitFile(file, fileVectors, ctx, stats, truncatedFragmentCount));
|
|
326
462
|
onProgress(stats, finishedFileDetail(committed, file.file.relativePath));
|
|
327
463
|
}
|
|
328
464
|
}
|
|
329
465
|
catch (error) {
|
|
466
|
+
if (indexIsCancelled(ctx)) {
|
|
467
|
+
throw indexCancellationError(ctx);
|
|
468
|
+
}
|
|
330
469
|
if (isRetryableEmbeddingError(error)) {
|
|
331
470
|
for (const file of files) {
|
|
332
471
|
const reason = markFileFailed(ctx, file.file, error, "embed");
|
|
@@ -343,29 +482,57 @@ async function embedAndCommitBatch(files, ctx, stats, onProgress, embeddingSched
|
|
|
343
482
|
}
|
|
344
483
|
async function embedAndCommitFile(file, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming) {
|
|
345
484
|
try {
|
|
346
|
-
|
|
347
|
-
const
|
|
485
|
+
throwIfIndexCancelled(ctx);
|
|
486
|
+
const embedding = await embeddingTiming.time(() => embedFragments(file.fragments, ctx.embeddingModel, embeddingScheduler, ctx.signal, (progress) => reportModelDownloadProgress(stats, progress, onProgress, embeddingScheduler)));
|
|
487
|
+
throwIfIndexCancelled(ctx);
|
|
488
|
+
const committed = timings.timeSync("index_commit", () => commitFile(file, embedding.vectors, ctx, stats, embedding.truncated.length));
|
|
348
489
|
onProgress(stats, finishedFileDetail(committed, file.file.relativePath));
|
|
349
490
|
}
|
|
350
491
|
catch (error) {
|
|
492
|
+
if (indexIsCancelled(ctx)) {
|
|
493
|
+
throw indexCancellationError(ctx);
|
|
494
|
+
}
|
|
351
495
|
const reason = markFileFailed(ctx, file.file, error, "embed");
|
|
352
496
|
recordFileFailed(stats, file.file, reason);
|
|
353
497
|
onProgress(stats, finishedFileDetail(false, file.file.relativePath));
|
|
354
498
|
}
|
|
355
499
|
}
|
|
356
|
-
function commitFile(file, vectors, ctx, stats) {
|
|
500
|
+
function commitFile(file, vectors, ctx, stats, truncatedFragmentCount = 0) {
|
|
357
501
|
try {
|
|
358
|
-
ctx
|
|
502
|
+
throwIfIndexCancelled(ctx);
|
|
503
|
+
ctx.storage.upsertFile(file.file, file.fragments, vectors, {
|
|
504
|
+
truncatedFragmentCount,
|
|
505
|
+
});
|
|
359
506
|
stats.filesIndexed++;
|
|
360
507
|
stats.entitiesCreated += countPublicEntities(file.fragments);
|
|
361
508
|
return true;
|
|
362
509
|
}
|
|
363
510
|
catch (error) {
|
|
511
|
+
if (indexIsCancelled(ctx)) {
|
|
512
|
+
throw indexCancellationError(ctx);
|
|
513
|
+
}
|
|
364
514
|
const reason = markFileFailed(ctx, file.file, error, "commit");
|
|
365
515
|
recordFileFailed(stats, file.file, reason);
|
|
366
516
|
return false;
|
|
367
517
|
}
|
|
368
518
|
}
|
|
519
|
+
function throwIfIndexCancelled(ctx) {
|
|
520
|
+
if (!indexIsCancelled(ctx)) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
throw indexCancellationError(ctx);
|
|
524
|
+
}
|
|
525
|
+
function indexIsCancelled(ctx) {
|
|
526
|
+
return ctx.signal?.aborted === true;
|
|
527
|
+
}
|
|
528
|
+
function indexCancellationError(ctx) {
|
|
529
|
+
return ctx.signal?.reason instanceof Error
|
|
530
|
+
? ctx.signal.reason
|
|
531
|
+
: new EngineError("Indexing was cancelled.", {
|
|
532
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.CANCELLED",
|
|
533
|
+
context: workspaceIndexContext(ctx.workspaceIndex),
|
|
534
|
+
});
|
|
535
|
+
}
|
|
369
536
|
function recordFileFailed(stats, file, reason) {
|
|
370
537
|
stats.filesFailed++;
|
|
371
538
|
stats.failedFiles.push(file.relativePath);
|
|
@@ -417,14 +584,15 @@ async function readSource(file) {
|
|
|
417
584
|
text: bytes.toString("utf8"),
|
|
418
585
|
};
|
|
419
586
|
}
|
|
420
|
-
async function embedFragments(fragments, model, embeddingScheduler) {
|
|
587
|
+
async function embedFragments(fragments, model, embeddingScheduler, signal, onModelProgress) {
|
|
421
588
|
const batches = [];
|
|
422
|
-
for (let start = 0; start < fragments.length; start += model.limits.maxBatchSize) {
|
|
423
|
-
const batch = fragments.slice(start, start + model.limits.maxBatchSize);
|
|
589
|
+
for (let start = 0; start < fragments.length; start += model.info.limits.maxBatchSize) {
|
|
590
|
+
const batch = fragments.slice(start, start + model.info.limits.maxBatchSize);
|
|
424
591
|
batches.push({ start, fragments: batch });
|
|
425
592
|
}
|
|
426
593
|
const vectors = new Array(fragments.length);
|
|
427
|
-
const
|
|
594
|
+
const truncatedInputIndexes = [];
|
|
595
|
+
const results = await Promise.allSettled(batches.map((batch) => embedFragmentBatch(batch.fragments, model, batch.start, embeddingScheduler, signal, onModelProgress)));
|
|
428
596
|
let firstError;
|
|
429
597
|
for (const [index, result] of results.entries()) {
|
|
430
598
|
if (result.status === "rejected") {
|
|
@@ -432,43 +600,54 @@ async function embedFragments(fragments, model, embeddingScheduler) {
|
|
|
432
600
|
continue;
|
|
433
601
|
}
|
|
434
602
|
const start = batches[index].start;
|
|
435
|
-
for (const [offset, vector] of result.value.entries()) {
|
|
603
|
+
for (const [offset, vector] of result.value.vectors.entries()) {
|
|
436
604
|
vectors[start + offset] = vector;
|
|
437
605
|
}
|
|
606
|
+
truncatedInputIndexes.push(...result.value.truncated.map((inputIndex) => start + inputIndex));
|
|
438
607
|
}
|
|
439
608
|
if (firstError) {
|
|
440
609
|
throw firstError;
|
|
441
610
|
}
|
|
442
|
-
return
|
|
611
|
+
return {
|
|
612
|
+
vectors,
|
|
613
|
+
truncated: truncatedInputIndexes,
|
|
614
|
+
};
|
|
443
615
|
}
|
|
444
|
-
async function embedFragmentBatch(fragments, model, startIndex, embeddingScheduler) {
|
|
616
|
+
async function embedFragmentBatch(fragments, model, startIndex, embeddingScheduler, signal, onModelProgress) {
|
|
445
617
|
const contents = fragments.map(vectorContentForFragment);
|
|
446
618
|
try {
|
|
447
|
-
return await embedContentsWithRetry(contents, model, embeddingScheduler);
|
|
619
|
+
return await embedContentsWithRetry(contents, model, embeddingScheduler, signal, onModelProgress);
|
|
448
620
|
}
|
|
449
621
|
catch (error) {
|
|
450
622
|
if (fragments.length === 1 || isRetryableEmbeddingError(error)) {
|
|
451
623
|
throw error;
|
|
452
624
|
}
|
|
453
|
-
return await embedFragmentBatchOneByOne(fragments, model, startIndex, embeddingScheduler);
|
|
625
|
+
return await embedFragmentBatchOneByOne(fragments, model, startIndex, embeddingScheduler, signal, onModelProgress);
|
|
454
626
|
}
|
|
455
627
|
}
|
|
456
|
-
async function embedFragmentBatchOneByOne(fragments, model, startIndex, embeddingScheduler) {
|
|
628
|
+
async function embedFragmentBatchOneByOne(fragments, model, startIndex, embeddingScheduler, signal, onModelProgress) {
|
|
457
629
|
const vectors = [];
|
|
630
|
+
const truncatedInputIndexes = [];
|
|
458
631
|
for (const [index, fragment] of fragments.entries()) {
|
|
459
632
|
try {
|
|
460
|
-
const
|
|
461
|
-
vectors.push(
|
|
633
|
+
const result = await embedContentsWithRetry([vectorContentForFragment(fragment)], model, embeddingScheduler, signal, onModelProgress);
|
|
634
|
+
vectors.push(result.vectors[0]);
|
|
635
|
+
if (result.truncated.length > 0) {
|
|
636
|
+
truncatedInputIndexes.push(index);
|
|
637
|
+
}
|
|
462
638
|
}
|
|
463
639
|
catch (error) {
|
|
464
640
|
throw new EngineError("Embedding entity fragment failed after one-by-one fallback", {
|
|
465
641
|
code: "ZVEC_GREP.ENGINE.INDEXING.EMBEDDING_FRAGMENT_FAILED",
|
|
466
|
-
context: `model=${model.
|
|
642
|
+
context: `model=${model.info.reference} fragmentId=${fragment.id} fragmentIndex=${startIndex + index}`,
|
|
467
643
|
cause: error,
|
|
468
644
|
});
|
|
469
645
|
}
|
|
470
646
|
}
|
|
471
|
-
return
|
|
647
|
+
return {
|
|
648
|
+
vectors,
|
|
649
|
+
truncated: truncatedInputIndexes,
|
|
650
|
+
};
|
|
472
651
|
}
|
|
473
652
|
function vectorContentForFragment(fragment) {
|
|
474
653
|
if (fragment.content.kind !== "text") {
|
|
@@ -513,13 +692,18 @@ function compactMetadataLines(lines) {
|
|
|
513
692
|
.filter((line) => line !== null && line.trim().length > 0)
|
|
514
693
|
.join("\n");
|
|
515
694
|
}
|
|
516
|
-
async function embedContentsWithRetry(contents, model, embeddingScheduler) {
|
|
695
|
+
async function embedContentsWithRetry(contents, model, embeddingScheduler, signal, onModelProgress) {
|
|
517
696
|
let attempt = 0;
|
|
518
697
|
while (true) {
|
|
519
698
|
let retry = nonRetryableEmbeddingError();
|
|
520
699
|
let delayMs = 0;
|
|
521
700
|
try {
|
|
522
|
-
|
|
701
|
+
throwIfAborted(signal);
|
|
702
|
+
const result = await embeddingScheduler.run(() => model.embed(contents, {
|
|
703
|
+
purpose: "document",
|
|
704
|
+
signal,
|
|
705
|
+
onProgress: onModelProgress,
|
|
706
|
+
}), signal, (error) => {
|
|
523
707
|
retry = classifyEmbeddingRetry(error);
|
|
524
708
|
delayMs = retryDelayMs(attempt, retry);
|
|
525
709
|
if (retry.retryable) {
|
|
@@ -530,9 +714,14 @@ async function embedContentsWithRetry(contents, model, embeddingScheduler) {
|
|
|
530
714
|
}
|
|
531
715
|
});
|
|
532
716
|
embeddingScheduler.recordSuccess();
|
|
533
|
-
return
|
|
717
|
+
return result;
|
|
534
718
|
}
|
|
535
719
|
catch (error) {
|
|
720
|
+
if (signal?.aborted) {
|
|
721
|
+
throw signal.reason instanceof Error
|
|
722
|
+
? signal.reason
|
|
723
|
+
: new Error("Embedding was cancelled.");
|
|
724
|
+
}
|
|
536
725
|
if (!retry.retryable) {
|
|
537
726
|
retry = classifyEmbeddingRetry(error);
|
|
538
727
|
delayMs = retryDelayMs(attempt, retry);
|
|
@@ -540,11 +729,17 @@ async function embedContentsWithRetry(contents, model, embeddingScheduler) {
|
|
|
540
729
|
if (attempt >= maxRetryAttempts(retry) || !retry.retryable) {
|
|
541
730
|
throw error;
|
|
542
731
|
}
|
|
543
|
-
await
|
|
732
|
+
await abortableDelay(delayMs, signal);
|
|
544
733
|
attempt++;
|
|
545
734
|
}
|
|
546
735
|
}
|
|
547
736
|
}
|
|
737
|
+
function reportModelDownloadProgress(stats, progress, onProgress, embeddingScheduler) {
|
|
738
|
+
onProgress(stats, `downloading ${progress.model}`, {
|
|
739
|
+
...embeddingScheduler.snapshot(),
|
|
740
|
+
...progress,
|
|
741
|
+
});
|
|
742
|
+
}
|
|
548
743
|
function createEmbeddingScheduler(requestedConcurrency, model) {
|
|
549
744
|
return new AdaptiveEmbeddingScheduler(resolveEmbeddingConcurrencyPolicy(requestedConcurrency, model));
|
|
550
745
|
}
|
|
@@ -559,10 +754,11 @@ function resolveEmbeddingConcurrencyPolicy(requestedConcurrency, model) {
|
|
|
559
754
|
adaptive: requestedConcurrency > 1,
|
|
560
755
|
};
|
|
561
756
|
}
|
|
562
|
-
const
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
const
|
|
757
|
+
const remote = model.info.provider === "qwen";
|
|
758
|
+
const multimodal = model.info.inputKinds.includes("image");
|
|
759
|
+
const initial = remote ? (multimodal ? 4 : 8) : 1;
|
|
760
|
+
const max = remote ? (multimodal ? 8 : 12) : 1;
|
|
761
|
+
const min = Math.min(initial, 4);
|
|
566
762
|
return {
|
|
567
763
|
initial,
|
|
568
764
|
min,
|
|
@@ -585,11 +781,13 @@ class AdaptiveEmbeddingScheduler {
|
|
|
585
781
|
get taskConcurrency() {
|
|
586
782
|
return this.policy.max;
|
|
587
783
|
}
|
|
588
|
-
async run(task, onError) {
|
|
589
|
-
|
|
590
|
-
await this.
|
|
784
|
+
async run(task, signal, onError) {
|
|
785
|
+
throwIfAborted(signal);
|
|
786
|
+
await this.waitForCooldown(signal);
|
|
787
|
+
await this.acquire(signal);
|
|
591
788
|
try {
|
|
592
|
-
await this.waitForCooldown();
|
|
789
|
+
await this.waitForCooldown(signal);
|
|
790
|
+
throwIfAborted(signal);
|
|
593
791
|
return await task();
|
|
594
792
|
}
|
|
595
793
|
catch (error) {
|
|
@@ -631,19 +829,36 @@ class AdaptiveEmbeddingScheduler {
|
|
|
631
829
|
retryableFailures: this.retryableFailures,
|
|
632
830
|
};
|
|
633
831
|
}
|
|
634
|
-
async waitForCooldown() {
|
|
832
|
+
async waitForCooldown(signal) {
|
|
635
833
|
const remaining = this.cooldownUntil - Date.now();
|
|
636
834
|
if (remaining > 0) {
|
|
637
|
-
await
|
|
835
|
+
await abortableDelay(remaining, signal);
|
|
638
836
|
}
|
|
639
837
|
}
|
|
640
|
-
async acquire() {
|
|
838
|
+
async acquire(signal) {
|
|
839
|
+
throwIfAborted(signal);
|
|
641
840
|
if (this.active < this.currentConcurrency) {
|
|
642
841
|
this.active++;
|
|
643
842
|
return;
|
|
644
843
|
}
|
|
645
|
-
await new Promise((resolve) => {
|
|
646
|
-
|
|
844
|
+
await new Promise((resolve, reject) => {
|
|
845
|
+
const queued = () => {
|
|
846
|
+
signal?.removeEventListener("abort", abort);
|
|
847
|
+
resolve();
|
|
848
|
+
};
|
|
849
|
+
const abort = () => {
|
|
850
|
+
const index = this.queue.indexOf(queued);
|
|
851
|
+
if (index >= 0) {
|
|
852
|
+
this.queue.splice(index, 1);
|
|
853
|
+
}
|
|
854
|
+
reject(abortError(signal));
|
|
855
|
+
};
|
|
856
|
+
if (signal?.aborted) {
|
|
857
|
+
reject(abortError(signal));
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
861
|
+
this.queue.push(queued);
|
|
647
862
|
});
|
|
648
863
|
}
|
|
649
864
|
release() {
|
|
@@ -724,8 +939,32 @@ function retryAfterMsFromText(text) {
|
|
|
724
939
|
}
|
|
725
940
|
return undefined;
|
|
726
941
|
}
|
|
727
|
-
function
|
|
728
|
-
|
|
942
|
+
function abortableDelay(ms, signal) {
|
|
943
|
+
if (ms <= 0) {
|
|
944
|
+
return Promise.resolve();
|
|
945
|
+
}
|
|
946
|
+
throwIfAborted(signal);
|
|
947
|
+
return new Promise((resolve, reject) => {
|
|
948
|
+
const timer = setTimeout(() => {
|
|
949
|
+
signal?.removeEventListener("abort", abort);
|
|
950
|
+
resolve();
|
|
951
|
+
}, ms);
|
|
952
|
+
const abort = () => {
|
|
953
|
+
clearTimeout(timer);
|
|
954
|
+
reject(abortError(signal));
|
|
955
|
+
};
|
|
956
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
function throwIfAborted(signal) {
|
|
960
|
+
if (signal?.aborted) {
|
|
961
|
+
throw abortError(signal);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
function abortError(signal) {
|
|
965
|
+
return signal?.reason instanceof Error
|
|
966
|
+
? signal.reason
|
|
967
|
+
: new Error("Embedding was cancelled.");
|
|
729
968
|
}
|
|
730
969
|
function countPublicEntities(fragments) {
|
|
731
970
|
let count = 0;
|
|
@@ -777,10 +1016,10 @@ function toEngineError(error, message, options) {
|
|
|
777
1016
|
cause: error,
|
|
778
1017
|
});
|
|
779
1018
|
}
|
|
780
|
-
function
|
|
1019
|
+
function workspaceIndexContext(workspaceIndex) {
|
|
781
1020
|
return (errorDetails([
|
|
782
|
-
detail("
|
|
783
|
-
|
|
1021
|
+
detail("workspace_index_id", workspaceIndex.id),
|
|
1022
|
+
workspaceIndexDetail(workspaceIndex.name),
|
|
784
1023
|
]) ?? "");
|
|
785
1024
|
}
|
|
786
1025
|
function fileContext(file) {
|