feature #1298 [Store] Change StoreInterface::add() from variadic to VectorDocument|array parameter (claude)

This PR was merged into the main branch.

Discussion
----------

[Store] Change `StoreInterface::add()` from variadic to `VectorDocument|array` parameter

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| Docs?         | yes
| Issues        | --
| License       | MIT

This change improves backwards compatibility by allowing future parameters to be added to the method signature. With variadic parameters, no additional arguments can be added after the variadic argument per PHP's BC policy.

Thanks for proposing `@stof` 👍

Commits
-------

8d7d919a [Store] Change StoreInterface::add() from variadic to array parameter
This commit is contained in:
Oskar Stark
2025-12-30 22:55:30 +01:00
74 changed files with 313 additions and 189 deletions

View File

@@ -60,4 +60,3 @@ jobs:
# issue_number: context.issue.number,
# name: 'Run examples'
# });

View File

@@ -13,3 +13,28 @@ AI Bundle
- private AgentInterface $supportMultiAgent,
+ private AgentInterface $support,
) {}
```
Store
-----
* The `StoreInterface::add()` method signature has changed from variadic to accept a single document or an array
*Before:*
```php
public function add(VectorDocument ...$documents): void;
// Usage
$store->add($document1, $document2);
$store->add(...$documents);
```
*After:*
```php
public function add(VectorDocument|array $documents): void;
// Usage
$store->add($document);
$store->add([$document1, $document2]);
$store->add($documents);
```

View File

@@ -13,14 +13,14 @@
"nyholm/psr7": "^1.8",
"php-http/discovery": "^1.20",
"symfony/ai-agent": "^0.1",
"symfony/ai-bundle": "^0.1",
"symfony/ai-bundle": "^0.2",
"symfony/ai-chat": "^0.1",
"symfony/ai-chroma-db-store": "^0.1",
"symfony/ai-chroma-db-store": "^0.2",
"symfony/ai-clock-tool": "^0.1",
"symfony/ai-hugging-face-platform": "^0.1",
"symfony/ai-open-ai-platform": "^0.1",
"symfony/ai-similarity-search-tool": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/ai-wikipedia-tool": "^0.1",
"symfony/asset": "^8.0",
"symfony/asset-mapper": "^8.0",

View File

@@ -16,7 +16,7 @@ Stores vectors in a PHP array. Data is not persisted and is lost when the PHP pr
use Symfony\AI\Store\InMemory\Store;
$store = new Store();
$store->add($document1, $document2);
$store->add([$document1, $document2]);
$results = $store->query($vector);
CacheStore
@@ -29,7 +29,7 @@ Stores vectors using a PSR-6 cache implementation. Persistence depends on the ca
$cache = new FilesystemAdapter();
$store = new Store($cache);
$store->add($document1, $document2);
$store->add([$document1, $document2]);
$results = $store->query($vector);
Distance Strategies

View File

@@ -28,65 +28,65 @@
"symfony/ai-albert-platform": "^0.1",
"symfony/ai-anthropic-platform": "^0.1",
"symfony/ai-azure-platform": "^0.1",
"symfony/ai-azure-search-store": "^0.1",
"symfony/ai-azure-search-store": "^0.2",
"symfony/ai-bedrock-platform": "^0.1",
"symfony/ai-brave-tool": "^0.1",
"symfony/ai-cache-message-store": "^0.1",
"symfony/ai-cache-store": "^0.1",
"symfony/ai-cache-store": "^0.2",
"symfony/ai-cartesia-platform": "^0.1",
"symfony/ai-cerebras-platform": "^0.1",
"symfony/ai-chat": "^0.1",
"symfony/ai-chroma-db-store": "^0.1",
"symfony/ai-click-house-store": "^0.1",
"symfony/ai-chroma-db-store": "^0.2",
"symfony/ai-click-house-store": "^0.2",
"symfony/ai-clock-tool": "^0.1",
"symfony/ai-cloudflare-message-store": "^0.1",
"symfony/ai-cloudflare-store": "^0.1",
"symfony/ai-cloudflare-store": "^0.2",
"symfony/ai-decart-platform": "^0.1",
"symfony/ai-deep-seek-platform": "^0.1",
"symfony/ai-doctrine-message-store": "^0.1",
"symfony/ai-docker-model-runner-platform": "^0.1",
"symfony/ai-elasticsearch-store": "^0.1",
"symfony/ai-elasticsearch-store": "^0.2",
"symfony/ai-eleven-labs-platform": "^0.1",
"symfony/ai-gemini-platform": "^0.1",
"symfony/ai-generic-platform": "^0.1",
"symfony/ai-session-message-store": "^0.1",
"symfony/ai-hugging-face-platform": "^0.1",
"symfony/ai-lm-studio-platform": "^0.1",
"symfony/ai-manticore-search-store": "^0.1",
"symfony/ai-maria-db-store": "^0.1",
"symfony/ai-manticore-search-store": "^0.2",
"symfony/ai-maria-db-store": "^0.2",
"symfony/ai-meilisearch-message-store": "^0.1",
"symfony/ai-meilisearch-store": "^0.1",
"symfony/ai-meilisearch-store": "^0.2",
"symfony/ai-meta-platform": "^0.1",
"symfony/ai-milvus-store": "^0.1",
"symfony/ai-milvus-store": "^0.2",
"symfony/ai-mistral-platform": "^0.1",
"symfony/ai-mongo-db-message-store": "^0.1",
"symfony/ai-mongo-db-store": "^0.1",
"symfony/ai-neo4j-store": "^0.1",
"symfony/ai-mongo-db-store": "^0.2",
"symfony/ai-neo4j-store": "^0.2",
"symfony/ai-ollama-platform": "^0.1",
"symfony/ai-open-ai-platform": "^0.1",
"symfony/ai-open-meteo-tool": "^0.1",
"symfony/ai-open-router-platform": "^0.1",
"symfony/ai-open-search-store": "^0.1",
"symfony/ai-open-search-store": "^0.2",
"symfony/ai-perplexity-platform": "^0.1",
"symfony/ai-pinecone-store": "^0.1",
"symfony/ai-pinecone-store": "^0.2",
"symfony/ai-pogocache-message-store": "^0.1",
"symfony/ai-postgres-store": "^0.1",
"symfony/ai-qdrant-store": "^0.1",
"symfony/ai-postgres-store": "^0.2",
"symfony/ai-qdrant-store": "^0.2",
"symfony/ai-redis-message-store": "^0.1",
"symfony/ai-redis-store": "^0.1",
"symfony/ai-redis-store": "^0.2",
"symfony/ai-scaleway-platform": "^0.1",
"symfony/ai-scraper-tool": "^0.1",
"symfony/ai-serp-api-tool": "^0.1",
"symfony/ai-similarity-search-tool": "^0.1",
"symfony/ai-supabase-store": "^0.1",
"symfony/ai-supabase-store": "^0.2",
"symfony/ai-surreal-db-message-store": "^0.1",
"symfony/ai-surreal-db-store": "^0.1",
"symfony/ai-surreal-db-store": "^0.2",
"symfony/ai-tavily-tool": "^0.1",
"symfony/ai-transformers-php-platform": "^0.1",
"symfony/ai-typesense-store": "^0.1",
"symfony/ai-typesense-store": "^0.2",
"symfony/ai-vertex-ai-platform": "^0.1",
"symfony/ai-voyage-platform": "^0.1",
"symfony/ai-weaviate-store": "^0.1",
"symfony/ai-weaviate-store": "^0.2",
"symfony/ai-wikipedia-tool": "^0.1",
"symfony/ai-youtube-tool": "^0.1",
"symfony/console": "^7.4|^8.0",

View File

@@ -38,60 +38,60 @@
"symfony/ai-albert-platform": "^0.1",
"symfony/ai-anthropic-platform": "^0.1",
"symfony/ai-azure-platform": "^0.1",
"symfony/ai-azure-search-store": "^0.1",
"symfony/ai-azure-search-store": "^0.2",
"symfony/ai-bedrock-platform": "^0.1",
"symfony/ai-cache-message-store": "^0.1",
"symfony/ai-cache-store": "^0.1",
"symfony/ai-cache-store": "^0.2",
"symfony/ai-cartesia-platform": "^0.1",
"symfony/ai-cerebras-platform": "^0.1",
"symfony/ai-chat": "^0.1",
"symfony/ai-chroma-db-store": "^0.1",
"symfony/ai-click-house-store": "^0.1",
"symfony/ai-chroma-db-store": "^0.2",
"symfony/ai-click-house-store": "^0.2",
"symfony/ai-cloudflare-message-store": "^0.1",
"symfony/ai-cloudflare-store": "^0.1",
"symfony/ai-cloudflare-store": "^0.2",
"symfony/ai-decart-platform": "^0.1",
"symfony/ai-deep-seek-platform": "^0.1",
"symfony/ai-docker-model-runner-platform": "^0.1",
"symfony/ai-doctrine-message-store": "^0.1",
"symfony/ai-elasticsearch-store": "^0.1",
"symfony/ai-elasticsearch-store": "^0.2",
"symfony/ai-eleven-labs-platform": "^0.1",
"symfony/ai-gemini-platform": "^0.1",
"symfony/ai-generic-platform": "^0.1",
"symfony/ai-hugging-face-platform": "^0.1",
"symfony/ai-lm-studio-platform": "^0.1",
"symfony/ai-manticore-search-store": "^0.1",
"symfony/ai-maria-db-store": "^0.1",
"symfony/ai-manticore-search-store": "^0.2",
"symfony/ai-maria-db-store": "^0.2",
"symfony/ai-meilisearch-message-store": "^0.1",
"symfony/ai-meilisearch-store": "^0.1",
"symfony/ai-meilisearch-store": "^0.2",
"symfony/ai-meta-platform": "^0.1",
"symfony/ai-milvus-store": "^0.1",
"symfony/ai-milvus-store": "^0.2",
"symfony/ai-mistral-platform": "^0.1",
"symfony/ai-mongo-db-message-store": "^0.1",
"symfony/ai-mongo-db-store": "^0.1",
"symfony/ai-neo4j-store": "^0.1",
"symfony/ai-mongo-db-store": "^0.2",
"symfony/ai-neo4j-store": "^0.2",
"symfony/ai-ollama-platform": "^0.1",
"symfony/ai-open-ai-platform": "^0.1",
"symfony/ai-open-router-platform": "^0.1",
"symfony/ai-open-search-store": "^0.1",
"symfony/ai-open-search-store": "^0.2",
"symfony/ai-perplexity-platform": "^0.1",
"symfony/ai-pinecone-store": "^0.1",
"symfony/ai-pinecone-store": "^0.2",
"symfony/ai-pogocache-message-store": "^0.1",
"symfony/ai-postgres-store": "^0.1",
"symfony/ai-qdrant-store": "^0.1",
"symfony/ai-postgres-store": "^0.2",
"symfony/ai-qdrant-store": "^0.2",
"symfony/ai-redis-message-store": "^0.1",
"symfony/ai-redis-store": "^0.1",
"symfony/ai-redis-store": "^0.2",
"symfony/ai-replicate-platform": "^0.1",
"symfony/ai-scaleway-platform": "^0.1",
"symfony/ai-session-message-store": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-supabase-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/ai-supabase-store": "^0.2",
"symfony/ai-surreal-db-message-store": "^0.1",
"symfony/ai-surreal-db-store": "^0.1",
"symfony/ai-surreal-db-store": "^0.2",
"symfony/ai-transformers-php-platform": "^0.1",
"symfony/ai-typesense-store": "^0.1",
"symfony/ai-typesense-store": "^0.2",
"symfony/ai-vertex-ai-platform": "^0.1",
"symfony/ai-voyage-platform": "^0.1",
"symfony/ai-weaviate-store": "^0.1",
"symfony/ai-weaviate-store": "^0.2",
"symfony/expression-language": "^7.3|^8.0",
"symfony/security-core": "^7.3|^8.0",
"symfony/translation": "^7.3|^8.0"

View File

@@ -1,6 +1,11 @@
CHANGELOG
=========
0.2
---
* [BC BREAK] Change `StoreInterface::add()` from variadic to accept array and `VectorDocument`
0.1
---

View File

@@ -37,8 +37,12 @@ final class SearchStore implements StoreInterface
) {
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$this->request('index', [
'value' => array_map([$this, 'convertToIndexableArray'], $documents),
]);

View File

@@ -28,7 +28,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -47,8 +47,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
$this->cache->get($this->cacheKey, static fn (): array => []);
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$existingVectors = $this->cache->get($this->cacheKey, static fn (): array => []);
$newVectors = array_map(static fn (VectorDocument $document): array => [

View File

@@ -35,11 +35,11 @@ final class StoreTest extends TestCase
public function testStoreCanDrop()
{
$store = new Store(new ArrayAdapter());
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6])));
$this->assertCount(3, $result);
@@ -53,21 +53,21 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingCosineDistance()
{
$store = new Store(new ArrayAdapter());
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6])));
$this->assertCount(3, $result);
$this->assertSame([0.1, 0.1, 0.5], $result[0]->vector->getData());
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6])));
$this->assertCount(6, $result);
@@ -77,13 +77,13 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingCosineDistanceAndReturnCorrectOrder()
{
$store = new Store(new ArrayAdapter());
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.1, 0.6])),
new VectorDocument(Uuid::v4(), new Vector([0.0, 0.1, 0.6])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6])));
$this->assertCount(5, $result);
@@ -97,11 +97,11 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingCosineDistanceWithMaxItems()
{
$store = new Store(new ArrayAdapter());
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1])),
);
]);
$this->assertCount(1, iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6]), [
'maxItems' => 1,
@@ -111,10 +111,10 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingAngularDistance()
{
$store = new Store(new ArrayAdapter(), new DistanceCalculator(DistanceStrategy::ANGULAR_DISTANCE));
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([1.0, 2.0, 3.0])),
new VectorDocument(Uuid::v4(), new Vector([1.0, 5.0, 7.0])),
);
]);
$result = iterator_to_array($store->query(new Vector([1.2, 2.3, 3.4])));
@@ -125,10 +125,10 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingEuclideanDistance()
{
$store = new Store(new ArrayAdapter(), new DistanceCalculator(DistanceStrategy::EUCLIDEAN_DISTANCE));
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([1.0, 5.0, 7.0])),
new VectorDocument(Uuid::v4(), new Vector([1.0, 2.0, 3.0])),
);
]);
$result = iterator_to_array($store->query(new Vector([1.2, 2.3, 3.4])));
@@ -139,10 +139,10 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingManhattanDistance()
{
$store = new Store(new ArrayAdapter(), new DistanceCalculator(DistanceStrategy::MANHATTAN_DISTANCE));
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([1.0, 2.0, 3.0])),
new VectorDocument(Uuid::v4(), new Vector([1.0, 5.0, 7.0])),
);
]);
$result = iterator_to_array($store->query(new Vector([1.2, 2.3, 3.4])));
@@ -153,10 +153,10 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingChebyshevDistance()
{
$store = new Store(new ArrayAdapter(), new DistanceCalculator(DistanceStrategy::CHEBYSHEV_DISTANCE));
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([1.0, 2.0, 3.0])),
new VectorDocument(Uuid::v4(), new Vector([1.0, 5.0, 7.0])),
);
]);
$result = iterator_to_array($store->query(new Vector([1.2, 2.3, 3.4])));
@@ -167,11 +167,11 @@ final class StoreTest extends TestCase
public function testStoreCanSearchWithFilter()
{
$store = new Store(new ArrayAdapter());
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5]), new Metadata(['category' => 'products', 'enabled' => true])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0]), new Metadata(['category' => 'articles', 'enabled' => true])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1]), new Metadata(['category' => 'products', 'enabled' => false])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6]), [
'filter' => fn (VectorDocument $doc) => 'products' === $doc->metadata['category'],
@@ -185,12 +185,12 @@ final class StoreTest extends TestCase
public function testStoreCanSearchWithFilterAndMaxItems()
{
$store = new Store(new ArrayAdapter());
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5]), new Metadata(['category' => 'products'])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0]), new Metadata(['category' => 'articles'])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1]), new Metadata(['category' => 'products'])),
new VectorDocument(Uuid::v4(), new Vector([0.0, 0.1, 0.6]), new Metadata(['category' => 'products'])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6]), [
'filter' => fn (VectorDocument $doc) => 'products' === $doc->metadata['category'],
@@ -205,11 +205,11 @@ final class StoreTest extends TestCase
public function testStoreCanSearchWithComplexFilter()
{
$store = new Store(new ArrayAdapter());
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5]), new Metadata(['price' => 100, 'stock' => 5])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0]), new Metadata(['price' => 200, 'stock' => 0])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1]), new Metadata(['price' => 50, 'stock' => 10])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6]), [
'filter' => fn (VectorDocument $doc) => $doc->metadata['price'] <= 150 && $doc->metadata['stock'] > 0,
@@ -221,11 +221,11 @@ final class StoreTest extends TestCase
public function testStoreCanSearchWithNestedMetadataFilter()
{
$store = new Store(new ArrayAdapter());
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5]), new Metadata(['options' => ['size' => 'S', 'color' => 'blue']])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0]), new Metadata(['options' => ['size' => 'M', 'color' => 'blue']])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1]), new Metadata(['options' => ['size' => 'S', 'color' => 'red']])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6]), [
'filter' => fn (VectorDocument $doc) => 'S' === $doc->metadata['options']['size'],
@@ -239,11 +239,11 @@ final class StoreTest extends TestCase
public function testStoreCanSearchWithInArrayFilter()
{
$store = new Store(new ArrayAdapter());
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5]), new Metadata(['brand' => 'Nike'])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0]), new Metadata(['brand' => 'Adidas'])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1]), new Metadata(['brand' => 'Generic'])),
);
]);
$allowedBrands = ['Nike', 'Adidas', 'Puma'];
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6]), [

View File

@@ -27,7 +27,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/cache": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -29,8 +29,12 @@ final class Store implements StoreInterface
) {
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$ids = [];
$vectors = [];
$metadata = [];

View File

@@ -53,7 +53,7 @@ final class StoreTest extends TestCase
$store = new Store($client, 'test-collection');
$store->add(...$documents);
$store->add($documents);
}
/**

View File

@@ -28,7 +28,7 @@
"php": ">=8.2",
"codewithkyrian/chromadb-php": "^1.0",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/uid": "^7.3|^8.0"
},
"require-dev": {

View File

@@ -53,8 +53,12 @@ class Store implements ManagedStoreInterface, StoreInterface
$this->execute('POST', 'DROP TABLE IF EXISTS {{ table }}');
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$rows = [];
foreach ($documents as $document) {

View File

@@ -107,7 +107,7 @@ final class StoreTest extends TestCase
$store = new Store($httpClient, 'test_db', 'test_table');
$store->add($document1, $document2);
$store->add([$document1, $document2]);
}
public function testAddThrowsExceptionOnHttpError()

View File

@@ -27,7 +27,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -57,8 +57,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
$this->request('DELETE', \sprintf('vectorize/v2/indexes/%s', $this->index));
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$payload = array_map(
$this->convertToIndexableArray(...),
$documents,

View File

@@ -151,7 +151,7 @@ final class StoreTest extends TestCase
$this->expectException(ClientException::class);
$this->expectExceptionMessage('HTTP 400 returned for "https://api.cloudflare.com/client/v4/accounts/foo/vectorize/v2/indexes/random/upsert".');
$this->expectExceptionCode(400);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
}
public function testStoreCanAdd()
@@ -174,7 +174,7 @@ final class StoreTest extends TestCase
'random',
);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
$this->assertSame(1, $mockHttpClient->getRequestsCount());
}

View File

@@ -28,7 +28,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -65,8 +65,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
$this->request('DELETE', $this->indexName);
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$documentToIndex = fn (VectorDocument $document): array => [
'index' => [
'_index' => $this->indexName,

View File

@@ -141,7 +141,7 @@ final class StoreTest extends TestCase
]);
$store = new Store($httpClient, 'http://127.0.0.1:9200', 'foo');
$store->add(new VectorDocument(Uuid::v7(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v7(), new Vector([0.1, 0.2, 0.3]))]);
$this->assertSame(1, $httpClient->getRequestsCount());
}

View File

@@ -23,7 +23,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -58,8 +58,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
/**
* @throws \Random\RandomException {@see random_int()}
*/
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$payload = array_map(
fn (VectorDocument $document): array => [
'insert' => [

View File

@@ -111,7 +111,7 @@ final class StoreTest extends TestCase
$this->expectException(ClientException::class);
$this->expectExceptionMessage('HTTP 400 returned for "http://127.0.0.1:9308/bulk".');
$this->expectExceptionCode(400);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
}
public function testStoreCanAdd()
@@ -141,7 +141,7 @@ final class StoreTest extends TestCase
]);
$store = new Store($httpClient, 'http://127.0.0.1:9308', 'bar', 'random');
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
$this->assertSame(1, $httpClient->getRequestsCount());
}

View File

@@ -27,7 +27,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -101,8 +101,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
return self::fromPdo($pdo, $tableName, $indexName, $vectorFieldName);
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$statement = $this->connection->prepare(
\sprintf(
<<<'SQL'

View File

@@ -28,7 +28,7 @@
"php": ">=8.2",
"ext-pdo": "*",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/uid": "^7.3|^8.0"
},
"require-dev": {

View File

@@ -70,8 +70,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
]);
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$this->request('PUT', \sprintf('indexes/%s/documents', $this->indexName), array_map(
$this->convertToIndexableArray(...), $documents)
);

View File

@@ -134,7 +134,7 @@ final class StoreTest extends TestCase
$this->expectException(ClientException::class);
$this->expectExceptionMessage('HTTP 400 returned for "http://127.0.0.1:7700/indexes/test/documents".');
$this->expectExceptionCode(400);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
}
public function testStoreCanAdd()
@@ -158,7 +158,7 @@ final class StoreTest extends TestCase
'test',
);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
$this->assertSame(1, $httpClient->getRequestsCount());
}

View File

@@ -27,7 +27,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -92,8 +92,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
]);
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$this->request('POST', 'v2/vectordb/entities/insert', [
'collectionName' => $this->collection,
'data' => array_map($this->convertToIndexableArray(...), $documents),

View File

@@ -143,7 +143,7 @@ final class StoreTest extends TestCase
$this->expectException(ClientException::class);
$this->expectExceptionMessage('HTTP 400 returned for "http://127.0.0.1:19530/v2/vectordb/entities/insert".');
$this->expectExceptionCode(400);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
}
public function testStoreCanAdd()
@@ -171,7 +171,7 @@ final class StoreTest extends TestCase
'test',
);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
$this->assertSame(1, $httpClient->getRequestsCount());
}

View File

@@ -27,7 +27,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -104,8 +104,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
$this->getCollection()->drop();
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$operations = [];
foreach ($documents as $document) {

View File

@@ -90,7 +90,7 @@ final class StoreTest extends TestCase
$document1 = new VectorDocument($uuid1, new Vector([0.1, 0.2, 0.3]));
$document2 = new VectorDocument($uuid2, new Vector([0.4, 0.5, 0.6]), new Metadata(['title' => 'Test']));
$store->add($document1, $document2);
$store->add([$document1, $document2]);
}
public function testAddWithBulkWrite()
@@ -142,7 +142,7 @@ final class StoreTest extends TestCase
$document1 = new VectorDocument($uuid1, new Vector([0.1, 0.2, 0.3]));
$document2 = new VectorDocument($uuid2, new Vector([0.4, 0.5, 0.6]), new Metadata(['title' => 'Test']));
$store->add($document1, $document2);
$store->add([$document1, $document2]);
}
public function testQueryReturnsDocuments()

View File

@@ -30,7 +30,7 @@
"mongodb/mongodb": "^1.21|^2.0",
"psr/log": "^3.0",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/uid": "^7.3|^8.0"
},
"require-dev": {

View File

@@ -51,8 +51,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
]);
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
foreach ($documents as $document) {
$this->request('POST', \sprintf('db/%s/query/v2', $this->databaseName), [
'statement' => \sprintf('CREATE (n:%s {id: $id, metadata: $metadata, %s: $embeddings}) RETURN n', $this->nodeName, $this->embeddingsField),

View File

@@ -187,8 +187,8 @@ final class StoreTest extends TestCase
$store = new Store($httpClient, 'http://127.0.0.1:7474', 'symfony', 'symfony', 'symfony', 'symfony', 'symfony');
$store->setup();
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
$this->assertSame(3, $httpClient->getRequestsCount());
}
@@ -286,7 +286,7 @@ final class StoreTest extends TestCase
$store = new Store($httpClient, 'http://127.0.0.1:7474', 'symfony', 'symfony', 'symfony', 'symfony', 'symfony');
$store->setup();
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
$results = iterator_to_array($store->query(new Vector([0.1, 0.2, 0.3])));

View File

@@ -28,7 +28,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -71,8 +71,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
$this->request('DELETE', $this->indexName);
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$documentToIndex = fn (VectorDocument $document): array => [
'index' => [
'_index' => $this->indexName,

View File

@@ -143,7 +143,7 @@ final class StoreTest extends TestCase
]);
$store = new Store($httpClient, 'http://127.0.0.1:9200', 'foo');
$store->add(new VectorDocument(Uuid::v7(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v7(), new Vector([0.1, 0.2, 0.3]))]);
$this->assertSame(1, $httpClient->getRequestsCount());
}

View File

@@ -27,7 +27,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -63,8 +63,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
);
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$vectors = [];
foreach ($documents as $document) {
$vectors[] = [

View File

@@ -98,7 +98,7 @@ final class StoreTest extends TestCase
$document1 = new VectorDocument($uuid1, new Vector([0.1, 0.2, 0.3]));
$document2 = new VectorDocument($uuid2, new Vector([0.4, 0.5, 0.6]), new Metadata(['title' => 'Second Document']));
self::createStore($client)->add($document1, $document2);
self::createStore($client)->add([$document1, $document2]);
}
public function testAddWithNamespace()
@@ -141,7 +141,7 @@ final class StoreTest extends TestCase
$client->expects($this->never())
->method('data');
self::createStore($client)->add();
self::createStore($client)->add([]);
}
public function testQueryReturnsDocuments()

View File

@@ -28,7 +28,7 @@
"php": ">=8.2",
"probots-io/pinecone-php": "^1.0",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/uid": "^7.3|^8.0"
},
"require-dev": {

View File

@@ -104,8 +104,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
return self::fromPdo($pdo, $tableName, $vectorFieldName, $distance);
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$statement = $this->connection->prepare(
\sprintf(
'INSERT INTO %1$s (id, metadata, %2$s)

View File

@@ -94,7 +94,7 @@ final class StoreTest extends TestCase
$document1 = new VectorDocument($uuid1, new Vector([0.1, 0.2, 0.3]));
$document2 = new VectorDocument($uuid2, new Vector([0.4, 0.5, 0.6]), new Metadata(['title' => 'Second']));
$store->add($document1, $document2);
$store->add([$document1, $document2]);
}
public function testQueryWithoutMaxScore()

View File

@@ -31,7 +31,7 @@
"ext-pdo": "*",
"oskarstark/enum-helper": "^1.5",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/uid": "^7.3|^8.0"
},
"require-dev": {

View File

@@ -57,8 +57,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
]);
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$this->request(
'PUT',
\sprintf('collections/%s/points', $this->collectionName),

View File

@@ -147,7 +147,7 @@ final class StoreTest extends TestCase
$this->expectException(ClientException::class);
$this->expectExceptionMessage('HTTP 400 returned for "http://127.0.0.1:6333/collections/test/points?wait=true".');
$this->expectExceptionCode(400);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
}
public function testStoreCanAdd()

View File

@@ -27,7 +27,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -78,8 +78,12 @@ class Store implements ManagedStoreInterface, StoreInterface
}
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$this->redis->clearLastError();
$pipeline = $this->redis->multi(\Redis::PIPELINE);

View File

@@ -93,7 +93,7 @@ final class StoreTest extends TestCase
$document1 = new VectorDocument($uuid1, new Vector([0.1, 0.2, 0.3]));
$document2 = new VectorDocument($uuid2, new Vector([0.4, 0.5, 0.6]), new Metadata(['title' => 'Second']));
$store->add($document1, $document2);
$store->add([$document1, $document2]);
}
public function testQueryWithoutMaxScore()

View File

@@ -29,7 +29,7 @@
"ext-redis": "*",
"oskarstark/enum-helper": "^1.5",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/uid": "^7.3|^8.0"
},
"require-dev": {

View File

@@ -46,8 +46,12 @@ final class Store implements StoreInterface
) {
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
if (0 === \count($documents)) {
return;
}

View File

@@ -40,7 +40,7 @@ class StoreTest extends TestCase
$httpClient = new MockHttpClient();
$store = $this->createStore($httpClient);
$store->add();
$store->add([]);
$this->assertSame(0, $httpClient->getRequestsCount());
}
@@ -65,10 +65,10 @@ class StoreTest extends TestCase
$httpClient = new MockHttpClient(new MockResponse('', ['http_code' => 201]));
$store = $this->createStore($httpClient);
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2]), new Metadata(['a' => '1'])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.4]), new Metadata(['b' => '2'])),
);
]);
$this->assertSame(1, $httpClient->getRequestsCount());
}
@@ -78,10 +78,10 @@ class StoreTest extends TestCase
$httpClient = new MockHttpClient(new MockResponse('', ['http_code' => 201]));
$store = $this->createStore($httpClient);
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2]), new Metadata(['valid' => true])),
new VectorDocument(Uuid::v4(), new Vector([0.1]), new Metadata(['invalid' => true])),
);
]);
$this->assertSame(1, $httpClient->getRequestsCount());
}

View File

@@ -28,7 +28,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -54,8 +54,12 @@ class Store implements ManagedStoreInterface, StoreInterface
));
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
foreach ($documents as $document) {
$this->request('POST', \sprintf('key/%s', $this->table), $this->convertToIndexableArray($document));
}

View File

@@ -181,7 +181,7 @@ final class StoreTest extends TestCase
$this->expectException(ClientException::class);
$this->expectExceptionMessage('HTTP 400 returned for "http://127.0.0.1:8000/key/test".');
$this->expectExceptionCode(400);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
}
public function testStoreCannotAddOnInvalidAddResponse()
@@ -214,7 +214,7 @@ final class StoreTest extends TestCase
$this->expectException(ClientException::class);
$this->expectExceptionMessage('HTTP 400 returned for "http://127.0.0.1:8000/key/test".');
$this->expectExceptionCode(400);
$store->add(new VectorDocument(Uuid::v4(), new Vector(array_fill(0, 1275, 0.1))));
$store->add([new VectorDocument(Uuid::v4(), new Vector(array_fill(0, 1275, 0.1)))]);
}
public function testStoreCanAdd()
@@ -265,7 +265,7 @@ final class StoreTest extends TestCase
$store = new Store($httpClient, 'http://127.0.0.1:8000', 'test', 'test', 'test', 'test', 'test');
$store->setup();
$store->add(new VectorDocument(Uuid::v4(), new Vector(array_fill(0, 1275, 0.1))));
$store->add([new VectorDocument(Uuid::v4(), new Vector(array_fill(0, 1275, 0.1)))]);
$this->assertSame(3, $httpClient->getRequestsCount());
}
@@ -321,7 +321,7 @@ final class StoreTest extends TestCase
$store = new Store($httpClient, 'http://127.0.0.1:8000', 'test', 'test', 'test', 'test', 'test');
$store->setup();
$store->add(new VectorDocument(Uuid::v4(), new Vector(array_fill(0, 1275, 0.1))));
$store->add([new VectorDocument(Uuid::v4(), new Vector(array_fill(0, 1275, 0.1)))]);
$this->expectException(ClientException::class);
$this->expectExceptionMessage('HTTP 400 returned for "http://127.0.0.1:8000/sql".');
@@ -398,7 +398,7 @@ final class StoreTest extends TestCase
$store = new Store($httpClient, 'http://127.0.0.1:8000', 'test', 'test', 'test', 'test', 'test');
$store->add(new VectorDocument(Uuid::v4(), new Vector(array_fill(0, 1275, 0.1))));
$store->add([new VectorDocument(Uuid::v4(), new Vector(array_fill(0, 1275, 0.1)))]);
$results = iterator_to_array($store->query(new Vector(array_fill(0, 1275, 0.1))));

View File

@@ -27,7 +27,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -62,8 +62,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
]);
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
foreach ($documents as $document) {
$this->request('POST', \sprintf('collections/%s/documents', $this->collection), $this->convertToIndexableArray($document));
}

View File

@@ -127,7 +127,7 @@ final class StoreTest extends TestCase
$this->expectException(ClientException::class);
$this->expectExceptionMessage('HTTP 400 returned for "http://127.0.0.1:8108/collections/test/documents".');
$this->expectExceptionCode(400);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
}
public function testStoreCanAdd()
@@ -145,7 +145,7 @@ final class StoreTest extends TestCase
'test',
);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
$this->assertSame(1, $httpClient->getRequestsCount());
}

View File

@@ -27,7 +27,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -45,8 +45,12 @@ final class Store implements ManagedStoreInterface, StoreInterface
]);
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
$this->request('POST', 'v1/batch/objects', [
'fields' => [
'ALL',

View File

@@ -154,7 +154,7 @@ final class StoreTest extends TestCase
$this->expectException(ClientException::class);
$this->expectExceptionMessage('HTTP 422 returned for "http://127.0.0.1:8080/v1/batch/objects".');
$this->expectExceptionCode(422);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
}
public function testStoreCanAdd()
@@ -180,7 +180,7 @@ final class StoreTest extends TestCase
'test',
);
$store->add(new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3])));
$store->add([new VectorDocument(Uuid::v4(), new Vector([0.1, 0.2, 0.3]))]);
$this->assertSame(1, $httpClient->getRequestsCount());
}

View File

@@ -27,7 +27,7 @@
"require": {
"php": ">=8.2",
"symfony/ai-platform": "^0.1",
"symfony/ai-store": "^0.1",
"symfony/ai-store": "^0.2",
"symfony/http-client": "^7.3|^8.0",
"symfony/uid": "^7.3|^8.0"
},

View File

@@ -42,8 +42,12 @@ class Store implements ManagedStoreInterface, StoreInterface
$this->drop();
}
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
array_push($this->documents, ...$documents);
}

View File

@@ -87,13 +87,13 @@ class Indexer implements IndexerInterface
++$counter;
if ($chunkSize === \count($chunk)) {
$this->store->add(...$this->vectorizer->vectorize($chunk, $options['platform_options'] ?? []));
$this->store->add($this->vectorizer->vectorize($chunk, $options['platform_options'] ?? []));
$chunk = [];
}
}
if ([] !== $chunk) {
$this->store->add(...$this->vectorizer->vectorize($chunk, $options['platform_options'] ?? []));
$this->store->add($this->vectorizer->vectorize($chunk, $options['platform_options'] ?? []));
}
$this->logger->debug('Document processing completed', ['total_documents' => $counter]);

View File

@@ -19,7 +19,10 @@ use Symfony\AI\Store\Document\VectorDocument;
*/
interface StoreInterface
{
public function add(VectorDocument ...$documents): void;
/**
* @param VectorDocument|VectorDocument[] $documents
*/
public function add(VectorDocument|array $documents): void;
/**
* @param array<string, mixed> $options

View File

@@ -24,8 +24,12 @@ final class TestStore implements StoreInterface
public int $addCalls = 0;
public function add(VectorDocument ...$documents): void
public function add(VectorDocument|array $documents): void
{
if ($documents instanceof VectorDocument) {
$documents = [$documents];
}
++$this->addCalls;
$this->documents = array_merge($this->documents, $documents);
}

View File

@@ -34,11 +34,11 @@ final class StoreTest extends TestCase
public function testStoreCanDrop()
{
$store = new Store();
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6])));
$this->assertCount(3, $result);
@@ -52,21 +52,21 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingCosineDistance()
{
$store = new Store();
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6])));
$this->assertCount(3, $result);
$this->assertSame([0.1, 0.1, 0.5], $result[0]->vector->getData());
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6])));
$this->assertCount(6, $result);
@@ -76,13 +76,13 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingCosineDistanceAndReturnCorrectOrder()
{
$store = new Store();
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.1, 0.6])),
new VectorDocument(Uuid::v4(), new Vector([0.0, 0.1, 0.6])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6])));
$this->assertCount(5, $result);
@@ -96,11 +96,11 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingCosineDistanceWithMaxItems()
{
$store = new Store();
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1])),
);
]);
$this->assertCount(1, iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6]), [
'maxItems' => 1,
@@ -110,10 +110,10 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingAngularDistance()
{
$store = new Store(new DistanceCalculator(DistanceStrategy::ANGULAR_DISTANCE));
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([1.0, 2.0, 3.0])),
new VectorDocument(Uuid::v4(), new Vector([1.0, 5.0, 7.0])),
);
]);
$result = iterator_to_array($store->query(new Vector([1.2, 2.3, 3.4])));
@@ -124,10 +124,10 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingEuclideanDistance()
{
$store = new Store(new DistanceCalculator(DistanceStrategy::EUCLIDEAN_DISTANCE));
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([1.0, 5.0, 7.0])),
new VectorDocument(Uuid::v4(), new Vector([1.0, 2.0, 3.0])),
);
]);
$result = iterator_to_array($store->query(new Vector([1.2, 2.3, 3.4])));
@@ -138,10 +138,10 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingManhattanDistance()
{
$store = new Store(new DistanceCalculator(DistanceStrategy::MANHATTAN_DISTANCE));
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([1.0, 2.0, 3.0])),
new VectorDocument(Uuid::v4(), new Vector([1.0, 5.0, 7.0])),
);
]);
$result = iterator_to_array($store->query(new Vector([1.2, 2.3, 3.4])));
@@ -152,10 +152,10 @@ final class StoreTest extends TestCase
public function testStoreCanSearchUsingChebyshevDistance()
{
$store = new Store(new DistanceCalculator(DistanceStrategy::CHEBYSHEV_DISTANCE));
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([1.0, 2.0, 3.0])),
new VectorDocument(Uuid::v4(), new Vector([1.0, 5.0, 7.0])),
);
]);
$result = iterator_to_array($store->query(new Vector([1.2, 2.3, 3.4])));
@@ -166,11 +166,11 @@ final class StoreTest extends TestCase
public function testStoreCanSearchWithFilter()
{
$store = new Store();
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5]), new Metadata(['category' => 'products', 'enabled' => true])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0]), new Metadata(['category' => 'articles', 'enabled' => true])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1]), new Metadata(['category' => 'products', 'enabled' => false])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6]), [
'filter' => fn (VectorDocument $doc) => 'products' === $doc->metadata['category'],
@@ -184,12 +184,12 @@ final class StoreTest extends TestCase
public function testStoreCanSearchWithFilterAndMaxItems()
{
$store = new Store();
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5]), new Metadata(['category' => 'products'])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0]), new Metadata(['category' => 'articles'])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1]), new Metadata(['category' => 'products'])),
new VectorDocument(Uuid::v4(), new Vector([0.0, 0.1, 0.6]), new Metadata(['category' => 'products'])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6]), [
'filter' => fn (VectorDocument $doc) => 'products' === $doc->metadata['category'],
@@ -204,11 +204,11 @@ final class StoreTest extends TestCase
public function testStoreCanSearchWithComplexFilter()
{
$store = new Store();
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5]), new Metadata(['price' => 100, 'stock' => 5])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0]), new Metadata(['price' => 200, 'stock' => 0])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1]), new Metadata(['price' => 50, 'stock' => 10])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6]), [
'filter' => fn (VectorDocument $doc) => $doc->metadata['price'] <= 150 && $doc->metadata['stock'] > 0,
@@ -220,11 +220,11 @@ final class StoreTest extends TestCase
public function testStoreCanSearchWithNestedMetadataFilter()
{
$store = new Store();
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5]), new Metadata(['options' => ['size' => 'S', 'color' => 'blue']])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0]), new Metadata(['options' => ['size' => 'M', 'color' => 'blue']])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1]), new Metadata(['options' => ['size' => 'S', 'color' => 'red']])),
);
]);
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6]), [
'filter' => fn (VectorDocument $doc) => 'S' === $doc->metadata['options']['size'],
@@ -238,11 +238,11 @@ final class StoreTest extends TestCase
public function testStoreCanSearchWithInArrayFilter()
{
$store = new Store();
$store->add(
$store->add([
new VectorDocument(Uuid::v4(), new Vector([0.1, 0.1, 0.5]), new Metadata(['brand' => 'Nike'])),
new VectorDocument(Uuid::v4(), new Vector([0.7, -0.3, 0.0]), new Metadata(['brand' => 'Adidas'])),
new VectorDocument(Uuid::v4(), new Vector([0.3, 0.7, 0.1]), new Metadata(['brand' => 'Generic'])),
);
]);
$allowedBrands = ['Nike', 'Adidas', 'Puma'];
$result = iterator_to_array($store->query(new Vector([0.0, 0.1, 0.6]), [

View File

@@ -41,7 +41,7 @@ final class RetrieverTest extends TestCase
);
$store = new TestStore();
$store->add($document1, $document2);
$store->add([$document1, $document2]);
$queryVector = new Vector([0.2, 0.3, 0.4]);
$vectorizer = new Vectorizer(