@angri450/zvec-grep 0.2.2
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/LICENSE +201 -0
- package/README.md +319 -0
- package/README_CN.md +297 -0
- 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 +12 -0
- package/dist/authorization/prompt.js +63 -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 +6 -0
- package/dist/cli/args.js +1255 -0
- package/dist/cli/args.js.map +1 -0
- 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.d.ts +4 -0
- package/dist/cli/commands.js +801 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/errors.d.ts +9 -0
- package/dist/cli/errors.js +131 -0
- package/dist/cli/errors.js.map +1 -0
- package/dist/cli/format/context.d.ts +9 -0
- package/dist/cli/format/context.js +786 -0
- package/dist/cli/format/context.js.map +1 -0
- package/dist/cli/format/debug.d.ts +4 -0
- package/dist/cli/format/debug.js +60 -0
- package/dist/cli/format/debug.js.map +1 -0
- package/dist/cli/format/highlight.d.ts +3 -0
- package/dist/cli/format/highlight.js +106 -0
- package/dist/cli/format/highlight.js.map +1 -0
- package/dist/cli/format/progress.d.ts +13 -0
- package/dist/cli/format/progress.js +290 -0
- package/dist/cli/format/progress.js.map +1 -0
- package/dist/cli/format/range.d.ts +3 -0
- package/dist/cli/format/range.js +24 -0
- package/dist/cli/format/range.js.map +1 -0
- package/dist/cli/format/status.d.ts +83 -0
- package/dist/cli/format/status.js +530 -0
- package/dist/cli/format/status.js.map +1 -0
- package/dist/cli/help.d.ts +1 -0
- package/dist/cli/help.js +546 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +40 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/install.d.ts +4 -0
- package/dist/cli/install.js +1735 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/cli/managed-rg.d.ts +10 -0
- package/dist/cli/managed-rg.js +337 -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 +87 -0
- package/dist/cli/types.js +10 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/version.d.ts +1 -0
- package/dist/cli/version.js +13 -0
- package/dist/cli/version.js.map +1 -0
- 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 +78 -0
- package/dist/daemon/backend.js +1118 -0
- package/dist/daemon/backend.js.map +1 -0
- package/dist/daemon/change-set.d.ts +30 -0
- package/dist/daemon/change-set.js +163 -0
- package/dist/daemon/change-set.js.map +1 -0
- package/dist/daemon/config.d.ts +27 -0
- package/dist/daemon/config.js +107 -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 +307 -0
- package/dist/daemon/http-server.js.map +1 -0
- package/dist/daemon/index-coordinator.d.ts +22 -0
- package/dist/daemon/index-coordinator.js +55 -0
- package/dist/daemon/index-coordinator.js.map +1 -0
- package/dist/daemon/job-scheduler.d.ts +85 -0
- package/dist/daemon/job-scheduler.js +492 -0
- package/dist/daemon/job-scheduler.js.map +1 -0
- package/dist/daemon/logger.d.ts +10 -0
- package/dist/daemon/logger.js +101 -0
- package/dist/daemon/logger.js.map +1 -0
- package/dist/daemon/model-pool.d.ts +48 -0
- package/dist/daemon/model-pool.js +190 -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 +197 -0
- package/dist/daemon/root-lease.js.map +1 -0
- package/dist/daemon/root-runtime.d.ts +80 -0
- package/dist/daemon/root-runtime.js +340 -0
- package/dist/daemon/root-runtime.js.map +1 -0
- package/dist/daemon/runtime-manager.d.ts +47 -0
- package/dist/daemon/runtime-manager.js +252 -0
- package/dist/daemon/runtime-manager.js.map +1 -0
- package/dist/daemon/runtime.d.ts +12 -0
- package/dist/daemon/runtime.js +105 -0
- package/dist/daemon/runtime.js.map +1 -0
- package/dist/daemon/server-controller.d.ts +38 -0
- package/dist/daemon/server-controller.js +327 -0
- package/dist/daemon/server-controller.js.map +1 -0
- package/dist/daemon/watch-manager.d.ts +62 -0
- package/dist/daemon/watch-manager.js +415 -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 +57 -0
- package/dist/engine/config.js +351 -0
- package/dist/engine/config.js.map +1 -0
- package/dist/engine/errors.d.ts +20 -0
- package/dist/engine/errors.js +51 -0
- package/dist/engine/errors.js.map +1 -0
- package/dist/engine/extraction/code/adapter.d.ts +19 -0
- package/dist/engine/extraction/code/adapter.js +24 -0
- package/dist/engine/extraction/code/adapter.js.map +1 -0
- package/dist/engine/extraction/code/extractor.d.ts +13 -0
- package/dist/engine/extraction/code/extractor.js +696 -0
- package/dist/engine/extraction/code/extractor.js.map +1 -0
- package/dist/engine/extraction/code/families/c-family.d.ts +2 -0
- package/dist/engine/extraction/code/families/c-family.js +122 -0
- package/dist/engine/extraction/code/families/c-family.js.map +1 -0
- package/dist/engine/extraction/code/families/js-ts.d.ts +12 -0
- package/dist/engine/extraction/code/families/js-ts.js +118 -0
- package/dist/engine/extraction/code/families/js-ts.js.map +1 -0
- package/dist/engine/extraction/code/families/metadata.d.ts +8 -0
- package/dist/engine/extraction/code/families/metadata.js +86 -0
- package/dist/engine/extraction/code/families/metadata.js.map +1 -0
- package/dist/engine/extraction/code/families/name-field.d.ts +3 -0
- package/dist/engine/extraction/code/families/name-field.js +22 -0
- package/dist/engine/extraction/code/families/name-field.js.map +1 -0
- package/dist/engine/extraction/code/languages/c.d.ts +1 -0
- package/dist/engine/extraction/code/languages/c.js +12 -0
- package/dist/engine/extraction/code/languages/c.js.map +1 -0
- package/dist/engine/extraction/code/languages/cpp.d.ts +1 -0
- package/dist/engine/extraction/code/languages/cpp.js +18 -0
- package/dist/engine/extraction/code/languages/cpp.js.map +1 -0
- package/dist/engine/extraction/code/languages/go.d.ts +2 -0
- package/dist/engine/extraction/code/languages/go.js +70 -0
- package/dist/engine/extraction/code/languages/go.js.map +1 -0
- package/dist/engine/extraction/code/languages/java.d.ts +1 -0
- package/dist/engine/extraction/code/languages/java.js +17 -0
- package/dist/engine/extraction/code/languages/java.js.map +1 -0
- package/dist/engine/extraction/code/languages/javascript.d.ts +2 -0
- package/dist/engine/extraction/code/languages/javascript.js +23 -0
- package/dist/engine/extraction/code/languages/javascript.js.map +1 -0
- package/dist/engine/extraction/code/languages/python.d.ts +2 -0
- package/dist/engine/extraction/code/languages/python.js +52 -0
- package/dist/engine/extraction/code/languages/python.js.map +1 -0
- package/dist/engine/extraction/code/languages/rust.d.ts +2 -0
- package/dist/engine/extraction/code/languages/rust.js +25 -0
- package/dist/engine/extraction/code/languages/rust.js.map +1 -0
- package/dist/engine/extraction/code/languages/typescript.d.ts +2 -0
- package/dist/engine/extraction/code/languages/typescript.js +37 -0
- package/dist/engine/extraction/code/languages/typescript.js.map +1 -0
- package/dist/engine/extraction/code/tree-sitter/grammar.d.ts +4 -0
- package/dist/engine/extraction/code/tree-sitter/grammar.js +49 -0
- package/dist/engine/extraction/code/tree-sitter/grammar.js.map +1 -0
- package/dist/engine/extraction/code/tree-sitter/nodes.d.ts +3 -0
- package/dist/engine/extraction/code/tree-sitter/nodes.js +27 -0
- package/dist/engine/extraction/code/tree-sitter/nodes.js.map +1 -0
- package/dist/engine/extraction/code/tree-sitter/parser.d.ts +2 -0
- package/dist/engine/extraction/code/tree-sitter/parser.js +31 -0
- package/dist/engine/extraction/code/tree-sitter/parser.js.map +1 -0
- package/dist/engine/extraction/ids.d.ts +1 -0
- package/dist/engine/extraction/ids.js +5 -0
- package/dist/engine/extraction/ids.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 -0
- package/dist/engine/extraction/index.js +3 -0
- package/dist/engine/extraction/index.js.map +1 -0
- package/dist/engine/extraction/markdown/extractor.d.ts +7 -0
- package/dist/engine/extraction/markdown/extractor.js +362 -0
- package/dist/engine/extraction/markdown/extractor.js.map +1 -0
- package/dist/engine/extraction/runtime.d.ts +9 -0
- package/dist/engine/extraction/runtime.js +37 -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/extraction/types.js +2 -0
- package/dist/engine/extraction/types.js.map +1 -0
- package/dist/engine/extraction/vector-content.d.ts +5 -0
- package/dist/engine/extraction/vector-content.js +83 -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/artifact-cache-lock.d.ts +16 -0
- package/dist/engine/models/artifact-cache-lock.js +228 -0
- package/dist/engine/models/artifact-cache-lock.js.map +1 -0
- package/dist/engine/models/artifact-downloader.d.ts +77 -0
- package/dist/engine/models/artifact-downloader.js +766 -0
- package/dist/engine/models/artifact-downloader.js.map +1 -0
- package/dist/engine/models/backends/llama-cpp.d.ts +100 -0
- package/dist/engine/models/backends/llama-cpp.js +567 -0
- 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 +38 -0
- package/dist/engine/models/backends/model2vec.js +344 -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/backends/qwen.js +411 -0
- package/dist/engine/models/backends/qwen.js.map +1 -0
- package/dist/engine/models/backends/transformers-js.d.ts +90 -0
- package/dist/engine/models/backends/transformers-js.js +411 -0
- package/dist/engine/models/backends/transformers-js.js.map +1 -0
- package/dist/engine/models/catalog.d.ts +490 -0
- package/dist/engine/models/catalog.js +522 -0
- package/dist/engine/models/catalog.js.map +1 -0
- package/dist/engine/models/download-progress.d.ts +16 -0
- package/dist/engine/models/download-progress.js +47 -0
- package/dist/engine/models/download-progress.js.map +1 -0
- package/dist/engine/models/embeddings.d.ts +73 -0
- package/dist/engine/models/embeddings.js +125 -0
- package/dist/engine/models/embeddings.js.map +1 -0
- package/dist/engine/models/factory.d.ts +2 -0
- package/dist/engine/models/factory.js +51 -0
- package/dist/engine/models/factory.js.map +1 -0
- package/dist/engine/models/index.d.ts +5 -0
- package/dist/engine/models/index.js +6 -0
- package/dist/engine/models/index.js.map +1 -0
- package/dist/engine/models/local-embedding-parallelism.d.ts +15 -0
- package/dist/engine/models/local-embedding-parallelism.js +63 -0
- package/dist/engine/models/local-embedding-parallelism.js.map +1 -0
- package/dist/engine/models/local-embedding-queue.d.ts +12 -0
- package/dist/engine/models/local-embedding-queue.js +65 -0
- package/dist/engine/models/local-embedding-queue.js.map +1 -0
- package/dist/engine/models/ranking.d.ts +19 -0
- package/dist/engine/models/ranking.js +2 -0
- package/dist/engine/models/ranking.js.map +1 -0
- 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 +14 -0
- package/dist/engine/pipeline/indexing/index.js +1219 -0
- package/dist/engine/pipeline/indexing/index.js.map +1 -0
- 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.d.ts +7 -0
- package/dist/engine/pipeline/indexing/root-paths.js +148 -0
- package/dist/engine/pipeline/indexing/root-paths.js.map +1 -0
- package/dist/engine/pipeline/indexing/scanner/index.d.ts +14 -0
- package/dist/engine/pipeline/indexing/scanner/index.js +776 -0
- package/dist/engine/pipeline/indexing/scanner/index.js.map +1 -0
- package/dist/engine/pipeline/search/index.d.ts +12 -0
- package/dist/engine/pipeline/search/index.js +811 -0
- package/dist/engine/pipeline/search/index.js.map +1 -0
- package/dist/engine/service/index.d.ts +3 -0
- package/dist/engine/service/index.js +2 -0
- package/dist/engine/service/index.js.map +1 -0
- package/dist/engine/service/lexical.d.ts +28 -0
- package/dist/engine/service/lexical.js +446 -0
- package/dist/engine/service/lexical.js.map +1 -0
- package/dist/engine/service/root.d.ts +15 -0
- package/dist/engine/service/root.js +57 -0
- package/dist/engine/service/root.js.map +1 -0
- package/dist/engine/service/structure-enrichment.d.ts +8 -0
- package/dist/engine/service/structure-enrichment.js +178 -0
- package/dist/engine/service/structure-enrichment.js.map +1 -0
- package/dist/engine/service/types.d.ts +212 -0
- package/dist/engine/service/types.js +2 -0
- package/dist/engine/service/types.js.map +1 -0
- 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 +15 -0
- package/dist/engine/service/zvec-grep.js +1520 -0
- package/dist/engine/service/zvec-grep.js.map +1 -0
- package/dist/engine/storage/index.d.ts +53 -0
- package/dist/engine/storage/index.js +3 -0
- package/dist/engine/storage/index.js.map +1 -0
- 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 -0
- package/dist/engine/storage/zvec.js +976 -0
- package/dist/engine/storage/zvec.js.map +1 -0
- package/dist/engine/types.d.ts +303 -0
- package/dist/engine/types.js +14 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/engine/utils/atomic-file.d.ts +3 -0
- package/dist/engine/utils/atomic-file.js +39 -0
- package/dist/engine/utils/atomic-file.js.map +1 -0
- 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.d.ts +10 -0
- package/dist/engine/utils/file-selection.js +157 -0
- package/dist/engine/utils/file-selection.js.map +1 -0
- package/dist/engine/utils/glob.d.ts +9 -0
- package/dist/engine/utils/glob.js +208 -0
- package/dist/engine/utils/glob.js.map +1 -0
- package/dist/engine/utils/hash.d.ts +2 -0
- package/dist/engine/utils/hash.js +8 -0
- package/dist/engine/utils/hash.js.map +1 -0
- package/dist/engine/utils/json.d.ts +8 -0
- package/dist/engine/utils/json.js +83 -0
- package/dist/engine/utils/json.js.map +1 -0
- package/dist/engine/utils/lock.d.ts +19 -0
- package/dist/engine/utils/lock.js +254 -0
- package/dist/engine/utils/lock.js.map +1 -0
- package/dist/engine/utils/path.d.ts +4 -0
- package/dist/engine/utils/path.js +18 -0
- package/dist/engine/utils/path.js.map +1 -0
- package/dist/engine/utils/timing.d.ts +19 -0
- package/dist/engine/utils/timing.js +87 -0
- package/dist/engine/utils/timing.js.map +1 -0
- 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 +6 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- 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 +668 -0
- package/dist/mcp/schemas.js +512 -0
- package/dist/mcp/schemas.js.map +1 -0
- package/dist/mcp/stdio-bridge.d.ts +15 -0
- package/dist/mcp/stdio-bridge.js +190 -0
- package/dist/mcp/stdio-bridge.js.map +1 -0
- package/dist/mcp/tools.d.ts +153 -0
- package/dist/mcp/tools.js +488 -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 +217 -0
- package/docs/02-cli.md +259 -0
- package/docs/03-mcp.md +185 -0
- package/docs/04-pipeline.md +234 -0
- package/docs/05-architecture.md +86 -0
- package/docs/06-server.md +279 -0
- package/docs/07-embedding.md +227 -0
- package/docs/08-roadmap.md +85 -0
- package/docs/README.md +53 -0
- package/package.json +87 -0
|
@@ -0,0 +1,1219 @@
|
|
|
1
|
+
import { lstat, readFile } from "node:fs/promises";
|
|
2
|
+
import { workspaceIndexDetail, detail, EngineError, errorDetails, isEngineError, } from "../../errors.js";
|
|
3
|
+
import { sha256Bytes } from "../../utils/hash.js";
|
|
4
|
+
import { normalizePath } from "../../utils/path.js";
|
|
5
|
+
import { ConcurrentTiming, TimingCollector } from "../../utils/timing.js";
|
|
6
|
+
import { extractForIndexing, vectorContentForFragment, } from "../../extraction/index.js";
|
|
7
|
+
import { scanDirectoryPath, scanFilePath, scanRootPaths, } from "./scanner/index.js";
|
|
8
|
+
import { indexChunkOptions } from "./input-budget.js";
|
|
9
|
+
const EMBEDDING_TRANSIENT_MAX_RETRIES = 3;
|
|
10
|
+
const EMBEDDING_RATE_LIMIT_MAX_RETRIES = 6;
|
|
11
|
+
const EMBEDDING_TRANSIENT_RETRY_BASE_DELAY_MS = 500;
|
|
12
|
+
const EMBEDDING_RATE_LIMIT_RETRY_BASE_DELAY_MS = 2000;
|
|
13
|
+
const EMBEDDING_TRANSIENT_RETRY_MAX_DELAY_MS = 8000;
|
|
14
|
+
const EMBEDDING_RATE_LIMIT_RETRY_MAX_DELAY_MS = 30000;
|
|
15
|
+
const EMBEDDING_RETRY_JITTER_MS = 500;
|
|
16
|
+
const EMBEDDING_SUCCESS_STREAK_MIN = 4;
|
|
17
|
+
const PERMANENT_REMOTE_MODEL_PROVIDER_CODES = new Set([
|
|
18
|
+
"invalid_model",
|
|
19
|
+
"model_not_found",
|
|
20
|
+
"unsupported_model",
|
|
21
|
+
"invalid_dimension",
|
|
22
|
+
"invalid_dimensions",
|
|
23
|
+
"unsupported_dimension",
|
|
24
|
+
"unsupported_dimensions",
|
|
25
|
+
"dimension_out_of_range",
|
|
26
|
+
"invalid_embedding_dimension",
|
|
27
|
+
"unsupported_embedding_dimension",
|
|
28
|
+
]);
|
|
29
|
+
export async function indexWorkspace(ctx) {
|
|
30
|
+
try {
|
|
31
|
+
return await indexWorkspaceUnchecked(ctx);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
throw toEngineError(error, "Indexing workspace failed", {
|
|
35
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.WORKSPACE_FAILED",
|
|
36
|
+
context: workspaceIndexContext(ctx.workspaceIndex),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function indexWorkspacePaths(ctx, changedPaths) {
|
|
41
|
+
try {
|
|
42
|
+
return await indexWorkspacePathsUnchecked(ctx, changedPaths);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
throw toEngineError(error, "Indexing changed paths failed", {
|
|
46
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.WORKSPACE_FAILED",
|
|
47
|
+
context: workspaceIndexContext(ctx.workspaceIndex),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function getWorkspaceIndexStatus(workspaceIndex, storedFiles) {
|
|
52
|
+
try {
|
|
53
|
+
const scan = await scanRootPaths(workspaceIndex.id, workspaceIndex.rootPaths, { knownFiles: storedFiles });
|
|
54
|
+
const diff = await computeDiffFromFiles(scan.files, storedFiles);
|
|
55
|
+
const pendingFiles = storedFiles.filter((file) => file.indexStatus?.indexedTime === null);
|
|
56
|
+
const failedFiles = pendingFiles.filter((file) => file.indexStatus?.error !== undefined);
|
|
57
|
+
const indexedFiles = storedFiles.filter((file) => file.indexStatus?.indexedTime !== undefined &&
|
|
58
|
+
file.indexStatus.indexedTime !== null);
|
|
59
|
+
const entitiesIndexed = indexedFiles.reduce((count, file) => count + (file.indexStatus?.entityCount ?? 0), 0);
|
|
60
|
+
const fragmentsTruncated = indexedFiles.reduce((count, file) => count + (file.indexStatus?.truncatedFragmentCount ?? 0), 0);
|
|
61
|
+
return {
|
|
62
|
+
filesScanned: scan.files.length,
|
|
63
|
+
filesStored: storedFiles.length,
|
|
64
|
+
filesIndexed: indexedFiles.length,
|
|
65
|
+
entitiesIndexed,
|
|
66
|
+
fragmentsTruncated,
|
|
67
|
+
filesPending: pendingFiles.length,
|
|
68
|
+
filesFailed: failedFiles.length,
|
|
69
|
+
filesAdded: diff.added.length,
|
|
70
|
+
filesModified: diff.modified.length,
|
|
71
|
+
filesDeleted: diff.deleted.length,
|
|
72
|
+
filesUnchanged: diff.unchanged.length,
|
|
73
|
+
pendingFiles,
|
|
74
|
+
failedFiles,
|
|
75
|
+
addedFiles: diff.added,
|
|
76
|
+
modifiedFiles: diff.modified,
|
|
77
|
+
deletedFiles: diff.deleted,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
throw toEngineError(error, "Inspecting workspace index status failed", {
|
|
82
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.STATUS_FAILED",
|
|
83
|
+
context: workspaceIndexContext(workspaceIndex),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function indexWorkspaceUnchecked(ctx) {
|
|
88
|
+
const start = Date.now();
|
|
89
|
+
const report = ctx.onProgress ?? (() => undefined);
|
|
90
|
+
const timings = new TimingCollector();
|
|
91
|
+
throwIfIndexCancelled(ctx);
|
|
92
|
+
const firstPass = await runIndexPass(ctx, report, "Scanning files...", timings);
|
|
93
|
+
const passes = [firstPass];
|
|
94
|
+
let progressBase;
|
|
95
|
+
if (firstPass.stats.filesFailed > 0) {
|
|
96
|
+
const failed = firstPass.stats.filesFailed;
|
|
97
|
+
progressBase = retryProgressBase(firstPass);
|
|
98
|
+
report({
|
|
99
|
+
phase: "scanning",
|
|
100
|
+
filesTotal: progressBase.filesTotal,
|
|
101
|
+
filesIndexed: progressBase.filesSucceeded,
|
|
102
|
+
detail: `Retrying ${failed} failed ${failed === 1 ? "file" : "files"}...`,
|
|
103
|
+
});
|
|
104
|
+
passes.push(await runIndexPass(ctx, report, "Scanning retry candidates...", timings, progressBase));
|
|
105
|
+
}
|
|
106
|
+
const finalPass = passes[passes.length - 1];
|
|
107
|
+
throwIfIndexCancelled(ctx);
|
|
108
|
+
reportIndexFinalizing(ctx, report, finalPass, progressBase);
|
|
109
|
+
await timings.time("index_optimize", () => optimizeStorage(ctx));
|
|
110
|
+
const result = buildIndexResult(ctx, passes, Date.now() - start, timings);
|
|
111
|
+
if (result.filesFailed > 0) {
|
|
112
|
+
report({ phase: "done", detail: "Indexing completed with failed files" });
|
|
113
|
+
throw filesFailedError(ctx, result, finalPass, passes.length);
|
|
114
|
+
}
|
|
115
|
+
report({ phase: "done", detail: "Indexing complete" });
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
async function indexWorkspacePathsUnchecked(ctx, changedPaths) {
|
|
119
|
+
const start = Date.now();
|
|
120
|
+
const report = ctx.onProgress ?? (() => undefined);
|
|
121
|
+
const timings = new TimingCollector();
|
|
122
|
+
const normalizedPaths = [...new Set(changedPaths.map(normalizePath))];
|
|
123
|
+
throwIfIndexCancelled(ctx);
|
|
124
|
+
const firstPass = await runPathIndexPass(ctx, report, normalizedPaths, timings);
|
|
125
|
+
const passes = [firstPass];
|
|
126
|
+
let progressBase;
|
|
127
|
+
if (firstPass.stats.filesFailed > 0) {
|
|
128
|
+
progressBase = retryProgressBase(firstPass);
|
|
129
|
+
passes.push(await runPathIndexPass(ctx, report, normalizedPaths, timings, progressBase));
|
|
130
|
+
}
|
|
131
|
+
const finalPass = passes[passes.length - 1];
|
|
132
|
+
throwIfIndexCancelled(ctx);
|
|
133
|
+
reportIndexFinalizing(ctx, report, finalPass, progressBase);
|
|
134
|
+
await timings.time("index_optimize", () => optimizeStorage(ctx));
|
|
135
|
+
const result = buildIndexResult(ctx, passes, Date.now() - start, timings);
|
|
136
|
+
if (result.filesFailed > 0) {
|
|
137
|
+
throw filesFailedError(ctx, result, finalPass, passes.length);
|
|
138
|
+
}
|
|
139
|
+
report({ phase: "done", detail: "Indexing complete" });
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
function filesFailedError(ctx, result, finalPass, passCount) {
|
|
143
|
+
return new EngineError(`Indexing completed with ${result.filesFailed} failed ${result.filesFailed === 1 ? "file" : "files"}`, {
|
|
144
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.FILES_FAILED",
|
|
145
|
+
context: errorDetails([
|
|
146
|
+
workspaceIndexDetail(ctx.workspaceIndex.name),
|
|
147
|
+
detail("filesFailed", result.filesFailed),
|
|
148
|
+
detail("filesScanned", result.filesScanned),
|
|
149
|
+
detail("failedFiles", summarizeFailedFiles(finalPass.stats.failedFiles)),
|
|
150
|
+
detail("failedReasons", summarizeFailedFiles(finalPass.stats.failedFileReasons)),
|
|
151
|
+
detail("hint", passCount > 1
|
|
152
|
+
? "Retried failed files once automatically; if failures persist, fix the failed files or embedding configuration."
|
|
153
|
+
: "If the failure was transient, rerun the same indexing command; if it persists, fix the failed files or embedding configuration."),
|
|
154
|
+
]),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
async function runPathIndexPass(ctx, report, changedPaths, timings, progressBase) {
|
|
158
|
+
reportScanning(report, "Scanning changed paths...", progressBase);
|
|
159
|
+
const scanned = await timings.time("index_scan_paths", async () => {
|
|
160
|
+
const files = [];
|
|
161
|
+
const diagnostics = emptyScanDiagnostics();
|
|
162
|
+
for (const path of changedPaths) {
|
|
163
|
+
throwIfIndexCancelled(ctx);
|
|
164
|
+
const info = await lstat(path).catch(() => null);
|
|
165
|
+
const scan = info?.isDirectory()
|
|
166
|
+
? await scanDirectoryPath(ctx.workspaceIndex.id, ctx.workspaceIndex.rootPaths, path, { signal: ctx.signal })
|
|
167
|
+
: await scanFilePath(ctx.workspaceIndex.id, ctx.workspaceIndex.rootPaths, path, {
|
|
168
|
+
signal: ctx.signal,
|
|
169
|
+
});
|
|
170
|
+
files.push(...scan.files);
|
|
171
|
+
mergeScanDiagnostics(diagnostics, scan.diagnostics);
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
files: [...new Map(files.map((file) => [file.id, file])).values()],
|
|
175
|
+
diagnostics,
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
throwIfIndexCancelled(ctx);
|
|
179
|
+
const exactExisting = [];
|
|
180
|
+
const prefixPaths = [];
|
|
181
|
+
for (const path of changedPaths) {
|
|
182
|
+
const exact = ctx.storage.getFileByPath(path);
|
|
183
|
+
if (exact) {
|
|
184
|
+
exactExisting.push(exact);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
prefixPaths.push(path);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const existing = [
|
|
191
|
+
...new Map([
|
|
192
|
+
...exactExisting,
|
|
193
|
+
...ctx.storage.listFilesByPathPrefixes(prefixPaths),
|
|
194
|
+
].map((file) => [file.id, file])).values(),
|
|
195
|
+
];
|
|
196
|
+
return runDiffPass(ctx, report, scanned.files, existing, timings, progressBase, scanned.diagnostics);
|
|
197
|
+
}
|
|
198
|
+
async function runIndexPass(ctx, report, scanningDetail, timings, progressBase) {
|
|
199
|
+
reportScanning(report, scanningDetail, progressBase);
|
|
200
|
+
const existing = ctx.storage.listFiles();
|
|
201
|
+
const scan = await timings.time("index_scan", () => scanRootPaths(ctx.workspaceIndex.id, ctx.workspaceIndex.rootPaths, {
|
|
202
|
+
signal: ctx.signal,
|
|
203
|
+
knownFiles: existing,
|
|
204
|
+
}));
|
|
205
|
+
throwIfIndexCancelled(ctx);
|
|
206
|
+
return runDiffPass(ctx, report, scan.files, existing, timings, progressBase, scan.diagnostics);
|
|
207
|
+
}
|
|
208
|
+
async function runDiffPass(ctx, report, scannedFiles, existingFiles, timings, progressBase, scanDiagnostics = emptyScanDiagnostics()) {
|
|
209
|
+
const diff = await timings.time("index_diff", () => computeDiffFromFiles(scannedFiles, existingFiles));
|
|
210
|
+
throwIfIndexCancelled(ctx);
|
|
211
|
+
const pending = [...diff.added, ...diff.modified, ...diff.pending];
|
|
212
|
+
report({
|
|
213
|
+
phase: "scanning",
|
|
214
|
+
filesTotal: progressBase?.filesTotal ?? scannedFiles.length,
|
|
215
|
+
filesIndexed: progressBase?.filesSucceeded ?? 0,
|
|
216
|
+
detail: `${diff.added.length} added, ${diff.modified.length} modified, ${diff.pending.length} pending, ${diff.deleted.length} deleted, ${diff.unchanged.length} unchanged`,
|
|
217
|
+
});
|
|
218
|
+
timings.timeSync("index_delete_stale", () => {
|
|
219
|
+
for (const file of diff.deleted) {
|
|
220
|
+
throwIfIndexCancelled(ctx);
|
|
221
|
+
try {
|
|
222
|
+
ctx.storage.deleteFile(file.id);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
throw toEngineError(error, "Indexing failed to delete stale file records", {
|
|
226
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.DELETE_FILE_FAILED",
|
|
227
|
+
context: fileContext(file),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
const reportIndexing = (currentStats, detail, embedding) => {
|
|
233
|
+
report({
|
|
234
|
+
phase: "indexing",
|
|
235
|
+
filesTotal: progressBase?.filesTotal ?? pending.length,
|
|
236
|
+
filesIndexed: (progressBase?.filesSucceeded ?? 0) +
|
|
237
|
+
currentStats.filesIndexed +
|
|
238
|
+
currentStats.filesFailed,
|
|
239
|
+
filesFailed: currentStats.filesFailed,
|
|
240
|
+
detail,
|
|
241
|
+
embedding,
|
|
242
|
+
});
|
|
243
|
+
};
|
|
244
|
+
reportIndexing({
|
|
245
|
+
filesIndexed: 0,
|
|
246
|
+
filesFailed: 0,
|
|
247
|
+
failedFiles: [],
|
|
248
|
+
failedFileReasons: [],
|
|
249
|
+
entitiesCreated: 0,
|
|
250
|
+
});
|
|
251
|
+
const stats = await indexFiles(pending, ctx, reportIndexing, timings);
|
|
252
|
+
throwIfIndexCancelled(ctx);
|
|
253
|
+
return {
|
|
254
|
+
filesScanned: scannedFiles.length,
|
|
255
|
+
scanDiagnostics,
|
|
256
|
+
diff,
|
|
257
|
+
stats,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function reportIndexFinalizing(ctx, report, pass, progressBase) {
|
|
261
|
+
report({
|
|
262
|
+
phase: "indexing",
|
|
263
|
+
filesTotal: progressBase?.filesTotal ??
|
|
264
|
+
pass.diff.added.length +
|
|
265
|
+
pass.diff.modified.length +
|
|
266
|
+
pass.diff.pending.length,
|
|
267
|
+
filesIndexed: (progressBase?.filesSucceeded ?? 0) +
|
|
268
|
+
pass.stats.filesIndexed +
|
|
269
|
+
pass.stats.filesFailed,
|
|
270
|
+
filesFailed: pass.stats.filesFailed,
|
|
271
|
+
detail: "finalizing index",
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
function retryProgressBase(pass) {
|
|
275
|
+
return {
|
|
276
|
+
filesSucceeded: pass.stats.filesIndexed,
|
|
277
|
+
filesTotal: pass.diff.added.length +
|
|
278
|
+
pass.diff.modified.length +
|
|
279
|
+
pass.diff.pending.length,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function reportScanning(report, detail, progressBase) {
|
|
283
|
+
report({
|
|
284
|
+
phase: "scanning",
|
|
285
|
+
...(progressBase
|
|
286
|
+
? {
|
|
287
|
+
filesTotal: progressBase.filesTotal,
|
|
288
|
+
filesIndexed: progressBase.filesSucceeded,
|
|
289
|
+
}
|
|
290
|
+
: {}),
|
|
291
|
+
detail,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
function buildIndexResult(ctx, passes, durationMs, timings) {
|
|
295
|
+
const firstPass = passes[0];
|
|
296
|
+
const retryPasses = passes.slice(1);
|
|
297
|
+
const finalPass = passes[passes.length - 1];
|
|
298
|
+
return {
|
|
299
|
+
filesScanned: finalPass.filesScanned,
|
|
300
|
+
filesAdded: firstPass.diff.added.length +
|
|
301
|
+
retryPasses.reduce((count, pass) => count + pass.diff.added.length, 0),
|
|
302
|
+
filesModified: firstPass.diff.modified.length +
|
|
303
|
+
retryPasses.reduce((count, pass) => count + pass.diff.modified.length, 0),
|
|
304
|
+
filesPending: firstPass.diff.pending.length +
|
|
305
|
+
retryPasses.reduce((count, pass) => count + pass.diff.pending.length, 0),
|
|
306
|
+
filesDeleted: firstPass.diff.deleted.length +
|
|
307
|
+
retryPasses.reduce((count, pass) => count + pass.diff.deleted.length, 0),
|
|
308
|
+
filesUnchanged: firstPass.diff.unchanged.length,
|
|
309
|
+
filesFailed: finalPass.stats.filesFailed,
|
|
310
|
+
entitiesCreated: passes.reduce((count, pass) => count + pass.stats.entitiesCreated, 0),
|
|
311
|
+
durationMs,
|
|
312
|
+
timings: timings.entries(),
|
|
313
|
+
...(finalPass.scanDiagnostics.skippedFiles > 0
|
|
314
|
+
? { scanDiagnostics: finalPass.scanDiagnostics }
|
|
315
|
+
: {}),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
const MAX_SKIPPED_FILE_SAMPLES = 20;
|
|
319
|
+
function emptyScanDiagnostics() {
|
|
320
|
+
return {
|
|
321
|
+
skippedFiles: 0,
|
|
322
|
+
skippedByReason: {
|
|
323
|
+
empty: 0,
|
|
324
|
+
too_large: 0,
|
|
325
|
+
unsupported: 0,
|
|
326
|
+
binary: 0,
|
|
327
|
+
},
|
|
328
|
+
skippedSamples: [],
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function mergeScanDiagnostics(target, source) {
|
|
332
|
+
target.skippedFiles += source.skippedFiles;
|
|
333
|
+
for (const reason of [
|
|
334
|
+
"empty",
|
|
335
|
+
"too_large",
|
|
336
|
+
"unsupported",
|
|
337
|
+
"binary",
|
|
338
|
+
]) {
|
|
339
|
+
target.skippedByReason[reason] += source.skippedByReason[reason];
|
|
340
|
+
}
|
|
341
|
+
const remaining = MAX_SKIPPED_FILE_SAMPLES - target.skippedSamples.length;
|
|
342
|
+
if (remaining > 0) {
|
|
343
|
+
target.skippedSamples.push(...source.skippedSamples.slice(0, remaining));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
async function optimizeStorage(ctx) {
|
|
347
|
+
try {
|
|
348
|
+
await ctx.storage.finalizeWrites();
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
throw toEngineError(error, "Indexing failed to finalize storage", {
|
|
352
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.OPTIMIZE_FAILED",
|
|
353
|
+
context: workspaceIndexContext(ctx.workspaceIndex),
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
async function computeDiffFromFiles(scannedFiles, existingFiles) {
|
|
358
|
+
const existingById = new Map(existingFiles.map((file) => [file.id, file]));
|
|
359
|
+
const seen = new Set();
|
|
360
|
+
const added = [];
|
|
361
|
+
const modified = [];
|
|
362
|
+
const pending = [];
|
|
363
|
+
const unchanged = [];
|
|
364
|
+
for (const file of scannedFiles) {
|
|
365
|
+
seen.add(file.id);
|
|
366
|
+
const existing = existingById.get(file.id);
|
|
367
|
+
if (!existing) {
|
|
368
|
+
added.push(await withContentHash(file));
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if (existing.indexStatus?.indexedTime === null) {
|
|
372
|
+
pending.push(await withContentHash(file));
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (existing.sizeBytes === file.sizeBytes &&
|
|
376
|
+
existing.lastModifiedTime === file.lastModifiedTime &&
|
|
377
|
+
existing.contentHash) {
|
|
378
|
+
unchanged.push(existing);
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
const hashed = await withContentHash(file);
|
|
382
|
+
if (existing.sizeBytes === hashed.sizeBytes &&
|
|
383
|
+
existing.contentHash === hashed.contentHash) {
|
|
384
|
+
unchanged.push(existing);
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
modified.push(hashed);
|
|
388
|
+
}
|
|
389
|
+
const deleted = [...existingById.values()].filter((file) => !seen.has(file.id));
|
|
390
|
+
return { added, modified, pending, deleted, unchanged };
|
|
391
|
+
}
|
|
392
|
+
async function indexFiles(files, ctx, onProgress, timings) {
|
|
393
|
+
const stats = {
|
|
394
|
+
filesIndexed: 0,
|
|
395
|
+
filesFailed: 0,
|
|
396
|
+
failedFiles: [],
|
|
397
|
+
failedFileReasons: [],
|
|
398
|
+
entitiesCreated: 0,
|
|
399
|
+
};
|
|
400
|
+
const batchFiles = [];
|
|
401
|
+
let batchFragmentCount = 0;
|
|
402
|
+
const embeddingScheduler = createEmbeddingScheduler(ctx.embeddingConcurrency, ctx.embeddingModel);
|
|
403
|
+
const embeddingTiming = new ConcurrentTiming(timings, "index_embedding");
|
|
404
|
+
const runningEmbeddings = new Set();
|
|
405
|
+
let firstEmbeddingError;
|
|
406
|
+
const prepareModel = ctx.embeddingModel.info.provider === "local"
|
|
407
|
+
? ctx.embeddingModel.prepare?.bind(ctx.embeddingModel)
|
|
408
|
+
: undefined;
|
|
409
|
+
let modelPrepared = false;
|
|
410
|
+
const reportEmbeddingProgress = (currentStats, detail) => {
|
|
411
|
+
onProgress(currentStats, detail, embeddingScheduler.snapshot());
|
|
412
|
+
};
|
|
413
|
+
const throwFirstEmbeddingError = () => {
|
|
414
|
+
const failure = firstEmbeddingError;
|
|
415
|
+
if (failure) {
|
|
416
|
+
throw failure.error;
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
const flushBatch = async () => {
|
|
420
|
+
throwFirstEmbeddingError();
|
|
421
|
+
throwIfIndexCancelled(ctx);
|
|
422
|
+
if (batchFiles.length === 0) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
const filesToEmbed = batchFiles.splice(0);
|
|
426
|
+
batchFragmentCount = 0;
|
|
427
|
+
await scheduleEmbeddingTask(() => embedAndCommitBatch(filesToEmbed, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming));
|
|
428
|
+
};
|
|
429
|
+
const scheduleEmbeddingTask = async (task) => {
|
|
430
|
+
throwFirstEmbeddingError();
|
|
431
|
+
throwIfIndexCancelled(ctx);
|
|
432
|
+
if (!modelPrepared && prepareModel) {
|
|
433
|
+
// Finish shared initialization before any file or fragment batch is queued.
|
|
434
|
+
try {
|
|
435
|
+
await embeddingTiming.time(() => prepareModel({
|
|
436
|
+
signal: ctx.signal,
|
|
437
|
+
onProgress: (progress) => reportModelDownloadProgress(stats, progress, onProgress, embeddingScheduler),
|
|
438
|
+
}));
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
throwIfIndexCancelled(ctx);
|
|
442
|
+
throw error;
|
|
443
|
+
}
|
|
444
|
+
throwIfIndexCancelled(ctx);
|
|
445
|
+
}
|
|
446
|
+
modelPrepared = true;
|
|
447
|
+
const cleanup = () => {
|
|
448
|
+
runningEmbeddings.delete(promise);
|
|
449
|
+
};
|
|
450
|
+
const promise = Promise.resolve()
|
|
451
|
+
.then(task)
|
|
452
|
+
.then(cleanup, (error) => {
|
|
453
|
+
if (!firstEmbeddingError) {
|
|
454
|
+
firstEmbeddingError = { error };
|
|
455
|
+
}
|
|
456
|
+
cleanup();
|
|
457
|
+
});
|
|
458
|
+
runningEmbeddings.add(promise);
|
|
459
|
+
if (runningEmbeddings.size >= embeddingScheduler.taskConcurrency) {
|
|
460
|
+
await Promise.race(runningEmbeddings);
|
|
461
|
+
}
|
|
462
|
+
throwFirstEmbeddingError();
|
|
463
|
+
throwIfIndexCancelled(ctx);
|
|
464
|
+
};
|
|
465
|
+
try {
|
|
466
|
+
for (const file of files) {
|
|
467
|
+
throwFirstEmbeddingError();
|
|
468
|
+
throwIfIndexCancelled(ctx);
|
|
469
|
+
onProgress(stats, `reading ${file.relativePath}`);
|
|
470
|
+
const prepared = await timings.time("index_prepare", () => prepareFile(file, ctx));
|
|
471
|
+
throwFirstEmbeddingError();
|
|
472
|
+
throwIfIndexCancelled(ctx);
|
|
473
|
+
if ("failedReason" in prepared) {
|
|
474
|
+
recordFileFailed(stats, file, prepared.failedReason);
|
|
475
|
+
onProgress(stats, `failed ${file.relativePath}`);
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
if (prepared.fragments.length === 0) {
|
|
479
|
+
const committed = commitFile(prepared, [], ctx, stats);
|
|
480
|
+
onProgress(stats, finishedFileDetail(committed, file.relativePath));
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (prepared.fragments.length > ctx.embeddingModel.info.limits.maxBatchSize) {
|
|
484
|
+
await flushBatch();
|
|
485
|
+
await scheduleEmbeddingTask(async () => {
|
|
486
|
+
throwIfIndexCancelled(ctx);
|
|
487
|
+
reportEmbeddingProgress(stats, `embedding ${file.relativePath}`);
|
|
488
|
+
await embedAndCommitFile(prepared, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming);
|
|
489
|
+
});
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
if (batchFragmentCount > 0 &&
|
|
493
|
+
batchFragmentCount + prepared.fragments.length >
|
|
494
|
+
ctx.embeddingModel.info.limits.maxBatchSize) {
|
|
495
|
+
await flushBatch();
|
|
496
|
+
}
|
|
497
|
+
batchFiles.push(prepared);
|
|
498
|
+
batchFragmentCount += prepared.fragments.length;
|
|
499
|
+
if (batchFragmentCount === ctx.embeddingModel.info.limits.maxBatchSize) {
|
|
500
|
+
await flushBatch();
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
await flushBatch();
|
|
504
|
+
await Promise.all(runningEmbeddings);
|
|
505
|
+
throwFirstEmbeddingError();
|
|
506
|
+
throwIfIndexCancelled(ctx);
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
await Promise.allSettled(runningEmbeddings);
|
|
510
|
+
throw error;
|
|
511
|
+
}
|
|
512
|
+
throwIfIndexCancelled(ctx);
|
|
513
|
+
return stats;
|
|
514
|
+
}
|
|
515
|
+
async function prepareFile(file, ctx) {
|
|
516
|
+
try {
|
|
517
|
+
throwIfIndexCancelled(ctx);
|
|
518
|
+
const source = await readSource(file);
|
|
519
|
+
const chunkOptions = indexChunkOptions(ctx.embeddingModel.info.limits.maxInputTokens, source.kind === "text" ? source.text : undefined);
|
|
520
|
+
const extracted = await extractForIndexing(source, chunkOptions);
|
|
521
|
+
throwIfIndexCancelled(ctx);
|
|
522
|
+
const fragments = extracted
|
|
523
|
+
.filter(({ fragment }) => ctx.embeddingModel.info.inputKinds.includes(fragment.content.kind))
|
|
524
|
+
.map(({ fragment, embeddingSource }) => ({
|
|
525
|
+
fragment,
|
|
526
|
+
embeddingContent: vectorContentForFragment(fragment, embeddingSource, chunkOptions.maxChunkChars),
|
|
527
|
+
}));
|
|
528
|
+
return { file, fragments };
|
|
529
|
+
}
|
|
530
|
+
catch (error) {
|
|
531
|
+
if (indexIsCancelled(ctx)) {
|
|
532
|
+
throw indexCancellationError(ctx);
|
|
533
|
+
}
|
|
534
|
+
return {
|
|
535
|
+
file,
|
|
536
|
+
failedReason: markFileFailed(ctx, file, error, "prepare"),
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
async function embedAndCommitBatch(files, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming) {
|
|
541
|
+
try {
|
|
542
|
+
throwIfIndexCancelled(ctx);
|
|
543
|
+
const contents = files.flatMap((file) => file.fragments.map((fragment) => fragment.embeddingContent));
|
|
544
|
+
onProgress(stats, `embedding ${describePreparedFiles(files)}`, embeddingScheduler.snapshot());
|
|
545
|
+
const embedding = await embeddingTiming.time(() => embedContentsWithRetry(contents, ctx.embeddingModel, embeddingScheduler, ctx.signal, (progress) => reportModelDownloadProgress(stats, progress, onProgress, embeddingScheduler)));
|
|
546
|
+
const truncatedInputIndexes = new Set(embedding.truncated);
|
|
547
|
+
throwIfIndexCancelled(ctx);
|
|
548
|
+
let offset = 0;
|
|
549
|
+
for (const file of files) {
|
|
550
|
+
throwIfIndexCancelled(ctx);
|
|
551
|
+
const end = offset + file.fragments.length;
|
|
552
|
+
const fileVectors = embedding.vectors.slice(offset, end);
|
|
553
|
+
let truncatedFragmentCount = 0;
|
|
554
|
+
for (let index = offset; index < end; index++) {
|
|
555
|
+
if (truncatedInputIndexes.has(index)) {
|
|
556
|
+
truncatedFragmentCount++;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
offset += file.fragments.length;
|
|
560
|
+
const committed = timings.timeSync("index_commit", () => commitFile(file, fileVectors, ctx, stats, truncatedFragmentCount));
|
|
561
|
+
onProgress(stats, finishedFileDetail(committed, file.file.relativePath));
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
catch (error) {
|
|
565
|
+
if (indexIsCancelled(ctx)) {
|
|
566
|
+
throw indexCancellationError(ctx);
|
|
567
|
+
}
|
|
568
|
+
if (shouldFailFastEmbeddingError(error, ctx.embeddingModel)) {
|
|
569
|
+
throw error;
|
|
570
|
+
}
|
|
571
|
+
for (const file of files) {
|
|
572
|
+
onProgress(stats, `embedding ${file.file.relativePath}`, embeddingScheduler.snapshot());
|
|
573
|
+
await embedAndCommitFile(file, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
async function embedAndCommitFile(file, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming) {
|
|
578
|
+
try {
|
|
579
|
+
throwIfIndexCancelled(ctx);
|
|
580
|
+
const embedding = await embeddingTiming.time(() => embedFragments(file.fragments, ctx.embeddingModel, embeddingScheduler, ctx.signal, (progress) => reportModelDownloadProgress(stats, progress, onProgress, embeddingScheduler)));
|
|
581
|
+
throwIfIndexCancelled(ctx);
|
|
582
|
+
const committed = timings.timeSync("index_commit", () => commitFile(file, embedding.vectors, ctx, stats, embedding.truncated.length));
|
|
583
|
+
onProgress(stats, finishedFileDetail(committed, file.file.relativePath));
|
|
584
|
+
}
|
|
585
|
+
catch (error) {
|
|
586
|
+
if (indexIsCancelled(ctx)) {
|
|
587
|
+
throw indexCancellationError(ctx);
|
|
588
|
+
}
|
|
589
|
+
if (shouldFailFastEmbeddingError(error, ctx.embeddingModel)) {
|
|
590
|
+
throw error;
|
|
591
|
+
}
|
|
592
|
+
const reason = markFileFailed(ctx, file.file, error, "embed");
|
|
593
|
+
recordFileFailed(stats, file.file, reason);
|
|
594
|
+
onProgress(stats, finishedFileDetail(false, file.file.relativePath));
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function commitFile(file, vectors, ctx, stats, truncatedFragmentCount = 0) {
|
|
598
|
+
try {
|
|
599
|
+
throwIfIndexCancelled(ctx);
|
|
600
|
+
if (file.fragments.length !== vectors.length) {
|
|
601
|
+
throw new EngineError("Embedding returned mismatched entity/vector counts", {
|
|
602
|
+
code: "ZVEC_GREP.ENGINE.STORAGE.ENTITY_VECTOR_COUNT_MISMATCH",
|
|
603
|
+
context: `fileId=${file.file.id} fragmentCount=${file.fragments.length} vectorCount=${vectors.length}`,
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
ctx.storage.replaceFile(file.file, file.fragments.map(({ fragment }, index) => ({
|
|
607
|
+
fragment,
|
|
608
|
+
vector: vectors[index],
|
|
609
|
+
})), {
|
|
610
|
+
truncatedFragmentCount,
|
|
611
|
+
});
|
|
612
|
+
stats.filesIndexed++;
|
|
613
|
+
stats.entitiesCreated += countPublicEntities(file.fragments.map(({ fragment }) => fragment));
|
|
614
|
+
return true;
|
|
615
|
+
}
|
|
616
|
+
catch (error) {
|
|
617
|
+
if (indexIsCancelled(ctx)) {
|
|
618
|
+
throw indexCancellationError(ctx);
|
|
619
|
+
}
|
|
620
|
+
const reason = markFileFailed(ctx, file.file, error, "commit");
|
|
621
|
+
recordFileFailed(stats, file.file, reason);
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
function throwIfIndexCancelled(ctx) {
|
|
626
|
+
if (!indexIsCancelled(ctx)) {
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
throw indexCancellationError(ctx);
|
|
630
|
+
}
|
|
631
|
+
function indexIsCancelled(ctx) {
|
|
632
|
+
return ctx.signal?.aborted === true;
|
|
633
|
+
}
|
|
634
|
+
function indexCancellationError(ctx) {
|
|
635
|
+
return ctx.signal?.reason instanceof Error
|
|
636
|
+
? ctx.signal.reason
|
|
637
|
+
: new EngineError("Indexing was cancelled.", {
|
|
638
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.CANCELLED",
|
|
639
|
+
context: workspaceIndexContext(ctx.workspaceIndex),
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
function recordFileFailed(stats, file, reason) {
|
|
643
|
+
stats.filesFailed++;
|
|
644
|
+
stats.failedFiles.push(file.relativePath);
|
|
645
|
+
if (reason) {
|
|
646
|
+
stats.failedFileReasons.push(`${file.relativePath}: ${reason}`);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function summarizeFailedFiles(files) {
|
|
650
|
+
const shown = files.slice(0, 5);
|
|
651
|
+
const remaining = files.length - shown.length;
|
|
652
|
+
return remaining > 0
|
|
653
|
+
? `${shown.join(", ")} and ${remaining} more`
|
|
654
|
+
: shown.join(", ");
|
|
655
|
+
}
|
|
656
|
+
function describePreparedFiles(files) {
|
|
657
|
+
if (files.length === 0) {
|
|
658
|
+
return "0 files";
|
|
659
|
+
}
|
|
660
|
+
if (files.length === 1) {
|
|
661
|
+
return files[0].file.relativePath;
|
|
662
|
+
}
|
|
663
|
+
return `${files.length} files, starting with ${files[0].file.relativePath}`;
|
|
664
|
+
}
|
|
665
|
+
function finishedFileDetail(succeeded, relativePath) {
|
|
666
|
+
return succeeded ? `indexed ${relativePath}` : `failed ${relativePath}`;
|
|
667
|
+
}
|
|
668
|
+
async function readSource(file) {
|
|
669
|
+
let bytes;
|
|
670
|
+
try {
|
|
671
|
+
bytes = await readFile(file.absolutePath);
|
|
672
|
+
}
|
|
673
|
+
catch (error) {
|
|
674
|
+
throw toEngineError(error, "Indexing failed to read source file", {
|
|
675
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.READ_SOURCE_FAILED",
|
|
676
|
+
context: fileContext(file),
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
if (file.kind === "image") {
|
|
680
|
+
return {
|
|
681
|
+
kind: "image",
|
|
682
|
+
file,
|
|
683
|
+
data: bytes,
|
|
684
|
+
format: file.format,
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
return {
|
|
688
|
+
kind: "text",
|
|
689
|
+
file,
|
|
690
|
+
text: bytes.toString("utf8"),
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
async function embedFragments(fragments, model, embeddingScheduler, signal, onModelProgress) {
|
|
694
|
+
const batches = [];
|
|
695
|
+
for (let start = 0; start < fragments.length; start += model.info.limits.maxBatchSize) {
|
|
696
|
+
const batch = fragments.slice(start, start + model.info.limits.maxBatchSize);
|
|
697
|
+
batches.push({ start, fragments: batch });
|
|
698
|
+
}
|
|
699
|
+
const vectors = new Array(fragments.length);
|
|
700
|
+
const truncatedInputIndexes = [];
|
|
701
|
+
const batchAbortController = new AbortController();
|
|
702
|
+
const batchSignal = signal
|
|
703
|
+
? AbortSignal.any([signal, batchAbortController.signal])
|
|
704
|
+
: batchAbortController.signal;
|
|
705
|
+
const results = await Promise.allSettled(batches.map((batch) => embedFragmentBatch(batch.fragments, model, batch.start, embeddingScheduler, batchSignal, onModelProgress, (error) => batchAbortController.abort(error))));
|
|
706
|
+
let firstError;
|
|
707
|
+
for (const [index, result] of results.entries()) {
|
|
708
|
+
if (result.status === "rejected") {
|
|
709
|
+
firstError ??= result.reason;
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
const start = batches[index].start;
|
|
713
|
+
for (const [offset, vector] of result.value.vectors.entries()) {
|
|
714
|
+
vectors[start + offset] = vector;
|
|
715
|
+
}
|
|
716
|
+
truncatedInputIndexes.push(...result.value.truncated.map((inputIndex) => start + inputIndex));
|
|
717
|
+
}
|
|
718
|
+
if (firstError) {
|
|
719
|
+
throw firstError;
|
|
720
|
+
}
|
|
721
|
+
return {
|
|
722
|
+
vectors,
|
|
723
|
+
truncated: truncatedInputIndexes,
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
async function embedFragmentBatch(fragments, model, startIndex, embeddingScheduler, signal, onModelProgress, onTerminalFailure) {
|
|
727
|
+
const contents = fragments.map((fragment) => fragment.embeddingContent);
|
|
728
|
+
try {
|
|
729
|
+
return await embedContentsWithRetry(contents, model, embeddingScheduler, signal, onModelProgress, onTerminalFailure);
|
|
730
|
+
}
|
|
731
|
+
catch (error) {
|
|
732
|
+
if (fragments.length === 1 || shouldFailFastEmbeddingError(error, model)) {
|
|
733
|
+
throw error;
|
|
734
|
+
}
|
|
735
|
+
return await embedFragmentBatchOneByOne(fragments, model, startIndex, embeddingScheduler, signal, onModelProgress, onTerminalFailure);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
async function embedFragmentBatchOneByOne(fragments, model, startIndex, embeddingScheduler, signal, onModelProgress, onTerminalFailure) {
|
|
739
|
+
const vectors = [];
|
|
740
|
+
const truncatedInputIndexes = [];
|
|
741
|
+
for (const [index, fragment] of fragments.entries()) {
|
|
742
|
+
try {
|
|
743
|
+
const result = await embedContentsWithRetry([fragment.embeddingContent], model, embeddingScheduler, signal, onModelProgress, onTerminalFailure);
|
|
744
|
+
vectors.push(result.vectors[0]);
|
|
745
|
+
if (result.truncated.length > 0) {
|
|
746
|
+
truncatedInputIndexes.push(index);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
catch (error) {
|
|
750
|
+
if (shouldFailFastEmbeddingError(error, model)) {
|
|
751
|
+
throw error;
|
|
752
|
+
}
|
|
753
|
+
throw new EngineError("Embedding entity fragment failed after one-by-one fallback", {
|
|
754
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.EMBEDDING_FRAGMENT_FAILED",
|
|
755
|
+
context: errorDetails([
|
|
756
|
+
detail("model", model.info.reference),
|
|
757
|
+
detail("fragmentId", fragment.fragment.id),
|
|
758
|
+
detail("fragmentIndex", startIndex + index),
|
|
759
|
+
isEngineError(error) ? detail("causeCode", error.code) : null,
|
|
760
|
+
error instanceof Error ? detail("cause", error.message) : null,
|
|
761
|
+
isEngineError(error) ? error.context : null,
|
|
762
|
+
]),
|
|
763
|
+
cause: error,
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return {
|
|
768
|
+
vectors,
|
|
769
|
+
truncated: truncatedInputIndexes,
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
async function embedContentsWithRetry(contents, model, embeddingScheduler, signal, onModelProgress, onTerminalFailure) {
|
|
773
|
+
let attempt = 0;
|
|
774
|
+
while (true) {
|
|
775
|
+
let retry = nonRetryableEmbeddingError();
|
|
776
|
+
let delayMs = 0;
|
|
777
|
+
try {
|
|
778
|
+
throwIfAborted(signal);
|
|
779
|
+
const result = await embeddingScheduler.run(() => model.embed(contents, {
|
|
780
|
+
purpose: "document",
|
|
781
|
+
signal,
|
|
782
|
+
onProgress: onModelProgress,
|
|
783
|
+
}), signal, (error) => {
|
|
784
|
+
retry = classifyEmbeddingRetry(error, model);
|
|
785
|
+
delayMs = retryDelayMs(attempt, retry);
|
|
786
|
+
if (retry.retryable) {
|
|
787
|
+
embeddingScheduler.recordRetryableFailure({
|
|
788
|
+
rateLimited: retry.rateLimited,
|
|
789
|
+
delayMs,
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
if (retry.failFast &&
|
|
793
|
+
(!retry.retryable || attempt >= maxRetryAttempts(retry))) {
|
|
794
|
+
onTerminalFailure?.(error);
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
embeddingScheduler.recordSuccess();
|
|
798
|
+
return result;
|
|
799
|
+
}
|
|
800
|
+
catch (error) {
|
|
801
|
+
if (signal?.aborted) {
|
|
802
|
+
throw signal.reason instanceof Error
|
|
803
|
+
? signal.reason
|
|
804
|
+
: new Error("Embedding was cancelled.");
|
|
805
|
+
}
|
|
806
|
+
if (!retry.retryable) {
|
|
807
|
+
retry = classifyEmbeddingRetry(error, model);
|
|
808
|
+
delayMs = retryDelayMs(attempt, retry);
|
|
809
|
+
}
|
|
810
|
+
if (attempt >= maxRetryAttempts(retry) || !retry.retryable) {
|
|
811
|
+
throw error;
|
|
812
|
+
}
|
|
813
|
+
await abortableDelay(delayMs, signal);
|
|
814
|
+
attempt++;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function reportModelDownloadProgress(stats, progress, onProgress, embeddingScheduler) {
|
|
819
|
+
onProgress(stats, `downloading ${progress.model}`, {
|
|
820
|
+
...embeddingScheduler.snapshot(),
|
|
821
|
+
...progress,
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
function createEmbeddingScheduler(requestedConcurrency, model) {
|
|
825
|
+
return new AdaptiveEmbeddingScheduler(resolveEmbeddingConcurrencyPolicy(requestedConcurrency, model));
|
|
826
|
+
}
|
|
827
|
+
function resolveEmbeddingConcurrencyPolicy(requestedConcurrency, model) {
|
|
828
|
+
if (requestedConcurrency !== undefined &&
|
|
829
|
+
Number.isInteger(requestedConcurrency) &&
|
|
830
|
+
requestedConcurrency > 0) {
|
|
831
|
+
const concurrency = Math.min(requestedConcurrency, model.info.limits.maxConcurrentBatches ?? requestedConcurrency);
|
|
832
|
+
return {
|
|
833
|
+
initial: concurrency,
|
|
834
|
+
min: 1,
|
|
835
|
+
max: concurrency,
|
|
836
|
+
adaptive: concurrency > 1,
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
const remote = model.info.provider === "qwen";
|
|
840
|
+
const multimodal = model.info.inputKinds.includes("image");
|
|
841
|
+
const configuredLocalDefault = model.info.defaultConcurrency;
|
|
842
|
+
const localDefault = configuredLocalDefault !== undefined &&
|
|
843
|
+
Number.isInteger(configuredLocalDefault) &&
|
|
844
|
+
configuredLocalDefault > 0
|
|
845
|
+
? configuredLocalDefault
|
|
846
|
+
: 1;
|
|
847
|
+
const initial = remote ? (multimodal ? 4 : 8) : localDefault;
|
|
848
|
+
const max = remote ? (multimodal ? 8 : 12) : localDefault;
|
|
849
|
+
const min = Math.min(initial, 4);
|
|
850
|
+
return {
|
|
851
|
+
initial,
|
|
852
|
+
min,
|
|
853
|
+
max: Math.max(initial, max),
|
|
854
|
+
adaptive: max > 1,
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
class AdaptiveEmbeddingScheduler {
|
|
858
|
+
policy;
|
|
859
|
+
active = 0;
|
|
860
|
+
cooldownUntil = 0;
|
|
861
|
+
currentConcurrency;
|
|
862
|
+
retryableFailures = 0;
|
|
863
|
+
successStreak = 0;
|
|
864
|
+
queue = [];
|
|
865
|
+
constructor(policy) {
|
|
866
|
+
this.policy = policy;
|
|
867
|
+
this.currentConcurrency = policy.initial;
|
|
868
|
+
}
|
|
869
|
+
get taskConcurrency() {
|
|
870
|
+
return this.policy.max;
|
|
871
|
+
}
|
|
872
|
+
async run(task, signal, onError) {
|
|
873
|
+
throwIfAborted(signal);
|
|
874
|
+
await this.waitForCooldown(signal);
|
|
875
|
+
await this.acquire(signal);
|
|
876
|
+
try {
|
|
877
|
+
await this.waitForCooldown(signal);
|
|
878
|
+
throwIfAborted(signal);
|
|
879
|
+
return await task();
|
|
880
|
+
}
|
|
881
|
+
catch (error) {
|
|
882
|
+
onError?.(error);
|
|
883
|
+
throw error;
|
|
884
|
+
}
|
|
885
|
+
finally {
|
|
886
|
+
this.release();
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
recordSuccess() {
|
|
890
|
+
if (!this.policy.adaptive || this.currentConcurrency >= this.policy.max) {
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
this.successStreak++;
|
|
894
|
+
if (this.successStreak <
|
|
895
|
+
Math.max(EMBEDDING_SUCCESS_STREAK_MIN, this.currentConcurrency * 2)) {
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
this.currentConcurrency++;
|
|
899
|
+
this.successStreak = 0;
|
|
900
|
+
this.drainQueue();
|
|
901
|
+
}
|
|
902
|
+
recordRetryableFailure(retry) {
|
|
903
|
+
this.retryableFailures++;
|
|
904
|
+
if (retry.rateLimited && retry.delayMs > 0) {
|
|
905
|
+
this.cooldownUntil = Math.max(this.cooldownUntil, Date.now() + retry.delayMs);
|
|
906
|
+
}
|
|
907
|
+
if (!this.policy.adaptive) {
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
this.currentConcurrency = Math.max(this.policy.min, Math.floor(this.currentConcurrency / 2));
|
|
911
|
+
this.successStreak = 0;
|
|
912
|
+
}
|
|
913
|
+
snapshot() {
|
|
914
|
+
return {
|
|
915
|
+
concurrency: this.currentConcurrency,
|
|
916
|
+
maxConcurrency: this.policy.max,
|
|
917
|
+
retryableFailures: this.retryableFailures,
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
async waitForCooldown(signal) {
|
|
921
|
+
const remaining = this.cooldownUntil - Date.now();
|
|
922
|
+
if (remaining > 0) {
|
|
923
|
+
await abortableDelay(remaining, signal);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
async acquire(signal) {
|
|
927
|
+
throwIfAborted(signal);
|
|
928
|
+
if (this.active < this.currentConcurrency) {
|
|
929
|
+
this.active++;
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
await new Promise((resolve, reject) => {
|
|
933
|
+
const queued = () => {
|
|
934
|
+
signal?.removeEventListener("abort", abort);
|
|
935
|
+
resolve();
|
|
936
|
+
};
|
|
937
|
+
const abort = () => {
|
|
938
|
+
const index = this.queue.indexOf(queued);
|
|
939
|
+
if (index >= 0) {
|
|
940
|
+
this.queue.splice(index, 1);
|
|
941
|
+
}
|
|
942
|
+
reject(abortError(signal));
|
|
943
|
+
};
|
|
944
|
+
if (signal?.aborted) {
|
|
945
|
+
reject(abortError(signal));
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
949
|
+
this.queue.push(queued);
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
release() {
|
|
953
|
+
this.active--;
|
|
954
|
+
this.drainQueue();
|
|
955
|
+
}
|
|
956
|
+
drainQueue() {
|
|
957
|
+
while (this.active < this.currentConcurrency) {
|
|
958
|
+
const resolve = this.queue.shift();
|
|
959
|
+
if (!resolve) {
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
this.active++;
|
|
963
|
+
resolve();
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
function oneLine(value) {
|
|
968
|
+
return value.replace(/\s+/g, " ").trim();
|
|
969
|
+
}
|
|
970
|
+
function shouldFailFastEmbeddingError(error, model) {
|
|
971
|
+
return classifyEmbeddingRetry(error, model).failFast;
|
|
972
|
+
}
|
|
973
|
+
function classifyEmbeddingRetry(error, model) {
|
|
974
|
+
const codes = errorCodes(error);
|
|
975
|
+
const text = errorChainText(error);
|
|
976
|
+
const status = httpStatusFromText(text);
|
|
977
|
+
const remoteEmbedding = model.info.provider !== "local";
|
|
978
|
+
const rateLimited = remoteEmbedding &&
|
|
979
|
+
(status === 429 ||
|
|
980
|
+
/rate limit|quota exceeded|too many requests|request rate increased too quickly/i.test(text));
|
|
981
|
+
const serverError = remoteEmbedding &&
|
|
982
|
+
typeof status === "number" &&
|
|
983
|
+
status >= 500 &&
|
|
984
|
+
status <= 599;
|
|
985
|
+
const requestTimeout = remoteEmbedding && status === 408;
|
|
986
|
+
const requestFailure = codes.some((code) => code.endsWith("_REQUEST_FAILED"));
|
|
987
|
+
const transientNetworkFailure = remoteEmbedding && requestFailure && isTransientNetworkFailure(text);
|
|
988
|
+
const sharedLocalModelFailure = codes.some((code) => code === "ZVEC_GREP.ENGINE.MODELS.MODEL2VEC_DOWNLOAD_FAILED" ||
|
|
989
|
+
code === "ZVEC_GREP.ENGINE.MODELS.MODEL2VEC_LOAD_FAILED" ||
|
|
990
|
+
code === "ZVEC_GREP.ENGINE.MODELS.MODEL2VEC_DISPOSED" ||
|
|
991
|
+
code === "ZVEC_GREP.ENGINE.MODELS.TRANSFORMERS_JS_LOAD_FAILED");
|
|
992
|
+
const retryable = !sharedLocalModelFailure &&
|
|
993
|
+
(rateLimited || serverError || requestTimeout || transientNetworkFailure);
|
|
994
|
+
const remoteConfigurationFailure = remoteEmbedding &&
|
|
995
|
+
(codes.some((code) => code.endsWith("_MISSING_API_KEY") || code.endsWith("_MISSING_ENDPOINT")) ||
|
|
996
|
+
status === 401 ||
|
|
997
|
+
status === 403 ||
|
|
998
|
+
status === 404 ||
|
|
999
|
+
/\b(?:invalid|missing|unauthorized|forbidden)[ _-]?(?:api[ _-]?)?key\b/i.test(text));
|
|
1000
|
+
const permanentRemoteModelFailure = remoteEmbedding &&
|
|
1001
|
+
(codes.includes("ZVEC_GREP.ENGINE.MODELS.EMBEDDING_DIMENSION_MISMATCH") ||
|
|
1002
|
+
(status === 400 && isPermanentRemoteModelBadRequest(text)));
|
|
1003
|
+
return {
|
|
1004
|
+
retryable,
|
|
1005
|
+
rateLimited,
|
|
1006
|
+
failFast: retryable ||
|
|
1007
|
+
sharedLocalModelFailure ||
|
|
1008
|
+
remoteConfigurationFailure ||
|
|
1009
|
+
permanentRemoteModelFailure ||
|
|
1010
|
+
(remoteEmbedding && requestFailure),
|
|
1011
|
+
retryAfterMs: retryAfterMsFromText(text),
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
function isPermanentRemoteModelBadRequest(text) {
|
|
1015
|
+
const providerCode = /\bproviderCode=([^\s]+)/i.exec(text)?.[1];
|
|
1016
|
+
const normalizedCode = providerCode
|
|
1017
|
+
?.replace(/[^a-z0-9]+/gi, "_")
|
|
1018
|
+
.replace(/^_+|_+$/g, "")
|
|
1019
|
+
.toLowerCase();
|
|
1020
|
+
if (normalizedCode &&
|
|
1021
|
+
PERMANENT_REMOTE_MODEL_PROVIDER_CODES.has(normalizedCode)) {
|
|
1022
|
+
return true;
|
|
1023
|
+
}
|
|
1024
|
+
const providerMessage = /\bproviderMessage=(.*?)(?=\s+ZVEC_GREP\.|$)/i.exec(text)?.[1];
|
|
1025
|
+
return (providerMessage !== undefined &&
|
|
1026
|
+
/(?:\b(?:invalid|unsupported|unknown)\b.{0,48}\bmodel\b|\bmodel\b.{0,48}\b(?:invalid|unsupported|unknown|not found|does not exist)\b|\b(?:invalid|unsupported|out of range)\b.{0,48}\bdimensions?\b|\bdimensions?\b.{0,48}\b(?:invalid|unsupported|not supported|out of range|must|should|expected|between|only supports?)\b)/i.test(providerMessage));
|
|
1027
|
+
}
|
|
1028
|
+
function nonRetryableEmbeddingError() {
|
|
1029
|
+
return {
|
|
1030
|
+
retryable: false,
|
|
1031
|
+
rateLimited: false,
|
|
1032
|
+
failFast: false,
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
function errorCodes(error) {
|
|
1036
|
+
const codes = [];
|
|
1037
|
+
for (const current of errorChain(error)) {
|
|
1038
|
+
if (typeof current === "object" &&
|
|
1039
|
+
current !== null &&
|
|
1040
|
+
"code" in current &&
|
|
1041
|
+
typeof current.code === "string") {
|
|
1042
|
+
codes.push(current.code);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
return codes;
|
|
1046
|
+
}
|
|
1047
|
+
function errorChainText(error) {
|
|
1048
|
+
const parts = [];
|
|
1049
|
+
for (const current of errorChain(error)) {
|
|
1050
|
+
if (current instanceof Error) {
|
|
1051
|
+
parts.push(current.name, current.message);
|
|
1052
|
+
}
|
|
1053
|
+
else {
|
|
1054
|
+
parts.push(String(current));
|
|
1055
|
+
}
|
|
1056
|
+
if (isEngineError(current) && current.context) {
|
|
1057
|
+
parts.push(current.context);
|
|
1058
|
+
}
|
|
1059
|
+
if (typeof current === "object" &&
|
|
1060
|
+
current !== null &&
|
|
1061
|
+
"code" in current &&
|
|
1062
|
+
typeof current.code === "string") {
|
|
1063
|
+
parts.push(current.code);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
return parts.join(" ");
|
|
1067
|
+
}
|
|
1068
|
+
function errorChain(error) {
|
|
1069
|
+
const chain = [];
|
|
1070
|
+
const seen = new Set();
|
|
1071
|
+
let current = error;
|
|
1072
|
+
while (current !== undefined && current !== null && !seen.has(current)) {
|
|
1073
|
+
seen.add(current);
|
|
1074
|
+
chain.push(current);
|
|
1075
|
+
if (typeof current !== "object" ||
|
|
1076
|
+
!("cause" in current) ||
|
|
1077
|
+
current.cause === undefined) {
|
|
1078
|
+
break;
|
|
1079
|
+
}
|
|
1080
|
+
current = current.cause;
|
|
1081
|
+
}
|
|
1082
|
+
return chain;
|
|
1083
|
+
}
|
|
1084
|
+
function isTransientNetworkFailure(text) {
|
|
1085
|
+
return /\b(?:EAI_AGAIN|ECONNREFUSED|ECONNRESET|EHOSTUNREACH|ENETDOWN|ENETUNREACH|ENOTFOUND|ETIMEDOUT|UND_ERR_CONNECT_TIMEOUT|UND_ERR_HEADERS_TIMEOUT|UND_ERR_SOCKET|TimeoutError)\b|connection (?:reset|timed out)|network connection (?:failed|was lost)|socket hang up|temporary failure/i.test(text);
|
|
1086
|
+
}
|
|
1087
|
+
function maxRetryAttempts(retry) {
|
|
1088
|
+
return retry.rateLimited
|
|
1089
|
+
? EMBEDDING_RATE_LIMIT_MAX_RETRIES
|
|
1090
|
+
: EMBEDDING_TRANSIENT_MAX_RETRIES;
|
|
1091
|
+
}
|
|
1092
|
+
function retryDelayMs(attempt, retry) {
|
|
1093
|
+
if (typeof retry.retryAfterMs === "number") {
|
|
1094
|
+
return retry.retryAfterMs;
|
|
1095
|
+
}
|
|
1096
|
+
const base = retry.rateLimited
|
|
1097
|
+
? EMBEDDING_RATE_LIMIT_RETRY_BASE_DELAY_MS
|
|
1098
|
+
: EMBEDDING_TRANSIENT_RETRY_BASE_DELAY_MS;
|
|
1099
|
+
const max = retry.rateLimited
|
|
1100
|
+
? EMBEDDING_RATE_LIMIT_RETRY_MAX_DELAY_MS
|
|
1101
|
+
: EMBEDDING_TRANSIENT_RETRY_MAX_DELAY_MS;
|
|
1102
|
+
const exponential = base * 2 ** attempt;
|
|
1103
|
+
const jitter = Math.floor(Math.random() * EMBEDDING_RETRY_JITTER_MS);
|
|
1104
|
+
return Math.min(exponential + jitter, max);
|
|
1105
|
+
}
|
|
1106
|
+
function httpStatusFromText(text) {
|
|
1107
|
+
const match = /\bstatus=(\d{3})\b/i.exec(text);
|
|
1108
|
+
if (!match) {
|
|
1109
|
+
return undefined;
|
|
1110
|
+
}
|
|
1111
|
+
return Number(match[1]);
|
|
1112
|
+
}
|
|
1113
|
+
function retryAfterMsFromText(text) {
|
|
1114
|
+
const milliseconds = /\bretryAfterMs=(\d+)\b/i.exec(text);
|
|
1115
|
+
if (milliseconds) {
|
|
1116
|
+
return Number(milliseconds[1]);
|
|
1117
|
+
}
|
|
1118
|
+
const seconds = /\bretryAfter=(\d+(?:\.\d+)?)\b/i.exec(text);
|
|
1119
|
+
if (seconds) {
|
|
1120
|
+
return Math.round(Number(seconds[1]) * 1000);
|
|
1121
|
+
}
|
|
1122
|
+
return undefined;
|
|
1123
|
+
}
|
|
1124
|
+
function abortableDelay(ms, signal) {
|
|
1125
|
+
if (ms <= 0) {
|
|
1126
|
+
return Promise.resolve();
|
|
1127
|
+
}
|
|
1128
|
+
throwIfAborted(signal);
|
|
1129
|
+
return new Promise((resolve, reject) => {
|
|
1130
|
+
const timer = setTimeout(() => {
|
|
1131
|
+
signal?.removeEventListener("abort", abort);
|
|
1132
|
+
resolve();
|
|
1133
|
+
}, ms);
|
|
1134
|
+
const abort = () => {
|
|
1135
|
+
clearTimeout(timer);
|
|
1136
|
+
reject(abortError(signal));
|
|
1137
|
+
};
|
|
1138
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
function throwIfAborted(signal) {
|
|
1142
|
+
if (signal?.aborted) {
|
|
1143
|
+
throw abortError(signal);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
function abortError(signal) {
|
|
1147
|
+
return signal?.reason instanceof Error
|
|
1148
|
+
? signal.reason
|
|
1149
|
+
: new Error("Embedding was cancelled.");
|
|
1150
|
+
}
|
|
1151
|
+
function countPublicEntities(fragments) {
|
|
1152
|
+
let count = 0;
|
|
1153
|
+
for (const fragment of fragments) {
|
|
1154
|
+
if (!fragment.group || fragment.group === fragment.id) {
|
|
1155
|
+
count++;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
return count;
|
|
1159
|
+
}
|
|
1160
|
+
async function withContentHash(file) {
|
|
1161
|
+
let bytes;
|
|
1162
|
+
try {
|
|
1163
|
+
bytes = await readFile(file.absolutePath);
|
|
1164
|
+
}
|
|
1165
|
+
catch (error) {
|
|
1166
|
+
throw toEngineError(error, "Indexing failed to compute file content hash", {
|
|
1167
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.CONTENT_HASH_FAILED",
|
|
1168
|
+
context: fileContext(file),
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
return {
|
|
1172
|
+
...file,
|
|
1173
|
+
contentHash: sha256Bytes(bytes),
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
function markFileFailed(ctx, file, error, stage) {
|
|
1177
|
+
const reason = fileFailureReason(stage, error);
|
|
1178
|
+
try {
|
|
1179
|
+
ctx.storage.markFileFailed(file, reason);
|
|
1180
|
+
return reason;
|
|
1181
|
+
}
|
|
1182
|
+
catch (markError) {
|
|
1183
|
+
throw toEngineError(markError, "Indexing failed to record file failure", {
|
|
1184
|
+
code: "ZVEC_GREP.ENGINE.INDEXING.MARK_FILE_FAILED",
|
|
1185
|
+
context: `${fileContext(file)} stage=${stage} original=${reason}`,
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
function fileFailureReason(stage, error) {
|
|
1190
|
+
return oneLine(`${stage}: ${errorToMessage(error)}`);
|
|
1191
|
+
}
|
|
1192
|
+
function toEngineError(error, message, options) {
|
|
1193
|
+
if (isEngineError(error)) {
|
|
1194
|
+
return error;
|
|
1195
|
+
}
|
|
1196
|
+
return new EngineError(message, {
|
|
1197
|
+
...options,
|
|
1198
|
+
cause: error,
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
function workspaceIndexContext(workspaceIndex) {
|
|
1202
|
+
return (errorDetails([
|
|
1203
|
+
detail("workspace_index_id", workspaceIndex.id),
|
|
1204
|
+
workspaceIndexDetail(workspaceIndex.name),
|
|
1205
|
+
]) ?? "");
|
|
1206
|
+
}
|
|
1207
|
+
function fileContext(file) {
|
|
1208
|
+
return `fileId=${file.id} path=${file.relativePath}`;
|
|
1209
|
+
}
|
|
1210
|
+
function errorToMessage(error) {
|
|
1211
|
+
if (isEngineError(error) && error.context) {
|
|
1212
|
+
return `${error.code}: ${error.message} (${error.context})`;
|
|
1213
|
+
}
|
|
1214
|
+
if (isEngineError(error)) {
|
|
1215
|
+
return `${error.code}: ${error.message}`;
|
|
1216
|
+
}
|
|
1217
|
+
return error instanceof Error ? error.message : String(error);
|
|
1218
|
+
}
|
|
1219
|
+
//# sourceMappingURL=index.js.map
|