@vfarcic/dot-ai 0.194.0 → 0.194.1
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mermaid-tools.d.ts","sourceRoot":"","sources":["../../src/core/mermaid-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"mermaid-tools.d.ts","sourceRoot":"","sources":["../../src/core/mermaid-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AASjD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,MA0BnC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,EAEjC,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,GAAG,GACT,OAAO,CAAC,GAAG,CAAC,CAoDd"}
|
|
@@ -17,9 +17,7 @@ const mermaid_1 = __importDefault(require("mermaid"));
|
|
|
17
17
|
// Initialize mermaid with strict validation
|
|
18
18
|
mermaid_1.default.initialize({
|
|
19
19
|
securityLevel: 'strict',
|
|
20
|
-
startOnLoad: false
|
|
21
|
-
// Disable rendering, we only want parsing/validation
|
|
22
|
-
suppressErrorRendering: true
|
|
20
|
+
startOnLoad: false
|
|
23
21
|
});
|
|
24
22
|
/**
|
|
25
23
|
* Tool: validate_mermaid
|
|
@@ -43,6 +43,11 @@ export declare class VectorDBService {
|
|
|
43
43
|
* Handles conflict errors gracefully (collection already exists from race condition or restart)
|
|
44
44
|
*/
|
|
45
45
|
private createCollection;
|
|
46
|
+
/**
|
|
47
|
+
* Ensure text index exists on searchText field for efficient keyword search
|
|
48
|
+
* This is idempotent - safe to call multiple times
|
|
49
|
+
*/
|
|
50
|
+
private ensureTextIndex;
|
|
46
51
|
/**
|
|
47
52
|
* Store a document with optional vector
|
|
48
53
|
*/
|
|
@@ -53,6 +58,7 @@ export declare class VectorDBService {
|
|
|
53
58
|
searchSimilar(vector: number[], options?: SearchOptions): Promise<SearchResult[]>;
|
|
54
59
|
/**
|
|
55
60
|
* Search for documents using payload filtering (keyword search)
|
|
61
|
+
* Uses Qdrant's native text index for efficient server-side filtering
|
|
56
62
|
*/
|
|
57
63
|
searchByKeywords(keywords: string[], options?: SearchOptions): Promise<SearchResult[]>;
|
|
58
64
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vector-db-service.d.ts","sourceRoot":"","sources":["../../src/core/vector-db-service.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgDH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,cAAc,CAAS;gBAEnB,MAAM,GAAE,cAAmB;
|
|
1
|
+
{"version":3,"file":"vector-db-service.d.ts","sourceRoot":"","sources":["../../src/core/vector-db-service.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgDH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,cAAc,CAAS;gBAEnB,MAAM,GAAE,cAAmB;IAuBvC,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,sBAAsB;IAM9B;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IAa1C;;OAEG;IACG,oBAAoB,CAAC,UAAU,GAAE,MAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAoDnE;;;OAGG;YACW,gBAAgB;IAmC9B;;;OAGG;YACW,eAAe;IAwC7B;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC7D;;OAEG;IACG,aAAa,CACjB,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,EAAE,CAAC;IAuC1B;;;OAGG;IACG,gBAAgB,CACpB,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,EAAE,CAAC;IA8H1B;;OAEG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAqC7D;;OAEG;IACG,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B/C;;;;OAIG;IACG,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAuCzC;;;;;OAKG;IACG,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,GAAE,MAAY,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAuCnF;;;;OAIG;IACG,eAAe,CAAC,KAAK,GAAE,MAAc,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAgDvE;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC;IAqBvC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAsBrC;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,SAAS,IAAI,cAAc;CAG5B"}
|
|
@@ -44,8 +44,8 @@ class QdrantSemaphore {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
// Limit
|
|
48
|
-
const qdrantSemaphore = new QdrantSemaphore(
|
|
47
|
+
// Limit concurrent Qdrant bulk operations (scroll, getAllDocuments)
|
|
48
|
+
const qdrantSemaphore = new QdrantSemaphore(100);
|
|
49
49
|
class VectorDBService {
|
|
50
50
|
client = null;
|
|
51
51
|
config;
|
|
@@ -64,7 +64,8 @@ class VectorDBService {
|
|
|
64
64
|
if (this.shouldInitializeClient()) {
|
|
65
65
|
this.client = new js_client_rest_1.QdrantClient({
|
|
66
66
|
url: this.config.url,
|
|
67
|
-
apiKey: this.config.apiKey
|
|
67
|
+
apiKey: this.config.apiKey,
|
|
68
|
+
maxConnections: 100, // HTTP keep-alive pool for connection reuse
|
|
68
69
|
});
|
|
69
70
|
}
|
|
70
71
|
}
|
|
@@ -125,6 +126,10 @@ class VectorDBService {
|
|
|
125
126
|
await this.client.deleteCollection(this.collectionName);
|
|
126
127
|
await this.createCollection(vectorSize);
|
|
127
128
|
}
|
|
129
|
+
else {
|
|
130
|
+
// Ensure text index exists for existing collections (transparent upgrade)
|
|
131
|
+
await this.ensureTextIndex();
|
|
132
|
+
}
|
|
128
133
|
}
|
|
129
134
|
catch (error) {
|
|
130
135
|
// If we can't get collection info, assume it's corrupted and recreate
|
|
@@ -158,11 +163,12 @@ class VectorDBService {
|
|
|
158
163
|
distance: 'Cosine',
|
|
159
164
|
on_disk: true // Enable on-disk storage for better performance with large collections
|
|
160
165
|
},
|
|
161
|
-
// Enable payload indexing for better keyword search performance
|
|
162
166
|
optimizers_config: {
|
|
163
167
|
default_segment_number: 2
|
|
164
168
|
}
|
|
165
169
|
});
|
|
170
|
+
// Create text index on searchText field for efficient keyword search
|
|
171
|
+
await this.ensureTextIndex();
|
|
166
172
|
}
|
|
167
173
|
catch (error) {
|
|
168
174
|
// Handle race condition where collection was created between check and create
|
|
@@ -173,11 +179,53 @@ class VectorDBService {
|
|
|
173
179
|
if (process.env.DEBUG_DOT_AI) {
|
|
174
180
|
console.debug(`Collection ${this.collectionName} already exists, skipping creation`);
|
|
175
181
|
}
|
|
182
|
+
await this.ensureTextIndex();
|
|
176
183
|
return;
|
|
177
184
|
}
|
|
178
185
|
throw error;
|
|
179
186
|
}
|
|
180
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Ensure text index exists on searchText field for efficient keyword search
|
|
190
|
+
* This is idempotent - safe to call multiple times
|
|
191
|
+
*/
|
|
192
|
+
async ensureTextIndex() {
|
|
193
|
+
if (!this.client) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
// Check if index already exists
|
|
198
|
+
const collectionInfo = await this.client.getCollection(this.collectionName);
|
|
199
|
+
const payloadSchema = collectionInfo.payload_schema || {};
|
|
200
|
+
// Check if searchText already has a text index
|
|
201
|
+
const searchTextIndex = payloadSchema['searchText'];
|
|
202
|
+
if (searchTextIndex && searchTextIndex.data_type === 'text') {
|
|
203
|
+
return; // Index already exists
|
|
204
|
+
}
|
|
205
|
+
// Create text index on searchText field
|
|
206
|
+
await this.client.createPayloadIndex(this.collectionName, {
|
|
207
|
+
field_name: 'searchText',
|
|
208
|
+
field_schema: 'text',
|
|
209
|
+
});
|
|
210
|
+
if (process.env.DEBUG_DOT_AI) {
|
|
211
|
+
console.debug(`Created text index on searchText field for collection ${this.collectionName}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
216
|
+
const isAlreadyExists = errorMessage.toLowerCase().includes('already exists') ||
|
|
217
|
+
errorMessage.toLowerCase().includes('conflict');
|
|
218
|
+
if (isAlreadyExists) {
|
|
219
|
+
if (process.env.DEBUG_DOT_AI) {
|
|
220
|
+
console.debug(`Text index already exists for collection ${this.collectionName}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
// Unexpected error - log at warn level for visibility
|
|
225
|
+
console.warn(`Failed to create text index on ${this.collectionName}: ${errorMessage}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
181
229
|
/**
|
|
182
230
|
* Store a document with optional vector
|
|
183
231
|
*/
|
|
@@ -250,12 +298,16 @@ class VectorDBService {
|
|
|
250
298
|
}
|
|
251
299
|
/**
|
|
252
300
|
* Search for documents using payload filtering (keyword search)
|
|
301
|
+
* Uses Qdrant's native text index for efficient server-side filtering
|
|
253
302
|
*/
|
|
254
303
|
async searchByKeywords(keywords, options = {}) {
|
|
255
304
|
if (!this.client) {
|
|
256
305
|
throw new Error('Vector DB client not initialized');
|
|
257
306
|
}
|
|
258
307
|
const limit = options.limit || 10;
|
|
308
|
+
if (keywords.length === 0) {
|
|
309
|
+
return [];
|
|
310
|
+
}
|
|
259
311
|
return (0, qdrant_tracing_1.withQdrantTracing)({
|
|
260
312
|
operation: 'vector.search_keywords',
|
|
261
313
|
collectionName: this.collectionName,
|
|
@@ -264,15 +316,50 @@ class VectorDBService {
|
|
|
264
316
|
serverUrl: this.config.url
|
|
265
317
|
}, async () => {
|
|
266
318
|
try {
|
|
267
|
-
//
|
|
268
|
-
//
|
|
319
|
+
// Build Qdrant filter for text search
|
|
320
|
+
// Use "should" (OR) to match any keyword in searchText or triggers
|
|
321
|
+
const keywordConditions = [];
|
|
322
|
+
for (const keyword of keywords) {
|
|
323
|
+
// Text match on searchText field (uses text index)
|
|
324
|
+
keywordConditions.push({
|
|
325
|
+
key: 'searchText',
|
|
326
|
+
match: { text: keyword }
|
|
327
|
+
});
|
|
328
|
+
// Match on triggers array (for patterns/policies)
|
|
329
|
+
keywordConditions.push({
|
|
330
|
+
key: 'triggers',
|
|
331
|
+
match: { any: [keyword, keyword.toLowerCase()] }
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
// Combine keyword conditions with any user-provided filter
|
|
335
|
+
const filter = {
|
|
336
|
+
should: keywordConditions
|
|
337
|
+
};
|
|
338
|
+
// If user provided additional filters, merge them properly
|
|
339
|
+
if (options.filter) {
|
|
340
|
+
// Merge must conditions
|
|
341
|
+
if (options.filter.must) {
|
|
342
|
+
filter.must = Array.isArray(options.filter.must)
|
|
343
|
+
? options.filter.must
|
|
344
|
+
: [options.filter.must];
|
|
345
|
+
}
|
|
346
|
+
// Preserve must_not conditions
|
|
347
|
+
if (options.filter.must_not) {
|
|
348
|
+
filter.must_not = options.filter.must_not;
|
|
349
|
+
}
|
|
350
|
+
// If user filter has should conditions, wrap in must to AND with keyword should
|
|
351
|
+
if (options.filter.should) {
|
|
352
|
+
filter.must = [...(filter.must || []), { should: options.filter.should }];
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Use scroll with native Qdrant filtering - much faster than client-side
|
|
269
356
|
const scrollResult = await this.client.scroll(this.collectionName, {
|
|
270
|
-
limit:
|
|
357
|
+
limit: limit * 10, // Get more candidates for scoring, but not 1000
|
|
271
358
|
with_payload: true,
|
|
272
359
|
with_vector: false,
|
|
273
|
-
|
|
360
|
+
filter
|
|
274
361
|
});
|
|
275
|
-
//
|
|
362
|
+
// Score the filtered results (small set now)
|
|
276
363
|
const scoredPoints = scrollResult.points
|
|
277
364
|
.map(point => {
|
|
278
365
|
if (!point.payload)
|
|
@@ -286,35 +373,27 @@ class VectorDBService {
|
|
|
286
373
|
let exactMatch = false;
|
|
287
374
|
for (const keyword of keywords) {
|
|
288
375
|
const kw = keyword.toLowerCase();
|
|
289
|
-
// Check searchText
|
|
376
|
+
// Check searchText
|
|
290
377
|
if (searchText.includes(kw)) {
|
|
291
378
|
matchCount++;
|
|
292
|
-
// Bonus for exact word match (surrounded by spaces/punctuation)
|
|
293
379
|
const wordPattern = new RegExp(`\\b${escapeRegExp(kw)}\\b`, 'i');
|
|
294
380
|
if (wordPattern.test(searchText)) {
|
|
295
381
|
exactMatch = true;
|
|
296
382
|
}
|
|
297
383
|
}
|
|
298
|
-
// Check triggers
|
|
384
|
+
// Check triggers
|
|
299
385
|
if (triggers.some(t => t.includes(kw) || kw.includes(t))) {
|
|
300
386
|
matchCount++;
|
|
301
387
|
}
|
|
302
388
|
}
|
|
303
389
|
if (matchCount === 0)
|
|
304
390
|
return null;
|
|
305
|
-
// Score based on match quality
|
|
306
|
-
// - Base score from match ratio
|
|
307
|
-
// - Bonus for exact word matches
|
|
308
391
|
const baseScore = matchCount / keywords.length;
|
|
309
392
|
const score = exactMatch ? Math.min(1.0, baseScore + 0.3) : baseScore;
|
|
310
|
-
return {
|
|
311
|
-
point,
|
|
312
|
-
score
|
|
313
|
-
};
|
|
393
|
+
return { point, score };
|
|
314
394
|
})
|
|
315
395
|
.filter((item) => item !== null)
|
|
316
396
|
.sort((a, b) => b.score - a.score);
|
|
317
|
-
// Apply limit after filtering
|
|
318
397
|
const limitedResults = scoredPoints.slice(0, limit);
|
|
319
398
|
return limitedResults.map(({ point, score }) => ({
|
|
320
399
|
id: point.id.toString(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vfarcic/dot-ai",
|
|
3
|
-
"version": "0.194.
|
|
3
|
+
"version": "0.194.1",
|
|
4
4
|
"description": "AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance",
|
|
5
5
|
"mcpName": "io.github.vfarcic/dot-ai",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"@qdrant/js-client-rest": "^1.15.0",
|
|
115
115
|
"ai": "^5.0.60",
|
|
116
116
|
"handlebars": "^4.7.8",
|
|
117
|
-
"mermaid": "^
|
|
117
|
+
"mermaid": "^10.9.5",
|
|
118
118
|
"posthog-node": "^5.23.0",
|
|
119
119
|
"yaml": "^2.8.0",
|
|
120
120
|
"zod-to-json-schema": "^3.24.6"
|