@mastra/qdrant 0.0.0-commonjs-20250227130920
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 +23 -0
- package/CHANGELOG.md +710 -0
- package/LICENSE +44 -0
- package/README.md +82 -0
- package/dist/_tsup-dts-rollup.d.cts +58 -0
- package/dist/_tsup-dts-rollup.d.ts +58 -0
- package/dist/index.cjs +336 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +334 -0
- package/eslint.config.js +6 -0
- package/package.json +40 -0
- package/src/index.ts +1 -0
- package/src/vector/filter.test.ts +857 -0
- package/src/vector/filter.ts +290 -0
- package/src/vector/index.test.ts +726 -0
- package/src/vector/index.ts +147 -0
- package/tsconfig.json +5 -0
- package/vitest.config.ts +11 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { Filter } from '@mastra/core/filter';
|
|
2
|
+
import { MastraVector } from '@mastra/core/vector';
|
|
3
|
+
import type { QueryResult, IndexStats } from '@mastra/core/vector';
|
|
4
|
+
import { QdrantClient } from '@qdrant/js-client-rest';
|
|
5
|
+
import type { Schemas } from '@qdrant/js-client-rest';
|
|
6
|
+
|
|
7
|
+
import { QdrantFilterTranslator } from './filter';
|
|
8
|
+
|
|
9
|
+
const BATCH_SIZE = 256;
|
|
10
|
+
const DISTANCE_MAPPING: Record<string, Schemas['Distance']> = {
|
|
11
|
+
cosine: 'Cosine',
|
|
12
|
+
euclidean: 'Euclid',
|
|
13
|
+
dotproduct: 'Dot',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export class QdrantVector extends MastraVector {
|
|
17
|
+
private client: QdrantClient;
|
|
18
|
+
|
|
19
|
+
constructor(url: string, apiKey?: string, https?: boolean) {
|
|
20
|
+
super();
|
|
21
|
+
|
|
22
|
+
const baseClient = new QdrantClient({
|
|
23
|
+
url,
|
|
24
|
+
apiKey,
|
|
25
|
+
https,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const telemetry = this.__getTelemetry();
|
|
29
|
+
this.client =
|
|
30
|
+
telemetry?.traceClass(baseClient, {
|
|
31
|
+
spanNamePrefix: 'qdrant-vector',
|
|
32
|
+
attributes: {
|
|
33
|
+
'vector.type': 'qdrant',
|
|
34
|
+
},
|
|
35
|
+
}) ?? baseClient;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async upsert(
|
|
39
|
+
indexName: string,
|
|
40
|
+
vectors: number[][],
|
|
41
|
+
metadata?: Record<string, any>[],
|
|
42
|
+
ids?: string[],
|
|
43
|
+
): Promise<string[]> {
|
|
44
|
+
const pointIds = ids || vectors.map(() => crypto.randomUUID());
|
|
45
|
+
|
|
46
|
+
const records = vectors.map((vector, i) => ({
|
|
47
|
+
id: pointIds[i],
|
|
48
|
+
vector: vector,
|
|
49
|
+
payload: metadata?.[i] || {},
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
for (let i = 0; i < records.length; i += BATCH_SIZE) {
|
|
53
|
+
const batch = records.slice(i, i + BATCH_SIZE);
|
|
54
|
+
await this.client.upsert(indexName, {
|
|
55
|
+
// @ts-expect-error
|
|
56
|
+
points: batch,
|
|
57
|
+
wait: true,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return pointIds;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async createIndex(
|
|
65
|
+
indexName: string,
|
|
66
|
+
dimension: number,
|
|
67
|
+
metric: 'cosine' | 'euclidean' | 'dotproduct' = 'cosine',
|
|
68
|
+
): Promise<void> {
|
|
69
|
+
if (!Number.isInteger(dimension) || dimension <= 0) {
|
|
70
|
+
throw new Error('Dimension must be a positive integer');
|
|
71
|
+
}
|
|
72
|
+
await this.client.createCollection(indexName, {
|
|
73
|
+
vectors: {
|
|
74
|
+
// @ts-expect-error
|
|
75
|
+
size: dimension,
|
|
76
|
+
// @ts-expect-error
|
|
77
|
+
distance: DISTANCE_MAPPING[metric],
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
transformFilter(filter?: Filter) {
|
|
83
|
+
const translator = new QdrantFilterTranslator();
|
|
84
|
+
return translator.translate(filter);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async query(
|
|
88
|
+
indexName: string,
|
|
89
|
+
queryVector: number[],
|
|
90
|
+
topK: number = 10,
|
|
91
|
+
filter?: Filter,
|
|
92
|
+
includeVector: boolean = false,
|
|
93
|
+
): Promise<QueryResult[]> {
|
|
94
|
+
const translatedFilter = this.transformFilter(filter);
|
|
95
|
+
|
|
96
|
+
const results = (
|
|
97
|
+
await this.client.query(indexName, {
|
|
98
|
+
query: queryVector,
|
|
99
|
+
limit: topK,
|
|
100
|
+
filter: translatedFilter,
|
|
101
|
+
with_payload: true,
|
|
102
|
+
with_vector: includeVector,
|
|
103
|
+
})
|
|
104
|
+
).points;
|
|
105
|
+
|
|
106
|
+
return results.map(match => {
|
|
107
|
+
let vector: number[] = [];
|
|
108
|
+
if (includeVector) {
|
|
109
|
+
if (Array.isArray(match.vector)) {
|
|
110
|
+
// If it's already an array of numbers
|
|
111
|
+
vector = match.vector as number[];
|
|
112
|
+
} else if (typeof match.vector === 'object' && match.vector !== null) {
|
|
113
|
+
// If it's an object with vector data
|
|
114
|
+
vector = Object.values(match.vector).filter(v => typeof v === 'number');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
id: match.id as string,
|
|
120
|
+
score: match.score || 0,
|
|
121
|
+
metadata: match.payload as Record<string, any>,
|
|
122
|
+
...(includeVector && { vector }),
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async listIndexes(): Promise<string[]> {
|
|
128
|
+
const response = await this.client.getCollections();
|
|
129
|
+
return response.collections.map(collection => collection.name) || [];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async describeIndex(indexName: string): Promise<IndexStats> {
|
|
133
|
+
const { config, points_count } = await this.client.getCollection(indexName);
|
|
134
|
+
|
|
135
|
+
const distance = config.params.vectors?.distance as Schemas['Distance'];
|
|
136
|
+
return {
|
|
137
|
+
dimension: config.params.vectors?.size as number,
|
|
138
|
+
count: points_count || 0,
|
|
139
|
+
// @ts-expect-error
|
|
140
|
+
metric: Object.keys(DISTANCE_MAPPING).find(key => DISTANCE_MAPPING[key] === distance),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async deleteIndex(indexName: string): Promise<void> {
|
|
145
|
+
await this.client.deleteCollection(indexName);
|
|
146
|
+
}
|
|
147
|
+
}
|
package/tsconfig.json
ADDED