promptgraph-mcp 2.8.1 → 2.8.3

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.
@@ -1,187 +1,187 @@
1
- import { VectorStore } from './vector-store.js';
2
- import fs from 'fs';
3
- import path from 'path';
4
-
5
- const DIMS = 384;
6
- const EF_SEARCH = 100;
7
-
8
- let HierarchicalNSW = null;
9
- let moduleError = null;
10
-
11
- async function getHnswModule() {
12
- if (HierarchicalNSW) return HierarchicalNSW;
13
- if (moduleError) throw moduleError;
14
- try {
15
- const mod = await import('hnswlib-node');
16
- HierarchicalNSW = mod.default.HierarchicalNSW;
17
- return HierarchicalNSW;
18
- } catch (e) {
19
- moduleError = new Error(
20
- 'hnswlib-node not available. Run: npm install hnswlib-node'
21
- );
22
- throw moduleError;
23
- }
24
- }
25
-
26
- function toArray(v) {
27
- return Array.from(v);
28
- }
29
-
30
- export class HNSWVectorStore extends VectorStore {
31
- constructor() {
32
- super();
33
- this._index = null;
34
- this._vectors = [];
35
- this._idMap = [];
36
- this._itemCount = 0;
37
- this._maxElements = 0;
38
- }
39
-
40
- async add(id, vector) {
41
- const HNSW = await getHnswModule();
42
- const vec = toArray(vector);
43
- if (!this._index) {
44
- this._maxElements = 10000;
45
- this._index = new HNSW('cosine', DIMS);
46
- this._index.initIndex(this._maxElements, 200);
47
- }
48
- if (this._itemCount >= this._maxElements) {
49
- await this._rebuild(this._maxElements * 2);
50
- }
51
- this._index.addPoint(vec, this._itemCount);
52
- this._vectors.push(vec);
53
- this._idMap.push(id);
54
- this._itemCount++;
55
- }
56
-
57
- async addBatch(entries) {
58
- for (const { id, vector } of entries) {
59
- await this.add(id, vector);
60
- }
61
- }
62
-
63
- async remove(id) {
64
- const remainingVectors = [];
65
- const remainingIds = [];
66
- for (let i = 0; i < this._itemCount; i++) {
67
- if (this._idMap[i] !== id) {
68
- remainingVectors.push(this._vectors[i]);
69
- remainingIds.push(this._idMap[i]);
70
- }
71
- }
72
- this._index = null;
73
- this._vectors = remainingVectors;
74
- this._idMap = remainingIds;
75
- this._itemCount = remainingVectors.length;
76
- this._maxElements = 0;
77
- if (this._itemCount > 0) {
78
- await this._rebuild(this._itemCount + 1000);
79
- }
80
- }
81
-
82
- async search(vector, topK = 20) {
83
- await getHnswModule();
84
- if (!this._index || this._itemCount === 0) return [];
85
-
86
- const efSearch = Math.max(topK * 4, EF_SEARCH);
87
- this._index.setEf(efSearch);
88
-
89
- const numCandidates = Math.min(topK * 8, this._itemCount);
90
- const result = this._index.searchKnn(toArray(vector), numCandidates);
91
-
92
- const bestBySkill = new Map();
93
- const neighbors = result.neighbors;
94
- const distances = result.distances;
95
- for (let i = 0; i < neighbors.length; i++) {
96
- const idx = neighbors[i];
97
- const score = 1 - distances[i];
98
- const skillId = this._idMap[idx];
99
- const prev = bestBySkill.get(skillId);
100
- if (!prev || score > prev) bestBySkill.set(skillId, score);
101
- }
102
-
103
- return [...bestBySkill.entries()]
104
- .sort((a, b) => b[1] - a[1])
105
- .slice(0, topK)
106
- .map(([skill_id, score]) => ({ skill_id, score }));
107
- }
108
-
109
- async build(entries) {
110
- const HNSW = await getHnswModule();
111
- const count = entries.length;
112
-
113
- this._vectors = [];
114
- this._idMap = [];
115
- this._itemCount = 0;
116
- this._maxElements = 0;
117
- this._index = null;
118
-
119
- if (count === 0) return;
120
-
121
- this._maxElements = count + 1000;
122
- this._index = new HNSW('cosine', DIMS);
123
- this._index.initIndex(this._maxElements, 200);
124
-
125
- for (let i = 0; i < count; i++) {
126
- const { skill_id, vector } = entries[i];
127
- const vec = toArray(vector);
128
- this._index.addPoint(vec, i);
129
- this._vectors.push(vec);
130
- this._idMap.push(skill_id);
131
- this._itemCount++;
132
- }
133
- }
134
-
135
- async clear() {
136
- this._index = null;
137
- this._vectors = [];
138
- this._idMap = [];
139
- this._itemCount = 0;
140
- this._maxElements = 0;
141
- }
142
-
143
- get size() {
144
- return this._itemCount;
145
- }
146
-
147
- async save(dir) {
148
- const HNSW = await getHnswModule();
149
- if (!this._index) return;
150
- fs.mkdirSync(dir, { recursive: true });
151
- this._index.writeIndexSync(path.join(dir, 'index.bin'));
152
- fs.writeFileSync(path.join(dir, 'vectors.json'), JSON.stringify(this._vectors), 'utf8');
153
- fs.writeFileSync(path.join(dir, 'idmap.json'), JSON.stringify(this._idMap), 'utf8');
154
- }
155
-
156
- async load(dir) {
157
- const HNSW = await getHnswModule();
158
- const indexPath = path.join(dir, 'index.bin');
159
- const vectorsPath = path.join(dir, 'vectors.json');
160
- const idmapPath = path.join(dir, 'idmap.json');
161
- if (!fs.existsSync(indexPath)) return false;
162
-
163
- this._index = new HNSW('cosine', DIMS);
164
- this._index.readIndexSync(indexPath);
165
- this._vectors = JSON.parse(fs.readFileSync(vectorsPath, 'utf8'));
166
- this._idMap = JSON.parse(fs.readFileSync(idmapPath, 'utf8'));
167
- this._itemCount = this._idMap.length;
168
- this._maxElements = this._index.getMaxElements();
169
- return true;
170
- }
171
-
172
- static async fromDir(dir) {
173
- const store = new HNSWVectorStore();
174
- await store.load(dir);
175
- return store;
176
- }
177
-
178
- async _rebuild(newMax) {
179
- const HNSW = await getHnswModule();
180
- this._maxElements = newMax;
181
- this._index = new HNSW('cosine', DIMS);
182
- this._index.initIndex(this._maxElements, 200);
183
- for (let i = 0; i < this._itemCount; i++) {
184
- this._index.addPoint(this._vectors[i], i);
185
- }
186
- }
187
- }
1
+ import { VectorStore } from './vector-store.js';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ const DIMS = 384;
6
+ const EF_SEARCH = 100;
7
+
8
+ let HierarchicalNSW = null;
9
+ let moduleError = null;
10
+
11
+ async function getHnswModule() {
12
+ if (HierarchicalNSW) return HierarchicalNSW;
13
+ if (moduleError) throw moduleError;
14
+ try {
15
+ const mod = await import('hnswlib-node');
16
+ HierarchicalNSW = mod.default.HierarchicalNSW;
17
+ return HierarchicalNSW;
18
+ } catch (e) {
19
+ moduleError = new Error(
20
+ 'hnswlib-node not available. Run: npm install hnswlib-node'
21
+ );
22
+ throw moduleError;
23
+ }
24
+ }
25
+
26
+ function toArray(v) {
27
+ return Array.from(v);
28
+ }
29
+
30
+ export class HNSWVectorStore extends VectorStore {
31
+ constructor() {
32
+ super();
33
+ this._index = null;
34
+ this._vectors = [];
35
+ this._idMap = [];
36
+ this._itemCount = 0;
37
+ this._maxElements = 0;
38
+ }
39
+
40
+ async add(id, vector) {
41
+ const HNSW = await getHnswModule();
42
+ const vec = toArray(vector);
43
+ if (!this._index) {
44
+ this._maxElements = 10000;
45
+ this._index = new HNSW('cosine', DIMS);
46
+ this._index.initIndex(this._maxElements, 200);
47
+ }
48
+ if (this._itemCount >= this._maxElements) {
49
+ await this._rebuild(this._maxElements * 2);
50
+ }
51
+ this._index.addPoint(vec, this._itemCount);
52
+ this._vectors.push(vec);
53
+ this._idMap.push(id);
54
+ this._itemCount++;
55
+ }
56
+
57
+ async addBatch(entries) {
58
+ for (const { id, vector } of entries) {
59
+ await this.add(id, vector);
60
+ }
61
+ }
62
+
63
+ async remove(id) {
64
+ const remainingVectors = [];
65
+ const remainingIds = [];
66
+ for (let i = 0; i < this._itemCount; i++) {
67
+ if (this._idMap[i] !== id) {
68
+ remainingVectors.push(this._vectors[i]);
69
+ remainingIds.push(this._idMap[i]);
70
+ }
71
+ }
72
+ this._index = null;
73
+ this._vectors = remainingVectors;
74
+ this._idMap = remainingIds;
75
+ this._itemCount = remainingVectors.length;
76
+ this._maxElements = 0;
77
+ if (this._itemCount > 0) {
78
+ await this._rebuild(this._itemCount + 1000);
79
+ }
80
+ }
81
+
82
+ async search(vector, topK = 20) {
83
+ await getHnswModule();
84
+ if (!this._index || this._itemCount === 0) return [];
85
+
86
+ const efSearch = Math.max(topK * 4, EF_SEARCH);
87
+ this._index.setEf(efSearch);
88
+
89
+ const numCandidates = Math.min(topK * 8, this._itemCount);
90
+ const result = this._index.searchKnn(toArray(vector), numCandidates);
91
+
92
+ const bestBySkill = new Map();
93
+ const neighbors = result.neighbors;
94
+ const distances = result.distances;
95
+ for (let i = 0; i < neighbors.length; i++) {
96
+ const idx = neighbors[i];
97
+ const score = 1 - distances[i];
98
+ const skillId = this._idMap[idx];
99
+ const prev = bestBySkill.get(skillId);
100
+ if (!prev || score > prev) bestBySkill.set(skillId, score);
101
+ }
102
+
103
+ return [...bestBySkill.entries()]
104
+ .sort((a, b) => b[1] - a[1])
105
+ .slice(0, topK)
106
+ .map(([skill_id, score]) => ({ skill_id, score }));
107
+ }
108
+
109
+ async build(entries) {
110
+ const HNSW = await getHnswModule();
111
+ const count = entries.length;
112
+
113
+ this._vectors = [];
114
+ this._idMap = [];
115
+ this._itemCount = 0;
116
+ this._maxElements = 0;
117
+ this._index = null;
118
+
119
+ if (count === 0) return;
120
+
121
+ this._maxElements = count + 1000;
122
+ this._index = new HNSW('cosine', DIMS);
123
+ this._index.initIndex(this._maxElements, 200);
124
+
125
+ for (let i = 0; i < count; i++) {
126
+ const { skill_id, vector } = entries[i];
127
+ const vec = toArray(vector);
128
+ this._index.addPoint(vec, i);
129
+ this._vectors.push(vec);
130
+ this._idMap.push(skill_id);
131
+ this._itemCount++;
132
+ }
133
+ }
134
+
135
+ async clear() {
136
+ this._index = null;
137
+ this._vectors = [];
138
+ this._idMap = [];
139
+ this._itemCount = 0;
140
+ this._maxElements = 0;
141
+ }
142
+
143
+ get size() {
144
+ return this._itemCount;
145
+ }
146
+
147
+ async save(dir) {
148
+ const HNSW = await getHnswModule();
149
+ if (!this._index) return;
150
+ fs.mkdirSync(dir, { recursive: true });
151
+ this._index.writeIndexSync(path.join(dir, 'index.bin'));
152
+ fs.writeFileSync(path.join(dir, 'vectors.json'), JSON.stringify(this._vectors), 'utf8');
153
+ fs.writeFileSync(path.join(dir, 'idmap.json'), JSON.stringify(this._idMap), 'utf8');
154
+ }
155
+
156
+ async load(dir) {
157
+ const HNSW = await getHnswModule();
158
+ const indexPath = path.join(dir, 'index.bin');
159
+ const vectorsPath = path.join(dir, 'vectors.json');
160
+ const idmapPath = path.join(dir, 'idmap.json');
161
+ if (!fs.existsSync(indexPath)) return false;
162
+
163
+ this._index = new HNSW('cosine', DIMS);
164
+ this._index.readIndexSync(indexPath);
165
+ this._vectors = JSON.parse(fs.readFileSync(vectorsPath, 'utf8'));
166
+ this._idMap = JSON.parse(fs.readFileSync(idmapPath, 'utf8'));
167
+ this._itemCount = this._idMap.length;
168
+ this._maxElements = this._index.getMaxElements();
169
+ return true;
170
+ }
171
+
172
+ static async fromDir(dir) {
173
+ const store = new HNSWVectorStore();
174
+ await store.load(dir);
175
+ return store;
176
+ }
177
+
178
+ async _rebuild(newMax) {
179
+ const HNSW = await getHnswModule();
180
+ this._maxElements = newMax;
181
+ this._index = new HNSW('cosine', DIMS);
182
+ this._index.initIndex(this._maxElements, 200);
183
+ for (let i = 0; i < this._itemCount; i++) {
184
+ this._index.addPoint(this._vectors[i], i);
185
+ }
186
+ }
187
+ }
@@ -1,19 +1,19 @@
1
- import { FlatVectorStore } from './flat-store.js';
2
- import { HNSWVectorStore } from './hnsw-store.js';
3
-
4
- let _store = null;
5
-
6
- export function getStore(type = null) {
7
- if (_store) return _store;
8
-
9
- if (!type) {
10
- type = process.env.PG_VECTOR_STORE || 'hnsw';
11
- }
12
-
13
- _store = type === 'hnsw' ? new HNSWVectorStore() : new FlatVectorStore();
14
- return _store;
15
- }
16
-
17
- export function resetStore() {
18
- _store = null;
19
- }
1
+ import { FlatVectorStore } from './flat-store.js';
2
+ import { HNSWVectorStore } from './hnsw-store.js';
3
+
4
+ let _store = null;
5
+
6
+ export function getStore(type = null) {
7
+ if (_store) return _store;
8
+
9
+ if (!type) {
10
+ type = process.env.PG_VECTOR_STORE || 'hnsw';
11
+ }
12
+
13
+ _store = type === 'hnsw' ? new HNSWVectorStore() : new FlatVectorStore();
14
+ return _store;
15
+ }
16
+
17
+ export function resetStore() {
18
+ _store = null;
19
+ }
@@ -1,9 +1,9 @@
1
- export class VectorStore {
2
- async add(id, vector) { throw new Error('not implemented'); }
3
- async addBatch(entries) { throw new Error('not implemented'); }
4
- async remove(id) { throw new Error('not implemented'); }
5
- async search(vector, topK) { throw new Error('not implemented'); }
6
- async build(entries) { throw new Error('not implemented'); }
7
- async clear() { throw new Error('not implemented'); }
8
- get size() { return 0; }
9
- }
1
+ export class VectorStore {
2
+ async add(id, vector) { throw new Error('not implemented'); }
3
+ async addBatch(entries) { throw new Error('not implemented'); }
4
+ async remove(id) { throw new Error('not implemented'); }
5
+ async search(vector, topK) { throw new Error('not implemented'); }
6
+ async build(entries) { throw new Error('not implemented'); }
7
+ async clear() { throw new Error('not implemented'); }
8
+ get size() { return 0; }
9
+ }
@@ -1,33 +1,33 @@
1
- export class RateLimiter {
2
- constructor({ maxRequests, windowMs }) {
3
- this.maxRequests = maxRequests
4
- this.windowMs = windowMs
5
- this.timestamps = []
6
- this._chain = Promise.resolve()
7
- }
8
-
9
- async acquire() {
10
- const prev = this._chain
11
- this._chain = prev.then(() => this._doAcquire())
12
- return this._chain
13
- }
14
-
15
- async _doAcquire() {
16
- while (!this.tryAcquire()) {
17
- const oldest = this.timestamps[0]
18
- const waitMs = oldest
19
- ? Math.max(1, oldest + this.windowMs - Date.now())
20
- : 100
21
- await new Promise(r => setTimeout(r, Math.min(waitMs, this.windowMs)))
22
- }
23
- }
24
-
25
- tryAcquire() {
26
- const now = Date.now()
27
- const cutoff = now - this.windowMs
28
- this.timestamps = this.timestamps.filter(t => t > cutoff)
29
- if (this.timestamps.length >= this.maxRequests) return false
30
- this.timestamps.push(now)
31
- return true
32
- }
33
- }
1
+ export class RateLimiter {
2
+ constructor({ maxRequests, windowMs }) {
3
+ this.maxRequests = maxRequests
4
+ this.windowMs = windowMs
5
+ this.timestamps = []
6
+ this._chain = Promise.resolve()
7
+ }
8
+
9
+ async acquire() {
10
+ const prev = this._chain
11
+ this._chain = prev.then(() => this._doAcquire())
12
+ return this._chain
13
+ }
14
+
15
+ async _doAcquire() {
16
+ while (!this.tryAcquire()) {
17
+ const oldest = this.timestamps[0]
18
+ const waitMs = oldest
19
+ ? Math.max(1, oldest + this.windowMs - Date.now())
20
+ : 100
21
+ await new Promise(r => setTimeout(r, Math.min(waitMs, this.windowMs)))
22
+ }
23
+ }
24
+
25
+ tryAcquire() {
26
+ const now = Date.now()
27
+ const cutoff = now - this.windowMs
28
+ this.timestamps = this.timestamps.filter(t => t > cutoff)
29
+ if (this.timestamps.length >= this.maxRequests) return false
30
+ this.timestamps.push(now)
31
+ return true
32
+ }
33
+ }