Skip to content

Commit 6dcfd01

Browse files
committed
feat: Updated Chroma Examples
- Added examples for connect, create collection, using embedding functions, auth and persistent dirs
1 parent 441743c commit 6dcfd01

File tree

3 files changed

+152
-52
lines changed

3 files changed

+152
-52
lines changed

docs/modules/chromadb.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,37 @@
88
npm install @testcontainers/chromadb --save-dev
99
```
1010

11-
## Example
11+
## Resources
12+
13+
* [GitHub](https://github.com/chroma-core/chroma)
14+
* [Node.js Client](https://www.npmjs.com/package/chromadb)
15+
* [Docs](https://docs.trychroma.com)
16+
* [Discord](https://discord.gg/MMeYNTmh3x)
17+
* [Cookbook](https://cookbook.chromadb.dev)
18+
19+
## Examples
20+
21+
<!--codeinclude-->
22+
[Connect to Chroma:](../../packages/modules/chromadb/src/chromadb-container.test.ts)
23+
inside_block:simpleConnect
24+
<!--/codeinclude-->
25+
26+
<!--codeinclude-->
27+
[Create Collection:](../../packages/modules/chromadb/src/chromadb-container.test.ts)
28+
inside_block:createCollection
29+
<!--/codeinclude-->
30+
31+
<!--codeinclude-->
32+
[Query Collection with Embedding Function:](../../packages/modules/chromadb/src/chromadb-container.test.ts)
33+
inside_block:queryCollectionWithEmbeddingFunction
34+
<!--/codeinclude-->
35+
36+
<!--codeinclude-->
37+
[Work with persistent directory:](../../packages/modules/chromadb/src/chromadb-container.test.ts)
38+
inside_block:persistentData
39+
<!--/codeinclude-->
1240

1341
<!--codeinclude-->
14-
[](../../packages/modules/chromadb/src/chromadb-container.test.ts) inside_block:docs
42+
[Work with authentication:](../../packages/modules/chromadb/src/chromadb-container.test.ts) inside_block:auth
1543
<!--/codeinclude-->
1644

Lines changed: 121 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,133 @@
1-
import { ChromaClient, AdminClient } from "chromadb";
2-
import { ChromaDBContainer } from "./chromadb-container";
3-
1+
import { ChromaClient, AdminClient, OllamaEmbeddingFunction } from "chromadb";
2+
import { ChromaDBContainer, StartedChromaDBContainer } from "./chromadb-container";
3+
import * as path from "node:path";
4+
import { GenericContainer } from "testcontainers";
5+
import * as os from "node:os";
6+
import * as fs from "node:fs";
7+
// run tests with NODE_OPTIONS=--experimental-vm-modules jest packages/modules/chromadb/src/chromadb-container.test.ts
48
describe("ChromaDB", () => {
5-
jest.setTimeout(180_000);
9+
jest.setTimeout(240_000);
10+
11+
// startContainer {
12+
it("should connect", async () => {
13+
const container = await new ChromaDBContainer().start();
14+
const client = await connectTo(container);
15+
expect(await client.heartbeat()).toBeDefined();
16+
// Do something with the client
17+
await container.stop();
18+
});
19+
// }
20+
21+
// simpleConnect {
22+
async function connectTo(container: StartedChromaDBContainer) {
23+
const client = new ChromaClient({
24+
path: container.getHttpUrl(),
25+
});
26+
const hb = await client.heartbeat();
27+
expect(hb).toBeDefined();
28+
return client;
29+
}
30+
// }
631

7-
// docs {
8-
it("should connect and return a query result", async () => {
32+
// createCollection {
33+
it("should create collection and get data", async () => {
934
const container = await new ChromaDBContainer().start();
35+
const client = await connectTo(container);
36+
const collection = await client.createCollection({ name: "test", metadata: { "hnsw:space": "cosine" } });
37+
expect(collection.name).toBe("test");
38+
expect(collection.metadata).toBeDefined();
39+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
40+
// @ts-ignore
41+
expect(collection.metadata["hnsw:space"]).toBe("cosine");
42+
await collection.add({ ids: ["1"], embeddings: [[1, 2, 3]], documents: ["my doc"], metadatas: [{ key: "value" }] });
43+
const getResults = await collection.get({ ids: ["1"] });
44+
expect(getResults.ids[0]).toBe("1");
45+
expect(getResults.documents[0]).toStrictEqual("my doc");
46+
expect(getResults.metadatas).toBeDefined();
47+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
48+
// @ts-ignore
49+
expect(getResults.metadatas[0].key).toStrictEqual("value");
50+
await container.stop();
51+
});
52+
// }
53+
54+
// queryCollectionWithEmbeddingFunction {
55+
it("should create collection and query", async () => {
56+
const container = await new ChromaDBContainer().start();
57+
const ollama = await new GenericContainer("ollama/ollama").withExposedPorts(11434).start();
58+
await ollama.exec(["ollama", "pull", "nomic-embed-text"]);
59+
const client = await connectTo(container);
60+
const embedder = new OllamaEmbeddingFunction({
61+
url: `http://${ollama.getHost()}:${ollama.getMappedPort(11434)}/api/embeddings`,
62+
model: "nomic-embed-text",
63+
});
64+
const collection = await client.createCollection({
65+
name: "test",
66+
metadata: { "hnsw:space": "cosine" },
67+
embeddingFunction: embedder,
68+
});
69+
expect(collection.name).toBe("test");
70+
await collection.add({
71+
ids: ["1", "2"],
72+
documents: [
73+
"This is a document about dogs. Dogs are awesome.",
74+
"This is a document about cats. Cats are awesome.",
75+
],
76+
});
77+
const results = await collection.query({ queryTexts: ["Tell me about dogs"], nResults: 1 });
78+
expect(results).toBeDefined();
79+
expect(results.ids[0]).toEqual(["1"]);
80+
expect(results.ids[0][0]).toBe("1");
81+
await container.stop();
82+
});
83+
84+
// persistentData {
85+
it("should reconnect with volume and persistence data", async () => {
86+
const sourcePath = fs.mkdtempSync(path.join(os.tmpdir(), "chroma-temp"));
87+
const container = await new ChromaDBContainer()
88+
.withBindMounts([{ source: sourcePath, target: "/chroma/chroma" }])
89+
.start();
90+
const client = await connectTo(container);
91+
const collection = await client.createCollection({ name: "test", metadata: { "hnsw:space": "cosine" } });
92+
expect(collection.name).toBe("test");
93+
expect(collection.metadata).toBeDefined();
94+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
95+
// @ts-ignore
96+
expect(collection.metadata["hnsw:space"]).toBe("cosine");
97+
await collection.add({ ids: ["1"], embeddings: [[1, 2, 3]], documents: ["my doc"] });
98+
const getResults = await collection.get({ ids: ["1"] });
99+
expect(getResults.ids[0]).toBe("1");
100+
expect(getResults.documents[0]).toStrictEqual("my doc");
101+
await container.stop();
102+
expect(fs.existsSync(`${sourcePath}/chroma.sqlite3`)).toBe(true);
103+
try {
104+
fs.rmSync(sourcePath, { force: true, recursive: true });
105+
} catch (e) {
106+
//Ignore clean up, when have no access on fs.
107+
console.log(e);
108+
}
109+
});
110+
// }
111+
112+
// auth {
113+
it("should use auth", async () => {
10114
const tenant = "test-tenant";
11115
const key = "test-key";
12116
const database = "test-db";
117+
const container = await new ChromaDBContainer()
118+
.withEnvironment({
119+
CHROMA_SERVER_AUTHN_CREDENTIALS: key,
120+
CHROMA_SERVER_AUTHN_PROVIDER: "chromadb.auth.token_authn.TokenAuthenticationServerProvider",
121+
CHROMA_AUTH_TOKEN_TRANSPORT_HEADER: "X_CHROMA_TOKEN",
122+
})
123+
.start();
124+
13125
const adminClient = new AdminClient({
14126
tenant: tenant,
15127
auth: {
16128
provider: "token",
17129
credentials: key,
18-
providerOptions: {
19-
headerType: "X_CHROMA_TOKEN",
20-
},
130+
tokenHeaderType: "X_CHROMA_TOKEN",
21131
},
22132
path: container.getHttpUrl(),
23133
});
@@ -30,52 +140,14 @@ describe("ChromaDB", () => {
30140
auth: {
31141
provider: "token",
32142
credentials: key,
33-
providerOptions: {
34-
headerType: "X_CHROMA_TOKEN",
35-
},
143+
tokenHeaderType: "X_CHROMA_TOKEN",
36144
},
37145
path: container.getHttpUrl(),
38146
database,
39147
});
40148

41149
const collection = await dbClient.createCollection({ name: "test-collection" });
42-
43-
await collection.add({
44-
ids: ["1", "2", "3"],
45-
documents: ["apple", "oranges", "pineapple"],
46-
embeddings: [
47-
[1, 2, 3],
48-
[4, 5, 6],
49-
[7, 8, 9],
50-
],
51-
});
52-
53-
const result = await collection.get({ ids: ["1", "2", "3"] });
54-
55-
expect(result).toMatchInlineSnapshot(`
56-
{
57-
"data": null,
58-
"documents": [
59-
"apple",
60-
"oranges",
61-
"pineapple",
62-
],
63-
"embeddings": null,
64-
"ids": [
65-
"1",
66-
"2",
67-
"3",
68-
],
69-
"metadatas": [
70-
null,
71-
null,
72-
null,
73-
],
74-
"uris": null,
75-
}
76-
`);
77-
78-
await container.stop();
150+
expect(collection.name).toBe("test-collection");
79151
});
80152
// }
81153
});

packages/modules/chromadb/src/chromadb-container.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { AbstractStartedContainer, GenericContainer, StartedTestContainer, Wait
33
const CHROMADB_PORT = 8000;
44

55
export class ChromaDBContainer extends GenericContainer {
6-
constructor(image = "chromadb/chroma:0.4.22") {
6+
constructor(image = "chromadb/chroma:0.5.0") {
77
super(image);
88
this.withExposedPorts(CHROMADB_PORT)
99
.withWaitStrategy(Wait.forHttp("/api/v1/heartbeat", CHROMADB_PORT))

0 commit comments

Comments
 (0)