chromadb 3.3.2 → 3.4.0

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/src/collection.ts CHANGED
@@ -45,7 +45,7 @@ import {
45
45
  processUpdateCollectionConfig,
46
46
  UpdateCollectionConfiguration,
47
47
  } from "./collection-configuration";
48
- import { SearchLike, SearchResult, toSearch } from "./execution/expression";
48
+ import { SearchLike, SearchResult, toSearch } from "./execution";
49
49
  import { isPlainObject } from "./execution/expression/common";
50
50
  import { Schema, EMBEDDING_KEY, DOCUMENT_KEY } from "./schema";
51
51
  import type { SparseVectorIndexConfig } from "./schema";
@@ -167,6 +167,11 @@ export interface Collection {
167
167
  * @returns Promise resolving to the new Collection instance
168
168
  */
169
169
  fork({ name }: { name: string }): Promise<Collection>;
170
+ /**
171
+ * Gets the number of forks for this collection.
172
+ * @returns Promise resolving to the number of forks
173
+ */
174
+ forkCount(): Promise<number>;
170
175
  /**
171
176
  * Updates existing records in the collection.
172
177
  * @param args - Record data to update
@@ -216,6 +221,7 @@ export interface Collection {
216
221
  /**
217
222
  * Performs hybrid search on the collection using expression builders.
218
223
  * @param searches - Single search payload or array of payloads
224
+ * @param options
219
225
  * @returns Promise resolving to column-major search results
220
226
  */
221
227
  search(
@@ -366,7 +372,9 @@ export class CollectionImpl implements Collection {
366
372
 
367
373
  if (!embeddingFunction) {
368
374
  throw new ChromaValueError(
369
- "Embedding function must be defined for operations requiring embeddings.",
375
+ `No embedding function found for collection '${this._name}'. ` +
376
+ "You can either provide embeddings directly, or ensure the appropriate " +
377
+ "embedding function package (e.g. @chroma-core/default-embed) is installed.",
370
378
  );
371
379
  }
372
380
 
@@ -459,10 +467,8 @@ export class CollectionImpl implements Collection {
459
467
  // Get document at this position
460
468
  if (index < documentsList.length) {
461
469
  const doc = documentsList[index];
462
- if (typeof doc === "string") {
463
- inputs.push(doc);
464
- positions.push(index);
465
- }
470
+ inputs.push(doc);
471
+ positions.push(index);
466
472
  }
467
473
  });
468
474
 
@@ -1027,6 +1033,15 @@ export class CollectionImpl implements Collection {
1027
1033
  });
1028
1034
  }
1029
1035
 
1036
+ public async forkCount(): Promise<number> {
1037
+ const { data } = await CollectionService.forkCount({
1038
+ client: this.apiClient,
1039
+ path: await this.path(),
1040
+ });
1041
+
1042
+ return data.count;
1043
+ }
1044
+
1030
1045
  public async update({
1031
1046
  ids,
1032
1047
  embeddings,
@@ -1140,3 +1155,144 @@ export class CollectionImpl implements Collection {
1140
1155
  return data;
1141
1156
  }
1142
1157
  }
1158
+
1159
+ /**
1160
+ * Arguments for creating a CollectionHandle instance.
1161
+ */
1162
+ export interface CollectionHandleArgs {
1163
+ /** ChromaDB client instance */
1164
+ chromaClient: ChromaClient;
1165
+ /** HTTP API client */
1166
+ apiClient: ReturnType<typeof createClient>;
1167
+ /** Collection ID */
1168
+ id: string;
1169
+ /** Tenant name */
1170
+ tenant: string;
1171
+ /** Database name */
1172
+ database: string;
1173
+ }
1174
+
1175
+ const HANDLE_EMBEDDING_ERROR =
1176
+ "This operation requires an embedding function, which is not available on " +
1177
+ "a collection obtained via client.collection(id). Provide pre-computed " +
1178
+ "embeddings directly, or use client.getCollection() to get a full " +
1179
+ "collection with embedding support.";
1180
+
1181
+ const HANDLE_NOT_SUPPORTED_ERROR =
1182
+ "is not supported on a collection obtained via client.collection(id). " +
1183
+ "Use client.getCollection() to get a full collection instance.";
1184
+
1185
+ /**
1186
+ * A lightweight collection handle that holds only the collection ID and
1187
+ * client context. Supports operations that don't require an embedding
1188
+ * function or schema. Obtained via {@link ChromaClient.collection}.
1189
+ */
1190
+ export class CollectionHandle extends CollectionImpl {
1191
+ constructor({ chromaClient, apiClient, id, tenant, database }: CollectionHandleArgs) {
1192
+ super({
1193
+ chromaClient,
1194
+ apiClient,
1195
+ id,
1196
+ tenant,
1197
+ database,
1198
+ name: "",
1199
+ configuration: {},
1200
+ });
1201
+ }
1202
+
1203
+ public override async add(args: {
1204
+ ids: string[];
1205
+ embeddings?: number[][];
1206
+ metadatas?: Metadata[];
1207
+ documents?: string[];
1208
+ uris?: string[];
1209
+ }): Promise<void> {
1210
+ if (!args.embeddings) {
1211
+ throw new ChromaValueError(HANDLE_EMBEDDING_ERROR);
1212
+ }
1213
+ return super.add(args);
1214
+ }
1215
+
1216
+ public override async update(args: {
1217
+ ids: string[];
1218
+ embeddings?: number[][];
1219
+ metadatas?: Metadata[];
1220
+ documents?: string[];
1221
+ uris?: string[];
1222
+ }): Promise<void> {
1223
+ if (!args.embeddings && args.documents) {
1224
+ throw new ChromaValueError(HANDLE_EMBEDDING_ERROR);
1225
+ }
1226
+ return super.update(args);
1227
+ }
1228
+
1229
+ public override async upsert(args: {
1230
+ ids: string[];
1231
+ embeddings?: number[][];
1232
+ metadatas?: Metadata[];
1233
+ documents?: string[];
1234
+ uris?: string[];
1235
+ }): Promise<void> {
1236
+ if (!args.embeddings) {
1237
+ throw new ChromaValueError(HANDLE_EMBEDDING_ERROR);
1238
+ }
1239
+ return super.upsert(args);
1240
+ }
1241
+
1242
+ public override async query<TMeta extends Metadata = Metadata>(args: {
1243
+ queryEmbeddings?: number[][];
1244
+ queryTexts?: string[];
1245
+ queryURIs?: string[];
1246
+ ids?: string[];
1247
+ nResults?: number;
1248
+ where?: Where;
1249
+ whereDocument?: WhereDocument;
1250
+ include?: Include[];
1251
+ }): Promise<QueryResult<TMeta>> {
1252
+ if (!args.queryEmbeddings) {
1253
+ throw new ChromaValueError(HANDLE_EMBEDDING_ERROR);
1254
+ }
1255
+ return super.query(args);
1256
+ }
1257
+
1258
+ public override async search(
1259
+ searches: SearchLike | SearchLike[],
1260
+ options?: { readLevel?: ReadLevel },
1261
+ ): Promise<SearchResult> {
1262
+ const items = Array.isArray(searches) ? searches : [searches];
1263
+ for (const search of items) {
1264
+ const payload = toSearch(search).toPayload();
1265
+ if (this.hasStringKnnQuery(payload.rank)) {
1266
+ throw new ChromaValueError(HANDLE_EMBEDDING_ERROR);
1267
+ }
1268
+ }
1269
+ return super.search(searches, options);
1270
+ }
1271
+
1272
+ public override async modify(_args: {
1273
+ name?: string;
1274
+ metadata?: CollectionMetadata;
1275
+ configuration?: UpdateCollectionConfiguration;
1276
+ }): Promise<void> {
1277
+ throw new ChromaValueError(`modify() ${HANDLE_NOT_SUPPORTED_ERROR}`);
1278
+ }
1279
+
1280
+ public override async fork(_args: { name: string }): Promise<Collection> {
1281
+ throw new ChromaValueError(`fork() ${HANDLE_NOT_SUPPORTED_ERROR}`);
1282
+ }
1283
+
1284
+ private hasStringKnnQuery(obj: unknown): boolean {
1285
+ if (!obj || typeof obj !== "object") return false;
1286
+ if (Array.isArray(obj)) {
1287
+ return obj.some((item) => this.hasStringKnnQuery(item));
1288
+ }
1289
+ const record = obj as Record<string, unknown>;
1290
+ if ("$knn" in record && isPlainObject(record.$knn)) {
1291
+ const knn = record.$knn as Record<string, unknown>;
1292
+ if (typeof knn.query === "string") return true;
1293
+ }
1294
+ return Object.values(record).some((value) =>
1295
+ this.hasStringKnnQuery(value),
1296
+ );
1297
+ }
1298
+ }
@@ -1,6 +1,5 @@
1
1
  import { EmbeddingFunctionConfiguration, SparseVector } from "./api";
2
2
  import { ChromaValueError } from "./errors";
3
- import { DefaultEmbeddingFunction } from "@chroma-core/default-embed";
4
3
  import { ChromaClient } from "./chroma-client";
5
4
 
6
5
  /**
@@ -140,7 +139,8 @@ const pythonEmbeddingFunctions: Record<string, string> = {
140
139
  default: "default-embed",
141
140
  together_ai: "together-ai",
142
141
  sentence_transformer: "sentence-transformer",
143
- google_genai: "google-gemini",
142
+ google_gemini: "google-gemini",
143
+ google_genai: "google-gemini", // Backward compatibility alias
144
144
  };
145
145
 
146
146
  const unsupportedEmbeddingFunctions: Set<string> = new Set([
@@ -225,41 +225,16 @@ export const registerSparseEmbeddingFunction = (
225
225
  * @returns EmbeddingFunction instance or undefined if it cannot be constructed
226
226
  */
227
227
  export const getEmbeddingFunction = async (args: {
228
- collectionName: string;
229
228
  client: ChromaClient;
230
229
  efConfig?: EmbeddingFunctionConfiguration;
231
230
  }) => {
232
- const { collectionName, client, efConfig } = args;
231
+ const { client, efConfig } = args;
233
232
 
234
- if (!efConfig) {
235
- console.warn(
236
- `No embedding function configuration found for collection ${collectionName}. 'add' and 'query' will fail unless you provide them embeddings directly.`,
237
- );
238
- return undefined;
239
- }
240
-
241
- if (efConfig.type === "legacy") {
242
- console.warn(
243
- `No embedding function configuration found for collection ${collectionName}. 'add' and 'query' will fail unless you provide them embeddings directly.`,
244
- );
245
- return undefined;
246
- }
247
-
248
- if (efConfig.type === "unknown") {
249
- console.warn(
250
- `Unknown embedding function configuration for collection ${collectionName}. 'add' and 'query' will fail unless you provide them embeddings directly.`,
251
- );
252
- return undefined;
253
- }
254
-
255
- if (efConfig.type !== "known") {
233
+ if (efConfig?.type !== "known") {
256
234
  return undefined;
257
235
  }
258
236
 
259
237
  if (unsupportedEmbeddingFunctions.has(efConfig.name)) {
260
- console.warn(
261
- `Embedding function ${efConfig.name} is not supported in the JS/TS SDK. 'add' and 'query' will fail unless you provide them embeddings directly.`,
262
- );
263
238
  return undefined;
264
239
  }
265
240
 
@@ -276,33 +251,23 @@ export const getEmbeddingFunction = async (args: {
276
251
  await import(fullPackageName);
277
252
  embeddingFunction = knownEmbeddingFunctions.get(packageName);
278
253
  } catch (error) {
279
- // Dynamic loading failed, proceed with warning
254
+ // Dynamic loading failed
280
255
  }
281
256
 
282
257
  if (!embeddingFunction) {
283
- console.warn(
284
- `Collection ${collectionName} was created with the ${packageName} embedding function. However, the @chroma-core/${packageName} package is not installed. 'add' and 'query' will fail unless you provide them embeddings directly, or install the @chroma-core/${packageName} package.`,
285
- );
286
258
  return undefined;
287
259
  }
288
260
  }
289
261
 
290
- let constructorConfig: Record<string, any> =
291
- efConfig.type === "known" ? (efConfig.config as Record<string, any>) : {};
262
+ const constructorConfig: Record<string, any> =
263
+ (efConfig.config as Record<string, any>) ?? {};
292
264
 
293
265
  try {
294
266
  if (embeddingFunction.buildFromConfig) {
295
267
  return embeddingFunction.buildFromConfig(constructorConfig, client);
296
268
  }
297
-
298
- console.warn(
299
- `Embedding function ${packageName} does not define a 'buildFromConfig' function. 'add' and 'query' will fail unless you provide them embeddings directly.`,
300
- );
301
269
  return undefined;
302
270
  } catch (e) {
303
- console.warn(
304
- `Embedding function ${packageName} failed to build with config: ${constructorConfig}. 'add' and 'query' will fail unless you provide them embeddings directly. Error: ${e}`,
305
- );
306
271
  return undefined;
307
272
  }
308
273
  };
@@ -312,26 +277,14 @@ export const getEmbeddingFunction = async (args: {
312
277
  * @returns SparseEmbeddingFunction instance or undefined if it cannot be constructed
313
278
  */
314
279
  export const getSparseEmbeddingFunction = async (
315
- collectionName: string,
316
280
  client: ChromaClient,
317
281
  efConfig?: EmbeddingFunctionConfiguration,
318
282
  ) => {
319
- if (!efConfig) {
320
- return undefined;
321
- }
322
-
323
- if (efConfig.type === "legacy") {
324
- return undefined;
325
- }
326
-
327
- if (efConfig.type !== "known") {
283
+ if (efConfig?.type !== "known") {
328
284
  return undefined;
329
285
  }
330
286
 
331
287
  if (unsupportedSparseEmbeddingFunctions.has(efConfig.name)) {
332
- console.warn(
333
- "Embedding function ${efConfig.name} is not supported in the JS/TS SDK. 'add' and 'query' will fail unless you provide them embeddings directly.",
334
- );
335
288
  return undefined;
336
289
  }
337
290
 
@@ -345,33 +298,23 @@ export const getSparseEmbeddingFunction = async (
345
298
  await import(fullPackageName);
346
299
  sparseEmbeddingFunction = knownSparseEmbeddingFunctions.get(packageName);
347
300
  } catch (error) {
348
- // Dynamic loading failed, proceed with warning
301
+ // Dynamic loading failed
349
302
  }
350
303
 
351
304
  if (!sparseEmbeddingFunction) {
352
- console.warn(
353
- `Collection ${collectionName} was created with the ${packageName} sparse embedding function. However, the @chroma-core/${packageName} package is not installed.`,
354
- );
355
305
  return undefined;
356
306
  }
357
307
  }
358
308
 
359
- let constructorConfig: Record<string, any> =
360
- efConfig.type === "known" ? (efConfig.config as Record<string, any>) : {};
309
+ const constructorConfig: Record<string, any> =
310
+ (efConfig.config as Record<string, any>) ?? {};
361
311
 
362
312
  try {
363
313
  if (sparseEmbeddingFunction.buildFromConfig) {
364
314
  return sparseEmbeddingFunction.buildFromConfig(constructorConfig, client);
365
315
  }
366
-
367
- console.warn(
368
- `Sparse embedding function ${packageName} does not define a 'buildFromConfig' function.`,
369
- );
370
316
  return undefined;
371
317
  } catch (e) {
372
- console.warn(
373
- `Sparse embedding function ${packageName} failed to build with config: ${constructorConfig}. Error: ${e}`,
374
- );
375
318
  return undefined;
376
319
  }
377
320
  };
package/src/index.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  // Apply Deno compatibility patch
7
7
  import "./deno";
8
8
 
9
- export { Collection } from "./collection";
9
+ export { Collection, CollectionHandle } from "./collection";
10
10
  export { withChroma } from "./next";
11
11
  export * from "./types";
12
12
  export * from "./admin-client";
package/src/schema.ts CHANGED
@@ -1285,7 +1285,6 @@ export class Schema {
1285
1285
  });
1286
1286
 
1287
1287
  config.embeddingFunction = await getEmbeddingFunction({
1288
- collectionName: "schema deserialization",
1289
1288
  client,
1290
1289
  efConfig: json.embedding_function as EmbeddingFunctionConfiguration,
1291
1290
  });
@@ -1307,7 +1306,6 @@ export class Schema {
1307
1306
 
1308
1307
  const embeddingFunction =
1309
1308
  (await getSparseEmbeddingFunction(
1310
- "schema deserialization",
1311
1309
  client,
1312
1310
  json.embedding_function as EmbeddingFunctionConfiguration,
1313
1311
  )) ??