@vfarcic/dot-ai 1.0.3 → 1.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 +1 -0
- package/dist/core/ai-provider.interface.d.ts +12 -8
- package/dist/core/ai-provider.interface.d.ts.map +1 -1
- package/dist/core/artifacthub.d.ts +1 -1
- package/dist/core/artifacthub.d.ts.map +1 -1
- package/dist/core/base-vector-service.d.ts +22 -9
- package/dist/core/base-vector-service.d.ts.map +1 -1
- package/dist/core/base-vector-service.js +106 -37
- package/dist/core/capabilities.d.ts.map +1 -1
- package/dist/core/capabilities.js +5 -2
- package/dist/core/capability-operations.d.ts +55 -7
- package/dist/core/capability-operations.d.ts.map +1 -1
- package/dist/core/capability-operations.js +1 -3
- package/dist/core/capability-scan-workflow.d.ts +64 -8
- package/dist/core/capability-scan-workflow.d.ts.map +1 -1
- package/dist/core/capability-scan-workflow.js +14 -13
- package/dist/core/capability-tools.d.ts +1 -1
- package/dist/core/capability-tools.d.ts.map +1 -1
- package/dist/core/capability-tools.js +1 -1
- package/dist/core/capability-vector-service.d.ts +3 -4
- package/dist/core/capability-vector-service.d.ts.map +1 -1
- package/dist/core/capability-vector-service.js +2 -2
- package/dist/core/command-executor.d.ts +3 -4
- package/dist/core/command-executor.d.ts.map +1 -1
- package/dist/core/command-executor.js +8 -4
- package/dist/core/crd-availability.d.ts +3 -5
- package/dist/core/crd-availability.d.ts.map +1 -1
- package/dist/core/crd-availability.js +8 -18
- package/dist/core/deploy-operation.d.ts +6 -5
- package/dist/core/deploy-operation.d.ts.map +1 -1
- package/dist/core/deploy-operation.js +16 -10
- package/dist/core/discovery.d.ts +6 -14
- package/dist/core/discovery.d.ts.map +1 -1
- package/dist/core/discovery.js +35 -51
- package/dist/core/embedding-service.d.ts.map +1 -1
- package/dist/core/embedding-service.js +1 -1
- package/dist/core/error-handling.d.ts +13 -13
- package/dist/core/error-handling.d.ts.map +1 -1
- package/dist/core/error-handling.js +2 -3
- package/dist/core/generic-session-manager.d.ts +2 -2
- package/dist/core/generic-session-manager.d.ts.map +1 -1
- package/dist/core/helm-types.d.ts +5 -5
- package/dist/core/helm-types.d.ts.map +1 -1
- package/dist/core/index.d.ts +4 -11
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +8 -14
- package/dist/core/knowledge-types.d.ts +114 -0
- package/dist/core/knowledge-types.d.ts.map +1 -0
- package/dist/core/knowledge-types.js +10 -0
- package/dist/core/memory.d.ts +12 -12
- package/dist/core/memory.d.ts.map +1 -1
- package/dist/core/mermaid-tools.d.ts +24 -1
- package/dist/core/mermaid-tools.d.ts.map +1 -1
- package/dist/core/mermaid-tools.js +10 -8
- package/dist/core/model-config.d.ts +1 -1
- package/dist/core/model-config.js +1 -1
- package/dist/core/packaging.d.ts +23 -1
- package/dist/core/packaging.d.ts.map +1 -1
- package/dist/core/pattern-operations.d.ts +32 -9
- package/dist/core/pattern-operations.d.ts.map +1 -1
- package/dist/core/pattern-operations.js +17 -22
- package/dist/core/pattern-vector-service.d.ts +3 -4
- package/dist/core/pattern-vector-service.d.ts.map +1 -1
- package/dist/core/pattern-vector-service.js +2 -2
- package/dist/core/platform-utils.d.ts +2 -2
- package/dist/core/platform-utils.d.ts.map +1 -1
- package/dist/core/plugin-manager.d.ts +6 -2
- package/dist/core/plugin-manager.d.ts.map +1 -1
- package/dist/core/plugin-manager.js +9 -16
- package/dist/core/plugin-registry.d.ts +59 -0
- package/dist/core/plugin-registry.d.ts.map +1 -0
- package/dist/core/plugin-registry.js +80 -0
- package/dist/core/policy-operations.d.ts +101 -21
- package/dist/core/policy-operations.d.ts.map +1 -1
- package/dist/core/policy-operations.js +45 -47
- package/dist/core/policy-vector-service.d.ts +3 -4
- package/dist/core/policy-vector-service.d.ts.map +1 -1
- package/dist/core/policy-vector-service.js +2 -2
- package/dist/core/providers/host-provider.d.ts +1 -1
- package/dist/core/providers/host-provider.d.ts.map +1 -1
- package/dist/core/providers/host-provider.js +2 -2
- package/dist/core/providers/provider-debug-utils.d.ts +2 -2
- package/dist/core/providers/provider-debug-utils.d.ts.map +1 -1
- package/dist/core/providers/tool-utils.d.ts +10 -2
- package/dist/core/providers/tool-utils.d.ts.map +1 -1
- package/dist/core/providers/tool-utils.js +2 -2
- package/dist/core/providers/vercel-provider.d.ts.map +1 -1
- package/dist/core/providers/vercel-provider.js +29 -23
- package/dist/core/resource-tools.d.ts +29 -1
- package/dist/core/resource-tools.d.ts.map +1 -1
- package/dist/core/resource-vector-service.d.ts +3 -4
- package/dist/core/resource-vector-service.d.ts.map +1 -1
- package/dist/core/resource-vector-service.js +2 -2
- package/dist/core/schema.d.ts +15 -14
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +32 -34
- package/dist/core/shared-prompt-loader.d.ts +1 -1
- package/dist/core/shared-prompt-loader.d.ts.map +1 -1
- package/dist/core/solution-cr.js +1 -1
- package/dist/core/solution-utils.d.ts +22 -3
- package/dist/core/solution-utils.d.ts.map +1 -1
- package/dist/core/solution-utils.js +1 -1
- package/dist/core/telemetry/client.d.ts +0 -6
- package/dist/core/telemetry/client.d.ts.map +1 -1
- package/dist/core/telemetry/client.js +6 -17
- package/dist/core/telemetry/config.js +1 -1
- package/dist/core/telemetry/index.d.ts +1 -1
- package/dist/core/telemetry/index.d.ts.map +1 -1
- package/dist/core/telemetry/index.js +1 -2
- package/dist/core/tracing/tool-tracing.d.ts +1 -1
- package/dist/core/tracing/tool-tracing.d.ts.map +1 -1
- package/dist/core/unified-creation-session.d.ts +15 -8
- package/dist/core/unified-creation-session.d.ts.map +1 -1
- package/dist/core/unified-creation-session.js +19 -19
- package/dist/core/unified-creation-types.d.ts +2 -2
- package/dist/core/unified-creation-types.d.ts.map +1 -1
- package/dist/core/visualization.d.ts +1 -1
- package/dist/core/visualization.d.ts.map +1 -1
- package/dist/core/workflow.d.ts +8 -5
- package/dist/core/workflow.d.ts.map +1 -1
- package/dist/evaluation/dataset-analyzer.d.ts +13 -7
- package/dist/evaluation/dataset-analyzer.d.ts.map +1 -1
- package/dist/evaluation/dataset-analyzer.js +1 -1
- package/dist/evaluation/datasets/loader.d.ts +2 -2
- package/dist/evaluation/datasets/loader.d.ts.map +1 -1
- package/dist/evaluation/eval-runner.js +7 -5
- package/dist/evaluation/evaluators/base-comparative.d.ts +1 -1
- package/dist/evaluation/evaluators/base-comparative.d.ts.map +1 -1
- package/dist/evaluation/evaluators/base-comparative.js +4 -3
- package/dist/evaluation/evaluators/base.d.ts +5 -5
- package/dist/evaluation/evaluators/base.d.ts.map +1 -1
- package/dist/evaluation/evaluators/capability-comparative.js +1 -1
- package/dist/evaluation/platform-synthesizer.d.ts.map +1 -1
- package/dist/interfaces/mcp.d.ts.map +1 -1
- package/dist/interfaces/mcp.js +26 -15
- package/dist/interfaces/openapi-generator.d.ts +116 -12
- package/dist/interfaces/openapi-generator.d.ts.map +1 -1
- package/dist/interfaces/openapi-generator.js +500 -207
- package/dist/interfaces/rest-api.d.ts +28 -6
- package/dist/interfaces/rest-api.d.ts.map +1 -1
- package/dist/interfaces/rest-api.js +436 -245
- package/dist/interfaces/rest-registry.d.ts +5 -5
- package/dist/interfaces/rest-registry.d.ts.map +1 -1
- package/dist/interfaces/rest-registry.js +12 -27
- package/dist/interfaces/rest-route-registry.d.ts +165 -0
- package/dist/interfaces/rest-route-registry.d.ts.map +1 -0
- package/dist/interfaces/rest-route-registry.js +230 -0
- package/dist/interfaces/routes/index.d.ts +22 -0
- package/dist/interfaces/routes/index.d.ts.map +1 -0
- package/dist/interfaces/routes/index.js +347 -0
- package/dist/interfaces/schemas/common.d.ts +170 -0
- package/dist/interfaces/schemas/common.d.ts.map +1 -0
- package/dist/interfaces/schemas/common.js +114 -0
- package/dist/interfaces/schemas/events.d.ts +127 -0
- package/dist/interfaces/schemas/events.d.ts.map +1 -0
- package/dist/interfaces/schemas/events.js +66 -0
- package/dist/interfaces/schemas/index.d.ts +21 -0
- package/dist/interfaces/schemas/index.d.ts.map +1 -0
- package/dist/interfaces/schemas/index.js +138 -0
- package/dist/interfaces/schemas/knowledge.d.ts +201 -0
- package/dist/interfaces/schemas/knowledge.d.ts.map +1 -0
- package/dist/interfaces/schemas/knowledge.js +117 -0
- package/dist/interfaces/schemas/logs.d.ts +78 -0
- package/dist/interfaces/schemas/logs.d.ts.map +1 -0
- package/dist/interfaces/schemas/logs.js +46 -0
- package/dist/interfaces/schemas/prompts.d.ts +185 -0
- package/dist/interfaces/schemas/prompts.d.ts.map +1 -0
- package/dist/interfaces/schemas/prompts.js +91 -0
- package/dist/interfaces/schemas/resources.d.ts +363 -0
- package/dist/interfaces/schemas/resources.d.ts.map +1 -0
- package/dist/interfaces/schemas/resources.js +173 -0
- package/dist/interfaces/schemas/sessions.d.ts +87 -0
- package/dist/interfaces/schemas/sessions.d.ts.map +1 -0
- package/dist/interfaces/schemas/sessions.js +56 -0
- package/dist/interfaces/schemas/tools.d.ts +188 -0
- package/dist/interfaces/schemas/tools.d.ts.map +1 -0
- package/dist/interfaces/schemas/tools.js +101 -0
- package/dist/interfaces/schemas/visualization.d.ts +369 -0
- package/dist/interfaces/schemas/visualization.d.ts.map +1 -0
- package/dist/interfaces/schemas/visualization.js +134 -0
- package/dist/mcp/server.js +5 -4
- package/dist/tools/answer-question.d.ts +1 -1
- package/dist/tools/answer-question.d.ts.map +1 -1
- package/dist/tools/answer-question.js +9 -8
- package/dist/tools/deploy-manifests.d.ts +4 -2
- package/dist/tools/deploy-manifests.d.ts.map +1 -1
- package/dist/tools/deploy-manifests.js +10 -6
- package/dist/tools/generate-manifests.d.ts.map +1 -1
- package/dist/tools/generate-manifests.js +28 -20
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +6 -1
- package/dist/tools/manage-knowledge.d.ts +77 -0
- package/dist/tools/manage-knowledge.d.ts.map +1 -0
- package/dist/tools/manage-knowledge.js +573 -0
- package/dist/tools/operate-analysis.d.ts +31 -2
- package/dist/tools/operate-analysis.d.ts.map +1 -1
- package/dist/tools/operate-execution.d.ts +2 -3
- package/dist/tools/operate-execution.d.ts.map +1 -1
- package/dist/tools/operate-execution.js +7 -7
- package/dist/tools/operate.d.ts +7 -2
- package/dist/tools/operate.d.ts.map +1 -1
- package/dist/tools/operate.js +2 -2
- package/dist/tools/organizational-data.d.ts +30 -4
- package/dist/tools/organizational-data.d.ts.map +1 -1
- package/dist/tools/organizational-data.js +24 -19
- package/dist/tools/project-setup/discovery.d.ts.map +1 -1
- package/dist/tools/project-setup/generate-scope.d.ts +1 -1
- package/dist/tools/project-setup/generate-scope.d.ts.map +1 -1
- package/dist/tools/project-setup/types.d.ts +1 -0
- package/dist/tools/project-setup/types.d.ts.map +1 -1
- package/dist/tools/prompts.d.ts +29 -3
- package/dist/tools/prompts.d.ts.map +1 -1
- package/dist/tools/prompts.js +6 -3
- package/dist/tools/query.d.ts +17 -3
- package/dist/tools/query.d.ts.map +1 -1
- package/dist/tools/query.js +1 -7
- package/dist/tools/recommend.d.ts +24 -6
- package/dist/tools/recommend.d.ts.map +1 -1
- package/dist/tools/recommend.js +18 -15
- package/dist/tools/remediate.d.ts +12 -3
- package/dist/tools/remediate.d.ts.map +1 -1
- package/dist/tools/remediate.js +22 -14
- package/dist/tools/version.d.ts +19 -5
- package/dist/tools/version.d.ts.map +1 -1
- package/dist/tools/version.js +106 -54
- package/package.json +15 -9
- package/prompts/knowledge-ask.md +29 -0
- package/scripts/generate-openapi.sh +56 -0
- package/scripts/grafana-stack.nu +118 -0
- package/shared-prompts/prd-create.md +2 -0
- package/dist/core/vector-db-service.d.ts +0 -108
- package/dist/core/vector-db-service.d.ts.map +0 -1
- package/dist/core/vector-db-service.js +0 -647
|
@@ -1,647 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Vector DB Service
|
|
4
|
-
*
|
|
5
|
-
* Handles Qdrant Vector Database integration for semantic search and storage
|
|
6
|
-
*/
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.VectorDBService = void 0;
|
|
9
|
-
const js_client_rest_1 = require("@qdrant/js-client-rest");
|
|
10
|
-
const qdrant_tracing_1 = require("./tracing/qdrant-tracing");
|
|
11
|
-
/**
|
|
12
|
-
* Escape special regex characters to prevent ReDoS attacks
|
|
13
|
-
*/
|
|
14
|
-
function escapeRegExp(str) {
|
|
15
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Simple semaphore to limit concurrent Qdrant operations
|
|
19
|
-
* Prevents overwhelming Qdrant with too many parallel requests
|
|
20
|
-
*/
|
|
21
|
-
class QdrantSemaphore {
|
|
22
|
-
maxConcurrent;
|
|
23
|
-
currentCount = 0;
|
|
24
|
-
waitQueue = [];
|
|
25
|
-
constructor(maxConcurrent = 20) {
|
|
26
|
-
this.maxConcurrent = maxConcurrent;
|
|
27
|
-
}
|
|
28
|
-
async acquire() {
|
|
29
|
-
if (this.currentCount < this.maxConcurrent) {
|
|
30
|
-
this.currentCount++;
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
return new Promise((resolve) => {
|
|
34
|
-
this.waitQueue.push(resolve);
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
release() {
|
|
38
|
-
if (this.waitQueue.length > 0) {
|
|
39
|
-
const next = this.waitQueue.shift();
|
|
40
|
-
next();
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
this.currentCount--;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
// Limit concurrent Qdrant bulk operations (scroll, getAllDocuments)
|
|
48
|
-
const qdrantSemaphore = new QdrantSemaphore(100);
|
|
49
|
-
class VectorDBService {
|
|
50
|
-
client = null;
|
|
51
|
-
config;
|
|
52
|
-
collectionName;
|
|
53
|
-
constructor(config = {}) {
|
|
54
|
-
if (!config.collectionName) {
|
|
55
|
-
throw new Error('Collection name is required for Vector DB service');
|
|
56
|
-
}
|
|
57
|
-
this.config = {
|
|
58
|
-
url: config.url !== undefined ? config.url : (process.env.QDRANT_URL || 'http://localhost:6333'),
|
|
59
|
-
apiKey: config.apiKey || process.env.QDRANT_API_KEY,
|
|
60
|
-
collectionName: config.collectionName
|
|
61
|
-
};
|
|
62
|
-
this.collectionName = this.config.collectionName;
|
|
63
|
-
this.validateConfig();
|
|
64
|
-
if (this.shouldInitializeClient()) {
|
|
65
|
-
this.client = new js_client_rest_1.QdrantClient({
|
|
66
|
-
url: this.config.url,
|
|
67
|
-
apiKey: this.config.apiKey,
|
|
68
|
-
maxConnections: 100, // HTTP keep-alive pool for connection reuse
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
validateConfig() {
|
|
73
|
-
// Allow test-friendly initialization
|
|
74
|
-
if (this.config.url === 'test-url' || this.config.url === 'mock-url') {
|
|
75
|
-
return; // Allow test configurations
|
|
76
|
-
}
|
|
77
|
-
if (!this.config.url || this.config.url.trim() === '') {
|
|
78
|
-
throw new Error('Qdrant URL is required for Vector DB integration');
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
shouldInitializeClient() {
|
|
82
|
-
// Don't initialize for test configurations
|
|
83
|
-
const testUrls = ['test-url', 'mock-url'];
|
|
84
|
-
return !testUrls.includes(this.config.url || '');
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Check if collection exists without creating it
|
|
88
|
-
*/
|
|
89
|
-
async collectionExists() {
|
|
90
|
-
if (!this.client) {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
const collections = await this.client.getCollections();
|
|
95
|
-
return collections.collections.some(col => col.name === this.collectionName);
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Initialize the collection if it doesn't exist
|
|
103
|
-
*/
|
|
104
|
-
async initializeCollection(vectorSize = 384) {
|
|
105
|
-
if (!this.client) {
|
|
106
|
-
throw new Error('Vector DB client not initialized');
|
|
107
|
-
}
|
|
108
|
-
return (0, qdrant_tracing_1.withQdrantTracing)({
|
|
109
|
-
operation: 'collection.initialize',
|
|
110
|
-
collectionName: this.collectionName,
|
|
111
|
-
vectorSize,
|
|
112
|
-
serverUrl: this.config.url
|
|
113
|
-
}, async () => {
|
|
114
|
-
try {
|
|
115
|
-
// Check if collection exists
|
|
116
|
-
const collections = await this.client.getCollections();
|
|
117
|
-
const collectionExists = collections.collections.some(col => col.name === this.collectionName);
|
|
118
|
-
if (collectionExists) {
|
|
119
|
-
// Verify existing collection has correct vector dimensions
|
|
120
|
-
try {
|
|
121
|
-
const collectionInfo = await this.client.getCollection(this.collectionName);
|
|
122
|
-
const existingVectorSize = collectionInfo.config?.params?.vectors?.size;
|
|
123
|
-
if (existingVectorSize && existingVectorSize !== vectorSize) {
|
|
124
|
-
// Dimension mismatch - recreate collection
|
|
125
|
-
console.warn(`Vector dimension mismatch: existing collection has ${existingVectorSize} dimensions, but ${vectorSize} expected. Recreating collection.`);
|
|
126
|
-
await this.client.deleteCollection(this.collectionName);
|
|
127
|
-
await this.createCollection(vectorSize);
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
// Ensure text index exists for existing collections (transparent upgrade)
|
|
131
|
-
await this.ensureTextIndex();
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
// If we can't get collection info, assume it's corrupted and recreate
|
|
136
|
-
console.warn(`Failed to get collection info, recreating collection: ${error}`);
|
|
137
|
-
await this.client.deleteCollection(this.collectionName);
|
|
138
|
-
await this.createCollection(vectorSize);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
// Create new collection
|
|
143
|
-
await this.createCollection(vectorSize);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
catch (error) {
|
|
147
|
-
throw new Error(`Failed to initialize collection: ${error}`);
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Create collection with specified vector size
|
|
153
|
-
* Handles conflict errors gracefully (collection already exists from race condition or restart)
|
|
154
|
-
*/
|
|
155
|
-
async createCollection(vectorSize) {
|
|
156
|
-
if (!this.client) {
|
|
157
|
-
throw new Error('Vector DB client not initialized');
|
|
158
|
-
}
|
|
159
|
-
try {
|
|
160
|
-
await this.client.createCollection(this.collectionName, {
|
|
161
|
-
vectors: {
|
|
162
|
-
size: vectorSize,
|
|
163
|
-
distance: 'Cosine',
|
|
164
|
-
on_disk: true // Enable on-disk storage for better performance with large collections
|
|
165
|
-
},
|
|
166
|
-
optimizers_config: {
|
|
167
|
-
default_segment_number: 2
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
// Create text index on searchText field for efficient keyword search
|
|
171
|
-
await this.ensureTextIndex();
|
|
172
|
-
}
|
|
173
|
-
catch (error) {
|
|
174
|
-
// Handle race condition where collection was created between check and create
|
|
175
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
176
|
-
if (errorMessage.toLowerCase().includes('conflict') ||
|
|
177
|
-
errorMessage.toLowerCase().includes('already exists')) {
|
|
178
|
-
// Collection exists - this is fine (race condition or restart)
|
|
179
|
-
if (process.env.DEBUG_DOT_AI) {
|
|
180
|
-
console.debug(`Collection ${this.collectionName} already exists, skipping creation`);
|
|
181
|
-
}
|
|
182
|
-
await this.ensureTextIndex();
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
throw error;
|
|
186
|
-
}
|
|
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
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Store a document with optional vector
|
|
231
|
-
*/
|
|
232
|
-
async upsertDocument(document) {
|
|
233
|
-
if (!this.client) {
|
|
234
|
-
throw new Error('Vector DB client not initialized');
|
|
235
|
-
}
|
|
236
|
-
return (0, qdrant_tracing_1.withQdrantTracing)({
|
|
237
|
-
operation: 'vector.upsert',
|
|
238
|
-
collectionName: this.collectionName,
|
|
239
|
-
documentId: document.id,
|
|
240
|
-
vectorSize: document.vector?.length,
|
|
241
|
-
serverUrl: this.config.url
|
|
242
|
-
}, async () => {
|
|
243
|
-
try {
|
|
244
|
-
const point = {
|
|
245
|
-
id: document.id,
|
|
246
|
-
payload: document.payload,
|
|
247
|
-
vector: document.vector
|
|
248
|
-
};
|
|
249
|
-
// Validate vector is provided
|
|
250
|
-
if (!document.vector || document.vector.length === 0) {
|
|
251
|
-
throw new Error('Vector is required for vector database storage');
|
|
252
|
-
}
|
|
253
|
-
await this.client.upsert(this.collectionName, {
|
|
254
|
-
wait: true,
|
|
255
|
-
points: [point]
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
catch (error) {
|
|
259
|
-
throw new Error(`Failed to upsert document: ${error}`);
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Search for similar documents using vector similarity
|
|
265
|
-
*/
|
|
266
|
-
async searchSimilar(vector, options = {}) {
|
|
267
|
-
if (!this.client) {
|
|
268
|
-
throw new Error('Vector DB client not initialized');
|
|
269
|
-
}
|
|
270
|
-
const limit = options.limit || 10;
|
|
271
|
-
const scoreThreshold = options.scoreThreshold || 0.5;
|
|
272
|
-
return (0, qdrant_tracing_1.withQdrantTracing)({
|
|
273
|
-
operation: 'vector.search',
|
|
274
|
-
collectionName: this.collectionName,
|
|
275
|
-
vectorSize: vector.length,
|
|
276
|
-
limit,
|
|
277
|
-
scoreThreshold,
|
|
278
|
-
serverUrl: this.config.url
|
|
279
|
-
}, async () => {
|
|
280
|
-
try {
|
|
281
|
-
const searchResult = await this.client.search(this.collectionName, {
|
|
282
|
-
vector,
|
|
283
|
-
limit,
|
|
284
|
-
score_threshold: scoreThreshold,
|
|
285
|
-
with_payload: true,
|
|
286
|
-
...(options.filter && { filter: options.filter })
|
|
287
|
-
});
|
|
288
|
-
return searchResult.map(result => ({
|
|
289
|
-
id: result.id.toString(),
|
|
290
|
-
score: result.score,
|
|
291
|
-
payload: result.payload || {}
|
|
292
|
-
}));
|
|
293
|
-
}
|
|
294
|
-
catch (error) {
|
|
295
|
-
throw new Error(`Failed to search documents: ${error}`);
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Search for documents using payload filtering (keyword search)
|
|
301
|
-
* Uses Qdrant's native text index for efficient server-side filtering
|
|
302
|
-
*/
|
|
303
|
-
async searchByKeywords(keywords, options = {}) {
|
|
304
|
-
if (!this.client) {
|
|
305
|
-
throw new Error('Vector DB client not initialized');
|
|
306
|
-
}
|
|
307
|
-
const limit = options.limit || 10;
|
|
308
|
-
if (keywords.length === 0) {
|
|
309
|
-
return [];
|
|
310
|
-
}
|
|
311
|
-
return (0, qdrant_tracing_1.withQdrantTracing)({
|
|
312
|
-
operation: 'vector.search_keywords',
|
|
313
|
-
collectionName: this.collectionName,
|
|
314
|
-
keywordCount: keywords.length,
|
|
315
|
-
limit,
|
|
316
|
-
serverUrl: this.config.url
|
|
317
|
-
}, async () => {
|
|
318
|
-
try {
|
|
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
|
|
356
|
-
const scrollResult = await this.client.scroll(this.collectionName, {
|
|
357
|
-
limit: limit * 10, // Get more candidates for scoring, but not 1000
|
|
358
|
-
with_payload: true,
|
|
359
|
-
with_vector: false,
|
|
360
|
-
filter
|
|
361
|
-
});
|
|
362
|
-
// Score the filtered results (small set now)
|
|
363
|
-
const scoredPoints = scrollResult.points
|
|
364
|
-
.map(point => {
|
|
365
|
-
if (!point.payload)
|
|
366
|
-
return null;
|
|
367
|
-
const searchText = (point.payload.searchText || '').toLowerCase();
|
|
368
|
-
const triggers = Array.isArray(point.payload.triggers)
|
|
369
|
-
? point.payload.triggers.map(t => t.toLowerCase())
|
|
370
|
-
: [];
|
|
371
|
-
// Count keyword matches for scoring
|
|
372
|
-
let matchCount = 0;
|
|
373
|
-
let exactMatch = false;
|
|
374
|
-
for (const keyword of keywords) {
|
|
375
|
-
const kw = keyword.toLowerCase();
|
|
376
|
-
// Check searchText
|
|
377
|
-
if (searchText.includes(kw)) {
|
|
378
|
-
matchCount++;
|
|
379
|
-
const wordPattern = new RegExp(`\\b${escapeRegExp(kw)}\\b`, 'i');
|
|
380
|
-
if (wordPattern.test(searchText)) {
|
|
381
|
-
exactMatch = true;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
// Check triggers
|
|
385
|
-
if (triggers.some(t => t.includes(kw) || kw.includes(t))) {
|
|
386
|
-
matchCount++;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
if (matchCount === 0)
|
|
390
|
-
return null;
|
|
391
|
-
const baseScore = matchCount / keywords.length;
|
|
392
|
-
const score = exactMatch ? Math.min(1.0, baseScore + 0.3) : baseScore;
|
|
393
|
-
return { point, score };
|
|
394
|
-
})
|
|
395
|
-
.filter((item) => item !== null)
|
|
396
|
-
.sort((a, b) => b.score - a.score);
|
|
397
|
-
const limitedResults = scoredPoints.slice(0, limit);
|
|
398
|
-
return limitedResults.map(({ point, score }) => ({
|
|
399
|
-
id: point.id.toString(),
|
|
400
|
-
score,
|
|
401
|
-
payload: point.payload || {}
|
|
402
|
-
}));
|
|
403
|
-
}
|
|
404
|
-
catch (error) {
|
|
405
|
-
throw new Error(`Failed to search by keywords: ${error}`);
|
|
406
|
-
}
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Get a document by ID
|
|
411
|
-
*/
|
|
412
|
-
async getDocument(id) {
|
|
413
|
-
if (!this.client) {
|
|
414
|
-
throw new Error('Vector DB client not initialized');
|
|
415
|
-
}
|
|
416
|
-
return (0, qdrant_tracing_1.withQdrantTracing)({
|
|
417
|
-
operation: 'vector.retrieve',
|
|
418
|
-
collectionName: this.collectionName,
|
|
419
|
-
documentId: id,
|
|
420
|
-
serverUrl: this.config.url
|
|
421
|
-
}, async () => {
|
|
422
|
-
try {
|
|
423
|
-
const result = await this.client.retrieve(this.collectionName, {
|
|
424
|
-
ids: [id],
|
|
425
|
-
with_payload: true,
|
|
426
|
-
with_vector: true
|
|
427
|
-
});
|
|
428
|
-
if (result.length === 0) {
|
|
429
|
-
return null;
|
|
430
|
-
}
|
|
431
|
-
const point = result[0];
|
|
432
|
-
return {
|
|
433
|
-
id: point.id.toString(),
|
|
434
|
-
payload: point.payload || {},
|
|
435
|
-
vector: point.vector || undefined
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
catch (error) {
|
|
439
|
-
throw new Error(`Failed to get document: ${error}`);
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* Delete a document by ID
|
|
445
|
-
*/
|
|
446
|
-
async deleteDocument(id) {
|
|
447
|
-
if (!this.client) {
|
|
448
|
-
throw new Error('Vector DB client not initialized');
|
|
449
|
-
}
|
|
450
|
-
return (0, qdrant_tracing_1.withQdrantTracing)({
|
|
451
|
-
operation: 'vector.delete',
|
|
452
|
-
collectionName: this.collectionName,
|
|
453
|
-
documentId: id,
|
|
454
|
-
serverUrl: this.config.url
|
|
455
|
-
}, async () => {
|
|
456
|
-
try {
|
|
457
|
-
await this.client.delete(this.collectionName, {
|
|
458
|
-
wait: true,
|
|
459
|
-
points: [id]
|
|
460
|
-
});
|
|
461
|
-
// Qdrant's wait:true ensures the write operation completes, but there can be
|
|
462
|
-
// a brief window where subsequent reads may still return stale data due to
|
|
463
|
-
// internal segment synchronization. This delay ensures consistency for
|
|
464
|
-
// immediate read-after-delete operations in integration tests and workflows.
|
|
465
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
466
|
-
}
|
|
467
|
-
catch (error) {
|
|
468
|
-
throw new Error(`Failed to delete document: ${error}`);
|
|
469
|
-
}
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Delete all documents by clearing all points from the collection
|
|
474
|
-
* More efficient than retrieving and deleting individual records
|
|
475
|
-
* Collection structure is preserved, avoiding Qdrant storage cleanup issues
|
|
476
|
-
*/
|
|
477
|
-
async deleteAllDocuments() {
|
|
478
|
-
if (!this.client) {
|
|
479
|
-
throw new Error('Vector DB client not initialized');
|
|
480
|
-
}
|
|
481
|
-
return (0, qdrant_tracing_1.withQdrantTracing)({
|
|
482
|
-
operation: 'vector.delete_all',
|
|
483
|
-
collectionName: this.collectionName,
|
|
484
|
-
serverUrl: this.config.url
|
|
485
|
-
}, async () => {
|
|
486
|
-
try {
|
|
487
|
-
// Check if collection exists first
|
|
488
|
-
const collections = await this.client.getCollections();
|
|
489
|
-
const collectionExists = collections.collections.some(col => col.name === this.collectionName);
|
|
490
|
-
if (!collectionExists) {
|
|
491
|
-
// Collection doesn't exist, nothing to delete
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
// Delete all points from collection instead of deleting entire collection
|
|
495
|
-
// This avoids Qdrant's known storage directory cleanup bug
|
|
496
|
-
await this.client.delete(this.collectionName, {
|
|
497
|
-
filter: {
|
|
498
|
-
must: [] // Empty must array matches all points
|
|
499
|
-
},
|
|
500
|
-
wait: true // Wait for operation to complete synchronously
|
|
501
|
-
});
|
|
502
|
-
console.warn(`All points deleted from collection ${this.collectionName} (collection structure preserved)`);
|
|
503
|
-
}
|
|
504
|
-
catch (error) {
|
|
505
|
-
throw new Error(`Failed to delete all documents: ${error}`);
|
|
506
|
-
}
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
/**
|
|
510
|
-
* Scroll documents with Qdrant filter
|
|
511
|
-
* @param filter - Qdrant filter object (must/should/must_not conditions)
|
|
512
|
-
* @param limit - Maximum documents to retrieve
|
|
513
|
-
* Uses semaphore to limit concurrent Qdrant operations
|
|
514
|
-
*/
|
|
515
|
-
async scrollWithFilter(filter, limit = 100) {
|
|
516
|
-
if (!this.client) {
|
|
517
|
-
throw new Error('Vector DB client not initialized');
|
|
518
|
-
}
|
|
519
|
-
// Acquire semaphore slot before executing
|
|
520
|
-
await qdrantSemaphore.acquire();
|
|
521
|
-
try {
|
|
522
|
-
return await (0, qdrant_tracing_1.withQdrantTracing)({
|
|
523
|
-
operation: 'vector.scroll_filtered',
|
|
524
|
-
collectionName: this.collectionName,
|
|
525
|
-
limit,
|
|
526
|
-
serverUrl: this.config.url
|
|
527
|
-
}, async () => {
|
|
528
|
-
try {
|
|
529
|
-
const scrollResult = await this.client.scroll(this.collectionName, {
|
|
530
|
-
filter,
|
|
531
|
-
limit,
|
|
532
|
-
with_payload: true,
|
|
533
|
-
with_vector: false
|
|
534
|
-
});
|
|
535
|
-
return scrollResult.points.map(point => ({
|
|
536
|
-
id: point.id.toString(),
|
|
537
|
-
payload: point.payload || {}
|
|
538
|
-
}));
|
|
539
|
-
}
|
|
540
|
-
catch (error) {
|
|
541
|
-
throw new Error(`Failed to scroll with filter: ${error}`);
|
|
542
|
-
}
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
finally {
|
|
546
|
-
qdrantSemaphore.release();
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* Get all documents (for listing)
|
|
551
|
-
* @param limit - Maximum number of documents to retrieve. Defaults to unlimited (10000).
|
|
552
|
-
* Uses semaphore to limit concurrent Qdrant operations
|
|
553
|
-
*/
|
|
554
|
-
async getAllDocuments(limit = 10000) {
|
|
555
|
-
if (!this.client) {
|
|
556
|
-
throw new Error('Vector DB client not initialized');
|
|
557
|
-
}
|
|
558
|
-
// Acquire semaphore slot before executing
|
|
559
|
-
await qdrantSemaphore.acquire();
|
|
560
|
-
try {
|
|
561
|
-
return await (0, qdrant_tracing_1.withQdrantTracing)({
|
|
562
|
-
operation: 'vector.list',
|
|
563
|
-
collectionName: this.collectionName,
|
|
564
|
-
limit,
|
|
565
|
-
serverUrl: this.config.url
|
|
566
|
-
}, async () => {
|
|
567
|
-
try {
|
|
568
|
-
// Check if collection exists first
|
|
569
|
-
const collections = await this.client.getCollections();
|
|
570
|
-
const collectionExists = collections.collections.some(col => col.name === this.collectionName);
|
|
571
|
-
if (!collectionExists) {
|
|
572
|
-
throw new Error(`Collection '${this.collectionName}' does not exist. No data has been stored yet.`);
|
|
573
|
-
}
|
|
574
|
-
const scrollResult = await this.client.scroll(this.collectionName, {
|
|
575
|
-
limit,
|
|
576
|
-
with_payload: true,
|
|
577
|
-
with_vector: false
|
|
578
|
-
});
|
|
579
|
-
return scrollResult.points.map(point => ({
|
|
580
|
-
id: point.id.toString(),
|
|
581
|
-
payload: point.payload || {}
|
|
582
|
-
}));
|
|
583
|
-
}
|
|
584
|
-
catch (error) {
|
|
585
|
-
throw new Error(`Failed to get all documents: ${error}`);
|
|
586
|
-
}
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
finally {
|
|
590
|
-
qdrantSemaphore.release();
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
/**
|
|
594
|
-
* Get collection info and statistics
|
|
595
|
-
*/
|
|
596
|
-
async getCollectionInfo() {
|
|
597
|
-
if (!this.client) {
|
|
598
|
-
throw new Error('Vector DB client not initialized');
|
|
599
|
-
}
|
|
600
|
-
return (0, qdrant_tracing_1.withQdrantTracing)({
|
|
601
|
-
operation: 'collection.get',
|
|
602
|
-
collectionName: this.collectionName,
|
|
603
|
-
serverUrl: this.config.url
|
|
604
|
-
}, async () => {
|
|
605
|
-
try {
|
|
606
|
-
return await this.client.getCollection(this.collectionName);
|
|
607
|
-
}
|
|
608
|
-
catch (error) {
|
|
609
|
-
throw new Error(`Failed to get collection info: ${error}`);
|
|
610
|
-
}
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
|
-
/**
|
|
614
|
-
* Check if Vector DB is available and responsive
|
|
615
|
-
*/
|
|
616
|
-
async healthCheck() {
|
|
617
|
-
if (!this.client) {
|
|
618
|
-
return false;
|
|
619
|
-
}
|
|
620
|
-
return (0, qdrant_tracing_1.withQdrantTracing)({
|
|
621
|
-
operation: 'health_check',
|
|
622
|
-
collectionName: this.collectionName,
|
|
623
|
-
serverUrl: this.config.url
|
|
624
|
-
}, async () => {
|
|
625
|
-
try {
|
|
626
|
-
await this.client.getCollections();
|
|
627
|
-
return true;
|
|
628
|
-
}
|
|
629
|
-
catch (error) {
|
|
630
|
-
return false;
|
|
631
|
-
}
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* Check if client is initialized
|
|
636
|
-
*/
|
|
637
|
-
isInitialized() {
|
|
638
|
-
return this.client !== null;
|
|
639
|
-
}
|
|
640
|
-
/**
|
|
641
|
-
* Get configuration (for debugging)
|
|
642
|
-
*/
|
|
643
|
-
getConfig() {
|
|
644
|
-
return { ...this.config };
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
exports.VectorDBService = VectorDBService;
|