Skip to content

Commit ef864cb

Browse files
rescrvAnush008
andauthored
feat: Add support for Chroma's Database feature. (#16)
* Add support for Chroma's Database feature. Different collections can be in different databases. Passing the optional database argument at client creation time binds the client to the appropriate database. * Fix doctest example * chore: Review updates Signed-off-by: Anush008 <[email protected]> * docs: nit update README.md Signed-off-by: Anush008 <[email protected]> --------- Co-authored-by: Anush008 <[email protected]>
1 parent 4d3919d commit ef864cb

File tree

7 files changed

+118
-159
lines changed

7 files changed

+118
-159
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

+29-29
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,57 @@
11
<div align="center">
22
<h1><a href="https://crates.io/crates/chromadb">ChromaDB-rs</a></h1>
3-
<h3>A Rust based client library for the Chroma vector database.</h3>
3+
<h3>A Rust client library for the Chroma vector database.</h3>
44
<a href="https://crates.io/crates/chromadb"><img src="https://img.shields.io/crates/v/chromadb.svg" alt="Crates.io"></a>
55
<a href="https://github.com/Anush008/chromadb-rs/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-mit-blue.svg" alt="MIT Licensed"></a>
66
<a href="https://github.com/Anush008/chromadb-rs/actions/workflows/cargo-test.yml"><img src="https://github.com/Anush008/chromadb-rs/actions/workflows/release.yml/badge.svg?branch=master" alt="Tests"></a>
77
</div>
88

9-
## ⚙️ Running ChromaDB
10-
> ℹ Chroma can be run in-memory in Python (without Docker), but this feature is not yet available in other languages.
11-
> To use this library you either need a hosted or local version of ChromaDB running.
12-
13-
If you can run `docker-compose up -d --build` you can run Chroma.
14-
15-
```shell
16-
git clone https://github.com/chroma-core/chroma.git
17-
cd chroma
18-
# Run a ChromaDB instance at localhost:8000
19-
docker-compose up -d --build
20-
```
21-
22-
More information about deploying Chroma to production can be found [here](https://docs.trychroma.com/deployment).
23-
249
## 🚀 Installing the library
10+
2511
```shell
2612
cargo add chromadb
2713
```
28-
The library crate can be found at [crates.io](https://crates.io/crates/chromadb).
14+
15+
The crate can be found at [crates.io](https://crates.io/crates/chromadb).
2916

3017
## 📖 Documentation
18+
3119
The library reference can be found [here](https://docs.rs/chromadb).
3220

3321
## 🔍 Overview
3422

35-
#### The library provides 2 modules to interact with the ChromaDB server via API V1:
36-
* `client` - To interface with the ChromaDB server.
37-
* `collection` - To interface with an associated ChromaDB collection.
23+
#### The library provides 2 modules to interact with the ChromaDB server via API V1
24+
25+
* `client` - To interface with the ChromaDB server.
26+
* `collection` - To interface with an associated ChromaDB collection.
3827

3928
#### You can connect to ChromaDB by instantiating a [ChromaClient](https://docs.rs/chromadb/latest/chromadb/v1/client/struct.ChromaClient.html)
40-
29+
4130
```rust
42-
use chromadb::v2::ChromaClient;
31+
use chromadb::v2::client::{ChromaAuthMethod, ChromaClient, ChromaClientOptions, ChromaTokenHeader};
4332
use chromadb::v2::collection::{ChromaCollection, GetQuery, GetResult, CollectionEntries};
44-
use serde_json::json;
4533

4634
// With default ChromaClientOptions
4735
// Defaults to http://localhost:8000
4836
let client: ChromaClient = ChromaClient::new(Default::default());
4937

5038
// With custom ChromaClientOptions
51-
let client: ChromaClient = ChromaClient::new(ChromaClientOptions { url: "<CHROMADB_URL>".into() });
39+
let auth = ChromaAuthMethod::TokenAuth {
40+
token: "<TOKEN>".to_string(),
41+
header: ChromaTokenHeader::Authorization
42+
};
43+
let client: ChromaClient = ChromaClient::new(ChromaClientOptions {
44+
url: "<CHROMADB_URL>".into(),
45+
database: Some("<DATABASE>".into()),
46+
auth
47+
});
5248
```
5349

54-
#### Now that a client is instantiated, we can interface with the ChromaDB server.
50+
#### Now that a client is instantiated, we can interface with the ChromaDB server
5551

5652
```rust
53+
use serde_json::json;
54+
5755
// Get or create a collection with the given name and no metadata.
5856
let collection: ChromaCollection = client.get_or_create_collection("my_collection", None).await?;
5957

@@ -62,7 +60,7 @@ let collection_uuid = collection.id();
6260
println!("Collection UUID: {}", collection_uuid);
6361
```
6462

65-
### With a collection instance, we can perform queries on the database
63+
### With a collection instance, we can perform queries on the database
6664

6765
```rust
6866
// Upsert some embeddings with documents and no metadata.
@@ -81,7 +79,7 @@ let result: bool = collection.upsert(collection_entries, None).await?;
8179
// Create a filter object to filter by document content.
8280
let where_document = json!({
8381
"$contains": "Superman"
84-
});
82+
});
8583

8684
// Get embeddings from a collection with filters and limit set to 1.
8785
// An empty IDs vec will return all embeddings.
@@ -97,10 +95,11 @@ let get_result: GetResult = collection.get(get_query).await?;
9795
println!("Get result: {:?}", get_result);
9896

9997
```
100-
Find more information about the available filters and options in the [get()](https://docs.rs/chromadb/latest/chromadb/v1/collection/struct.ChromaCollection.html#method.get) documentation.
10198

99+
Find more information about the available filters and options in the [get()](https://docs.rs/chromadb/latest/chromadb/v1/collection/struct.ChromaCollection.html#method.get) documentation.
102100

103101
### Performing a similarity search
102+
104103
```rust
105104
//Instantiate QueryOptions to perform a similarity search on the collection
106105
//Alternatively, an embedding_function can also be provided with query_texts to perform the search
@@ -117,7 +116,8 @@ let query_result: QueryResult = collection.query(query, None).await?;
117116
println!("Query result: {:?}", query_result);
118117
```
119118

120-
### Support for Embedding providers
119+
### Support for Embedding providers
120+
121121
This crate has built-in support for OpenAI and SBERT embeddings.
122122

123123
To use [OpenAI](https://platform.openai.com/docs/guides/embeddings) embeddings, enable the `openai` feature in your Cargo.toml.

src/lib.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//!
77
//! ### Instantiating [ChromaClient](crate::v2::ChromaClient)
88
//! ```
9-
//! use chromadb::v2::client::{ChromaAuthMethod, ChromaClient, ChromaClientOptions};
9+
//! use chromadb::v2::client::{ChromaAuthMethod, ChromaClient, ChromaClientOptions, ChromaTokenHeader};
1010
//! use chromadb::v2::collection::{ChromaCollection, GetResult, GetOptions};
1111
//! use serde_json::json;
1212
//!
@@ -16,13 +16,14 @@
1616
//! let client: ChromaClient = ChromaClient::new(Default::default());
1717
//!
1818
//! // With custom ChromaClientOptions
19-
//! let auth = ChromaAuthMethod::TokenAuth {
20-
//! token: "<TOKEN>".to_string(),
21-
//! header: chromadb::v2::client::ChromaTokenHeader::Authorization
19+
//! let auth = ChromaAuthMethod::TokenAuth {
20+
//! token: "<TOKEN>".to_string(),
21+
//! header: ChromaTokenHeader::Authorization
2222
//! };
23-
//! let client: ChromaClient = ChromaClient::new(ChromaClientOptions {
23+
//! let client: ChromaClient = ChromaClient::new(ChromaClientOptions {
2424
//! url: "<CHROMADB_URL>".into(),
25-
//! auth
25+
//! database: Some("<DATABASE>".into()),
26+
//! auth
2627
//! });
2728
//!
2829
//! # Ok(())

src/v2/api.rs

+30-19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::commons::Result;
22
use base64::prelude::*;
3-
use reqwest::{Client, Response, Method};
3+
use reqwest::{Client, Method, Response};
44
use serde_json::Value;
55

66
#[derive(Clone, Debug)]
@@ -12,7 +12,10 @@ pub enum ChromaTokenHeader {
1212
#[derive(Clone, Debug)]
1313
pub enum ChromaAuthMethod {
1414
None,
15-
BasicAuth { username: String, password: String },
15+
BasicAuth {
16+
username: String,
17+
password: String,
18+
},
1619
TokenAuth {
1720
token: String,
1821
header: ChromaTokenHeader,
@@ -29,14 +32,16 @@ impl Default for ChromaAuthMethod {
2932
pub(super) struct APIClientAsync {
3033
pub(super) api_endpoint: String,
3134
pub(super) auth_method: ChromaAuthMethod,
35+
pub(super) database: Option<String>,
3236
client: Client,
3337
}
3438

3539
impl APIClientAsync {
36-
pub fn new(endpoint: String, auth_method: ChromaAuthMethod) -> Self {
40+
pub fn new(endpoint: String, auth_method: ChromaAuthMethod, database: Option<String>) -> Self {
3741
Self {
3842
api_endpoint: format!("{}/api/v1", endpoint),
3943
auth_method,
44+
database,
4045
client: Client::new(),
4146
}
4247
}
@@ -57,29 +62,35 @@ impl APIClientAsync {
5762
self.send_request(Method::DELETE, path, None).await
5863
}
5964

60-
async fn send_request(&self, method: Method, path: &str, json_body: Option<Value>) -> Result<Response> {
61-
let url = format!("{}{}", self.api_endpoint, path);
62-
63-
let mut request = self.client
64-
.request(method, &url);
65+
async fn send_request(
66+
&self,
67+
method: Method,
68+
path: &str,
69+
json_body: Option<Value>,
70+
) -> Result<Response> {
71+
let url = if let Some(database) = &self.database {
72+
format!("{}{}?database={}", self.api_endpoint, path, database)
73+
} else {
74+
format!("{}{}", self.api_endpoint, path,)
75+
};
76+
77+
let mut request = self.client.request(method, &url);
6578

6679
// Add auth headers if needed
6780
match &self.auth_method {
68-
ChromaAuthMethod::None => {},
81+
ChromaAuthMethod::None => {}
6982
ChromaAuthMethod::BasicAuth { username, password } => {
7083
let credentials = BASE64_STANDARD.encode(format!("{username}:{password}"));
7184
request = request.header("Authorization", format!("Basic {credentials}"));
7285
}
73-
ChromaAuthMethod::TokenAuth { token, header } => {
74-
match header {
75-
ChromaTokenHeader::Authorization => {
76-
request = request.header("Authorization", format!("Bearer {token}"));
77-
}
78-
ChromaTokenHeader::XChromaToken => {
79-
request = request.header("X-Chroma-Token", token);
80-
}
86+
ChromaAuthMethod::TokenAuth { token, header } => match header {
87+
ChromaTokenHeader::Authorization => {
88+
request = request.header("Authorization", format!("Bearer {token}"));
8189
}
82-
}
90+
ChromaTokenHeader::XChromaToken => {
91+
request = request.header("X-Chroma-Token", token);
92+
}
93+
},
8394
}
8495

8596
// Add JSON body if present
@@ -97,7 +108,7 @@ impl APIClientAsync {
97108
} else {
98109
let error_text = response.text().await?;
99110
anyhow::bail!(
100-
"{} {}: {}",
111+
"{} {}: {}",
101112
status.as_u16(),
102113
status.canonical_reason().unwrap_or("Unknown"),
103114
error_text

src/v2/client.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,32 @@ pub struct ChromaClient {
2020
/// The options for instantiating ChromaClient.
2121
#[derive(Debug, Default)]
2222
pub struct ChromaClientOptions {
23+
/// The URL of the Chroma Server.
2324
pub url: String,
25+
/// Authentication to use to connect to the Chroma Server.
2426
pub auth: ChromaAuthMethod,
27+
/// Optional database name to scope all queries to.
28+
pub database: Option<String>,
2529
}
2630

2731
impl ChromaClient {
2832
/// Create a new Chroma client with the given options.
2933
/// * Defaults to `url`: http://localhost:8000
30-
pub fn new(ChromaClientOptions { url, auth }: ChromaClientOptions) -> ChromaClient {
34+
pub fn new(
35+
ChromaClientOptions {
36+
url,
37+
auth,
38+
database,
39+
}: ChromaClientOptions,
40+
) -> ChromaClient {
3141
let endpoint = if url.is_empty() {
3242
std::env::var("CHROMA_URL").unwrap_or(DEFAULT_ENDPOINT.to_string())
3343
} else {
3444
url
3545
};
3646

3747
ChromaClient {
38-
api: Arc::new(APIClientAsync::new(endpoint, auth)),
48+
api: Arc::new(APIClientAsync::new(endpoint, auth, database)),
3949
}
4050
}
4151

@@ -165,7 +175,6 @@ mod tests {
165175
use super::*;
166176
use tokio;
167177

168-
169178
const TEST_COLLECTION: &str = "8-recipies-for-octopus";
170179

171180
#[tokio::test]
@@ -208,7 +217,7 @@ mod tests {
208217
#[tokio::test]
209218
async fn test_get_collection() {
210219
let client: ChromaClient = ChromaClient::new(Default::default());
211-
220+
212221
const GET_TEST_COLLECTION: &str = "100-recipes-for-octopus";
213222

214223
client

0 commit comments

Comments
 (0)