@neuralis/brain-core 0.1.0 → 0.2.0
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 +26 -35
- package/app/cards/DiffView.tsx +9 -7
- package/app/cards/README.md +1 -1
- package/app/cards/diffLine.tsx +2 -4
- package/app/filesystem/README.md +1 -1
- package/app/filesystem/api/index.ts +14 -5
- package/app/filesystem/domain/types.ts +24 -5
- package/app/filesystem/store/filesystemStore.ts +20 -1
- package/app/filesystem/store/listSlice.ts +2 -0
- package/app/filesystem/store/searchSlice.ts +24 -1
- package/app/filesystem/ui/FileTreeSearch.tsx +29 -2
- package/app/filesystem/ui/PendingChangesPanel.tsx +74 -17
- package/app/filesystem/ui/PendingReviewDialog.tsx +45 -20
- package/app/filesystem/ui/README.md +8 -7
- package/app/filesystem/ui/ReembedDialog.tsx +20 -9
- package/app/filesystem/ui/SourceDeleteConfirm.tsx +1 -1
- package/app/filesystem/ui/SourcesPanel.tsx +110 -17
- package/app/filesystem/ui/VectorHealthPanel.tsx +14 -0
- package/app/filesystem/ui/pendingChangesModel.ts +9 -5
- package/dist/app/chunks/{astro-LRAFGXNX.js → astro-B2J4AS2D.js} +1 -1
- package/dist/app/chunks/bash-KY6SAQTH.js +1 -0
- package/dist/app/chunks/c-OBOPYVZ7.js +1 -0
- package/dist/app/chunks/{chunk-5H7ZCOD3.js → chunk-2X3GNKJ3.js} +1 -1
- package/dist/app/chunks/{chunk-XIFGTNRM.js → chunk-JPJIVRFI.js} +1 -1
- package/dist/app/chunks/{chunk-RH3QY5LF.js → chunk-LEFIFWND.js} +1 -1
- package/dist/app/chunks/{chunk-G4RZ3UNX.js → chunk-N6RXX3PH.js} +1 -1
- package/dist/app/chunks/{chunk-QXXJ73P5.js → chunk-PZAW4HPB.js} +1 -1
- package/dist/app/chunks/{chunk-3QLE7Y4L.js → chunk-ZBYWWTO6.js} +1 -1
- package/dist/app/chunks/cpp-TVWVOTEQ.js +1 -0
- package/dist/app/chunks/css-MANZBYA4.js +1 -0
- package/dist/app/chunks/{elixir-M36XROA6.js → elixir-S725Z7EH.js} +1 -1
- package/dist/app/chunks/{elm-7BG6QAIK.js → elm-5WUUSTNB.js} +1 -1
- package/dist/app/chunks/graphql-UGOPR3WU.js +1 -0
- package/dist/app/chunks/html-F6FWTBSG.js +1 -0
- package/dist/app/chunks/java-ANIVV5H5.js +1 -0
- package/dist/app/chunks/javascript-IH374YWG.js +1 -0
- package/dist/app/chunks/json-WGGVVXSB.js +1 -0
- package/dist/app/chunks/jsx-OTXJQD32.js +1 -0
- package/dist/app/chunks/{latex-ZWK7SXB6.js → latex-QCZNITGN.js} +1 -1
- package/dist/app/chunks/lua-4CRZ64PU.js +1 -0
- package/dist/app/chunks/{nginx-2L6VRV3A.js → nginx-V4CYS7WR.js} +1 -1
- package/dist/app/chunks/{perl-PBUAXXK6.js → perl-BHTX7F5B.js} +1 -1
- package/dist/app/chunks/{php-QJX57PA6.js → php-AYGJ5WYZ.js} +1 -1
- package/dist/app/chunks/r-4YFCFA4B.js +1 -0
- package/dist/app/chunks/{ruby-RLOWMVPO.js → ruby-42NMJWAL.js} +1 -1
- package/dist/app/chunks/{scss-B5CAI3HR.js → scss-XD7VU2CU.js} +1 -1
- package/dist/app/chunks/sql-B55MH6JJ.js +1 -0
- package/dist/app/chunks/{svelte-GT3D4C44.js → svelte-WR3QPIJU.js} +1 -1
- package/dist/app/chunks/tsx-S73GAE2L.js +1 -0
- package/dist/app/chunks/typescript-NEWOAB5Z.js +1 -0
- package/dist/app/chunks/{vue-JOW62MHD.js → vue-CCDS4VZK.js} +1 -1
- package/dist/app/chunks/xml-UXKAGLDR.js +1 -0
- package/dist/app/chunks/yaml-UMWKDTK7.js +1 -0
- package/dist/app/host.js +40 -40
- package/dist/scripts/embeddingLockV3.d.ts +30 -0
- package/dist/scripts/embeddingLockV3.d.ts.map +1 -0
- package/dist/scripts/embeddingLockV3.js +100 -0
- package/dist/scripts/embeddingLockV3.js.map +1 -0
- package/dist/scripts/vectorProofV1.d.ts +90 -0
- package/dist/scripts/vectorProofV1.d.ts.map +1 -0
- package/dist/scripts/vectorProofV1.js +220 -0
- package/dist/scripts/vectorProofV1.js.map +1 -0
- package/dist/src/config/env.d.ts +21 -0
- package/dist/src/config/env.d.ts.map +1 -1
- package/dist/src/config/env.js +55 -0
- package/dist/src/config/env.js.map +1 -1
- package/dist/src/connectors/AppSourceConfigStore.d.ts +12 -2
- package/dist/src/connectors/AppSourceConfigStore.d.ts.map +1 -1
- package/dist/src/connectors/AppSourceConfigStore.js +48 -40
- package/dist/src/connectors/AppSourceConfigStore.js.map +1 -1
- package/dist/src/connectors/LocalDiskConnector.d.ts +11 -14
- package/dist/src/connectors/LocalDiskConnector.d.ts.map +1 -1
- package/dist/src/connectors/LocalDiskConnector.js +87 -107
- package/dist/src/connectors/LocalDiskConnector.js.map +1 -1
- package/dist/src/connectors/sourceConfigMutation.d.ts +11 -1
- package/dist/src/connectors/sourceConfigMutation.d.ts.map +1 -1
- package/dist/src/connectors/sourceConfigMutation.js +36 -3
- package/dist/src/connectors/sourceConfigMutation.js.map +1 -1
- package/dist/src/domain/chunker.js +8 -3
- package/dist/src/domain/chunker.js.map +1 -1
- package/dist/src/domain/healthTypes.d.ts +11 -0
- package/dist/src/domain/healthTypes.d.ts.map +1 -1
- package/dist/src/domain/mime.d.ts +1 -0
- package/dist/src/domain/mime.d.ts.map +1 -1
- package/dist/src/domain/mime.js +48 -0
- package/dist/src/domain/mime.js.map +1 -1
- package/dist/src/domain/pendingChange.d.ts +7 -0
- package/dist/src/domain/pendingChange.d.ts.map +1 -1
- package/dist/src/domain/pendingChange.js +11 -0
- package/dist/src/domain/pendingChange.js.map +1 -1
- package/dist/src/domain/sourceConfig.d.ts +7 -14
- package/dist/src/domain/sourceConfig.d.ts.map +1 -1
- package/dist/src/domain/sourceConfig.js +1 -13
- package/dist/src/domain/sourceConfig.js.map +1 -1
- package/dist/src/domain/sourcePackages.d.ts +11 -2
- package/dist/src/domain/sourcePackages.d.ts.map +1 -1
- package/dist/src/domain/syncTrigger.d.ts +2 -3
- package/dist/src/domain/syncTrigger.d.ts.map +1 -1
- package/dist/src/domain/syncTrigger.js +2 -3
- package/dist/src/domain/syncTrigger.js.map +1 -1
- package/dist/src/domain/syncTypes.d.ts +21 -4
- package/dist/src/domain/syncTypes.d.ts.map +1 -1
- package/dist/src/domain/syncTypes.js.map +1 -1
- package/dist/src/domain/text.d.ts +3 -0
- package/dist/src/domain/text.d.ts.map +1 -1
- package/dist/src/domain/text.js +5 -0
- package/dist/src/domain/text.js.map +1 -1
- package/dist/src/domain/types.d.ts +9 -0
- package/dist/src/domain/types.d.ts.map +1 -1
- package/dist/src/embeddings/clients/GeminiEmbedder.d.ts.map +1 -1
- package/dist/src/embeddings/clients/GeminiEmbedder.js +11 -5
- package/dist/src/embeddings/clients/GeminiEmbedder.js.map +1 -1
- package/dist/src/embeddings/clients/OllamaEmbedder.d.ts.map +1 -1
- package/dist/src/embeddings/clients/OllamaEmbedder.js +13 -6
- package/dist/src/embeddings/clients/OllamaEmbedder.js.map +1 -1
- package/dist/src/embeddings/clients/OpenAIEmbedder.d.ts.map +1 -1
- package/dist/src/embeddings/clients/OpenAIEmbedder.js +12 -9
- package/dist/src/embeddings/clients/OpenAIEmbedder.js.map +1 -1
- package/dist/src/embeddings/clients/QwenEmbedder.d.ts.map +1 -1
- package/dist/src/embeddings/clients/QwenEmbedder.js +12 -5
- package/dist/src/embeddings/clients/QwenEmbedder.js.map +1 -1
- package/dist/src/embeddings/clients/VoyageEmbedder.d.ts.map +1 -1
- package/dist/src/embeddings/clients/VoyageEmbedder.js +12 -5
- package/dist/src/embeddings/clients/VoyageEmbedder.js.map +1 -1
- package/dist/src/embeddings/factory.d.ts +2 -0
- package/dist/src/embeddings/factory.d.ts.map +1 -1
- package/dist/src/embeddings/factory.js +23 -3
- package/dist/src/embeddings/factory.js.map +1 -1
- package/dist/src/embeddings/lock.d.ts +22 -11
- package/dist/src/embeddings/lock.d.ts.map +1 -1
- package/dist/src/embeddings/lock.js +47 -29
- package/dist/src/embeddings/lock.js.map +1 -1
- package/dist/src/embeddings/retry.d.ts +5 -0
- package/dist/src/embeddings/retry.d.ts.map +1 -1
- package/dist/src/embeddings/retry.js +35 -0
- package/dist/src/embeddings/retry.js.map +1 -1
- package/dist/src/events/pending.d.ts +56 -0
- package/dist/src/events/pending.d.ts.map +1 -0
- package/dist/src/events/pending.js +108 -0
- package/dist/src/events/pending.js.map +1 -0
- package/dist/src/events/source.d.ts +60 -14
- package/dist/src/events/source.d.ts.map +1 -1
- package/dist/src/events/source.js +149 -31
- package/dist/src/events/source.js.map +1 -1
- package/dist/src/events/vector.d.ts +33 -0
- package/dist/src/events/vector.d.ts.map +1 -0
- package/dist/src/events/vector.js +57 -0
- package/dist/src/events/vector.js.map +1 -0
- package/dist/src/git/GitRunner.d.ts.map +1 -1
- package/dist/src/git/GitRunner.js +11 -8
- package/dist/src/git/GitRunner.js.map +1 -1
- package/dist/src/git/sourceGitControl.d.ts.map +1 -1
- package/dist/src/git/sourceGitControl.js +2 -1
- package/dist/src/git/sourceGitControl.js.map +1 -1
- package/dist/src/index.d.ts +5 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/lifecycle.d.ts +20 -4
- package/dist/src/lifecycle.d.ts.map +1 -1
- package/dist/src/lifecycle.js +5 -1
- package/dist/src/lifecycle.js.map +1 -1
- package/dist/src/routes/health.js +6 -1
- package/dist/src/routes/health.js.map +1 -1
- package/dist/src/routes/pending.d.ts.map +1 -1
- package/dist/src/routes/pending.js +2 -1
- package/dist/src/routes/pending.js.map +1 -1
- package/dist/src/routes/sources.d.ts.map +1 -1
- package/dist/src/routes/sources.js +60 -11
- package/dist/src/routes/sources.js.map +1 -1
- package/dist/src/services/BridgeService.d.ts +2 -2
- package/dist/src/services/BridgeService.d.ts.map +1 -1
- package/dist/src/services/BridgeService.js +11 -2
- package/dist/src/services/BridgeService.js.map +1 -1
- package/dist/src/services/FilesystemService.d.ts +3 -1
- package/dist/src/services/FilesystemService.d.ts.map +1 -1
- package/dist/src/services/FilesystemService.js +3 -1
- package/dist/src/services/FilesystemService.js.map +1 -1
- package/dist/src/services/FilesystemUiService.js +1 -1
- package/dist/src/services/FilesystemUiService.js.map +1 -1
- package/dist/src/services/SyncScheduler.d.ts.map +1 -1
- package/dist/src/services/SyncScheduler.js +0 -14
- package/dist/src/services/SyncScheduler.js.map +1 -1
- package/dist/src/services/VectorHealthService.d.ts.map +1 -1
- package/dist/src/services/VectorHealthService.js +2 -46
- package/dist/src/services/VectorHealthService.js.map +1 -1
- package/dist/src/services/baselineRepair.d.ts +2 -2
- package/dist/src/services/baselineRepair.js +2 -2
- package/dist/src/services/declaredSources.d.ts +8 -1
- package/dist/src/services/declaredSources.d.ts.map +1 -1
- package/dist/src/services/declaredSources.js +19 -19
- package/dist/src/services/declaredSources.js.map +1 -1
- package/dist/src/store/dataFormats.d.ts +14 -3
- package/dist/src/store/dataFormats.d.ts.map +1 -1
- package/dist/src/store/dataFormats.js +15 -5
- package/dist/src/store/dataFormats.js.map +1 -1
- package/dist/src/sync/DeltaSyncRunner.d.ts.map +1 -1
- package/dist/src/sync/DeltaSyncRunner.js +78 -243
- package/dist/src/sync/DeltaSyncRunner.js.map +1 -1
- package/dist/src/sync/SyncJobQueue.d.ts +33 -4
- package/dist/src/sync/SyncJobQueue.d.ts.map +1 -1
- package/dist/src/sync/SyncJobQueue.js +179 -19
- package/dist/src/sync/SyncJobQueue.js.map +1 -1
- package/dist/src/sync/moveDetector.d.ts +2 -1
- package/dist/src/sync/moveDetector.d.ts.map +1 -1
- package/dist/src/sync/moveDetector.js +2 -1
- package/dist/src/sync/moveDetector.js.map +1 -1
- package/dist/src/tools/fs_search.d.ts.map +1 -1
- package/dist/src/tools/fs_search.js +8 -0
- package/dist/src/tools/fs_search.js.map +1 -1
- package/dist/src/tools/fs_write.js +3 -1
- package/dist/src/tools/fs_write.js.map +1 -1
- package/dist/src/usecases/chunkUpsert.d.ts +4 -0
- package/dist/src/usecases/chunkUpsert.d.ts.map +1 -1
- package/dist/src/usecases/chunkUpsert.js +103 -60
- package/dist/src/usecases/chunkUpsert.js.map +1 -1
- package/dist/src/usecases/embeddingBacklog.d.ts +31 -0
- package/dist/src/usecases/embeddingBacklog.d.ts.map +1 -0
- package/dist/src/usecases/embeddingBacklog.js +244 -0
- package/dist/src/usecases/embeddingBacklog.js.map +1 -0
- package/dist/src/usecases/listSourcePackages.js +17 -1
- package/dist/src/usecases/listSourcePackages.js.map +1 -1
- package/dist/src/usecases/mediaLane.d.ts +11 -4
- package/dist/src/usecases/mediaLane.d.ts.map +1 -1
- package/dist/src/usecases/mediaLane.js +67 -30
- package/dist/src/usecases/mediaLane.js.map +1 -1
- package/dist/src/usecases/pendingReview.d.ts.map +1 -1
- package/dist/src/usecases/pendingReview.js +24 -7
- package/dist/src/usecases/pendingReview.js.map +1 -1
- package/dist/src/usecases/purgeExcludedPoints.d.ts +2 -0
- package/dist/src/usecases/purgeExcludedPoints.d.ts.map +1 -1
- package/dist/src/usecases/purgeExcludedPoints.js +3 -2
- package/dist/src/usecases/purgeExcludedPoints.js.map +1 -1
- package/dist/src/usecases/readFiles.d.ts.map +1 -1
- package/dist/src/usecases/readFiles.js +33 -6
- package/dist/src/usecases/readFiles.js.map +1 -1
- package/dist/src/usecases/reconcileSourcePackageRoots.d.ts +31 -0
- package/dist/src/usecases/reconcileSourcePackageRoots.d.ts.map +1 -0
- package/dist/src/usecases/reconcileSourcePackageRoots.js +114 -0
- package/dist/src/usecases/reconcileSourcePackageRoots.js.map +1 -0
- package/dist/src/usecases/searchFiles.d.ts.map +1 -1
- package/dist/src/usecases/searchFiles.js +62 -64
- package/dist/src/usecases/searchFiles.js.map +1 -1
- package/dist/src/usecases/syncIndexedFile.d.ts +42 -0
- package/dist/src/usecases/syncIndexedFile.d.ts.map +1 -0
- package/dist/src/usecases/syncIndexedFile.js +242 -0
- package/dist/src/usecases/syncIndexedFile.js.map +1 -0
- package/dist/src/usecases/syncPointWrites.d.ts +5 -0
- package/dist/src/usecases/syncPointWrites.d.ts.map +1 -1
- package/dist/src/usecases/syncPointWrites.js +32 -5
- package/dist/src/usecases/syncPointWrites.js.map +1 -1
- package/dist/src/usecases/uploadBinary.d.ts.map +1 -1
- package/dist/src/usecases/uploadBinary.js +36 -49
- package/dist/src/usecases/uploadBinary.js.map +1 -1
- package/dist/src/usecases/vector/endpointGuard.js +2 -1
- package/dist/src/usecases/vector/endpointGuard.js.map +1 -1
- package/dist/src/usecases/vector/estimate.d.ts +7 -4
- package/dist/src/usecases/vector/estimate.d.ts.map +1 -1
- package/dist/src/usecases/vector/estimate.js +8 -5
- package/dist/src/usecases/vector/estimate.js.map +1 -1
- package/dist/src/usecases/vector/rebuildGeneration.d.ts +29 -5
- package/dist/src/usecases/vector/rebuildGeneration.d.ts.map +1 -1
- package/dist/src/usecases/vector/rebuildGeneration.js +224 -82
- package/dist/src/usecases/vector/rebuildGeneration.js.map +1 -1
- package/dist/src/usecases/vectorMaintenance.d.ts +46 -4
- package/dist/src/usecases/vectorMaintenance.d.ts.map +1 -1
- package/dist/src/usecases/vectorMaintenance.js +190 -11
- package/dist/src/usecases/vectorMaintenance.js.map +1 -1
- package/dist/src/usecases/write/commit.d.ts +6 -8
- package/dist/src/usecases/write/commit.d.ts.map +1 -1
- package/dist/src/usecases/write/commit.js +96 -112
- package/dist/src/usecases/write/commit.js.map +1 -1
- package/dist/src/usecases/write/content.js +1 -2
- package/dist/src/usecases/write/content.js.map +1 -1
- package/dist/src/usecases/write/ports.d.ts +1 -0
- package/dist/src/usecases/write/ports.d.ts.map +1 -1
- package/dist/src/vector/FilesystemVectorIndex.d.ts +34 -3
- package/dist/src/vector/FilesystemVectorIndex.d.ts.map +1 -1
- package/dist/src/vector/FilesystemVectorIndex.js +254 -20
- package/dist/src/vector/FilesystemVectorIndex.js.map +1 -1
- package/dist/src/vector/InMemoryVectorIndex.d.ts +9 -2
- package/dist/src/vector/InMemoryVectorIndex.d.ts.map +1 -1
- package/dist/src/vector/InMemoryVectorIndex.js +25 -6
- package/dist/src/vector/InMemoryVectorIndex.js.map +1 -1
- package/dist/src/vector/QdrantVectorIndex.d.ts +35 -9
- package/dist/src/vector/QdrantVectorIndex.d.ts.map +1 -1
- package/dist/src/vector/QdrantVectorIndex.js +209 -90
- package/dist/src/vector/QdrantVectorIndex.js.map +1 -1
- package/dist/src/vector/qdrantFilter.d.ts +3 -1
- package/dist/src/vector/qdrantFilter.d.ts.map +1 -1
- package/dist/src/vector/qdrantFilter.js +2 -0
- package/dist/src/vector/qdrantFilter.js.map +1 -1
- package/dist/src/vector/types.d.ts +81 -0
- package/dist/src/vector/types.d.ts.map +1 -1
- package/dist/src/vector/types.js +2 -0
- package/dist/src/vector/types.js.map +1 -1
- package/dist/src/wiring.d.ts +6 -0
- package/dist/src/wiring.d.ts.map +1 -1
- package/dist/src/wiring.js +62 -3
- package/dist/src/wiring.js.map +1 -1
- package/dist/tools/fs_search.json +2 -2
- package/docs/drive-overview.md +2 -2
- package/instructions/brain-model.md +1 -1
- package/package.json +999 -10
- package/rules/brain-rules.md +1 -1
- package/skills/connect-business-data/SKILL.md +6 -4
- package/skills/create-package/SKILL.md +14 -12
- package/skills/create-package/references/config-and-credentials.md +3 -3
- package/skills/create-package/references/connectors-and-uri-policies.md +5 -3
- package/skills/create-package/references/manifest-and-discovery.md +4 -3
- package/skills/create-package/references/runtime-and-build.md +9 -9
- package/skills/create-package/references/skills-and-context.md +3 -3
- package/skills/create-package/references/surfaces-and-design.md +2 -1
- package/skills/create-package/references/team-and-workflows.md +2 -2
- package/skills/create-package/references/test-and-ship.md +15 -15
- package/skills/create-package/references/tools-and-schemas.md +1 -1
- package/skills/create-package/scripts/check-package.sh +3 -3
- package/skills/create-package/scripts/verify-loaded.sh +16 -16
- package/skills/manage-files/SKILL.md +2 -2
- package/skills/manage-sources/SKILL.md +16 -15
- package/skills/manage-sources/scripts/reembed.sh +9 -3
- package/skills/sync/SKILL.md +4 -4
- package/tools/fs_search.json +2 -2
- package/dist/app/chunks/bash-M45GHPBQ.js +0 -1
- package/dist/app/chunks/c-X3REOAWD.js +0 -1
- package/dist/app/chunks/cpp-VOWI743W.js +0 -1
- package/dist/app/chunks/css-2ZVPHE25.js +0 -1
- package/dist/app/chunks/graphql-7V2NP54C.js +0 -1
- package/dist/app/chunks/html-UF3LFLNA.js +0 -1
- package/dist/app/chunks/java-PNTAU4L3.js +0 -1
- package/dist/app/chunks/javascript-4JJTQGEL.js +0 -1
- package/dist/app/chunks/json-VLQ6D3JH.js +0 -1
- package/dist/app/chunks/jsx-KQKSLAAH.js +0 -1
- package/dist/app/chunks/lua-UFPYUJ2O.js +0 -1
- package/dist/app/chunks/r-QEZB45SR.js +0 -1
- package/dist/app/chunks/sql-DTZHNUZP.js +0 -1
- package/dist/app/chunks/tsx-WPR2WRZD.js +0 -1
- package/dist/app/chunks/typescript-LWO362OA.js +0 -1
- package/dist/app/chunks/xml-A5H72RTS.js +0 -1
- package/dist/app/chunks/yaml-MXHSIJCI.js +0 -1
- package/dist/src/services/purgePendingNodes.d.ts +0 -32
- package/dist/src/services/purgePendingNodes.d.ts.map +0 -1
- package/dist/src/services/purgePendingNodes.js +0 -61
- package/dist/src/services/purgePendingNodes.js.map +0 -1
- /package/dist/app/chunks/{chunk-737Y24HS.js → chunk-3JASFUPF.js} +0 -0
- /package/dist/app/chunks/{chunk-DKD2L57N.js → chunk-6EYLOQ4F.js} +0 -0
- /package/dist/app/chunks/{chunk-KYH5EHHY.js → chunk-7YRZ5LOQ.js} +0 -0
- /package/dist/app/chunks/{chunk-6TC3DVWV.js → chunk-C7LU4RID.js} +0 -0
- /package/dist/app/chunks/{chunk-TY33BMKR.js → chunk-CD52YDZP.js} +0 -0
- /package/dist/app/chunks/{chunk-OLEIKJ3X.js → chunk-DCJKTCJ5.js} +0 -0
- /package/dist/app/chunks/{chunk-QLBZ4JR2.js → chunk-EXIU2KBV.js} +0 -0
- /package/dist/app/chunks/{chunk-SFUNBBEA.js → chunk-FDP4LO4P.js} +0 -0
- /package/dist/app/chunks/{chunk-BGRVFATT.js → chunk-HNETUS7U.js} +0 -0
- /package/dist/app/chunks/{chunk-4QNMHAIR.js → chunk-LBFZJEDR.js} +0 -0
- /package/dist/app/chunks/{chunk-TRRUHDPN.js → chunk-ULRXL5VM.js} +0 -0
- /package/dist/app/chunks/{chunk-S6B4WWIF.js → chunk-XJK47SMJ.js} +0 -0
- /package/dist/app/chunks/{chunk-DZ5C5FKI.js → chunk-YYAAIAU2.js} +0 -0
- /package/dist/app/chunks/{clojure-OC6KPSJO.js → clojure-4QCN4GHG.js} +0 -0
- /package/dist/app/chunks/{core-KGE7TQJ3.js → core-ZHTX3P2Q.js} +0 -0
- /package/dist/app/chunks/{csharp-E7VIFITL.js → csharp-HQWR4N52.js} +0 -0
- /package/dist/app/chunks/{dark-plus-BOSCZXE4.js → dark-plus-GYPKKZES.js} +0 -0
- /package/dist/app/chunks/{dart-6Y3X27SZ.js → dart-FO4TTVO2.js} +0 -0
- /package/dist/app/chunks/{diff-TR2NPA64.js → diff-SHANV4YC.js} +0 -0
- /package/dist/app/chunks/{dist-T32U2VVR.js → dist-WSDF34DM.js} +0 -0
- /package/dist/app/chunks/{docker-N33HOW54.js → docker-VX55HTDS.js} +0 -0
- /package/dist/app/chunks/{go-6DGG5UTP.js → go-5UCPEC6S.js} +0 -0
- /package/dist/app/chunks/{haskell-KK36PMMM.js → haskell-25PJ3OSG.js} +0 -0
- /package/dist/app/chunks/{ini-VHVQ5TPB.js → ini-BJIVD5MK.js} +0 -0
- /package/dist/app/chunks/{kotlin-44WS3JHE.js → kotlin-MNR6XOEQ.js} +0 -0
- /package/dist/app/chunks/{less-5WZ3KLAK.js → less-KA5JURX6.js} +0 -0
- /package/dist/app/chunks/{light-plus-HGGBC43G.js → light-plus-QXH2UUWZ.js} +0 -0
- /package/dist/app/chunks/{make-X7RXMVW7.js → make-LPD5KOCO.js} +0 -0
- /package/dist/app/chunks/{markdown-46IKUAIB.js → markdown-LMWF6MQR.js} +0 -0
- /package/dist/app/chunks/{mdx-DGLHDAO7.js → mdx-WPMNN5BS.js} +0 -0
- /package/dist/app/chunks/{nix-Q6FYQZLX.js → nix-QGWLUUMZ.js} +0 -0
- /package/dist/app/chunks/{powershell-L4KJJ6DK.js → powershell-7GSBBEFF.js} +0 -0
- /package/dist/app/chunks/{prisma-IKS4LVH2.js → prisma-FPMPQISO.js} +0 -0
- /package/dist/app/chunks/{proto-AYCDOTJO.js → proto-7KDX5Z2U.js} +0 -0
- /package/dist/app/chunks/{python-KXMCSAFQ.js → python-CFG4FTQW.js} +0 -0
- /package/dist/app/chunks/{rust-J3BC3P6V.js → rust-X7A37WU3.js} +0 -0
- /package/dist/app/chunks/{scala-AYIIHBJK.js → scala-OYHJSUG2.js} +0 -0
- /package/dist/app/chunks/{swift-QPHHCQD3.js → swift-PE7VNYW5.js} +0 -0
- /package/dist/app/chunks/{toml-UV7JK7K7.js → toml-ETQRDILP.js} +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { indexEligibilityReader, recoveryPayload } from './indexEligibility.js';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { computeContentHash } from '../../domain/contentHash.js';
|
|
4
|
+
import { mediaEmbeddingInput } from '../syncPointWrites.js';
|
|
5
|
+
import { VECTOR_WRITE_REQUEST_BYTES } from '../../vector/types.js';
|
|
2
6
|
import { getActiveLock, mutateLock, sameVectorSpace, generationOf, } from '../../embeddings/lock.js';
|
|
3
7
|
import { embedModelMarker, supportsEmbedInput } from '../../embeddings/models.js';
|
|
4
|
-
import { invalidateEmbedders } from '../../embeddings/factory.js';
|
|
5
|
-
import { generationFor, getBrainConfigBoolean, getBrainConfigNumber, resolveEmbeddingModel, resolveTargetEmbeddingShape, } from '../../config/env.js';
|
|
8
|
+
import { embeddingUnavailableReason, invalidateEmbedders } from '../../embeddings/factory.js';
|
|
9
|
+
import { generationFor, getBrainConfigBoolean, getBrainConfigNumber, resolveEmbeddingModel, resolveTargetEmbeddingShape, embeddingEnabled, } from '../../config/env.js';
|
|
6
10
|
import { buildEmbedderFor } from '../../embeddings/factory.js';
|
|
7
11
|
import { readHealContent } from '../syncPointWrites.js';
|
|
8
12
|
import { readMediaInputForPoint } from '../mediaLane.js';
|
|
@@ -39,6 +43,9 @@ export class RebuildError extends Error {
|
|
|
39
43
|
this.name = 'RebuildError';
|
|
40
44
|
}
|
|
41
45
|
}
|
|
46
|
+
function hasWriteBoundary(vector) {
|
|
47
|
+
return typeof vector.exclusive === 'function';
|
|
48
|
+
}
|
|
42
49
|
/**
|
|
43
50
|
* The text a point's vector is built from — the ONE rule the scan and the
|
|
44
51
|
* mirror share: a chunk embeds its own `content`; a document embeds its
|
|
@@ -62,7 +69,7 @@ export function embedTextForPoint(payload) {
|
|
|
62
69
|
* content-less text point's stored text through the same text rule, a media
|
|
63
70
|
* point's bytes or page. `undefined` = unreadable.
|
|
64
71
|
*/
|
|
65
|
-
async function inputFor(deps, payload) {
|
|
72
|
+
export async function inputFor(deps, payload) {
|
|
66
73
|
const inline = embedTextForPoint(payload);
|
|
67
74
|
if (inline !== undefined)
|
|
68
75
|
return { kind: 'text', text: inline };
|
|
@@ -77,7 +84,7 @@ async function inputFor(deps, payload) {
|
|
|
77
84
|
* one call each. A media input the target cannot take (`supportsEmbedInput`
|
|
78
85
|
* over its `modalities`) answers `null` — the caller leaves it out.
|
|
79
86
|
*/
|
|
80
|
-
async function embedInputs(embedder, inputs, signal) {
|
|
87
|
+
export async function embedInputs(embedder, inputs, signal) {
|
|
81
88
|
const out = new Array(inputs.length).fill(null);
|
|
82
89
|
const textAt = [];
|
|
83
90
|
for (let i = 0; i < inputs.length; i += 1)
|
|
@@ -101,10 +108,13 @@ async function embedInputs(embedder, inputs, signal) {
|
|
|
101
108
|
}
|
|
102
109
|
/** Eligibility handles recovery state; compare content/provenance independently of index markers. */
|
|
103
110
|
function samePayload(a, b) {
|
|
111
|
+
if (a.embedding_status !== 'current' || typeof a.embedding_generation !== 'number'
|
|
112
|
+
|| typeof a.embedding_input_hash !== 'string' || typeof a.embed_model !== 'string')
|
|
113
|
+
return false;
|
|
104
114
|
return stableStringify(withoutMarker(a)) === stableStringify(withoutMarker(b));
|
|
105
115
|
}
|
|
106
116
|
function withoutMarker(payload) {
|
|
107
|
-
const { embed_model: _marker, recovery_only: _recovery, ...rest } = payload;
|
|
117
|
+
const { embed_model: _marker, recovery_only: _recovery, embedding_status: _status, embedding_generation: _generation, embedding_input_hash: _hash, embedding_updated_at: _updated, ...rest } = payload;
|
|
108
118
|
return rest;
|
|
109
119
|
}
|
|
110
120
|
function stableStringify(value) {
|
|
@@ -134,6 +144,7 @@ export class GenerationMirror {
|
|
|
134
144
|
#shadow;
|
|
135
145
|
#embedder;
|
|
136
146
|
#marker;
|
|
147
|
+
generation;
|
|
137
148
|
#queue = [];
|
|
138
149
|
#queueBytes = 0;
|
|
139
150
|
#dirty = new Set();
|
|
@@ -150,6 +161,7 @@ export class GenerationMirror {
|
|
|
150
161
|
this.#shadow = shadow;
|
|
151
162
|
this.#embedder = embedder;
|
|
152
163
|
this.#marker = embedModelMarker(target.modelId, target.dimension, target.embedRecipe);
|
|
164
|
+
this.generation = seed.generation;
|
|
153
165
|
for (const id of seed.dirty)
|
|
154
166
|
this.#dirty.add(id);
|
|
155
167
|
for (const filter of seed.failedDeletes)
|
|
@@ -194,6 +206,17 @@ export class GenerationMirror {
|
|
|
194
206
|
afterRetention(point) {
|
|
195
207
|
this.#enqueue({ kind: 'retention', id: point.id, payload: point.payload });
|
|
196
208
|
}
|
|
209
|
+
/** A native multi-operation failure may have committed a prefix; re-read before swap. */
|
|
210
|
+
afterUncertainWrite(ids) {
|
|
211
|
+
for (const id of ids)
|
|
212
|
+
this.#dirty.add(id);
|
|
213
|
+
this.#persistDirty();
|
|
214
|
+
}
|
|
215
|
+
/** A failed primary delete is reconciliation intent, never a destructive replay. */
|
|
216
|
+
afterUncertainDelete(filter) {
|
|
217
|
+
this.#failedDeletes.push(filter);
|
|
218
|
+
this.#persistDirty();
|
|
219
|
+
}
|
|
197
220
|
afterDelete(filter) {
|
|
198
221
|
this.#enqueue({ kind: 'delete', filter });
|
|
199
222
|
}
|
|
@@ -250,8 +273,18 @@ export class GenerationMirror {
|
|
|
250
273
|
window.touched.add(item.id);
|
|
251
274
|
return;
|
|
252
275
|
}
|
|
253
|
-
const
|
|
276
|
+
const input = await inputFor(this.#deps, item.payload);
|
|
277
|
+
if (!input)
|
|
278
|
+
throw new RebuildError('classification_unresolved', 'source-input-unavailable');
|
|
279
|
+
const payload = searchablePayload(item.payload, this.#marker, this.generation, input);
|
|
254
280
|
if (item.kind === 'payload' && !item.payload.recovery_only) {
|
|
281
|
+
const previous = (await this.#deps.generations.payloads(this.#shadow, [item.id])).get(item.id);
|
|
282
|
+
if (!previous || previous.embedding_status !== 'current' || previous.embedding_generation !== this.generation
|
|
283
|
+
|| previous.embed_model !== this.#marker || previous.embedding_input_hash !== payload.embedding_input_hash) {
|
|
284
|
+
payload.embedding_status = 'stale';
|
|
285
|
+
this.#dirty.add(item.id);
|
|
286
|
+
this.#persistDirty();
|
|
287
|
+
}
|
|
255
288
|
if (!(await this.#deps.generations.overwritePayload(this.#shadow, item.id, payload))) {
|
|
256
289
|
// Not in the shadow yet: the scan may already hold an OLDER read of
|
|
257
290
|
// it, so the point is re-processed from the active one before the swap.
|
|
@@ -261,7 +294,6 @@ export class GenerationMirror {
|
|
|
261
294
|
}
|
|
262
295
|
}
|
|
263
296
|
else {
|
|
264
|
-
const input = await inputFor(this.#deps, item.payload);
|
|
265
297
|
const [embedding] = input ? await embedInputs(this.#embedder, [input]) : [null];
|
|
266
298
|
if (!embedding) {
|
|
267
299
|
throw new RebuildError('classification_unresolved', 'source-input-unavailable');
|
|
@@ -345,6 +377,8 @@ let runningLoop = null;
|
|
|
345
377
|
export function getGenerationMirror() {
|
|
346
378
|
return installedMirror;
|
|
347
379
|
}
|
|
380
|
+
/** Cancellation cannot take over maintenance admission while setup is still awaiting creation. */
|
|
381
|
+
export function isRebuildRunning() { return runningLoop !== null; }
|
|
348
382
|
/** Test seam: drop the process state between cases. */
|
|
349
383
|
export function resetRebuildStateForTesting() {
|
|
350
384
|
installedMirror = null;
|
|
@@ -373,14 +407,18 @@ export async function pauseInterruptedRebuild(appRoot, logger) {
|
|
|
373
407
|
* Start a rebuild toward the configured target, or resume a paused one.
|
|
374
408
|
* Returns once the run is set up (shadow created, mirror installed); the scan
|
|
375
409
|
* continues in the background and calls `release` when it ends — the caller's
|
|
376
|
-
* latch is held until then.
|
|
410
|
+
* latch is held until then. `onEnd` hears how the background run ended
|
|
411
|
+
* (`completed` = swapped to the new generation, `paused` = dirty or failed); a
|
|
412
|
+
* cancel reports nothing. It lives in this process only (never in the lock).
|
|
377
413
|
*/
|
|
378
|
-
export async function startRebuild(deps, release) {
|
|
414
|
+
export async function startRebuild(deps, release, onEnd) {
|
|
379
415
|
if (runningLoop)
|
|
380
416
|
throw new RebuildError('rebuild_running', 'A rebuild is already running in this process');
|
|
417
|
+
if (!embeddingEnabled())
|
|
418
|
+
throw new RebuildError('target_unavailable', 'embedding_disabled');
|
|
381
419
|
const lock = await getActiveLock(deps.appRoot);
|
|
382
420
|
if (!lock)
|
|
383
|
-
throw new RebuildError('no_active_generation', '
|
|
421
|
+
throw new RebuildError('no_active_generation', 'Initialize the serving data index before starting a rebuild');
|
|
384
422
|
let rebuild = lock.rebuild;
|
|
385
423
|
const resumed = Boolean(rebuild);
|
|
386
424
|
if (!rebuild) {
|
|
@@ -388,15 +426,18 @@ export async function startRebuild(deps, release) {
|
|
|
388
426
|
if ('error' in resolved)
|
|
389
427
|
throw new RebuildError('target_unavailable', resolved.error);
|
|
390
428
|
const target = generationFor(resolved.info, resolved.dimension);
|
|
391
|
-
if (sameVectorSpace(generationOf(lock), target)) {
|
|
429
|
+
if (lock.mode === 'dense' && sameVectorSpace(generationOf(lock), target)) {
|
|
392
430
|
throw new RebuildError('no_switch_pending', 'The configured model already is the active one — nothing to rebuild');
|
|
393
431
|
}
|
|
394
|
-
const generation = lock.generation + 1;
|
|
432
|
+
const generation = lock.mode === 'dense' ? lock.generation + 1 : 1;
|
|
395
433
|
rebuild = {
|
|
396
434
|
target,
|
|
397
435
|
generation,
|
|
398
|
-
shadowCollection:
|
|
436
|
+
shadowCollection: lock.mode === 'metadata'
|
|
437
|
+
? `${deps.generations.collectionName(generation)}__shadow_${randomUUID()}`
|
|
438
|
+
: deps.generations.collectionName(generation),
|
|
399
439
|
state: 'running',
|
|
440
|
+
phase: 'prepared',
|
|
400
441
|
startedAt: now(deps),
|
|
401
442
|
processed: 0,
|
|
402
443
|
dirty: [],
|
|
@@ -411,36 +452,68 @@ export async function startRebuild(deps, release) {
|
|
|
411
452
|
embedder = await deps.buildTargetEmbedder(rebuild.target);
|
|
412
453
|
}
|
|
413
454
|
catch (error) {
|
|
414
|
-
|
|
455
|
+
const reason = embeddingUnavailableReason(error);
|
|
456
|
+
if (!reason)
|
|
457
|
+
throw error;
|
|
458
|
+
throw new RebuildError('target_unavailable', reason);
|
|
459
|
+
}
|
|
460
|
+
if (!resumed && await deps.generations.exists(rebuild.shadowCollection)) {
|
|
461
|
+
throw new RebuildError('target_unavailable', 'unrecorded_shadow_exists');
|
|
415
462
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
if (
|
|
420
|
-
|
|
463
|
+
// Record ownership before creating a shadow; restart/cancel never guesses.
|
|
464
|
+
await mutateLock(deps.appRoot, current => current ? { ...current, rebuild } : null);
|
|
465
|
+
try {
|
|
466
|
+
if (!(await deps.generations.exists(rebuild.shadowCollection))) {
|
|
467
|
+
await deps.generations.create(rebuild.shadowCollection, rebuild.target.dimension);
|
|
468
|
+
// A shadow that vanished while paused is rebuilt from nothing.
|
|
469
|
+
if (resumed)
|
|
470
|
+
rebuild = { ...rebuild, processed: 0, dirty: [], failedDeletes: [] };
|
|
471
|
+
}
|
|
472
|
+
await deps.generations.validate(rebuild.shadowCollection, rebuild.target.dimension);
|
|
421
473
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
await deps.generations.create(rebuild.shadowCollection, rebuild.target.dimension);
|
|
474
|
+
catch (error) {
|
|
475
|
+
await pause(deps, 'error', error instanceof Error ? error.message : String(error));
|
|
476
|
+
throw error;
|
|
426
477
|
}
|
|
427
478
|
// A resume re-verifies from the start (see the module doc).
|
|
428
479
|
const running = {
|
|
429
480
|
...rebuild,
|
|
430
481
|
state: 'running',
|
|
482
|
+
phase: 'copying',
|
|
431
483
|
cursor: undefined,
|
|
432
484
|
scanDone: false,
|
|
433
485
|
updatedAt: now(deps),
|
|
434
486
|
};
|
|
435
487
|
delete running.pausedReason;
|
|
436
488
|
delete running.lastError;
|
|
437
|
-
await mutateLock(deps.appRoot, (current) => current ? { ...current, rebuild: running } : null);
|
|
438
489
|
const mirror = new GenerationMirror(deps, running.shadowCollection, embedder, running.target, running);
|
|
439
|
-
|
|
490
|
+
const install = async () => {
|
|
491
|
+
await mutateLock(deps.appRoot, current => current ? { ...current, rebuild: running } : null);
|
|
492
|
+
installedMirror = mirror;
|
|
493
|
+
};
|
|
494
|
+
try {
|
|
495
|
+
await deps.vector.exclusive(install);
|
|
496
|
+
}
|
|
497
|
+
catch (error) {
|
|
498
|
+
await pause(deps, 'error', error instanceof Error ? error.message : String(error));
|
|
499
|
+
throw error;
|
|
500
|
+
}
|
|
440
501
|
const abort = new AbortController();
|
|
441
502
|
const done = runLoop(deps, mirror, embedder, abort.signal)
|
|
442
503
|
.catch(async (error) => {
|
|
443
504
|
await pause(deps, 'error', error instanceof Error ? error.message : String(error));
|
|
505
|
+
})
|
|
506
|
+
.then(async () => {
|
|
507
|
+
if (!onEnd)
|
|
508
|
+
return;
|
|
509
|
+
const after = await getActiveLock(deps.appRoot).catch(() => null);
|
|
510
|
+
try {
|
|
511
|
+
if (after?.mode === 'dense' && !after.rebuild && after.generation === running.generation)
|
|
512
|
+
onEnd('completed');
|
|
513
|
+
else if (after?.rebuild?.state === 'paused')
|
|
514
|
+
onEnd('paused');
|
|
515
|
+
}
|
|
516
|
+
catch { /* a notification never changes the run */ }
|
|
444
517
|
})
|
|
445
518
|
.finally(() => {
|
|
446
519
|
runningLoop = null;
|
|
@@ -478,6 +551,24 @@ async function runLoop(deps, mirror, embedder, signal) {
|
|
|
478
551
|
const ids = page.points.map((p) => p.id);
|
|
479
552
|
const inShadow = await deps.generations.payloads(shadow, ids);
|
|
480
553
|
const work = [];
|
|
554
|
+
let inputBytes = 0;
|
|
555
|
+
const flush = async () => {
|
|
556
|
+
if (!work.length)
|
|
557
|
+
return;
|
|
558
|
+
const vectors = await embedInputs(embedder, work.map(w => w.input), signal);
|
|
559
|
+
const points = work.flatMap((w, i) => {
|
|
560
|
+
if (!vectors[i])
|
|
561
|
+
throw new RebuildError('classification_unresolved', 'unsupported-input-kind');
|
|
562
|
+
if (window.touched.has(w.id))
|
|
563
|
+
return [];
|
|
564
|
+
return [{ id: w.id, vector: vectors[i], payload: searchablePayload(w.payload, mirror.marker, mirror.generation, w.input) }];
|
|
565
|
+
});
|
|
566
|
+
if (points.length)
|
|
567
|
+
await deps.generations.upsert(shadow, points, { signal });
|
|
568
|
+
processed += points.length;
|
|
569
|
+
work.length = 0;
|
|
570
|
+
inputBytes = 0;
|
|
571
|
+
};
|
|
481
572
|
const leaveOut = (uri) => {
|
|
482
573
|
unreadable.count += 1;
|
|
483
574
|
if (unreadable.sample.length < UNREADABLE_SAMPLE && typeof uri === 'string')
|
|
@@ -488,30 +579,25 @@ async function runLoop(deps, mirror, embedder, signal) {
|
|
|
488
579
|
const copy = inShadow.get(point.id);
|
|
489
580
|
if (!(await carryEligibility(deps, shadow, point.id, point.payload)))
|
|
490
581
|
continue;
|
|
491
|
-
if (copy && !copy.recovery_only && samePayload(copy, payload))
|
|
492
|
-
continue;
|
|
493
582
|
const input = await inputFor(deps, point.payload);
|
|
494
583
|
if (input === undefined) {
|
|
495
584
|
leaveOut(point.payload.uri);
|
|
496
585
|
throw new RebuildError('classification_unresolved', 'source-input-unavailable');
|
|
497
586
|
}
|
|
587
|
+
const proof = searchablePayload(payload, mirror.marker, mirror.generation, input);
|
|
588
|
+
if (copy && !copy.recovery_only && samePayload(copy, payload) && copy.embedding_status === 'current'
|
|
589
|
+
&& copy.embedding_generation === mirror.generation && copy.embed_model === mirror.marker
|
|
590
|
+
&& copy.embedding_input_hash === proof.embedding_input_hash)
|
|
591
|
+
continue;
|
|
592
|
+
const bytes = input.kind === 'text' ? Buffer.byteLength(input.text, 'utf8') : input.bytes.length;
|
|
593
|
+
if (work.length && inputBytes + bytes > VECTOR_WRITE_REQUEST_BYTES)
|
|
594
|
+
await flush();
|
|
498
595
|
work.push({ id: point.id, input, payload });
|
|
596
|
+
inputBytes += bytes;
|
|
597
|
+
if (inputBytes >= VECTOR_WRITE_REQUEST_BYTES)
|
|
598
|
+
await flush();
|
|
499
599
|
}
|
|
500
|
-
|
|
501
|
-
const vectors = await embedInputs(embedder, work.map((w) => w.input), signal);
|
|
502
|
-
const points = [];
|
|
503
|
-
work.forEach((w, i) => {
|
|
504
|
-
// A media point the target model cannot embed is left out and named.
|
|
505
|
-
if (!vectors[i])
|
|
506
|
-
throw new RebuildError('classification_unresolved', 'unsupported-input-kind');
|
|
507
|
-
// A mirror write that landed after this page was read is newer — keep it.
|
|
508
|
-
if (window.touched.has(w.id))
|
|
509
|
-
return;
|
|
510
|
-
points.push({ id: w.id, vector: vectors[i], payload: searchablePayload(w.payload, mirror.marker) });
|
|
511
|
-
});
|
|
512
|
-
await deps.generations.upsert(shadow, points, { signal });
|
|
513
|
-
processed += points.length;
|
|
514
|
-
}
|
|
600
|
+
await flush();
|
|
515
601
|
// Retained payloads can overwrite newer mirror writes too; keep the window
|
|
516
602
|
// open through every page write and re-process raced IDs from the active store.
|
|
517
603
|
const touched = mirror.closeWindow(window);
|
|
@@ -519,7 +605,7 @@ async function runLoop(deps, mirror, embedder, signal) {
|
|
|
519
605
|
if (raced.length > 0)
|
|
520
606
|
mirror.restoreDirty(raced, []);
|
|
521
607
|
for (const filter of touched.deletes)
|
|
522
|
-
|
|
608
|
+
mirror.afterUncertainDelete(filter);
|
|
523
609
|
cursor = page.nextCursor;
|
|
524
610
|
await mutateLock(deps.appRoot, (current) => current?.rebuild
|
|
525
611
|
? { ...current, rebuild: { ...current.rebuild, cursor, processed, unreadable, updatedAt: now(deps) } }
|
|
@@ -594,19 +680,25 @@ async function runLoop(deps, mirror, embedder, signal) {
|
|
|
594
680
|
mirror.restoreDirty(dirty, []);
|
|
595
681
|
return false;
|
|
596
682
|
}
|
|
683
|
+
await mutateLock(deps.appRoot, current => current?.rebuild
|
|
684
|
+
? { ...current, rebuild: { ...current.rebuild, phase: 'ready', updatedAt: now(deps) } }
|
|
685
|
+
: null);
|
|
597
686
|
await swap(deps, shadow);
|
|
598
687
|
deps.mutations?.clear(changed);
|
|
599
688
|
return true;
|
|
600
689
|
};
|
|
601
|
-
const
|
|
690
|
+
const finishSwap = () => mirror.trySwap(() => deps.mutations ? deps.mutations.exclusive(finish) : finish());
|
|
691
|
+
const swapped = await deps.vector.exclusive(finishSwap);
|
|
602
692
|
if (swapped)
|
|
603
693
|
return;
|
|
604
694
|
}
|
|
605
695
|
await pause(deps, 'dirty', `${mirror.dirtyCount} point(s) still differ after ${MAX_DIRTY_ROUNDS} rounds`);
|
|
606
696
|
}
|
|
607
|
-
function searchablePayload(payload, marker) {
|
|
697
|
+
function searchablePayload(payload, marker, generation, input) {
|
|
608
698
|
const { recovery_only: ignored, ...rest } = payload;
|
|
609
|
-
return { ...rest, embed_model: marker
|
|
699
|
+
return { ...rest, embed_model: marker, embedding_status: 'current', embedding_generation: generation,
|
|
700
|
+
embedding_input_hash: computeContentHash(input.kind === 'text' ? input.text : mediaEmbeddingInput(payload)),
|
|
701
|
+
embedding_updated_at: new Date().toISOString(), };
|
|
610
702
|
}
|
|
611
703
|
async function carryEligibility(deps, shadow, id, payload) {
|
|
612
704
|
const classification = await deps.classify(payload);
|
|
@@ -634,8 +726,28 @@ async function embedAll(embedder, texts, signal) {
|
|
|
634
726
|
return out;
|
|
635
727
|
}
|
|
636
728
|
async function reprocess(deps, mirror, embedder, shadow, ids, deletes, signal) {
|
|
637
|
-
|
|
638
|
-
|
|
729
|
+
// The existing id-only shadow port has no filter projection. On uncertain
|
|
730
|
+
// deletion, reconcile bounded pages against the primary rather than replay
|
|
731
|
+
// a filter that could destroy a surviving or newly written point.
|
|
732
|
+
if (deletes.length) {
|
|
733
|
+
let cursor;
|
|
734
|
+
do {
|
|
735
|
+
signal.throwIfAborted();
|
|
736
|
+
const scanned = await deps.generations.scanIds(shadow, { limit: DIRTY_BATCH, ...(cursor ? { cursor } : {}) });
|
|
737
|
+
if (scanned.ids.length) {
|
|
738
|
+
const primary = await deps.vector.scroll({ filter: { mustMatchAny: [{ field: 'id', values: scanned.ids }] }, limit: scanned.ids.length });
|
|
739
|
+
const alive = new Map(primary.points.map(point => [point.id, point.payload]));
|
|
740
|
+
const copies = await deps.generations.payloads(shadow, scanned.ids);
|
|
741
|
+
const absent = scanned.ids.filter(id => !alive.has(id));
|
|
742
|
+
if (absent.length)
|
|
743
|
+
await deps.generations.deleteByFilter(shadow, { mustMatchAny: [{ field: 'id', values: absent }] });
|
|
744
|
+
const changed = primary.points.filter(point => !samePayload(copies.get(point.id) ?? {}, point.payload)).map(point => point.id);
|
|
745
|
+
if (changed.length)
|
|
746
|
+
await reprocess(deps, mirror, embedder, shadow, changed, [], signal);
|
|
747
|
+
}
|
|
748
|
+
cursor = scanned.nextCursor;
|
|
749
|
+
} while (cursor);
|
|
750
|
+
}
|
|
639
751
|
for (let i = 0; i < ids.length; i += DIRTY_BATCH) {
|
|
640
752
|
const batch = ids.slice(i, i + DIRTY_BATCH);
|
|
641
753
|
const page = await deps.vector.scroll({ filter: { mustMatchAny: [{ field: 'id', values: batch }] }, limit: batch.length });
|
|
@@ -644,24 +756,35 @@ async function reprocess(deps, mirror, embedder, shadow, ids, deletes, signal) {
|
|
|
644
756
|
if (gone.length > 0)
|
|
645
757
|
await deps.generations.deleteByFilter(shadow, { mustMatchAny: [{ field: 'id', values: gone }] });
|
|
646
758
|
const work = [];
|
|
759
|
+
let inputBytes = 0;
|
|
760
|
+
const flush = async () => {
|
|
761
|
+
if (!work.length)
|
|
762
|
+
return;
|
|
763
|
+
const vectors = await embedInputs(embedder, work.map(w => w.input), signal);
|
|
764
|
+
const points = work.map((w, j) => {
|
|
765
|
+
if (!vectors[j])
|
|
766
|
+
throw new RebuildError('classification_unresolved', 'unsupported-input-kind');
|
|
767
|
+
return { id: w.id, vector: vectors[j], payload: searchablePayload(w.payload, mirror.marker, mirror.generation, w.input) };
|
|
768
|
+
});
|
|
769
|
+
await deps.generations.upsert(shadow, points, { signal });
|
|
770
|
+
work.length = 0;
|
|
771
|
+
inputBytes = 0;
|
|
772
|
+
};
|
|
647
773
|
for (const [id, payload] of alive) {
|
|
648
774
|
if (!(await carryEligibility(deps, shadow, id, payload)))
|
|
649
775
|
continue;
|
|
650
776
|
const input = await inputFor(deps, payload);
|
|
651
777
|
if (input === undefined)
|
|
652
778
|
throw new RebuildError('classification_unresolved', 'source-input-unavailable');
|
|
779
|
+
const bytes = input.kind === 'text' ? Buffer.byteLength(input.text, 'utf8') : input.bytes.length;
|
|
780
|
+
if (work.length && inputBytes + bytes > VECTOR_WRITE_REQUEST_BYTES)
|
|
781
|
+
await flush();
|
|
653
782
|
work.push({ id, input, payload: payload });
|
|
783
|
+
inputBytes += bytes;
|
|
784
|
+
if (inputBytes >= VECTOR_WRITE_REQUEST_BYTES)
|
|
785
|
+
await flush();
|
|
654
786
|
}
|
|
655
|
-
|
|
656
|
-
continue;
|
|
657
|
-
const vectors = await embedInputs(embedder, work.map((w) => w.input), signal);
|
|
658
|
-
if (vectors.some(vector => !vector))
|
|
659
|
-
throw new RebuildError('classification_unresolved', 'unsupported-input-kind');
|
|
660
|
-
const points = work.flatMap((w, j) => (vectors[j]
|
|
661
|
-
? [{ id: w.id, vector: vectors[j], payload: searchablePayload(w.payload, mirror.marker) }]
|
|
662
|
-
: []));
|
|
663
|
-
if (points.length > 0)
|
|
664
|
-
await deps.generations.upsert(shadow, points, { signal });
|
|
787
|
+
await flush();
|
|
665
788
|
}
|
|
666
789
|
}
|
|
667
790
|
async function swap(deps, shadow) {
|
|
@@ -672,7 +795,8 @@ async function swap(deps, shadow) {
|
|
|
672
795
|
previous = lock.collection;
|
|
673
796
|
const { target, generation } = lock.rebuild;
|
|
674
797
|
return {
|
|
675
|
-
version:
|
|
798
|
+
version: 3,
|
|
799
|
+
mode: 'dense',
|
|
676
800
|
generation,
|
|
677
801
|
collection: shadow,
|
|
678
802
|
...target,
|
|
@@ -709,19 +833,32 @@ export async function cancelRebuild(deps) {
|
|
|
709
833
|
await loop.done;
|
|
710
834
|
}
|
|
711
835
|
const shadow = lock.rebuild.shadowCollection;
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
836
|
+
const remove = async () => {
|
|
837
|
+
const current = await getActiveLock(deps.appRoot);
|
|
838
|
+
if (current?.collection === shadow || current?.rebuild?.shadowCollection !== shadow) {
|
|
839
|
+
throw new RebuildError('no_rebuild', 'The recorded rebuild changed before cancellation');
|
|
840
|
+
}
|
|
841
|
+
let dropped;
|
|
842
|
+
try {
|
|
843
|
+
if (await deps.generations.exists(shadow)) {
|
|
844
|
+
await deps.generations.drop(shadow);
|
|
845
|
+
dropped = shadow;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
catch (error) {
|
|
849
|
+
await pause(deps, 'error', error instanceof Error ? error.message : String(error));
|
|
850
|
+
throw error;
|
|
851
|
+
}
|
|
852
|
+
await mutateLock(deps.appRoot, current => {
|
|
853
|
+
if (!current?.rebuild || current.rebuild.shadowCollection !== shadow)
|
|
854
|
+
return null;
|
|
855
|
+
const { rebuild: _dropped, ...rest } = current;
|
|
856
|
+
return rest;
|
|
857
|
+
});
|
|
858
|
+
deps.logger?.info('vector.rebuild.cancelled', { shadow });
|
|
859
|
+
return { cancelled: true, ...(dropped ? { dropped } : {}) };
|
|
860
|
+
};
|
|
861
|
+
return deps.vector.exclusive(remove);
|
|
725
862
|
}
|
|
726
863
|
/** Wait for a running loop (tests; a swap/cancel is observable through the lock). */
|
|
727
864
|
export async function awaitRebuildLoop() {
|
|
@@ -775,24 +912,26 @@ export async function describeEmbeddingState(deps, pointsTotal = null) {
|
|
|
775
912
|
const lock = await getActiveLock(deps.appRoot);
|
|
776
913
|
const resolved = deps.resolveTarget();
|
|
777
914
|
const target = 'error' in resolved ? null : generationFor(resolved.info, resolved.dimension);
|
|
778
|
-
const
|
|
779
|
-
const
|
|
915
|
+
const dense = lock?.mode === 'dense' ? lock : null;
|
|
916
|
+
const active = dense ? generationOf(dense) : null;
|
|
917
|
+
const marker = dense ? embedModelMarker(dense.modelId, dense.dimension, dense.embedRecipe) : null;
|
|
780
918
|
let points = null;
|
|
781
|
-
if (
|
|
919
|
+
if (dense && deps.vector.countByFilter) {
|
|
782
920
|
const total = pointsTotal ?? await deps.vector.countByFilter({});
|
|
783
921
|
const stampedActive = await deps.vector.countByFilter({ must: [{ field: 'embed_model', equals: marker }] });
|
|
784
922
|
points = { total, stampedActive, unstamped: Math.max(0, total - stampedActive) };
|
|
785
923
|
}
|
|
786
924
|
const targetInfo = 'error' in resolved ? null : resolved.info;
|
|
787
925
|
return {
|
|
788
|
-
active:
|
|
926
|
+
active: dense
|
|
789
927
|
? {
|
|
790
928
|
...active,
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
929
|
+
mode: 'dense',
|
|
930
|
+
generation: dense.generation,
|
|
931
|
+
collection: dense.collection,
|
|
932
|
+
createdAt: dense.createdAt,
|
|
933
|
+
...(dense.backfilledAt ? { backfilledAt: dense.backfilledAt } : {}),
|
|
934
|
+
resolvable: Boolean(resolveEmbeddingModel(dense.modelId)),
|
|
796
935
|
}
|
|
797
936
|
: null,
|
|
798
937
|
target: target
|
|
@@ -800,6 +939,7 @@ export async function describeEmbeddingState(deps, pointsTotal = null) {
|
|
|
800
939
|
: { error: resolved.error },
|
|
801
940
|
switchPending: Boolean(active && target && !sameVectorSpace(active, target)),
|
|
802
941
|
points,
|
|
942
|
+
serving: lock ? { mode: lock.mode, collection: lock.collection } : null,
|
|
803
943
|
rebuild: describeRebuild(lock?.rebuild),
|
|
804
944
|
lastEstimate: currentTargetEstimate(target?.modelId),
|
|
805
945
|
spend: getEmbeddingUsageMeter()?.snapshot() ?? null,
|
|
@@ -847,6 +987,8 @@ export async function primeEmbeddingState(opts) {
|
|
|
847
987
|
* through ITS project's connector (only bytes that still hash to the point).
|
|
848
988
|
*/
|
|
849
989
|
export function buildRebuildDeps(opts) {
|
|
990
|
+
if (!hasWriteBoundary(opts.vector))
|
|
991
|
+
throw new Error('FILESYSTEM_WRITE_BOUNDARY_REQUIRED');
|
|
850
992
|
return {
|
|
851
993
|
appRoot: opts.appRoot,
|
|
852
994
|
classify: indexEligibilityReader(opts.sourceConfigs),
|
|
@@ -916,7 +1058,7 @@ export async function bootEmbeddingGeneration(opts) {
|
|
|
916
1058
|
}
|
|
917
1059
|
await opts.vector.ensureAgentCollection('');
|
|
918
1060
|
const lock = await getActiveLock(opts.appRoot).catch(() => null);
|
|
919
|
-
if (lock && !resolveEmbeddingModel(lock.modelId)) {
|
|
1061
|
+
if (lock?.mode === 'dense' && !resolveEmbeddingModel(lock.modelId)) {
|
|
920
1062
|
opts.logger.warn('vector.active_model_unresolvable', {
|
|
921
1063
|
modelId: lock.modelId,
|
|
922
1064
|
impact: 'semantic search falls back to full-text; writes are stored unembedded until the model is defined again or the index is rebuilt',
|