@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/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
1
2
  import { MastraVector } from '@mastra/core/vector';
2
3
  import { QdrantClient } from '@qdrant/js-client-rest';
3
4
  import { BaseFilterTranslator } from '@mastra/core/vector/filter';
@@ -26,7 +27,7 @@ var QdrantFilterTranslator = class extends BaseFilterTranslator {
26
27
  return fieldKey ? { key: fieldKey, ...condition } : condition;
27
28
  }
28
29
  translateNode(node, isNested = false, fieldKey) {
29
- if (!this.isEmpty(node) && typeof node === "object" && "must" in node) {
30
+ if (!this.isEmpty(node) && !!node && typeof node === "object" && "must" in node) {
30
31
  return node;
31
32
  }
32
33
  if (this.isPrimitive(node)) {
@@ -265,22 +266,46 @@ var QdrantVector = class extends MastraVector {
265
266
  vector,
266
267
  payload: metadata?.[i] || {}
267
268
  }));
268
- for (let i = 0; i < records.length; i += BATCH_SIZE) {
269
- const batch = records.slice(i, i + BATCH_SIZE);
270
- await this.client.upsert(indexName, {
271
- // @ts-expect-error
272
- points: batch,
273
- wait: true
274
- });
269
+ try {
270
+ for (let i = 0; i < records.length; i += BATCH_SIZE) {
271
+ const batch = records.slice(i, i + BATCH_SIZE);
272
+ await this.client.upsert(indexName, {
273
+ // @ts-expect-error
274
+ points: batch,
275
+ wait: true
276
+ });
277
+ }
278
+ return pointIds;
279
+ } catch (error) {
280
+ throw new MastraError(
281
+ {
282
+ id: "STORAGE_QDRANT_VECTOR_UPSERT_FAILED",
283
+ domain: ErrorDomain.STORAGE,
284
+ category: ErrorCategory.THIRD_PARTY,
285
+ details: { indexName, vectorCount: vectors.length }
286
+ },
287
+ error
288
+ );
275
289
  }
276
- return pointIds;
277
290
  }
278
291
  async createIndex({ indexName, dimension, metric = "cosine" }) {
279
- if (!Number.isInteger(dimension) || dimension <= 0) {
280
- throw new Error("Dimension must be a positive integer");
281
- }
282
- if (!DISTANCE_MAPPING[metric]) {
283
- throw new Error(`Invalid metric: "${metric}". Must be one of: cosine, euclidean, dotproduct`);
292
+ try {
293
+ if (!Number.isInteger(dimension) || dimension <= 0) {
294
+ throw new Error("Dimension must be a positive integer");
295
+ }
296
+ if (!DISTANCE_MAPPING[metric]) {
297
+ throw new Error(`Invalid metric: "${metric}". Must be one of: cosine, euclidean, dotproduct`);
298
+ }
299
+ } catch (validationError) {
300
+ throw new MastraError(
301
+ {
302
+ id: "STORAGE_QDRANT_VECTOR_CREATE_INDEX_INVALID_ARGS",
303
+ domain: ErrorDomain.STORAGE,
304
+ category: ErrorCategory.USER,
305
+ details: { indexName, dimension, metric }
306
+ },
307
+ validationError
308
+ );
284
309
  }
285
310
  try {
286
311
  await this.client.createCollection(indexName, {
@@ -295,6 +320,15 @@ var QdrantVector = class extends MastraVector {
295
320
  await this.validateExistingIndex(indexName, dimension, metric);
296
321
  return;
297
322
  }
323
+ throw new MastraError(
324
+ {
325
+ id: "STORAGE_QDRANT_VECTOR_CREATE_INDEX_FAILED",
326
+ domain: ErrorDomain.STORAGE,
327
+ category: ErrorCategory.THIRD_PARTY,
328
+ details: { indexName, dimension, metric }
329
+ },
330
+ error
331
+ );
298
332
  }
299
333
  }
300
334
  transformFilter(filter) {
@@ -309,33 +343,56 @@ var QdrantVector = class extends MastraVector {
309
343
  includeVector = false
310
344
  }) {
311
345
  const translatedFilter = this.transformFilter(filter) ?? {};
312
- const results = (await this.client.query(indexName, {
313
- query: queryVector,
314
- limit: topK,
315
- filter: translatedFilter,
316
- with_payload: true,
317
- with_vector: includeVector
318
- })).points;
319
- return results.map((match) => {
320
- let vector = [];
321
- if (includeVector) {
322
- if (Array.isArray(match.vector)) {
323
- vector = match.vector;
324
- } else if (typeof match.vector === "object" && match.vector !== null) {
325
- vector = Object.values(match.vector).filter((v) => typeof v === "number");
346
+ try {
347
+ const results = (await this.client.query(indexName, {
348
+ query: queryVector,
349
+ limit: topK,
350
+ filter: translatedFilter,
351
+ with_payload: true,
352
+ with_vector: includeVector
353
+ })).points;
354
+ return results.map((match) => {
355
+ let vector = [];
356
+ if (includeVector) {
357
+ if (Array.isArray(match.vector)) {
358
+ vector = match.vector;
359
+ } else if (typeof match.vector === "object" && match.vector !== null) {
360
+ vector = Object.values(match.vector).filter((v) => typeof v === "number");
361
+ }
326
362
  }
327
- }
328
- return {
329
- id: match.id,
330
- score: match.score || 0,
331
- metadata: match.payload,
332
- ...includeVector && { vector }
333
- };
334
- });
363
+ return {
364
+ id: match.id,
365
+ score: match.score || 0,
366
+ metadata: match.payload,
367
+ ...includeVector && { vector }
368
+ };
369
+ });
370
+ } catch (error) {
371
+ throw new MastraError(
372
+ {
373
+ id: "STORAGE_QDRANT_VECTOR_QUERY_FAILED",
374
+ domain: ErrorDomain.STORAGE,
375
+ category: ErrorCategory.THIRD_PARTY,
376
+ details: { indexName, topK }
377
+ },
378
+ error
379
+ );
380
+ }
335
381
  }
336
382
  async listIndexes() {
337
- const response = await this.client.getCollections();
338
- return response.collections.map((collection) => collection.name) || [];
383
+ try {
384
+ const response = await this.client.getCollections();
385
+ return response.collections.map((collection) => collection.name) || [];
386
+ } catch (error) {
387
+ throw new MastraError(
388
+ {
389
+ id: "STORAGE_QDRANT_VECTOR_LIST_INDEXES_FAILED",
390
+ domain: ErrorDomain.STORAGE,
391
+ category: ErrorCategory.THIRD_PARTY
392
+ },
393
+ error
394
+ );
395
+ }
339
396
  }
340
397
  /**
341
398
  * Retrieves statistics about a vector index.
@@ -344,17 +401,41 @@ var QdrantVector = class extends MastraVector {
344
401
  * @returns A promise that resolves to the index statistics including dimension, count and metric
345
402
  */
346
403
  async describeIndex({ indexName }) {
347
- const { config, points_count } = await this.client.getCollection(indexName);
348
- const distance = config.params.vectors?.distance;
349
- return {
350
- dimension: config.params.vectors?.size,
351
- count: points_count || 0,
352
- // @ts-expect-error
353
- metric: Object.keys(DISTANCE_MAPPING).find((key) => DISTANCE_MAPPING[key] === distance)
354
- };
404
+ try {
405
+ const { config, points_count } = await this.client.getCollection(indexName);
406
+ const distance = config.params.vectors?.distance;
407
+ return {
408
+ dimension: config.params.vectors?.size,
409
+ count: points_count || 0,
410
+ // @ts-expect-error
411
+ metric: Object.keys(DISTANCE_MAPPING).find((key) => DISTANCE_MAPPING[key] === distance)
412
+ };
413
+ } catch (error) {
414
+ throw new MastraError(
415
+ {
416
+ id: "STORAGE_QDRANT_VECTOR_DESCRIBE_INDEX_FAILED",
417
+ domain: ErrorDomain.STORAGE,
418
+ category: ErrorCategory.THIRD_PARTY,
419
+ details: { indexName }
420
+ },
421
+ error
422
+ );
423
+ }
355
424
  }
356
425
  async deleteIndex({ indexName }) {
357
- await this.client.deleteCollection(indexName);
426
+ try {
427
+ await this.client.deleteCollection(indexName);
428
+ } catch (error) {
429
+ throw new MastraError(
430
+ {
431
+ id: "STORAGE_QDRANT_VECTOR_DELETE_INDEX_FAILED",
432
+ domain: ErrorDomain.STORAGE,
433
+ category: ErrorCategory.THIRD_PARTY,
434
+ details: { indexName }
435
+ },
436
+ error
437
+ );
438
+ }
358
439
  }
359
440
  /**
360
441
  * Updates a vector by its ID with the provided vector and/or metadata.
@@ -367,8 +448,20 @@ var QdrantVector = class extends MastraVector {
367
448
  * @throws Will throw an error if no updates are provided or if the update operation fails.
368
449
  */
369
450
  async updateVector({ indexName, id, update }) {
370
- if (!update.vector && !update.metadata) {
371
- throw new Error("No updates provided");
451
+ try {
452
+ if (!update.vector && !update.metadata) {
453
+ throw new Error("No updates provided");
454
+ }
455
+ } catch (validationError) {
456
+ throw new MastraError(
457
+ {
458
+ id: "STORAGE_QDRANT_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
459
+ domain: ErrorDomain.STORAGE,
460
+ category: ErrorCategory.USER,
461
+ details: { indexName, id }
462
+ },
463
+ validationError
464
+ );
372
465
  }
373
466
  const pointId = this.parsePointId(id);
374
467
  try {
@@ -399,8 +492,15 @@ var QdrantVector = class extends MastraVector {
399
492
  return;
400
493
  }
401
494
  } catch (error) {
402
- console.error(`Failed to update vector by id: ${id} for index name: ${indexName}:`, error);
403
- throw error;
495
+ throw new MastraError(
496
+ {
497
+ id: "STORAGE_QDRANT_VECTOR_UPDATE_VECTOR_FAILED",
498
+ domain: ErrorDomain.STORAGE,
499
+ category: ErrorCategory.THIRD_PARTY,
500
+ details: { indexName, id }
501
+ },
502
+ error
503
+ );
404
504
  }
405
505
  }
406
506
  /**
@@ -417,7 +517,15 @@ var QdrantVector = class extends MastraVector {
417
517
  points: [pointId]
418
518
  });
419
519
  } catch (error) {
420
- throw new Error(`Failed to delete vector by id: ${id} for index name: ${indexName}: ${error.message}`);
520
+ throw new MastraError(
521
+ {
522
+ id: "STORAGE_QDRANT_VECTOR_DELETE_VECTOR_FAILED",
523
+ domain: ErrorDomain.STORAGE,
524
+ category: ErrorCategory.THIRD_PARTY,
525
+ details: { indexName, id }
526
+ },
527
+ error
528
+ );
421
529
  }
422
530
  }
423
531
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/qdrant",
3
- "version": "0.10.3",
3
+ "version": "0.11.0-alpha.1",
4
4
  "description": "Qdrant vector store provider for Mastra",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,15 +25,15 @@
25
25
  "devDependencies": {
26
26
  "@microsoft/api-extractor": "^7.52.8",
27
27
  "@types/node": "^20.19.0",
28
- "eslint": "^9.28.0",
28
+ "eslint": "^9.29.0",
29
29
  "tsup": "^8.5.0",
30
30
  "typescript": "^5.8.3",
31
31
  "vitest": "^3.2.3",
32
32
  "@internal/lint": "0.0.13",
33
- "@mastra/core": "0.10.6"
33
+ "@mastra/core": "0.10.7-alpha.3"
34
34
  },
35
35
  "peerDependencies": {
36
- "@mastra/core": ">=0.10.4-0 <0.11.0"
36
+ "@mastra/core": ">=0.10.7-0 <0.11.0-0"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
-
2
+ import type { QdrantVectorFilter } from './filter';
3
3
  import { QdrantFilterTranslator } from './filter';
4
4
 
5
5
  describe('QdrantFilterTranslator', () => {
@@ -7,7 +7,7 @@ describe('QdrantFilterTranslator', () => {
7
7
 
8
8
  describe('Basic Operators', () => {
9
9
  it('should translate direct value match', () => {
10
- const filter = { field: 'value' };
10
+ const filter: QdrantVectorFilter = { field: 'value' };
11
11
  const expected = { must: [{ key: 'field', match: { value: 'value' } }] };
12
12
  expect(translator.translate(filter)).toEqual(expected);
13
13
  });
@@ -63,7 +63,7 @@ describe('QdrantFilterTranslator', () => {
63
63
  });
64
64
 
65
65
  it('should handle multiple comparison operators on same field', () => {
66
- const filter = { field: { $gt: 10, $lt: 20 } };
66
+ const filter: QdrantVectorFilter = { field: { $gt: 10, $lt: 20 } };
67
67
  const expected = { must: [{ key: 'field', range: { gt: 10, lt: 20 } }] };
68
68
  expect(translator.translate(filter)).toEqual(expected);
69
69
  });
@@ -71,7 +71,7 @@ describe('QdrantFilterTranslator', () => {
71
71
 
72
72
  describe('Logical Operators', () => {
73
73
  it('should translate $and operator', () => {
74
- const filter = {
74
+ const filter: QdrantVectorFilter = {
75
75
  $and: [{ field1: 'value1' }, { field2: { $gt: 100 } }],
76
76
  };
77
77
 
@@ -86,7 +86,7 @@ describe('QdrantFilterTranslator', () => {
86
86
  });
87
87
 
88
88
  it('should translate $or operator', () => {
89
- const filter = {
89
+ const filter: QdrantVectorFilter = {
90
90
  $or: [{ field1: 'value1' }, { field2: { $lt: 100 } }],
91
91
  };
92
92
 
@@ -101,7 +101,7 @@ describe('QdrantFilterTranslator', () => {
101
101
  });
102
102
 
103
103
  it('should translate $not operator', () => {
104
- const filter = {
104
+ const filter: QdrantVectorFilter = {
105
105
  $not: { field: 'value' },
106
106
  };
107
107
 
@@ -113,7 +113,7 @@ describe('QdrantFilterTranslator', () => {
113
113
  });
114
114
 
115
115
  it('should handle nested logical operators', () => {
116
- const filter = {
116
+ const filter: QdrantVectorFilter = {
117
117
  $and: [
118
118
  { 'user.age': { $gte: 18 } },
119
119
  {
@@ -155,7 +155,7 @@ describe('QdrantFilterTranslator', () => {
155
155
  });
156
156
 
157
157
  it('should handle nested must_not operators', () => {
158
- const filter = {
158
+ const filter: QdrantVectorFilter = {
159
159
  $not: {
160
160
  $not: { field: 'value' },
161
161
  },
@@ -171,7 +171,7 @@ describe('QdrantFilterTranslator', () => {
171
171
  });
172
172
 
173
173
  it('should handle complex logical combinations with ranges', () => {
174
- const filter = {
174
+ const filter: QdrantVectorFilter = {
175
175
  $or: [
176
176
  { $and: [{ price: { $gte: 100, $lt: 200 } }, { stock: { $gt: 0 } }] },
177
177
  { $and: [{ price: { $lt: 100 } }, { featured: true }] },
@@ -199,13 +199,13 @@ describe('QdrantFilterTranslator', () => {
199
199
 
200
200
  describe('Custom Operators', () => {
201
201
  it('should translate $count operator', () => {
202
- const filter = { field: { $count: { $gt: 5 } } };
202
+ const filter: QdrantVectorFilter = { field: { $count: { $gt: 5 } } };
203
203
  const expected = { must: [{ key: 'field', values_count: { gt: 5 } }] };
204
204
  expect(translator.translate(filter)).toEqual(expected);
205
205
  });
206
206
 
207
207
  it('should translate $geo operator with radius', () => {
208
- const filter = {
208
+ const filter: QdrantVectorFilter = {
209
209
  location: {
210
210
  $geo: {
211
211
  type: 'radius',
@@ -229,7 +229,7 @@ describe('QdrantFilterTranslator', () => {
229
229
  });
230
230
 
231
231
  it('should translate $geo operator with bounding box', () => {
232
- const filter = {
232
+ const filter: QdrantVectorFilter = {
233
233
  location: {
234
234
  $geo: {
235
235
  type: 'box',
@@ -255,7 +255,7 @@ describe('QdrantFilterTranslator', () => {
255
255
  });
256
256
 
257
257
  it('should translate $geo operator with polygon', () => {
258
- const filter = {
258
+ const filter: QdrantVectorFilter = {
259
259
  location: {
260
260
  $geo: {
261
261
  type: 'polygon',
@@ -286,7 +286,7 @@ describe('QdrantFilterTranslator', () => {
286
286
  });
287
287
 
288
288
  it('should translate $nested operator', () => {
289
- const filter = {
289
+ const filter: QdrantVectorFilter = {
290
290
  diet: {
291
291
  $nested: {
292
292
  food: 'meat',
@@ -322,7 +322,7 @@ describe('QdrantFilterTranslator', () => {
322
322
 
323
323
  it('should translate $datetime operator', () => {
324
324
  const now = new Date();
325
- const filter = {
325
+ const filter: QdrantVectorFilter = {
326
326
  timestamp: {
327
327
  $datetime: {
328
328
  range: {
@@ -357,7 +357,7 @@ describe('QdrantFilterTranslator', () => {
357
357
  });
358
358
 
359
359
  it('should handle nested $count with multiple conditions', () => {
360
- const filter = { 'array.items': { $count: { $gt: 5, $lt: 10 } } };
360
+ const filter: QdrantVectorFilter = { 'array.items': { $count: { $gt: 5, $lt: 10 } } };
361
361
  const expected = {
362
362
  must: [
363
363
  {
@@ -370,7 +370,7 @@ describe('QdrantFilterTranslator', () => {
370
370
  });
371
371
 
372
372
  it('should translate $nested operator with complex conditions', () => {
373
- const filter = {
373
+ const filter: QdrantVectorFilter = {
374
374
  nested_field: {
375
375
  $nested: {
376
376
  inner_field: { $gt: 100 },
@@ -399,7 +399,7 @@ describe('QdrantFilterTranslator', () => {
399
399
 
400
400
  it('should translate $datetime operator with multiple range conditions', () => {
401
401
  const now = new Date();
402
- const filter = {
402
+ const filter: QdrantVectorFilter = {
403
403
  timestamp: {
404
404
  $datetime: {
405
405
  range: {
@@ -430,7 +430,7 @@ describe('QdrantFilterTranslator', () => {
430
430
  });
431
431
 
432
432
  it('should translate $datetime operator with string dates', () => {
433
- const filter = {
433
+ const filter: QdrantVectorFilter = {
434
434
  timestamp: {
435
435
  $datetime: {
436
436
  key: 'timestamp',
@@ -458,7 +458,7 @@ describe('QdrantFilterTranslator', () => {
458
458
  });
459
459
 
460
460
  it('should translate complex $nested operator', () => {
461
- const filter = {
461
+ const filter: QdrantVectorFilter = {
462
462
  diet: {
463
463
  $nested: {
464
464
  food: { $in: ['meat', 'fish'] },
@@ -491,19 +491,19 @@ describe('QdrantFilterTranslator', () => {
491
491
 
492
492
  describe('Special Cases', () => {
493
493
  it('should handle nested paths', () => {
494
- const filter = { 'obj.field': 'value' };
494
+ const filter: QdrantVectorFilter = { 'obj.field': 'value' };
495
495
  const expected = { must: [{ key: 'obj.field', match: { value: 'value' } }] };
496
496
  expect(translator.translate(filter)).toEqual(expected);
497
497
  });
498
498
 
499
499
  it('should handle deep nested paths', () => {
500
- const filter = { 'a.b.c.d': { $gt: 100 } };
500
+ const filter: QdrantVectorFilter = { 'a.b.c.d': { $gt: 100 } };
501
501
  const expected = { must: [{ key: 'a.b.c.d', range: { gt: 100 } }] };
502
502
  expect(translator.translate(filter)).toEqual(expected);
503
503
  });
504
504
 
505
505
  it('should handle complex combinations', () => {
506
- const filter = {
506
+ const filter: QdrantVectorFilter = {
507
507
  $and: [
508
508
  { 'user.age': { $gte: 18 } },
509
509
  {
@@ -534,7 +534,7 @@ describe('QdrantFilterTranslator', () => {
534
534
  });
535
535
 
536
536
  it('should handle multiple nested paths with same prefix', () => {
537
- const filter = {
537
+ const filter: QdrantVectorFilter = {
538
538
  $and: [{ 'user.profile.age': { $gte: 18 } }, { 'user.profile.name': 'John' }],
539
539
  };
540
540
 
@@ -549,7 +549,7 @@ describe('QdrantFilterTranslator', () => {
549
549
  });
550
550
 
551
551
  it('should handle mixed array and object paths', () => {
552
- const filter = {
552
+ const filter: QdrantVectorFilter = {
553
553
  'items[].category': { $in: ['A', 'B'] },
554
554
  'items[].details.price': { $gt: 100 },
555
555
  };
@@ -565,7 +565,7 @@ describe('QdrantFilterTranslator', () => {
565
565
  });
566
566
 
567
567
  it('should handle empty logical operators in combination', () => {
568
- const filter = {
568
+ const filter: QdrantVectorFilter = {
569
569
  $and: [{ $or: [] }, { field: 'value' }],
570
570
  };
571
571
  const expected = {
@@ -575,7 +575,7 @@ describe('QdrantFilterTranslator', () => {
575
575
  });
576
576
 
577
577
  it('should handle deeply nested paths with array notation', () => {
578
- const filter = {
578
+ const filter: QdrantVectorFilter = {
579
579
  'users[].addresses[].geo.location': {
580
580
  $geo: {
581
581
  type: 'radius',
@@ -599,7 +599,7 @@ describe('QdrantFilterTranslator', () => {
599
599
  });
600
600
 
601
601
  it('should handle array paths with multiple levels', () => {
602
- const filter = {
602
+ const filter: QdrantVectorFilter = {
603
603
  'users[].addresses[].location[].coordinates': { $gt: 100 },
604
604
  };
605
605
  const expected = {
@@ -614,7 +614,7 @@ describe('QdrantFilterTranslator', () => {
614
614
  });
615
615
 
616
616
  it('should handle combination of array and object paths', () => {
617
- const filter = {
617
+ const filter: QdrantVectorFilter = {
618
618
  'users[].profile.addresses[].location.coordinates': { $gt: 100 },
619
619
  };
620
620
  const expected = {
@@ -629,7 +629,7 @@ describe('QdrantFilterTranslator', () => {
629
629
  });
630
630
 
631
631
  it('should handle multiple conditions with same array path', () => {
632
- const filter = {
632
+ const filter: QdrantVectorFilter = {
633
633
  $and: [
634
634
  { 'items[].price': { $gt: 100 } },
635
635
  { 'items[].quantity': { $gt: 0 } },
@@ -654,7 +654,7 @@ describe('QdrantFilterTranslator', () => {
654
654
  });
655
655
 
656
656
  it('should throw error for invalid geo filter type', () => {
657
- const filter = {
657
+ const filter: QdrantVectorFilter = {
658
658
  location: {
659
659
  $geo: {
660
660
  type: 'invalid',
@@ -667,7 +667,7 @@ describe('QdrantFilterTranslator', () => {
667
667
  });
668
668
 
669
669
  it('should throw error for invalid custom operator', () => {
670
- const filter = { $invalidCustom: 'value' };
670
+ const filter: QdrantVectorFilter = { $invalidCustom: 'value' };
671
671
  expect(() => translator.translate(filter)).toThrow();
672
672
  });
673
673
  });
@@ -686,7 +686,7 @@ describe('QdrantFilterTranslator', () => {
686
686
  });
687
687
 
688
688
  it('should validate array operator values', () => {
689
- const invalidFilters = [
689
+ const invalidFilters: any = [
690
690
  { field: { $in: 'not-an-array' } }, // Should be array
691
691
  { field: { $nin: 123 } }, // Should be array
692
692
  { field: { $in: {} } }, // Should be array
@@ -697,14 +697,14 @@ describe('QdrantFilterTranslator', () => {
697
697
  });
698
698
  });
699
699
  it('throws error for non-logical operators at top level', () => {
700
- const invalidFilters = [{ $gt: 100 }, { $in: ['value1', 'value2'] }, { $eq: true }];
700
+ const invalidFilters: any = [{ $gt: 100 }, { $in: ['value1', 'value2'] }, { $eq: true }];
701
701
 
702
702
  invalidFilters.forEach(filter => {
703
703
  expect(() => translator.translate(filter)).toThrow(/Invalid top-level operator/);
704
704
  });
705
705
  });
706
706
  it('allows logical operators at top level', () => {
707
- const validFilters = [{ $and: [{ field: 'value' }] }, { $or: [{ field: 'value' }] }];
707
+ const validFilters: QdrantVectorFilter[] = [{ $and: [{ field: 'value' }] }, { $or: [{ field: 'value' }] }];
708
708
 
709
709
  validFilters.forEach(filter => {
710
710
  expect(() => translator.translate(filter)).not.toThrow();
@@ -732,7 +732,7 @@ describe('QdrantFilterTranslator', () => {
732
732
  });
733
733
 
734
734
  it('should wrap multiple field conditions in single must', () => {
735
- const filter = {
735
+ const filter: QdrantVectorFilter = {
736
736
  field1: 'value1',
737
737
  field2: 'value2',
738
738
  };
@@ -746,7 +746,7 @@ describe('QdrantFilterTranslator', () => {
746
746
  });
747
747
 
748
748
  it('should wrap complex single field conditions', () => {
749
- const filter = {
749
+ const filter: QdrantVectorFilter = {
750
750
  field: {
751
751
  $gt: 10,
752
752
  $lt: 20,
@@ -771,7 +771,7 @@ describe('QdrantFilterTranslator', () => {
771
771
 
772
772
  describe('No Must Wrapper Cases', () => {
773
773
  it('should not wrap nested logical operators', () => {
774
- const filter = {
774
+ const filter: QdrantVectorFilter = {
775
775
  $and: [{ field1: 'value1' }, { field2: 'value2' }],
776
776
  };
777
777
  const expected = {
@@ -791,7 +791,7 @@ describe('QdrantFilterTranslator', () => {
791
791
  });
792
792
 
793
793
  it('should preserve existing logical structure', () => {
794
- const filter = {
794
+ const filter: QdrantVectorFilter = {
795
795
  $or: [{ field1: 'value1' }, { $and: [{ field2: 'value2' }, { field3: 'value3' }] }],
796
796
  };
797
797
  const expected = {
@@ -826,7 +826,7 @@ describe('QdrantFilterTranslator', () => {
826
826
  });
827
827
 
828
828
  it('should handle regex in nested conditions', () => {
829
- const filter = {
829
+ const filter: QdrantVectorFilter = {
830
830
  diet: {
831
831
  $nested: {
832
832
  food: { $regex: 'meat' },