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