Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ echo $queryResponse->ids[0][1]; // test2

In order to use this library, you need to have ChromaDB running somewhere. You can either run it locally or in the
cloud.
(Chroma doesn't support cloud yet, but it will soon.)

For now, ChromaDB can only run in-memory in Python. You can however run it in client/server mode by either running the
python
Expand Down Expand Up @@ -164,10 +163,14 @@ If the tenant or database doesn't exist, the package will automatically create t

### Authentication

ChromaDB supports static token-based authentication. To use it, you need to start the Chroma server passing the required
environment variables as stated in the documentation. If you're using the docker image, you can pass in the environment
variables using the `--env` flag or by using a `.env` file and for the docker-compose file, you can use the `env_file`
option, or pass in the environment variables directly like so:
ChromaDB supports multiple authentication methods.

#### Self-Hosted (Static Token)

For self-hosted instances, you can use static token-based authentication. You need to start the Chroma server passing
the required environment variables as stated in the documentation. If you're using the docker image, you can pass in the
environment variables using the `--env` flag or by using a `.env` file and for the docker-compose file, you can use the
env_file option, or pass in the environment variables directly like so:

```yaml
version: '3.9'
Expand All @@ -194,6 +197,22 @@ $chroma = ChromaDB::factory()
->connect();
```

#### Chroma Cloud

To authenticate with [Chroma Cloud](https://www.trychroma.com/cloud), you must provide your API key using the `withChromaApiKey()` method. This will
correctly send the key in the `X-CHROMA-TOKEN` header.

You also need to set the host to your Chroma Cloud instance URL and the port to 443 (if you want to use TLS).

```php
use Codewithkyrian\ChromaDB\ChromaDB;

$chroma = ChromaDB::factory()
->withHost('https://api.trychroma.com')
->withPort(443)
->withChromaApiKey('your-chroma-cloud-api-key')
->connect();
```
### Getting the version

```php
Expand Down
20 changes: 20 additions & 0 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ class Factory
*/
protected string $authToken;

/**
* The API key used to authenticate requests to Chroma Cloud.
* This will be sent as the X-CHROMA-TOKEN header.
*/
protected ?string $chromaApiKey = null;

/**
* The http client to use for the requests.
*/
Expand Down Expand Up @@ -93,6 +99,16 @@ public function withAuthToken(string $authToken): self
return $this;
}

/**
* The API key used to authenticate requests to Chroma Cloud.
* This will be sent as the X-CHROMA-TOKEN header.
*/
public function withChromaApiKey(string $apiKey): self
{
$this->chromaApiKey = $apiKey;
return $this;
}

/**
* The http client to use for the requests.
*/
Expand Down Expand Up @@ -122,6 +138,10 @@ public function createApiClient() : ChromaApiClient
$headers['Authorization'] = 'Bearer ' . $this->authToken;
}

if (!empty($this->chromaApiKey)) {
$headers['X-CHROMA-TOKEN'] = $this->chromaApiKey;
}

$this->httpClient ??= new \GuzzleHttp\Client([
'base_uri' => $this->baseUrl,
'headers' => $headers,
Expand Down
94 changes: 94 additions & 0 deletions tests/Factory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

declare(strict_types=1);

use Codewithkyrian\ChromaDB\ChromaDB;

describe('Factory', function () {

it('can set chroma api key for authentication', function () {
$factory = ChromaDB::factory()
->withHost('https://api.trychroma.com')
->withPort(443)
->withChromaApiKey('test-api-key-12345');

$reflection = new ReflectionClass($factory);
$property = $reflection->getProperty('chromaApiKey');
$property->setAccessible(true);

expect($property->getValue($factory))->toBe('test-api-key-12345');
});

it('can set both auth token and chroma api key', function () {
$factory = ChromaDB::factory()
->withAuthToken('bearer-token-123')
->withChromaApiKey('chroma-key-456');

$reflection = new ReflectionClass($factory);

$authTokenProperty = $reflection->getProperty('authToken');
$authTokenProperty->setAccessible(true);

$chromaKeyProperty = $reflection->getProperty('chromaApiKey');
$chromaKeyProperty->setAccessible(true);

expect($authTokenProperty->getValue($factory))
->toBe('bearer-token-123')
->and($chromaKeyProperty->getValue($factory))
->toBe('chroma-key-456');
});

it('includes X-CHROMA-TOKEN header when api key is set', function () {
$factory = ChromaDB::factory()
->withChromaApiKey('test-key-789');

$apiClient = $factory->createApiClient();

$reflection = new ReflectionClass($apiClient);
$clientProperty = $reflection->getProperty('httpClient');
$clientProperty->setAccessible(true);
$httpClient = $clientProperty->getValue($apiClient);

$config = $httpClient->getConfig();

expect($config['headers']['X-CHROMA-TOKEN'] ?? null)
->toBe('test-key-789');
});

it('includes both Authorization and X-CHROMA-TOKEN headers when both are set', function () {
$factory = ChromaDB::factory()
->withAuthToken('my-bearer-token')
->withChromaApiKey('my-chroma-key');

$apiClient = $factory->createApiClient();

$reflection = new ReflectionClass($apiClient);
$clientProperty = $reflection->getProperty('httpClient');
$clientProperty->setAccessible(true);
$httpClient = $clientProperty->getValue($apiClient);

$config = $httpClient->getConfig();

expect($config['headers']['Authorization'] ?? null)
->toBe('Bearer my-bearer-token')
->and($config['headers']['X-CHROMA-TOKEN'] ?? null)
->toBe('my-chroma-key');
});

it('does not include X-CHROMA-TOKEN header when api key is not set', function () {
$factory = ChromaDB::factory();

$apiClient = $factory->createApiClient();

$reflection = new ReflectionClass($apiClient);
$clientProperty = $reflection->getProperty('httpClient');
$clientProperty->setAccessible(true);
$httpClient = $clientProperty->getValue($apiClient);

$config = $httpClient->getConfig();

expect($config['headers']['X-CHROMA-TOKEN'] ?? null)
->toBeNull();
});

});