├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── LICENSE
├── README.md
├── composer.json
├── docker-compose.yml
├── examples
├── .gitignore
├── composer.json
└── index.php
├── phpunit.xml
├── src
├── ChromaDB.php
├── Client.php
├── Embeddings
│ ├── EmbeddingFunction.php
│ ├── HuggingFaceEmbeddingServerFunction.php
│ ├── JinaEmbeddingFunction.php
│ ├── MistralAIEmbeddingFunction.php
│ ├── OllamaEmbeddingFunction.php
│ └── OpenAIEmbeddingFunction.php
├── Factory.php
├── Generated
│ ├── ChromaApiClient.php
│ ├── Exceptions
│ │ ├── ChromaAuthorizationException.php
│ │ ├── ChromaConnectionException.php
│ │ ├── ChromaDimensionalityException.php
│ │ ├── ChromaException.php
│ │ ├── ChromaInvalidCollectionException.php
│ │ ├── ChromaNotFoundException.php
│ │ ├── ChromaTypeException.php
│ │ ├── ChromaUniqueConstraintException.php
│ │ ├── ChromaValueException.php
│ │ └── ValidationException.php
│ ├── Models
│ │ ├── Collection.php
│ │ ├── Database.php
│ │ └── Tenant.php
│ ├── Requests
│ │ ├── AddEmbeddingRequest.php
│ │ ├── CreateCollectionRequest.php
│ │ ├── CreateDatabaseRequest.php
│ │ ├── CreateTenantRequest.php
│ │ ├── DeleteEmbeddingRequest.php
│ │ ├── GetEmbeddingRequest.php
│ │ ├── QueryEmbeddingRequest.php
│ │ ├── UpdateCollectionRequest.php
│ │ └── UpdateEmbeddingRequest.php
│ └── Responses
│ │ ├── GetItemsResponse.php
│ │ └── QueryItemsResponse.php
└── Resources
│ └── CollectionResource.php
└── tests
├── ChromaDB.php
└── Client.php
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: tests
2 |
3 | on: [ 'push', 'pull_request', 'workflow_dispatch' ]
4 |
5 | jobs:
6 | test:
7 | runs-on: ubuntu-latest
8 |
9 | strategy:
10 | fail-fast: true
11 | matrix:
12 | php: [ 8.1, 8.2, 8.3, 8.4 ]
13 | dependency-version: [ prefer-lowest, prefer-stable ]
14 |
15 | name: Tests on PHP ${{ matrix.php }} - ${{ matrix.dependency-version }}
16 |
17 | services:
18 | chroma-wo-auth:
19 | image: chromadb/chroma:0.5.0
20 | ports:
21 | - 8000:8000
22 |
23 | chroma-w-auth:
24 | image: chromadb/chroma:0.5.0
25 | ports:
26 | - 8001:8000
27 | env:
28 | CHROMA_SERVER_AUTHN_CREDENTIALS: 'test-token'
29 | CHROMA_SERVER_AUTHN_PROVIDER: 'chromadb.auth.token_authn.TokenAuthenticationServerProvider'
30 |
31 | steps:
32 | - name: Checkout
33 | uses: actions/checkout@v3
34 |
35 | - name: Cache dependencies
36 | uses: actions/cache@v3
37 | with:
38 | path: ~/.composer/cache/files
39 | key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
40 |
41 | - name: Setup PHP
42 | uses: shivammathur/setup-php@v2
43 | with:
44 | php-version: ${{ matrix.php }}
45 | extensions: dom, mbstring, zip
46 | coverage: none
47 |
48 | - name: Install Composer dependencies
49 | run: composer update --${{ matrix.dependency-version }} --no-interaction --prefer-dist
50 |
51 | - name: Run tests
52 | run: composer test
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.phpunit.cache
2 | /.php-cs-fixer.cache
3 | /.php-cs-fixer.php
4 | /composer.lock
5 | /vendor/
6 | *.swp
7 | *.swo
8 | playground/*
9 | .idea
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Obikwelu Kyrian Sochima
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## ChromaDB PHP
2 |
3 | **A PHP library for interacting with [Chroma](https://github.com/chroma-core/chroma) vector database seamlessly.**
4 |
5 | [](https://packagist.org/packages/codewithkyrian/chromadb-php)
6 | [](https://packagist.org/packages/codewithkyrian/chromadb-php)
7 | [](https://github.com/CodeWithKyrian/chromadb-php/blob/main/LICENSE)
8 | [](https://github.com/CodeWithKyrian/chromadb-php/actions/workflows/test.yml)
9 |
10 | > **Note:** This package is framework-agnostic, and can be used in any PHP project. If you're using Laravel however, you
11 | > might want to check out the Laravel-specific package [here](https://github.com/CodeWithKyrian/chromadb-laravel) which
12 | > provides a more Laravel-like experience, and includes a few extra features.
13 |
14 | ## Description
15 |
16 | [Chroma](https://www.trychroma.com/) is an open-source vector database that allows you to store, search, and analyze high-dimensional data at scale.
17 | It is designed to be fast, scalable, and reliable. It makes it easy to build LLM (Large Language Model) applications and
18 | services that require high-dimensional vector search.
19 |
20 | ChromaDB PHP provides a simple and intuitive interface for interacting with Chroma from PHP. It enables you to:
21 |
22 | - Create, read, update, and delete documents.
23 | - Execute queries and aggregations.
24 | - Manage collections and indexes.
25 | - Handle authentication and authorization.
26 | - Utilize other ChromaDB features seamlessly.
27 | - And more...
28 |
29 | ## Small Example
30 |
31 | ```php
32 | use Codewithkyrian\ChromaDB\ChromaDB;
33 |
34 | $chromaDB = ChromaDB::client();
35 |
36 | // Check current ChromaDB version
37 | echo $chromaDB->version();
38 |
39 | // Create a collection
40 | $collection = $chromaDB->createCollection('test-collection');
41 |
42 | echo $collection->name; // test-collection
43 | echo $collection->id; // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx
44 |
45 | // Insert some documents into the collection
46 | $ids = ['test1', 'test2', 'test3'];
47 | $embeddings = [
48 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
49 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
50 | [10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0],
51 | ];
52 | $metadatas = [
53 | ['url' => 'https://example.com/test1'],
54 | ['url' => 'https://example.com/test2'],
55 | ['url' => 'https://example.com/test3'],
56 | ];
57 |
58 | $collection->add($ids, $embeddings, $metadatas);
59 |
60 | // Search for similar embeddings
61 | $queryResponse = $collection->query(
62 | queryEmbeddings: [
63 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
64 | ],
65 | nResults: 2
66 | );
67 |
68 | // Print results
69 | echo $queryResponse->ids[0][0]; // test1
70 | echo $queryResponse->ids[0][1]; // test2
71 |
72 |
73 | ```
74 |
75 | ## Requirements
76 |
77 | - PHP 8.1 or higher
78 | - ChromaDB 0.4.0 or higher running in client/server mode
79 |
80 | ## Running ChromaDB
81 |
82 | In order to use this library, you need to have ChromaDB running somewhere. You can either run it locally or in the
83 | cloud.
84 | (Chroma doesn't support cloud yet, but it will soon.)
85 |
86 | For now, ChromaDB can only run in-memory in Python. You can however run it in client/server mode by either running the
87 | python
88 | project or using the docker image (recommended).
89 |
90 | To run the docker image, you can use the following command:
91 |
92 | ```bash
93 | docker run -p 8000:8000 chromadb/chroma
94 | ```
95 |
96 | You can also pass in some environment variables using a `.env` file:
97 |
98 | ```bash
99 | docker run -p 8000:8000 --env-file .env chromadb/chroma
100 | ```
101 |
102 | Or if you prefer using a docker-compose file, you can use the following:
103 |
104 | ```yaml
105 | version: '3.9'
106 |
107 | services:
108 | chroma:
109 | image: 'chromadb/chroma'
110 | ports:
111 | - '8000:8000'
112 | volumes:
113 | - chroma-data:/chroma/chroma
114 |
115 | volumes:
116 | chroma-data:
117 | driver: local
118 | ```
119 |
120 | And then run it using:
121 |
122 | ```bash
123 | docker-compose up -d
124 | ```
125 |
126 | (Check out the [Chroma Documentation](https://docs.trychroma.com/deployment) for more information on how to run
127 | ChromaDB.)
128 |
129 | Either way, you can now access ChromaDB at `http://localhost:8000`.
130 |
131 | ## Installation
132 |
133 | ```bash
134 | composer require codewithkyrian/chromadb-php
135 | ```
136 |
137 | ## Usage
138 |
139 | ### Connecting to ChromaDB
140 |
141 | ```php
142 | use Codewithkyrian\ChromaDB\ChromaDB;
143 |
144 | $chroma = ChromaDB::client();
145 |
146 | ```
147 |
148 | By default, ChromaDB will try to connect to `http://localhost:8000` using the default database name `default_database`
149 | and default tenant name `default_tenant`. You can however change these values by constructing the client using the
150 | factory method:
151 |
152 | ```php
153 | use Codewithkyrian\ChromaDB\ChromaDB;
154 |
155 | $chroma = ChromaDB::factory()
156 | ->withHost('http://localhost')
157 | ->withPort(8000)
158 | ->withDatabase('new_database')
159 | ->withTenant('new_tenant')
160 | ->connect();
161 | ```
162 |
163 | If the tenant or database doesn't exist, the package will automatically create them for you.
164 |
165 | ### Authentication
166 |
167 | ChromaDB supports static token-based authentication. To use it, you need to start the Chroma server passing the required
168 | environment variables as stated in the documentation. If you're using the docker image, you can pass in the environment
169 | variables using the `--env` flag or by using a `.env` file and for the docker-compose file, you can use the `env_file`
170 | option, or pass in the environment variables directly like so:
171 |
172 | ```yaml
173 | version: '3.9'
174 |
175 | services:
176 | chroma:
177 | image: 'chromadb/chroma'
178 | ports:
179 | - '8000:8000'
180 | environment:
181 | - CHROMA_SERVER_AUTHN_CREDENTIALS=test-token
182 | - CHROMA_SERVER_AUTHN_PROVIDER=chromadb.auth.token_authn.TokenAuthenticationServerProvider
183 |
184 | ...
185 | ```
186 |
187 | You can then connect to ChromaDB using the factory method:
188 |
189 | ```php
190 | use Codewithkyrian\ChromaDB\ChromaDB;
191 |
192 | $chroma = ChromaDB::factory()
193 | ->withAuthToken('test-token')
194 | ->connect();
195 | ```
196 |
197 | ### Getting the version
198 |
199 | ```php
200 |
201 | echo $chroma->version(); // 0.4.0
202 |
203 | ```
204 |
205 | ### Creating a Collection
206 |
207 | Creating a collection is as simple as calling the `createCollection` method on the client and passing in the name of
208 | the collection.
209 |
210 | ```php
211 |
212 | $collection = $chroma->createCollection('test-collection');
213 |
214 | ```
215 |
216 | If the collection already exists in the database, the package will throw an exception.
217 |
218 | ### Inserting Documents
219 |
220 | ```php
221 | $ids = ['test1', 'test2', 'test3'];
222 | $embeddings = [
223 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
224 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
225 | [10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0],
226 | ];
227 | $metadatas = [
228 | ['url' => 'https://example.com/test1'],
229 | ['url' => 'https://example.com/test2'],
230 | ['url' => 'https://example.com/test3'],
231 | ];
232 |
233 | $collection->add($ids, $embeddings, $metadatas);
234 | ```
235 |
236 | To insert documents into a collection, you need to provide the following:
237 |
238 | - `ids`: An array of document ids. The ids must be unique and must be strings.
239 | - `embeddings`: An array of document embeddings. The embeddings must be a 1D array of floats with a consistent length. You
240 | can compute the embeddings using any embedding model of your choice (just make sure that's what you use when querying as
241 | well).
242 | - `metadatas`: An array of document metadatas. The metadatas must be an array of key-value pairs.
243 |
244 | If you don't have the embeddings, you can pass in the documents and provide an embedding function that will be used to
245 | compute the embeddings for you.
246 |
247 | ### Passing in Embedding Function
248 |
249 | To use an embedding function, you need to pass it in as an argument when creating the collection:
250 |
251 | ```php
252 | use CodeWithKyrian\ChromaDB\EmbeddingFunction\EmbeddingFunctionInterface;
253 |
254 | $embeddingFunction = new OpenAIEmbeddingFunction('api-key', 'org-id', 'model-name');
255 |
256 | $collection = $chroma->createCollection('test-collection', embeddingFunction: $embeddingFunction);
257 | ```
258 |
259 | The embedding function must be an instance of `EmbeddingFunctionInterface`. There are a few built-in embedding functions
260 | that you can use:
261 |
262 | - `OpenAIEmbeddingFunction`: This embedding function uses the OpenAI API to compute the embeddings. You can use it like
263 | this:
264 | ```php
265 | use CodeWithKyrian\Chroma\EmbeddingFunction\OpenAIEmbeddingFunction;
266 |
267 | $embeddingFunction = new OpenAIEmbeddingFunction('api-key', 'org-id', 'model-name');
268 |
269 | $collection = $chromaDB->createCollection('test-collection', embeddingFunction: $embeddingFunction);
270 | ```
271 | You can get your OpenAI API key and organization id from your [OpenAI dashboard](https://beta.openai.com/), and you
272 | can omit the organization id if your API key doesn't belong to an organization. The model name is optional as well and
273 | defaults to `text-embedding-ada-002`
274 |
275 | - `JinaEmbeddingFunction`: This is a wrapper for the Jina Embedding models. You can use by passing your Jina API key and
276 | the desired model. THis defaults to `jina-embeddings-v2-base-en`
277 | ```php
278 | use Codewithkyrian\ChromaDB\Embeddings\JinaEmbeddingFunction;
279 |
280 | $embeddingFunction = new JinaEmbeddingFunction('api-key');
281 |
282 | $collection = $chromaDB->createCollection('test-collection', embeddingFunction: $embeddingFunction);
283 | ```
284 |
285 | - `HuggingFaceEmbeddingServerFunction`: This embedding function is a wrapper around the HuggingFace Text Embedding
286 | Server. Before using it, you need to have
287 | the [HuggingFace Embedding Server](https://github.com/huggingface/text-embeddings-inference) running somewhere locally. Here's how you can use it:
288 | ```php
289 | use CodeWithKyrian\Chroma\EmbeddingFunction\HuggingFaceEmbeddingFunction;
290 |
291 | $embeddingFunction = new HuggingFaceEmbeddingFunction('api-key', 'model-name');
292 |
293 | $collection = $chromaDB->createCollection('test-collection', embeddingFunction: $embeddingFunction);
294 | ```
295 |
296 | Besides the built-in embedding functions, you can also create your own embedding function by implementing
297 | the `EmbeddingFunction` interface (including Anonymous Classes):
298 |
299 | ```php
300 | use CodeWithKyrian\ChromaDB\EmbeddingFunction\EmbeddingFunctionInterface;
301 |
302 | $embeddingFunction = new class implements EmbeddingFunctionInterface {
303 | public function generate(array $texts): array
304 | {
305 | // Compute the embeddings here and return them as an array of arrays
306 | }
307 | };
308 |
309 | $collection = $chroma->createCollection('test-collection', embeddingFunction: $embeddingFunction);
310 | ```
311 |
312 | > The embedding function will be called for each batch of documents that are inserted into the collection, and must be
313 | > provided either when creating the collection or when querying the collection. If you don't provide an embedding
314 | > function, and you don't provide the embeddings, the package will throw an exception.
315 |
316 | ### Inserting Documents into a Collection with an Embedding Function
317 |
318 | ```php
319 | $ids = ['test1', 'test2', 'test3'];
320 | $documents = [
321 | 'This is a test document',
322 | 'This is another test document',
323 | 'This is yet another test document',
324 | ];
325 | $metadatas = [
326 | ['url' => 'https://example.com/test1'],
327 | ['url' => 'https://example.com/test2'],
328 | ['url' => 'https://example.com/test3'],
329 | ];
330 |
331 | $collection->add(
332 | ids: $ids,
333 | documents: $documents,
334 | metadatas: $metadatas
335 | );
336 | ```
337 |
338 | ### Getting a Collection
339 |
340 | ```php
341 | $collection = $chromaDB->getCollection('test-collection');
342 | ```
343 |
344 | Or with an embedding function:
345 |
346 | ```php
347 | $collection = $chromaDB->getCollection('test-collection', embeddingFunction: $embeddingFunction);
348 | ```
349 |
350 | > Make sure that the embedding function you provide is the same one that was used when creating the collection.
351 |
352 | ### Counting the items in a collection
353 |
354 | ```php
355 | $collection->count() // 2
356 | ```
357 |
358 | ### Updating a collection
359 |
360 | ```php
361 | $collection->update(
362 | ids: ['test1', 'test2', 'test3'],
363 | embeddings: [
364 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
365 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
366 | [10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0],
367 | ],
368 | metadatas: [
369 | ['url' => 'https://example.com/test1'],
370 | ['url' => 'https://example.com/test2'],
371 | ['url' => 'https://example.com/test3'],
372 | ]
373 | );
374 | ```
375 |
376 | ### Deleting Documents
377 |
378 | ```php
379 | $collection->delete(['test1', 'test2', 'test3']);
380 | ```
381 |
382 | ### Querying a Collection
383 |
384 | ```php
385 | $queryResponse = $collection->query(
386 | queryEmbeddings: [
387 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
388 | ],
389 | nResults: 2
390 | );
391 |
392 | echo $queryResponse->ids[0][0]; // test1
393 | echo $queryResponse->ids[0][1]; // test2
394 | ```
395 |
396 | To query a collection, you need to provide the following:
397 |
398 | - `queryEmbeddings` (optional): An array of query embeddings. The embeddings must be a 1D array of floats. You
399 | can compute the embeddings using any embedding model of your choice (just make sure that's what you use when inserting
400 | as
401 | well).
402 | - `nResults`: The number of results to return. Defaults to 10.
403 | - `queryTexts` (optional): An array of query texts. The texts must be strings. You can omit this if you provide the
404 | embeddings. Here's
405 | an example:
406 | ```php
407 | $queryResponse = $collection->query(
408 | queryTexts: [
409 | 'This is a test document'
410 | ],
411 | nResults: 2
412 | );
413 |
414 | echo $queryResponse->ids[0][0]; // test1
415 | echo $queryResponse->ids[0][1]; // test2
416 | ```
417 | - `where` (optional): The where clause to use to filter items based on their metadata. Here's an example:
418 | ```php
419 | $queryResponse = $collection->query(
420 | queryEmbeddings: [
421 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
422 | ],
423 | nResults: 2,
424 | where: [
425 | 'url' => 'https://example.com/test1'
426 | ]
427 | );
428 |
429 | echo $queryResponse->ids[0][0]; // test1
430 | ```
431 | The where clause must be an array of key-value pairs. The key must be a string, and the value can be a string or
432 | an array of valid filter values. Here are the valid filters (`$eq`, `$ne`, `$in`, `$nin`, `$gt`, `$gte`, `$lt`,
433 | `$lte`):
434 | - `$eq`: Equals
435 | - `$ne`: Not equals
436 | - `$gt`: Greater than
437 | - `$gte`: Greater than or equal to
438 | - `$lt`: Less than
439 | - `$lte`: Less than or equal to
440 |
441 | Here's an example:
442 | ```php
443 | $queryResponse = $collection->query(
444 | queryEmbeddings: [
445 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
446 | ],
447 | nResults: 2,
448 | where: [
449 | 'url' => [
450 | '$eq' => 'https://example.com/test1'
451 | ]
452 | ]
453 | );
454 | ```
455 | You can also use multiple filters:
456 | ```php
457 | $queryResponse = $collection->query(
458 | queryEmbeddings: [
459 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
460 | ],
461 | nResults: 2,
462 | where: [
463 | 'url' => [
464 | '$eq' => 'https://example.com/test1'
465 | ],
466 | 'title' => [
467 | '$ne' => 'Test 1'
468 | ]
469 | ]
470 | );
471 | ```
472 | - `whereDocument` (optional): The where clause to use to filter items based on their document. Here's an example:
473 | ```php
474 | $queryResponse = $collection->query(
475 | queryEmbeddings: [
476 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
477 | ],
478 | nResults: 2,
479 | whereDocument: [
480 | 'text' => 'This is a test document'
481 | ]
482 | );
483 |
484 | echo $queryResponse->ids[0][0]; // test1
485 | ```
486 | The where clause must be an array of key-value pairs. The key must be a string, and the value can be a string or
487 | an array of valid filter values. In this case, only two filtering keys are supported - `$contains`
488 | and `$not_contains`.
489 |
490 | Here's an example:
491 | ```php
492 | $queryResponse = $collection->query(
493 | queryEmbeddings: [
494 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
495 | ],
496 | nResults: 2,
497 | whereDocument: [
498 | 'text' => [
499 | '$contains' => 'test document'
500 | ]
501 | ]
502 | );
503 | ```
504 | - `include` (optional): An array of fields to include in the response. Possible values
505 | are `embeddings`, `documents`, `metadatas` and `distances`. It defaults to `embeddings`
506 | and `metadatas` (`documents` are not included by default because they can be large).
507 | ```php
508 | $queryResponse = $collection->query(
509 | queryEmbeddings: [
510 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
511 | ],
512 | nResults: 2,
513 | include: ['embeddings']
514 | );
515 | ```
516 | `distances` is only valid for querying and not for getting. It returns the distances between the query embeddings
517 | and the embeddings of the results.
518 |
519 | Other relevant information about querying and retrieving a collection can be found in the [ChromaDB Documentation](https://docs.trychroma.com/usage-guide).
520 |
521 | ### Deleting items in a collection
522 |
523 | To delete the documents in a collection, pass in an array of the ids of the items:
524 |
525 | ```php
526 | $collection->delete(['test1', 'test2']);
527 |
528 | $collection->count() // 1
529 | ```
530 |
531 | Passing the ids is optional. You can delete items from a collection using a where filter:
532 |
533 | ```php
534 | $collection->add(
535 | ['test1', 'test2', 'test3'],
536 | [
537 | [1.0, 2.0, 3.0, 4.0, 5.0],
538 | [6.0, 7.0, 8.0, 9.0, 10.0],
539 | [11.0, 12.0, 13.0, 14.0, 15.0],
540 | ],
541 | [
542 | ['some' => 'metadata1'],
543 | ['some' => 'metadata2'],
544 | ['some' => 'metadata3'],
545 | ]
546 | );
547 |
548 | $collection->delete(
549 | where: [
550 | 'some' => 'metadata1'
551 | ]
552 | );
553 |
554 | $collection->count() // 2
555 | ```
556 |
557 | ### Deleting a collection
558 |
559 | Deleting a collection is as simple as passing in the name of the collection to be deleted.
560 |
561 | ```php
562 | $chroma->deleteCollection('test_collection');
563 | ```
564 |
565 | ## Testing
566 |
567 | ```
568 | // Run chroma by running the docker compose file in the repo
569 | docker compose up -d
570 |
571 | composer test
572 | ```
573 |
574 | ## Contributors
575 |
576 | - [Kyrian Obikwelu](https://github.com/CodeWithKyrian)
577 | - Other contributors are welcome.
578 |
579 | ## License
580 |
581 | This project is licensed under the MIT License. See
582 | the [LICENSE](https://github.com/codewithkyrian/chromadb-php/blob/main/LICENSE) file for more information.
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codewithkyrian/chromadb-php",
3 | "description": "A PHP client for the Chroma Open Source Embedding Database",
4 | "keywords": ["chromadb", "php", "embedding", "database", "vectors", "semantic", "search", "chroma", "open-source"],
5 | "type": "library",
6 | "license": "MIT",
7 | "require": {
8 | "php": "^8.1",
9 | "guzzlehttp/guzzle": "^7.0"
10 | },
11 | "require-dev": {
12 | "pestphp/pest": "^2.19",
13 | "symfony/var-dumper": "^6.3",
14 | "mockery/mockery": "^1.6"
15 | },
16 | "autoload": {
17 | "psr-4": {
18 | "Codewithkyrian\\ChromaDB\\": "src/"
19 | }
20 | },
21 | "authors": [
22 | {
23 | "name": "Kyrian Obikwelu",
24 | "email": "kyrianobikwelu@gmail.com"
25 | }
26 | ],
27 | "config": {
28 | "allow-plugins": {
29 | "pestphp/pest-plugin": true
30 | }
31 | },
32 | "scripts": {
33 | "test": "vendor/bin/pest",
34 | "test:coverage": "XDEBUG_MODE=coverage ./vendor/bin/pest --coverage"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.9'
2 |
3 | services:
4 | chroma_wo_auth:
5 | image: 'chromadb/chroma:0.5.0'
6 | ports:
7 | - '8000:8000'
8 |
9 | chroma_w_auth:
10 | image: 'chromadb/chroma:0.5.0'
11 | ports:
12 | - '8001:8000'
13 | environment:
14 | CHROMA_SERVER_AUTHN_CREDENTIALS: 'test-token'
15 | CHROMA_SERVER_AUTHN_PROVIDER: 'chromadb.auth.token_authn.TokenAuthenticationServerProvider'
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.lock
--------------------------------------------------------------------------------
/examples/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kyrian/examples",
3 | "type": "project",
4 | "autoload": {
5 | "psr-4": {
6 | }
7 | },
8 | "authors": [
9 | {
10 | "name": "Kyrian Obikwelu",
11 | "email": "koshnawaza@gmail.com"
12 | }
13 | ],
14 | "repositories": [
15 | {
16 | "type": "path",
17 | "url": "../",
18 | "options": {
19 | "symlink": true
20 | }
21 | }
22 | ],
23 | "require": {
24 | "codewithkyrian/chromadb-php": "@dev",
25 | "symfony/var-dumper": "^6.3"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/index.php:
--------------------------------------------------------------------------------
1 | withDatabase('test_database')
13 | ->withTenant('test_tenant')
14 | ->connect();
15 |
16 | $chroma->deleteAllCollections();
17 |
18 | $embeddingFunction = new OllamaEmbeddingFunction();
19 |
20 | $collection = $chroma->createCollection(
21 | name: 'test_collection',
22 | embeddingFunction: $embeddingFunction
23 | );
24 |
25 |
26 | $collection->add(
27 | ids: ['1', '2', '3'],
28 | documents: ['He seems very happy', 'He was very sad when we last talked', 'She made him angry']
29 | );
30 |
31 | $queryResponse = $collection->query(
32 | queryTexts: ['She annoyed him'],
33 | include: ['documents', 'distances']
34 | );
35 |
36 | dd($queryResponse->documents[0], $queryResponse->distances[0]);
37 |
38 |
39 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | ./tests
10 |
11 |
12 |
13 |
14 | ./src
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/ChromaDB.php:
--------------------------------------------------------------------------------
1 | connect();
13 | }
14 |
15 | /**
16 | * Creates a new factory instance to configure a custom Alchemy Client
17 | */
18 | public static function factory(): Factory
19 | {
20 | return new Factory();
21 | }
22 |
23 | /**
24 | * Resets the database. This will delete all collections and entries and
25 | * return true if the database was reset successfully.
26 | */
27 | public static function reset() : bool
28 | {
29 | return (new Factory())->createApiClient()->reset();
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Client.php:
--------------------------------------------------------------------------------
1 | initDatabaseAndTenant();
22 | }
23 |
24 |
25 | public function initDatabaseAndTenant(): void
26 | {
27 | try {
28 | $this->apiClient->getTenant($this->tenant);
29 | } catch (ChromaNotFoundException) {
30 | $createTenantRequest = new Generated\Requests\CreateTenantRequest($this->tenant);
31 | $this->apiClient->createTenant($createTenantRequest);
32 | }
33 |
34 | try {
35 | $this->apiClient->getDatabase($this->database, $this->tenant);
36 | } catch (ChromaNotFoundException) {
37 | $createDatabaseRequest = new Generated\Requests\CreateDatabaseRequest($this->database);
38 | $this->apiClient->createDatabase($this->tenant, $createDatabaseRequest);
39 | }
40 | }
41 |
42 | /**
43 | * Returns the version of the Chroma API.
44 | */
45 | public function version(): string
46 | {
47 | return $this->apiClient->version();
48 | }
49 |
50 | /**
51 | * Returns the current time in nanoseconds since epoch. This is useful for
52 | * checking if the server is alive.
53 | */
54 | public function heartbeat(): int
55 | {
56 | $res = $this->apiClient->heartbeat();
57 |
58 | return $res['nanosecond heartbeat'] ?? 0;
59 | }
60 |
61 | /**
62 | * Lists all collections.
63 | *
64 | * @return Collection[]
65 | */
66 | public function listCollections(): array
67 | {
68 | return $this->apiClient->listCollections($this->database, $this->tenant);
69 | }
70 |
71 |
72 | /**
73 | * Creates a new collection with the specified properties.
74 | *
75 | * @param string $name The name of the collection.
76 | * @param ?array $metadata Optional metadata associated with the collection.
77 | * @param ?EmbeddingFunction $embeddingFunction Optional custom embedding function for the collection.
78 | *
79 | * @return CollectionResource
80 | */
81 | public function createCollection(string $name, ?array $metadata = null, ?EmbeddingFunction $embeddingFunction = null): CollectionResource
82 | {
83 | $request = new Generated\Requests\CreateCollectionRequest($name, $metadata);
84 |
85 | $collection = $this->apiClient->createCollection($this->database, $this->tenant, $request);
86 |
87 |
88 | return CollectionResource::make(
89 | $collection,
90 | $this->database,
91 | $this->tenant,
92 | $embeddingFunction,
93 | $this->apiClient
94 | );
95 | }
96 |
97 | /**
98 | * Gets or creates a collection with the specified properties.
99 | *
100 | * @param string $name The name of the collection.
101 | * @param ?array $metadata Optional metadata associated with the collection.
102 | * @param ?EmbeddingFunction $embeddingFunction Optional custom embedding function for the collection.
103 | *
104 | * @return CollectionResource
105 | */
106 | public function getOrCreateCollection(string $name, ?array $metadata = null, ?EmbeddingFunction $embeddingFunction = null): CollectionResource
107 | {
108 | $request = new Generated\Requests\CreateCollectionRequest($name, $metadata, true);
109 |
110 | $collection = $this->apiClient->createCollection($this->database, $this->tenant, $request);
111 |
112 | return CollectionResource::make(
113 | $collection,
114 | $this->database,
115 | $this->tenant,
116 | $embeddingFunction,
117 | $this->apiClient
118 | );
119 | }
120 |
121 | /**
122 | * Gets a collection with the specified name. Will raise an exception if the
123 | * collection does not exist.
124 | *
125 | * @param string $name The name of the collection.
126 | * @param ?EmbeddingFunction $embeddingFunction Optional custom embedding function for the collection.
127 | *
128 | * @return CollectionResource
129 | */
130 | public function getCollection(string $name, ?EmbeddingFunction $embeddingFunction = null): CollectionResource
131 | {
132 | $collection = $this->apiClient->getCollection($name, $this->database, $this->tenant);
133 |
134 | return CollectionResource::make(
135 | $collection,
136 | $this->database,
137 | $this->tenant,
138 | $embeddingFunction,
139 | $this->apiClient
140 | );
141 | }
142 |
143 | /**
144 | * Deletes a collection with the specified name.
145 | *
146 | * @param string $name The name of the collection.
147 | */
148 | public function deleteCollection(string $name): void
149 | {
150 | $this->apiClient->deleteCollection($name, $this->database, $this->tenant);
151 | }
152 |
153 | /**
154 | * De
155 | */
156 | public function deleteAllCollections(): void
157 | {
158 | $collections = $this->listCollections();
159 |
160 | foreach ($collections as $collection) {
161 | $this->deleteCollection($collection->name);
162 | }
163 | }
164 |
165 |
166 |
167 | }
--------------------------------------------------------------------------------
/src/Embeddings/EmbeddingFunction.php:
--------------------------------------------------------------------------------
1 | $this->baseUrl,
24 | 'headers' => [
25 | 'Content-Type' => 'application/json',
26 | ]
27 | ]);
28 |
29 | try {
30 | $response = $client->post('embed', [
31 | 'json' => [
32 | 'inputs' => $texts,
33 | ]
34 | ]);
35 | } catch (GuzzleException $e) {
36 | throw new \RuntimeException('Failed to generate embeddings', 0, $e);
37 | }
38 |
39 | return json_decode($response->getBody()->getContents(), true);
40 | }
41 | }
--------------------------------------------------------------------------------
/src/Embeddings/JinaEmbeddingFunction.php:
--------------------------------------------------------------------------------
1 | client = new Client([
23 | 'base_uri' => 'https://api.jina.ai/v1/',
24 | 'headers' => [
25 | 'Authorization' => "Bearer $this->apiKey",
26 | 'Content-Type' => 'application/json',
27 | 'Accept-Encoding' => 'identity',
28 | ]
29 | ]);
30 | }
31 |
32 | /**
33 | * @inheritDoc
34 | */
35 | public function generate(array $texts): array
36 | {
37 | try {
38 | $response = $this->client->post('embeddings', [
39 | 'json' => [
40 | 'model' => $this->model,
41 | 'input' => $texts,
42 | ]
43 | ]);
44 |
45 | $result = json_decode($response->getBody()->getContents(), true);
46 | $embeddings = $result['data'];
47 | usort($embeddings, fn($a, $b) => $a['index'] <=> $b['index']);
48 |
49 | return array_map(fn($embedding) => $embedding['embedding'], $embeddings);
50 | } catch (ClientExceptionInterface $e) {
51 | throw new \RuntimeException("Error calling Jina AI API: {$e->getMessage()}", 0, $e);
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/src/Embeddings/MistralAIEmbeddingFunction.php:
--------------------------------------------------------------------------------
1 | "Bearer $this->apiKey",
23 | 'Content-Type' => 'application/json',
24 | ];
25 |
26 |
27 | $this->client = new Client([
28 | 'base_uri' => 'https://api.mistral.ai/v1/',
29 | 'headers' => $headers
30 | ]);
31 | }
32 |
33 | /**
34 | * @inheritDoc
35 | */
36 | public function generate(array $texts): array
37 | {
38 | try {
39 | $response = $this->client->post('embeddings', [
40 | 'json' => [
41 | 'model' => $this->model,
42 | 'input' => $texts,
43 | ]
44 | ]);
45 |
46 | $result = json_decode($response->getBody()->getContents(), true);
47 | $embeddings = $result['data'];
48 | usort($embeddings, fn($a, $b) => $a['index'] <=> $b['index']);
49 |
50 | return array_map(fn($embedding) => $embedding['embedding'], $embeddings);
51 | } catch (ClientExceptionInterface $e) {
52 | throw new \RuntimeException("Error calling MistralAI API: {$e->getMessage()}", 0, $e);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Embeddings/OllamaEmbeddingFunction.php:
--------------------------------------------------------------------------------
1 | client = new Client([
20 | 'base_uri' => $this->baseUrl,
21 | 'headers' => [
22 | 'Content-Type' => 'application/json',
23 | ]
24 | ]);
25 | }
26 |
27 | /**
28 | * @inheritDoc
29 | */
30 | public function generate(array $texts): array
31 | {
32 | try {
33 | $embeddings = [];
34 |
35 | foreach ($texts as $text) {
36 | $response = $this->client->post('api/embeddings', [
37 | 'json' => [
38 | 'prompt' => $text,
39 | 'model' => $this->model,
40 | ]
41 | ]);
42 |
43 | $result = json_decode($response->getBody()->getContents(), true);
44 |
45 | $embeddings[] = $result['embedding'];
46 | }
47 |
48 | return $embeddings;
49 | } catch (\Exception $e) {
50 | throw new \RuntimeException('Failed to generate embeddings', 0, $e);
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/src/Embeddings/OpenAIEmbeddingFunction.php:
--------------------------------------------------------------------------------
1 | "Bearer $this->apiKey",
23 | 'Content-Type' => 'application/json',
24 | ];
25 |
26 | if (!empty($this->organization)) {
27 | $headers['OpenAI-Organization'] = $this->organization;
28 | }
29 |
30 | $this->client = new Client([
31 | 'base_uri' => 'https://api.openai.com/v1/',
32 | 'headers' => $headers
33 | ]);
34 | }
35 |
36 | /**
37 | * @inheritDoc
38 | */
39 | public function generate(array $texts): array
40 | {
41 | try {
42 | $response = $this->client->post('embeddings', [
43 | 'json' => [
44 | 'model' => $this->model,
45 | 'input' => $texts,
46 | ]
47 | ]);
48 |
49 | $result = json_decode($response->getBody()->getContents(), true);
50 | $embeddings = $result['data'];
51 | usort($embeddings, fn($a, $b) => $a['index'] <=> $b['index']);
52 |
53 | return array_map(fn($embedding) => $embedding['embedding'], $embeddings);
54 | } catch (ClientExceptionInterface $e) {
55 | throw new \RuntimeException("Error calling OpenAI API: {$e->getMessage()}", 0, $e);
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/src/Factory.php:
--------------------------------------------------------------------------------
1 | host = $host;
57 | return $this;
58 | }
59 |
60 | /**
61 | * The port of the client to use for the requests.
62 | */
63 | public function withPort(int $port): self
64 | {
65 | $this->port = $port;
66 | return $this;
67 | }
68 |
69 | /**
70 | * The database to use for the instance.
71 | */
72 | public function withDatabase(string $database): self
73 | {
74 | $this->database = $database;
75 | return $this;
76 | }
77 |
78 | /**
79 | * The tenant to use for the instance.
80 | */
81 | public function withTenant(string $tenant): self
82 | {
83 | $this->tenant = $tenant;
84 | return $this;
85 | }
86 |
87 | /**
88 | * The bearer token used to authenticate requests.
89 | */
90 | public function withAuthToken(string $authToken): self
91 | {
92 | $this->authToken = $authToken;
93 | return $this;
94 | }
95 |
96 | /**
97 | * The http client to use for the requests.
98 | */
99 | public function withHttpClient(\GuzzleHttp\Client $httpClient): self
100 | {
101 | $this->httpClient = $httpClient;
102 | return $this;
103 | }
104 |
105 | public function connect(): Client
106 | {
107 | $this->apiClient = $this->createApiClient();
108 |
109 | return new Client($this->apiClient, $this->database, $this->tenant);
110 | }
111 |
112 | public function createApiClient() : ChromaApiClient
113 | {
114 | $this->baseUrl = $this->host . ':' . $this->port;
115 |
116 | $headers = [
117 | 'Content-Type' => 'application/json',
118 | 'Accept' => 'application/json',
119 | ];
120 |
121 | if (!empty($this->authToken)) {
122 | $headers['Authorization'] = 'Bearer ' . $this->authToken;
123 | }
124 |
125 | $this->httpClient ??= new \GuzzleHttp\Client([
126 | 'base_uri' => $this->baseUrl,
127 | 'headers' => $headers,
128 | ]);
129 |
130 | return new ChromaApiClient($this->httpClient);
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/Generated/ChromaApiClient.php:
--------------------------------------------------------------------------------
1 | httpClient->get('/api/v1');
45 | } catch (ClientExceptionInterface $e) {
46 | $this->handleChromaApiException($e);
47 | }
48 | return json_decode($response->getBody()->getContents(), true);
49 | }
50 |
51 |
52 | public function version(): string
53 | {
54 | try {
55 | $response = $this->httpClient->get('/api/v1/version');
56 |
57 | // remove the quo
58 | return trim($response->getBody()->getContents(), '"');
59 | } catch (ClientExceptionInterface $e) {
60 | $this->handleChromaApiException($e);
61 | }
62 | }
63 |
64 | public function heartbeat(): array
65 | {
66 | try {
67 | $response = $this->httpClient->get('/api/v1/heartbeat');
68 | } catch (ClientExceptionInterface $e) {
69 | $this->handleChromaApiException($e);
70 | }
71 | return json_decode($response->getBody()->getContents(), true);
72 | }
73 |
74 | public function preFlightChecks(): mixed
75 | {
76 | try {
77 | $response = $this->httpClient->get('/api/v1/pre-flight-checks');
78 | } catch (ClientExceptionInterface $e) {
79 | $this->handleChromaApiException($e);
80 | }
81 | return json_decode($response->getBody()->getContents(), true);
82 | }
83 |
84 |
85 | public function createDatabase(string $tenant, CreateDatabaseRequest $request): void
86 | {
87 | try {
88 | $this->httpClient->post('/api/v1/databases', [
89 | 'json' => $request->toArray(),
90 | 'query' => [
91 | 'tenant' => $tenant,
92 | ]
93 | ]);
94 | } catch (ClientExceptionInterface $e) {
95 | $this->handleChromaApiException($e);
96 | }
97 | }
98 |
99 | public function getDatabase(string $database, string $tenant): Database
100 | {
101 | try {
102 | $response = $this->httpClient->get("/api/v1/databases/$database", [
103 | 'query' => [
104 | 'tenant' => $tenant,
105 | ]
106 | ]);
107 | } catch (ClientExceptionInterface $e) {
108 | $this->handleChromaApiException($e);
109 | }
110 |
111 | $result = json_decode($response->getBody()->getContents(), true);
112 |
113 | return Database::make($result);
114 | }
115 |
116 | public function createTenant(CreateTenantRequest $request): void
117 | {
118 | try {
119 | $this->httpClient->post('/api/v1/tenants', [
120 | 'json' => $request->toArray(),
121 | ]);
122 | } catch (ClientExceptionInterface $e) {
123 | $this->handleChromaApiException($e);
124 | }
125 | }
126 |
127 | public function getTenant(string $tenant): ?Tenant
128 | {
129 | try {
130 | $response = $this->httpClient->get("/api/v1/tenants/$tenant");
131 |
132 | $result = json_decode($response->getBody()->getContents(), true);
133 |
134 | return Tenant::make($result);
135 | } catch (ClientExceptionInterface $e) {
136 | $this->handleChromaApiException($e);
137 | }
138 |
139 | }
140 |
141 |
142 | public function listCollections(string $database, string $tenant): array
143 | {
144 | try {
145 | $response = $this->httpClient->get('/api/v1/collections', [
146 | 'query' => [
147 | 'database' => $database,
148 | 'tenant' => $tenant,
149 | ]
150 | ]);
151 | } catch (ClientExceptionInterface $e) {
152 | $this->handleChromaApiException($e);
153 | }
154 |
155 | $result = json_decode($response->getBody()->getContents(), true);
156 |
157 | return array_map(function (array $item) {
158 | return Collection::make($item);
159 | }, $result);
160 | }
161 |
162 | public function createCollection(string $database, string $tenant, CreateCollectionRequest $request): Collection
163 | {
164 | try {
165 | $response = $this->httpClient->post('/api/v1/collections', [
166 | 'json' => $request->toArray(),
167 | 'query' => [
168 | 'database' => $database,
169 | 'tenant' => $tenant,
170 | ]
171 | ]);
172 |
173 | } catch (ClientExceptionInterface $e) {
174 | $this->handleChromaApiException($e);
175 | }
176 |
177 | $result = json_decode($response->getBody()->getContents(), true);
178 |
179 | return Collection::make($result);
180 | }
181 |
182 | public function getCollection(string $collectionId, string $database, string $tenant): Collection
183 | {
184 | try {
185 | $response = $this->httpClient->get("/api/v1/collections/$collectionId", [
186 | 'query' => [
187 | 'database' => $database,
188 | 'tenant' => $tenant,
189 | ]
190 | ]);
191 | } catch (ClientExceptionInterface $e) {
192 | $this->handleChromaApiException($e);
193 | }
194 |
195 | $result = json_decode($response->getBody()->getContents(), true);
196 |
197 | return Collection::make($result);
198 | }
199 |
200 | public function updateCollection(string $collectionId, UpdateCollectionRequest $request): void
201 | {
202 | try {
203 | $response = $this->httpClient->put("/api/v1/collections/$collectionId", [
204 | 'json' => $request->toArray(),
205 | ]);
206 | } catch (ClientExceptionInterface $e) {
207 | $this->handleChromaApiException($e);
208 | }
209 | }
210 |
211 | public function deleteCollection(string $collectionId, string $database, string $tenant): void
212 | {
213 | try {
214 | $this->httpClient->delete("/api/v1/collections/$collectionId", [
215 | 'query' => [
216 | 'database' => $database,
217 | 'tenant' => $tenant,
218 | ]
219 | ]);
220 | } catch (ClientExceptionInterface $e) {
221 | $this->handleChromaApiException($e);
222 | }
223 | }
224 |
225 | public function add(string $collectionId, AddEmbeddingRequest $request): void
226 | {
227 | try {
228 | $this->httpClient->post("/api/v1/collections/$collectionId/add", [
229 | 'json' => $request->toArray(),
230 | ]);
231 | } catch (ClientExceptionInterface $e) {
232 | $this->handleChromaApiException($e);
233 | }
234 | }
235 |
236 | public function update(string $collectionId, UpdateEmbeddingRequest $request): void
237 | {
238 | try {
239 | $this->httpClient->post("/api/v1/collections/$collectionId/update", [
240 | 'json' => $request->toArray(),
241 | ]);
242 | } catch (ClientExceptionInterface $e) {
243 | $this->handleChromaApiException($e);
244 | }
245 | }
246 |
247 | public function upsert(string $collectionId, AddEmbeddingRequest $request): void
248 | {
249 | try {
250 | $this->httpClient->post("/api/v1/collections/$collectionId/upsert", [
251 | 'json' => $request->toArray(),
252 | ]);
253 | } catch (ClientExceptionInterface $e) {
254 | $this->handleChromaApiException($e);
255 | }
256 | }
257 |
258 | public function get(string $collectionId, GetEmbeddingRequest $request): GetItemsResponse
259 | {
260 | try {
261 | $response = $this->httpClient->post("/api/v1/collections/$collectionId/get", [
262 | 'json' => $request->toArray(),
263 | ]);
264 | } catch (ClientExceptionInterface $e) {
265 | $this->handleChromaApiException($e);
266 | }
267 |
268 | $result = json_decode($response->getBody()->getContents(), true);
269 |
270 | return GetItemsResponse::from($result);
271 | }
272 |
273 | public function delete(string $collectionId, DeleteEmbeddingRequest $request): void
274 | {
275 | try {
276 | $this->httpClient->post("/api/v1/collections/$collectionId/delete", [
277 | 'json' => $request->toArray(),
278 | ]);
279 | } catch (ClientExceptionInterface $e) {
280 | $this->handleChromaApiException($e);
281 | }
282 | }
283 |
284 | public function count(string $collectionId): int
285 | {
286 | try {
287 | $response = $this->httpClient->get("/api/v1/collections/$collectionId/count");
288 | } catch (ClientExceptionInterface $e) {
289 | $this->handleChromaApiException($e);
290 | }
291 |
292 | return json_decode($response->getBody()->getContents(), true);
293 | }
294 |
295 | public function getNearestNeighbors(string $collectionId, QueryEmbeddingRequest $request): QueryItemsResponse
296 | {
297 | try {
298 | $response = $this->httpClient->post("/api/v1/collections/$collectionId/query", [
299 | 'json' => $request->toArray(),
300 | ]);
301 | } catch (ClientExceptionInterface $e) {
302 | $this->handleChromaApiException($e);
303 | }
304 |
305 | $result = json_decode($response->getBody()->getContents(), true);
306 |
307 | return QueryItemsResponse::from($result);
308 | }
309 |
310 | public function reset(): bool
311 | {
312 | try {
313 | $response = $this->httpClient->post('/api/v1/reset');
314 | } catch (ClientExceptionInterface $e) {
315 | $this->handleChromaApiException($e);
316 | }
317 |
318 | return json_decode($response->getBody()->getContents(), true);
319 | }
320 |
321 | private function handleChromaApiException(\Exception|ClientExceptionInterface $e): void
322 | {
323 | if ($e instanceof ConnectException) {
324 | $context = $e->getHandlerContext();
325 | $message = $context['error'] ?? $e->getMessage();
326 | $code = $context['errno'] ?? $e->getCode();
327 | throw new ChromaConnectionException($message, $code);
328 | }
329 |
330 | if ($e instanceof RequestException) {
331 | $errorString = $e->getResponse()->getBody()->getContents();
332 |
333 | if (preg_match('/(?<={"\"error\"\:\")([^"]*)/', $errorString, $matches)) {
334 | $errorString = $matches[1];
335 | }
336 |
337 | $error = json_decode($errorString, true);
338 |
339 | if ($error !== null) {
340 |
341 | // If the structure is 'error' => 'NotFoundError("Collection not found")'
342 | if (preg_match(
343 | '/^(?P\w+)\((?P.*)\)$/',
344 | $error['error'] ?? '',
345 | $matches)
346 | ) {
347 | if (isset($matches['message'])) {
348 | $error_type = $matches['error_type'] ?? 'UnknownError';
349 | $message = $matches['message'];
350 |
351 | // Remove trailing and leading quotes
352 | if (str_starts_with($message, "'") && str_ends_with($message, "'")) {
353 | $message = substr($message, 1, -1);
354 | }
355 |
356 | ChromaException::throwSpecific($message, $error_type, $e->getCode());
357 | }
358 | }
359 |
360 | // If the structure is 'detail' => 'Collection not found'
361 | if (isset($error['detail'])) {
362 | $message = $error['detail'];
363 | $error_type = ChromaException::inferTypeFromMessage($message);
364 |
365 |
366 | ChromaException::throwSpecific($message, $error_type, $e->getCode());
367 | }
368 |
369 | // If the structure is {'error': 'Error Type', 'message' : 'Error message'}
370 | if (isset($error['error']) && isset($error['message'])) {
371 | ChromaException::throwSpecific($error['message'], $error['error'], $e->getCode());
372 | }
373 |
374 | // If the structure is 'error' => 'Collection not found'
375 | if (isset($error['error'])) {
376 | $message = $error['error'];
377 | $error_type = ChromaException::inferTypeFromMessage($message);
378 |
379 | ChromaException::throwSpecific($message, $error_type, $e->getCode());
380 | }
381 | }
382 | }
383 |
384 | throw new ChromaException($e->getMessage(), $e->getCode());
385 | }
386 | }
--------------------------------------------------------------------------------
/src/Generated/Exceptions/ChromaAuthorizationException.php:
--------------------------------------------------------------------------------
1 | new ChromaNotFoundException($message, $code),
15 | 'AuthorizationError' => new ChromaAuthorizationException($message, $code),
16 | 'ValueError' => new ChromaValueException($message, $code),
17 | 'UniqueConstraintError' => new ChromaUniqueConstraintException($message, $code),
18 | 'DimensionalityError' => new ChromaDimensionalityException($message, $code),
19 | 'InvalidCollection' => new ChromaInvalidCollectionException($message, $code),
20 | 'TypeError' => new ChromaTypeException($message, $code),
21 | default => new self($message, $code),
22 | };
23 | }
24 |
25 | public static function inferTypeFromMessage(string $message): string
26 | {
27 | return match (true) {
28 | str_contains($message, 'NotFoundError') => 'NotFoundError',
29 | str_contains($message, 'AuthorizationError') => 'AuthorizationError',
30 | str_contains($message, 'Forbidden') => 'AuthorizationError',
31 | str_contains($message, 'UniqueConstraintError') => 'UniqueConstraintError',
32 | str_contains($message, 'ValueError') => 'ValueError',
33 | str_contains($message, 'dimensionality') => 'DimensionalityError',
34 | default => 'UnknownError',
35 | };
36 | }
37 | }
--------------------------------------------------------------------------------
/src/Generated/Exceptions/ChromaInvalidCollectionException.php:
--------------------------------------------------------------------------------
1 | message}";
32 | }
33 |
34 | public function toArray(): array
35 | {
36 | return [
37 | 'loc' => $this->loc,
38 | 'message' => $this->message,
39 | 'type' => $this->type,
40 | ];
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/Generated/Models/Collection.php:
--------------------------------------------------------------------------------
1 | $this->name,
32 | 'id' => $this->id,
33 | 'metadata' => $this->metadata,
34 | ];
35 | }
36 | }
--------------------------------------------------------------------------------
/src/Generated/Models/Database.php:
--------------------------------------------------------------------------------
1 | $this->id,
42 | 'name' => $this->name,
43 | 'tenant' => $this->tenant,
44 | ];
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/src/Generated/Models/Tenant.php:
--------------------------------------------------------------------------------
1 | $this->name,
32 | ];
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Generated/Requests/AddEmbeddingRequest.php:
--------------------------------------------------------------------------------
1 | >
25 | */
26 | public readonly ?array $metadatas,
27 |
28 | /**
29 | * IDs of the items to add.
30 | *
31 | * @var string[]
32 | */
33 | public readonly array $ids,
34 |
35 | /**
36 | * Optional documents of the items to add.
37 | *
38 | * @var string[]
39 | */
40 | public readonly ?array $documents,
41 |
42 | public readonly ?array $images,
43 |
44 | )
45 | {
46 | }
47 |
48 | public static function create(array $data): self
49 | {
50 | return new self(
51 | embeddings: $data['embeddings'] ?? null,
52 | metadatas: $data['metadatas'] ?? null,
53 | ids: $data['ids'],
54 | documents: $data['documents'] ?? null,
55 | images: $data['images'] ?? null,
56 | );
57 | }
58 |
59 | public function toArray(): array
60 | {
61 | return [
62 | 'embeddings' => $this->embeddings,
63 | 'metadatas' => $this->metadatas,
64 | 'ids' => $this->ids,
65 | 'documents' => $this->documents,
66 | ];
67 | }
68 | }
--------------------------------------------------------------------------------
/src/Generated/Requests/CreateCollectionRequest.php:
--------------------------------------------------------------------------------
1 |
23 | */
24 | public readonly ?array $metadata,
25 |
26 | /**
27 | * If true, will return existing collection if it exists.
28 | */
29 | public readonly bool $getOrCreate = false,
30 | )
31 | {
32 | }
33 |
34 | public static function create(array $data): self
35 | {
36 | return new self(
37 | name: $data['name'],
38 | metadata: $data['metadata'] ?? null,
39 | getOrCreate: $data['get_or_create'] ?? false,
40 | );
41 | }
42 |
43 | public function toArray(): array
44 | {
45 | return [
46 | 'name' => $this->name,
47 | 'metadata' => $this->metadata,
48 | 'get_or_create' => $this->getOrCreate,
49 | ];
50 | }
51 | }
--------------------------------------------------------------------------------
/src/Generated/Requests/CreateDatabaseRequest.php:
--------------------------------------------------------------------------------
1 | $this->name,
27 | ];
28 | }
29 | }
--------------------------------------------------------------------------------
/src/Generated/Requests/CreateTenantRequest.php:
--------------------------------------------------------------------------------
1 | $this->name,
27 | ];
28 | }
29 | }
--------------------------------------------------------------------------------
/src/Generated/Requests/DeleteEmbeddingRequest.php:
--------------------------------------------------------------------------------
1 |
22 | */
23 | public readonly ?array $where,
24 |
25 | /**
26 | * Optional query condition to filter items to delete based on document content.
27 | *
28 | * @var array
29 | */
30 | public readonly ?array $whereDocument,
31 | )
32 | {
33 | }
34 |
35 | public static function create(array $data): self
36 | {
37 | return new self(
38 | ids: $data['ids'] ?? null,
39 | where: $data['where'] ?? null,
40 | whereDocument: $data['where_document'] ?? null,
41 | );
42 | }
43 |
44 | public function toArray(): array
45 | {
46 | return [
47 | 'ids' => $this->ids,
48 | 'where' => $this->where,
49 | 'where_document' => $this->whereDocument,
50 | ];
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/src/Generated/Requests/GetEmbeddingRequest.php:
--------------------------------------------------------------------------------
1 |
25 | */
26 | public readonly ?array $where= null,
27 |
28 | /**
29 | * Optional where clause to filter items by.
30 | *
31 | * @var array
32 | */
33 | public readonly ?array $whereDocument= null,
34 |
35 | /**
36 | * Sort items.
37 | */
38 | public readonly ?string $sort= null,
39 |
40 | /**
41 | * Optional limit on the number of items to get.
42 | */
43 | public readonly ?int $limit= null,
44 |
45 | /**
46 | * Optional offset on the number of items to get.
47 | */
48 | public readonly ?int $offset= null,
49 |
50 | /**
51 | * Optional list of items to include in the response.
52 | *
53 | * @var string[]
54 | */
55 | public readonly ?array $include= null,
56 | )
57 | {
58 | }
59 |
60 | public static function create(array $data): self
61 | {
62 | return new self(
63 | ids: $data['ids'] ?? null,
64 | where: $data['where'] ?? null,
65 | whereDocument: $data['where_document'] ?? null,
66 | sort: $data['sort'] ?? null,
67 | limit: $data['limit'] ?? null,
68 | offset: $data['offset'] ?? null,
69 | include: $data['include'] ?? null,
70 | );
71 | }
72 |
73 | public function toArray(): array
74 | {
75 | return [
76 | 'ids' => $this->ids,
77 | 'where' => $this->where,
78 | 'whereDocument' => $this->whereDocument,
79 | 'sort' => $this->sort,
80 | 'limit' => $this->limit,
81 | 'offset' => $this->offset,
82 | 'include' => $this->include,
83 | ];
84 | }
85 | }
--------------------------------------------------------------------------------
/src/Generated/Requests/QueryEmbeddingRequest.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | public readonly ?array $where,
17 |
18 | /**
19 | * Optional query condition to filter results based on document content.
20 | *
21 | * @var array
22 | */
23 | public readonly ?array $whereDocument,
24 |
25 | /**
26 | * Optional query condition to filter results based on embedding content.
27 | *
28 | * @var float[][]
29 | */
30 | public readonly ?array $queryEmbeddings,
31 |
32 | /**
33 | * Optional number of results to return. Defaults to 10.
34 | */
35 | public readonly ?int $nResults,
36 |
37 | /**
38 | * Optional list of items to include in the response.
39 | *
40 | * @var string[]
41 | */
42 | public readonly ?array $include,
43 | )
44 | {
45 | }
46 |
47 | public static function create(array $data): self
48 | {
49 | return new self(
50 | where: $data['where'] ?? null,
51 | whereDocument: $data['where_document'] ?? null,
52 | queryEmbeddings: $data['query_embeddings'] ?? null,
53 | nResults: $data['n_results'] ?? null,
54 | include: $data['include'] ?? null,
55 | );
56 | }
57 |
58 | public function toArray(): array
59 | {
60 | return array_filter([
61 | 'where' => $this->where,
62 | 'where_document' => $this->whereDocument,
63 | 'query_embeddings' => $this->queryEmbeddings,
64 | 'n_results' => $this->nResults,
65 | 'include' => $this->include,
66 | ], fn($value) => $value !== null);
67 | }
68 | }
--------------------------------------------------------------------------------
/src/Generated/Requests/UpdateCollectionRequest.php:
--------------------------------------------------------------------------------
1 |
20 | */
21 | public readonly ?array $newMetadata,
22 |
23 | )
24 | {
25 | }
26 |
27 | public static function create(array $data): self
28 | {
29 | return new self(
30 | newName: $data['new_name'] ?? null,
31 | newMetadata: $data['new_metadata'] ?? null,
32 | );
33 | }
34 |
35 | public function toArray(): array
36 | {
37 | return array_filter([
38 | 'new_name' => $this->newName,
39 | 'new_metadata' => $this->newMetadata,
40 | ]);
41 | }
42 | }
--------------------------------------------------------------------------------
/src/Generated/Requests/UpdateEmbeddingRequest.php:
--------------------------------------------------------------------------------
1 | []
30 | */
31 | public readonly ?array $metadatas,
32 |
33 | /**
34 | * Optional documents of the items to update.
35 | *
36 | * @var string[]
37 | */
38 | public readonly ?array $documents,
39 |
40 | /**
41 | * Optional uris of the items to update.
42 | *
43 | * @var string[]
44 | */
45 | public readonly ?array $images,
46 | )
47 | {
48 | }
49 |
50 | public static function create(array $data): self
51 | {
52 | return new self(
53 | embeddings: $data['embeddings'] ?? null,
54 | ids: $data['ids'],
55 | metadatas: $data['metadatas'] ?? null,
56 | documents: $data['documents'] ?? null,
57 | images: $data['images'] ?? null,
58 | );
59 | }
60 |
61 | public function toArray(): array
62 | {
63 | return array_filter([
64 | 'embeddings' => $this->embeddings,
65 | 'ids' => $this->ids,
66 | 'metadatas' => $this->metadatas,
67 | 'documents' => $this->documents,
68 | ]);
69 | }
70 | }
--------------------------------------------------------------------------------
/src/Generated/Responses/GetItemsResponse.php:
--------------------------------------------------------------------------------
1 | []
25 | */
26 | public readonly ?array $metadatas,
27 |
28 | /**
29 | * List of embeddings of the items.
30 | *
31 | * @var float[][]
32 | */
33 | public readonly ?array $embeddings,
34 |
35 | /**
36 | * List of documents of the items.
37 | *
38 | * @var string[]
39 | */
40 | public readonly ?array $documents,
41 | )
42 | {
43 | }
44 |
45 | public static function from(array $data): self
46 | {
47 | return new self(
48 | ids: $data['ids'],
49 | metadatas: $data['metadatas'] ?? null,
50 | embeddings: $data['embeddings'] ?? null,
51 | documents: $data['documents'] ?? null,
52 | );
53 | }
54 |
55 | public function toArray(): array
56 | {
57 | return array_filter([
58 | 'ids' => $this->ids,
59 | 'metadatas' => $this->metadatas,
60 | 'embeddings' => $this->embeddings,
61 | 'documents' => $this->documents,
62 | ]);
63 | }
64 | }
--------------------------------------------------------------------------------
/src/Generated/Responses/QueryItemsResponse.php:
--------------------------------------------------------------------------------
1 | [][]
33 | */
34 | public readonly ?array $metadatas,
35 |
36 | /**
37 | * List of documents of the items.
38 | *
39 | * @var string[][]
40 | */
41 | public readonly ?array $documents,
42 |
43 | /**
44 | * List of data of the items.
45 | *
46 | * @var string[][]
47 | */
48 | public readonly ?array $data,
49 |
50 | /**
51 | * List of uris of the items.
52 | *
53 | * @var string[][]
54 | */
55 | public readonly ?array $uris,
56 |
57 | /**
58 | * List of distances of the items.
59 | *
60 | * @var float[][]
61 | */
62 | public readonly ?array $distances,
63 | )
64 | {
65 | }
66 |
67 | public static function from(array $data): self
68 | {
69 | return new self(
70 | ids: $data['ids'],
71 | embeddings: $data['embeddings'] ?? null,
72 | metadatas: $data['metadatas'] ?? null,
73 | documents: $data['documents'] ?? null,
74 | data: $data['data'] ?? null,
75 | uris: $data['uris'] ?? null,
76 | distances: $data['distances'] ?? null,
77 | );
78 | }
79 |
80 | public function toArray(): array
81 | {
82 | return array_filter([
83 | 'ids' => $this->ids,
84 | 'embeddings' => $this->embeddings,
85 | 'metadatas' => $this->metadatas,
86 | 'documents' => $this->documents,
87 | 'data' => $this->data,
88 | 'uris' => $this->uris,
89 | 'distances' => $this->distances,
90 | ]);
91 | }
92 |
93 | }
--------------------------------------------------------------------------------
/src/Resources/CollectionResource.php:
--------------------------------------------------------------------------------
1 | name,
66 | id: $collection->id,
67 | metadata: $collection->metadata,
68 | database: $database,
69 | tenant: $tenant,
70 | embeddingFunction: $embeddingFunction,
71 | apiClient: $apiClient,
72 | );
73 | }
74 |
75 | /**
76 | * Add items to the collection.
77 | *
78 | * @param array $ids The IDs of the items to add.
79 | * @param ?array $embeddings The embeddings of the items to add (optional).
80 | * @param ?array $metadatas The metadatas of the items to add (optional).
81 | * @param ?array $documents The documents of the items to add (optional).
82 | * @param ?array $images The base64 encoded images of the items to add (optional).
83 | * @return void
84 | */
85 | public function add(
86 | array $ids,
87 | ?array $embeddings = null,
88 | ?array $metadatas = null,
89 | ?array $documents = null,
90 | ?array $images = null
91 | ): void
92 | {
93 | $validated = $this->validate(
94 | ids: $ids,
95 | embeddings: $embeddings,
96 | metadatas: $metadatas,
97 | documents: $documents,
98 | images: $images,
99 | requireEmbeddingsOrDocuments: true,
100 | );
101 |
102 |
103 | $request = new AddEmbeddingRequest(
104 | embeddings: $validated['embeddings'],
105 | metadatas: $validated['metadatas'],
106 | ids: $validated['ids'],
107 | documents: $validated['documents'],
108 | images: $validated['images'],
109 | );
110 |
111 |
112 | $this->apiClient->add($this->id, $request);
113 | }
114 |
115 |
116 | /**
117 | * Update the embeddings, documents, and/or metadatas of existing items.
118 | *
119 | * @param array $ids The IDs of the items to update.
120 | * @param ?array $embeddings The embeddings of the items to update (optional).
121 | * @param ?array $metadatas The metadatas of the items to update (optional).
122 | * @param ?array $documents The documents of the items to update (optional).
123 | * @param ?array $images The base64 encoded images of the items to update (optional).
124 | *
125 | */
126 | public function update(
127 | array $ids,
128 | ?array $embeddings = null,
129 | ?array $metadatas = null,
130 | ?array $documents = null,
131 | ?array $images = null
132 | )
133 | {
134 | $validated = $this->validate(
135 | ids: $ids,
136 | embeddings: $embeddings,
137 | metadatas: $metadatas,
138 | documents: $documents,
139 | images: $images,
140 | requireEmbeddingsOrDocuments: false,
141 | );
142 |
143 | $request = new UpdateEmbeddingRequest(
144 | embeddings: $validated['embeddings'],
145 | ids: $validated['ids'],
146 | metadatas: $validated['metadatas'],
147 | documents: $validated['documents'],
148 | images: $validated['images'],
149 | );
150 |
151 | $this->apiClient->update($this->id, $request);
152 | }
153 |
154 | /**
155 | * Upsert items in the collection.
156 | *
157 | * @param array $ids The IDs of the items to upsert.
158 | * @param ?array $embeddings The embeddings of the items to upsert (optional).
159 | * @param ?array $metadatas The metadatas of the items to upsert (optional).
160 | * @param ?array $documents The documents of the items to upsert (optional).
161 | * @param ?array $images The base64 encoded images of the items to upsert (optional).
162 | *
163 | */
164 | public function upsert(
165 | array $ids,
166 | ?array $embeddings = null,
167 | ?array $metadatas = null,
168 | ?array $documents = null,
169 | ?array $images = null
170 | ): void
171 | {
172 | $validated = $this->validate(
173 | ids: $ids,
174 | embeddings: $embeddings,
175 | metadatas: $metadatas,
176 | documents: $documents,
177 | images: $images,
178 | requireEmbeddingsOrDocuments: true,
179 | );
180 |
181 | $request = new AddEmbeddingRequest(
182 | embeddings: $validated['embeddings'],
183 | metadatas: $validated['metadatas'],
184 | ids: $validated['ids'],
185 | documents: $validated['documents'],
186 | images: $validated['images'],
187 | );
188 |
189 | $this->apiClient->upsert($this->id, $request);
190 | }
191 |
192 | /**
193 | * Count the number of items in the collection.
194 | */
195 | public function count(): int
196 | {
197 | return $this->apiClient->count($this->id);
198 | }
199 |
200 | /**
201 | * Returns the first `$limit` entries of the collection.
202 | *
203 | * @param int $limit The number of entries to return. Defaults to 10.
204 | * @param string[] $include The list of fields to include in the response (optional).
205 | */
206 | public function peek(
207 | int $limit = 10,
208 | ?array $include = null
209 | ): GetItemsResponse
210 | {
211 | $include ??= ['embeddings', 'metadatas', 'distances'];
212 |
213 | $request = new GetEmbeddingRequest(
214 | limit: $limit,
215 | include: $include,
216 | );
217 |
218 | return $this->apiClient->get($this->id, $request);
219 | }
220 |
221 | /**
222 | * Get items from the collection.
223 | *
224 | * @param array $ids The IDs of the items to get (optional).
225 | * @param array $where The where clause to filter items by (optional).
226 | * @param array $whereDocument The where clause to filter items by (optional).
227 | * @param int $limit The limit on the number of items to get (optional).
228 | * @param int $offset The offset on the number of items to get (optional).
229 | * @param string[] $include The list of fields to include in the response (optional).
230 | */
231 | public function get(
232 | ?array $ids = null,
233 | ?array $where = null,
234 | ?array $whereDocument = null,
235 | ?int $limit = null,
236 | ?int $offset = null,
237 | ?array $include = null
238 | ): GetItemsResponse
239 | {
240 | $include ??= ['embeddings', 'metadatas', 'distances'];
241 |
242 | $request = new GetEmbeddingRequest(
243 | ids: $ids,
244 | where: $where,
245 | whereDocument: $whereDocument,
246 | limit: $limit,
247 | offset: $offset,
248 | include: $include,
249 | );
250 |
251 | return $this->apiClient->get($this->id, $request);
252 | }
253 |
254 | /**
255 | * Deletes items from the collection.
256 | *
257 | * @param ?array $ids The IDs of the items to delete.
258 | * @param ?array $where The where clause to filter items to delete based on metadata values (optional).
259 | * @param ?array $whereDocument The where clause to filter to delete based on document content (optional).
260 | */
261 | public function delete(?array $ids = null, ?array $where = null, ?array $whereDocument = null): void
262 | {
263 | $request = new DeleteEmbeddingRequest(
264 | ids: $ids,
265 | where: $where,
266 | whereDocument: $whereDocument,
267 | );
268 |
269 | $this->apiClient->delete($this->id, $request);
270 | }
271 |
272 | /**
273 | * Performs a query on the collection using the specified parameters.
274 | *
275 | *
276 | */
277 | public function query(
278 | ?array $queryEmbeddings = null,
279 | ?array $queryTexts = null,
280 | ?array $queryImages = null,
281 | int $nResults = 10,
282 | ?array $where = null,
283 | ?array $whereDocument = null,
284 | ?array $include = null
285 | ): QueryItemsResponse
286 | {
287 | $include ??= ['embeddings', 'metadatas', 'distances'];
288 |
289 | if (
290 | !(($queryEmbeddings != null xor $queryTexts != null xor $queryImages != null))
291 | ) {
292 | throw new \InvalidArgumentException(
293 | 'You must provide only one of queryEmbeddings, queryTexts, queryImages, or queryUris'
294 | );
295 | }
296 |
297 | $finalEmbeddings = [];
298 |
299 | if ($queryEmbeddings == null) {
300 | if ($this->embeddingFunction == null) {
301 | throw new \InvalidArgumentException(
302 | 'You must provide an embedding function if you did not provide embeddings'
303 | );
304 | } elseif ($queryTexts != null) {
305 | $finalEmbeddings = $this->embeddingFunction->generate($queryTexts);
306 | } elseif ($queryImages != null) {
307 | $finalEmbeddings = $this->embeddingFunction->generate($queryImages);
308 | } else {
309 | throw new \InvalidArgumentException(
310 | 'If you did not provide embeddings, you must provide documents or images'
311 | );
312 | }
313 | } else {
314 | $finalEmbeddings = $queryEmbeddings;
315 | }
316 |
317 |
318 | $request = new QueryEmbeddingRequest(
319 | where: $where,
320 | whereDocument: $whereDocument,
321 | queryEmbeddings: $finalEmbeddings,
322 | nResults: $nResults,
323 | include: $include,
324 | );
325 |
326 | return $this->apiClient->getNearestNeighbors($this->id, $request);
327 |
328 | }
329 |
330 |
331 | /**
332 | * Modify the collection name or metadata.
333 | */
334 | public function modify(string $name, array $metadata): void
335 | {
336 | $request = new UpdateCollectionRequest($name, $metadata);
337 |
338 | $this->apiClient->updateCollection($this->id, $request);
339 | }
340 |
341 | /**
342 | * Validates the inputs to the add, upsert, and update methods.
343 | *
344 | * @return array{ids: string[], embeddings: int[][], metadatas: array[], documents: string[], images: string[], uris: string[]}
345 | */
346 | protected
347 | function validate(
348 | array $ids,
349 | ?array $embeddings,
350 | ?array $metadatas,
351 | ?array $documents,
352 | ?array $images,
353 | bool $requireEmbeddingsOrDocuments
354 | ): array
355 | {
356 |
357 | if ($requireEmbeddingsOrDocuments) {
358 | if ($embeddings === null && $documents === null && $images === null) {
359 | throw new \InvalidArgumentException(
360 | 'You must provide embeddings, documents, or images'
361 | );
362 | }
363 | }
364 |
365 | if (
366 | $embeddings != null && count($embeddings) != count($ids)
367 | || $metadatas != null && count($metadatas) != count($ids)
368 | || $documents != null && count($documents) != count($ids)
369 | || $images != null && count($images) != count($ids)
370 | ) {
371 | throw new \InvalidArgumentException(
372 | 'The number of ids, embeddings, metadatas, documents, and images must be the same'
373 | );
374 | }
375 |
376 | if ($embeddings == null) {
377 | if ($this->embeddingFunction == null) {
378 | throw new \InvalidArgumentException(
379 | 'You must provide an embedding function if you did not provide embeddings'
380 | );
381 | } elseif ($documents != null) {
382 | $finalEmbeddings = $this->embeddingFunction->generate($documents);
383 | } elseif ($images != null) {
384 | $finalEmbeddings = $this->embeddingFunction->generate($images);
385 | } else {
386 | throw new \InvalidArgumentException(
387 | 'If you did not provide embeddings, you must provide documents or images'
388 | );
389 | }
390 | } else {
391 | $finalEmbeddings = $embeddings;
392 | }
393 |
394 | $ids = array_map(function ($id) {
395 | $id = (string)$id;
396 | if ($id === '') {
397 | throw new \InvalidArgumentException('Expected IDs to be non-empty strings');
398 | }
399 | return $id;
400 | }, $ids);
401 |
402 | $uniqueIds = array_unique($ids);
403 | if (count($uniqueIds) !== count($ids)) {
404 | $duplicateIds = array_filter($ids, function ($id) use ($ids) {
405 | return count(array_keys($ids, $id)) > 1;
406 | });
407 | throw new \InvalidArgumentException('Expected IDs to be unique, found duplicates for: ' . implode(', ', $duplicateIds));
408 | }
409 |
410 |
411 | return [
412 | 'ids' => $ids,
413 | 'embeddings' => $finalEmbeddings,
414 | 'metadatas' => $metadatas,
415 | 'documents' => $documents,
416 | 'images' => $images,
417 | ];
418 |
419 |
420 | }
421 | }
422 |
--------------------------------------------------------------------------------
/tests/ChromaDB.php:
--------------------------------------------------------------------------------
1 | toBeInstanceOf(Client::class);
14 | });
15 |
16 | it('can connect to a chroma server using factory', function () {
17 | $client = ChromaDB::factory()
18 | ->withHost('http://localhost')
19 | ->withPort(8000)
20 | ->connect();
21 |
22 | expect($client)->toBeInstanceOf(Client::class);
23 | });
24 |
25 | test('can connect to an API token authenticated chroma server', function () {
26 | $client = ChromaDB::factory()
27 | ->withPort(8001)
28 | ->withAuthToken('test-token')
29 | ->connect();
30 |
31 | expect($client)->toBeInstanceOf(Client::class);
32 | });
33 |
34 | it('cannot connect to an API token authenticated chroma server with wrong token', function () {
35 | ChromaDB::factory()
36 | ->withPort(8001)
37 | ->withAuthToken('wrong-token')
38 | ->connect();
39 | })->throws(ChromaAuthorizationException::class);
40 |
41 | it('throws exception when connecting to API token authenticated chroma server with no token', function () {
42 | ChromaDB::factory()
43 | ->withPort(8001)
44 | ->connect();
45 | })->throws(ChromaAuthorizationException::class);
46 |
47 | it('throws a connection exception when connecting to a non-existent chroma server', function () {
48 | ChromaDB::factory()
49 | ->withHost('http://localhost')
50 | ->withPort(8002)
51 | ->connect();
52 | })->throws(ChromaConnectionException::class);
53 |
--------------------------------------------------------------------------------
/tests/Client.php:
--------------------------------------------------------------------------------
1 | client = ChromaDB::factory()
14 | ->withDatabase('test_database')
15 | ->withTenant('test_tenant')
16 | ->connect();
17 |
18 | $this->client->deleteAllCollections();
19 |
20 | $this->embeddingFunction = new class implements EmbeddingFunction {
21 | public function generate(array $texts): array
22 | {
23 | return array_map(function ($text) {
24 | return [1.0, 2.0, 3.0, 4.0, 5.0];
25 | }, $texts);
26 | }
27 | };
28 |
29 | $this->collection = $this->client->createCollection(
30 | name: 'test_collection',
31 | embeddingFunction: $this->embeddingFunction
32 | );
33 | });
34 |
35 |
36 | it('can get the version', function () {
37 | $version = $this->client->version();
38 |
39 | expect($version)
40 | ->toBeString()
41 | ->toMatch('/^[0-9]+\.[0-9]+\.[0-9]+$/');
42 | });
43 |
44 | it('can get the heartbeat', function () {
45 | $heartbeat = $this->client->heartbeat();
46 |
47 | expect($heartbeat)
48 | ->toBeInt()
49 | ->toBeGreaterThan(0);
50 | });
51 |
52 | it('can list collections', function () {
53 | $collections = $this->client->listCollections();
54 |
55 | expect($collections)
56 | ->toBeArray()
57 | ->toHaveCount(1);
58 |
59 | $this->client->createCollection('test_collection_2');
60 |
61 | $collections = $this->client->listCollections();
62 |
63 | expect($collections)
64 | ->toBeArray()
65 | ->toHaveCount(2);
66 | });
67 |
68 |
69 | it('can create or get collections', function () {
70 | $collection = $this->client->getOrCreateCollection('test_collection');
71 |
72 | expect($collection)
73 | ->toBeInstanceOf(CollectionResource::class)
74 | ->toHaveProperty('name', 'test_collection');
75 |
76 | $collection = $this->client->getOrCreateCollection('test_collection_2');
77 |
78 | expect($collection)
79 | ->toBeInstanceOf(CollectionResource::class)
80 | ->toHaveProperty('name', 'test_collection_2');
81 | });
82 |
83 | it('can get a collection', function () {
84 | $collection = $this->client->getCollection('test_collection');
85 |
86 | expect($collection)
87 | ->toBeInstanceOf(CollectionResource::class)
88 | ->toHaveProperty('name', 'test_collection');
89 | });
90 |
91 | it('throws a value error when getting a collection that does not exist', function () {
92 | $this->client->getCollection('test_collection_2');
93 | })->throws(ChromaValueException::class, 'Collection test_collection_2 does not exist.');
94 |
95 | it('can modify a collection name or metadata', function () {
96 | $this->collection->modify('test_collection_2', ['test' => 'test_2']);
97 |
98 | $collection = $this->client->getCollection('test_collection_2');
99 |
100 | expect($collection->name)
101 | ->toBe('test_collection_2')
102 | ->and($collection->metadata)
103 | ->toMatchArray(['test' => 'test_2']);
104 |
105 | });
106 |
107 | it('can delete a collection', function () {
108 | $this->client->deleteCollection('test_collection');
109 |
110 | expect(fn() => $this->client->getCollection('test_collection'))
111 | ->toThrow(ChromaValueException::class);
112 | });
113 |
114 | it('can delete all collections', function () {
115 | $this->client->createCollection('test_collection_2');
116 |
117 | $collections = $this->client->listCollections();
118 |
119 | expect($collections)
120 | ->toBeArray()
121 | ->toHaveCount(2);
122 |
123 | $this->client->deleteAllCollections();
124 |
125 | $collections = $this->client->listCollections();
126 |
127 | expect($collections)
128 | ->toBeArray()
129 | ->toHaveCount(0);
130 | });
131 |
132 | it('throws a value error when deleting a collection that does not exist', function () {
133 | $this->client->deleteCollection('test_collection_2');
134 | })->throws(ChromaValueException::class, 'Collection test_collection_2 does not exist.');
135 |
136 | it('can add single embeddings to a collection', function () {
137 | $ids = ['test1'];
138 | $embeddings = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]];
139 | $metadatas = [['test' => 'test']];
140 |
141 | $this->collection->add($ids, $embeddings, $metadatas);
142 |
143 | expect($this->collection->count())->toBe(1);
144 | });
145 |
146 | it('cannot add invalid single embeddings to a collection', function () {
147 | $ids = ['test1'];
148 | $embeddings = ['this is not an embedding'];
149 | $metadatas = [['test' => 'test']];
150 |
151 | $this->collection->add($ids, $embeddings, $metadatas);
152 | })->throws(ChromaTypeException::class);
153 |
154 | it('can add single text documents to a collection', function () {
155 | $ids = ['test1'];
156 | $documents = ['This is a test document'];
157 | $metadatas = [['test' => 'test']];
158 |
159 | $this->collection->add(
160 | $ids,
161 | metadatas: $metadatas,
162 | documents: $documents
163 | );
164 |
165 | expect($this->collection->count())->toBe(1);
166 | });
167 |
168 | it('cannot add single embeddings to a collection with a different dimensionality', function () {
169 | $ids = ['test1'];
170 | $embeddings = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]];
171 | $metadatas = [['test' => 'test']];
172 |
173 | $this->collection->add($ids, $embeddings, $metadatas);
174 |
175 | // Dimensionality is now 10. Other embeddings must have the same dimensionality.
176 |
177 | $ids = ['test2'];
178 | $embeddings = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]];
179 | $metadatas = [['test' => 'test2']];
180 |
181 | $this->collection->add($ids, $embeddings, $metadatas);
182 | })->throws(ChromaDimensionalityException::class, 'Embedding dimension 11 does not match collection dimensionality 10');
183 |
184 | it('can upsert single embeddings to a collection', function () {
185 | $ids = ['test1'];
186 | $embeddings = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]];
187 | $metadatas = [['test' => 'test']];
188 |
189 | $this->collection->upsert($ids, $embeddings, $metadatas);
190 |
191 | expect($this->collection->count())->toBe(1);
192 |
193 | $this->collection->upsert($ids, $embeddings, $metadatas);
194 |
195 | expect($this->collection->count())->toBe(1);
196 | });
197 |
198 |
199 | it('can update single embeddings in a collection', function () {
200 | $ids = ['test1'];
201 | $embeddings = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]];
202 | $metadatas = [['test' => 'test']];
203 |
204 | $this->collection->add($ids, $embeddings, $metadatas);
205 |
206 | expect($this->collection->count())->toBe(1);
207 |
208 | $this->collection->update($ids, $embeddings, $metadatas);
209 |
210 | expect($this->collection->count())->toBe(1);
211 |
212 | $collectionItems = $this->collection->get($ids);
213 |
214 | expect($collectionItems->ids)
215 | ->toMatchArray($ids)
216 | ->and($collectionItems->embeddings)
217 | ->toMatchArray($embeddings)
218 | ->and($collectionItems->metadatas)
219 | ->toMatchArray($metadatas);
220 | });
221 |
222 | it('can update single documents in a collection', function () {
223 | $ids = ['test1'];
224 | $documents = ['This is a test document'];
225 | $metadatas = [['test' => 'test']];
226 |
227 | $this->collection->add(
228 | $ids,
229 | metadatas: $metadatas,
230 | documents: $documents
231 | );
232 |
233 | expect($this->collection->count())->toBe(1);
234 |
235 | $newDocuments = ['This is a new test document'];
236 | $newMetadatas = [['test' => 'test2']];
237 |
238 | $this->collection->update(
239 | $ids,
240 | metadatas: $newMetadatas,
241 | documents: $newDocuments
242 | );
243 |
244 | expect($this->collection->count())->toBe(1);
245 |
246 | $collectionItems = $this->collection->get($ids, include: ['documents', 'metadatas']);
247 |
248 | expect($collectionItems->ids)
249 | ->toMatchArray($ids)
250 | ->and($collectionItems->documents)
251 | ->toMatchArray($newDocuments)
252 | ->and($collectionItems->metadatas)
253 | ->toMatchArray($newMetadatas);
254 | });
255 |
256 | it('can add batch embeddings to a collection', function () {
257 | $ids = ['test1', 'test2', 'test3'];
258 | $embeddings = [
259 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
260 | [11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
261 | [21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
262 | ];
263 | $metadatas = [
264 | ['some' => 'metadata1'],
265 | ['some' => 'metadata2'],
266 | ['some' => 'metadata3'],
267 | ];
268 |
269 | $this->collection->add($ids, $embeddings, $metadatas);
270 |
271 | expect($this->collection->count())->toBe(3);
272 |
273 | $getResponse = $this->collection->get($ids);
274 |
275 | expect($getResponse->ids)
276 | ->toMatchArray($ids)
277 | ->and($getResponse->embeddings)
278 | ->toMatchArray($embeddings)
279 | ->and($getResponse->metadatas)
280 | ->toMatchArray($metadatas);
281 | });
282 |
283 | it('cannot add batch embeddings with different dimensionality to a collection', function () {
284 | $ids = ['test1', 'test2', 'test3'];
285 | $embeddings = [
286 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
287 | [11, 12, 13, 14, 15, 16, 17, 18, 19],
288 | [21, 22, 23, 24, 25, 26, 27, 28],
289 | ];
290 | $metadatas = [
291 | ['some' => 'metadata1'],
292 | ['some' => 'metadata2'],
293 | ['some' => 'metadata3'],
294 | ];
295 |
296 | $this->collection->add($ids, $embeddings, $metadatas);
297 | })->throws(ChromaDimensionalityException::class);
298 |
299 | it('can add batch documents to a collection', function () {
300 | $ids = ['test1', 'test2', 'test3'];
301 | $documents = [
302 | 'This is a test document',
303 | 'This is another test document',
304 | 'This is a third test document',
305 | ];
306 | $metadatas = [
307 | ['some' => 'metadata1'],
308 | ['some' => 'metadata2'],
309 | ['some' => 'metadata3'],
310 | ];
311 |
312 | $this->collection->add(
313 | $ids,
314 | metadatas: $metadatas,
315 | documents: $documents
316 | );
317 |
318 | expect($this->collection->count())->toBe(3);
319 |
320 | $getResponse = $this->collection->get($ids, include: ['documents', 'metadatas']);
321 |
322 | expect($getResponse->ids)
323 | ->toMatchArray($ids)
324 | ->and($getResponse->documents)
325 | ->toMatchArray($documents)
326 | ->and($getResponse->metadatas)
327 | ->toMatchArray($metadatas);
328 | });
329 |
330 |
331 | it('can peek a collection', function () {
332 | $ids = ['test1', 'test2', 'test3'];
333 | $embeddings = [
334 | [1.0, 2.0, 3.0, 4.0, 5.0],
335 | [6.0, 7.0, 8.0, 9.0, 10.0],
336 | [11.0, 12.0, 13.0, 14.0, 15.0],
337 | ];
338 |
339 | $this->collection->add($ids, $embeddings);
340 |
341 | expect($this->collection->count())->toBe(3);
342 |
343 | $peekResponse = $this->collection->peek(2);
344 |
345 | expect($peekResponse->ids)
346 | ->toMatchArray(['test1', 'test2']);
347 |
348 | });
349 |
350 | it('can query a collection', function () {
351 | $ids = ['test1', 'test2', 'test3'];
352 | $embeddings = [
353 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
354 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
355 | [10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0],
356 | ];
357 |
358 | $this->collection->add($ids, $embeddings);
359 |
360 | expect($this->collection->count())->toBe(3);
361 |
362 | $queryResponse = $this->collection->query(
363 | queryEmbeddings: [
364 | [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
365 | ],
366 | nResults: 2
367 | );
368 |
369 | expect($queryResponse->ids[0])
370 | ->toMatchArray(['test1', 'test2'])
371 | ->and($queryResponse->distances[0])
372 | ->toMatchArray([0.0, 0.0]);
373 |
374 | });
375 |
376 | it('can get a collection by id', function () {
377 | $ids = ['test1', 'test2', 'test3'];
378 | $embeddings = [
379 | [1.0, 2.0, 3.0, 4.0, 5.0],
380 | [6.0, 7.0, 8.0, 9.0, 10.0],
381 | [11.0, 12.0, 13.0, 14.0, 15.0],
382 | ];
383 | $metadatas = [
384 | ['some' => 'metadata1'],
385 | ['some' => 'metadata2'],
386 | ['some' => 'metadata3'],
387 | ];
388 |
389 | $this->collection->add($ids, $embeddings, $metadatas);
390 |
391 | expect($this->collection->count())->toBe(3);
392 |
393 | $collectionItems = $this->collection->get(['test1', 'test2']);
394 |
395 | expect($collectionItems->ids)
396 | ->toMatchArray(['test1', 'test2'])
397 | ->and($collectionItems->embeddings)
398 | ->toMatchArray([
399 | [1.0, 2.0, 3.0, 4.0, 5.0],
400 | [6.0, 7.0, 8.0, 9.0, 10.0],
401 | ]);
402 | });
403 |
404 |
405 | it('can get a collection by where', function () {
406 | $ids = ['test1', 'test2', 'test3'];
407 | $embeddings = [
408 | [1.0, 2.0, 3.0, 4.0, 5.0],
409 | [6.0, 7.0, 8.0, 9.0, 10.0],
410 | [11.0, 12.0, 13.0, 14.0, 15.0],
411 | ];
412 | $metadatas = [
413 | ['some' => 'metadata1'],
414 | ['some' => 'metadata2'],
415 | ['some' => 'metadata3'],
416 | ];
417 |
418 | $this->collection->add($ids, $embeddings, $metadatas);
419 |
420 | expect($this->collection->count())->toBe(3);
421 |
422 | $collectionItems = $this->collection->get(
423 | where: [
424 | 'some' => ['$eq' => 'metadata1']
425 | ]
426 | );
427 |
428 | expect($collectionItems->ids)
429 | ->toHaveCount(1)
430 | ->and($collectionItems->ids[0])
431 | ->toBe('test1');
432 | });
433 |
434 | it('can query a collection using query texts', function () {
435 | $ids = ['test1', 'test2', 'test3'];
436 | $documents = [
437 | 'This is a test document',
438 | 'This is another test document',
439 | 'This is a third test document',
440 | ];
441 | $metadatas = [
442 | ['some' => 'metadata1'],
443 | ['some' => 'metadata2'],
444 | ['some' => 'metadata3'],
445 | ];
446 |
447 | $this->collection->add(
448 | $ids,
449 | metadatas: $metadatas,
450 | documents: $documents
451 | );
452 |
453 | expect($this->collection->count())->toBe(3);
454 |
455 | $queryResponse = $this->collection->query(
456 | queryTexts: ['This is a test document'],
457 | nResults: 1
458 | );
459 |
460 | expect($queryResponse->ids[0])
461 | ->toMatchArray(['test1']);
462 | });
463 |
464 | it('throws a value error when getting a collection by where with an invalid operator', function () {
465 | $ids = ['test1', 'test2', 'test3'];
466 | $embeddings = [
467 | [1.0, 2.0, 3.0, 4.0, 5.0],
468 | [6.0, 7.0, 8.0, 9.0, 10.0],
469 | [11.0, 12.0, 13.0, 14.0, 15.0],
470 | ];
471 | $metadatas = [
472 | ['some' => 'metadata1'],
473 | ['some' => 'metadata2'],
474 | ['some' => 'metadata3'],
475 | ];
476 |
477 | $this->collection->add($ids, $embeddings, $metadatas);
478 |
479 | expect($this->collection->count())->toBe(3);
480 |
481 | $collectionItems = $this->collection->get(
482 | where: [
483 | 'some' => ['$invalid' => 'metadata1']
484 | ]
485 | );
486 | })->throws(ChromaValueException::class);
487 |
488 | it('can delete a collection by id', function () {
489 | $ids = ['test1', 'test2', 'test3'];
490 | $embeddings = [
491 | [1.0, 2.0, 3.0, 4.0, 5.0],
492 | [6.0, 7.0, 8.0, 9.0, 10.0],
493 | [11.0, 12.0, 13.0, 14.0, 15.0],
494 | ];
495 | $metadatas = [
496 | ['some' => 'metadata1'],
497 | ['some' => 'metadata2'],
498 | ['some' => 'metadata3'],
499 | ];
500 |
501 | $this->collection->add($ids, $embeddings, $metadatas);
502 |
503 | expect($this->collection->count())->toBe(3);
504 |
505 | $this->collection->delete(['test1', 'test2']);
506 |
507 | expect($this->collection->count())->toBe(1);
508 | });
509 |
510 | it('can delete a collection by where', function () {
511 | $ids = ['test1', 'test2', 'test3'];
512 | $embeddings = [
513 | [1.0, 2.0, 3.0, 4.0, 5.0],
514 | [6.0, 7.0, 8.0, 9.0, 10.0],
515 | [11.0, 12.0, 13.0, 14.0, 15.0],
516 | ];
517 | $metadatas = [
518 | ['some' => 'metadata1'],
519 | ['some' => 'metadata2'],
520 | ['some' => 'metadata3'],
521 | ];
522 |
523 | $this->collection->add($ids, $embeddings, $metadatas);
524 |
525 | expect($this->collection->count())->toBe(3);
526 |
527 | $this->collection->delete(
528 | where: [
529 | 'some' => 'metadata1'
530 | ]
531 | );
532 |
533 | expect($this->collection->count())->toBe(2);
534 | });
--------------------------------------------------------------------------------