@mastra/opensearch 0.0.0-vector-sources-20250516175436 → 0.0.0-vector-query-tool-provider-options-20250828222356
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/CHANGELOG.md +377 -2
- package/LICENSE.md +11 -42
- package/dist/index.cjs +150 -124
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +142 -116
- package/dist/index.js.map +1 -0
- package/dist/vector/filter.d.ts +47 -0
- package/dist/vector/filter.d.ts.map +1 -0
- package/dist/vector/index.d.ts +101 -0
- package/dist/vector/index.d.ts.map +1 -0
- package/dist/vector/prompt.d.ts +6 -0
- package/dist/vector/prompt.d.ts.map +1 -0
- package/package.json +17 -13
- package/src/vector/filter.test.ts +55 -54
- package/src/vector/filter.ts +23 -5
- package/src/vector/index.test.ts +4 -5
- package/src/vector/index.ts +150 -146
- package/src/vector/prompt.ts +82 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +1 -1
- package/tsup.config.ts +17 -0
- package/dist/_tsup-dts-rollup.d.cts +0 -186
- package/dist/_tsup-dts-rollup.d.ts +0 -186
- package/dist/index.d.cts +0 -1
package/src/vector/filter.ts
CHANGED
|
@@ -1,30 +1,48 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
BlacklistedRootOperators,
|
|
3
|
+
LogicalOperatorValueMap,
|
|
4
|
+
OperatorSupport,
|
|
5
|
+
OperatorValueMap,
|
|
6
|
+
QueryOperator,
|
|
7
|
+
VectorFilter,
|
|
8
|
+
} from '@mastra/core/vector/filter';
|
|
2
9
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
3
10
|
|
|
11
|
+
type OpenSearchOperatorValueMap = Omit<OperatorValueMap, '$options' | '$nor' | '$elemMatch'>;
|
|
12
|
+
|
|
13
|
+
type OpenSearchLogicalOperatorValueMap = Omit<LogicalOperatorValueMap, '$nor'>;
|
|
14
|
+
|
|
15
|
+
type OpenSearchBlacklisted = BlacklistedRootOperators | '$nor';
|
|
16
|
+
|
|
17
|
+
export type OpenSearchVectorFilter = VectorFilter<
|
|
18
|
+
keyof OpenSearchOperatorValueMap,
|
|
19
|
+
OpenSearchOperatorValueMap,
|
|
20
|
+
OpenSearchLogicalOperatorValueMap,
|
|
21
|
+
OpenSearchBlacklisted
|
|
22
|
+
>;
|
|
4
23
|
/**
|
|
5
24
|
* Translator for OpenSearch filter queries.
|
|
6
25
|
* Maintains OpenSearch-compatible syntax while ensuring proper validation
|
|
7
26
|
* and normalization of values.
|
|
8
27
|
*/
|
|
9
|
-
export class OpenSearchFilterTranslator extends BaseFilterTranslator {
|
|
28
|
+
export class OpenSearchFilterTranslator extends BaseFilterTranslator<OpenSearchVectorFilter> {
|
|
10
29
|
protected override getSupportedOperators(): OperatorSupport {
|
|
11
30
|
return {
|
|
12
31
|
...BaseFilterTranslator.DEFAULT_OPERATORS,
|
|
13
32
|
logical: ['$and', '$or', '$not'],
|
|
14
33
|
array: ['$in', '$nin', '$all'],
|
|
15
|
-
element: ['$exists'],
|
|
16
34
|
regex: ['$regex'],
|
|
17
35
|
custom: [],
|
|
18
36
|
};
|
|
19
37
|
}
|
|
20
38
|
|
|
21
|
-
translate(filter?:
|
|
39
|
+
translate(filter?: OpenSearchVectorFilter): OpenSearchVectorFilter {
|
|
22
40
|
if (this.isEmpty(filter)) return undefined;
|
|
23
41
|
this.validateFilter(filter);
|
|
24
42
|
return this.translateNode(filter);
|
|
25
43
|
}
|
|
26
44
|
|
|
27
|
-
private translateNode(node:
|
|
45
|
+
private translateNode(node: OpenSearchVectorFilter): any {
|
|
28
46
|
// Handle primitive values and arrays
|
|
29
47
|
if (this.isPrimitive(node) || Array.isArray(node)) {
|
|
30
48
|
return node;
|
package/src/vector/index.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// To setup a Opensearch server, run the docker compose file in the opensearch directory
|
|
2
|
-
import type { QueryResult
|
|
2
|
+
import type { QueryResult } from '@mastra/core/vector';
|
|
3
3
|
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
4
|
|
|
5
5
|
import { OpenSearchVector } from './index';
|
|
@@ -215,12 +215,11 @@ describe('OpenSearchVector', () => {
|
|
|
215
215
|
|
|
216
216
|
await vectorDB.upsert({ indexName: testIndexName, vectors: testVectors, metadata: testMetadata });
|
|
217
217
|
|
|
218
|
-
const
|
|
218
|
+
const results = await vectorDB.query({
|
|
219
219
|
indexName: testIndexName,
|
|
220
220
|
queryVector: [1.0, 0.1, 0.1],
|
|
221
221
|
topK: 3,
|
|
222
|
-
};
|
|
223
|
-
const results = await vectorDB.query(queryParams);
|
|
222
|
+
});
|
|
224
223
|
|
|
225
224
|
expect(results).toHaveLength(3);
|
|
226
225
|
expect(results[0]?.score).toBeGreaterThan(0);
|
|
@@ -1197,7 +1196,7 @@ describe('OpenSearchVector', () => {
|
|
|
1197
1196
|
vectorDB.query({
|
|
1198
1197
|
indexName,
|
|
1199
1198
|
queryVector: [1, 0, 0],
|
|
1200
|
-
filter: { price: { $invalid: 100 } },
|
|
1199
|
+
filter: { price: { $invalid: 100 } as any },
|
|
1201
1200
|
}),
|
|
1202
1201
|
).rejects.toThrow('Unsupported operator: $invalid');
|
|
1203
1202
|
});
|
package/src/vector/index.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
+
import { MastraError, ErrorDomain, ErrorCategory } from '@mastra/core/error';
|
|
1
2
|
import type {
|
|
2
3
|
CreateIndexParams,
|
|
3
4
|
DeleteIndexParams,
|
|
4
5
|
DeleteVectorParams,
|
|
5
6
|
DescribeIndexParams,
|
|
6
7
|
IndexStats,
|
|
7
|
-
ParamsToArgs,
|
|
8
8
|
QueryResult,
|
|
9
9
|
QueryVectorParams,
|
|
10
10
|
UpdateVectorParams,
|
|
11
11
|
UpsertVectorParams,
|
|
12
|
-
} from '@mastra/core';
|
|
12
|
+
} from '@mastra/core/vector';
|
|
13
13
|
import { MastraVector } from '@mastra/core/vector';
|
|
14
|
-
import type { VectorFilter } from '@mastra/core/vector/filter';
|
|
15
14
|
import { Client as OpenSearchClient } from '@opensearch-project/opensearch';
|
|
16
15
|
import { OpenSearchFilterTranslator } from './filter';
|
|
16
|
+
import type { OpenSearchVectorFilter } from './filter';
|
|
17
17
|
|
|
18
18
|
const METRIC_MAPPING = {
|
|
19
19
|
cosine: 'cosinesimil',
|
|
@@ -27,36 +27,18 @@ const REVERSE_METRIC_MAPPING = {
|
|
|
27
27
|
innerproduct: 'dotproduct',
|
|
28
28
|
} as const;
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
type OpenSearchVectorParams = QueryVectorParams<OpenSearchVectorFilter>;
|
|
31
|
+
|
|
32
|
+
export class OpenSearchVector extends MastraVector<OpenSearchVectorFilter> {
|
|
31
33
|
private client: OpenSearchClient;
|
|
32
34
|
|
|
33
|
-
/**
|
|
34
|
-
* @deprecated Passing a string URL is deprecated. Use an object parameter: { url }.
|
|
35
|
-
* @param url - The OpenSearch node URL (deprecated)
|
|
36
|
-
*/
|
|
37
|
-
constructor(url: string);
|
|
38
35
|
/**
|
|
39
36
|
* Creates a new OpenSearchVector client.
|
|
40
37
|
*
|
|
41
|
-
* @param
|
|
38
|
+
* @param {string} url - The url of the OpenSearch node.
|
|
42
39
|
*/
|
|
43
|
-
constructor(
|
|
44
|
-
constructor(paramsOrUrl: { url: string } | string) {
|
|
40
|
+
constructor({ url }: { url: string }) {
|
|
45
41
|
super();
|
|
46
|
-
let url: string;
|
|
47
|
-
if (typeof paramsOrUrl === 'string') {
|
|
48
|
-
// Deprecation warning for positional argument
|
|
49
|
-
if (typeof console !== 'undefined' && console.warn) {
|
|
50
|
-
console.warn(
|
|
51
|
-
'Deprecation Warning: OpenSearchVector constructor positional arguments are deprecated. Please use a single object parameter instead. This signature will be removed on May 20th, 2025.',
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
url = paramsOrUrl;
|
|
55
|
-
} else if (typeof paramsOrUrl === 'object' && paramsOrUrl !== null && 'url' in paramsOrUrl) {
|
|
56
|
-
url = paramsOrUrl.url;
|
|
57
|
-
} else {
|
|
58
|
-
throw new Error('Invalid parameters for OpenSearchVector constructor. Expected { url: string }.');
|
|
59
|
-
}
|
|
60
42
|
this.client = new OpenSearchClient({ node: url });
|
|
61
43
|
}
|
|
62
44
|
|
|
@@ -68,11 +50,15 @@ export class OpenSearchVector extends MastraVector {
|
|
|
68
50
|
* @param {'cosine' | 'euclidean' | 'dotproduct'} [metric=cosine] - The metric to use to sort vectors in the collection.
|
|
69
51
|
* @returns {Promise<void>} A promise that resolves when the collection is created.
|
|
70
52
|
*/
|
|
71
|
-
async createIndex(
|
|
72
|
-
const { indexName, dimension, metric = 'cosine' } = params;
|
|
73
|
-
|
|
53
|
+
async createIndex({ indexName, dimension, metric = 'cosine' }: CreateIndexParams): Promise<void> {
|
|
74
54
|
if (!Number.isInteger(dimension) || dimension <= 0) {
|
|
75
|
-
throw new
|
|
55
|
+
throw new MastraError({
|
|
56
|
+
id: 'STORAGE_OPENSEARCH_VECTOR_CREATE_INDEX_INVALID_ARGS',
|
|
57
|
+
domain: ErrorDomain.STORAGE,
|
|
58
|
+
category: ErrorCategory.USER,
|
|
59
|
+
text: 'Dimension must be a positive integer',
|
|
60
|
+
details: { indexName, dimension },
|
|
61
|
+
});
|
|
76
62
|
}
|
|
77
63
|
|
|
78
64
|
try {
|
|
@@ -105,8 +91,15 @@ export class OpenSearchVector extends MastraVector {
|
|
|
105
91
|
await this.validateExistingIndex(indexName, dimension, metric);
|
|
106
92
|
return;
|
|
107
93
|
}
|
|
108
|
-
|
|
109
|
-
|
|
94
|
+
throw new MastraError(
|
|
95
|
+
{
|
|
96
|
+
id: 'STORAGE_OPENSEARCH_VECTOR_CREATE_INDEX_FAILED',
|
|
97
|
+
domain: ErrorDomain.STORAGE,
|
|
98
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
99
|
+
details: { indexName, dimension, metric },
|
|
100
|
+
},
|
|
101
|
+
error,
|
|
102
|
+
);
|
|
110
103
|
}
|
|
111
104
|
}
|
|
112
105
|
|
|
@@ -123,23 +116,25 @@ export class OpenSearchVector extends MastraVector {
|
|
|
123
116
|
.filter((index: string | undefined) => index !== undefined);
|
|
124
117
|
|
|
125
118
|
return indexes;
|
|
126
|
-
} catch (error
|
|
127
|
-
|
|
128
|
-
|
|
119
|
+
} catch (error) {
|
|
120
|
+
throw new MastraError(
|
|
121
|
+
{
|
|
122
|
+
id: 'STORAGE_OPENSEARCH_VECTOR_LIST_INDEXES_FAILED',
|
|
123
|
+
domain: ErrorDomain.STORAGE,
|
|
124
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
125
|
+
},
|
|
126
|
+
error,
|
|
127
|
+
);
|
|
129
128
|
}
|
|
130
129
|
}
|
|
131
130
|
|
|
132
131
|
/**
|
|
133
132
|
* Retrieves statistics about a vector index.
|
|
134
133
|
*
|
|
135
|
-
* @param
|
|
136
|
-
* @param params.indexName - The name of the index to describe
|
|
134
|
+
* @param {string} indexName - The name of the index to describe
|
|
137
135
|
* @returns A promise that resolves to the index statistics including dimension, count and metric
|
|
138
136
|
*/
|
|
139
|
-
async describeIndex(
|
|
140
|
-
const params = this.normalizeArgs<DescribeIndexParams>('describeIndex', args);
|
|
141
|
-
|
|
142
|
-
const { indexName } = params;
|
|
137
|
+
async describeIndex({ indexName }: DescribeIndexParams): Promise<IndexStats> {
|
|
143
138
|
const { body: indexInfo } = await this.client.indices.get({ index: indexName });
|
|
144
139
|
const mappings = indexInfo[indexName]?.mappings;
|
|
145
140
|
const embedding: any = mappings?.properties?.embedding;
|
|
@@ -160,14 +155,21 @@ export class OpenSearchVector extends MastraVector {
|
|
|
160
155
|
* @param {string} indexName - The name of the index to delete.
|
|
161
156
|
* @returns {Promise<void>} A promise that resolves when the index is deleted.
|
|
162
157
|
*/
|
|
163
|
-
async deleteIndex(
|
|
164
|
-
const params = this.normalizeArgs<DeleteIndexParams>('deleteIndex', args);
|
|
165
|
-
|
|
166
|
-
const { indexName } = params;
|
|
158
|
+
async deleteIndex({ indexName }: DeleteIndexParams): Promise<void> {
|
|
167
159
|
try {
|
|
168
160
|
await this.client.indices.delete({ index: indexName });
|
|
169
161
|
} catch (error) {
|
|
170
|
-
|
|
162
|
+
const mastraError = new MastraError(
|
|
163
|
+
{
|
|
164
|
+
id: 'STORAGE_OPENSEARCH_VECTOR_DELETE_INDEX_FAILED',
|
|
165
|
+
domain: ErrorDomain.STORAGE,
|
|
166
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
167
|
+
details: { indexName },
|
|
168
|
+
},
|
|
169
|
+
error,
|
|
170
|
+
);
|
|
171
|
+
this.logger?.error(mastraError.toString());
|
|
172
|
+
this.logger?.trackException(mastraError);
|
|
171
173
|
}
|
|
172
174
|
}
|
|
173
175
|
|
|
@@ -180,45 +182,50 @@ export class OpenSearchVector extends MastraVector {
|
|
|
180
182
|
* @param {string[]} [ids] - An optional array of IDs corresponding to each vector. If not provided, new IDs will be generated.
|
|
181
183
|
* @returns {Promise<string[]>} A promise that resolves to an array of IDs of the upserted vectors.
|
|
182
184
|
*/
|
|
183
|
-
async upsert(
|
|
184
|
-
const { indexName, vectors, metadata = [], ids } = params;
|
|
185
|
-
|
|
185
|
+
async upsert({ indexName, vectors, metadata = [], ids }: UpsertVectorParams): Promise<string[]> {
|
|
186
186
|
const vectorIds = ids || vectors.map(() => crypto.randomUUID());
|
|
187
187
|
const operations = [];
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
try {
|
|
190
|
+
// Get index stats to check dimension
|
|
191
|
+
const indexInfo = await this.describeIndex({ indexName });
|
|
191
192
|
|
|
192
|
-
|
|
193
|
-
|
|
193
|
+
// Validate vector dimensions
|
|
194
|
+
this.validateVectorDimensions(vectors, indexInfo.dimension);
|
|
194
195
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
196
|
+
for (let i = 0; i < vectors.length; i++) {
|
|
197
|
+
const operation = {
|
|
198
|
+
index: {
|
|
199
|
+
_index: indexName,
|
|
200
|
+
_id: vectorIds[i],
|
|
201
|
+
},
|
|
202
|
+
};
|
|
202
203
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
204
|
+
const document = {
|
|
205
|
+
id: vectorIds[i],
|
|
206
|
+
embedding: vectors[i],
|
|
207
|
+
metadata: metadata[i] || {},
|
|
208
|
+
};
|
|
208
209
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
operations.push(operation);
|
|
211
|
+
operations.push(document);
|
|
212
|
+
}
|
|
212
213
|
|
|
213
|
-
try {
|
|
214
214
|
if (operations.length > 0) {
|
|
215
215
|
await this.client.bulk({ body: operations, refresh: true });
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
return vectorIds;
|
|
219
219
|
} catch (error) {
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
throw new MastraError(
|
|
221
|
+
{
|
|
222
|
+
id: 'STORAGE_OPENSEARCH_VECTOR_UPSERT_FAILED',
|
|
223
|
+
domain: ErrorDomain.STORAGE,
|
|
224
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
225
|
+
details: { indexName, vectorCount: vectors?.length || 0 },
|
|
226
|
+
},
|
|
227
|
+
error,
|
|
228
|
+
);
|
|
222
229
|
}
|
|
223
230
|
}
|
|
224
231
|
|
|
@@ -232,9 +239,13 @@ export class OpenSearchVector extends MastraVector {
|
|
|
232
239
|
* @param {boolean} [includeVectors=false] - Whether to include the vectors in the response.
|
|
233
240
|
* @returns {Promise<QueryResult[]>} A promise that resolves to an array of query results.
|
|
234
241
|
*/
|
|
235
|
-
async query(
|
|
236
|
-
|
|
237
|
-
|
|
242
|
+
async query({
|
|
243
|
+
indexName,
|
|
244
|
+
queryVector,
|
|
245
|
+
filter,
|
|
246
|
+
topK = 10,
|
|
247
|
+
includeVector = false,
|
|
248
|
+
}: OpenSearchVectorParams): Promise<QueryResult[]> {
|
|
238
249
|
try {
|
|
239
250
|
const translatedFilter = this.transformFilter(filter);
|
|
240
251
|
|
|
@@ -262,9 +273,16 @@ export class OpenSearchVector extends MastraVector {
|
|
|
262
273
|
});
|
|
263
274
|
|
|
264
275
|
return results;
|
|
265
|
-
} catch (error
|
|
266
|
-
|
|
267
|
-
|
|
276
|
+
} catch (error) {
|
|
277
|
+
throw new MastraError(
|
|
278
|
+
{
|
|
279
|
+
id: 'STORAGE_OPENSEARCH_VECTOR_QUERY_FAILED',
|
|
280
|
+
domain: ErrorDomain.STORAGE,
|
|
281
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
282
|
+
details: { indexName, topK },
|
|
283
|
+
},
|
|
284
|
+
error,
|
|
285
|
+
);
|
|
268
286
|
}
|
|
269
287
|
}
|
|
270
288
|
|
|
@@ -284,39 +302,14 @@ export class OpenSearchVector extends MastraVector {
|
|
|
284
302
|
/**
|
|
285
303
|
* Transforms the filter to the OpenSearch DSL.
|
|
286
304
|
*
|
|
287
|
-
* @param {
|
|
305
|
+
* @param {OpenSearchVectorFilter} filter - The filter to transform.
|
|
288
306
|
* @returns {Record<string, any>} The transformed filter.
|
|
289
307
|
*/
|
|
290
|
-
private transformFilter(filter?:
|
|
308
|
+
private transformFilter(filter?: OpenSearchVectorFilter): any {
|
|
291
309
|
const translator = new OpenSearchFilterTranslator();
|
|
292
310
|
return translator.translate(filter);
|
|
293
311
|
}
|
|
294
312
|
|
|
295
|
-
/**
|
|
296
|
-
* @deprecated Use {@link updateVector} instead. This method will be removed on May 20th, 2025.
|
|
297
|
-
*
|
|
298
|
-
* Updates a vector by its ID with the provided vector and/or metadata.
|
|
299
|
-
* @param indexName - The name of the index containing the vector.
|
|
300
|
-
* @param id - The ID of the vector to update.
|
|
301
|
-
* @param update - An object containing the vector and/or metadata to update.
|
|
302
|
-
* @param update.vector - An optional array of numbers representing the new vector.
|
|
303
|
-
* @param update.metadata - An optional record containing the new metadata.
|
|
304
|
-
* @returns A promise that resolves when the update is complete.
|
|
305
|
-
* @throws Will throw an error if no updates are provided or if the update operation fails.
|
|
306
|
-
*/
|
|
307
|
-
async updateIndexById(
|
|
308
|
-
indexName: string,
|
|
309
|
-
id: string,
|
|
310
|
-
update: { vector?: number[]; metadata?: Record<string, any> },
|
|
311
|
-
): Promise<void> {
|
|
312
|
-
this.logger.warn(
|
|
313
|
-
`Deprecation Warning: updateIndexById() is deprecated.
|
|
314
|
-
Please use updateVector() instead.
|
|
315
|
-
updateIndexById() will be removed on May 20th, 2025.`,
|
|
316
|
-
);
|
|
317
|
-
await this.updateVector({ indexName, id, update });
|
|
318
|
-
}
|
|
319
|
-
|
|
320
313
|
/**
|
|
321
314
|
* Updates a vector by its ID with the provided vector and/or metadata.
|
|
322
315
|
* @param indexName - The name of the index containing the vector.
|
|
@@ -327,16 +320,16 @@ export class OpenSearchVector extends MastraVector {
|
|
|
327
320
|
* @returns A promise that resolves when the update is complete.
|
|
328
321
|
* @throws Will throw an error if no updates are provided or if the update operation fails.
|
|
329
322
|
*/
|
|
330
|
-
async updateVector(
|
|
331
|
-
|
|
332
|
-
const { indexName, id, update } = params;
|
|
333
|
-
if (!update.vector && !update.metadata) {
|
|
334
|
-
throw new Error('No updates provided');
|
|
335
|
-
}
|
|
336
|
-
|
|
323
|
+
async updateVector({ indexName, id, update }: UpdateVectorParams): Promise<void> {
|
|
324
|
+
let existingDoc;
|
|
337
325
|
try {
|
|
326
|
+
if (!update.vector && !update.metadata) {
|
|
327
|
+
throw new Error('No updates provided');
|
|
328
|
+
}
|
|
329
|
+
|
|
338
330
|
// First get the current document to merge with updates
|
|
339
|
-
const { body
|
|
331
|
+
const { body } = await this.client
|
|
332
|
+
|
|
340
333
|
.get({
|
|
341
334
|
index: indexName,
|
|
342
335
|
id: id,
|
|
@@ -345,25 +338,40 @@ export class OpenSearchVector extends MastraVector {
|
|
|
345
338
|
throw new Error(`Document with ID ${id} not found in index ${indexName}`);
|
|
346
339
|
});
|
|
347
340
|
|
|
348
|
-
if (!
|
|
341
|
+
if (!body || !body._source) {
|
|
349
342
|
throw new Error(`Document with ID ${id} has no source data in index ${indexName}`);
|
|
350
343
|
}
|
|
344
|
+
existingDoc = body;
|
|
345
|
+
} catch (error) {
|
|
346
|
+
throw new MastraError(
|
|
347
|
+
{
|
|
348
|
+
id: 'STORAGE_OPENSEARCH_VECTOR_UPDATE_VECTOR_FAILED',
|
|
349
|
+
domain: ErrorDomain.STORAGE,
|
|
350
|
+
category: ErrorCategory.USER,
|
|
351
|
+
details: { indexName, id },
|
|
352
|
+
},
|
|
353
|
+
error,
|
|
354
|
+
);
|
|
355
|
+
}
|
|
351
356
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
357
|
+
const source = existingDoc._source;
|
|
358
|
+
const updatedDoc: Record<string, any> = {
|
|
359
|
+
id: source?.id || id,
|
|
360
|
+
};
|
|
356
361
|
|
|
362
|
+
try {
|
|
357
363
|
// Update vector if provided
|
|
358
364
|
if (update.vector) {
|
|
359
365
|
// Get index stats to check dimension
|
|
366
|
+
console.log(`1`);
|
|
360
367
|
const indexInfo = await this.describeIndex({ indexName });
|
|
361
368
|
|
|
362
369
|
// Validate vector dimensions
|
|
370
|
+
console.log(`2`);
|
|
363
371
|
this.validateVectorDimensions([update.vector], indexInfo.dimension);
|
|
364
372
|
|
|
365
373
|
updatedDoc.embedding = update.vector;
|
|
366
|
-
} else if (source
|
|
374
|
+
} else if (source?.embedding) {
|
|
367
375
|
updatedDoc.embedding = source.embedding;
|
|
368
376
|
}
|
|
369
377
|
|
|
@@ -371,10 +379,11 @@ export class OpenSearchVector extends MastraVector {
|
|
|
371
379
|
if (update.metadata) {
|
|
372
380
|
updatedDoc.metadata = update.metadata;
|
|
373
381
|
} else {
|
|
374
|
-
updatedDoc.metadata = source
|
|
382
|
+
updatedDoc.metadata = source?.metadata || {};
|
|
375
383
|
}
|
|
376
384
|
|
|
377
385
|
// Update the document
|
|
386
|
+
console.log(`3`);
|
|
378
387
|
await this.client.index({
|
|
379
388
|
index: indexName,
|
|
380
389
|
id: id,
|
|
@@ -382,29 +391,18 @@ export class OpenSearchVector extends MastraVector {
|
|
|
382
391
|
refresh: true,
|
|
383
392
|
});
|
|
384
393
|
} catch (error) {
|
|
385
|
-
|
|
386
|
-
|
|
394
|
+
throw new MastraError(
|
|
395
|
+
{
|
|
396
|
+
id: 'STORAGE_OPENSEARCH_VECTOR_UPDATE_VECTOR_FAILED',
|
|
397
|
+
domain: ErrorDomain.STORAGE,
|
|
398
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
399
|
+
details: { indexName, id },
|
|
400
|
+
},
|
|
401
|
+
error,
|
|
402
|
+
);
|
|
387
403
|
}
|
|
388
404
|
}
|
|
389
405
|
|
|
390
|
-
/**
|
|
391
|
-
* @deprecated Use {@link deleteVector} instead. This method will be removed on May 20th, 2025.
|
|
392
|
-
*
|
|
393
|
-
* Deletes a vector by its ID.
|
|
394
|
-
* @param indexName - The name of the index containing the vector.
|
|
395
|
-
* @param id - The ID of the vector to delete.
|
|
396
|
-
* @returns A promise that resolves when the deletion is complete.
|
|
397
|
-
* @throws Will throw an error if the deletion operation fails.
|
|
398
|
-
*/
|
|
399
|
-
async deleteIndexById(indexName: string, id: string): Promise<void> {
|
|
400
|
-
this.logger.warn(
|
|
401
|
-
`Deprecation Warning: deleteIndexById() is deprecated.
|
|
402
|
-
Please use deleteVector() instead.
|
|
403
|
-
deleteIndexById() will be removed on May 20th, 2025.`,
|
|
404
|
-
);
|
|
405
|
-
await this.deleteVector({ indexName, id });
|
|
406
|
-
}
|
|
407
|
-
|
|
408
406
|
/**
|
|
409
407
|
* Deletes a vector by its ID.
|
|
410
408
|
* @param indexName - The name of the index containing the vector.
|
|
@@ -412,9 +410,7 @@ export class OpenSearchVector extends MastraVector {
|
|
|
412
410
|
* @returns A promise that resolves when the deletion is complete.
|
|
413
411
|
* @throws Will throw an error if the deletion operation fails.
|
|
414
412
|
*/
|
|
415
|
-
async deleteVector(
|
|
416
|
-
const params = this.normalizeArgs<DeleteVectorParams>('deleteVector', args);
|
|
417
|
-
const { indexName, id } = params;
|
|
413
|
+
async deleteVector({ indexName, id }: DeleteVectorParams): Promise<void> {
|
|
418
414
|
try {
|
|
419
415
|
await this.client.delete({
|
|
420
416
|
index: indexName,
|
|
@@ -422,11 +418,19 @@ export class OpenSearchVector extends MastraVector {
|
|
|
422
418
|
refresh: true,
|
|
423
419
|
});
|
|
424
420
|
} catch (error: unknown) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
throw error;
|
|
421
|
+
// Don't throw error if document doesn't exist (404)
|
|
422
|
+
if (error && typeof error === 'object' && 'statusCode' in error && error.statusCode === 404) {
|
|
423
|
+
return;
|
|
429
424
|
}
|
|
425
|
+
throw new MastraError(
|
|
426
|
+
{
|
|
427
|
+
id: 'STORAGE_OPENSEARCH_VECTOR_DELETE_VECTOR_FAILED',
|
|
428
|
+
domain: ErrorDomain.STORAGE,
|
|
429
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
430
|
+
details: { indexName, id },
|
|
431
|
+
},
|
|
432
|
+
error,
|
|
433
|
+
);
|
|
430
434
|
}
|
|
431
435
|
}
|
|
432
436
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector store prompt for OpenSearch. This prompt details supported filter operators, syntax, and usage examples.
|
|
3
|
+
* Use this as a guide for constructing valid filters for OpenSearch vector queries in Mastra.
|
|
4
|
+
*/
|
|
5
|
+
export const OPENSEARCH_PROMPT = `When querying OpenSearch, you can ONLY use the operators listed below. Any other operators will be rejected.
|
|
6
|
+
Important: Do not explain how to construct the filter—use the specified operators and fields to search the content and return relevant results.
|
|
7
|
+
If a user tries to use an unsupported operator, reject the filter entirely and let them know that the operator is not supported.
|
|
8
|
+
|
|
9
|
+
Basic Comparison Operators:
|
|
10
|
+
- $eq: Exact match (default for field: value)
|
|
11
|
+
Example: { "category": "electronics" }
|
|
12
|
+
- $ne: Not equal
|
|
13
|
+
Example: { "category": { "$ne": "electronics" } }
|
|
14
|
+
- $gt: Greater than
|
|
15
|
+
Example: { "price": { "$gt": 100 } }
|
|
16
|
+
- $gte: Greater than or equal
|
|
17
|
+
Example: { "price": { "$gte": 100 } }
|
|
18
|
+
- $lt: Less than
|
|
19
|
+
Example: { "price": { "$lt": 100 } }
|
|
20
|
+
- $lte: Less than or equal
|
|
21
|
+
Example: { "price": { "$lte": 100 } }
|
|
22
|
+
|
|
23
|
+
Array Operators:
|
|
24
|
+
- $in: Match any value in array
|
|
25
|
+
Example: { "category": { "$in": ["electronics", "books"] } }
|
|
26
|
+
- $nin: Does not match any value in array
|
|
27
|
+
Example: { "category": { "$nin": ["electronics", "books"] } }
|
|
28
|
+
- $all: Match all values in array
|
|
29
|
+
Example: { "tags": { "$all": ["premium", "sale"] } }
|
|
30
|
+
|
|
31
|
+
Logical Operators:
|
|
32
|
+
- $and: Logical AND (implicit when using multiple conditions)
|
|
33
|
+
Example: { "$and": [{ "price": { "$gt": 100 } }, { "category": "electronics" }] }
|
|
34
|
+
- $or: Logical OR
|
|
35
|
+
Example: { "$or": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
|
|
36
|
+
- $not: Logical NOT
|
|
37
|
+
Example: { "$not": { "category": "electronics" } }
|
|
38
|
+
|
|
39
|
+
Element Operators:
|
|
40
|
+
- $exists: Check if field exists
|
|
41
|
+
Example: { "rating": { "$exists": true } }
|
|
42
|
+
|
|
43
|
+
Regex Operator:
|
|
44
|
+
- $regex: Match using a regular expression (ECMAScript syntax)
|
|
45
|
+
Example: { "name": { "$regex": "^Sam.*son$" } }
|
|
46
|
+
Note: Regex queries are supported for string fields only. Use valid ECMAScript patterns; invalid patterns will throw an error.
|
|
47
|
+
|
|
48
|
+
Restrictions:
|
|
49
|
+
- Nested fields are supported using dot notation (e.g., "address.city").
|
|
50
|
+
- Multiple conditions on the same field are supported (e.g., { "price": { "$gte": 100, "$lte": 1000 } }).
|
|
51
|
+
- Only logical operators ($and, $or, $not) can be used at the top level.
|
|
52
|
+
- All other operators must be used within a field condition.
|
|
53
|
+
Valid: { "field": { "$gt": 100 } }
|
|
54
|
+
Valid: { "$and": [...] }
|
|
55
|
+
Invalid: { "$gt": 100 }
|
|
56
|
+
- Logical operators must contain field conditions, not direct operators.
|
|
57
|
+
Valid: { "$and": [{ "field": { "$gt": 100 } }] }
|
|
58
|
+
Invalid: { "$and": [{ "$gt": 100 }] }
|
|
59
|
+
- $not operator:
|
|
60
|
+
- Must be an object
|
|
61
|
+
- Cannot be empty
|
|
62
|
+
- Can be used at field level or top level
|
|
63
|
+
- Valid: { "$not": { "field": "value" } }
|
|
64
|
+
- Valid: { "field": { "$not": { "$eq": "value" } } }
|
|
65
|
+
- Array operators work on array fields only.
|
|
66
|
+
- Empty arrays in conditions are handled gracefully.
|
|
67
|
+
- Regex queries are case-sensitive by default; use patterns accordingly.
|
|
68
|
+
|
|
69
|
+
Example Complex Query:
|
|
70
|
+
{
|
|
71
|
+
"$and": [
|
|
72
|
+
{ "category": { "$in": ["electronics", "computers"] } },
|
|
73
|
+
{ "price": { "$gte": 100, "$lte": 1000 } },
|
|
74
|
+
{ "tags": { "$all": ["premium"] } },
|
|
75
|
+
{ "rating": { "$exists": true, "$gt": 4 } },
|
|
76
|
+
{ "$or": [
|
|
77
|
+
{ "stock": { "$gt": 0 } },
|
|
78
|
+
{ "preorder": true }
|
|
79
|
+
]},
|
|
80
|
+
{ "name": { "$regex": "^Sam.*son$" } }
|
|
81
|
+
]
|
|
82
|
+
}`;
|
package/tsconfig.json
CHANGED
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { generateTypes } from '@internal/types-builder';
|
|
2
|
+
import { defineConfig } from 'tsup';
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
entry: ['src/index.ts'],
|
|
6
|
+
format: ['esm', 'cjs'],
|
|
7
|
+
clean: true,
|
|
8
|
+
dts: false,
|
|
9
|
+
splitting: true,
|
|
10
|
+
treeshake: {
|
|
11
|
+
preset: 'smallest',
|
|
12
|
+
},
|
|
13
|
+
sourcemap: true,
|
|
14
|
+
onSuccess: async () => {
|
|
15
|
+
await generateTypes(process.cwd());
|
|
16
|
+
},
|
|
17
|
+
});
|