@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.
@@ -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
- for (let i = 0; i < records.length; i += BATCH_SIZE) {
62
- const batch = records.slice(i, i + BATCH_SIZE);
63
- await this.client.upsert(indexName, {
64
- // @ts-expect-error
65
- points: batch,
66
- wait: true,
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
- return pointIds;
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
- if (!Number.isInteger(dimension) || dimension <= 0) {
75
- throw new Error('Dimension must be a positive integer');
76
- }
77
- if (!DISTANCE_MAPPING[metric]) {
78
- throw new Error(`Invalid metric: "${metric}". Must be one of: cosine, euclidean, dotproduct`);
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?: VectorFilter) {
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
- }: QueryVectorParams): Promise<QueryResult[]> {
147
+ }: QdrantQueryVectorParams): Promise<QueryResult[]> {
110
148
  const translatedFilter = this.transformFilter(filter) ?? {};
111
149
 
112
- const results = (
113
- await this.client.query(indexName, {
114
- query: queryVector,
115
- limit: topK,
116
- filter: translatedFilter,
117
- with_payload: true,
118
- with_vector: includeVector,
119
- })
120
- ).points;
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
- return results.map(match => {
123
- let vector: number[] = [];
124
- if (includeVector) {
125
- if (Array.isArray(match.vector)) {
126
- // If it's already an array of numbers
127
- vector = match.vector as number[];
128
- } else if (typeof match.vector === 'object' && match.vector !== null) {
129
- // If it's an object with vector data
130
- vector = Object.values(match.vector).filter(v => typeof v === 'number');
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
- return {
135
- id: match.id as string,
136
- score: match.score || 0,
137
- metadata: match.payload as Record<string, any>,
138
- ...(includeVector && { vector }),
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
- const response = await this.client.getCollections();
145
- return response.collections.map(collection => collection.name) || [];
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
- const { config, points_count } = await this.client.getCollection(indexName);
216
+ try {
217
+ const { config, points_count } = await this.client.getCollection(indexName);
156
218
 
157
- const distance = config.params.vectors?.distance as Schemas['Distance'];
158
- return {
159
- dimension: config.params.vectors?.size as number,
160
- count: points_count || 0,
161
- // @ts-expect-error
162
- metric: Object.keys(DISTANCE_MAPPING).find(key => DISTANCE_MAPPING[key] === distance),
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
- await this.client.deleteCollection(indexName);
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
- if (!update.vector && !update.metadata) {
182
- throw new Error('No updates provided');
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
- console.error(`Failed to update vector by id: ${id} for index name: ${indexName}:`, error);
223
- throw error;
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: any) {
244
- throw new Error(`Failed to delete vector by id: ${id} for index name: ${indexName}: ${error.message}`);
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