Skip to content

Commit fa3101d

Browse files
feat: add collection forking support
Note: Collection forking is only supported for Chroma Cloud, not local Chroma instances.
1 parent 8397a91 commit fa3101d

6 files changed

Lines changed: 125 additions & 4 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ $collection = $client->getCollection('my-collection');
119119
// Get or Create =
120120
$collection = $client->getOrCreateCollection('my-collection', $ef);
121121

122+
// Fork (creates a copy of an existing collection)
123+
// Note: Forking is only supported for Chroma Cloud, not local Chroma instances
124+
$forkedCollection = $client->forkCollection('my-collection', 'my-collection-fork', $ef);
125+
122126
// Delete
123127
$client->deleteCollection('my-collection');
124128
```

src/Api.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Codewithkyrian\ChromaDB\Requests\CreateDatabaseRequest;
1515
use Codewithkyrian\ChromaDB\Requests\CreateTenantRequest;
1616
use Codewithkyrian\ChromaDB\Requests\DeleteItemsRequest;
17+
use Codewithkyrian\ChromaDB\Requests\ForkCollectionRequest;
1718
use Codewithkyrian\ChromaDB\Requests\GetEmbeddingRequest;
1819
use Codewithkyrian\ChromaDB\Requests\QueryItemsRequest;
1920
use Codewithkyrian\ChromaDB\Requests\UpdateCollectionRequest;
@@ -38,8 +39,7 @@ public function __construct(
3839
public readonly StreamFactoryInterface $streamFactory,
3940
public readonly string $baseUri,
4041
public readonly array $headers = [],
41-
) {
42-
}
42+
) {}
4343

4444
/**
4545
* Retrieves the current user's identity, tenant, and databases.
@@ -288,6 +288,27 @@ public function updateCollection(string $collectionId, string $database, string
288288
]);
289289
}
290290

291+
/**
292+
* Forks an existing collection.
293+
*
294+
* @param string $collectionId The UUID of the collection to fork.
295+
* @param string $database The database name to fork the collection from.
296+
* @param string $tenant The tenant ID to fork the collection from.
297+
* @param ForkCollectionRequest $request The request to fork the collection.
298+
*
299+
* @return Collection
300+
*/
301+
public function forkCollection(string $collectionId, string $database, string $tenant, ForkCollectionRequest $request): Collection
302+
{
303+
$response = $this->sendRequest('POST', "/api/v2/tenants/$tenant/databases/$database/collections/$collectionId/fork", [
304+
'json' => $request->toArray()
305+
]);
306+
307+
$result = json_decode($response->getBody()->getContents(), true);
308+
309+
return Collection::fromArray($result, $this, $database, $tenant);
310+
}
311+
291312
/**
292313
* Deletes a collection in a given database.
293314
*
@@ -313,7 +334,6 @@ public function countCollections(string $database, string $tenant): int
313334
$response = $this->sendRequest('GET', "/api/v2/tenants/$tenant/databases/$database/collections_count");
314335

315336
return json_decode($response->getBody()->getContents(), true);
316-
317337
}
318338

319339
/**
@@ -392,7 +412,7 @@ public function getCollectionItems(string $collectionId, string $database, strin
392412
$response = $this->sendRequest('POST', "/api/v2/tenants/$tenant/databases/$database/collections/$collectionId/get", [
393413
'json' => $request->toArray(),
394414
]);
395-
415+
396416
$result = json_decode($response->getBody()->getContents(), true);
397417

398418
return GetItemsResponse::fromArray($result);

src/Client.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Codewithkyrian\ChromaDB\Requests\CreateDatabaseRequest;
1212
use Codewithkyrian\ChromaDB\Requests\CreateTenantRequest;
1313
use Codewithkyrian\ChromaDB\Requests\CreateCollectionRequest;
14+
use Codewithkyrian\ChromaDB\Requests\ForkCollectionRequest;
1415

1516
class Client
1617
{
@@ -133,6 +134,29 @@ public function getCollection(string $name, ?EmbeddingFunction $embeddingFunctio
133134
return $collection;
134135
}
135136

137+
/**
138+
* Forks an existing collection.
139+
*
140+
* @param string $name The name of the collection to fork.
141+
* @param string $newName The name for the forked collection.
142+
* @param ?EmbeddingFunction $embeddingFunction Optional custom embedding function for the forked collection.
143+
*
144+
* @return Collection
145+
*/
146+
public function forkCollection(string $name, string $newName, ?EmbeddingFunction $embeddingFunction = null): Collection
147+
{
148+
$collection = $this->api->getCollection($name, $this->database, $this->tenant);
149+
$request = new ForkCollectionRequest($newName);
150+
151+
$forkedCollection = $this->api->forkCollection($collection->id, $this->database, $this->tenant, $request);
152+
153+
if ($embeddingFunction) {
154+
$forkedCollection->setEmbeddingFunction($embeddingFunction);
155+
}
156+
157+
return $forkedCollection;
158+
}
159+
136160
/**
137161
* Deletes a collection with the specified name.
138162
*
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
6+
namespace Codewithkyrian\ChromaDB\Requests;
7+
8+
/**
9+
* Request model for forking a collection.
10+
*/
11+
class ForkCollectionRequest
12+
{
13+
/**
14+
* @param string $newName The name for the forked collection
15+
*/
16+
public function __construct(
17+
public readonly string $newName,
18+
) {}
19+
20+
public static function fromArray(array $data): self
21+
{
22+
return new self(
23+
newName: $data['new_name'],
24+
);
25+
}
26+
27+
public function toArray(): array
28+
{
29+
return [
30+
'new_name' => $this->newName,
31+
];
32+
}
33+
}

tests/Feature/ApiTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Codewithkyrian\ChromaDB\Requests\CreateDatabaseRequest;
1414
use Codewithkyrian\ChromaDB\Requests\CreateTenantRequest;
1515
use Codewithkyrian\ChromaDB\Requests\DeleteItemsRequest;
16+
use Codewithkyrian\ChromaDB\Requests\ForkCollectionRequest;
1617
use Codewithkyrian\ChromaDB\Requests\GetEmbeddingRequest;
1718
use Codewithkyrian\ChromaDB\Requests\QueryItemsRequest;
1819
use Codewithkyrian\ChromaDB\Requests\UpdateCollectionRequest;
@@ -190,6 +191,26 @@
190191
->and($updatedCollection->metadata)->toBe(['new' => 'metadata']);
191192
});
192193

194+
it('can fork a collection', function () {
195+
if (str_contains($this->api->baseUri, 'localhost') || !str_contains($this->api->baseUri, 'api.trychroma.com')) {
196+
test()->markTestSkipped('Collection forking is not supported for local Chroma');
197+
}
198+
199+
$collectionName = 'test-collection-' . uniqid();
200+
$collection = $this->api->createCollection('default_database', 'default_tenant', new CreateCollectionRequest($collectionName, ['original' => 'metadata']));
201+
202+
$forkedName = 'forked-' . $collectionName;
203+
$forkedCollection = $this->api->forkCollection($collection->id, 'default_database', 'default_tenant', new ForkCollectionRequest($forkedName));
204+
205+
expect($forkedCollection->name)->toBe($forkedName)
206+
->and($forkedCollection->id)->not->toBe($collection->id);
207+
208+
$collections = $this->api->listCollections('default_database', 'default_tenant');
209+
$names = array_map(fn($c) => $c->name, $collections);
210+
expect($names)->toContain($collectionName)
211+
->and($names)->toContain($forkedName);
212+
});
213+
193214
it('can delete a collection', function () {
194215
$collectionName = 'test-collection-' . uniqid();
195216
$collection = $this->api->createCollection('default_database', 'default_tenant', new CreateCollectionRequest($collectionName, null));

tests/Feature/ClientTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
declare(strict_types=1);
44

55
namespace Codewithkyrian\ChromaDB\Tests\Feature;
6+
67
use Codewithkyrian\ChromaDB\ChromaDB;
78
use Codewithkyrian\ChromaDB\Embeddings\EmbeddingFunction;
89
use Codewithkyrian\ChromaDB\Exceptions\NotFoundException;
@@ -106,6 +107,24 @@ public function generate(array $texts): array
106107
->toMatchArray(['test' => 'test_2']);
107108
});
108109

110+
it('can fork a collection', function () {
111+
if (str_contains($this->client->api->baseUri, 'localhost') || !str_contains($this->client->api->baseUri, 'api.trychroma.com')) {
112+
test()->markTestSkipped('Collection forking is not supported for local Chroma');
113+
}
114+
115+
$forkedCollection = $this->client->forkCollection('test_collection', 'test_collection_fork', $this->embeddingFunction);
116+
117+
expect($forkedCollection)
118+
->toBeInstanceOf(Collection::class)
119+
->toHaveProperty('name', 'test_collection_fork')
120+
->and($forkedCollection->id)->not->toBe($this->collection->id);
121+
122+
$collections = $this->client->listCollections();
123+
$names = array_map(fn($c) => $c->name, $collections);
124+
expect($names)->toContain('test_collection')
125+
->and($names)->toContain('test_collection_fork');
126+
});
127+
109128
it('can delete a collection', function () {
110129
$this->client->deleteCollection('test_collection');
111130

0 commit comments

Comments
 (0)