@skillsmith/core 0.5.8 → 0.6.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/CHANGELOG.md +13 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/audit/exclusions.d.ts +67 -0
- package/dist/src/audit/exclusions.d.ts.map +1 -0
- package/dist/src/audit/exclusions.js +133 -0
- package/dist/src/audit/exclusions.js.map +1 -0
- package/dist/src/audit/exclusions.types.d.ts +45 -0
- package/dist/src/audit/exclusions.types.d.ts.map +1 -0
- package/dist/src/audit/exclusions.types.js +11 -0
- package/dist/src/audit/exclusions.types.js.map +1 -0
- package/dist/src/audit/index.d.ts +15 -0
- package/dist/src/audit/index.d.ts.map +1 -0
- package/dist/src/audit/index.js +14 -0
- package/dist/src/audit/index.js.map +1 -0
- package/dist/src/benchmarks/embeddingBenchmark.d.ts.map +1 -1
- package/dist/src/benchmarks/embeddingBenchmark.js +5 -2
- package/dist/src/benchmarks/embeddingBenchmark.js.map +1 -1
- package/dist/src/config/audit-mode.d.ts +71 -0
- package/dist/src/config/audit-mode.d.ts.map +1 -0
- package/dist/src/config/audit-mode.js +69 -0
- package/dist/src/config/audit-mode.js.map +1 -0
- package/dist/src/config/index.d.ts +13 -0
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/config/index.js +24 -0
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/db/migration-runner.d.ts +9 -2
- package/dist/src/db/migration-runner.d.ts.map +1 -1
- package/dist/src/db/migration-runner.js +30 -3
- package/dist/src/db/migration-runner.js.map +1 -1
- package/dist/src/db/migration.d.ts.map +1 -1
- package/dist/src/db/migration.js +9 -1
- package/dist/src/db/migration.js.map +1 -1
- package/dist/src/db/migrations/v16-skill-source.d.ts +41 -0
- package/dist/src/db/migrations/v16-skill-source.d.ts.map +1 -0
- package/dist/src/db/migrations/v16-skill-source.js +87 -0
- package/dist/src/db/migrations/v16-skill-source.js.map +1 -0
- package/dist/src/db/schema-sql.d.ts +1 -1
- package/dist/src/db/schema-sql.d.ts.map +1 -1
- package/dist/src/db/schema-sql.js +2 -1
- package/dist/src/db/schema-sql.js.map +1 -1
- package/dist/src/db/schema.d.ts +9 -3
- package/dist/src/db/schema.d.ts.map +1 -1
- package/dist/src/db/schema.js +12 -2
- package/dist/src/db/schema.js.map +1 -1
- package/dist/src/embeddings/embedding-utils.d.ts +23 -0
- package/dist/src/embeddings/embedding-utils.d.ts.map +1 -1
- package/dist/src/embeddings/embedding-utils.js +39 -0
- package/dist/src/embeddings/embedding-utils.js.map +1 -1
- package/dist/src/embeddings/hnsw-search.d.ts +133 -0
- package/dist/src/embeddings/hnsw-search.d.ts.map +1 -0
- package/dist/src/embeddings/hnsw-search.js +387 -0
- package/dist/src/embeddings/hnsw-search.js.map +1 -0
- package/dist/src/embeddings/hnsw-store.d.ts +9 -3
- package/dist/src/embeddings/hnsw-store.d.ts.map +1 -1
- package/dist/src/embeddings/hnsw-store.js +17 -7
- package/dist/src/embeddings/hnsw-store.js.map +1 -1
- package/dist/src/embeddings/hnsw-store.types.d.ts +17 -4
- package/dist/src/embeddings/hnsw-store.types.d.ts.map +1 -1
- package/dist/src/embeddings/hnsw-store.types.js.map +1 -1
- package/dist/src/embeddings/index.d.ts +50 -4
- package/dist/src/embeddings/index.d.ts.map +1 -1
- package/dist/src/embeddings/index.js +166 -24
- package/dist/src/embeddings/index.js.map +1 -1
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/install/fan-out.d.ts +81 -0
- package/dist/src/install/fan-out.d.ts.map +1 -0
- package/dist/src/install/fan-out.js +236 -0
- package/dist/src/install/fan-out.js.map +1 -0
- package/dist/src/install/index.d.ts +16 -0
- package/dist/src/install/index.d.ts.map +1 -0
- package/dist/src/install/index.js +14 -0
- package/dist/src/install/index.js.map +1 -0
- package/dist/src/install/paths.d.ts +16 -0
- package/dist/src/install/paths.d.ts.map +1 -0
- package/dist/src/install/paths.js +56 -0
- package/dist/src/install/paths.js.map +1 -0
- package/dist/src/repositories/AdvisoryRepository.test.js +2 -2
- package/dist/src/repositories/AdvisoryRepository.test.js.map +1 -1
- package/dist/src/repositories/SkillRepository.d.ts.map +1 -1
- package/dist/src/repositories/SkillRepository.js +12 -4
- package/dist/src/repositories/SkillRepository.js.map +1 -1
- package/dist/src/search/hybrid.js +2 -2
- package/dist/src/search/hybrid.js.map +1 -1
- package/dist/src/security/pathValidation.d.ts +6 -1
- package/dist/src/security/pathValidation.d.ts.map +1 -1
- package/dist/src/security/pathValidation.js +6 -1
- package/dist/src/security/pathValidation.js.map +1 -1
- package/dist/src/services/skill-installation.service.d.ts.map +1 -1
- package/dist/src/services/skill-installation.service.js +4 -2
- package/dist/src/services/skill-installation.service.js.map +1 -1
- package/dist/src/skills/index-local.d.ts +107 -0
- package/dist/src/skills/index-local.d.ts.map +1 -0
- package/dist/src/skills/index-local.js +208 -0
- package/dist/src/skills/index-local.js.map +1 -0
- package/dist/src/sync/SyncEngine.d.ts.map +1 -1
- package/dist/src/sync/SyncEngine.js +20 -3
- package/dist/src/sync/SyncEngine.js.map +1 -1
- package/dist/src/types/skill.d.ts +19 -4
- package/dist/src/types/skill.d.ts.map +1 -1
- package/dist/src/types/skill.js.map +1 -1
- package/dist/tests/EmbeddingService.test.js +1 -1
- package/dist/tests/EmbeddingService.test.js.map +1 -1
- package/dist/tests/SkillVersionRepository.test.js +2 -2
- package/dist/tests/SkillVersionRepository.test.js.map +1 -1
- package/dist/tests/db/migration.test.js +13 -5
- package/dist/tests/db/migration.test.js.map +1 -1
- package/dist/tests/embeddings/hnsw-bench-gate.test.d.ts +15 -0
- package/dist/tests/embeddings/hnsw-bench-gate.test.d.ts.map +1 -0
- package/dist/tests/embeddings/hnsw-bench-gate.test.js +117 -0
- package/dist/tests/embeddings/hnsw-bench-gate.test.js.map +1 -0
- package/dist/tests/embeddings/hnsw-integration.test.d.ts +14 -0
- package/dist/tests/embeddings/hnsw-integration.test.d.ts.map +1 -0
- package/dist/tests/embeddings/hnsw-integration.test.js +160 -0
- package/dist/tests/embeddings/hnsw-integration.test.js.map +1 -0
- package/dist/tests/embeddings/hnsw-vs-brute-force.bench.d.ts +15 -0
- package/dist/tests/embeddings/hnsw-vs-brute-force.bench.d.ts.map +1 -0
- package/dist/tests/embeddings/hnsw-vs-brute-force.bench.js +64 -0
- package/dist/tests/embeddings/hnsw-vs-brute-force.bench.js.map +1 -0
- package/dist/tests/embeddings/seed-bench.d.ts +15 -0
- package/dist/tests/embeddings/seed-bench.d.ts.map +1 -0
- package/dist/tests/embeddings/seed-bench.js +61 -0
- package/dist/tests/embeddings/seed-bench.js.map +1 -0
- package/dist/tests/helpers/database.d.ts +11 -2
- package/dist/tests/helpers/database.d.ts.map +1 -1
- package/dist/tests/helpers/database.js +23 -7
- package/dist/tests/helpers/database.js.map +1 -1
- package/dist/tests/install/fan-out.test.d.ts +2 -0
- package/dist/tests/install/fan-out.test.d.ts.map +1 -0
- package/dist/tests/install/fan-out.test.js +238 -0
- package/dist/tests/install/fan-out.test.js.map +1 -0
- package/dist/tests/install/paths.test.d.ts +2 -0
- package/dist/tests/install/paths.test.d.ts.map +1 -0
- package/dist/tests/install/paths.test.js +79 -0
- package/dist/tests/install/paths.test.js.map +1 -0
- package/dist/tests/repositories/CoInstallRepository.test.js +2 -2
- package/dist/tests/repositories/CoInstallRepository.test.js.map +1 -1
- package/dist/tests/repositories/SkillDependencyRepository.test.js +2 -2
- package/dist/tests/repositories/SkillDependencyRepository.test.js.map +1 -1
- package/dist/tests/schema.test.js +12 -0
- package/dist/tests/schema.test.js.map +1 -1
- package/dist/tests/skill-scanner/allowlist.test.js +27 -4
- package/dist/tests/skill-scanner/allowlist.test.js.map +1 -1
- package/dist/tests/unit/audit/exclusions.test.d.ts +7 -0
- package/dist/tests/unit/audit/exclusions.test.d.ts.map +1 -0
- package/dist/tests/unit/audit/exclusions.test.js +157 -0
- package/dist/tests/unit/audit/exclusions.test.js.map +1 -0
- package/dist/tests/unit/config/audit-mode.test.d.ts +11 -0
- package/dist/tests/unit/config/audit-mode.test.d.ts.map +1 -0
- package/dist/tests/unit/config/audit-mode.test.js +86 -0
- package/dist/tests/unit/config/audit-mode.test.js.map +1 -0
- package/dist/tests/unit/migrations/migration-v16.test.d.ts +11 -0
- package/dist/tests/unit/migrations/migration-v16.test.d.ts.map +1 -0
- package/dist/tests/unit/migrations/migration-v16.test.js +134 -0
- package/dist/tests/unit/migrations/migration-v16.test.js.map +1 -0
- package/dist/tests/unit/migrations/v10-dependencies.test.js +2 -2
- package/dist/tests/unit/migrations/v10-dependencies.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation-extended.test.js +1 -1
- package/dist/tests/unit/services/skill-installation-extended.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.service.test.js +1 -1
- package/dist/tests/unit/services/skill-installation.service.test.js.map +1 -1
- package/dist/tests/unit/skills/index-local.test.d.ts +11 -0
- package/dist/tests/unit/skills/index-local.test.d.ts.map +1 -0
- package/dist/tests/unit/skills/index-local.test.js +88 -0
- package/dist/tests/unit/skills/index-local.test.js.map +1 -0
- package/dist/tests/unit/sync-engine.source-aware.test.d.ts +11 -0
- package/dist/tests/unit/sync-engine.source-aware.test.d.ts.map +1 -0
- package/dist/tests/unit/sync-engine.source-aware.test.js +147 -0
- package/dist/tests/unit/sync-engine.source-aware.test.js.map +1 -0
- package/package.json +26 -3
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-4577: HNSW search backend for `EmbeddingService.findSimilar`.
|
|
3
|
+
*
|
|
4
|
+
* Lazily loads `hnswlib-node` (declared as `optionalDependencies` on
|
|
5
|
+
* @skillsmith/core), builds or loads an on-disk index at
|
|
6
|
+
* `~/.skillsmith/cache/hnsw-{modelName}.{bin,meta.json,labels.json}`,
|
|
7
|
+
* and exposes incremental `addPoint`/`markDelete` semantics with a debounced
|
|
8
|
+
* atomic-rename persist.
|
|
9
|
+
*
|
|
10
|
+
* Failure modes:
|
|
11
|
+
* - `MODULE_NOT_FOUND` on import → permanently disable; brute-force fallback
|
|
12
|
+
* in `EmbeddingService.findSimilar` covers the case.
|
|
13
|
+
* - `readIndex` failure on a corrupt cache → delete + rebuild on next call
|
|
14
|
+
* (treat as transient).
|
|
15
|
+
* - Concurrent writers → atomic-rename (`writeIndex` to `.tmp`,
|
|
16
|
+
* `fs.renameSync` to final). Loser-of-race acceptable; readers re-read
|
|
17
|
+
* via the atomic pointer.
|
|
18
|
+
*
|
|
19
|
+
* @see ADR-009 (2026-05 amendment): brute-force fallback retained for
|
|
20
|
+
* environments where the optional dep failed to install.
|
|
21
|
+
*/
|
|
22
|
+
import type { HierarchicalNSW } from './hnsw-store.types.js';
|
|
23
|
+
/**
|
|
24
|
+
* Persisted metadata describing the on-disk HNSW index. Used to invalidate the
|
|
25
|
+
* cached graph when the embedding count, model, or vector dimension drift.
|
|
26
|
+
*/
|
|
27
|
+
export interface HnswMeta {
|
|
28
|
+
/** Schema version. Bump on incompatible meta changes. */
|
|
29
|
+
version: 1;
|
|
30
|
+
/** Model identifier (e.g. `Xenova/all-MiniLM-L6-v2`). */
|
|
31
|
+
modelName: string;
|
|
32
|
+
/** Vector dimensionality. */
|
|
33
|
+
dim: number;
|
|
34
|
+
/** Number of points the cache was built from. */
|
|
35
|
+
count: number;
|
|
36
|
+
/** ISO timestamp the cache was last persisted. */
|
|
37
|
+
builtAt: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Wrapper exposing the live HNSW index plus the bookkeeping needed by
|
|
41
|
+
* `EmbeddingService` to rewire incremental upserts/removes.
|
|
42
|
+
*/
|
|
43
|
+
export interface HnswHandle {
|
|
44
|
+
/** The live HNSW index. */
|
|
45
|
+
index: HierarchicalNSW;
|
|
46
|
+
/** label → skillId mapping (HNSW returns numeric labels). */
|
|
47
|
+
labelToId: Map<number, string>;
|
|
48
|
+
/** skillId → label mapping (for incremental updates / deletes). */
|
|
49
|
+
idToLabel: Map<string, number>;
|
|
50
|
+
/** Next label to assign for new points. */
|
|
51
|
+
nextLabel: number;
|
|
52
|
+
/** Filesystem paths the index will read/write. Exposed for diagnostics/tests. */
|
|
53
|
+
paths: HnswCachePaths;
|
|
54
|
+
/** Schedule a debounced persist (5s). Safe to call repeatedly. */
|
|
55
|
+
schedulePersist: () => void;
|
|
56
|
+
/** Persist immediately (used at shutdown / for tests). */
|
|
57
|
+
persistNow: () => void;
|
|
58
|
+
}
|
|
59
|
+
export interface HnswCachePaths {
|
|
60
|
+
bin: string;
|
|
61
|
+
meta: string;
|
|
62
|
+
labels: string;
|
|
63
|
+
binTmp: string;
|
|
64
|
+
metaTmp: string;
|
|
65
|
+
labelsTmp: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Status reported back to `EmbeddingService` so it can distinguish
|
|
69
|
+
* "the optional dep is missing" (permanent) from "we hit a transient
|
|
70
|
+
* write error" (try again next time).
|
|
71
|
+
*/
|
|
72
|
+
export type HnswStatus = {
|
|
73
|
+
kind: 'ok';
|
|
74
|
+
handle: HnswHandle;
|
|
75
|
+
} | {
|
|
76
|
+
kind: 'permanently-unavailable';
|
|
77
|
+
reason: string;
|
|
78
|
+
} | {
|
|
79
|
+
kind: 'temporarily-unavailable';
|
|
80
|
+
reason: string;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Build (or load from cache) an HNSW index for the supplied embedding map.
|
|
84
|
+
*
|
|
85
|
+
* `embeddings` is the canonical source of truth (from `EmbeddingService`'s
|
|
86
|
+
* SQLite cache). On a cold start with a populated cache file matching
|
|
87
|
+
* meta.json, we `readIndex` and skip the rebuild. Otherwise we initialise
|
|
88
|
+
* a fresh index and add every point — same I/O cost as a brute-force seed
|
|
89
|
+
* but the resulting graph survives subsequent `findSimilar` calls.
|
|
90
|
+
*/
|
|
91
|
+
export declare function loadOrBuildHnsw(args: {
|
|
92
|
+
embeddings: Map<string, Float32Array>;
|
|
93
|
+
modelName: string;
|
|
94
|
+
dim: number;
|
|
95
|
+
/** Capacity hint. Defaults to ~2x current size, clamped to 1024 minimum. */
|
|
96
|
+
maxElements?: number;
|
|
97
|
+
/** Override hyperparams. Defaults match `DEFAULT_HNSW_CONFIG` in hnsw-store.types.ts. */
|
|
98
|
+
m?: number;
|
|
99
|
+
efConstruction?: number;
|
|
100
|
+
efSearch?: number;
|
|
101
|
+
}): Promise<HnswStatus>;
|
|
102
|
+
/**
|
|
103
|
+
* Top-K nearest-neighbour search via the supplied handle.
|
|
104
|
+
*
|
|
105
|
+
* @param handle - HNSW handle returned by `loadOrBuildHnsw`
|
|
106
|
+
* @param query - Query vector (must match `handle.dim`)
|
|
107
|
+
* @param topK - Maximum neighbours to return
|
|
108
|
+
* @returns Result rows in HNSW score order; `score` is `1 - cosineDistance`.
|
|
109
|
+
*/
|
|
110
|
+
export declare function findSimilarHnsw(handle: HnswHandle, query: Float32Array, topK: number): Array<{
|
|
111
|
+
skillId: string;
|
|
112
|
+
score: number;
|
|
113
|
+
}>;
|
|
114
|
+
/**
|
|
115
|
+
* Add or replace a point. Used by `EmbeddingService.storeEmbedding` to keep
|
|
116
|
+
* the in-memory graph aligned with the SQLite cache. Marks the handle dirty;
|
|
117
|
+
* persist happens via the debounced 5s timer (or `persistNow`).
|
|
118
|
+
*/
|
|
119
|
+
export declare function upsertPoint(handle: HnswHandle, skillId: string, vector: Float32Array): void;
|
|
120
|
+
/**
|
|
121
|
+
* Mark a point deleted. The point stays in the graph for traversal correctness
|
|
122
|
+
* but `findSimilarHnsw` filters it out via the labelToId lookup.
|
|
123
|
+
*/
|
|
124
|
+
export declare function removePoint(handle: HnswHandle, skillId: string): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Test-only helper — clears the cached `hnswlib-node` constructor reference so
|
|
127
|
+
* tests can simulate "module reinstalled" scenarios. Not part of the public
|
|
128
|
+
* API; do not use in production code.
|
|
129
|
+
*
|
|
130
|
+
* @internal
|
|
131
|
+
*/
|
|
132
|
+
export declare function __resetCachedHnswCtorForTests(): void;
|
|
133
|
+
//# sourceMappingURL=hnsw-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hnsw-search.d.ts","sourceRoot":"","sources":["../../../src/embeddings/hnsw-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH,OAAO,KAAK,EAAE,eAAe,EAA8B,MAAM,uBAAuB,CAAA;AAExF;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,yDAAyD;IACzD,OAAO,EAAE,CAAC,CAAA;IACV,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAA;IACjB,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,2BAA2B;IAC3B,KAAK,EAAE,eAAe,CAAA;IACtB,6DAA6D;IAC7D,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,mEAAmE;IACnE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB,iFAAiF;IACjF,KAAK,EAAE,cAAc,CAAA;IACrB,kEAAkE;IAClE,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,0DAA0D;IAC1D,UAAU,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,yBAAyB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,yBAAyB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AA8FvD;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;IACX,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,yFAAyF;IACzF,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,GAAG,OAAO,CAAC,UAAU,CAAC,CA6GtB;AAgGD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,GACX,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAc3C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAgB3F;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQxE;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD"}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-4577: HNSW search backend for `EmbeddingService.findSimilar`.
|
|
3
|
+
*
|
|
4
|
+
* Lazily loads `hnswlib-node` (declared as `optionalDependencies` on
|
|
5
|
+
* @skillsmith/core), builds or loads an on-disk index at
|
|
6
|
+
* `~/.skillsmith/cache/hnsw-{modelName}.{bin,meta.json,labels.json}`,
|
|
7
|
+
* and exposes incremental `addPoint`/`markDelete` semantics with a debounced
|
|
8
|
+
* atomic-rename persist.
|
|
9
|
+
*
|
|
10
|
+
* Failure modes:
|
|
11
|
+
* - `MODULE_NOT_FOUND` on import → permanently disable; brute-force fallback
|
|
12
|
+
* in `EmbeddingService.findSimilar` covers the case.
|
|
13
|
+
* - `readIndex` failure on a corrupt cache → delete + rebuild on next call
|
|
14
|
+
* (treat as transient).
|
|
15
|
+
* - Concurrent writers → atomic-rename (`writeIndex` to `.tmp`,
|
|
16
|
+
* `fs.renameSync` to final). Loser-of-race acceptable; readers re-read
|
|
17
|
+
* via the atomic pointer.
|
|
18
|
+
*
|
|
19
|
+
* @see ADR-009 (2026-05 amendment): brute-force fallback retained for
|
|
20
|
+
* environments where the optional dep failed to install.
|
|
21
|
+
*/
|
|
22
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'fs';
|
|
23
|
+
import { dirname, join } from 'path';
|
|
24
|
+
import { getCacheDir } from '../config/index.js';
|
|
25
|
+
/**
|
|
26
|
+
* One-shot module loader. Cached across calls; on `MODULE_NOT_FOUND` the
|
|
27
|
+
* caller receives a sentinel and is expected to permanently disable HNSW.
|
|
28
|
+
*/
|
|
29
|
+
let cachedCtor = null;
|
|
30
|
+
/**
|
|
31
|
+
* Dynamically load `hnswlib-node`. Returns the constructor or `null` when the
|
|
32
|
+
* optional dependency is not installed (Vercel build, restricted hosts).
|
|
33
|
+
*
|
|
34
|
+
* Uses a literal dynamic `import()` (not the `Function('return import(...)')()`
|
|
35
|
+
* pattern that lives in `hnsw-store.helpers.ts`); vitest's vm-mode ESM rejects
|
|
36
|
+
* the latter with "A dynamic import callback was not specified." A native
|
|
37
|
+
* dynamic import is safe here because `hnswlib-node` is a CJS module that's
|
|
38
|
+
* not type-imported anywhere — only TS will error on resolution failure, but
|
|
39
|
+
* we catch that in the `catch` block below.
|
|
40
|
+
*/
|
|
41
|
+
async function loadHnswCtor() {
|
|
42
|
+
if (cachedCtor === 'unavailable')
|
|
43
|
+
return null;
|
|
44
|
+
if (cachedCtor !== null)
|
|
45
|
+
return cachedCtor;
|
|
46
|
+
try {
|
|
47
|
+
// hnswlib-node is CJS; ESM dynamic import lifts its `module.exports` onto
|
|
48
|
+
// `.default`. Some bundlers / older Node versions also surface named
|
|
49
|
+
// exports at the top level — check both shapes so we work in every
|
|
50
|
+
// environment.
|
|
51
|
+
//
|
|
52
|
+
// Note: a previous codebase pattern used `Function('return import(...)')()`
|
|
53
|
+
// here. Vitest's vm-mode ESM rejects that with "A dynamic import callback
|
|
54
|
+
// was not specified" so we use a literal dynamic `import()`. The string
|
|
55
|
+
// literal is intentional to keep TypeScript from re-routing the specifier
|
|
56
|
+
// (`hnswlib-node` is in `optionalDependencies`, not a hard dep).
|
|
57
|
+
const mod = (await import('hnswlib-node'));
|
|
58
|
+
const ctor = mod.HierarchicalNSW ?? mod.default?.HierarchicalNSW;
|
|
59
|
+
if (!ctor) {
|
|
60
|
+
cachedCtor = 'unavailable';
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
cachedCtor = ctor;
|
|
64
|
+
return cachedCtor;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
cachedCtor = 'unavailable';
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function cachePaths(modelName) {
|
|
72
|
+
// Sanitize model name (slashes become double-underscore so we don't
|
|
73
|
+
// accidentally create nested directories under cache/).
|
|
74
|
+
const safeName = modelName.replace(/[/\\]/g, '__');
|
|
75
|
+
const dir = getCacheDir();
|
|
76
|
+
const base = join(dir, `hnsw-${safeName}`);
|
|
77
|
+
return {
|
|
78
|
+
bin: `${base}.bin`,
|
|
79
|
+
meta: `${base}.meta.json`,
|
|
80
|
+
labels: `${base}.labels.json`,
|
|
81
|
+
binTmp: `${base}.bin.tmp`,
|
|
82
|
+
metaTmp: `${base}.meta.json.tmp`,
|
|
83
|
+
labelsTmp: `${base}.labels.json.tmp`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function readMeta(metaPath) {
|
|
87
|
+
if (!existsSync(metaPath))
|
|
88
|
+
return null;
|
|
89
|
+
try {
|
|
90
|
+
const parsed = JSON.parse(readFileSync(metaPath, 'utf-8'));
|
|
91
|
+
if (parsed.version !== 1)
|
|
92
|
+
return null;
|
|
93
|
+
return parsed;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function readLabels(labelsPath) {
|
|
100
|
+
if (!existsSync(labelsPath))
|
|
101
|
+
return null;
|
|
102
|
+
try {
|
|
103
|
+
const parsed = JSON.parse(readFileSync(labelsPath, 'utf-8'));
|
|
104
|
+
if (!Array.isArray(parsed))
|
|
105
|
+
return null;
|
|
106
|
+
return parsed;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function writeAtomic(tmp, final, contents) {
|
|
113
|
+
mkdirSync(dirname(tmp), { recursive: true });
|
|
114
|
+
writeFileSync(tmp, contents, typeof contents === 'string' ? { encoding: 'utf-8' } : undefined);
|
|
115
|
+
renameSync(tmp, final);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Build (or load from cache) an HNSW index for the supplied embedding map.
|
|
119
|
+
*
|
|
120
|
+
* `embeddings` is the canonical source of truth (from `EmbeddingService`'s
|
|
121
|
+
* SQLite cache). On a cold start with a populated cache file matching
|
|
122
|
+
* meta.json, we `readIndex` and skip the rebuild. Otherwise we initialise
|
|
123
|
+
* a fresh index and add every point — same I/O cost as a brute-force seed
|
|
124
|
+
* but the resulting graph survives subsequent `findSimilar` calls.
|
|
125
|
+
*/
|
|
126
|
+
export async function loadOrBuildHnsw(args) {
|
|
127
|
+
const Ctor = await loadHnswCtor();
|
|
128
|
+
if (!Ctor) {
|
|
129
|
+
return {
|
|
130
|
+
kind: 'permanently-unavailable',
|
|
131
|
+
reason: 'hnswlib-node not installed (optionalDependencies)',
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const paths = cachePaths(args.modelName);
|
|
135
|
+
const meta = readMeta(paths.meta);
|
|
136
|
+
const labels = readLabels(paths.labels);
|
|
137
|
+
const count = args.embeddings.size;
|
|
138
|
+
// R2 mitigation: defaults tuned to clear the recall@10 ≥ 0.95 gate.
|
|
139
|
+
// m=32/efConstruction=400/efSearch=200 is the `large` preset from
|
|
140
|
+
// hnsw-store.types.ts and matches what the original SMI-1519 design
|
|
141
|
+
// documented for 100k-scale skill registries. At 14k synthetic vectors
|
|
142
|
+
// we still see >100x speedup vs brute-force p99.
|
|
143
|
+
const m = args.m ?? 32;
|
|
144
|
+
const efConstruction = args.efConstruction ?? 400;
|
|
145
|
+
const efSearch = args.efSearch ?? 200;
|
|
146
|
+
const capacity = Math.max(args.maxElements ?? Math.max(count * 2, 1024), 1024);
|
|
147
|
+
const reusable = meta !== null &&
|
|
148
|
+
labels !== null &&
|
|
149
|
+
meta.modelName === args.modelName &&
|
|
150
|
+
meta.dim === args.dim &&
|
|
151
|
+
meta.count === count &&
|
|
152
|
+
existsSync(paths.bin);
|
|
153
|
+
let index;
|
|
154
|
+
let labelToId;
|
|
155
|
+
let idToLabel;
|
|
156
|
+
let nextLabel;
|
|
157
|
+
if (reusable) {
|
|
158
|
+
try {
|
|
159
|
+
index = new Ctor('cosine', args.dim);
|
|
160
|
+
// Use sync read; the async `readIndex` returns a Promise we'd have to
|
|
161
|
+
// await, defeating the synchronous boot path. Sync is fine here — the
|
|
162
|
+
// file is < a few MB at expected scale.
|
|
163
|
+
index.readIndexSync(paths.bin, true);
|
|
164
|
+
index.setEf(efSearch);
|
|
165
|
+
labelToId = new Map(labels);
|
|
166
|
+
idToLabel = new Map(labels.map(([label, id]) => [id, label]));
|
|
167
|
+
nextLabel = labels.reduce((max, [label]) => Math.max(max, label), -1) + 1;
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
// Corrupt cache — wipe and recurse for a fresh build. The retry will
|
|
171
|
+
// hit `reusable === false` because we just removed the artefacts.
|
|
172
|
+
try {
|
|
173
|
+
if (existsSync(paths.bin))
|
|
174
|
+
unlinkSync(paths.bin);
|
|
175
|
+
if (existsSync(paths.meta))
|
|
176
|
+
unlinkSync(paths.meta);
|
|
177
|
+
if (existsSync(paths.labels))
|
|
178
|
+
unlinkSync(paths.labels);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
/* best-effort cleanup */
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
return await loadOrBuildHnsw(args);
|
|
185
|
+
}
|
|
186
|
+
catch (retryErr) {
|
|
187
|
+
return {
|
|
188
|
+
kind: 'temporarily-unavailable',
|
|
189
|
+
reason: `cache rebuild failed after corrupt-load: ${String(retryErr)} (initial: ${String(err)})`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
index = new Ctor('cosine', args.dim);
|
|
196
|
+
index.initIndex(capacity, m, efConstruction);
|
|
197
|
+
index.setEf(efSearch);
|
|
198
|
+
labelToId = new Map();
|
|
199
|
+
idToLabel = new Map();
|
|
200
|
+
let nextLabelLocal = 0;
|
|
201
|
+
for (const [skillId, vec] of args.embeddings) {
|
|
202
|
+
// hnswlib-node@3 expects a plain Array<number> for addPoint, not a
|
|
203
|
+
// typed array — passing Float32Array surfaces as
|
|
204
|
+
// "Invalid the first argument type, must be an Array."
|
|
205
|
+
index.addPoint(Array.from(vec), nextLabelLocal);
|
|
206
|
+
labelToId.set(nextLabelLocal, skillId);
|
|
207
|
+
idToLabel.set(skillId, nextLabelLocal);
|
|
208
|
+
nextLabelLocal++;
|
|
209
|
+
}
|
|
210
|
+
nextLabel = nextLabelLocal;
|
|
211
|
+
}
|
|
212
|
+
const handle = createHandle({
|
|
213
|
+
index,
|
|
214
|
+
labelToId,
|
|
215
|
+
idToLabel,
|
|
216
|
+
nextLabel,
|
|
217
|
+
dim: args.dim,
|
|
218
|
+
modelName: args.modelName,
|
|
219
|
+
paths,
|
|
220
|
+
});
|
|
221
|
+
// Immediate persist after a fresh build. Reusable path skips this — the
|
|
222
|
+
// on-disk artefacts already match.
|
|
223
|
+
if (!reusable) {
|
|
224
|
+
try {
|
|
225
|
+
handle.persistNow();
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
return {
|
|
229
|
+
kind: 'temporarily-unavailable',
|
|
230
|
+
reason: `persist after build failed: ${String(err)}`,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return { kind: 'ok', handle };
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Wrap the raw HNSW index with the bookkeeping needed for incremental upserts
|
|
238
|
+
* + debounced persist. Caller owns the handle's lifecycle (no auto-cleanup).
|
|
239
|
+
*/
|
|
240
|
+
function createHandle(args) {
|
|
241
|
+
let dirty = false;
|
|
242
|
+
let timer = null;
|
|
243
|
+
// Mutable closure state — we update via the handle methods below, but the
|
|
244
|
+
// returned object exposes the current values via getters.
|
|
245
|
+
const state = {
|
|
246
|
+
nextLabel: args.nextLabel,
|
|
247
|
+
};
|
|
248
|
+
const persistNow = () => {
|
|
249
|
+
if (timer) {
|
|
250
|
+
clearTimeout(timer);
|
|
251
|
+
timer = null;
|
|
252
|
+
}
|
|
253
|
+
if (!dirty && existsSync(args.paths.bin) && existsSync(args.paths.meta)) {
|
|
254
|
+
// Nothing to write and the cache is already consistent on disk.
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
// `writeIndex` is async (returns a Promise) — we want sync semantics so
|
|
258
|
+
// the persist runs to completion inside the debounce callback / on close.
|
|
259
|
+
args.index.writeIndexSync(args.paths.binTmp);
|
|
260
|
+
renameSync(args.paths.binTmp, args.paths.bin);
|
|
261
|
+
const labelsArr = Array.from(args.labelToId.entries());
|
|
262
|
+
writeAtomic(args.paths.labelsTmp, args.paths.labels, JSON.stringify(labelsArr));
|
|
263
|
+
const meta = {
|
|
264
|
+
version: 1,
|
|
265
|
+
modelName: args.modelName,
|
|
266
|
+
dim: args.dim,
|
|
267
|
+
count: args.idToLabel.size,
|
|
268
|
+
builtAt: new Date().toISOString(),
|
|
269
|
+
};
|
|
270
|
+
writeAtomic(args.paths.metaTmp, args.paths.meta, JSON.stringify(meta, null, 2));
|
|
271
|
+
dirty = false;
|
|
272
|
+
};
|
|
273
|
+
const schedulePersist = () => {
|
|
274
|
+
dirty = true;
|
|
275
|
+
if (timer)
|
|
276
|
+
clearTimeout(timer);
|
|
277
|
+
timer = setTimeout(() => {
|
|
278
|
+
timer = null;
|
|
279
|
+
try {
|
|
280
|
+
persistNow();
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
// Persist failures are non-fatal — the in-memory index stays valid;
|
|
284
|
+
// a future rebuild will recover from the cached embeddings map.
|
|
285
|
+
// Log the cache path so a user can spot a read-only cache dir.
|
|
286
|
+
console.warn(`[hnsw-search] debounced persist failed (path=${args.paths.bin}):`, err instanceof Error ? err.message : err);
|
|
287
|
+
}
|
|
288
|
+
}, 5000);
|
|
289
|
+
// Don't keep the event loop alive just for this timer.
|
|
290
|
+
if (timer && typeof timer.unref === 'function')
|
|
291
|
+
timer.unref();
|
|
292
|
+
};
|
|
293
|
+
return {
|
|
294
|
+
get index() {
|
|
295
|
+
return args.index;
|
|
296
|
+
},
|
|
297
|
+
get labelToId() {
|
|
298
|
+
return args.labelToId;
|
|
299
|
+
},
|
|
300
|
+
get idToLabel() {
|
|
301
|
+
return args.idToLabel;
|
|
302
|
+
},
|
|
303
|
+
get nextLabel() {
|
|
304
|
+
return state.nextLabel;
|
|
305
|
+
},
|
|
306
|
+
set nextLabel(v) {
|
|
307
|
+
state.nextLabel = v;
|
|
308
|
+
},
|
|
309
|
+
get paths() {
|
|
310
|
+
return args.paths;
|
|
311
|
+
},
|
|
312
|
+
schedulePersist,
|
|
313
|
+
persistNow,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Top-K nearest-neighbour search via the supplied handle.
|
|
318
|
+
*
|
|
319
|
+
* @param handle - HNSW handle returned by `loadOrBuildHnsw`
|
|
320
|
+
* @param query - Query vector (must match `handle.dim`)
|
|
321
|
+
* @param topK - Maximum neighbours to return
|
|
322
|
+
* @returns Result rows in HNSW score order; `score` is `1 - cosineDistance`.
|
|
323
|
+
*/
|
|
324
|
+
export function findSimilarHnsw(handle, query, topK) {
|
|
325
|
+
const liveCount = handle.idToLabel.size;
|
|
326
|
+
if (liveCount === 0)
|
|
327
|
+
return [];
|
|
328
|
+
const k = Math.min(topK, liveCount);
|
|
329
|
+
// searchKnn also requires plain Array — Float32Array triggers
|
|
330
|
+
// "Invalid the first argument type, must be an Array."
|
|
331
|
+
const result = handle.index.searchKnn(Array.from(query), k);
|
|
332
|
+
const out = [];
|
|
333
|
+
for (let i = 0; i < result.neighbors.length; i++) {
|
|
334
|
+
const skillId = handle.labelToId.get(result.neighbors[i]);
|
|
335
|
+
if (!skillId)
|
|
336
|
+
continue; // marked-deleted points may still surface; skip
|
|
337
|
+
out.push({ skillId, score: 1 - result.distances[i] });
|
|
338
|
+
}
|
|
339
|
+
return out;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Add or replace a point. Used by `EmbeddingService.storeEmbedding` to keep
|
|
343
|
+
* the in-memory graph aligned with the SQLite cache. Marks the handle dirty;
|
|
344
|
+
* persist happens via the debounced 5s timer (or `persistNow`).
|
|
345
|
+
*/
|
|
346
|
+
export function upsertPoint(handle, skillId, vector) {
|
|
347
|
+
const existing = handle.idToLabel.get(skillId);
|
|
348
|
+
if (existing !== undefined) {
|
|
349
|
+
// hnswlib supports `addPoint(..., replaceDeleted=true)` for true
|
|
350
|
+
// replacement; for non-deleted points we mark + reinsert under a new
|
|
351
|
+
// label to preserve correctness across efConstruction tuning.
|
|
352
|
+
handle.index.markDelete(existing);
|
|
353
|
+
handle.labelToId.delete(existing);
|
|
354
|
+
}
|
|
355
|
+
const label = handle.nextLabel;
|
|
356
|
+
// Plain Array required (see addPoint comment above).
|
|
357
|
+
handle.index.addPoint(Array.from(vector), label);
|
|
358
|
+
handle.idToLabel.set(skillId, label);
|
|
359
|
+
handle.labelToId.set(label, skillId);
|
|
360
|
+
handle.nextLabel = label + 1;
|
|
361
|
+
handle.schedulePersist();
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Mark a point deleted. The point stays in the graph for traversal correctness
|
|
365
|
+
* but `findSimilarHnsw` filters it out via the labelToId lookup.
|
|
366
|
+
*/
|
|
367
|
+
export function removePoint(handle, skillId) {
|
|
368
|
+
const label = handle.idToLabel.get(skillId);
|
|
369
|
+
if (label === undefined)
|
|
370
|
+
return false;
|
|
371
|
+
handle.index.markDelete(label);
|
|
372
|
+
handle.idToLabel.delete(skillId);
|
|
373
|
+
handle.labelToId.delete(label);
|
|
374
|
+
handle.schedulePersist();
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Test-only helper — clears the cached `hnswlib-node` constructor reference so
|
|
379
|
+
* tests can simulate "module reinstalled" scenarios. Not part of the public
|
|
380
|
+
* API; do not use in production code.
|
|
381
|
+
*
|
|
382
|
+
* @internal
|
|
383
|
+
*/
|
|
384
|
+
export function __resetCachedHnswCtorForTests() {
|
|
385
|
+
cachedCtor = null;
|
|
386
|
+
}
|
|
387
|
+
//# sourceMappingURL=hnsw-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hnsw-search.js","sourceRoot":"","sources":["../../../src/embeddings/hnsw-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,CAAA;AAC/F,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AA4DhD;;;GAGG;AACH,IAAI,UAAU,GAAsD,IAAI,CAAA;AAExE;;;;;;;;;;GAUG;AACH,KAAK,UAAU,YAAY;IACzB,IAAI,UAAU,KAAK,aAAa;QAAE,OAAO,IAAI,CAAA;IAC7C,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,UAAU,CAAA;IAC1C,IAAI,CAAC;QACH,0EAA0E;QAC1E,qEAAqE;QACrE,mEAAmE;QACnE,eAAe;QACf,EAAE;QACF,4EAA4E;QAC5E,0EAA0E;QAC1E,wEAAwE;QACxE,0EAA0E;QAC1E,iEAAiE;QACjE,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,cAAc,CAAC,CAGxC,CAAA;QACD,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,OAAO,EAAE,eAAe,CAAA;QAChE,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,UAAU,GAAG,aAAa,CAAA;YAC1B,OAAO,IAAI,CAAA;QACb,CAAC;QACD,UAAU,GAAG,IAAI,CAAA;QACjB,OAAO,UAAU,CAAA;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,UAAU,GAAG,aAAa,CAAA;QAC1B,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,SAAiB;IACnC,oEAAoE;IACpE,wDAAwD;IACxD,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;IAClD,MAAM,GAAG,GAAG,WAAW,EAAE,CAAA;IACzB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,QAAQ,EAAE,CAAC,CAAA;IAC1C,OAAO;QACL,GAAG,EAAE,GAAG,IAAI,MAAM;QAClB,IAAI,EAAE,GAAG,IAAI,YAAY;QACzB,MAAM,EAAE,GAAG,IAAI,cAAc;QAC7B,MAAM,EAAE,GAAG,IAAI,UAAU;QACzB,OAAO,EAAE,GAAG,IAAI,gBAAgB;QAChC,SAAS,EAAE,GAAG,IAAI,kBAAkB;KACrC,CAAA;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,QAAgB;IAChC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAA;IACtC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAa,CAAA;QACtE,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;QACrC,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,UAAkB;IACpC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAA;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAA4B,CAAA;QACvF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAA;QACvC,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,GAAW,EAAE,KAAa,EAAE,QAAyB;IACxE,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5C,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAC9F,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;AACxB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAUrC;IACC,MAAM,IAAI,GAAG,MAAM,YAAY,EAAE,CAAA;IACjC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO;YACL,IAAI,EAAE,yBAAyB;YAC/B,MAAM,EAAE,mDAAmD;SAC5D,CAAA;IACH,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAA;IAClC,oEAAoE;IACpE,kEAAkE;IAClE,oEAAoE;IACpE,uEAAuE;IACvE,iDAAiD;IACjD,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,EAAE,CAAA;IACtB,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,GAAG,CAAA;IACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,GAAG,CAAA;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAA;IAE9E,MAAM,QAAQ,GACZ,IAAI,KAAK,IAAI;QACb,MAAM,KAAK,IAAI;QACf,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS;QACjC,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG;QACrB,IAAI,CAAC,KAAK,KAAK,KAAK;QACpB,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAEvB,IAAI,KAAsB,CAAA;IAC1B,IAAI,SAA8B,CAAA;IAClC,IAAI,SAA8B,CAAA;IAClC,IAAI,SAAiB,CAAA;IAErB,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;YACpC,sEAAsE;YACtE,sEAAsE;YACtE,wCAAwC;YACxC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACpC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;YACrB,SAAS,GAAG,IAAI,GAAG,CAAC,MAAO,CAAC,CAAA;YAC5B,SAAS,GAAG,IAAI,GAAG,CAAC,MAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;YAC9D,SAAS,GAAG,MAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;QAC5E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,kEAAkE;YAClE,IAAI,CAAC;gBACH,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC;oBAAE,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;gBAChD,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC;oBAAE,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBAClD,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC;oBAAE,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YACxD,CAAC;YAAC,MAAM,CAAC;gBACP,yBAAyB;YAC3B,CAAC;YACD,IAAI,CAAC;gBACH,OAAO,MAAM,eAAe,CAAC,IAAI,CAAC,CAAA;YACpC,CAAC;YAAC,OAAO,QAAQ,EAAE,CAAC;gBAClB,OAAO;oBACL,IAAI,EAAE,yBAAyB;oBAC/B,MAAM,EAAE,4CAA4C,MAAM,CAAC,QAAQ,CAAC,cAAc,MAAM,CAAC,GAAG,CAAC,GAAG;iBACjG,CAAA;YACH,CAAC;QACH,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;QACpC,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,EAAE,cAAc,CAAC,CAAA;QAC5C,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;QACrB,SAAS,GAAG,IAAI,GAAG,EAAE,CAAA;QACrB,SAAS,GAAG,IAAI,GAAG,EAAE,CAAA;QACrB,IAAI,cAAc,GAAG,CAAC,CAAA;QACtB,KAAK,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC7C,mEAAmE;YACnE,iDAAiD;YACjD,uDAAuD;YACvD,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,CAAA;YAC/C,SAAS,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC,CAAA;YACtC,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,CAAA;YACtC,cAAc,EAAE,CAAA;QAClB,CAAC;QACD,SAAS,GAAG,cAAc,CAAA;IAC5B,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC;QAC1B,KAAK;QACL,SAAS;QACT,SAAS;QACT,SAAS;QACT,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,KAAK;KACN,CAAC,CAAA;IAEF,wEAAwE;IACxE,mCAAmC;IACnC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,IAAI,CAAC;YACH,MAAM,CAAC,UAAU,EAAE,CAAA;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,IAAI,EAAE,yBAAyB;gBAC/B,MAAM,EAAE,+BAA+B,MAAM,CAAC,GAAG,CAAC,EAAE;aACrD,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;AAC/B,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,IAQrB;IACC,IAAI,KAAK,GAAG,KAAK,CAAA;IACjB,IAAI,KAAK,GAAyC,IAAI,CAAA;IACtD,0EAA0E;IAC1E,0DAA0D;IAC1D,MAAM,KAAK,GAAG;QACZ,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAA;IAED,MAAM,UAAU,GAAG,GAAS,EAAE;QAC5B,IAAI,KAAK,EAAE,CAAC;YACV,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,KAAK,GAAG,IAAI,CAAA;QACd,CAAC;QACD,IAAI,CAAC,KAAK,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACxE,gEAAgE;YAChE,OAAM;QACR,CAAC;QACD,wEAAwE;QACxE,0EAA0E;QAC1E,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QAC5C,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAE7C,MAAM,SAAS,GAA4B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAA;QAC/E,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAA;QAE/E,MAAM,IAAI,GAAa;YACrB,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI;YAC1B,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SAClC,CAAA;QACD,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;QAC/E,KAAK,GAAG,KAAK,CAAA;IACf,CAAC,CAAA;IAED,MAAM,eAAe,GAAG,GAAS,EAAE;QACjC,KAAK,GAAG,IAAI,CAAA;QACZ,IAAI,KAAK;YAAE,YAAY,CAAC,KAAK,CAAC,CAAA;QAC9B,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACtB,KAAK,GAAG,IAAI,CAAA;YACZ,IAAI,CAAC;gBACH,UAAU,EAAE,CAAA;YACd,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,oEAAoE;gBACpE,gEAAgE;gBAChE,+DAA+D;gBAC/D,OAAO,CAAC,IAAI,CACV,gDAAgD,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,EAClE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CACzC,CAAA;YACH,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,CAAA;QACR,uDAAuD;QACvD,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,UAAU;YAAE,KAAK,CAAC,KAAK,EAAE,CAAA;IAC/D,CAAC,CAAA;IAED,OAAO;QACL,IAAI,KAAK;YACP,OAAO,IAAI,CAAC,KAAK,CAAA;QACnB,CAAC;QACD,IAAI,SAAS;YACX,OAAO,IAAI,CAAC,SAAS,CAAA;QACvB,CAAC;QACD,IAAI,SAAS;YACX,OAAO,IAAI,CAAC,SAAS,CAAA;QACvB,CAAC;QACD,IAAI,SAAS;YACX,OAAO,KAAK,CAAC,SAAS,CAAA;QACxB,CAAC;QACD,IAAI,SAAS,CAAC,CAAS;YACrB,KAAK,CAAC,SAAS,GAAG,CAAC,CAAA;QACrB,CAAC;QACD,IAAI,KAAK;YACP,OAAO,IAAI,CAAC,KAAK,CAAA;QACnB,CAAC;QACD,eAAe;QACf,UAAU;KACX,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAkB,EAClB,KAAmB,EACnB,IAAY;IAEZ,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAA;IACvC,IAAI,SAAS,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;IACnC,8DAA8D;IAC9D,uDAAuD;IACvD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;IAC3D,MAAM,GAAG,GAA8C,EAAE,CAAA;IACzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACjD,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;QACzD,IAAI,CAAC,OAAO;YAAE,SAAQ,CAAC,gDAAgD;QACvE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IACvD,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,MAAkB,EAAE,OAAe,EAAE,MAAoB;IACnF,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC9C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,iEAAiE;QACjE,qEAAqE;QACrE,8DAA8D;QAC9D,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;QACjC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IACnC,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAA;IAC9B,qDAAqD;IACrD,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAA;IAChD,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IACpC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IACpC,MAAM,CAAC,SAAS,GAAG,KAAK,GAAG,CAAC,CAAA;IAC5B,MAAM,CAAC,eAAe,EAAE,CAAA;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,MAAkB,EAAE,OAAe;IAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IACrC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;IAC9B,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAChC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC9B,MAAM,CAAC,eAAe,EAAE,CAAA;IACxB,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,6BAA6B;IAC3C,UAAU,GAAG,IAAI,CAAA;AACnB,CAAC"}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SMI-1519: HNSW Embedding Store
|
|
3
|
+
* SMI-4577: Production HNSW path now lives in `embeddings/hnsw-search.ts` and is
|
|
4
|
+
* wired into `EmbeddingService.findSimilar`. This class retains its
|
|
5
|
+
* historical API for callers that want a self-contained store without
|
|
6
|
+
* the full `EmbeddingService` surface, but no longer references the
|
|
7
|
+
* V3 VectorDB (decommissioned after the claude-flow → ruflo rename).
|
|
3
8
|
*
|
|
4
|
-
* High-performance vector storage
|
|
5
|
-
*
|
|
9
|
+
* High-performance vector storage with SQLite-backed metadata. `findSimilar`
|
|
10
|
+
* uses brute-force search; consumers wanting the HNSW backend should use
|
|
11
|
+
* `EmbeddingService` instead, which lazy-loads `hnswlib-node` from the
|
|
12
|
+
* shared search module.
|
|
6
13
|
*
|
|
7
|
-
* Enable via: SKILLSMITH_USE_HNSW=true
|
|
8
14
|
* @see ADR-009: Embedding Service Fallback Strategy
|
|
9
15
|
*/
|
|
10
16
|
import type { SimilarityResult } from './index.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hnsw-store.d.ts","sourceRoot":"","sources":["../../../src/embeddings/hnsw-store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"hnsw-store.d.ts","sourceRoot":"","sources":["../../../src/embeddings/hnsw-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAMlD,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,0BAA0B,EAC1B,UAAU,EACV,yBAAyB,EACzB,cAAc,EACd,iBAAiB,EACjB,eAAe,GAChB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAGzE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAGvF,OAAO,KAAK,EACV,UAAU,EACV,yBAAyB,EACzB,cAAc,EACd,iBAAiB,EACjB,eAAe,EAEhB,MAAM,uBAAuB,CAAA;AAI9B;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,eAAe;IACxD,OAAO,CAAC,EAAE,CAA4B;IACtC,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;IACvD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,cAAc,CAAiC;IACvD,OAAO,CAAC,cAAc,CAAiC;IACvD,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,WAAW,CAA6B;IAEhD;;;;OAIG;gBACS,OAAO,GAAE,yBAA8B;IAqBnD;;;;;OAKG;WACU,MAAM,CAAC,OAAO,GAAE,yBAA8B,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAUnF,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAsB5E,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAalD,gBAAgB,IAAI,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC;IAiB7C,WAAW,CAAC,cAAc,EAAE,YAAY,EAAE,IAAI,GAAE,MAAW,GAAG,gBAAgB,EAAE;IA0B1E,gBAAgB,CACpB,cAAc,EAAE,YAAY,EAC5B,IAAI,GAAE,MAAW,GAChB,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAgB9B,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM;IAgB1D,eAAe,IAAI,OAAO;IAI1B,KAAK,IAAI,IAAI;IAOb,QAAQ,IAAI,cAAc;IA4B1B,WAAW,CACT,UAAU,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,YAAY,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,GAC5E,iBAAiB;IAqDpB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAmBzC,SAAS,IAAI,IAAI;IAQjB,SAAS,IAAI,IAAI;IAOX,YAAY,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBlE,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAOnC,OAAO,CAAC,YAAY;YAKN,iBAAiB;IAK/B,OAAO,CAAC,mBAAmB;YAYb,aAAa;IAS3B,OAAO,CAAC,oBAAoB;CAI7B"}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SMI-1519: HNSW Embedding Store
|
|
3
|
+
* SMI-4577: Production HNSW path now lives in `embeddings/hnsw-search.ts` and is
|
|
4
|
+
* wired into `EmbeddingService.findSimilar`. This class retains its
|
|
5
|
+
* historical API for callers that want a self-contained store without
|
|
6
|
+
* the full `EmbeddingService` surface, but no longer references the
|
|
7
|
+
* V3 VectorDB (decommissioned after the claude-flow → ruflo rename).
|
|
3
8
|
*
|
|
4
|
-
* High-performance vector storage
|
|
5
|
-
*
|
|
9
|
+
* High-performance vector storage with SQLite-backed metadata. `findSimilar`
|
|
10
|
+
* uses brute-force search; consumers wanting the HNSW backend should use
|
|
11
|
+
* `EmbeddingService` instead, which lazy-loads `hnswlib-node` from the
|
|
12
|
+
* shared search module.
|
|
6
13
|
*
|
|
7
|
-
* Enable via: SKILLSMITH_USE_HNSW=true
|
|
8
14
|
* @see ADR-009: Embedding Service Fallback Strategy
|
|
9
15
|
*/
|
|
10
16
|
import { createDatabaseSync, createDatabaseAsync } from '../db/createDatabase.js';
|
|
@@ -277,12 +283,13 @@ export class HNSWEmbeddingStore {
|
|
|
277
283
|
saveIndex() {
|
|
278
284
|
if (!this.indexPath)
|
|
279
285
|
throw new Error('Cannot save index: indexPath not configured');
|
|
280
|
-
|
|
286
|
+
// SMI-4577: persistence now handled by EmbeddingService HNSW backend; this store is a no-op shim.
|
|
287
|
+
console.log('[HNSWEmbeddingStore] saveIndex() is a no-op; use EmbeddingService for HNSW persistence');
|
|
281
288
|
}
|
|
282
289
|
loadIndex() {
|
|
283
290
|
if (!this.indexPath)
|
|
284
291
|
throw new Error('Cannot load index: indexPath not configured');
|
|
285
|
-
console.log('[HNSWEmbeddingStore]
|
|
292
|
+
console.log('[HNSWEmbeddingStore] loadIndex() is a no-op; use EmbeddingService for HNSW persistence');
|
|
286
293
|
}
|
|
287
294
|
async rebuildIndex(newConfig) {
|
|
288
295
|
if (newConfig)
|
|
@@ -339,8 +346,11 @@ export class HNSWEmbeddingStore {
|
|
|
339
346
|
`);
|
|
340
347
|
}
|
|
341
348
|
async initHNSWIndex() {
|
|
342
|
-
// V3 VectorDB
|
|
343
|
-
//
|
|
349
|
+
// SMI-4577: V3 VectorDB was decommissioned with the claude-flow → ruflo rename
|
|
350
|
+
// (SMI-3600). The replacement HNSW backend lives in `embeddings/hnsw-search.ts`
|
|
351
|
+
// and is exposed through `EmbeddingService.findSimilar`. This store retains
|
|
352
|
+
// its public API but always uses the brute-force code path; callers wanting
|
|
353
|
+
// HNSW should switch to `EmbeddingService` directly.
|
|
344
354
|
this.vectorDB = null;
|
|
345
355
|
}
|
|
346
356
|
distanceToSimilarity(distance) {
|