@mastra/chroma 0.1.0-alpha.36 → 0.1.0-alpha.38

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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # @mastra/chroma
2
2
 
3
+ ## 0.1.0-alpha.38
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [4534e77]
8
+ - @mastra/core@0.2.0-alpha.103
9
+
10
+ ## 0.1.0-alpha.37
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [a9345f9]
15
+ - @mastra/core@0.2.0-alpha.102
16
+
3
17
  ## 0.1.0-alpha.36
4
18
 
5
19
  ### Patch Changes
@@ -0,0 +1,43 @@
1
+ import { BaseFilterTranslator } from '@mastra/core/filter';
2
+ import { Filter } from '@mastra/core/filter';
3
+ import { IndexStats } from '@mastra/core/vector';
4
+ import { MastraVector } from '@mastra/core/vector';
5
+ import { OperatorSupport } from '@mastra/core/filter';
6
+ import { QueryResult } from '@mastra/core/vector';
7
+
8
+ /**
9
+ * Translator for Chroma filter queries.
10
+ * Maintains MongoDB-compatible syntax while ensuring proper validation
11
+ * and normalization of values.
12
+ */
13
+ export declare class ChromaFilterTranslator extends BaseFilterTranslator {
14
+ protected getSupportedOperators(): OperatorSupport;
15
+ translate(filter?: Filter): Filter | undefined;
16
+ private translateNode;
17
+ private translateOperator;
18
+ }
19
+
20
+ declare class ChromaVector extends MastraVector {
21
+ private client;
22
+ private collections;
23
+ constructor({ path, auth, }: {
24
+ path: string;
25
+ auth?: {
26
+ provider: string;
27
+ credentials: string;
28
+ };
29
+ });
30
+ private getCollection;
31
+ private validateVectorDimensions;
32
+ upsert(indexName: string, vectors: number[][], metadata?: Record<string, any>[], ids?: string[]): Promise<string[]>;
33
+ createIndex(indexName: string, dimension: number, metric?: 'cosine' | 'euclidean' | 'dotproduct'): Promise<void>;
34
+ transformFilter(filter?: Filter): Filter | undefined;
35
+ query(indexName: string, queryVector: number[], topK?: number, filter?: Filter, includeVector?: boolean): Promise<QueryResult[]>;
36
+ listIndexes(): Promise<string[]>;
37
+ describeIndex(indexName: string): Promise<IndexStats>;
38
+ deleteIndex(indexName: string): Promise<void>;
39
+ }
40
+ export { ChromaVector }
41
+ export { ChromaVector as ChromaVector_alias_1 }
42
+
43
+ export { }
@@ -0,0 +1 @@
1
+ export { ChromaVector } from './_tsup-dts-rollup.js';
package/dist/index.js ADDED
@@ -0,0 +1,192 @@
1
+ import { BaseFilterTranslator } from '@mastra/core/filter';
2
+ import { MastraVector } from '@mastra/core/vector';
3
+ import { ChromaClient } from 'chromadb';
4
+
5
+ // src/vector/index.ts
6
+ var ChromaFilterTranslator = class extends BaseFilterTranslator {
7
+ getSupportedOperators() {
8
+ return {
9
+ ...BaseFilterTranslator.DEFAULT_OPERATORS,
10
+ logical: ["$and", "$or"],
11
+ array: ["$in", "$nin"],
12
+ element: [],
13
+ regex: [],
14
+ custom: []
15
+ };
16
+ }
17
+ translate(filter) {
18
+ if (this.isEmpty(filter)) return filter;
19
+ this.validateFilter(filter);
20
+ return this.translateNode(filter);
21
+ }
22
+ translateNode(node, currentPath = "") {
23
+ if (this.isRegex(node)) {
24
+ throw new Error("Regex is not supported in Chroma");
25
+ }
26
+ if (this.isPrimitive(node)) return this.normalizeComparisonValue(node);
27
+ if (Array.isArray(node)) return { $in: this.normalizeArrayValues(node) };
28
+ const entries = Object.entries(node);
29
+ const firstEntry = entries[0];
30
+ if (entries.length === 1 && firstEntry && this.isOperator(firstEntry[0])) {
31
+ const [operator, value] = firstEntry;
32
+ const translated = this.translateOperator(operator, value);
33
+ return this.isLogicalOperator(operator) ? { [operator]: translated } : translated;
34
+ }
35
+ const result = {};
36
+ const multiOperatorConditions = [];
37
+ for (const [key, value] of entries) {
38
+ const newPath = currentPath ? `${currentPath}.${key}` : key;
39
+ if (this.isOperator(key)) {
40
+ result[key] = this.translateOperator(key, value);
41
+ continue;
42
+ }
43
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
44
+ const valueEntries = Object.entries(value);
45
+ if (valueEntries.every(([op]) => this.isOperator(op)) && valueEntries.length > 1) {
46
+ valueEntries.forEach(([op, opValue]) => {
47
+ multiOperatorConditions.push({
48
+ [newPath]: { [op]: this.normalizeComparisonValue(opValue) }
49
+ });
50
+ });
51
+ continue;
52
+ }
53
+ if (Object.keys(value).length === 0) {
54
+ result[newPath] = this.translateNode(value);
55
+ } else {
56
+ const hasOperators = Object.keys(value).some((k) => this.isOperator(k));
57
+ if (hasOperators) {
58
+ const normalizedValue = {};
59
+ for (const [op, opValue] of Object.entries(value)) {
60
+ normalizedValue[op] = this.isOperator(op) ? this.translateOperator(op, opValue) : opValue;
61
+ }
62
+ result[newPath] = normalizedValue;
63
+ } else {
64
+ Object.assign(result, this.translateNode(value, newPath));
65
+ }
66
+ }
67
+ } else {
68
+ result[newPath] = this.translateNode(value);
69
+ }
70
+ }
71
+ if (multiOperatorConditions.length > 0) {
72
+ return { $and: multiOperatorConditions };
73
+ }
74
+ if (Object.keys(result).length > 1 && !currentPath) {
75
+ return {
76
+ $and: Object.entries(result).map(([key, value]) => ({ [key]: value }))
77
+ };
78
+ }
79
+ return result;
80
+ }
81
+ translateOperator(operator, value) {
82
+ if (this.isLogicalOperator(operator)) {
83
+ return Array.isArray(value) ? value.map((item) => this.translateNode(item)) : this.translateNode(value);
84
+ }
85
+ return this.normalizeComparisonValue(value);
86
+ }
87
+ };
88
+
89
+ // src/vector/index.ts
90
+ var ChromaVector = class extends MastraVector {
91
+ client;
92
+ collections;
93
+ constructor({
94
+ path,
95
+ auth
96
+ }) {
97
+ super();
98
+ this.client = new ChromaClient({
99
+ path,
100
+ auth
101
+ });
102
+ this.collections = /* @__PURE__ */ new Map();
103
+ }
104
+ async getCollection(indexName, throwIfNotExists = true) {
105
+ try {
106
+ const collection = await this.client.getCollection({ name: indexName, embeddingFunction: void 0 });
107
+ this.collections.set(indexName, collection);
108
+ } catch (error) {
109
+ if (throwIfNotExists) {
110
+ throw new Error(`Index ${indexName} does not exist`);
111
+ }
112
+ return null;
113
+ }
114
+ return this.collections.get(indexName);
115
+ }
116
+ validateVectorDimensions(vectors, dimension) {
117
+ for (let i = 0; i < vectors.length; i++) {
118
+ if (vectors?.[i]?.length !== dimension) {
119
+ throw new Error(
120
+ `Vector at index ${i} has invalid dimension ${vectors?.[i]?.length}. Expected ${dimension} dimensions.`
121
+ );
122
+ }
123
+ }
124
+ }
125
+ async upsert(indexName, vectors, metadata, ids) {
126
+ const collection = await this.getCollection(indexName);
127
+ const stats = await this.describeIndex(indexName);
128
+ this.validateVectorDimensions(vectors, stats.dimension);
129
+ const generatedIds = ids || vectors.map(() => crypto.randomUUID());
130
+ const normalizedMetadata = metadata || vectors.map(() => ({}));
131
+ await collection.upsert({
132
+ ids: generatedIds,
133
+ embeddings: vectors,
134
+ metadatas: normalizedMetadata
135
+ });
136
+ return generatedIds;
137
+ }
138
+ async createIndex(indexName, dimension, metric = "cosine") {
139
+ if (!Number.isInteger(dimension) || dimension <= 0) {
140
+ throw new Error("Dimension must be a positive integer");
141
+ }
142
+ await this.client.createCollection({
143
+ name: indexName,
144
+ metadata: {
145
+ dimension,
146
+ metric
147
+ }
148
+ });
149
+ }
150
+ transformFilter(filter) {
151
+ const chromaFilter = new ChromaFilterTranslator();
152
+ const translatedFilter = chromaFilter.translate(filter);
153
+ return translatedFilter;
154
+ }
155
+ async query(indexName, queryVector, topK = 10, filter, includeVector = false) {
156
+ const collection = await this.getCollection(indexName, true);
157
+ const defaultInclude = ["documents", "metadatas", "distances"];
158
+ const translatedFilter = this.transformFilter(filter);
159
+ const results = await collection.query({
160
+ queryEmbeddings: [queryVector],
161
+ nResults: topK,
162
+ where: translatedFilter,
163
+ include: includeVector ? [...defaultInclude, "embeddings"] : defaultInclude
164
+ });
165
+ return (results.ids[0] || []).map((id, index) => ({
166
+ id,
167
+ score: results.distances?.[0]?.[index] || 0,
168
+ metadata: results.metadatas?.[0]?.[index] || {},
169
+ ...includeVector && { vector: results.embeddings?.[0]?.[index] || [] }
170
+ }));
171
+ }
172
+ async listIndexes() {
173
+ const collections = await this.client.listCollections();
174
+ return collections.map((collection) => collection);
175
+ }
176
+ async describeIndex(indexName) {
177
+ const collection = await this.getCollection(indexName);
178
+ const count = await collection.count();
179
+ const metadata = collection.metadata;
180
+ return {
181
+ dimension: metadata?.dimension || 0,
182
+ count,
183
+ metric: metadata?.metric
184
+ };
185
+ }
186
+ async deleteIndex(indexName) {
187
+ await this.client.deleteCollection({ name: indexName });
188
+ this.collections.delete(indexName);
189
+ }
190
+ };
191
+
192
+ export { ChromaVector };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/chroma",
3
- "version": "0.1.0-alpha.36",
3
+ "version": "0.1.0-alpha.38",
4
4
  "description": "Chroma vector store provider for Mastra",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "chromadb": "^1.9.4",
19
- "@mastra/core": "^0.2.0-alpha.101"
19
+ "@mastra/core": "^0.2.0-alpha.103"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@microsoft/api-extractor": "^7.49.2",