@mastra/chroma 0.0.0-storage-20250225005900 → 0.0.0-vnext-inngest-20250506121901

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,6 +1,6 @@
1
- import { BaseFilterTranslator } from '@mastra/core/filter';
2
1
  import { MastraVector } from '@mastra/core/vector';
3
2
  import { ChromaClient } from 'chromadb';
3
+ import { BaseFilterTranslator } from '@mastra/core/vector/filter';
4
4
 
5
5
  // src/vector/index.ts
6
6
  var ChromaFilterTranslator = class extends BaseFilterTranslator {
@@ -105,7 +105,7 @@ var ChromaVector = class extends MastraVector {
105
105
  try {
106
106
  const collection = await this.client.getCollection({ name: indexName, embeddingFunction: void 0 });
107
107
  this.collections.set(indexName, collection);
108
- } catch (error) {
108
+ } catch {
109
109
  if (throwIfNotExists) {
110
110
  throw new Error(`Index ${indexName} does not exist`);
111
111
  }
@@ -122,7 +122,9 @@ var ChromaVector = class extends MastraVector {
122
122
  }
123
123
  }
124
124
  }
125
- async upsert(indexName, vectors, metadata, ids) {
125
+ async upsert(...args) {
126
+ const params = this.normalizeArgs("upsert", args, ["documents"]);
127
+ const { indexName, vectors, metadata, ids, documents } = params;
126
128
  const collection = await this.getCollection(indexName);
127
129
  const stats = await this.describeIndex(indexName);
128
130
  this.validateVectorDimensions(vectors, stats.dimension);
@@ -131,28 +133,43 @@ var ChromaVector = class extends MastraVector {
131
133
  await collection.upsert({
132
134
  ids: generatedIds,
133
135
  embeddings: vectors,
134
- metadatas: normalizedMetadata
136
+ metadatas: normalizedMetadata,
137
+ documents
135
138
  });
136
139
  return generatedIds;
137
140
  }
138
- async createIndex(indexName, dimension, metric = "cosine") {
141
+ HnswSpaceMap = {
142
+ cosine: "cosine",
143
+ euclidean: "l2",
144
+ dotproduct: "ip",
145
+ l2: "euclidean",
146
+ ip: "dotproduct"
147
+ };
148
+ async createIndex(...args) {
149
+ const params = this.normalizeArgs("createIndex", args);
150
+ const { indexName, dimension, metric = "cosine" } = params;
139
151
  if (!Number.isInteger(dimension) || dimension <= 0) {
140
152
  throw new Error("Dimension must be a positive integer");
141
153
  }
154
+ const hnswSpace = this.HnswSpaceMap[metric];
155
+ if (!["cosine", "l2", "ip"].includes(hnswSpace)) {
156
+ throw new Error(`Invalid metric: "${metric}". Must be one of: cosine, euclidean, dotproduct`);
157
+ }
142
158
  await this.client.createCollection({
143
159
  name: indexName,
144
160
  metadata: {
145
161
  dimension,
146
- metric
162
+ "hnsw:space": this.HnswSpaceMap[metric]
147
163
  }
148
164
  });
149
165
  }
150
166
  transformFilter(filter) {
151
- const chromaFilter = new ChromaFilterTranslator();
152
- const translatedFilter = chromaFilter.translate(filter);
153
- return translatedFilter;
167
+ const translator = new ChromaFilterTranslator();
168
+ return translator.translate(filter);
154
169
  }
155
- async query(indexName, queryVector, topK = 10, filter, includeVector = false) {
170
+ async query(...args) {
171
+ const params = this.normalizeArgs("query", args, ["documentFilter"]);
172
+ const { indexName, queryVector, topK = 10, filter, includeVector = false, documentFilter } = params;
156
173
  const collection = await this.getCollection(indexName, true);
157
174
  const defaultInclude = ["documents", "metadatas", "distances"];
158
175
  const translatedFilter = this.transformFilter(filter);
@@ -160,12 +177,14 @@ var ChromaVector = class extends MastraVector {
160
177
  queryEmbeddings: [queryVector],
161
178
  nResults: topK,
162
179
  where: translatedFilter,
180
+ whereDocument: documentFilter,
163
181
  include: includeVector ? [...defaultInclude, "embeddings"] : defaultInclude
164
182
  });
165
183
  return (results.ids[0] || []).map((id, index) => ({
166
184
  id,
167
185
  score: results.distances?.[0]?.[index] || 0,
168
186
  metadata: results.metadatas?.[0]?.[index] || {},
187
+ document: results.documents?.[0]?.[index],
169
188
  ...includeVector && { vector: results.embeddings?.[0]?.[index] || [] }
170
189
  }));
171
190
  }
@@ -177,16 +196,109 @@ var ChromaVector = class extends MastraVector {
177
196
  const collection = await this.getCollection(indexName);
178
197
  const count = await collection.count();
179
198
  const metadata = collection.metadata;
199
+ const hnswSpace = metadata?.["hnsw:space"];
180
200
  return {
181
201
  dimension: metadata?.dimension || 0,
182
202
  count,
183
- metric: metadata?.metric
203
+ metric: this.HnswSpaceMap[hnswSpace]
184
204
  };
185
205
  }
186
206
  async deleteIndex(indexName) {
187
207
  await this.client.deleteCollection({ name: indexName });
188
208
  this.collections.delete(indexName);
189
209
  }
210
+ async updateIndexById(indexName, id, update) {
211
+ if (!update.vector && !update.metadata) {
212
+ throw new Error("No updates provided");
213
+ }
214
+ const collection = await this.getCollection(indexName, true);
215
+ const updateOptions = { ids: [id] };
216
+ if (update?.vector) {
217
+ updateOptions.embeddings = [update.vector];
218
+ }
219
+ if (update?.metadata) {
220
+ updateOptions.metadatas = [update.metadata];
221
+ }
222
+ return await collection.update(updateOptions);
223
+ }
224
+ async deleteIndexById(indexName, id) {
225
+ try {
226
+ const collection = await this.getCollection(indexName, true);
227
+ await collection.delete({ ids: [id] });
228
+ } catch (error) {
229
+ throw new Error(`Failed to delete index by id: ${id} for index name: ${indexName}: ${error.message}`);
230
+ }
231
+ }
190
232
  };
191
233
 
192
- export { ChromaVector };
234
+ // src/vector/prompt.ts
235
+ var CHROMA_PROMPT = `When querying Chroma, you can ONLY use the operators listed below. Any other operators will be rejected.
236
+ Important: Don't explain how to construct the filter - use the specified operators and fields to search the content and return relevant results.
237
+ If a user tries to give an explicit operator that is not supported, reject the filter entirely and let them know that the operator is not supported.
238
+
239
+ Basic Comparison Operators:
240
+ - $eq: Exact match (default when using field: value)
241
+ Example: { "category": "electronics" }
242
+ - $ne: Not equal
243
+ Example: { "category": { "$ne": "electronics" } }
244
+ - $gt: Greater than
245
+ Example: { "price": { "$gt": 100 } }
246
+ - $gte: Greater than or equal
247
+ Example: { "price": { "$gte": 100 } }
248
+ - $lt: Less than
249
+ Example: { "price": { "$lt": 100 } }
250
+ - $lte: Less than or equal
251
+ Example: { "price": { "$lte": 100 } }
252
+
253
+ Array Operators:
254
+ - $in: Match any value in array
255
+ Example: { "category": { "$in": ["electronics", "books"] } }
256
+ - $nin: Does not match any value in array
257
+ Example: { "category": { "$nin": ["electronics", "books"] } }
258
+
259
+ Logical Operators:
260
+ - $and: Logical AND
261
+ Example: { "$and": [{ "price": { "$gt": 100 } }, { "category": "electronics" }] }
262
+ - $or: Logical OR
263
+ Example: { "$or": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
264
+
265
+ Restrictions:
266
+ - Regex patterns are not supported
267
+ - Element operators are not supported
268
+ - Only $and and $or logical operators are supported
269
+ - Nested fields are supported using dot notation
270
+ - Multiple conditions on the same field are supported with both implicit and explicit $and
271
+ - Empty arrays in $in/$nin will return no results
272
+ - If multiple top-level fields exist, they're wrapped in $and
273
+ - Only logical operators ($and, $or) can be used at the top level
274
+ - All other operators must be used within a field condition
275
+ Valid: { "field": { "$gt": 100 } }
276
+ Valid: { "$and": [...] }
277
+ Invalid: { "$gt": 100 }
278
+ Invalid: { "$in": [...] }
279
+ - Logical operators must contain field conditions, not direct operators
280
+ Valid: { "$and": [{ "field": { "$gt": 100 } }] }
281
+ Invalid: { "$and": [{ "$gt": 100 }] }
282
+ - Logical operators ($and, $or):
283
+ - Can only be used at top level or nested within other logical operators
284
+ - Can not be used on a field level, or be nested inside a field
285
+ - Can not be used inside an operator
286
+ - Valid: { "$and": [{ "field": { "$gt": 100 } }] }
287
+ - Valid: { "$or": [{ "$and": [{ "field": { "$gt": 100 } }] }] }
288
+ - Invalid: { "field": { "$and": [{ "$gt": 100 }] } }
289
+ - Invalid: { "field": { "$or": [{ "$gt": 100 }] } }
290
+ - Invalid: { "field": { "$gt": { "$and": [{...}] } } }
291
+
292
+ Example Complex Query:
293
+ {
294
+ "$and": [
295
+ { "category": { "$in": ["electronics", "computers"] } },
296
+ { "price": { "$gte": 100, "$lte": 1000 } },
297
+ { "$or": [
298
+ { "inStock": true },
299
+ { "preorder": true }
300
+ ]}
301
+ ]
302
+ }`;
303
+
304
+ export { CHROMA_PROMPT, ChromaVector };
@@ -0,0 +1,7 @@
1
+ services:
2
+ chroma:
3
+ image: chromadb/chroma
4
+ ports:
5
+ - 8000:8000
6
+ volumes:
7
+ pgdata:
@@ -0,0 +1,6 @@
1
+ import { createConfig } from '@internal/lint/eslint';
2
+
3
+ const config = await createConfig();
4
+
5
+ /** @type {import("eslint").Linter.Config[]} */
6
+ export default [...config];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/chroma",
3
- "version": "0.0.0-storage-20250225005900",
3
+ "version": "0.0.0-vnext-inngest-20250506121901",
4
4
  "description": "Chroma vector store provider for Mastra",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -10,24 +10,32 @@
10
10
  "import": {
11
11
  "types": "./dist/index.d.ts",
12
12
  "default": "./dist/index.js"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.cts",
16
+ "default": "./dist/index.cjs"
13
17
  }
14
18
  },
15
19
  "./package.json": "./package.json"
16
20
  },
21
+ "license": "MIT",
17
22
  "dependencies": {
18
- "chromadb": "^1.9.4",
19
- "@mastra/core": "^0.0.0-storage-20250225005900"
23
+ "chromadb": "^2.2.0",
24
+ "@mastra/core": "0.0.0-vnext-inngest-20250506121901"
20
25
  },
21
26
  "devDependencies": {
22
- "@microsoft/api-extractor": "^7.49.2",
23
- "@types/node": "^22.13.1",
24
- "tsup": "^8.0.1",
25
- "typescript": "^5.7.3",
26
- "vitest": "^3.0.4"
27
+ "@microsoft/api-extractor": "^7.52.5",
28
+ "@types/node": "^20.17.27",
29
+ "eslint": "^9.23.0",
30
+ "tsup": "^8.4.0",
31
+ "typescript": "^5.8.2",
32
+ "vitest": "^3.1.2",
33
+ "@internal/lint": "0.0.0-vnext-inngest-20250506121901"
27
34
  },
28
35
  "scripts": {
29
- "build": "tsup src/index.ts --format esm --experimental-dts --clean --treeshake",
36
+ "build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
30
37
  "build:watch": "pnpm build --watch",
31
- "test": "vitest run"
38
+ "test": "vitest run",
39
+ "lint": "eslint ."
32
40
  }
33
41
  }
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './vector/index';
2
+ export { CHROMA_PROMPT } from './vector/prompt';
@@ -1,10 +1,5 @@
1
- import {
2
- BaseFilterTranslator,
3
- type FieldCondition,
4
- type Filter,
5
- type OperatorSupport,
6
- type QueryOperator,
7
- } from '@mastra/core/filter';
1
+ import { BaseFilterTranslator } from '@mastra/core/vector/filter';
2
+ import type { FieldCondition, VectorFilter, OperatorSupport, QueryOperator } from '@mastra/core/vector/filter';
8
3
 
9
4
  /**
10
5
  * Translator for Chroma filter queries.
@@ -23,14 +18,14 @@ export class ChromaFilterTranslator extends BaseFilterTranslator {
23
18
  };
24
19
  }
25
20
 
26
- translate(filter?: Filter): Filter | undefined {
21
+ translate(filter?: VectorFilter): VectorFilter {
27
22
  if (this.isEmpty(filter)) return filter;
28
- this.validateFilter(filter as Filter);
23
+ this.validateFilter(filter);
29
24
 
30
25
  return this.translateNode(filter);
31
26
  }
32
27
 
33
- private translateNode(node: Filter | FieldCondition, currentPath: string = ''): any {
28
+ private translateNode(node: VectorFilter | FieldCondition, currentPath: string = ''): any {
34
29
  // Handle primitive values and arrays
35
30
  if (this.isRegex(node)) {
36
31
  throw new Error('Regex is not supported in Chroma');