From 97c7539bf1420f1293ab906eaaa8babe276ddd5b Mon Sep 17 00:00:00 2001 From: Matthew Keller Date: Wed, 13 Mar 2024 19:58:05 -0400 Subject: [PATCH] [CLN] Apply Prettier Config to JS Client Repo (#1831) ## Description of changes *Summarize the changes made by this PR.* Applies Prettier formatting to the JS Client Repo and add a presubmit rule to enforce that behavior. ## Test plan N/A just formatting changes. --- .pre-commit-config.yaml | 7 + clients/js/examples/browser/app.ts | 4 +- clients/js/examples/node/app.js | 20 +- clients/js/package.json | 5 +- clients/js/src/AdminClient.ts | 462 ++++---- clients/js/src/ChromaClient.ts | 614 +++++----- clients/js/src/CloudClient.ts | 58 +- clients/js/src/Collection.ts | 1039 +++++++++-------- clients/js/src/auth.ts | 555 +++++---- .../embeddings/DefaultEmbeddingFunction.ts | 60 +- .../GoogleGeminiEmbeddingFunction.ts | 121 +- .../HuggingFaceEmbeddingServerFunction.ts | 39 +- .../js/src/embeddings/IEmbeddingFunction.ts | 2 +- .../src/embeddings/JinaEmbeddingFunction.ts | 18 +- .../src/embeddings/OpenAIEmbeddingFunction.ts | 264 +++-- .../TransformersEmbeddingFunction.ts | 60 +- clients/js/src/index.ts | 84 +- clients/js/src/types.ts | 111 +- clients/js/src/utils.ts | 35 +- clients/js/test/add.collections.test.ts | 94 +- clients/js/test/admin.test.ts | 80 +- clients/js/test/auth.basic.test.ts | 42 +- clients/js/test/auth.token.test.ts | 117 +- clients/js/test/client.test.ts | 361 +++--- clients/js/test/collection.client.test.ts | 35 +- clients/js/test/collection.test.ts | 7 +- clients/js/test/delete.collection.test.ts | 8 +- clients/js/test/get.collection.test.ts | 47 +- clients/js/test/initClientWithAuth.ts | 34 +- clients/js/test/query.collection.test.ts | 108 +- clients/js/test/update.collection.test.ts | 13 +- clients/js/test/upsert.collections.test.ts | 45 +- clients/js/tsconfig.json | 4 +- clients/js/tsup.config.ts | 30 +- 34 files changed, 2430 insertions(+), 2153 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7b1d50ec940..eb7c7916b66 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,3 +34,10 @@ repos: - id: mypy args: [--strict, --ignore-missing-imports, --follow-imports=silent, --disable-error-code=type-abstract, --config-file=./pyproject.toml] additional_dependencies: ["types-requests", "pydantic", "overrides", "hypothesis", "pytest", "pypika", "numpy", "types-protobuf", "kubernetes"] + + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v3.1.0" + hooks: + - id: prettier + files: "^clients/js/.+" diff --git a/clients/js/examples/browser/app.ts b/clients/js/examples/browser/app.ts index afc9ddbb766..47e4c02c5cf 100644 --- a/clients/js/examples/browser/app.ts +++ b/clients/js/examples/browser/app.ts @@ -1,4 +1,4 @@ -import { ChromaClient } from '../../src/ChromaClient'; +import { ChromaClient } from "../../src/ChromaClient"; // import env.ts window.onload = async () => { @@ -27,7 +27,7 @@ window.onload = async () => { const queryData = await collection.query({ queryEmbeddings: [1, 2, 3, 4, 5], nResults: 5, - where: { test: "test" } + where: { test: "test" }, }); console.log("queryData", queryData); diff --git a/clients/js/examples/node/app.js b/clients/js/examples/node/app.js index 0b153aaae35..b4ad303ab58 100644 --- a/clients/js/examples/node/app.js +++ b/clients/js/examples/node/app.js @@ -9,7 +9,9 @@ app.get("/", async (req, res) => { const cc = new chroma.ChromaClient({ path: "http://localhost:8000" }); await cc.reset(); - const google = new chroma.GoogleGenerativeAiEmbeddingFunction({ googleApiKey:"" }); + const google = new chroma.GoogleGenerativeAiEmbeddingFunction({ + googleApiKey: "", + }); const collection = await cc.createCollection({ name: "test-from-js", @@ -18,16 +20,16 @@ app.get("/", async (req, res) => { await collection.add({ ids: ["doc1", "doc2"], - documents: [ - "doc1", - "doc2", - ] + documents: ["doc1", "doc2"], }); let count = await collection.count(); console.log("count", count); - const googleQuery = new chroma.GoogleGenerativeAiEmbeddingFunction({ googleApiKey:"", taskType: 'RETRIEVAL_QUERY' }); + const googleQuery = new chroma.GoogleGenerativeAiEmbeddingFunction({ + googleApiKey: "", + taskType: "RETRIEVAL_QUERY", + }); const queryCollection = await cc.getCollection({ name: "test-from-js", @@ -36,16 +38,16 @@ app.get("/", async (req, res) => { const query = await collection.query({ queryTexts: ["doc1"], - nResults: 1 + nResults: 1, }); console.log("query", query); console.log("COMPLETED"); const collections = await cc.listCollections(); - console.log('collections', collections) + console.log("collections", collections); - res.send('Hello World!'); + res.send("Hello World!"); }); app.listen(3000, function () { console.log("Example app listening on port 3000!"); diff --git a/clients/js/package.json b/clients/js/package.json index 5fa81664bad..08ccf5da229 100644 --- a/clients/js/package.json +++ b/clients/js/package.json @@ -63,6 +63,7 @@ "db:run-auth-xtoken": "cd ../.. && echo \"CHROMA_SERVER_AUTH_TOKEN_TRANSPORT_HEADER=X_CHROMA_TOKEN\nCHROMA_SERVER_AUTH_CREDENTIALS=test-token\nCHROMA_SERVER_AUTH_CREDENTIALS_PROVIDER=chromadb.auth.token.TokenConfigServerAuthCredentialsProvider\nCHROMA_SERVER_AUTH_PROVIDER=chromadb.auth.token.TokenAuthServerProvider\\nCHROMA_PORT=8001\" > .chroma_env && docker-compose -f docker-compose.test-auth.yml --env-file ./.chroma_env up --detach && sleep 5", "prebuild": "rimraf dist", "build": "tsup", + "watch": "tsup --watch", "genapi": "./genapi.sh", "prettier": "prettier --write .", "release": "run-s build test:run && npm publish", @@ -72,8 +73,8 @@ "node": ">=14.17.0" }, "dependencies": { - "isomorphic-fetch": "^3.0.0", - "cliui": "^8.0.1" + "cliui": "^8.0.1", + "isomorphic-fetch": "^3.0.0" }, "peerDependencies": { "@google/generative-ai": "^0.1.1", diff --git a/clients/js/src/AdminClient.ts b/clients/js/src/AdminClient.ts index 7de713e8d4e..246f181710e 100644 --- a/clients/js/src/AdminClient.ts +++ b/clients/js/src/AdminClient.ts @@ -1,272 +1,266 @@ import { Configuration, ApiApi as DefaultApi } from "./generated"; import { handleSuccess, handleError, validateTenantDatabase } from "./utils"; -import { ConfigOptions } from './types'; +import { ConfigOptions } from "./types"; import { - AuthOptions, - ClientAuthProtocolAdapter, - IsomorphicFetchClientAuthProtocolAdapter + AuthOptions, + ClientAuthProtocolAdapter, + IsomorphicFetchClientAuthProtocolAdapter, } from "./auth"; -const DEFAULT_TENANT = "default_tenant" -const DEFAULT_DATABASE = "default_database" +const DEFAULT_TENANT = "default_tenant"; +const DEFAULT_DATABASE = "default_database"; // interface for tenant interface Tenant { - name: string, + name: string; } // interface for tenant interface Database { - name: string, + name: string; } export class AdminClient { - /** - * @ignore - */ - private api: DefaultApi & ConfigOptions; - private apiAdapter: ClientAuthProtocolAdapter|undefined; - public tenant: string = DEFAULT_TENANT; - public database: string = DEFAULT_DATABASE; + /** + * @ignore + */ + private api: DefaultApi & ConfigOptions; + private apiAdapter: ClientAuthProtocolAdapter | undefined; + public tenant: string = DEFAULT_TENANT; + public database: string = DEFAULT_DATABASE; - /** - * Creates a new AdminClient instance. - * @param {Object} params - The parameters for creating a new client - * @param {string} [params.path] - The base path for the Chroma API. - * @returns {AdminClient} A new AdminClient instance. - * - * @example - * ```typescript - * const client = new AdminClient({ - * path: "http://localhost:8000" - * }); - * ``` - */ - constructor({ - path, - fetchOptions, - auth, - tenant = DEFAULT_TENANT, - database = DEFAULT_DATABASE - }: { - path?: string, - fetchOptions?: RequestInit, - auth?: AuthOptions, - tenant?: string, - database?: string, - } = {}) { - if (path === undefined) path = "http://localhost:8000"; - this.tenant = tenant; - this.database = database; - - const apiConfig: Configuration = new Configuration({ - basePath: path, - }); - if (auth !== undefined) { - this.apiAdapter = new IsomorphicFetchClientAuthProtocolAdapter(new DefaultApi(apiConfig), auth); - this.api = this.apiAdapter.getApi(); - } else { - this.api = new DefaultApi(apiConfig); - } + /** + * Creates a new AdminClient instance. + * @param {Object} params - The parameters for creating a new client + * @param {string} [params.path] - The base path for the Chroma API. + * @returns {AdminClient} A new AdminClient instance. + * + * @example + * ```typescript + * const client = new AdminClient({ + * path: "http://localhost:8000" + * }); + * ``` + */ + constructor({ + path, + fetchOptions, + auth, + tenant = DEFAULT_TENANT, + database = DEFAULT_DATABASE, + }: { + path?: string; + fetchOptions?: RequestInit; + auth?: AuthOptions; + tenant?: string; + database?: string; + } = {}) { + if (path === undefined) path = "http://localhost:8000"; + this.tenant = tenant; + this.database = database; - this.api.options = fetchOptions ?? {}; + const apiConfig: Configuration = new Configuration({ + basePath: path, + }); + if (auth !== undefined) { + this.apiAdapter = new IsomorphicFetchClientAuthProtocolAdapter( + new DefaultApi(apiConfig), + auth, + ); + this.api = this.apiAdapter.getApi(); + } else { + this.api = new DefaultApi(apiConfig); } - /** - * Sets the tenant and database for the client. - * - * @param {Object} params - The parameters for setting tenant and database. - * @param {string} params.tenant - The name of the tenant. - * @param {string} params.database - The name of the database. - * - * @returns {Promise} A promise that returns nothing - * @throws {Error} Any issues - * - * @example - * ```typescript - * await adminClient.setTenant({ - * tenant: "my_tenant", - * database: "my_database", - * }); - * ``` - */ - public async setTenant({ - tenant = DEFAULT_TENANT, - database = DEFAULT_DATABASE - }: { - tenant: string, - database?: string, - }): Promise { - await validateTenantDatabase(this, tenant, database); - this.tenant = tenant; - this.database = database; - } + this.api.options = fetchOptions ?? {}; + } - /** - * Sets the database for the client. - * - * @param {Object} params - The parameters for setting the database. - * @param {string} params.database - The name of the database. - * - * @returns {Promise} A promise that returns nothing - * @throws {Error} Any issues - * - * @example - * ```typescript - * await adminClient.setDatabase({ - * database: "my_database", - * }); - * ``` - */ - public async setDatabase({ - database = DEFAULT_DATABASE - }: { - database?: string, - }): Promise { - await validateTenantDatabase(this, this.tenant, database); - this.database = database; - } + /** + * Sets the tenant and database for the client. + * + * @param {Object} params - The parameters for setting tenant and database. + * @param {string} params.tenant - The name of the tenant. + * @param {string} params.database - The name of the database. + * + * @returns {Promise} A promise that returns nothing + * @throws {Error} Any issues + * + * @example + * ```typescript + * await adminClient.setTenant({ + * tenant: "my_tenant", + * database: "my_database", + * }); + * ``` + */ + public async setTenant({ + tenant = DEFAULT_TENANT, + database = DEFAULT_DATABASE, + }: { + tenant: string; + database?: string; + }): Promise { + await validateTenantDatabase(this, tenant, database); + this.tenant = tenant; + this.database = database; + } - /** - * Creates a new tenant with the specified properties. - * - * @param {Object} params - The parameters for creating a new tenant. - * @param {string} params.name - The name of the tenant. - * - * @returns {Promise} A promise that resolves to the created tenant. - * @throws {Error} If there is an issue creating the tenant. - * - * @example - * ```typescript - * await adminClient.createTenant({ - * name: "my_tenant", - * }); - * ``` - */ - public async createTenant({ - name, - }: { - name: string, - }): Promise { - const newTenant = await this.api - .createTenant({name}, this.api.options) - .then(handleSuccess) - .catch(handleError); + /** + * Sets the database for the client. + * + * @param {Object} params - The parameters for setting the database. + * @param {string} params.database - The name of the database. + * + * @returns {Promise} A promise that returns nothing + * @throws {Error} Any issues + * + * @example + * ```typescript + * await adminClient.setDatabase({ + * database: "my_database", + * }); + * ``` + */ + public async setDatabase({ + database = DEFAULT_DATABASE, + }: { + database?: string; + }): Promise { + await validateTenantDatabase(this, this.tenant, database); + this.database = database; + } - // newTenant is null if successful - if (newTenant && newTenant.error) { - throw new Error(newTenant.error); - } + /** + * Creates a new tenant with the specified properties. + * + * @param {Object} params - The parameters for creating a new tenant. + * @param {string} params.name - The name of the tenant. + * + * @returns {Promise} A promise that resolves to the created tenant. + * @throws {Error} If there is an issue creating the tenant. + * + * @example + * ```typescript + * await adminClient.createTenant({ + * name: "my_tenant", + * }); + * ``` + */ + public async createTenant({ name }: { name: string }): Promise { + const newTenant = await this.api + .createTenant({ name }, this.api.options) + .then(handleSuccess) + .catch(handleError); - return {name: name} as Tenant + // newTenant is null if successful + if (newTenant && newTenant.error) { + throw new Error(newTenant.error); } - /** - * Gets a tenant with the specified properties. - * - * @param {Object} params - The parameters for getting a tenant. - * @param {string} params.name - The name of the tenant. - * - * @returns {Promise} A promise that resolves to the tenant. - * @throws {Error} If there is an issue getting the tenant. - * - * @example - * ```typescript - * await adminClient.getTenant({ - * name: "my_tenant", - * }); - * ``` - */ - public async getTenant({ - name, - }: { - name: string, - }): Promise { - const getTenant = await this.api - .getTenant(name, this.api.options) - .then(handleSuccess) - .catch(handleError); + return { name: name } as Tenant; + } - if (getTenant.error) { - throw new Error(getTenant.error); - } + /** + * Gets a tenant with the specified properties. + * + * @param {Object} params - The parameters for getting a tenant. + * @param {string} params.name - The name of the tenant. + * + * @returns {Promise} A promise that resolves to the tenant. + * @throws {Error} If there is an issue getting the tenant. + * + * @example + * ```typescript + * await adminClient.getTenant({ + * name: "my_tenant", + * }); + * ``` + */ + public async getTenant({ name }: { name: string }): Promise { + const getTenant = await this.api + .getTenant(name, this.api.options) + .then(handleSuccess) + .catch(handleError); - return {name: getTenant.name} as Tenant + if (getTenant.error) { + throw new Error(getTenant.error); } - /** - * Creates a new database with the specified properties. - * - * @param {Object} params - The parameters for creating a new database. - * @param {string} params.name - The name of the database. - * @param {string} params.tenantName - The name of the tenant. - * - * @returns {Promise} A promise that resolves to the created database. - * @throws {Error} If there is an issue creating the database. - * - * @example - * ```typescript - * await adminClient.createDatabase({ - * name: "my_database", - * tenantName: "my_tenant", - * }); - * ``` - */ - public async createDatabase({ - name, - tenantName - }: { - name: string, - tenantName: string, - }): Promise { - const newDatabase = await this.api - .createDatabase(tenantName, {name}, this.api.options) - .then(handleSuccess) - .catch(handleError); + return { name: getTenant.name } as Tenant; + } - // newDatabase is null if successful - if (newDatabase && newDatabase.error) { - throw new Error(newDatabase.error); - } + /** + * Creates a new database with the specified properties. + * + * @param {Object} params - The parameters for creating a new database. + * @param {string} params.name - The name of the database. + * @param {string} params.tenantName - The name of the tenant. + * + * @returns {Promise} A promise that resolves to the created database. + * @throws {Error} If there is an issue creating the database. + * + * @example + * ```typescript + * await adminClient.createDatabase({ + * name: "my_database", + * tenantName: "my_tenant", + * }); + * ``` + */ + public async createDatabase({ + name, + tenantName, + }: { + name: string; + tenantName: string; + }): Promise { + const newDatabase = await this.api + .createDatabase(tenantName, { name }, this.api.options) + .then(handleSuccess) + .catch(handleError); - return {name: name} as Database + // newDatabase is null if successful + if (newDatabase && newDatabase.error) { + throw new Error(newDatabase.error); } - /** - * Gets a database with the specified properties. - * - * @param {Object} params - The parameters for getting a database. - * @param {string} params.name - The name of the database. - * @param {string} params.tenantName - The name of the tenant. - * - * @returns {Promise} A promise that resolves to the database. - * @throws {Error} If there is an issue getting the database. - * - * @example - * ```typescript - * await adminClient.getDatabase({ - * name: "my_database", - * tenantName: "my_tenant", - * }); - * ``` - */ - public async getDatabase({ - name, - tenantName - }: { - name: string, - tenantName: string, - }): Promise { - const getDatabase = await this.api - .getDatabase(name, tenantName, this.api.options) - .then(handleSuccess) - .catch(handleError); + return { name: name } as Database; + } - if (getDatabase.error) { - throw new Error(getDatabase.error); - } + /** + * Gets a database with the specified properties. + * + * @param {Object} params - The parameters for getting a database. + * @param {string} params.name - The name of the database. + * @param {string} params.tenantName - The name of the tenant. + * + * @returns {Promise} A promise that resolves to the database. + * @throws {Error} If there is an issue getting the database. + * + * @example + * ```typescript + * await adminClient.getDatabase({ + * name: "my_database", + * tenantName: "my_tenant", + * }); + * ``` + */ + public async getDatabase({ + name, + tenantName, + }: { + name: string; + tenantName: string; + }): Promise { + const getDatabase = await this.api + .getDatabase(name, tenantName, this.api.options) + .then(handleSuccess) + .catch(handleError); - return {name: getDatabase.name} as Database + if (getDatabase.error) { + throw new Error(getDatabase.error); } + return { name: getDatabase.name } as Database; + } } diff --git a/clients/js/src/ChromaClient.ts b/clients/js/src/ChromaClient.ts index 76edd4e960e..0bc769b6ef6 100644 --- a/clients/js/src/ChromaClient.ts +++ b/clients/js/src/ChromaClient.ts @@ -1,327 +1,357 @@ -import { IEmbeddingFunction } from './embeddings/IEmbeddingFunction'; +import { IEmbeddingFunction } from "./embeddings/IEmbeddingFunction"; import { Configuration, ApiApi as DefaultApi } from "./generated"; import { handleSuccess, handleError } from "./utils"; -import { Collection } from './Collection'; -import { ChromaClientParams, CollectionMetadata, CollectionType, ConfigOptions, CreateCollectionParams, DeleteCollectionParams, GetCollectionParams, GetOrCreateCollectionParams, ListCollectionsParams } from './types'; +import { Collection } from "./Collection"; import { - AuthOptions, - ClientAuthProtocolAdapter, - IsomorphicFetchClientAuthProtocolAdapter + ChromaClientParams, + CollectionMetadata, + CollectionType, + ConfigOptions, + CreateCollectionParams, + DeleteCollectionParams, + GetCollectionParams, + GetOrCreateCollectionParams, + ListCollectionsParams, +} from "./types"; +import { + AuthOptions, + ClientAuthProtocolAdapter, + IsomorphicFetchClientAuthProtocolAdapter, } from "./auth"; -import { DefaultEmbeddingFunction } from './embeddings/DefaultEmbeddingFunction'; -import { AdminClient } from './AdminClient'; +import { DefaultEmbeddingFunction } from "./embeddings/DefaultEmbeddingFunction"; +import { AdminClient } from "./AdminClient"; -const DEFAULT_TENANT = "default_tenant" -const DEFAULT_DATABASE = "default_database" +const DEFAULT_TENANT = "default_tenant"; +const DEFAULT_DATABASE = "default_database"; export class ChromaClient { - /** - * @ignore - */ - private api: DefaultApi & ConfigOptions; - private apiAdapter: ClientAuthProtocolAdapter|undefined; - private tenant: string = DEFAULT_TENANT; - private database: string = DEFAULT_DATABASE; - private _adminClient?: AdminClient - - /** - * Creates a new ChromaClient instance. - * @param {Object} params - The parameters for creating a new client - * @param {string} [params.path] - The base path for the Chroma API. - * @returns {ChromaClient} A new ChromaClient instance. - * - * @example - * ```typescript - * const client = new ChromaClient({ - * path: "http://localhost:8000" - * }); - * ``` - */ - constructor({ - path, - fetchOptions, - auth, - tenant = DEFAULT_TENANT, - database = DEFAULT_DATABASE, - }: ChromaClientParams = {}) { - if (path === undefined) path = "http://localhost:8000"; - this.tenant = tenant; - this.database = database; - - const apiConfig: Configuration = new Configuration({ - basePath: path, - }); + /** + * @ignore + */ + private api: DefaultApi & ConfigOptions; + private apiAdapter: ClientAuthProtocolAdapter | undefined; + private tenant: string = DEFAULT_TENANT; + private database: string = DEFAULT_DATABASE; + private _adminClient?: AdminClient; - if (auth !== undefined) { - this.apiAdapter = new IsomorphicFetchClientAuthProtocolAdapter(new DefaultApi(apiConfig), auth); - this.api = this.apiAdapter.getApi(); - } else { - this.api = new DefaultApi(apiConfig); - } + /** + * Creates a new ChromaClient instance. + * @param {Object} params - The parameters for creating a new client + * @param {string} [params.path] - The base path for the Chroma API. + * @returns {ChromaClient} A new ChromaClient instance. + * + * @example + * ```typescript + * const client = new ChromaClient({ + * path: "http://localhost:8000" + * }); + * ``` + */ + constructor({ + path, + fetchOptions, + auth, + tenant = DEFAULT_TENANT, + database = DEFAULT_DATABASE, + }: ChromaClientParams = {}) { + if (path === undefined) path = "http://localhost:8000"; + this.tenant = tenant; + this.database = database; - this._adminClient = new AdminClient({ - path: path, - fetchOptions: fetchOptions, - auth: auth, - tenant: tenant, - database: database - }); + const apiConfig: Configuration = new Configuration({ + basePath: path, + }); - // TODO: Validate tenant and database on client creation - // this got tricky because: - // - the constructor is sync but the generated api is async - // - we need to inject auth information so a simple rewrite/fetch does not work - - this.api.options = fetchOptions ?? {}; + if (auth !== undefined) { + this.apiAdapter = new IsomorphicFetchClientAuthProtocolAdapter( + new DefaultApi(apiConfig), + auth, + ); + this.api = this.apiAdapter.getApi(); + } else { + this.api = new DefaultApi(apiConfig); } - /** - * Resets the state of the object by making an API call to the reset endpoint. - * - * @returns {Promise} A promise that resolves when the reset operation is complete. - * @throws {Error} If there is an issue resetting the state. - * - * @example - * ```typescript - * await client.reset(); - * ``` - */ - public async reset(): Promise { - return await this.api.reset(this.api.options); - } + this._adminClient = new AdminClient({ + path: path, + fetchOptions: fetchOptions, + auth: auth, + tenant: tenant, + database: database, + }); - /** - * Returns the version of the Chroma API. - * @returns {Promise} A promise that resolves to the version of the Chroma API. - * - * @example - * ```typescript - * const version = await client.version(); - * ``` - */ - public async version(): Promise { - const response = await this.api.version(this.api.options); - return await handleSuccess(response); - } + // TODO: Validate tenant and database on client creation + // this got tricky because: + // - the constructor is sync but the generated api is async + // - we need to inject auth information so a simple rewrite/fetch does not work - /** - * Returns a heartbeat from the Chroma API. - * @returns {Promise} A promise that resolves to the heartbeat from the Chroma API. - * - * @example - * ```typescript - * const heartbeat = await client.heartbeat(); - * ``` - */ - public async heartbeat(): Promise { - const response = await this.api.heartbeat(this.api.options); - let ret = await handleSuccess(response); - return ret["nanosecond heartbeat"] - } + this.api.options = fetchOptions ?? {}; + } - /** - * Creates a new collection with the specified properties. - * - * @param {Object} params - The parameters for creating a new collection. - * @param {string} params.name - The name of the collection. - * @param {CollectionMetadata} [params.metadata] - Optional metadata associated with the collection. - * @param {IEmbeddingFunction} [params.embeddingFunction] - Optional custom embedding function for the collection. - * - * @returns {Promise} A promise that resolves to the created collection. - * @throws {Error} If there is an issue creating the collection. - * - * @example - * ```typescript - * const collection = await client.createCollection({ - * name: "my_collection", - * metadata: { - * "description": "My first collection" - * } - * }); - * ``` - */ - public async createCollection({ - name, - metadata, - embeddingFunction - }: CreateCollectionParams): Promise { + /** + * Resets the state of the object by making an API call to the reset endpoint. + * + * @returns {Promise} A promise that resolves when the reset operation is complete. + * @throws {Error} If there is an issue resetting the state. + * + * @example + * ```typescript + * await client.reset(); + * ``` + */ + public async reset(): Promise { + return await this.api.reset(this.api.options); + } - if (embeddingFunction === undefined) { - embeddingFunction = new DefaultEmbeddingFunction(); - } - - const newCollection = await this.api - .createCollection(this.tenant, this.database, { - name, - metadata, - }, this.api.options) - .then(handleSuccess) - .catch(handleError); + /** + * Returns the version of the Chroma API. + * @returns {Promise} A promise that resolves to the version of the Chroma API. + * + * @example + * ```typescript + * const version = await client.version(); + * ``` + */ + public async version(): Promise { + const response = await this.api.version(this.api.options); + return await handleSuccess(response); + } - if (newCollection.error) { - throw new Error(newCollection.error); - } + /** + * Returns a heartbeat from the Chroma API. + * @returns {Promise} A promise that resolves to the heartbeat from the Chroma API. + * + * @example + * ```typescript + * const heartbeat = await client.heartbeat(); + * ``` + */ + public async heartbeat(): Promise { + const response = await this.api.heartbeat(this.api.options); + let ret = await handleSuccess(response); + return ret["nanosecond heartbeat"]; + } - return new Collection(name, newCollection.id, this.api, metadata, embeddingFunction); + /** + * Creates a new collection with the specified properties. + * + * @param {Object} params - The parameters for creating a new collection. + * @param {string} params.name - The name of the collection. + * @param {CollectionMetadata} [params.metadata] - Optional metadata associated with the collection. + * @param {IEmbeddingFunction} [params.embeddingFunction] - Optional custom embedding function for the collection. + * + * @returns {Promise} A promise that resolves to the created collection. + * @throws {Error} If there is an issue creating the collection. + * + * @example + * ```typescript + * const collection = await client.createCollection({ + * name: "my_collection", + * metadata: { + * "description": "My first collection" + * } + * }); + * ``` + */ + public async createCollection({ + name, + metadata, + embeddingFunction, + }: CreateCollectionParams): Promise { + if (embeddingFunction === undefined) { + embeddingFunction = new DefaultEmbeddingFunction(); } - /** - * Gets or creates a collection with the specified properties. - * - * @param {Object} params - The parameters for creating a new collection. - * @param {string} params.name - The name of the collection. - * @param {CollectionMetadata} [params.metadata] - Optional metadata associated with the collection. - * @param {IEmbeddingFunction} [params.embeddingFunction] - Optional custom embedding function for the collection. - * - * @returns {Promise} A promise that resolves to the got or created collection. - * @throws {Error} If there is an issue getting or creating the collection. - * - * @example - * ```typescript - * const collection = await client.getOrCreateCollection({ - * name: "my_collection", - * metadata: { - * "description": "My first collection" - * } - * }); - * ``` - */ - public async getOrCreateCollection({ - name, - metadata, - embeddingFunction - }: GetOrCreateCollectionParams): Promise { + const newCollection = await this.api + .createCollection( + this.tenant, + this.database, + { + name, + metadata, + }, + this.api.options, + ) + .then(handleSuccess) + .catch(handleError); - if (embeddingFunction === undefined) { - embeddingFunction = new DefaultEmbeddingFunction(); - } - - const newCollection = await this.api - .createCollection(this.tenant, this.database, { - name, - metadata, - 'get_or_create': true - }, this.api.options) - .then(handleSuccess) - .catch(handleError); + if (newCollection.error) { + throw new Error(newCollection.error); + } - if (newCollection.error) { - throw new Error(newCollection.error); - } + return new Collection( + name, + newCollection.id, + this.api, + metadata, + embeddingFunction, + ); + } - return new Collection( - name, - newCollection.id, - this.api, - newCollection.metadata, - embeddingFunction - ); + /** + * Gets or creates a collection with the specified properties. + * + * @param {Object} params - The parameters for creating a new collection. + * @param {string} params.name - The name of the collection. + * @param {CollectionMetadata} [params.metadata] - Optional metadata associated with the collection. + * @param {IEmbeddingFunction} [params.embeddingFunction] - Optional custom embedding function for the collection. + * + * @returns {Promise} A promise that resolves to the got or created collection. + * @throws {Error} If there is an issue getting or creating the collection. + * + * @example + * ```typescript + * const collection = await client.getOrCreateCollection({ + * name: "my_collection", + * metadata: { + * "description": "My first collection" + * } + * }); + * ``` + */ + public async getOrCreateCollection({ + name, + metadata, + embeddingFunction, + }: GetOrCreateCollectionParams): Promise { + if (embeddingFunction === undefined) { + embeddingFunction = new DefaultEmbeddingFunction(); } - /** - * Lists all collections. - * - * @returns {Promise} A promise that resolves to a list of collection names. - * @param {PositiveInteger} [params.limit] - Optional limit on the number of items to get. - * @param {PositiveInteger} [params.offset] - Optional offset on the items to get. - * @throws {Error} If there is an issue listing the collections. - * - * @example - * ```typescript - * const collections = await client.listCollections({ - * limit: 10, - * offset: 0, - * }); - * ``` - */ - public async listCollections({ - limit, - offset, - }: ListCollectionsParams = {}): Promise { - const response = await this.api.listCollections( - this.tenant, - this.database, - limit, - offset, - this.api.options); - return handleSuccess(response); - } + const newCollection = await this.api + .createCollection( + this.tenant, + this.database, + { + name, + metadata, + get_or_create: true, + }, + this.api.options, + ) + .then(handleSuccess) + .catch(handleError); - /** - * Counts all collections. - * - * @returns {Promise} A promise that resolves to the number of collections. - * @throws {Error} If there is an issue counting the collections. - * - * @example - * ```typescript - * const collections = await client.countCollections(); - * ``` - */ - public async countCollections(): Promise { - const response = await this.api.countCollections(this.tenant, this.database, this.api.options); - return handleSuccess(response); + if (newCollection.error) { + throw new Error(newCollection.error); } - /** - * Gets a collection with the specified name. - * @param {Object} params - The parameters for getting a collection. - * @param {string} params.name - The name of the collection. - * @param {IEmbeddingFunction} [params.embeddingFunction] - Optional custom embedding function for the collection. - * @returns {Promise} A promise that resolves to the collection. - * @throws {Error} If there is an issue getting the collection. - * - * @example - * ```typescript - * const collection = await client.getCollection({ - * name: "my_collection" - * }); - * ``` - */ - public async getCollection({ - name, - embeddingFunction - }: GetCollectionParams): Promise { - const response = await this.api - .getCollection(name, this.tenant, this.database, this.api.options) - .then(handleSuccess) - .catch(handleError); + return new Collection( + name, + newCollection.id, + this.api, + newCollection.metadata, + embeddingFunction, + ); + } - if (response.error) { - throw new Error(response.error); - } + /** + * Lists all collections. + * + * @returns {Promise} A promise that resolves to a list of collection names. + * @param {PositiveInteger} [params.limit] - Optional limit on the number of items to get. + * @param {PositiveInteger} [params.offset] - Optional offset on the items to get. + * @throws {Error} If there is an issue listing the collections. + * + * @example + * ```typescript + * const collections = await client.listCollections({ + * limit: 10, + * offset: 0, + * }); + * ``` + */ + public async listCollections({ + limit, + offset, + }: ListCollectionsParams = {}): Promise { + const response = await this.api.listCollections( + this.tenant, + this.database, + limit, + offset, + this.api.options, + ); + return handleSuccess(response); + } - return new Collection( - response.name, - response.id, - this.api, - response.metadata, - embeddingFunction - ); + /** + * Counts all collections. + * + * @returns {Promise} A promise that resolves to the number of collections. + * @throws {Error} If there is an issue counting the collections. + * + * @example + * ```typescript + * const collections = await client.countCollections(); + * ``` + */ + public async countCollections(): Promise { + const response = await this.api.countCollections( + this.tenant, + this.database, + this.api.options, + ); + return handleSuccess(response); + } - } + /** + * Gets a collection with the specified name. + * @param {Object} params - The parameters for getting a collection. + * @param {string} params.name - The name of the collection. + * @param {IEmbeddingFunction} [params.embeddingFunction] - Optional custom embedding function for the collection. + * @returns {Promise} A promise that resolves to the collection. + * @throws {Error} If there is an issue getting the collection. + * + * @example + * ```typescript + * const collection = await client.getCollection({ + * name: "my_collection" + * }); + * ``` + */ + public async getCollection({ + name, + embeddingFunction, + }: GetCollectionParams): Promise { + const response = await this.api + .getCollection(name, this.tenant, this.database, this.api.options) + .then(handleSuccess) + .catch(handleError); - /** - * Deletes a collection with the specified name. - * @param {Object} params - The parameters for deleting a collection. - * @param {string} params.name - The name of the collection. - * @returns {Promise} A promise that resolves when the collection is deleted. - * @throws {Error} If there is an issue deleting the collection. - * - * @example - * ```typescript - * await client.deleteCollection({ - * name: "my_collection" - * }); - * ``` - */ - public async deleteCollection({ - name - }: DeleteCollectionParams): Promise { - return await this.api - .deleteCollection(name, this.tenant, this.database, this.api.options) - .then(handleSuccess) - .catch(handleError); + if (response.error) { + throw new Error(response.error); } + return new Collection( + response.name, + response.id, + this.api, + response.metadata, + embeddingFunction, + ); + } + + /** + * Deletes a collection with the specified name. + * @param {Object} params - The parameters for deleting a collection. + * @param {string} params.name - The name of the collection. + * @returns {Promise} A promise that resolves when the collection is deleted. + * @throws {Error} If there is an issue deleting the collection. + * + * @example + * ```typescript + * await client.deleteCollection({ + * name: "my_collection" + * }); + * ``` + */ + public async deleteCollection({ + name, + }: DeleteCollectionParams): Promise { + return await this.api + .deleteCollection(name, this.tenant, this.database, this.api.options) + .then(handleSuccess) + .catch(handleError); + } } diff --git a/clients/js/src/CloudClient.ts b/clients/js/src/CloudClient.ts index 9ce77d2f59d..f2b4f2addf2 100644 --- a/clients/js/src/CloudClient.ts +++ b/clients/js/src/CloudClient.ts @@ -1,46 +1,44 @@ - // create a cloudclient class that takes in an api key and an optional database // this should wrap ChromaClient and specify the auth scheme correctly import { ChromaClient } from "./ChromaClient"; interface CloudClientParams { - apiKey?: string; - database?: string; - cloudHost?: string; - cloudPort?: string; + apiKey?: string; + database?: string; + cloudHost?: string; + cloudPort?: string; } -class CloudClient extends ChromaClient{ - - constructor({apiKey, database, cloudHost, cloudPort}: CloudClientParams) { - // If no API key is provided, try to load it from the environment variable - if (!apiKey) { - apiKey = process.env.CHROMA_API_KEY; - } - if (!apiKey) { - throw new Error("No API key provided"); - } +class CloudClient extends ChromaClient { + constructor({ apiKey, database, cloudHost, cloudPort }: CloudClientParams) { + // If no API key is provided, try to load it from the environment variable + if (!apiKey) { + apiKey = process.env.CHROMA_API_KEY; + } + if (!apiKey) { + throw new Error("No API key provided"); + } - cloudHost = cloudHost || "https://api.trychroma.com"; - cloudPort = cloudPort || "8000"; + cloudHost = cloudHost || "https://api.trychroma.com"; + cloudPort = cloudPort || "8000"; - const path = `${cloudHost}:${cloudPort}`; + const path = `${cloudHost}:${cloudPort}`; - const auth = { - provider: "token", - credentials: apiKey, - providerOptions: { headerType: "X_CHROMA_TOKEN" }, - } + const auth = { + provider: "token", + credentials: apiKey, + providerOptions: { headerType: "X_CHROMA_TOKEN" }, + }; - return new ChromaClient({ - path: path, - auth: auth, - database: database, - }) + return new ChromaClient({ + path: path, + auth: auth, + database: database, + }); - super() - } + super(); + } } export { CloudClient }; diff --git a/clients/js/src/Collection.ts b/clients/js/src/Collection.ts index 82fe3facbd9..430d444bc8a 100644 --- a/clients/js/src/Collection.ts +++ b/clients/js/src/Collection.ts @@ -1,540 +1,551 @@ import { - GetResponse, - QueryResponse, - AddResponse, - CollectionMetadata, - ConfigOptions, - GetParams, - AddParams, - UpsertParams, - ModifyCollectionParams, - UpdateParams, - QueryParams, - PeekParams, - DeleteParams + GetResponse, + QueryResponse, + AddResponse, + CollectionMetadata, + ConfigOptions, + GetParams, + AddParams, + UpsertParams, + ModifyCollectionParams, + UpdateParams, + QueryParams, + PeekParams, + DeleteParams, } from "./types"; -import { IEmbeddingFunction } from './embeddings/IEmbeddingFunction'; +import { IEmbeddingFunction } from "./embeddings/IEmbeddingFunction"; import { ApiApi as DefaultApi } from "./generated"; import { handleError, handleSuccess } from "./utils"; import { toArray, toArrayOfArrays } from "./utils"; - export class Collection { - public name: string; - public id: string; - public metadata: CollectionMetadata | undefined; - /** - * @ignore - */ - private api: DefaultApi & ConfigOptions; - /** - * @ignore - */ - public embeddingFunction: IEmbeddingFunction | undefined; - - /** - * @ignore - */ - constructor( - name: string, - id: string, - api: DefaultApi, - metadata?: CollectionMetadata, - embeddingFunction?: IEmbeddingFunction - ) { - this.name = name; - this.id = id; - this.metadata = metadata; - this.api = api; - if (embeddingFunction !== undefined) - this.embeddingFunction = embeddingFunction; + public name: string; + public id: string; + public metadata: CollectionMetadata | undefined; + /** + * @ignore + */ + private api: DefaultApi & ConfigOptions; + /** + * @ignore + */ + public embeddingFunction: IEmbeddingFunction | undefined; + + /** + * @ignore + */ + constructor( + name: string, + id: string, + api: DefaultApi, + metadata?: CollectionMetadata, + embeddingFunction?: IEmbeddingFunction, + ) { + this.name = name; + this.id = id; + this.metadata = metadata; + this.api = api; + if (embeddingFunction !== undefined) + this.embeddingFunction = embeddingFunction; + } + + /** + * @ignore + */ + private setName(name: string): void { + this.name = name; + } + /** + * @ignore + */ + private setMetadata(metadata: CollectionMetadata | undefined): void { + this.metadata = metadata; + } + + /** + * @ignore + */ + private async validate( + require_embeddings_or_documents: boolean, // set to false in the case of Update + ids: string | string[], + embeddings: number[] | number[][] | undefined, + metadatas?: object | object[], + documents?: string | string[], + ) { + if (require_embeddings_or_documents) { + if (embeddings === undefined && documents === undefined) { + throw new Error("embeddings and documents cannot both be undefined"); + } } - /** - * @ignore - */ - private setName(name: string): void { - this.name = name; - } - /** - * @ignore - */ - private setMetadata(metadata: CollectionMetadata | undefined): void { - this.metadata = metadata; + if (embeddings === undefined && documents !== undefined) { + const documentsArray = toArray(documents); + if (this.embeddingFunction !== undefined) { + embeddings = await this.embeddingFunction.generate(documentsArray); + } else { + throw new Error( + "embeddingFunction is undefined. Please configure an embedding function", + ); + } } + if (embeddings === undefined) + throw new Error("embeddings is undefined but shouldnt be"); - /** - * @ignore - */ - private async validate( - require_embeddings_or_documents: boolean, // set to false in the case of Update - ids: string | string[], - embeddings: number[] | number[][] | undefined, - metadatas?: object | object[], - documents?: string | string[], - ) { + const idsArray = toArray(ids); + const embeddingsArray: number[][] = toArrayOfArrays(embeddings); - if (require_embeddings_or_documents) { - if ((embeddings === undefined) && (documents === undefined)) { - throw new Error( - "embeddings and documents cannot both be undefined", - ); - } - } - - if ((embeddings === undefined) && (documents !== undefined)) { - const documentsArray = toArray(documents); - if (this.embeddingFunction !== undefined) { - embeddings = await this.embeddingFunction.generate(documentsArray); - } else { - throw new Error( - "embeddingFunction is undefined. Please configure an embedding function" - ); - } - } - if (embeddings === undefined) - throw new Error("embeddings is undefined but shouldnt be"); - - const idsArray = toArray(ids); - const embeddingsArray: number[][] = toArrayOfArrays(embeddings); - - let metadatasArray: object[] | undefined; - if (metadatas === undefined) { - metadatasArray = undefined; - } else { - metadatasArray = toArray(metadatas); - } - - let documentsArray: (string | undefined)[] | undefined; - if (documents === undefined) { - documentsArray = undefined; - } else { - documentsArray = toArray(documents); - } - - // validate all ids are strings - for (let i = 0; i < idsArray.length; i += 1) { - if (typeof idsArray[i] !== "string") { - throw new Error( - `Expected ids to be strings, found ${typeof idsArray[i]} at index ${i}` - ); - } - } - - if ( - (embeddingsArray !== undefined && - idsArray.length !== embeddingsArray.length) || - (metadatasArray !== undefined && - idsArray.length !== metadatasArray.length) || - (documentsArray !== undefined && - idsArray.length !== documentsArray.length) - ) { - throw new Error( - "ids, embeddings, metadatas, and documents must all be the same length" - ); - } - - const uniqueIds = new Set(idsArray); - if (uniqueIds.size !== idsArray.length) { - const duplicateIds = idsArray.filter((item, index) => idsArray.indexOf(item) !== index); - throw new Error( - `Expected IDs to be unique, found duplicates for: ${duplicateIds}`, - ); - } - - return [idsArray, embeddingsArray, metadatasArray, documentsArray] + let metadatasArray: object[] | undefined; + if (metadatas === undefined) { + metadatasArray = undefined; + } else { + metadatasArray = toArray(metadatas); } - /** - * Add items to the collection - * @param {Object} params - The parameters for the query. - * @param {ID | IDs} [params.ids] - IDs of the items to add. - * @param {Embedding | Embeddings} [params.embeddings] - Optional embeddings of the items to add. - * @param {Metadata | Metadatas} [params.metadatas] - Optional metadata of the items to add. - * @param {Document | Documents} [params.documents] - Optional documents of the items to add. - * @returns {Promise} - The response from the API. True if successful. - * - * @example - * ```typescript - * const response = await collection.add({ - * ids: ["id1", "id2"], - * embeddings: [[1, 2, 3], [4, 5, 6]], - * metadatas: [{ "key": "value" }, { "key": "value" }], - * documents: ["document1", "document2"] - * }); - * ``` - */ - public async add({ - ids, - embeddings, - metadatas, - documents, - }: AddParams): Promise { - - const [idsArray, embeddingsArray, metadatasArray, documentsArray] = await this.validate( - true, - ids, - embeddings, - metadatas, - documents - ) - - const response = await this.api.add(this.id, - { - // @ts-ignore - ids: idsArray, - embeddings: embeddingsArray as number[][], // We know this is defined because of the validate function - // @ts-ignore - documents: documentsArray, - // @ts-ignore - metadatas: metadatasArray, - }, this.api.options) - .then(handleSuccess) - .catch(handleError); - - return response - } - - /** - * Upsert items to the collection - * @param {Object} params - The parameters for the query. - * @param {ID | IDs} [params.ids] - IDs of the items to add. - * @param {Embedding | Embeddings} [params.embeddings] - Optional embeddings of the items to add. - * @param {Metadata | Metadatas} [params.metadatas] - Optional metadata of the items to add. - * @param {Document | Documents} [params.documents] - Optional documents of the items to add. - * @returns {Promise} - The response from the API. True if successful. - * - * @example - * ```typescript - * const response = await collection.upsert({ - * ids: ["id1", "id2"], - * embeddings: [[1, 2, 3], [4, 5, 6]], - * metadatas: [{ "key": "value" }, { "key": "value" }], - * documents: ["document1", "document2"], - * }); - * ``` - */ - public async upsert({ - ids, - embeddings, - metadatas, - documents, - }: UpsertParams): Promise { - const [idsArray, embeddingsArray, metadatasArray, documentsArray] = await this.validate( - true, - ids, - embeddings, - metadatas, - documents - ) - - const response = await this.api.upsert(this.id, - { - //@ts-ignore - ids: idsArray, - embeddings: embeddingsArray as number[][], // We know this is defined because of the validate function - //@ts-ignore - documents: documentsArray, - //@ts-ignore - metadatas: metadatasArray, - }, - this.api.options - ) - .then(handleSuccess) - .catch(handleError); - - return response - + let documentsArray: (string | undefined)[] | undefined; + if (documents === undefined) { + documentsArray = undefined; + } else { + documentsArray = toArray(documents); } - /** - * Count the number of items in the collection - * @returns {Promise} - The response from the API. - * - * @example - * ```typescript - * const response = await collection.count(); - * ``` - */ - public async count(): Promise { - const response = await this.api.count(this.id, this.api.options); - return handleSuccess(response); + // validate all ids are strings + for (let i = 0; i < idsArray.length; i += 1) { + if (typeof idsArray[i] !== "string") { + throw new Error( + `Expected ids to be strings, found ${typeof idsArray[ + i + ]} at index ${i}`, + ); + } } - /** - * Modify the collection name or metadata - * @param {Object} params - The parameters for the query. - * @param {string} [params.name] - Optional new name for the collection. - * @param {CollectionMetadata} [params.metadata] - Optional new metadata for the collection. - * @returns {Promise} - The response from the API. - * - * @example - * ```typescript - * const response = await collection.modify({ - * name: "new name", - * metadata: { "key": "value" }, - * }); - * ``` - */ - public async modify({ - name, - metadata - }: ModifyCollectionParams = {}): Promise { - const response = await this.api - .updateCollection( - this.id, - { - new_name: name, - new_metadata: metadata, - }, - this.api.options - ) - .then(handleSuccess) - .catch(handleError); - - this.setName(name || this.name); - this.setMetadata(metadata || this.metadata); - - return response; - } - - /** - * Get items from the collection - * @param {Object} params - The parameters for the query. - * @param {ID | IDs} [params.ids] - Optional IDs of the items to get. - * @param {Where} [params.where] - Optional where clause to filter items by. - * @param {PositiveInteger} [params.limit] - Optional limit on the number of items to get. - * @param {PositiveInteger} [params.offset] - Optional offset on the items to get. - * @param {IncludeEnum[]} [params.include] - Optional list of items to include in the response. - * @param {WhereDocument} [params.whereDocument] - Optional where clause to filter items by. - * @returns {Promise} - The response from the server. - * - * @example - * ```typescript - * const response = await collection.get({ - * ids: ["id1", "id2"], - * where: { "key": "value" }, - * limit: 10, - * offset: 0, - * include: ["embeddings", "metadatas", "documents"], - * whereDocument: { $contains: "value" }, - * }); - * ``` - */ - public async get({ - ids, - where, - limit, - offset, - include, - whereDocument, - }: GetParams = {}): Promise { - let idsArray = undefined; - if (ids !== undefined) idsArray = toArray(ids); - - return await this.api - .aGet(this.id, { - ids: idsArray, - where, - limit, - offset, - //@ts-ignore - include, - where_document: whereDocument, - }, this.api.options) - .then(handleSuccess) - .catch(handleError); - } - - /** - * Update the embeddings, documents, and/or metadatas of existing items - * @param {Object} params - The parameters for the query. - * @param {ID | IDs} [params.ids] - The IDs of the items to update. - * @param {Embedding | Embeddings} [params.embeddings] - Optional embeddings to update. - * @param {Metadata | Metadatas} [params.metadatas] - Optional metadatas to update. - * @param {Document | Documents} [params.documents] - Optional documents to update. - * @returns {Promise} - The API Response. True if successful. Else, error. - * - * @example - * ```typescript - * const response = await collection.update({ - * ids: ["id1", "id2"], - * embeddings: [[1, 2, 3], [4, 5, 6]], - * metadatas: [{ "key": "value" }, { "key": "value" }], - * documents: ["new document 1", "new document 2"], - * }); - * ``` - */ - public async update({ - ids, - embeddings, - metadatas, - documents, - }: UpdateParams): Promise { - if ( - embeddings === undefined && - documents === undefined && - metadatas === undefined - ) { - throw new Error( - "embeddings, documents, and metadatas cannot all be undefined" - ); - } else if (embeddings === undefined && documents !== undefined) { - const documentsArray = toArray(documents); - if (this.embeddingFunction !== undefined) { - embeddings = await this.embeddingFunction.generate(documentsArray); - } else { - throw new Error( - "embeddingFunction is undefined. Please configure an embedding function" - ); - } - } - - // backend expects None if metadatas is undefined - if (metadatas !== undefined) metadatas = toArray(metadatas); - if (documents !== undefined) documents = toArray(documents); - - var resp = await this.api - .update( - this.id, - { - ids: toArray(ids), - embeddings: embeddings ? toArrayOfArrays(embeddings) : undefined, - documents: documents, - metadatas: metadatas - }, - this.api.options - ) - .then(handleSuccess) - .catch(handleError); - - return resp; + if ( + (embeddingsArray !== undefined && + idsArray.length !== embeddingsArray.length) || + (metadatasArray !== undefined && + idsArray.length !== metadatasArray.length) || + (documentsArray !== undefined && + idsArray.length !== documentsArray.length) + ) { + throw new Error( + "ids, embeddings, metadatas, and documents must all be the same length", + ); } - /** - * Performs a query on the collection using the specified parameters. - * - * @param {Object} params - The parameters for the query. - * @param {Embedding | Embeddings} [params.queryEmbeddings] - Optional query embeddings to use for the search. - * @param {PositiveInteger} [params.nResults] - Optional number of results to return (default is 10). - * @param {Where} [params.where] - Optional query condition to filter results based on metadata values. - * @param {string | string[]} [params.queryTexts] - Optional query text(s) to search for in the collection. - * @param {WhereDocument} [params.whereDocument] - Optional query condition to filter results based on document content. - * @param {IncludeEnum[]} [params.include] - Optional array of fields to include in the result, such as "metadata" and "document". - * - * @returns {Promise} A promise that resolves to the query results. - * @throws {Error} If there is an issue executing the query. - * @example - * // Query the collection using embeddings - * const results = await collection.query({ - * queryEmbeddings: [[0.1, 0.2, ...], ...], - * nResults: 10, - * where: {"name": {"$eq": "John Doe"}}, - * include: ["metadata", "document"] - * }); - * @example - * ```js - * // Query the collection using query text - * const results = await collection.query({ - * queryTexts: "some text", - * nResults: 10, - * where: {"name": {"$eq": "John Doe"}}, - * include: ["metadata", "document"] - * }); - * ``` - * - */ - public async query({ - queryEmbeddings, - nResults, - where, - queryTexts, - whereDocument, - include, - }: QueryParams): Promise { - if (nResults === undefined) nResults = 10 - if (queryEmbeddings === undefined && queryTexts === undefined) { - throw new Error( - "queryEmbeddings and queryTexts cannot both be undefined" - ); - } else if (queryEmbeddings === undefined && queryTexts !== undefined) { - const queryTextsArray = toArray(queryTexts); - if (this.embeddingFunction !== undefined) { - queryEmbeddings = await this.embeddingFunction.generate(queryTextsArray); - } else { - throw new Error( - "embeddingFunction is undefined. Please configure an embedding function" - ); - } - } - if (queryEmbeddings === undefined) - throw new Error("embeddings is undefined but shouldnt be"); - - const query_embeddingsArray = toArrayOfArrays(queryEmbeddings); - - return await this.api - .getNearestNeighbors(this.id, { - query_embeddings: query_embeddingsArray, - where, - n_results: nResults, - where_document: whereDocument, - //@ts-ignore - include: include, - }, this.api.options) - .then(handleSuccess) - .catch(handleError); + const uniqueIds = new Set(idsArray); + if (uniqueIds.size !== idsArray.length) { + const duplicateIds = idsArray.filter( + (item, index) => idsArray.indexOf(item) !== index, + ); + throw new Error( + `Expected IDs to be unique, found duplicates for: ${duplicateIds}`, + ); } - /** - * Peek inside the collection - * @param {Object} params - The parameters for the query. - * @param {PositiveInteger} [params.limit] - Optional number of results to return (default is 10). - * @returns {Promise} A promise that resolves to the query results. - * @throws {Error} If there is an issue executing the query. - * - * @example - * ```typescript - * const results = await collection.peek({ - * limit: 10 - * }); - * ``` - */ - public async peek({ limit }: PeekParams = {}): Promise { - if (limit === undefined) limit = 10; - const response = await this.api.aGet(this.id, { - limit: limit, - }, this.api.options); - return handleSuccess(response); + return [idsArray, embeddingsArray, metadatasArray, documentsArray]; + } + + /** + * Add items to the collection + * @param {Object} params - The parameters for the query. + * @param {ID | IDs} [params.ids] - IDs of the items to add. + * @param {Embedding | Embeddings} [params.embeddings] - Optional embeddings of the items to add. + * @param {Metadata | Metadatas} [params.metadatas] - Optional metadata of the items to add. + * @param {Document | Documents} [params.documents] - Optional documents of the items to add. + * @returns {Promise} - The response from the API. True if successful. + * + * @example + * ```typescript + * const response = await collection.add({ + * ids: ["id1", "id2"], + * embeddings: [[1, 2, 3], [4, 5, 6]], + * metadatas: [{ "key": "value" }, { "key": "value" }], + * documents: ["document1", "document2"] + * }); + * ``` + */ + public async add({ + ids, + embeddings, + metadatas, + documents, + }: AddParams): Promise { + const [idsArray, embeddingsArray, metadatasArray, documentsArray] = + await this.validate(true, ids, embeddings, metadatas, documents); + + const response = await this.api + .add( + this.id, + { + // @ts-ignore + ids: idsArray, + embeddings: embeddingsArray as number[][], // We know this is defined because of the validate function + // @ts-ignore + documents: documentsArray, + // @ts-ignore + metadatas: metadatasArray, + }, + this.api.options, + ) + .then(handleSuccess) + .catch(handleError); + + return response; + } + + /** + * Upsert items to the collection + * @param {Object} params - The parameters for the query. + * @param {ID | IDs} [params.ids] - IDs of the items to add. + * @param {Embedding | Embeddings} [params.embeddings] - Optional embeddings of the items to add. + * @param {Metadata | Metadatas} [params.metadatas] - Optional metadata of the items to add. + * @param {Document | Documents} [params.documents] - Optional documents of the items to add. + * @returns {Promise} - The response from the API. True if successful. + * + * @example + * ```typescript + * const response = await collection.upsert({ + * ids: ["id1", "id2"], + * embeddings: [[1, 2, 3], [4, 5, 6]], + * metadatas: [{ "key": "value" }, { "key": "value" }], + * documents: ["document1", "document2"], + * }); + * ``` + */ + public async upsert({ + ids, + embeddings, + metadatas, + documents, + }: UpsertParams): Promise { + const [idsArray, embeddingsArray, metadatasArray, documentsArray] = + await this.validate(true, ids, embeddings, metadatas, documents); + + const response = await this.api + .upsert( + this.id, + { + //@ts-ignore + ids: idsArray, + embeddings: embeddingsArray as number[][], // We know this is defined because of the validate function + //@ts-ignore + documents: documentsArray, + //@ts-ignore + metadatas: metadatasArray, + }, + this.api.options, + ) + .then(handleSuccess) + .catch(handleError); + + return response; + } + + /** + * Count the number of items in the collection + * @returns {Promise} - The response from the API. + * + * @example + * ```typescript + * const response = await collection.count(); + * ``` + */ + public async count(): Promise { + const response = await this.api.count(this.id, this.api.options); + return handleSuccess(response); + } + + /** + * Modify the collection name or metadata + * @param {Object} params - The parameters for the query. + * @param {string} [params.name] - Optional new name for the collection. + * @param {CollectionMetadata} [params.metadata] - Optional new metadata for the collection. + * @returns {Promise} - The response from the API. + * + * @example + * ```typescript + * const response = await collection.modify({ + * name: "new name", + * metadata: { "key": "value" }, + * }); + * ``` + */ + public async modify({ + name, + metadata, + }: ModifyCollectionParams = {}): Promise { + const response = await this.api + .updateCollection( + this.id, + { + new_name: name, + new_metadata: metadata, + }, + this.api.options, + ) + .then(handleSuccess) + .catch(handleError); + + this.setName(name || this.name); + this.setMetadata(metadata || this.metadata); + + return response; + } + + /** + * Get items from the collection + * @param {Object} params - The parameters for the query. + * @param {ID | IDs} [params.ids] - Optional IDs of the items to get. + * @param {Where} [params.where] - Optional where clause to filter items by. + * @param {PositiveInteger} [params.limit] - Optional limit on the number of items to get. + * @param {PositiveInteger} [params.offset] - Optional offset on the items to get. + * @param {IncludeEnum[]} [params.include] - Optional list of items to include in the response. + * @param {WhereDocument} [params.whereDocument] - Optional where clause to filter items by. + * @returns {Promise} - The response from the server. + * + * @example + * ```typescript + * const response = await collection.get({ + * ids: ["id1", "id2"], + * where: { "key": "value" }, + * limit: 10, + * offset: 0, + * include: ["embeddings", "metadatas", "documents"], + * whereDocument: { $contains: "value" }, + * }); + * ``` + */ + public async get({ + ids, + where, + limit, + offset, + include, + whereDocument, + }: GetParams = {}): Promise { + let idsArray = undefined; + if (ids !== undefined) idsArray = toArray(ids); + + return await this.api + .aGet( + this.id, + { + ids: idsArray, + where, + limit, + offset, + //@ts-ignore + include, + where_document: whereDocument, + }, + this.api.options, + ) + .then(handleSuccess) + .catch(handleError); + } + + /** + * Update the embeddings, documents, and/or metadatas of existing items + * @param {Object} params - The parameters for the query. + * @param {ID | IDs} [params.ids] - The IDs of the items to update. + * @param {Embedding | Embeddings} [params.embeddings] - Optional embeddings to update. + * @param {Metadata | Metadatas} [params.metadatas] - Optional metadatas to update. + * @param {Document | Documents} [params.documents] - Optional documents to update. + * @returns {Promise} - The API Response. True if successful. Else, error. + * + * @example + * ```typescript + * const response = await collection.update({ + * ids: ["id1", "id2"], + * embeddings: [[1, 2, 3], [4, 5, 6]], + * metadatas: [{ "key": "value" }, { "key": "value" }], + * documents: ["new document 1", "new document 2"], + * }); + * ``` + */ + public async update({ + ids, + embeddings, + metadatas, + documents, + }: UpdateParams): Promise { + if ( + embeddings === undefined && + documents === undefined && + metadatas === undefined + ) { + throw new Error( + "embeddings, documents, and metadatas cannot all be undefined", + ); + } else if (embeddings === undefined && documents !== undefined) { + const documentsArray = toArray(documents); + if (this.embeddingFunction !== undefined) { + embeddings = await this.embeddingFunction.generate(documentsArray); + } else { + throw new Error( + "embeddingFunction is undefined. Please configure an embedding function", + ); + } } - /** - * Deletes items from the collection. - * @param {Object} params - The parameters for deleting items from the collection. - * @param {ID | IDs} [params.ids] - Optional ID or array of IDs of items to delete. - * @param {Where} [params.where] - Optional query condition to filter items to delete based on metadata values. - * @param {WhereDocument} [params.whereDocument] - Optional query condition to filter items to delete based on document content. - * @returns {Promise} A promise that resolves to the IDs of the deleted items. - * @throws {Error} If there is an issue deleting items from the collection. - * - * @example - * ```typescript - * const results = await collection.delete({ - * ids: "some_id", - * where: {"name": {"$eq": "John Doe"}}, - * whereDocument: {"$contains":"search_string"} - * }); - * ``` - */ - public async delete({ - ids, - where, - whereDocument - }: DeleteParams = {}): Promise { - let idsArray = undefined; - if (ids !== undefined) idsArray = toArray(ids); - return await this.api - .aDelete(this.id, { ids: idsArray, where: where, where_document: whereDocument }, this.api.options) - .then(handleSuccess) - .catch(handleError); + // backend expects None if metadatas is undefined + if (metadatas !== undefined) metadatas = toArray(metadatas); + if (documents !== undefined) documents = toArray(documents); + + var resp = await this.api + .update( + this.id, + { + ids: toArray(ids), + embeddings: embeddings ? toArrayOfArrays(embeddings) : undefined, + documents: documents, + metadatas: metadatas, + }, + this.api.options, + ) + .then(handleSuccess) + .catch(handleError); + + return resp; + } + + /** + * Performs a query on the collection using the specified parameters. + * + * @param {Object} params - The parameters for the query. + * @param {Embedding | Embeddings} [params.queryEmbeddings] - Optional query embeddings to use for the search. + * @param {PositiveInteger} [params.nResults] - Optional number of results to return (default is 10). + * @param {Where} [params.where] - Optional query condition to filter results based on metadata values. + * @param {string | string[]} [params.queryTexts] - Optional query text(s) to search for in the collection. + * @param {WhereDocument} [params.whereDocument] - Optional query condition to filter results based on document content. + * @param {IncludeEnum[]} [params.include] - Optional array of fields to include in the result, such as "metadata" and "document". + * + * @returns {Promise} A promise that resolves to the query results. + * @throws {Error} If there is an issue executing the query. + * @example + * // Query the collection using embeddings + * const results = await collection.query({ + * queryEmbeddings: [[0.1, 0.2, ...], ...], + * nResults: 10, + * where: {"name": {"$eq": "John Doe"}}, + * include: ["metadata", "document"] + * }); + * @example + * ```js + * // Query the collection using query text + * const results = await collection.query({ + * queryTexts: "some text", + * nResults: 10, + * where: {"name": {"$eq": "John Doe"}}, + * include: ["metadata", "document"] + * }); + * ``` + * + */ + public async query({ + queryEmbeddings, + nResults, + where, + queryTexts, + whereDocument, + include, + }: QueryParams): Promise { + if (nResults === undefined) nResults = 10; + if (queryEmbeddings === undefined && queryTexts === undefined) { + throw new Error( + "queryEmbeddings and queryTexts cannot both be undefined", + ); + } else if (queryEmbeddings === undefined && queryTexts !== undefined) { + const queryTextsArray = toArray(queryTexts); + if (this.embeddingFunction !== undefined) { + queryEmbeddings = + await this.embeddingFunction.generate(queryTextsArray); + } else { + throw new Error( + "embeddingFunction is undefined. Please configure an embedding function", + ); + } } + if (queryEmbeddings === undefined) + throw new Error("embeddings is undefined but shouldnt be"); + + const query_embeddingsArray = toArrayOfArrays(queryEmbeddings); + + return await this.api + .getNearestNeighbors( + this.id, + { + query_embeddings: query_embeddingsArray, + where, + n_results: nResults, + where_document: whereDocument, + //@ts-ignore + include: include, + }, + this.api.options, + ) + .then(handleSuccess) + .catch(handleError); + } + + /** + * Peek inside the collection + * @param {Object} params - The parameters for the query. + * @param {PositiveInteger} [params.limit] - Optional number of results to return (default is 10). + * @returns {Promise} A promise that resolves to the query results. + * @throws {Error} If there is an issue executing the query. + * + * @example + * ```typescript + * const results = await collection.peek({ + * limit: 10 + * }); + * ``` + */ + public async peek({ limit }: PeekParams = {}): Promise { + if (limit === undefined) limit = 10; + const response = await this.api.aGet( + this.id, + { + limit: limit, + }, + this.api.options, + ); + return handleSuccess(response); + } + + /** + * Deletes items from the collection. + * @param {Object} params - The parameters for deleting items from the collection. + * @param {ID | IDs} [params.ids] - Optional ID or array of IDs of items to delete. + * @param {Where} [params.where] - Optional query condition to filter items to delete based on metadata values. + * @param {WhereDocument} [params.whereDocument] - Optional query condition to filter items to delete based on document content. + * @returns {Promise} A promise that resolves to the IDs of the deleted items. + * @throws {Error} If there is an issue deleting items from the collection. + * + * @example + * ```typescript + * const results = await collection.delete({ + * ids: "some_id", + * where: {"name": {"$eq": "John Doe"}}, + * whereDocument: {"$contains":"search_string"} + * }); + * ``` + */ + public async delete({ + ids, + where, + whereDocument, + }: DeleteParams = {}): Promise { + let idsArray = undefined; + if (ids !== undefined) idsArray = toArray(ids); + return await this.api + .aDelete( + this.id, + { ids: idsArray, where: where, where_document: whereDocument }, + this.api.options, + ) + .then(handleSuccess) + .catch(handleError); + } } diff --git a/clients/js/src/auth.ts b/clients/js/src/auth.ts index 4f833f97d61..415c98e14b4 100644 --- a/clients/js/src/auth.ts +++ b/clients/js/src/auth.ts @@ -1,321 +1,376 @@ -import {ApiApi as DefaultApi} from "./generated"; +import { ApiApi as DefaultApi } from "./generated"; export interface ClientAuthProvider { - /** - * Abstract method for authenticating a client. - */ - authenticate(): ClientAuthResponse; + /** + * Abstract method for authenticating a client. + */ + authenticate(): ClientAuthResponse; } export interface ClientAuthConfigurationProvider { - /** - * Abstract method for getting the configuration for the client. - */ - getConfig(): T; + /** + * Abstract method for getting the configuration for the client. + */ + getConfig(): T; } export interface ClientAuthCredentialsProvider { - /** - * Abstract method for getting the credentials for the client. - * @param user - */ - getCredentials(user?: string): T; + /** + * Abstract method for getting the credentials for the client. + * @param user + */ + getCredentials(user?: string): T; } enum AuthInfoType { - COOKIE = "cookie", - HEADER = "header", - URL = "url", - METADATA = "metadata" - + COOKIE = "cookie", + HEADER = "header", + URL = "url", + METADATA = "metadata", } export interface ClientAuthResponse { - getAuthInfoType(): AuthInfoType; + getAuthInfoType(): AuthInfoType; - getAuthInfo(): { key: string, value: string }; + getAuthInfo(): { key: string; value: string }; } - export interface AbstractCredentials { - getCredentials(): T; + getCredentials(): T; } export interface ClientAuthProtocolAdapter { - injectCredentials(injectionContext: T): T; + injectCredentials(injectionContext: T): T; - getApi(): any; + getApi(): any; } - class SecretStr { - constructor(private readonly secret: string) { - } + constructor(private readonly secret: string) {} - getSecret(): string { - return this.secret; - } + getSecret(): string { + return this.secret; + } } const base64Encode = (str: string): string => { - return Buffer.from(str).toString('base64'); + return Buffer.from(str).toString("base64"); }; class BasicAuthCredentials implements AbstractCredentials { - private readonly credentials: SecretStr; + private readonly credentials: SecretStr; - constructor(_creds: string) { - this.credentials = new SecretStr(base64Encode(_creds)) - } + constructor(_creds: string) { + this.credentials = new SecretStr(base64Encode(_creds)); + } - getCredentials(): SecretStr { - //encode base64 - return this.credentials; - } + getCredentials(): SecretStr { + //encode base64 + return this.credentials; + } } - class BasicAuthClientAuthResponse implements ClientAuthResponse { - constructor(private readonly credentials: BasicAuthCredentials) { - } - - getAuthInfo(): { key: string; value: string } { - return {key: "Authorization", value: "Basic " + this.credentials.getCredentials().getSecret()}; - } - - getAuthInfoType(): AuthInfoType { - return AuthInfoType.HEADER; - } + constructor(private readonly credentials: BasicAuthCredentials) {} + + getAuthInfo(): { key: string; value: string } { + return { + key: "Authorization", + value: "Basic " + this.credentials.getCredentials().getSecret(), + }; + } + + getAuthInfoType(): AuthInfoType { + return AuthInfoType.HEADER; + } } -export class BasicAuthCredentialsProvider implements ClientAuthCredentialsProvider { - private readonly credentials: BasicAuthCredentials; - - /** - * Creates a new BasicAuthCredentialsProvider. This provider loads credentials from provided text credentials or from the environment variable CHROMA_CLIENT_AUTH_CREDENTIALS. - * @param _creds - The credentials - * @throws {Error} If neither credentials provider or text credentials are supplied. - */ - - constructor(_creds: string | undefined) { - if (_creds === undefined && !process.env.CHROMA_CLIENT_AUTH_CREDENTIALS) throw new Error("Credentials must be supplied via environment variable (CHROMA_CLIENT_AUTH_CREDENTIALS) or passed in as configuration."); - this.credentials = new BasicAuthCredentials((_creds ?? process.env.CHROMA_CLIENT_AUTH_CREDENTIALS) as string); - } - - getCredentials(): BasicAuthCredentials { - return this.credentials; - } +export class BasicAuthCredentialsProvider + implements ClientAuthCredentialsProvider +{ + private readonly credentials: BasicAuthCredentials; + + /** + * Creates a new BasicAuthCredentialsProvider. This provider loads credentials from provided text credentials or from the environment variable CHROMA_CLIENT_AUTH_CREDENTIALS. + * @param _creds - The credentials + * @throws {Error} If neither credentials provider or text credentials are supplied. + */ + + constructor(_creds: string | undefined) { + if (_creds === undefined && !process.env.CHROMA_CLIENT_AUTH_CREDENTIALS) + throw new Error( + "Credentials must be supplied via environment variable (CHROMA_CLIENT_AUTH_CREDENTIALS) or passed in as configuration.", + ); + this.credentials = new BasicAuthCredentials( + (_creds ?? process.env.CHROMA_CLIENT_AUTH_CREDENTIALS) as string, + ); + } + + getCredentials(): BasicAuthCredentials { + return this.credentials; + } } class BasicAuthClientAuthProvider implements ClientAuthProvider { - private readonly credentialsProvider: ClientAuthCredentialsProvider; - - /** - * Creates a new BasicAuthClientAuthProvider. - * @param options - The options for the authentication provider. - * @param options.textCredentials - The credentials for the authentication provider. - * @param options.credentialsProvider - The credentials provider for the authentication provider. - * @throws {Error} If neither credentials provider or text credentials are supplied. - */ - - constructor(options: { - textCredentials: any; - credentialsProvider: ClientAuthCredentialsProvider | undefined - }) { - if (!options.credentialsProvider && !options.textCredentials) { - throw new Error("Either credentials provider or text credentials must be supplied."); - } - this.credentialsProvider = options.credentialsProvider || new BasicAuthCredentialsProvider(options.textCredentials); - } - - authenticate(): ClientAuthResponse { - return new BasicAuthClientAuthResponse(this.credentialsProvider.getCredentials()); + private readonly credentialsProvider: ClientAuthCredentialsProvider; + + /** + * Creates a new BasicAuthClientAuthProvider. + * @param options - The options for the authentication provider. + * @param options.textCredentials - The credentials for the authentication provider. + * @param options.credentialsProvider - The credentials provider for the authentication provider. + * @throws {Error} If neither credentials provider or text credentials are supplied. + */ + + constructor(options: { + textCredentials: any; + credentialsProvider: ClientAuthCredentialsProvider | undefined; + }) { + if (!options.credentialsProvider && !options.textCredentials) { + throw new Error( + "Either credentials provider or text credentials must be supplied.", + ); } + this.credentialsProvider = + options.credentialsProvider || + new BasicAuthCredentialsProvider(options.textCredentials); + } + + authenticate(): ClientAuthResponse { + return new BasicAuthClientAuthResponse( + this.credentialsProvider.getCredentials(), + ); + } } class TokenAuthCredentials implements AbstractCredentials { - private readonly credentials: SecretStr; + private readonly credentials: SecretStr; - constructor(_creds: string) { - this.credentials = new SecretStr(_creds) - } + constructor(_creds: string) { + this.credentials = new SecretStr(_creds); + } - getCredentials(): SecretStr { - return this.credentials; - } + getCredentials(): SecretStr { + return this.credentials; + } } -export class TokenCredentialsProvider implements ClientAuthCredentialsProvider { - private readonly credentials: TokenAuthCredentials; - - constructor(_creds: string | undefined) { - if (_creds === undefined && !process.env.CHROMA_CLIENT_AUTH_CREDENTIALS) throw new Error("Credentials must be supplied via environment variable (CHROMA_CLIENT_AUTH_CREDENTIALS) or passed in as configuration."); - this.credentials = new TokenAuthCredentials((_creds ?? process.env.CHROMA_CLIENT_AUTH_CREDENTIALS) as string); - } - - getCredentials(): TokenAuthCredentials { - return this.credentials; - } +export class TokenCredentialsProvider + implements ClientAuthCredentialsProvider +{ + private readonly credentials: TokenAuthCredentials; + + constructor(_creds: string | undefined) { + if (_creds === undefined && !process.env.CHROMA_CLIENT_AUTH_CREDENTIALS) + throw new Error( + "Credentials must be supplied via environment variable (CHROMA_CLIENT_AUTH_CREDENTIALS) or passed in as configuration.", + ); + this.credentials = new TokenAuthCredentials( + (_creds ?? process.env.CHROMA_CLIENT_AUTH_CREDENTIALS) as string, + ); + } + + getCredentials(): TokenAuthCredentials { + return this.credentials; + } } export class TokenClientAuthProvider implements ClientAuthProvider { - private readonly credentialsProvider: ClientAuthCredentialsProvider; - private readonly providerOptions: { headerType: TokenHeaderType }; - - constructor(options: { - textCredentials: any; - credentialsProvider: ClientAuthCredentialsProvider | undefined, - providerOptions?: { headerType: TokenHeaderType } - }) { - if (!options.credentialsProvider && !options.textCredentials) { - throw new Error("Either credentials provider or text credentials must be supplied."); - } - if (options.providerOptions === undefined || !options.providerOptions.hasOwnProperty("headerType")) { - this.providerOptions = {headerType: "AUTHORIZATION"}; - } else { - this.providerOptions = {headerType: options.providerOptions.headerType}; - } - this.credentialsProvider = options.credentialsProvider || new TokenCredentialsProvider(options.textCredentials); + private readonly credentialsProvider: ClientAuthCredentialsProvider; + private readonly providerOptions: { headerType: TokenHeaderType }; + + constructor(options: { + textCredentials: any; + credentialsProvider: ClientAuthCredentialsProvider | undefined; + providerOptions?: { headerType: TokenHeaderType }; + }) { + if (!options.credentialsProvider && !options.textCredentials) { + throw new Error( + "Either credentials provider or text credentials must be supplied.", + ); } - - authenticate(): ClientAuthResponse { - return new TokenClientAuthResponse(this.credentialsProvider.getCredentials(), this.providerOptions.headerType); + if ( + options.providerOptions === undefined || + !options.providerOptions.hasOwnProperty("headerType") + ) { + this.providerOptions = { headerType: "AUTHORIZATION" }; + } else { + this.providerOptions = { headerType: options.providerOptions.headerType }; } - + this.credentialsProvider = + options.credentialsProvider || + new TokenCredentialsProvider(options.textCredentials); + } + + authenticate(): ClientAuthResponse { + return new TokenClientAuthResponse( + this.credentialsProvider.getCredentials(), + this.providerOptions.headerType, + ); + } } - -type TokenHeaderType = 'AUTHORIZATION' | 'X_CHROMA_TOKEN'; - -const TokenHeader: Record { key: string; value: string; }> = { - AUTHORIZATION: (value: string) => ({key: "Authorization", value: `Bearer ${value}`}), - X_CHROMA_TOKEN: (value: string) => ({key: "X-Chroma-Token", value: value}) -} +type TokenHeaderType = "AUTHORIZATION" | "X_CHROMA_TOKEN"; + +const TokenHeader: Record< + TokenHeaderType, + (value: string) => { key: string; value: string } +> = { + AUTHORIZATION: (value: string) => ({ + key: "Authorization", + value: `Bearer ${value}`, + }), + X_CHROMA_TOKEN: (value: string) => ({ key: "X-Chroma-Token", value: value }), +}; class TokenClientAuthResponse implements ClientAuthResponse { - constructor(private readonly credentials: TokenAuthCredentials, private readonly headerType: TokenHeaderType = 'AUTHORIZATION') { - } - - getAuthInfo(): { key: string; value: string } { - if (this.headerType === 'AUTHORIZATION') { - return TokenHeader.AUTHORIZATION(this.credentials.getCredentials().getSecret()); - } else if (this.headerType === 'X_CHROMA_TOKEN') { - return TokenHeader.X_CHROMA_TOKEN(this.credentials.getCredentials().getSecret()); - } else { - throw new Error("Invalid header type: " + this.headerType + ". Valid types are: " + Object.keys(TokenHeader).join(", ")); - } + constructor( + private readonly credentials: TokenAuthCredentials, + private readonly headerType: TokenHeaderType = "AUTHORIZATION", + ) {} + + getAuthInfo(): { key: string; value: string } { + if (this.headerType === "AUTHORIZATION") { + return TokenHeader.AUTHORIZATION( + this.credentials.getCredentials().getSecret(), + ); + } else if (this.headerType === "X_CHROMA_TOKEN") { + return TokenHeader.X_CHROMA_TOKEN( + this.credentials.getCredentials().getSecret(), + ); + } else { + throw new Error( + "Invalid header type: " + + this.headerType + + ". Valid types are: " + + Object.keys(TokenHeader).join(", "), + ); } + } - getAuthInfoType(): AuthInfoType { - return AuthInfoType.HEADER; - } + getAuthInfoType(): AuthInfoType { + return AuthInfoType.HEADER; + } } - -export class IsomorphicFetchClientAuthProtocolAdapter implements ClientAuthProtocolAdapter { - authProvider: ClientAuthProvider | undefined; - wrapperApi: DefaultApi | undefined; - - /** - * Creates a new adapter of IsomorphicFetchClientAuthProtocolAdapter. - * @param api - The API to wrap. - * @param authConfiguration - The configuration for the authentication provider. - */ - - constructor(private api: DefaultApi, authConfiguration: AuthOptions) { - - switch (authConfiguration.provider) { - case "basic": - this.authProvider = new BasicAuthClientAuthProvider({ - textCredentials: authConfiguration.credentials, - credentialsProvider: authConfiguration.credentialsProvider - }); - break; - case "token": - this.authProvider = new TokenClientAuthProvider({ - textCredentials: authConfiguration.credentials, - credentialsProvider: authConfiguration.credentialsProvider, - providerOptions: authConfiguration.providerOptions - }); - break; - default: - this.authProvider = undefined; - break; - } - if (this.authProvider !== undefined) { - this.wrapperApi = this.wrapMethods(this.api); - } - } - - getApi(): DefaultApi { - return this.wrapperApi ?? this.api; +export class IsomorphicFetchClientAuthProtocolAdapter + implements ClientAuthProtocolAdapter +{ + authProvider: ClientAuthProvider | undefined; + wrapperApi: DefaultApi | undefined; + + /** + * Creates a new adapter of IsomorphicFetchClientAuthProtocolAdapter. + * @param api - The API to wrap. + * @param authConfiguration - The configuration for the authentication provider. + */ + + constructor( + private api: DefaultApi, + authConfiguration: AuthOptions, + ) { + switch (authConfiguration.provider) { + case "basic": + this.authProvider = new BasicAuthClientAuthProvider({ + textCredentials: authConfiguration.credentials, + credentialsProvider: authConfiguration.credentialsProvider, + }); + break; + case "token": + this.authProvider = new TokenClientAuthProvider({ + textCredentials: authConfiguration.credentials, + credentialsProvider: authConfiguration.credentialsProvider, + providerOptions: authConfiguration.providerOptions, + }); + break; + default: + this.authProvider = undefined; + break; } - - getAllMethods(obj: any): string[] { - let methods: string[] = []; - let currentObj = obj; - - do { - const objMethods = Object.getOwnPropertyNames(currentObj) - .filter(name => typeof currentObj[name] === 'function' && name !== 'constructor'); - - methods = methods.concat(objMethods); - currentObj = Object.getPrototypeOf(currentObj); - } while (currentObj); - - return methods; + if (this.authProvider !== undefined) { + this.wrapperApi = this.wrapMethods(this.api); } - - wrapMethods(obj: any): any { - let self = this; - const methodNames = Object.getOwnPropertyNames(Object.getPrototypeOf(obj)) - .filter(name => typeof obj[name] === 'function' && name !== 'constructor'); - - return new Proxy(obj, { - get(target, prop: string) { - if (methodNames.includes(prop)) { - return new Proxy(target[prop], { - apply(fn, thisArg, args) { - const modifiedArgs = args.map(arg => { - if (arg && typeof arg === 'object' && 'method' in arg) { - return self.injectCredentials(arg as RequestInit); - } - return arg; - }); - if (Object.keys(modifiedArgs[modifiedArgs.length - 1]).length === 0) { - modifiedArgs[modifiedArgs.length - 1] = self.injectCredentials({} as RequestInit); - } else { - modifiedArgs[modifiedArgs.length - 1] = self.injectCredentials(modifiedArgs[modifiedArgs.length - 1] as RequestInit); - } - return fn.apply(thisArg, modifiedArgs); - } - }); + } + + getApi(): DefaultApi { + return this.wrapperApi ?? this.api; + } + + getAllMethods(obj: any): string[] { + let methods: string[] = []; + let currentObj = obj; + + do { + const objMethods = Object.getOwnPropertyNames(currentObj).filter( + (name) => + typeof currentObj[name] === "function" && name !== "constructor", + ); + + methods = methods.concat(objMethods); + currentObj = Object.getPrototypeOf(currentObj); + } while (currentObj); + + return methods; + } + + wrapMethods(obj: any): any { + let self = this; + const methodNames = Object.getOwnPropertyNames( + Object.getPrototypeOf(obj), + ).filter( + (name) => typeof obj[name] === "function" && name !== "constructor", + ); + + return new Proxy(obj, { + get(target, prop: string) { + if (methodNames.includes(prop)) { + return new Proxy(target[prop], { + apply(fn, thisArg, args) { + const modifiedArgs = args.map((arg) => { + if (arg && typeof arg === "object" && "method" in arg) { + return self.injectCredentials(arg as RequestInit); } - return target[prop]; - } - }); - } - - injectCredentials(injectionContext: RequestInit): RequestInit { - const authInfo = this.authProvider?.authenticate().getAuthInfo(); - if (authInfo) { - const {key, value} = authInfo; - injectionContext = { - ...injectionContext, - headers: { - [key]: value - }, - } + return arg; + }); + if ( + Object.keys(modifiedArgs[modifiedArgs.length - 1]).length === 0 + ) { + modifiedArgs[modifiedArgs.length - 1] = self.injectCredentials( + {} as RequestInit, + ); + } else { + modifiedArgs[modifiedArgs.length - 1] = self.injectCredentials( + modifiedArgs[modifiedArgs.length - 1] as RequestInit, + ); + } + return fn.apply(thisArg, modifiedArgs); + }, + }); } - return injectionContext; + return target[prop]; + }, + }); + } + + injectCredentials(injectionContext: RequestInit): RequestInit { + const authInfo = this.authProvider?.authenticate().getAuthInfo(); + if (authInfo) { + const { key, value } = authInfo; + injectionContext = { + ...injectionContext, + headers: { + [key]: value, + }, + }; } + return injectionContext; + } } - export type AuthOptions = { - provider: ClientAuthProvider | string | undefined, - credentialsProvider?: ClientAuthCredentialsProvider | undefined, - configProvider?: ClientAuthConfigurationProvider | undefined, - credentials?: any | undefined, - providerOptions?: any | undefined -} + provider: ClientAuthProvider | string | undefined; + credentialsProvider?: ClientAuthCredentialsProvider | undefined; + configProvider?: ClientAuthConfigurationProvider | undefined; + credentials?: any | undefined; + providerOptions?: any | undefined; +}; diff --git a/clients/js/src/embeddings/DefaultEmbeddingFunction.ts b/clients/js/src/embeddings/DefaultEmbeddingFunction.ts index 6ced79bbd48..be2394a7d22 100644 --- a/clients/js/src/embeddings/DefaultEmbeddingFunction.ts +++ b/clients/js/src/embeddings/DefaultEmbeddingFunction.ts @@ -39,21 +39,21 @@ export class DefaultEmbeddingFunction implements IEmbeddingFunction { public async generate(texts: string[]): Promise { await this.loadClient(); - // Store a promise that resolves to the pipeline + // Store a promise that resolves to the pipeline this.pipelinePromise = new Promise(async (resolve, reject) => { try { - const pipeline = this.transformersApi + const pipeline = this.transformersApi; - const quantized = this.quantized - const revision = this.revision - const progress_callback = this.progress_callback + const quantized = this.quantized; + const revision = this.revision; + const progress_callback = this.progress_callback; resolve( await pipeline("feature-extraction", this.model, { quantized, revision, progress_callback, - }) + }), ); } catch (e) { reject(e); @@ -66,34 +66,36 @@ export class DefaultEmbeddingFunction implements IEmbeddingFunction { } private async loadClient() { - if(this.transformersApi) return; - try { - // eslint-disable-next-line global-require,import/no-extraneous-dependencies - let { pipeline } = await DefaultEmbeddingFunction.import(); - TransformersApi = pipeline; - } catch (_a) { - // @ts-ignore - if (_a.code === 'MODULE_NOT_FOUND') { - throw new Error("Please install the chromadb-default-embed package to use the DefaultEmbeddingFunction, `npm install -S chromadb-default-embed`"); - } - throw _a; // Re-throw other errors + if (this.transformersApi) return; + try { + // eslint-disable-next-line global-require,import/no-extraneous-dependencies + let { pipeline } = await DefaultEmbeddingFunction.import(); + TransformersApi = pipeline; + } catch (_a) { + // @ts-ignore + if (_a.code === "MODULE_NOT_FOUND") { + throw new Error( + "Please install the chromadb-default-embed package to use the DefaultEmbeddingFunction, `npm install -S chromadb-default-embed`", + ); } - this.transformersApi = TransformersApi; + throw _a; // Re-throw other errors + } + this.transformersApi = TransformersApi; } /** @ignore */ static async import(): Promise<{ - // @ts-ignore - pipeline: typeof import("chromadb-default-embed"); + // @ts-ignore + pipeline: typeof import("chromadb-default-embed"); }> { - try { - // @ts-ignore - const { pipeline } = await import("chromadb-default-embed"); - return { pipeline }; - } catch (e) { - throw new Error( - "Please install chromadb-default-embed as a dependency with, e.g. `yarn add chromadb-default-embed`" - ); - } + try { + // @ts-ignore + const { pipeline } = await import("chromadb-default-embed"); + return { pipeline }; + } catch (e) { + throw new Error( + "Please install chromadb-default-embed as a dependency with, e.g. `yarn add chromadb-default-embed`", + ); + } } } diff --git a/clients/js/src/embeddings/GoogleGeminiEmbeddingFunction.ts b/clients/js/src/embeddings/GoogleGeminiEmbeddingFunction.ts index a1ab2abe995..54a356481b0 100644 --- a/clients/js/src/embeddings/GoogleGeminiEmbeddingFunction.ts +++ b/clients/js/src/embeddings/GoogleGeminiEmbeddingFunction.ts @@ -3,67 +3,76 @@ import { IEmbeddingFunction } from "./IEmbeddingFunction"; let googleGenAiApi: any; export class GoogleGenerativeAiEmbeddingFunction implements IEmbeddingFunction { - private api_key: string; - private model: string; - private googleGenAiApi?: any; - private taskType: string; + private api_key: string; + private model: string; + private googleGenAiApi?: any; + private taskType: string; - constructor({ googleApiKey, model, taskType }: { googleApiKey: string, model?: string, taskType?: string }) { - // we used to construct the client here, but we need to async import the types - // for the openai npm package, and the constructor can not be async - this.api_key = googleApiKey; - this.model = model || "embedding-001"; - this.taskType = taskType || "RETRIEVAL_DOCUMENT"; - } + constructor({ + googleApiKey, + model, + taskType, + }: { + googleApiKey: string; + model?: string; + taskType?: string; + }) { + // we used to construct the client here, but we need to async import the types + // for the openai npm package, and the constructor can not be async + this.api_key = googleApiKey; + this.model = model || "embedding-001"; + this.taskType = taskType || "RETRIEVAL_DOCUMENT"; + } - private async loadClient() { - if(this.googleGenAiApi) return; - try { - // eslint-disable-next-line global-require,import/no-extraneous-dependencies - const { googleGenAi } = await GoogleGenerativeAiEmbeddingFunction.import(); - googleGenAiApi = googleGenAi; - // googleGenAiApi.init(this.api_key); - googleGenAiApi = new googleGenAiApi(this.api_key); - } catch (_a) { - // @ts-ignore - if (_a.code === 'MODULE_NOT_FOUND') { - throw new Error("Please install the @google/generative-ai package to use the GoogleGenerativeAiEmbeddingFunction, `npm install -S @google/generative-ai`"); - } - throw _a; // Re-throw other errors - } - this.googleGenAiApi = googleGenAiApi; + private async loadClient() { + if (this.googleGenAiApi) return; + try { + // eslint-disable-next-line global-require,import/no-extraneous-dependencies + const { googleGenAi } = + await GoogleGenerativeAiEmbeddingFunction.import(); + googleGenAiApi = googleGenAi; + // googleGenAiApi.init(this.api_key); + googleGenAiApi = new googleGenAiApi(this.api_key); + } catch (_a) { + // @ts-ignore + if (_a.code === "MODULE_NOT_FOUND") { + throw new Error( + "Please install the @google/generative-ai package to use the GoogleGenerativeAiEmbeddingFunction, `npm install -S @google/generative-ai`", + ); + } + throw _a; // Re-throw other errors } + this.googleGenAiApi = googleGenAiApi; + } - public async generate(texts: string[]) { - - await this.loadClient(); - const model = this.googleGenAiApi.getGenerativeModel({ model: this.model}); - const response = await model.batchEmbedContents({ - requests: texts.map((t) => ({ - content: { parts: [{ text: t }] }, - taskType: this.taskType, - })), - }); - const embeddings = response.embeddings.map((e: any) => e.values); + public async generate(texts: string[]) { + await this.loadClient(); + const model = this.googleGenAiApi.getGenerativeModel({ model: this.model }); + const response = await model.batchEmbedContents({ + requests: texts.map((t) => ({ + content: { parts: [{ text: t }] }, + taskType: this.taskType, + })), + }); + const embeddings = response.embeddings.map((e: any) => e.values); - return embeddings; - } + return embeddings; + } - /** @ignore */ - static async import(): Promise<{ - // @ts-ignore - googleGenAi: typeof import("@google/generative-ai"); - }> { - try { - // @ts-ignore - const { GoogleGenerativeAI } = await import("@google/generative-ai"); - const googleGenAi = GoogleGenerativeAI; - return { googleGenAi }; - } catch (e) { - throw new Error( - "Please install @google/generative-ai as a dependency with, e.g. `yarn add @google/generative-ai`" - ); - } + /** @ignore */ + static async import(): Promise<{ + // @ts-ignore + googleGenAi: typeof import("@google/generative-ai"); + }> { + try { + // @ts-ignore + const { GoogleGenerativeAI } = await import("@google/generative-ai"); + const googleGenAi = GoogleGenerativeAI; + return { googleGenAi }; + } catch (e) { + throw new Error( + "Please install @google/generative-ai as a dependency with, e.g. `yarn add @google/generative-ai`", + ); } - + } } diff --git a/clients/js/src/embeddings/HuggingFaceEmbeddingServerFunction.ts b/clients/js/src/embeddings/HuggingFaceEmbeddingServerFunction.ts index dcbc62ecb70..b65c85f3f2f 100644 --- a/clients/js/src/embeddings/HuggingFaceEmbeddingServerFunction.ts +++ b/clients/js/src/embeddings/HuggingFaceEmbeddingServerFunction.ts @@ -3,29 +3,28 @@ import { IEmbeddingFunction } from "./IEmbeddingFunction"; let CohereAiApi: any; export class HuggingFaceEmbeddingServerFunction implements IEmbeddingFunction { - private url: string; + private url: string; - constructor({ url }: { url: string }) { - // we used to construct the client here, but we need to async import the types - // for the openai npm package, and the constructor can not be async - this.url = url; - } - - public async generate(texts: string[]) { - const response = await fetch(this.url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ 'inputs': texts }) - }); + constructor({ url }: { url: string }) { + // we used to construct the client here, but we need to async import the types + // for the openai npm package, and the constructor can not be async + this.url = url; + } - if (!response.ok) { - throw new Error(`Failed to generate embeddings: ${response.statusText}`); - } + public async generate(texts: string[]) { + const response = await fetch(this.url, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ inputs: texts }), + }); - const data = await response.json(); - return data; + if (!response.ok) { + throw new Error(`Failed to generate embeddings: ${response.statusText}`); } + const data = await response.json(); + return data; + } } diff --git a/clients/js/src/embeddings/IEmbeddingFunction.ts b/clients/js/src/embeddings/IEmbeddingFunction.ts index dcc21ab19d9..7c3f85bc473 100644 --- a/clients/js/src/embeddings/IEmbeddingFunction.ts +++ b/clients/js/src/embeddings/IEmbeddingFunction.ts @@ -1,3 +1,3 @@ export interface IEmbeddingFunction { - generate(texts: string[]): Promise; + generate(texts: string[]): Promise; } diff --git a/clients/js/src/embeddings/JinaEmbeddingFunction.ts b/clients/js/src/embeddings/JinaEmbeddingFunction.ts index a91f94749f8..0ac92b2a9fa 100644 --- a/clients/js/src/embeddings/JinaEmbeddingFunction.ts +++ b/clients/js/src/embeddings/JinaEmbeddingFunction.ts @@ -5,20 +5,26 @@ export class JinaEmbeddingFunction implements IEmbeddingFunction { private api_url: string; private headers: { [key: string]: string }; - constructor({ jinaai_api_key, model_name }: { jinaai_api_key: string; model_name?: string }) { - this.model_name = model_name || 'jina-embeddings-v2-base-en'; - this.api_url = 'https://api.jina.ai/v1/embeddings'; + constructor({ + jinaai_api_key, + model_name, + }: { + jinaai_api_key: string; + model_name?: string; + }) { + this.model_name = model_name || "jina-embeddings-v2-base-en"; + this.api_url = "https://api.jina.ai/v1/embeddings"; this.headers = { Authorization: `Bearer ${jinaai_api_key}`, - 'Accept-Encoding': 'identity', - 'Content-Type': 'application/json', + "Accept-Encoding": "identity", + "Content-Type": "application/json", }; } public async generate(texts: string[]) { try { const response = await fetch(this.api_url, { - method: 'POST', + method: "POST", headers: this.headers, body: JSON.stringify({ input: texts, diff --git a/clients/js/src/embeddings/OpenAIEmbeddingFunction.ts b/clients/js/src/embeddings/OpenAIEmbeddingFunction.ts index 0b4be92eec1..bd61236b439 100644 --- a/clients/js/src/embeddings/OpenAIEmbeddingFunction.ts +++ b/clients/js/src/embeddings/OpenAIEmbeddingFunction.ts @@ -1,151 +1,157 @@ -import {IEmbeddingFunction} from "./IEmbeddingFunction"; +import { IEmbeddingFunction } from "./IEmbeddingFunction"; let OpenAIApi: any; let openAiVersion = null; let openAiMajorVersion = null; interface OpenAIAPI { - createEmbedding: (params: { - model: string; - input: string[]; - user?: string; - }) => Promise; + createEmbedding: (params: { + model: string; + input: string[]; + user?: string; + }) => Promise; } class OpenAIAPIv3 implements OpenAIAPI { - private readonly configuration: any; - private openai: any; - - constructor(configuration: { organization: string, apiKey: string }) { - this.configuration = new OpenAIApi.Configuration({ - organization: configuration.organization, - apiKey: configuration.apiKey, - }); - this.openai = new OpenAIApi.OpenAIApi(this.configuration); - } - - public async createEmbedding(params: { - model: string, - input: string[], - user?: string - }): Promise { - const embeddings: number[][] = []; - const response = await this.openai.createEmbedding({ - model: params.model, - input: params.input, - }).catch((error: any) => { - throw error; - }); - // @ts-ignore - const data = response.data["data"]; - for (let i = 0; i < data.length; i += 1) { - embeddings.push(data[i]["embedding"]); - } - return embeddings + private readonly configuration: any; + private openai: any; + + constructor(configuration: { organization: string; apiKey: string }) { + this.configuration = new OpenAIApi.Configuration({ + organization: configuration.organization, + apiKey: configuration.apiKey, + }); + this.openai = new OpenAIApi.OpenAIApi(this.configuration); + } + + public async createEmbedding(params: { + model: string; + input: string[]; + user?: string; + }): Promise { + const embeddings: number[][] = []; + const response = await this.openai + .createEmbedding({ + model: params.model, + input: params.input, + }) + .catch((error: any) => { + throw error; + }); + // @ts-ignore + const data = response.data["data"]; + for (let i = 0; i < data.length; i += 1) { + embeddings.push(data[i]["embedding"]); } + return embeddings; + } } class OpenAIAPIv4 implements OpenAIAPI { - private readonly apiKey: any; - private openai: any; - - constructor(apiKey: any) { - this.apiKey = apiKey; - this.openai = new OpenAIApi({ - apiKey: this.apiKey, - }); - } - - public async createEmbedding(params: { - model: string, - input: string[], - user?: string - }): Promise { - const embeddings: number[][] = []; - const response = await this.openai.embeddings.create(params); - const data = response["data"]; - for (let i = 0; i < data.length; i += 1) { - embeddings.push(data[i]["embedding"]); - } - return embeddings + private readonly apiKey: any; + private openai: any; + + constructor(apiKey: any) { + this.apiKey = apiKey; + this.openai = new OpenAIApi({ + apiKey: this.apiKey, + }); + } + + public async createEmbedding(params: { + model: string; + input: string[]; + user?: string; + }): Promise { + const embeddings: number[][] = []; + const response = await this.openai.embeddings.create(params); + const data = response["data"]; + for (let i = 0; i < data.length; i += 1) { + embeddings.push(data[i]["embedding"]); } + return embeddings; + } } export class OpenAIEmbeddingFunction implements IEmbeddingFunction { - private api_key: string; - private org_id: string; - private model: string; - private openaiApi?: OpenAIAPI; - - constructor({openai_api_key, openai_model, openai_organization_id}: { - openai_api_key: string, - openai_model?: string, - openai_organization_id?: string - }) { - // we used to construct the client here, but we need to async import the types - // for the openai npm package, and the constructor can not be async - this.api_key = openai_api_key; - this.org_id = openai_organization_id || ""; - this.model = openai_model || "text-embedding-ada-002"; - } - - private async loadClient() { - // cache the client - if(this.openaiApi) return; - - try { - const { openai, version } = await OpenAIEmbeddingFunction.import(); - OpenAIApi = openai; - let versionVar: string = version; - openAiVersion = versionVar.replace(/[^0-9.]/g, ''); - openAiMajorVersion = parseInt(openAiVersion.split('.')[0]); - } catch (_a) { - // @ts-ignore - if (_a.code === 'MODULE_NOT_FOUND') { - throw new Error("Please install the openai package to use the OpenAIEmbeddingFunction, `npm install -S openai`"); - } - throw _a; // Re-throw other errors - } - - if (openAiMajorVersion > 3) { - this.openaiApi = new OpenAIAPIv4(this.api_key); - } else { - this.openaiApi = new OpenAIAPIv3({ - organization: this.org_id, - apiKey: this.api_key, - }); - } + private api_key: string; + private org_id: string; + private model: string; + private openaiApi?: OpenAIAPI; + + constructor({ + openai_api_key, + openai_model, + openai_organization_id, + }: { + openai_api_key: string; + openai_model?: string; + openai_organization_id?: string; + }) { + // we used to construct the client here, but we need to async import the types + // for the openai npm package, and the constructor can not be async + this.api_key = openai_api_key; + this.org_id = openai_organization_id || ""; + this.model = openai_model || "text-embedding-ada-002"; + } + + private async loadClient() { + // cache the client + if (this.openaiApi) return; + + try { + const { openai, version } = await OpenAIEmbeddingFunction.import(); + OpenAIApi = openai; + let versionVar: string = version; + openAiVersion = versionVar.replace(/[^0-9.]/g, ""); + openAiMajorVersion = parseInt(openAiVersion.split(".")[0]); + } catch (_a) { + // @ts-ignore + if (_a.code === "MODULE_NOT_FOUND") { + throw new Error( + "Please install the openai package to use the OpenAIEmbeddingFunction, `npm install -S openai`", + ); + } + throw _a; // Re-throw other errors } - public async generate(texts: string[]): Promise { - - await this.loadClient(); - - return await this.openaiApi!.createEmbedding({ - model: this.model, - input: texts, - }).catch((error: any) => { - throw error; - }); + if (openAiMajorVersion > 3) { + this.openaiApi = new OpenAIAPIv4(this.api_key); + } else { + this.openaiApi = new OpenAIAPIv3({ + organization: this.org_id, + apiKey: this.api_key, + }); } - - /** @ignore */ - static async import(): Promise<{ - // @ts-ignore - openai: typeof import("openai"); - version: string; - }> { - try { - // @ts-ignore - const { default: openai } = await import("openai"); - // @ts-ignore - const { VERSION } = await import('openai/version'); - return { openai, version: VERSION }; - } catch (e) { - throw new Error( - "Please install openai as a dependency with, e.g. `yarn add openai`" - ); - } + } + + public async generate(texts: string[]): Promise { + await this.loadClient(); + + return await this.openaiApi!.createEmbedding({ + model: this.model, + input: texts, + }).catch((error: any) => { + throw error; + }); + } + + /** @ignore */ + static async import(): Promise<{ + // @ts-ignore + openai: typeof import("openai"); + version: string; + }> { + try { + // @ts-ignore + const { default: openai } = await import("openai"); + // @ts-ignore + const { VERSION } = await import("openai/version"); + return { openai, version: VERSION }; + } catch (e) { + throw new Error( + "Please install openai as a dependency with, e.g. `yarn add openai`", + ); } - + } } diff --git a/clients/js/src/embeddings/TransformersEmbeddingFunction.ts b/clients/js/src/embeddings/TransformersEmbeddingFunction.ts index aece174b03c..45a39d2a129 100644 --- a/clients/js/src/embeddings/TransformersEmbeddingFunction.ts +++ b/clients/js/src/embeddings/TransformersEmbeddingFunction.ts @@ -39,21 +39,21 @@ export class TransformersEmbeddingFunction implements IEmbeddingFunction { public async generate(texts: string[]): Promise { await this.loadClient(); - // Store a promise that resolves to the pipeline + // Store a promise that resolves to the pipeline this.pipelinePromise = new Promise(async (resolve, reject) => { try { - const pipeline = this.transformersApi + const pipeline = this.transformersApi; - const quantized = this.quantized - const revision = this.revision - const progress_callback = this.progress_callback + const quantized = this.quantized; + const revision = this.revision; + const progress_callback = this.progress_callback; resolve( await pipeline("feature-extraction", this.model, { quantized, revision, progress_callback, - }) + }), ); } catch (e) { reject(e); @@ -66,34 +66,36 @@ export class TransformersEmbeddingFunction implements IEmbeddingFunction { } private async loadClient() { - if(this.transformersApi) return; - try { - // eslint-disable-next-line global-require,import/no-extraneous-dependencies - let { pipeline } = await TransformersEmbeddingFunction.import(); - TransformersApi = pipeline; - } catch (_a) { - // @ts-ignore - if (_a.code === 'MODULE_NOT_FOUND') { - throw new Error("Please install the @xenova/transformers package to use the TransformersEmbeddingFunction, `npm install -S @xenova/transformers`"); - } - throw _a; // Re-throw other errors + if (this.transformersApi) return; + try { + // eslint-disable-next-line global-require,import/no-extraneous-dependencies + let { pipeline } = await TransformersEmbeddingFunction.import(); + TransformersApi = pipeline; + } catch (_a) { + // @ts-ignore + if (_a.code === "MODULE_NOT_FOUND") { + throw new Error( + "Please install the @xenova/transformers package to use the TransformersEmbeddingFunction, `npm install -S @xenova/transformers`", + ); } - this.transformersApi = TransformersApi; + throw _a; // Re-throw other errors + } + this.transformersApi = TransformersApi; } /** @ignore */ static async import(): Promise<{ - // @ts-ignore - pipeline: typeof import("@xenova/transformers"); + // @ts-ignore + pipeline: typeof import("@xenova/transformers"); }> { - try { - // @ts-ignore - const { pipeline } = await import("@xenova/transformers"); - return { pipeline }; - } catch (e) { - throw new Error( - "Please install @xenova/transformers as a dependency with, e.g. `yarn add @xenova/transformers`" - ); - } + try { + // @ts-ignore + const { pipeline } = await import("@xenova/transformers"); + return { pipeline }; + } catch (e) { + throw new Error( + "Please install @xenova/transformers as a dependency with, e.g. `yarn add @xenova/transformers`", + ); + } } } diff --git a/clients/js/src/index.ts b/clients/js/src/index.ts index 27316d1164a..3eb0d0832b6 100644 --- a/clients/js/src/index.ts +++ b/clients/js/src/index.ts @@ -1,45 +1,45 @@ -export { ChromaClient } from './ChromaClient'; -export { AdminClient } from './AdminClient'; -export { CloudClient } from './CloudClient'; -export { Collection } from './Collection'; +export { ChromaClient } from "./ChromaClient"; +export { AdminClient } from "./AdminClient"; +export { CloudClient } from "./CloudClient"; +export { Collection } from "./Collection"; -export { IEmbeddingFunction } from './embeddings/IEmbeddingFunction'; -export { OpenAIEmbeddingFunction } from './embeddings/OpenAIEmbeddingFunction'; -export { CohereEmbeddingFunction } from './embeddings/CohereEmbeddingFunction'; -export { TransformersEmbeddingFunction } from './embeddings/TransformersEmbeddingFunction'; -export { DefaultEmbeddingFunction } from './embeddings/DefaultEmbeddingFunction'; -export { HuggingFaceEmbeddingServerFunction } from './embeddings/HuggingFaceEmbeddingServerFunction'; -export { JinaEmbeddingFunction } from './embeddings/JinaEmbeddingFunction'; -export { GoogleGenerativeAiEmbeddingFunction } from './embeddings/GoogleGeminiEmbeddingFunction'; +export { IEmbeddingFunction } from "./embeddings/IEmbeddingFunction"; +export { OpenAIEmbeddingFunction } from "./embeddings/OpenAIEmbeddingFunction"; +export { CohereEmbeddingFunction } from "./embeddings/CohereEmbeddingFunction"; +export { TransformersEmbeddingFunction } from "./embeddings/TransformersEmbeddingFunction"; +export { DefaultEmbeddingFunction } from "./embeddings/DefaultEmbeddingFunction"; +export { HuggingFaceEmbeddingServerFunction } from "./embeddings/HuggingFaceEmbeddingServerFunction"; +export { JinaEmbeddingFunction } from "./embeddings/JinaEmbeddingFunction"; +export { GoogleGenerativeAiEmbeddingFunction } from "./embeddings/GoogleGeminiEmbeddingFunction"; export { - IncludeEnum, - GetParams, - CollectionType, - CollectionMetadata, - Embedding, - Embeddings, - Metadata, - Metadatas, - Document, - Documents, - ID, - IDs, - Where, - WhereDocument, - GetResponse, - QueryResponse, - ListCollectionsParams, - ChromaClientParams, - CreateCollectionParams, - GetOrCreateCollectionParams, - GetCollectionParams, - DeleteCollectionParams, - AddParams, - UpsertParams, - UpdateParams, - ModifyCollectionParams, - QueryParams, - PeekParams, - DeleteParams -} from './types'; + IncludeEnum, + GetParams, + CollectionType, + CollectionMetadata, + Embedding, + Embeddings, + Metadata, + Metadatas, + Document, + Documents, + ID, + IDs, + Where, + WhereDocument, + GetResponse, + QueryResponse, + ListCollectionsParams, + ChromaClientParams, + CreateCollectionParams, + GetOrCreateCollectionParams, + GetCollectionParams, + DeleteCollectionParams, + AddParams, + UpsertParams, + UpdateParams, + ModifyCollectionParams, + QueryParams, + PeekParams, + DeleteParams, +} from "./types"; diff --git a/clients/js/src/types.ts b/clients/js/src/types.ts index 6be162338ca..92e66f516c7 100644 --- a/clients/js/src/types.ts +++ b/clients/js/src/types.ts @@ -2,10 +2,10 @@ import { AuthOptions } from "./auth"; import { IEmbeddingFunction } from "./embeddings/IEmbeddingFunction"; export enum IncludeEnum { - Documents = 'documents', - Embeddings = 'embeddings', - Metadatas = 'metadatas', - Distances = 'distances' + Documents = "documents", + Embeddings = "embeddings", + Metadatas = "metadatas", + Distances = "distances", } type Number = number; @@ -31,7 +31,9 @@ type InclusionOperator = "$in" | "$nin"; type WhereOperator = "$gt" | "$gte" | "$lt" | "$lte" | "$ne" | "$eq"; type OperatorExpression = { - [key in WhereOperator | InclusionOperator | LogicalOperator ]?: LiteralValue | ListLiteralValue; + [key in WhereOperator | InclusionOperator | LogicalOperator]?: + | LiteralValue + | ListLiteralValue; }; type BaseWhere = { @@ -47,7 +49,10 @@ export type Where = BaseWhere | LogicalWhere; type WhereDocumentOperator = "$contains" | "$not_contains" | LogicalOperator; export type WhereDocument = { - [key in WhereDocumentOperator]?: LiteralValue | LiteralNumber | WhereDocument[]; + [key in WhereDocumentOperator]?: + | LiteralValue + | LiteralNumber + | WhereDocument[]; }; export type CollectionType = { @@ -70,11 +75,11 @@ export type QueryResponse = { documents: (null | Document)[][]; metadatas: (null | Metadata)[][]; distances: null | number[][]; -} +}; export type AddResponse = { error: string; -} +}; export type CollectionMetadata = Record; @@ -85,72 +90,72 @@ export type ConfigOptions = { }; export type GetParams = { - ids?: ID | IDs, - where?: Where, - limit?: PositiveInteger, - offset?: PositiveInteger, - include?: IncludeEnum[], - whereDocument?: WhereDocument -} + ids?: ID | IDs; + where?: Where; + limit?: PositiveInteger; + offset?: PositiveInteger; + include?: IncludeEnum[]; + whereDocument?: WhereDocument; +}; export type ListCollectionsParams = { - limit?: PositiveInteger, - offset?: PositiveInteger, -} + limit?: PositiveInteger; + offset?: PositiveInteger; +}; export type ChromaClientParams = { - path?: string, - fetchOptions?: RequestInit, - auth?: AuthOptions, - tenant?: string, - database?: string, -} + path?: string; + fetchOptions?: RequestInit; + auth?: AuthOptions; + tenant?: string; + database?: string; +}; export type CreateCollectionParams = { - name: string, - metadata?: CollectionMetadata, - embeddingFunction?: IEmbeddingFunction -} + name: string; + metadata?: CollectionMetadata; + embeddingFunction?: IEmbeddingFunction; +}; -export type GetOrCreateCollectionParams = CreateCollectionParams +export type GetOrCreateCollectionParams = CreateCollectionParams; export type GetCollectionParams = { name: string; - embeddingFunction?: IEmbeddingFunction -} + embeddingFunction?: IEmbeddingFunction; +}; export type DeleteCollectionParams = { - name: string -} + name: string; +}; export type AddParams = { - ids: ID | IDs, - embeddings?: Embedding | Embeddings, - metadatas?: Metadata | Metadatas, - documents?: Document | Documents, -} + ids: ID | IDs; + embeddings?: Embedding | Embeddings; + metadatas?: Metadata | Metadatas; + documents?: Document | Documents; +}; export type UpsertParams = AddParams; export type UpdateParams = AddParams; export type ModifyCollectionParams = { - name?: string, - metadata?: CollectionMetadata -} + name?: string; + metadata?: CollectionMetadata; +}; export type QueryParams = { - queryEmbeddings?: Embedding | Embeddings, - nResults?: PositiveInteger, - where?: Where, - queryTexts?: string | string[], - whereDocument?: WhereDocument, // {"$contains":"search_string"} - include?: IncludeEnum[] // ["metadata", "document"] -} + queryEmbeddings?: Embedding | Embeddings; + nResults?: PositiveInteger; + where?: Where; + queryTexts?: string | string[]; + whereDocument?: WhereDocument; // {"$contains":"search_string"} + include?: IncludeEnum[]; // ["metadata", "document"] +}; -export type PeekParams = { limit?: PositiveInteger } +export type PeekParams = { limit?: PositiveInteger }; export type DeleteParams = { - ids?: ID | IDs, - where?: Where, - whereDocument?: WhereDocument -} + ids?: ID | IDs; + where?: Where; + whereDocument?: WhereDocument; +}; diff --git a/clients/js/src/utils.ts b/clients/js/src/utils.ts index e3ad5361e61..cf43c18ccb3 100644 --- a/clients/js/src/utils.ts +++ b/clients/js/src/utils.ts @@ -13,7 +13,7 @@ export function toArray(obj: T | Array): Array { // a function to convert an array to array of arrays export function toArrayOfArrays( - obj: Array> | Array + obj: Array> | Array, ): Array> { if (Array.isArray(obj[0])) { return obj as Array>; @@ -56,7 +56,7 @@ export async function handleError(error: unknown) { } export async function handleSuccess( - response: Response | string | Count200Response + response: Response | string | Count200Response, ) { switch (true) { case response instanceof Response: @@ -83,17 +83,24 @@ export async function importOptionalModule(moduleName: string) { return Function(`return import("${moduleName}")`)(); } +export async function validateTenantDatabase( + adminClient: AdminClient, + tenant: string, + database: string, +): Promise { + try { + await adminClient.getTenant({ name: tenant }); + } catch (error) { + throw new Error( + `Error: ${error}, Could not connect to tenant ${tenant}. Are you sure it exists?`, + ); + } -export async function validateTenantDatabase(adminClient: AdminClient, tenant: string, database: string): Promise { - try { - await adminClient.getTenant({name: tenant}); - } catch (error) { - throw new Error(`Error: ${error}, Could not connect to tenant ${tenant}. Are you sure it exists?`); - } - - try { - await adminClient.getDatabase({name: database, tenantName: tenant}); - } catch (error) { - throw new Error(`Error: ${error}, Could not connect to database ${database} for tenant ${tenant}. Are you sure it exists?`); - } + try { + await adminClient.getDatabase({ name: database, tenantName: tenant }); + } catch (error) { + throw new Error( + `Error: ${error}, Could not connect to database ${database} for tenant ${tenant}. Are you sure it exists?`, + ); + } } diff --git a/clients/js/test/add.collections.test.ts b/clients/js/test/add.collections.test.ts index 7ac271ff98e..b569f36bedc 100644 --- a/clients/js/test/add.collections.test.ts +++ b/clients/js/test/add.collections.test.ts @@ -1,10 +1,10 @@ -import { expect, test } from '@jest/globals'; -import chroma from './initClient' -import { DOCUMENTS, EMBEDDINGS, IDS } from './data'; -import { METADATAS } from './data'; +import { expect, test } from "@jest/globals"; +import chroma from "./initClient"; +import { DOCUMENTS, EMBEDDINGS, IDS } from "./data"; +import { METADATAS } from "./data"; import { IncludeEnum } from "../src/types"; -import {OpenAIEmbeddingFunction} from "../src/embeddings/OpenAIEmbeddingFunction"; -import {CohereEmbeddingFunction} from "../src/embeddings/CohereEmbeddingFunction"; +import { OpenAIEmbeddingFunction } from "../src/embeddings/OpenAIEmbeddingFunction"; +import { CohereEmbeddingFunction } from "../src/embeddings/CohereEmbeddingFunction"; test("it should add single embeddings to a collection", async () => { await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); @@ -15,9 +15,8 @@ test("it should add single embeddings to a collection", async () => { const count = await collection.count(); expect(count).toBe(1); var res = await collection.get({ - ids: [ids], include: [ - IncludeEnum.Embeddings, - ] + ids: [ids], + include: [IncludeEnum.Embeddings], }); expect(res.embeddings![0]).toEqual(embeddings); }); @@ -29,51 +28,55 @@ test("it should add batch embeddings to a collection", async () => { const count = await collection.count(); expect(count).toBe(3); var res = await collection.get({ - ids: IDS, include: [ - IncludeEnum.Embeddings, - ] + ids: IDS, + include: [IncludeEnum.Embeddings], }); expect(res.embeddings).toEqual(EMBEDDINGS); // reverse because of the order of the ids }); - if (!process.env.OPENAI_API_KEY) { - test.skip("it should add OpenAI embeddings", async () => { - }); + test.skip("it should add OpenAI embeddings", async () => {}); } else { test("it should add OpenAI embeddings", async () => { await chroma.reset(); - const embedder = new OpenAIEmbeddingFunction({ openai_api_key: process.env.OPENAI_API_KEY || "" }) - const collection = await chroma.createCollection({ name: "test" ,embeddingFunction: embedder}); + const embedder = new OpenAIEmbeddingFunction({ + openai_api_key: process.env.OPENAI_API_KEY || "", + }); + const collection = await chroma.createCollection({ + name: "test", + embeddingFunction: embedder, + }); const embeddings = await embedder.generate(DOCUMENTS); await collection.add({ ids: IDS, embeddings: embeddings }); const count = await collection.count(); expect(count).toBe(3); var res = await collection.get({ - ids: IDS, include: [ - IncludeEnum.Embeddings, - ] + ids: IDS, + include: [IncludeEnum.Embeddings], }); expect(res.embeddings).toEqual(embeddings); // reverse because of the order of the ids }); } if (!process.env.COHERE_API_KEY) { - test.skip("it should add Cohere embeddings", async () => { - }); + test.skip("it should add Cohere embeddings", async () => {}); } else { test("it should add Cohere embeddings", async () => { await chroma.reset(); - const embedder = new CohereEmbeddingFunction({ cohere_api_key: process.env.COHERE_API_KEY || "" }) - const collection = await chroma.createCollection({ name: "test" ,embeddingFunction: embedder}); + const embedder = new CohereEmbeddingFunction({ + cohere_api_key: process.env.COHERE_API_KEY || "", + }); + const collection = await chroma.createCollection({ + name: "test", + embeddingFunction: embedder, + }); const embeddings = await embedder.generate(DOCUMENTS); await collection.add({ ids: IDS, embeddings: embeddings }); const count = await collection.count(); expect(count).toBe(3); var res = await collection.get({ - ids: IDS, include: [ - IncludeEnum.Embeddings, - ] + ids: IDS, + include: [IncludeEnum.Embeddings], }); expect(res.embeddings).toEqual(embeddings); // reverse because of the order of the ids }); @@ -82,35 +85,38 @@ if (!process.env.COHERE_API_KEY) { test("add documents", async () => { await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); - let resp = await collection.add({ ids: IDS, embeddings: EMBEDDINGS, documents: DOCUMENTS }); - expect(resp).toBe(true) + let resp = await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + documents: DOCUMENTS, + }); + expect(resp).toBe(true); const results = await collection.get({ ids: ["test1"] }); expect(results.documents[0]).toBe("This is a test"); }); -test('It should return an error when inserting duplicate IDs in the same batch', async () => { - await chroma.reset() +test("It should return an error when inserting duplicate IDs in the same batch", async () => { + await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); - const ids = IDS.concat(["test1"]) - const embeddings = EMBEDDINGS.concat([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]) - const metadatas = METADATAS.concat([{ test: 'test1', 'float_value': 0.1 }]) + const ids = IDS.concat(["test1"]); + const embeddings = EMBEDDINGS.concat([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]); + const metadatas = METADATAS.concat([{ test: "test1", float_value: 0.1 }]); try { await collection.add({ ids, embeddings, metadatas }); } catch (e: any) { - expect(e.message).toMatch('duplicates') + expect(e.message).toMatch("duplicates"); } -}) - +}); -test('should error on empty embedding', async () => { - await chroma.reset() +test("should error on empty embedding", async () => { + await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); - const ids = ["id1"] - const embeddings = [[]] - const metadatas = [{ test: 'test1', 'float_value': 0.1 }] + const ids = ["id1"]; + const embeddings = [[]]; + const metadatas = [{ test: "test1", float_value: 0.1 }]; try { await collection.add({ ids, embeddings, metadatas }); } catch (e: any) { - expect(e.message).toMatch('got empty embedding at pos') + expect(e.message).toMatch("got empty embedding at pos"); } -}) \ No newline at end of file +}); diff --git a/clients/js/test/admin.test.ts b/clients/js/test/admin.test.ts index d0ee72db8c4..a0ba58b7fd9 100644 --- a/clients/js/test/admin.test.ts +++ b/clients/js/test/admin.test.ts @@ -3,48 +3,64 @@ import { AdminClient } from "../src/AdminClient"; import adminClient from "./initAdminClient"; test("it should create the admin client connection", async () => { - expect(adminClient).toBeDefined(); - expect(adminClient).toBeInstanceOf(AdminClient); + expect(adminClient).toBeDefined(); + expect(adminClient).toBeInstanceOf(AdminClient); }); test("it should create and get a tenant", async () => { - await adminClient.createTenant({ name: "testTenant" }); - const tenant = await adminClient.getTenant({ name: "testTenant" }); - expect(tenant).toBeDefined(); - expect(tenant).toHaveProperty('name') - expect(tenant.name).toBe("testTenant"); -}) + await adminClient.createTenant({ name: "testTenant" }); + const tenant = await adminClient.getTenant({ name: "testTenant" }); + expect(tenant).toBeDefined(); + expect(tenant).toHaveProperty("name"); + expect(tenant.name).toBe("testTenant"); +}); test("it should create and get a database for a tenant", async () => { - await adminClient.createTenant({ name: "test3" }); - const database = await adminClient.createDatabase({ name: "test", tenantName: "test3" }); - expect(database).toBeDefined(); - expect(database).toHaveProperty('name') - expect(database.name).toBe("test"); - - const getDatabase = await adminClient.getDatabase({ name: "test", tenantName: "test3" }); - expect(getDatabase).toBeDefined(); - expect(getDatabase).toHaveProperty('name') - expect(getDatabase.name).toBe("test"); -}) + await adminClient.createTenant({ name: "test3" }); + const database = await adminClient.createDatabase({ + name: "test", + tenantName: "test3", + }); + expect(database).toBeDefined(); + expect(database).toHaveProperty("name"); + expect(database.name).toBe("test"); + + const getDatabase = await adminClient.getDatabase({ + name: "test", + tenantName: "test3", + }); + expect(getDatabase).toBeDefined(); + expect(getDatabase).toHaveProperty("name"); + expect(getDatabase.name).toBe("test"); +}); // test that it can set the tenant and database test("it should set the tenant and database", async () => { - // doesnt exist so should throw - await expect(adminClient.setTenant({ tenant: "testTenant", database: "testDatabase" })).rejects.toThrow(); + // doesnt exist so should throw + await expect( + adminClient.setTenant({ tenant: "testTenant", database: "testDatabase" }), + ).rejects.toThrow(); - await adminClient.createTenant({ name: "testTenant!" }); - await adminClient.createDatabase({ name: "test3!", tenantName: "testTenant!" }); + await adminClient.createTenant({ name: "testTenant!" }); + await adminClient.createDatabase({ + name: "test3!", + tenantName: "testTenant!", + }); - await adminClient.setTenant({ tenant: "testTenant!", database: "test3!" }); - expect(adminClient.tenant).toBe("testTenant!"); - expect(adminClient.database).toBe("test3!"); + await adminClient.setTenant({ tenant: "testTenant!", database: "test3!" }); + expect(adminClient.tenant).toBe("testTenant!"); + expect(adminClient.database).toBe("test3!"); - // doesnt exist so should throw - await expect(adminClient.setDatabase({database: "testDatabase2"})).rejects.toThrow(); + // doesnt exist so should throw + await expect( + adminClient.setDatabase({ database: "testDatabase2" }), + ).rejects.toThrow(); - await adminClient.createDatabase({ name: "testDatabase2", tenantName: "testTenant!" }); - await adminClient.setDatabase({database: "testDatabase2"}) + await adminClient.createDatabase({ + name: "testDatabase2", + tenantName: "testTenant!", + }); + await adminClient.setDatabase({ database: "testDatabase2" }); - expect(adminClient.database).toBe("testDatabase2"); -}) + expect(adminClient.database).toBe("testDatabase2"); +}); diff --git a/clients/js/test/auth.basic.test.ts b/clients/js/test/auth.basic.test.ts index 6253bb758a3..16e3b084126 100644 --- a/clients/js/test/auth.basic.test.ts +++ b/clients/js/test/auth.basic.test.ts @@ -1,33 +1,33 @@ -import {expect, test} from "@jest/globals"; -import {chromaBasic} from "./initClientWithAuth"; +import { expect, test } from "@jest/globals"; +import { chromaBasic } from "./initClientWithAuth"; import chromaNoAuth from "./initClient"; import { ChromaClient } from "../src/ChromaClient"; test("it should get the version without auth needed", async () => { - const version = await chromaNoAuth.version(); - expect(version).toBeDefined(); - expect(version).toMatch(/^[0-9]+\.[0-9]+\.[0-9]+$/); + const version = await chromaNoAuth.version(); + expect(version).toBeDefined(); + expect(version).toMatch(/^[0-9]+\.[0-9]+\.[0-9]+$/); }); test("it should get the heartbeat without auth needed", async () => { - const heartbeat = await chromaNoAuth.heartbeat(); - expect(heartbeat).toBeDefined(); - expect(heartbeat).toBeGreaterThan(0); + const heartbeat = await chromaNoAuth.heartbeat(); + expect(heartbeat).toBeDefined(); + expect(heartbeat).toBeGreaterThan(0); }); test("it should raise error when non authenticated", async () => { - await expect(chromaNoAuth.listCollections()).rejects.toMatchObject({ - status: 401 - }); + await expect(chromaNoAuth.listCollections()).rejects.toMatchObject({ + status: 401, + }); }); -test('it should list collections', async () => { - await chromaBasic.reset() - let collections = await chromaBasic.listCollections() - expect(collections).toBeDefined() - expect(collections).toBeInstanceOf(Array) - expect(collections.length).toBe(0) - await chromaBasic.createCollection({name: "test"}); - collections = await chromaBasic.listCollections() - expect(collections.length).toBe(1) -}) +test("it should list collections", async () => { + await chromaBasic.reset(); + let collections = await chromaBasic.listCollections(); + expect(collections).toBeDefined(); + expect(collections).toBeInstanceOf(Array); + expect(collections.length).toBe(0); + await chromaBasic.createCollection({ name: "test" }); + collections = await chromaBasic.listCollections(); + expect(collections.length).toBe(1); +}); diff --git a/clients/js/test/auth.token.test.ts b/clients/js/test/auth.token.test.ts index a57ea09e1f5..3d371faed0b 100644 --- a/clients/js/test/auth.token.test.ts +++ b/clients/js/test/auth.token.test.ts @@ -1,70 +1,79 @@ -import {expect, test} from "@jest/globals"; -import {ChromaClient} from "../src/ChromaClient"; -import {chromaTokenDefault, chromaTokenBearer, chromaTokenXToken, cloudClient} from "./initClientWithAuth"; +import { expect, test } from "@jest/globals"; +import { ChromaClient } from "../src/ChromaClient"; +import { + chromaTokenDefault, + chromaTokenBearer, + chromaTokenXToken, + cloudClient, +} from "./initClientWithAuth"; import chromaNoAuth from "./initClient"; test("it should get the version without auth needed", async () => { - const version = await chromaNoAuth.version(); - expect(version).toBeDefined(); - expect(version).toMatch(/^[0-9]+\.[0-9]+\.[0-9]+$/); + const version = await chromaNoAuth.version(); + expect(version).toBeDefined(); + expect(version).toMatch(/^[0-9]+\.[0-9]+\.[0-9]+$/); }); test("it should get the heartbeat without auth needed", async () => { - const heartbeat = await chromaNoAuth.heartbeat(); - expect(heartbeat).toBeDefined(); - expect(heartbeat).toBeGreaterThan(0); + const heartbeat = await chromaNoAuth.heartbeat(); + expect(heartbeat).toBeDefined(); + expect(heartbeat).toBeGreaterThan(0); }); test("it should raise error when non authenticated", async () => { - await expect(chromaNoAuth.listCollections()).rejects.toMatchObject({ - status: 401 - }); + await expect(chromaNoAuth.listCollections()).rejects.toMatchObject({ + status: 401, + }); }); if (!process.env.XTOKEN_TEST) { - test('it should list collections with default token config', async () => { - await chromaTokenDefault.reset() - let collections = await chromaTokenDefault.listCollections() - expect(collections).toBeDefined() - expect(collections).toBeInstanceOf(Array) - expect(collections.length).toBe(0) - const collection = await chromaTokenDefault.createCollection({name: "test"}); - collections = await chromaTokenDefault.listCollections() - expect(collections.length).toBe(1) - }) + test("it should list collections with default token config", async () => { + await chromaTokenDefault.reset(); + let collections = await chromaTokenDefault.listCollections(); + expect(collections).toBeDefined(); + expect(collections).toBeInstanceOf(Array); + expect(collections.length).toBe(0); + const collection = await chromaTokenDefault.createCollection({ + name: "test", + }); + collections = await chromaTokenDefault.listCollections(); + expect(collections.length).toBe(1); + }); - test('it should list collections with explicit bearer token config', async () => { - await chromaTokenBearer.reset() - let collections = await chromaTokenBearer.listCollections() - expect(collections).toBeDefined() - expect(collections).toBeInstanceOf(Array) - expect(collections.length).toBe(0) - const collection = await chromaTokenBearer.createCollection({name: "test"}); - collections = await chromaTokenBearer.listCollections() - expect(collections.length).toBe(1) - }) + test("it should list collections with explicit bearer token config", async () => { + await chromaTokenBearer.reset(); + let collections = await chromaTokenBearer.listCollections(); + expect(collections).toBeDefined(); + expect(collections).toBeInstanceOf(Array); + expect(collections.length).toBe(0); + const collection = await chromaTokenBearer.createCollection({ + name: "test", + }); + collections = await chromaTokenBearer.listCollections(); + expect(collections.length).toBe(1); + }); } else { + test("it should list collections with explicit x-token token config", async () => { + await chromaTokenXToken.reset(); + let collections = await chromaTokenXToken.listCollections(); + expect(collections).toBeDefined(); + expect(collections).toBeInstanceOf(Array); + expect(collections.length).toBe(0); + const collection = await chromaTokenXToken.createCollection({ + name: "test", + }); + collections = await chromaTokenXToken.listCollections(); + expect(collections.length).toBe(1); + }); - test('it should list collections with explicit x-token token config', async () => { - await chromaTokenXToken.reset() - let collections = await chromaTokenXToken.listCollections() - expect(collections).toBeDefined() - expect(collections).toBeInstanceOf(Array) - expect(collections.length).toBe(0) - const collection = await chromaTokenXToken.createCollection({name: "test"}); - collections = await chromaTokenXToken.listCollections() - expect(collections.length).toBe(1) - }) - - test('it should list collections with explicit x-token token config in CloudClient', async () => { - await cloudClient.reset() - let collections = await cloudClient.listCollections() - expect(collections).toBeDefined() - expect(collections).toBeInstanceOf(Array) - expect(collections.length).toBe(0) - const collection = await cloudClient.createCollection({name: "test"}); - collections = await cloudClient.listCollections() - expect(collections.length).toBe(1) - }) - + test("it should list collections with explicit x-token token config in CloudClient", async () => { + await cloudClient.reset(); + let collections = await cloudClient.listCollections(); + expect(collections).toBeDefined(); + expect(collections).toBeInstanceOf(Array); + expect(collections.length).toBe(0); + const collection = await cloudClient.createCollection({ name: "test" }); + collections = await cloudClient.listCollections(); + expect(collections.length).toBe(1); + }); } diff --git a/clients/js/test/client.test.ts b/clients/js/test/client.test.ts index 512237a2457..d9d85486d0d 100644 --- a/clients/js/test/client.test.ts +++ b/clients/js/test/client.test.ts @@ -3,193 +3,198 @@ import { ChromaClient } from "../src/ChromaClient"; import chroma from "./initClient"; test("it should create the client connection", async () => { - expect(chroma).toBeDefined(); - expect(chroma).toBeInstanceOf(ChromaClient); + expect(chroma).toBeDefined(); + expect(chroma).toBeInstanceOf(ChromaClient); }); test("it should get the version", async () => { - const version = await chroma.version(); - expect(version).toBeDefined(); - expect(version).toMatch(/^[0-9]+\.[0-9]+\.[0-9]+$/); + const version = await chroma.version(); + expect(version).toBeDefined(); + expect(version).toMatch(/^[0-9]+\.[0-9]+\.[0-9]+$/); }); test("it should get the heartbeat", async () => { - const heartbeat = await chroma.heartbeat(); - expect(heartbeat).toBeDefined(); - expect(heartbeat).toBeGreaterThan(0); + const heartbeat = await chroma.heartbeat(); + expect(heartbeat).toBeDefined(); + expect(heartbeat).toBeGreaterThan(0); }); test("it should reset the database", async () => { - await chroma.reset(); - const collections = await chroma.listCollections(); - expect(collections).toBeDefined(); - expect(collections).toBeInstanceOf(Array); - expect(collections.length).toBe(0); - - const collection = await chroma.createCollection({ name: "test" }); - const collections2 = await chroma.listCollections(); - expect(collections2).toBeDefined(); - expect(collections2).toBeInstanceOf(Array); - expect(collections2.length).toBe(1); - - await chroma.reset(); - const collections3 = await chroma.listCollections(); - expect(collections3).toBeDefined(); - expect(collections3).toBeInstanceOf(Array); - expect(collections3.length).toBe(0); + await chroma.reset(); + const collections = await chroma.listCollections(); + expect(collections).toBeDefined(); + expect(collections).toBeInstanceOf(Array); + expect(collections.length).toBe(0); + + const collection = await chroma.createCollection({ name: "test" }); + const collections2 = await chroma.listCollections(); + expect(collections2).toBeDefined(); + expect(collections2).toBeInstanceOf(Array); + expect(collections2.length).toBe(1); + + await chroma.reset(); + const collections3 = await chroma.listCollections(); + expect(collections3).toBeDefined(); + expect(collections3).toBeInstanceOf(Array); + expect(collections3.length).toBe(0); }); -test('it should list collections', async () => { - await chroma.reset() - let collections = await chroma.listCollections() - expect(collections).toBeDefined() - expect(collections).toBeInstanceOf(Array) - expect(collections.length).toBe(0) - const collection = await chroma.createCollection({ name: "test" }); - collections = await chroma.listCollections() - expect(collections.length).toBe(1) -}) - -test('it should get a collection', async () => { - await chroma.reset() - const collection = await chroma.createCollection({ name: "test" }); - const collection2 = await chroma.getCollection({ name: "test" }); - expect(collection).toBeDefined() - expect(collection2).toBeDefined() - expect(collection).toHaveProperty('name') - expect(collection2).toHaveProperty('name') - expect(collection.name).toBe(collection2.name) - expect(collection).toHaveProperty('id') - expect(collection2).toHaveProperty('id') - expect(collection.id).toBe(collection2.id) -}) - -test('it should delete a collection', async () => { - await chroma.reset() - const collection = await chroma.createCollection({ name: "test" }); - let collections = await chroma.listCollections() - expect(collections.length).toBe(1) - var resp = await chroma.deleteCollection({ name: "test" }); - collections = await chroma.listCollections() - expect(collections.length).toBe(0) -}) - -test('it should add single embeddings to a collection', async () => { - await chroma.reset() - const collection = await chroma.createCollection({ name: "test" }); - const ids = 'test1' - const embeddings = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - const metadatas = { test: 'test' } - await collection.add({ ids, embeddings, metadatas }) - const count = await collection.count() - expect(count).toBe(1) -}) - -test('it should add batch embeddings to a collection', async () => { - await chroma.reset() - const collection = await chroma.createCollection({ name: "test" }); - const ids = ['test1', 'test2', 'test3'] - const embeddings = [ - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] - ] - await collection.add({ ids, embeddings }) - const count = await collection.count() - expect(count).toBe(3) -}) - -test('it should query a collection', async () => { - await chroma.reset() - const collection = await chroma.createCollection({ name: "test" }); - const ids = ['test1', 'test2', 'test3'] - const embeddings = [ - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] - ] - await collection.add({ ids, embeddings }) - const results = await collection.query({ queryEmbeddings: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], nResults: 2 }) - expect(results).toBeDefined() - expect(results).toBeInstanceOf(Object) - // expect(results.embeddings[0].length).toBe(2) - const result: string[] = ['test1', 'test2'] - expect(result).toEqual(expect.arrayContaining(results.ids[0])); - expect(['test3']).not.toEqual(expect.arrayContaining(results.ids[0])); -}) - -test('it should peek a collection', async () => { - await chroma.reset() - const collection = await chroma.createCollection({ name: "test" }); - const ids = ['test1', 'test2', 'test3'] - const embeddings = [ - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] - ] - await collection.add({ ids, embeddings }) - const results = await collection.peek({ limit: 2 }) - expect(results).toBeDefined() - expect(results).toBeInstanceOf(Object) - expect(results.ids.length).toBe(2) - expect(['test1', 'test2']).toEqual(expect.arrayContaining(results.ids)); -}) - -test('it should get a collection', async () => { - await chroma.reset() - const collection = await chroma.createCollection({ name: "test" }); - const ids = ['test1', 'test2', 'test3'] - const embeddings = [ - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] - ] - const metadatas = [{ test: 'test1' }, { test: 'test2' }, { test: 'test3' }] - await collection.add({ ids, embeddings, metadatas }) - const results = await collection.get({ ids: ['test1'] }) - expect(results).toBeDefined() - expect(results).toBeInstanceOf(Object) - expect(results.ids.length).toBe(1) - expect(['test1']).toEqual(expect.arrayContaining(results.ids)); - expect(['test2']).not.toEqual(expect.arrayContaining(results.ids)); - - const results2 = await collection.get({ where: { 'test': 'test1' } }) - expect(results2).toBeDefined() - expect(results2).toBeInstanceOf(Object) - expect(results2.ids.length).toBe(1) -}) - -test('it should delete a collection', async () => { - await chroma.reset() - const collection = await chroma.createCollection({ name: "test" }); - const ids = ['test1', 'test2', 'test3'] - const embeddings = [ - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] - ] - const metadatas = [{ test: 'test1' }, { test: 'test2' }, { test: 'test3' }] - await collection.add({ ids, embeddings, metadatas }) - let count = await collection.count() - expect(count).toBe(3) - var resp = await collection.delete({ where: { 'test': 'test1' } }) - count = await collection.count() - expect(count).toBe(2) -}) - -test('wrong code returns an error', async () => { - await chroma.reset() - const collection = await chroma.createCollection({ name: "test" }); - const ids = ['test1', 'test2', 'test3'] - const embeddings = [ - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] - ] - const metadatas = [{ test: 'test1' }, { test: 'test2' }, { test: 'test3' }] - await collection.add({ ids, embeddings, metadatas }) +test("it should list collections", async () => { + await chroma.reset(); + let collections = await chroma.listCollections(); + expect(collections).toBeDefined(); + expect(collections).toBeInstanceOf(Array); + expect(collections.length).toBe(0); + const collection = await chroma.createCollection({ name: "test" }); + collections = await chroma.listCollections(); + expect(collections.length).toBe(1); +}); + +test("it should get a collection", async () => { + await chroma.reset(); + const collection = await chroma.createCollection({ name: "test" }); + const collection2 = await chroma.getCollection({ name: "test" }); + expect(collection).toBeDefined(); + expect(collection2).toBeDefined(); + expect(collection).toHaveProperty("name"); + expect(collection2).toHaveProperty("name"); + expect(collection.name).toBe(collection2.name); + expect(collection).toHaveProperty("id"); + expect(collection2).toHaveProperty("id"); + expect(collection.id).toBe(collection2.id); +}); + +test("it should delete a collection", async () => { + await chroma.reset(); + const collection = await chroma.createCollection({ name: "test" }); + let collections = await chroma.listCollections(); + expect(collections.length).toBe(1); + var resp = await chroma.deleteCollection({ name: "test" }); + collections = await chroma.listCollections(); + expect(collections.length).toBe(0); +}); + +test("it should add single embeddings to a collection", async () => { + await chroma.reset(); + const collection = await chroma.createCollection({ name: "test" }); + const ids = "test1"; + const embeddings = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + const metadatas = { test: "test" }; + await collection.add({ ids, embeddings, metadatas }); + const count = await collection.count(); + expect(count).toBe(1); +}); + +test("it should add batch embeddings to a collection", async () => { + await chroma.reset(); + const collection = await chroma.createCollection({ name: "test" }); + const ids = ["test1", "test2", "test3"]; + const embeddings = [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + ]; + await collection.add({ ids, embeddings }); + const count = await collection.count(); + expect(count).toBe(3); +}); + +test("it should query a collection", async () => { + await chroma.reset(); + const collection = await chroma.createCollection({ name: "test" }); + const ids = ["test1", "test2", "test3"]; + const embeddings = [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + ]; + await collection.add({ ids, embeddings }); + const results = await collection.query({ + queryEmbeddings: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + nResults: 2, + }); + expect(results).toBeDefined(); + expect(results).toBeInstanceOf(Object); + // expect(results.embeddings[0].length).toBe(2) + const result: string[] = ["test1", "test2"]; + expect(result).toEqual(expect.arrayContaining(results.ids[0])); + expect(["test3"]).not.toEqual(expect.arrayContaining(results.ids[0])); +}); + +test("it should peek a collection", async () => { + await chroma.reset(); + const collection = await chroma.createCollection({ name: "test" }); + const ids = ["test1", "test2", "test3"]; + const embeddings = [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + ]; + await collection.add({ ids, embeddings }); + const results = await collection.peek({ limit: 2 }); + expect(results).toBeDefined(); + expect(results).toBeInstanceOf(Object); + expect(results.ids.length).toBe(2); + expect(["test1", "test2"]).toEqual(expect.arrayContaining(results.ids)); +}); + +test("it should get a collection", async () => { + await chroma.reset(); + const collection = await chroma.createCollection({ name: "test" }); + const ids = ["test1", "test2", "test3"]; + const embeddings = [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + ]; + const metadatas = [{ test: "test1" }, { test: "test2" }, { test: "test3" }]; + await collection.add({ ids, embeddings, metadatas }); + const results = await collection.get({ ids: ["test1"] }); + expect(results).toBeDefined(); + expect(results).toBeInstanceOf(Object); + expect(results.ids.length).toBe(1); + expect(["test1"]).toEqual(expect.arrayContaining(results.ids)); + expect(["test2"]).not.toEqual(expect.arrayContaining(results.ids)); + + const results2 = await collection.get({ where: { test: "test1" } }); + expect(results2).toBeDefined(); + expect(results2).toBeInstanceOf(Object); + expect(results2.ids.length).toBe(1); +}); + +test("it should delete a collection", async () => { + await chroma.reset(); + const collection = await chroma.createCollection({ name: "test" }); + const ids = ["test1", "test2", "test3"]; + const embeddings = [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + ]; + const metadatas = [{ test: "test1" }, { test: "test2" }, { test: "test3" }]; + await collection.add({ ids, embeddings, metadatas }); + let count = await collection.count(); + expect(count).toBe(3); + var resp = await collection.delete({ where: { test: "test1" } }); + count = await collection.count(); + expect(count).toBe(2); +}); + +test("wrong code returns an error", async () => { + await chroma.reset(); + const collection = await chroma.createCollection({ name: "test" }); + const ids = ["test1", "test2", "test3"]; + const embeddings = [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + ]; + const metadatas = [{ test: "test1" }, { test: "test2" }, { test: "test3" }]; + await collection.add({ ids, embeddings, metadatas }); + const results = await collection.get({ // @ts-ignore - supposed to fail - const results = await collection.get({ where: { "test": { "$contains": "hello" } } }); - expect(results.error).toBeDefined() - expect(results.error).toContain("ValueError('Expected where operator") -}) + where: { test: { $contains: "hello" } }, + }); + expect(results.error).toBeDefined(); + expect(results.error).toContain("ValueError('Expected where operator"); +}); diff --git a/clients/js/test/collection.client.test.ts b/clients/js/test/collection.client.test.ts index 0045067c7ea..660808504dc 100644 --- a/clients/js/test/collection.client.test.ts +++ b/clients/js/test/collection.client.test.ts @@ -19,29 +19,44 @@ test("it should create a collection", async () => { const collection = await chroma.createCollection({ name: "test" }); expect(collection).toBeDefined(); expect(collection).toHaveProperty("name"); - expect(collection).toHaveProperty('id') + expect(collection).toHaveProperty("id"); expect(collection.name).toBe("test"); let collections = await chroma.listCollections(); - expect([{ name: "test", metadata: null, id: collection.id, database: "default_database", tenant: "default_tenant" }]).toEqual( - expect.arrayContaining(collections) - ); + expect([ + { + name: "test", + metadata: null, + id: collection.id, + database: "default_database", + tenant: "default_tenant", + }, + ]).toEqual(expect.arrayContaining(collections)); expect([{ name: "test2", metadata: null }]).not.toEqual( - expect.arrayContaining(collections) + expect.arrayContaining(collections), ); await chroma.reset(); - const collection2 = await chroma.createCollection({ name: "test2", metadata: { test: "test" } }); + const collection2 = await chroma.createCollection({ + name: "test2", + metadata: { test: "test" }, + }); expect(collection2).toBeDefined(); expect(collection2).toHaveProperty("name"); - expect(collection2).toHaveProperty('id') + expect(collection2).toHaveProperty("id"); expect(collection2.name).toBe("test2"); expect(collection2).toHaveProperty("metadata"); expect(collection2.metadata).toHaveProperty("test"); expect(collection2.metadata).toEqual({ test: "test" }); let collections2 = await chroma.listCollections(); - expect([{ name: "test2", metadata: { test: "test" }, id: collection2.id, database: "default_database", tenant: "default_tenant" }]).toEqual( - expect.arrayContaining(collections2) - ); + expect([ + { + name: "test2", + metadata: { test: "test" }, + id: collection2.id, + database: "default_database", + tenant: "default_tenant", + }, + ]).toEqual(expect.arrayContaining(collections2)); }); test("it should get a collection", async () => { diff --git a/clients/js/test/collection.test.ts b/clients/js/test/collection.test.ts index 4e4919d4932..b6604c48356 100644 --- a/clients/js/test/collection.test.ts +++ b/clients/js/test/collection.test.ts @@ -24,7 +24,7 @@ test("it should modify collection", async () => { const collection3 = await chroma.createCollection({ name: original_name, - metadata: original_metadata + metadata: original_metadata, }); expect(collection3.name).toBe(original_name); expect(collection3.metadata).toEqual(original_metadata); @@ -48,7 +48,10 @@ test("it should modify collection", async () => { test("it should store metadata", async () => { await chroma.reset(); - const collection = await chroma.createCollection({ name: "test", metadata: { test: "test" } }); + const collection = await chroma.createCollection({ + name: "test", + metadata: { test: "test" }, + }); expect(collection.metadata).toEqual({ test: "test" }); // get the collection diff --git a/clients/js/test/delete.collection.test.ts b/clients/js/test/delete.collection.test.ts index a192972b599..c4a3f8310ee 100644 --- a/clients/js/test/delete.collection.test.ts +++ b/clients/js/test/delete.collection.test.ts @@ -5,7 +5,11 @@ import { EMBEDDINGS, IDS, METADATAS } from "./data"; test("it should delete a collection", async () => { await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + }); let count = await collection.count(); expect(count).toBe(3); var resp = await collection.delete({ where: { test: "test1" } }); @@ -14,6 +18,6 @@ test("it should delete a collection", async () => { var remainingEmbeddings = await collection.get(); expect(["test2", "test3"]).toEqual( - expect.arrayContaining(remainingEmbeddings.ids) + expect.arrayContaining(remainingEmbeddings.ids), ); }); diff --git a/clients/js/test/get.collection.test.ts b/clients/js/test/get.collection.test.ts index 95473a5b638..93323654e4c 100644 --- a/clients/js/test/get.collection.test.ts +++ b/clients/js/test/get.collection.test.ts @@ -5,7 +5,11 @@ import { DOCUMENTS, EMBEDDINGS, IDS, METADATAS } from "./data"; test("it should get a collection", async () => { await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + }); const results = await collection.get({ ids: ["test1"] }); expect(results).toBeDefined(); expect(results).toBeInstanceOf(Object); @@ -23,12 +27,16 @@ test("it should get a collection", async () => { test("wrong code returns an error", async () => { await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + }); const results = await collection.get({ where: { //@ts-ignore supposed to fail test: { $contains: "hello" }, - } + }, }); expect(results.error).toBeDefined(); expect(results.error).toContain("ValueError"); @@ -37,8 +45,15 @@ test("wrong code returns an error", async () => { test("it should get embedding with matching documents", async () => { await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS, documents: DOCUMENTS }); - const results2 = await collection.get({ whereDocument: { $contains: "This is a test" } }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + documents: DOCUMENTS, + }); + const results2 = await collection.get({ + whereDocument: { $contains: "This is a test" }, + }); expect(results2).toBeDefined(); expect(results2).toBeInstanceOf(Object); expect(results2.ids.length).toBe(1); @@ -48,28 +63,38 @@ test("it should get embedding with matching documents", async () => { test("it should get records not matching", async () => { await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS, documents: DOCUMENTS }); - const results2 = await collection.get({ whereDocument: { $not_contains: "This is another" } }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + documents: DOCUMENTS, + }); + const results2 = await collection.get({ + whereDocument: { $not_contains: "This is another" }, + }); expect(results2).toBeDefined(); expect(results2).toBeInstanceOf(Object); expect(results2.ids.length).toBe(2); - expect(["test1","test3"]).toEqual(expect.arrayContaining(results2.ids)); + expect(["test1", "test3"]).toEqual(expect.arrayContaining(results2.ids)); }); test("test gt, lt, in a simple small way", async () => { await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + }); const items = await collection.get({ where: { float_value: { $gt: -1.4 } } }); expect(items.ids.length).toBe(2); expect(["test2", "test3"]).toEqual(expect.arrayContaining(items.ids)); }); - test("it should throw an error if the collection does not exist", async () => { await chroma.reset(); await expect( - async () => await chroma.getCollection({ name: "test" }) + async () => await chroma.getCollection({ name: "test" }), ).rejects.toThrow(Error); }); diff --git a/clients/js/test/initClientWithAuth.ts b/clients/js/test/initClientWithAuth.ts index 0fd55c4e7d2..e92ad4df508 100644 --- a/clients/js/test/initClientWithAuth.ts +++ b/clients/js/test/initClientWithAuth.ts @@ -1,16 +1,34 @@ -import {ChromaClient} from "../src/ChromaClient"; +import { ChromaClient } from "../src/ChromaClient"; import { CloudClient } from "../src/CloudClient"; const PORT = process.env.PORT || "8000"; const URL = "http://localhost:" + PORT; -export const chromaBasic = new ChromaClient({path: URL, auth: {provider: "basic", credentials: "admin:admin"}}); -export const chromaTokenDefault = new ChromaClient({path: URL, auth: {provider: "token", credentials: "test-token"}}); +export const chromaBasic = new ChromaClient({ + path: URL, + auth: { provider: "basic", credentials: "admin:admin" }, +}); +export const chromaTokenDefault = new ChromaClient({ + path: URL, + auth: { provider: "token", credentials: "test-token" }, +}); export const chromaTokenBearer = new ChromaClient({ - path: URL, - auth: {provider: "token", credentials: "test-token", providerOptions: {headerType: "AUTHORIZATION"}} + path: URL, + auth: { + provider: "token", + credentials: "test-token", + providerOptions: { headerType: "AUTHORIZATION" }, + }, }); export const chromaTokenXToken = new ChromaClient({ - path: URL, - auth: {provider: "token", credentials: "test-token", providerOptions: {headerType: "X_CHROMA_TOKEN"}} + path: URL, + auth: { + provider: "token", + credentials: "test-token", + providerOptions: { headerType: "X_CHROMA_TOKEN" }, + }, +}); +export const cloudClient = new CloudClient({ + apiKey: "test-token", + cloudPort: PORT, + cloudHost: "http://localhost", }); -export const cloudClient = new CloudClient({apiKey: "test-token", cloudPort: PORT, cloudHost: "http://localhost"}) diff --git a/clients/js/test/query.collection.test.ts b/clients/js/test/query.collection.test.ts index 6e580e8fae8..2809716535c 100644 --- a/clients/js/test/query.collection.test.ts +++ b/clients/js/test/query.collection.test.ts @@ -6,8 +6,7 @@ import { EMBEDDINGS, IDS, METADATAS, DOCUMENTS } from "./data"; import { IEmbeddingFunction } from "../src/embeddings/IEmbeddingFunction"; export class TestEmbeddingFunction implements IEmbeddingFunction { - - constructor() { } + constructor() {} public async generate(texts: string[]): Promise { let embeddings: number[][] = []; @@ -22,7 +21,10 @@ test("it should query a collection", async () => { await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); await collection.add({ ids: IDS, embeddings: EMBEDDINGS }); - const results = await collection.query({ queryEmbeddings: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], nResults: 2 }); + const results = await collection.query({ + queryEmbeddings: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + nResults: 2, + }); expect(results).toBeDefined(); expect(results).toBeInstanceOf(Object); expect(["test1", "test2"]).toEqual(expect.arrayContaining(results.ids[0])); @@ -33,12 +35,17 @@ test("it should query a collection", async () => { test("it should get embedding with matching documents", async () => { await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS, documents: DOCUMENTS }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + documents: DOCUMENTS, + }); const results = await collection.query({ queryEmbeddings: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], nResults: 3, - whereDocument: { $contains: "This is a test" } + whereDocument: { $contains: "This is a test" }, }); // it should only return doc1 @@ -48,14 +55,14 @@ test("it should get embedding with matching documents", async () => { expect(["test1"]).toEqual(expect.arrayContaining(results.ids[0])); expect(["test2"]).not.toEqual(expect.arrayContaining(results.ids[0])); expect(["This is a test"]).toEqual( - expect.arrayContaining(results.documents[0]) + expect.arrayContaining(results.documents[0]), ); const results2 = await collection.query({ queryEmbeddings: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], nResults: 3, whereDocument: { $contains: "This is a test" }, - include: [IncludeEnum.Embeddings] + include: [IncludeEnum.Embeddings], }); // expect(results2.embeddings[0][0]).toBeInstanceOf(Array); @@ -63,37 +70,48 @@ test("it should get embedding with matching documents", async () => { expect(results2.embeddings![0][0]).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); }); - test("it should exclude documents matching - not_contains", async () => { await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS, documents: DOCUMENTS }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + documents: DOCUMENTS, + }); const results = await collection.query({ queryEmbeddings: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], nResults: 3, - whereDocument: { $not_contains: "This is a test" } + whereDocument: { $not_contains: "This is a test" }, }); // it should only return doc1 expect(results).toBeDefined(); expect(results).toBeInstanceOf(Object); expect(results.ids.length).toBe(1); - expect(["test2","test3"]).toEqual(expect.arrayContaining(results.ids[0])); + expect(["test2", "test3"]).toEqual(expect.arrayContaining(results.ids[0])); }); - // test queryTexts test("it should query a collection with text", async () => { await chroma.reset(); let embeddingFunction = new TestEmbeddingFunction(); - const collection = await chroma.createCollection({ name: "test", embeddingFunction: embeddingFunction }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS, documents: DOCUMENTS }); + const collection = await chroma.createCollection({ + name: "test", + embeddingFunction: embeddingFunction, + }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + documents: DOCUMENTS, + }); const results = await collection.query({ queryTexts: ["test"], nResults: 3, - whereDocument: { $contains: "This is a test" } + whereDocument: { $contains: "This is a test" }, }); expect(results).toBeDefined(); @@ -102,21 +120,28 @@ test("it should query a collection with text", async () => { expect(["test1"]).toEqual(expect.arrayContaining(results.ids[0])); expect(["test2"]).not.toEqual(expect.arrayContaining(results.ids[0])); expect(["This is a test"]).toEqual( - expect.arrayContaining(results.documents[0]) + expect.arrayContaining(results.documents[0]), ); -}) - +}); test("it should query a collection with text and where", async () => { await chroma.reset(); let embeddingFunction = new TestEmbeddingFunction(); - const collection = await chroma.createCollection({ name: "test", embeddingFunction: embeddingFunction }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS, documents: DOCUMENTS }); + const collection = await chroma.createCollection({ + name: "test", + embeddingFunction: embeddingFunction, + }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + documents: DOCUMENTS, + }); const results = await collection.query({ queryTexts: ["test"], nResults: 3, - where: { "float_value" : 2 } + where: { float_value: 2 }, }); expect(results).toBeDefined(); @@ -125,21 +150,28 @@ test("it should query a collection with text and where", async () => { expect(["test3"]).toEqual(expect.arrayContaining(results.ids[0])); expect(["test2"]).not.toEqual(expect.arrayContaining(results.ids[0])); expect(["This is a third test"]).toEqual( - expect.arrayContaining(results.documents[0]) + expect.arrayContaining(results.documents[0]), ); -}) - +}); test("it should query a collection with text and where in", async () => { await chroma.reset(); let embeddingFunction = new TestEmbeddingFunction(); - const collection = await chroma.createCollection({ name: "test", embeddingFunction: embeddingFunction }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS, documents: DOCUMENTS }); + const collection = await chroma.createCollection({ + name: "test", + embeddingFunction: embeddingFunction, + }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + documents: DOCUMENTS, + }); const results = await collection.query({ queryTexts: ["test"], nResults: 3, - where: { "float_value" : { '$in': [2,5,10] }} + where: { float_value: { $in: [2, 5, 10] } }, }); expect(results).toBeDefined(); @@ -148,20 +180,28 @@ test("it should query a collection with text and where in", async () => { expect(["test3"]).toEqual(expect.arrayContaining(results.ids[0])); expect(["test2"]).not.toEqual(expect.arrayContaining(results.ids[0])); expect(["This is a third test"]).toEqual( - expect.arrayContaining(results.documents[0]) + expect.arrayContaining(results.documents[0]), ); -}) +}); test("it should query a collection with text and where nin", async () => { await chroma.reset(); let embeddingFunction = new TestEmbeddingFunction(); - const collection = await chroma.createCollection({ name: "test", embeddingFunction: embeddingFunction }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS, documents: DOCUMENTS }); + const collection = await chroma.createCollection({ + name: "test", + embeddingFunction: embeddingFunction, + }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + documents: DOCUMENTS, + }); const results = await collection.query({ queryTexts: ["test"], nResults: 3, - where: { "float_value" : { '$nin': [-2,0] }} + where: { float_value: { $nin: [-2, 0] } }, }); expect(results).toBeDefined(); @@ -170,6 +210,6 @@ test("it should query a collection with text and where nin", async () => { expect(["test3"]).toEqual(expect.arrayContaining(results.ids[0])); expect(["test2"]).not.toEqual(expect.arrayContaining(results.ids[0])); expect(["This is a third test"]).toEqual( - expect.arrayContaining(results.documents[0]) + expect.arrayContaining(results.documents[0]), ); -}) +}); diff --git a/clients/js/test/update.collection.test.ts b/clients/js/test/update.collection.test.ts index e96f21d5b06..77537ac6bfb 100644 --- a/clients/js/test/update.collection.test.ts +++ b/clients/js/test/update.collection.test.ts @@ -6,7 +6,12 @@ import { IDS, DOCUMENTS, EMBEDDINGS, METADATAS } from "./data"; test("it should get embedding with matching documents", async () => { await chroma.reset(); const collection = await chroma.createCollection({ name: "test" }); - await collection.add({ ids: IDS, embeddings: EMBEDDINGS, metadatas: METADATAS, documents: DOCUMENTS }); + await collection.add({ + ids: IDS, + embeddings: EMBEDDINGS, + metadatas: METADATAS, + documents: DOCUMENTS, + }); const results = await collection.get({ ids: ["test1"], @@ -14,7 +19,7 @@ test("it should get embedding with matching documents", async () => { IncludeEnum.Embeddings, IncludeEnum.Metadatas, IncludeEnum.Documents, - ] + ], }); expect(results).toBeDefined(); expect(results).toBeInstanceOf(Object); @@ -24,7 +29,7 @@ test("it should get embedding with matching documents", async () => { ids: ["test1"], embeddings: [[1, 2, 3, 4, 5, 6, 7, 8, 9, 11]], metadatas: [{ test: "test1new" }], - documents: ["doc1new"] + documents: ["doc1new"], }); const results2 = await collection.get({ @@ -33,7 +38,7 @@ test("it should get embedding with matching documents", async () => { IncludeEnum.Embeddings, IncludeEnum.Metadatas, IncludeEnum.Documents, - ] + ], }); expect(results2).toBeDefined(); expect(results2).toBeInstanceOf(Object); diff --git a/clients/js/test/upsert.collections.test.ts b/clients/js/test/upsert.collections.test.ts index 9ce00820e2d..0fc1cdf1ef4 100644 --- a/clients/js/test/upsert.collections.test.ts +++ b/clients/js/test/upsert.collections.test.ts @@ -1,27 +1,26 @@ -import { expect, test } from '@jest/globals'; -import chroma from './initClient' +import { expect, test } from "@jest/globals"; +import chroma from "./initClient"; +test("it should upsert embeddings to a collection", async () => { + await chroma.reset(); + const collection = await chroma.createCollection({ name: "test" }); + const ids = ["test1", "test2"]; + const embeddings = [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + ]; + await collection.add({ ids, embeddings }); + const count = await collection.count(); + expect(count).toBe(2); -test('it should upsert embeddings to a collection', async () => { - await chroma.reset() - const collection = await chroma.createCollection({ name: "test" }); - const ids = ['test1', 'test2'] - const embeddings = [ - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] - ] - await collection.add({ ids, embeddings }) - const count = await collection.count() - expect(count).toBe(2) + const ids2 = ["test2", "test3"]; + const embeddings2 = [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 15], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + ]; - const ids2 = ["test2", "test3"] - const embeddings2 = [ - [1, 2, 3, 4, 5, 6, 7, 8, 9, 15], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - ] + await collection.upsert({ ids: ids2, embeddings: embeddings2 }); - await collection.upsert({ ids: ids2, embeddings: embeddings2 }) - - const count2 = await collection.count() - expect(count2).toBe(3) -}) + const count2 = await collection.count(); + expect(count2).toBe(3); +}); diff --git a/clients/js/tsconfig.json b/clients/js/tsconfig.json index 632127ed709..c46b79fc3ba 100644 --- a/clients/js/tsconfig.json +++ b/clients/js/tsconfig.json @@ -1,7 +1,5 @@ { - "include": [ - "src" - ], + "include": ["src"], "compilerOptions": { "declaration": true, "module": "ESNext", diff --git a/clients/js/tsup.config.ts b/clients/js/tsup.config.ts index 4b0cd8e264e..aff2492dbea 100644 --- a/clients/js/tsup.config.ts +++ b/clients/js/tsup.config.ts @@ -1,32 +1,32 @@ -import { defineConfig, Options } from 'tsup' -import fs from 'fs' +import { defineConfig, Options } from "tsup"; +import fs from "fs"; export default defineConfig((options: Options) => { const commonOptions: Partial = { entry: { - chromadb: 'src/index.ts' + chromadb: "src/index.ts", }, sourcemap: true, dts: true, - ...options - } + ...options, + }; return [ { ...commonOptions, - format: ['esm'], - outExtension: () => ({ js: '.mjs' }), + format: ["esm"], + outExtension: () => ({ js: ".mjs" }), clean: true, async onSuccess() { // Support Webpack 4 by pointing `"module"` to a file with a `.js` extension - fs.copyFileSync('dist/chromadb.mjs', 'dist/chromadb.legacy-esm.js') - } + fs.copyFileSync("dist/chromadb.mjs", "dist/chromadb.legacy-esm.js"); + }, }, { ...commonOptions, - format: 'cjs', - outDir: './dist/cjs/', - outExtension: () => ({ js: '.cjs' }) - } - ] -}) + format: "cjs", + outDir: "./dist/cjs/", + outExtension: () => ({ js: ".cjs" }), + }, + ]; +});