@mastra/qdrant 0.10.3 → 0.11.0-alpha.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.
- package/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +26 -0
- package/dist/_tsup-dts-rollup.d.cts +80 -4
- package/dist/_tsup-dts-rollup.d.ts +80 -4
- package/dist/index.cjs +165 -57
- package/dist/index.js +160 -52
- package/package.json +4 -4
- package/src/vector/filter.test.ts +40 -40
- package/src/vector/filter.ts +103 -5
- package/src/vector/index.test.ts +32 -27
- package/src/vector/index.ts +172 -60
package/src/vector/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MastraError, ErrorDomain, ErrorCategory } from '@mastra/core/error';
|
|
1
2
|
import { MastraVector } from '@mastra/core/vector';
|
|
2
3
|
import type {
|
|
3
4
|
QueryResult,
|
|
@@ -10,11 +11,11 @@ import type {
|
|
|
10
11
|
DeleteVectorParams,
|
|
11
12
|
UpdateVectorParams,
|
|
12
13
|
} from '@mastra/core/vector';
|
|
13
|
-
import type { VectorFilter } from '@mastra/core/vector/filter';
|
|
14
14
|
import { QdrantClient } from '@qdrant/js-client-rest';
|
|
15
15
|
import type { Schemas } from '@qdrant/js-client-rest';
|
|
16
16
|
|
|
17
17
|
import { QdrantFilterTranslator } from './filter';
|
|
18
|
+
import type { QdrantVectorFilter } from './filter';
|
|
18
19
|
|
|
19
20
|
const BATCH_SIZE = 256;
|
|
20
21
|
const DISTANCE_MAPPING: Record<string, Schemas['Distance']> = {
|
|
@@ -23,6 +24,8 @@ const DISTANCE_MAPPING: Record<string, Schemas['Distance']> = {
|
|
|
23
24
|
dotproduct: 'Dot',
|
|
24
25
|
};
|
|
25
26
|
|
|
27
|
+
type QdrantQueryVectorParams = QueryVectorParams<QdrantVectorFilter>;
|
|
28
|
+
|
|
26
29
|
export class QdrantVector extends MastraVector {
|
|
27
30
|
private client: QdrantClient;
|
|
28
31
|
|
|
@@ -58,25 +61,50 @@ export class QdrantVector extends MastraVector {
|
|
|
58
61
|
payload: metadata?.[i] || {},
|
|
59
62
|
}));
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
try {
|
|
65
|
+
for (let i = 0; i < records.length; i += BATCH_SIZE) {
|
|
66
|
+
const batch = records.slice(i, i + BATCH_SIZE);
|
|
67
|
+
await this.client.upsert(indexName, {
|
|
68
|
+
// @ts-expect-error
|
|
69
|
+
points: batch,
|
|
70
|
+
wait: true,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
69
73
|
|
|
70
|
-
|
|
74
|
+
return pointIds;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
throw new MastraError(
|
|
77
|
+
{
|
|
78
|
+
id: 'STORAGE_QDRANT_VECTOR_UPSERT_FAILED',
|
|
79
|
+
domain: ErrorDomain.STORAGE,
|
|
80
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
81
|
+
details: { indexName, vectorCount: vectors.length },
|
|
82
|
+
},
|
|
83
|
+
error,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
async createIndex({ indexName, dimension, metric = 'cosine' }: CreateIndexParams): Promise<void> {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
try {
|
|
90
|
+
if (!Number.isInteger(dimension) || dimension <= 0) {
|
|
91
|
+
throw new Error('Dimension must be a positive integer');
|
|
92
|
+
}
|
|
93
|
+
if (!DISTANCE_MAPPING[metric]) {
|
|
94
|
+
throw new Error(`Invalid metric: "${metric}". Must be one of: cosine, euclidean, dotproduct`);
|
|
95
|
+
}
|
|
96
|
+
} catch (validationError) {
|
|
97
|
+
throw new MastraError(
|
|
98
|
+
{
|
|
99
|
+
id: 'STORAGE_QDRANT_VECTOR_CREATE_INDEX_INVALID_ARGS',
|
|
100
|
+
domain: ErrorDomain.STORAGE,
|
|
101
|
+
category: ErrorCategory.USER,
|
|
102
|
+
details: { indexName, dimension, metric },
|
|
103
|
+
},
|
|
104
|
+
validationError,
|
|
105
|
+
);
|
|
79
106
|
}
|
|
107
|
+
|
|
80
108
|
try {
|
|
81
109
|
await this.client.createCollection(indexName, {
|
|
82
110
|
vectors: {
|
|
@@ -92,10 +120,20 @@ export class QdrantVector extends MastraVector {
|
|
|
92
120
|
await this.validateExistingIndex(indexName, dimension, metric);
|
|
93
121
|
return;
|
|
94
122
|
}
|
|
123
|
+
|
|
124
|
+
throw new MastraError(
|
|
125
|
+
{
|
|
126
|
+
id: 'STORAGE_QDRANT_VECTOR_CREATE_INDEX_FAILED',
|
|
127
|
+
domain: ErrorDomain.STORAGE,
|
|
128
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
129
|
+
details: { indexName, dimension, metric },
|
|
130
|
+
},
|
|
131
|
+
error,
|
|
132
|
+
);
|
|
95
133
|
}
|
|
96
134
|
}
|
|
97
135
|
|
|
98
|
-
transformFilter(filter?:
|
|
136
|
+
transformFilter(filter?: QdrantVectorFilter) {
|
|
99
137
|
const translator = new QdrantFilterTranslator();
|
|
100
138
|
return translator.translate(filter);
|
|
101
139
|
}
|
|
@@ -106,43 +144,66 @@ export class QdrantVector extends MastraVector {
|
|
|
106
144
|
topK = 10,
|
|
107
145
|
filter,
|
|
108
146
|
includeVector = false,
|
|
109
|
-
}:
|
|
147
|
+
}: QdrantQueryVectorParams): Promise<QueryResult[]> {
|
|
110
148
|
const translatedFilter = this.transformFilter(filter) ?? {};
|
|
111
149
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
query
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
150
|
+
try {
|
|
151
|
+
const results = (
|
|
152
|
+
await this.client.query(indexName, {
|
|
153
|
+
query: queryVector,
|
|
154
|
+
limit: topK,
|
|
155
|
+
filter: translatedFilter,
|
|
156
|
+
with_payload: true,
|
|
157
|
+
with_vector: includeVector,
|
|
158
|
+
})
|
|
159
|
+
).points;
|
|
121
160
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
161
|
+
return results.map(match => {
|
|
162
|
+
let vector: number[] = [];
|
|
163
|
+
if (includeVector) {
|
|
164
|
+
if (Array.isArray(match.vector)) {
|
|
165
|
+
// If it's already an array of numbers
|
|
166
|
+
vector = match.vector as number[];
|
|
167
|
+
} else if (typeof match.vector === 'object' && match.vector !== null) {
|
|
168
|
+
// If it's an object with vector data
|
|
169
|
+
vector = Object.values(match.vector).filter(v => typeof v === 'number');
|
|
170
|
+
}
|
|
131
171
|
}
|
|
132
|
-
}
|
|
133
172
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
173
|
+
return {
|
|
174
|
+
id: match.id as string,
|
|
175
|
+
score: match.score || 0,
|
|
176
|
+
metadata: match.payload as Record<string, any>,
|
|
177
|
+
...(includeVector && { vector }),
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
} catch (error) {
|
|
181
|
+
throw new MastraError(
|
|
182
|
+
{
|
|
183
|
+
id: 'STORAGE_QDRANT_VECTOR_QUERY_FAILED',
|
|
184
|
+
domain: ErrorDomain.STORAGE,
|
|
185
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
186
|
+
details: { indexName, topK },
|
|
187
|
+
},
|
|
188
|
+
error,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
141
191
|
}
|
|
142
192
|
|
|
143
193
|
async listIndexes(): Promise<string[]> {
|
|
144
|
-
|
|
145
|
-
|
|
194
|
+
try {
|
|
195
|
+
const response = await this.client.getCollections();
|
|
196
|
+
return response.collections.map(collection => collection.name) || [];
|
|
197
|
+
} catch (error) {
|
|
198
|
+
throw new MastraError(
|
|
199
|
+
{
|
|
200
|
+
id: 'STORAGE_QDRANT_VECTOR_LIST_INDEXES_FAILED',
|
|
201
|
+
domain: ErrorDomain.STORAGE,
|
|
202
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
203
|
+
},
|
|
204
|
+
error,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
146
207
|
}
|
|
147
208
|
|
|
148
209
|
/**
|
|
@@ -152,19 +213,43 @@ export class QdrantVector extends MastraVector {
|
|
|
152
213
|
* @returns A promise that resolves to the index statistics including dimension, count and metric
|
|
153
214
|
*/
|
|
154
215
|
async describeIndex({ indexName }: DescribeIndexParams): Promise<IndexStats> {
|
|
155
|
-
|
|
216
|
+
try {
|
|
217
|
+
const { config, points_count } = await this.client.getCollection(indexName);
|
|
156
218
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
219
|
+
const distance = config.params.vectors?.distance as Schemas['Distance'];
|
|
220
|
+
return {
|
|
221
|
+
dimension: config.params.vectors?.size as number,
|
|
222
|
+
count: points_count || 0,
|
|
223
|
+
// @ts-expect-error
|
|
224
|
+
metric: Object.keys(DISTANCE_MAPPING).find(key => DISTANCE_MAPPING[key] === distance),
|
|
225
|
+
};
|
|
226
|
+
} catch (error) {
|
|
227
|
+
throw new MastraError(
|
|
228
|
+
{
|
|
229
|
+
id: 'STORAGE_QDRANT_VECTOR_DESCRIBE_INDEX_FAILED',
|
|
230
|
+
domain: ErrorDomain.STORAGE,
|
|
231
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
232
|
+
details: { indexName },
|
|
233
|
+
},
|
|
234
|
+
error,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
164
237
|
}
|
|
165
238
|
|
|
166
239
|
async deleteIndex({ indexName }: DeleteIndexParams): Promise<void> {
|
|
167
|
-
|
|
240
|
+
try {
|
|
241
|
+
await this.client.deleteCollection(indexName);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
throw new MastraError(
|
|
244
|
+
{
|
|
245
|
+
id: 'STORAGE_QDRANT_VECTOR_DELETE_INDEX_FAILED',
|
|
246
|
+
domain: ErrorDomain.STORAGE,
|
|
247
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
248
|
+
details: { indexName },
|
|
249
|
+
},
|
|
250
|
+
error,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
168
253
|
}
|
|
169
254
|
|
|
170
255
|
/**
|
|
@@ -178,8 +263,20 @@ export class QdrantVector extends MastraVector {
|
|
|
178
263
|
* @throws Will throw an error if no updates are provided or if the update operation fails.
|
|
179
264
|
*/
|
|
180
265
|
async updateVector({ indexName, id, update }: UpdateVectorParams): Promise<void> {
|
|
181
|
-
|
|
182
|
-
|
|
266
|
+
try {
|
|
267
|
+
if (!update.vector && !update.metadata) {
|
|
268
|
+
throw new Error('No updates provided');
|
|
269
|
+
}
|
|
270
|
+
} catch (validationError) {
|
|
271
|
+
throw new MastraError(
|
|
272
|
+
{
|
|
273
|
+
id: 'STORAGE_QDRANT_VECTOR_UPDATE_VECTOR_INVALID_ARGS',
|
|
274
|
+
domain: ErrorDomain.STORAGE,
|
|
275
|
+
category: ErrorCategory.USER,
|
|
276
|
+
details: { indexName, id },
|
|
277
|
+
},
|
|
278
|
+
validationError,
|
|
279
|
+
);
|
|
183
280
|
}
|
|
184
281
|
|
|
185
282
|
const pointId = this.parsePointId(id);
|
|
@@ -219,8 +316,15 @@ export class QdrantVector extends MastraVector {
|
|
|
219
316
|
return;
|
|
220
317
|
}
|
|
221
318
|
} catch (error) {
|
|
222
|
-
|
|
223
|
-
|
|
319
|
+
throw new MastraError(
|
|
320
|
+
{
|
|
321
|
+
id: 'STORAGE_QDRANT_VECTOR_UPDATE_VECTOR_FAILED',
|
|
322
|
+
domain: ErrorDomain.STORAGE,
|
|
323
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
324
|
+
details: { indexName, id },
|
|
325
|
+
},
|
|
326
|
+
error,
|
|
327
|
+
);
|
|
224
328
|
}
|
|
225
329
|
}
|
|
226
330
|
|
|
@@ -240,8 +344,16 @@ export class QdrantVector extends MastraVector {
|
|
|
240
344
|
await this.client.delete(indexName, {
|
|
241
345
|
points: [pointId],
|
|
242
346
|
});
|
|
243
|
-
} catch (error
|
|
244
|
-
throw new
|
|
347
|
+
} catch (error) {
|
|
348
|
+
throw new MastraError(
|
|
349
|
+
{
|
|
350
|
+
id: 'STORAGE_QDRANT_VECTOR_DELETE_VECTOR_FAILED',
|
|
351
|
+
domain: ErrorDomain.STORAGE,
|
|
352
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
353
|
+
details: { indexName, id },
|
|
354
|
+
},
|
|
355
|
+
error,
|
|
356
|
+
);
|
|
245
357
|
}
|
|
246
358
|
}
|
|
247
359
|
|