diff --git a/src/ai-bundle/config/options.php b/src/ai-bundle/config/options.php index 46f24c4c1..0bef84431 100644 --- a/src/ai-bundle/config/options.php +++ b/src/ai-bundle/config/options.php @@ -577,13 +577,14 @@ ->children() ->stringNode('account_id')->cannotBeEmpty()->end() ->stringNode('api_key')->cannotBeEmpty()->end() - ->stringNode('index_name')->cannotBeEmpty()->end() - ->integerNode('dimensions')->isRequired()->end() + ->stringNode('index_name')->end() + ->integerNode('dimensions') + ->defaultValue(1536) + ->end() ->stringNode('metric') - ->cannotBeEmpty() ->defaultValue('cosine') ->end() - ->stringNode('endpoint_url')->end() + ->stringNode('endpoint')->end() ->end() ->end() ->end() @@ -592,11 +593,19 @@ ->arrayPrototype() ->children() ->stringNode('endpoint')->cannotBeEmpty()->end() - ->stringNode('table')->cannotBeEmpty()->end() - ->stringNode('field')->isRequired()->end() - ->stringNode('type')->isRequired()->end() - ->stringNode('similarity')->isRequired()->end() - ->integerNode('dimensions')->isRequired()->end() + ->stringNode('table')->end() + ->stringNode('field') + ->defaultValue('_vectors') + ->end() + ->stringNode('type') + ->defaultValue('hnsw') + ->end() + ->stringNode('similarity') + ->defaultValue('cosine') + ->end() + ->integerNode('dimensions') + ->defaultValue(1536) + ->end() ->stringNode('quantization')->end() ->end() ->end() @@ -606,7 +615,7 @@ ->arrayPrototype() ->children() ->stringNode('connection')->cannotBeEmpty()->end() - ->stringNode('table_name')->cannotBeEmpty()->end() + ->stringNode('table_name')->end() ->stringNode('index_name')->cannotBeEmpty()->end() ->stringNode('vector_field_name')->cannotBeEmpty()->end() ->arrayNode('setup_options') @@ -623,10 +632,16 @@ ->children() ->stringNode('endpoint')->cannotBeEmpty()->end() ->stringNode('api_key')->cannotBeEmpty()->end() - ->stringNode('index_name')->cannotBeEmpty()->end() - ->stringNode('embedder')->isRequired()->end() - ->stringNode('vector_field')->isRequired()->end() - ->integerNode('dimensions')->isRequired()->end() + ->stringNode('index_name')->end() + ->stringNode('embedder') + ->defaultValue('default') + ->end() + ->stringNode('vector_field') + ->defaultValue('_vectors') + ->end() + ->integerNode('dimensions') + ->defaultValue(1536) + ->end() ->floatNode('semantic_ratio') ->info('The ratio between semantic (vector) and full-text search (0.0 to 1.0). Default: 1.0 (100% semantic)') ->defaultValue(1.0) @@ -650,11 +665,17 @@ ->children() ->stringNode('endpoint')->cannotBeEmpty()->end() ->stringNode('api_key')->isRequired()->end() - ->stringNode('database')->isRequired()->end() + ->stringNode('database')->end() ->stringNode('collection')->isRequired()->end() - ->stringNode('vector_field')->isRequired()->end() - ->integerNode('dimensions')->isRequired()->end() - ->stringNode('metric_type')->end() + ->stringNode('vector_field') + ->defaultValue('_vectors') + ->end() + ->integerNode('dimensions') + ->defaultValue(1536) + ->end() + ->stringNode('metric_type') + ->defaultValue('COSINE') + ->end() ->end() ->end() ->end() @@ -667,10 +688,14 @@ ->defaultValue(MongoDbClient::class) ->end() ->stringNode('database')->isRequired()->end() - ->stringNode('collection')->isRequired()->end() + ->stringNode('collection')->end() ->stringNode('index_name')->isRequired()->end() - ->stringNode('vector_field')->isRequired()->end() - ->booleanNode('bulk_write')->end() + ->stringNode('vector_field') + ->defaultValue('vector') + ->end() + ->booleanNode('bulk_write') + ->defaultValue(false) + ->end() ->end() ->end() ->end() @@ -681,12 +706,18 @@ ->stringNode('endpoint')->cannotBeEmpty()->end() ->stringNode('username')->cannotBeEmpty()->end() ->stringNode('password')->cannotBeEmpty()->end() - ->stringNode('database')->cannotBeEmpty()->end() + ->stringNode('database')->end() ->stringNode('vector_index_name')->cannotBeEmpty()->end() ->stringNode('node_name')->cannotBeEmpty()->end() - ->stringNode('vector_field')->isRequired()->end() - ->integerNode('dimensions')->isRequired()->end() - ->stringNode('distance')->isRequired()->end() + ->stringNode('vector_field') + ->defaultValue('embeddings') + ->end() + ->integerNode('dimensions') + ->defaultValue(1536) + ->end() + ->stringNode('distance') + ->defaultValue('cosine') + ->end() ->booleanNode('quantization')->end() ->end() ->end() @@ -716,8 +747,10 @@ ->stringNode('dsn')->cannotBeEmpty()->end() ->stringNode('username')->end() ->stringNode('password')->end() - ->stringNode('table_name')->isRequired()->end() - ->stringNode('vector_field')->isRequired()->end() + ->stringNode('table_name')->end() + ->stringNode('vector_field') + ->defaultValue('embedding') + ->end() ->enumNode('distance') ->info('Distance metric to use for vector similarity search') ->enumFqcn(PostgresDistance::class) @@ -741,9 +774,13 @@ ->children() ->stringNode('endpoint')->cannotBeEmpty()->end() ->stringNode('api_key')->cannotBeEmpty()->end() - ->stringNode('collection_name')->cannotBeEmpty()->end() - ->integerNode('dimensions')->isRequired()->end() - ->stringNode('distance')->isRequired()->end() + ->stringNode('collection_name')->end() + ->integerNode('dimensions') + ->defaultValue(1536) + ->end() + ->stringNode('distance') + ->defaultValue('Cosine') + ->end() ->booleanNode('async')->end() ->end() ->end() @@ -760,8 +797,10 @@ ->info('a service id of a Redis client') ->cannotBeEmpty() ->end() - ->stringNode('index_name')->isRequired()->cannotBeEmpty()->end() - ->stringNode('key_prefix')->defaultValue('vector:')->end() + ->stringNode('index_name')->end() + ->stringNode('key_prefix') + ->defaultValue('vector:') + ->end() ->enumNode('distance') ->info('Distance metric to use for vector similarity search') ->values(Distance::cases()) @@ -790,9 +829,15 @@ ->stringNode('url')->isRequired()->cannotBeEmpty()->end() ->stringNode('api_key')->isRequired()->cannotBeEmpty()->end() ->stringNode('table')->end() - ->stringNode('vector_field')->end() - ->integerNode('vector_dimension')->end() - ->stringNode('function_name')->end() + ->stringNode('vector_field') + ->defaultValue('embedding') + ->end() + ->integerNode('vector_dimension') + ->defaultValue(1536) + ->end() + ->stringNode('function_name') + ->defaultValue('match_documents') + ->end() ->end() ->end() ->end() @@ -805,10 +850,16 @@ ->stringNode('password')->cannotBeEmpty()->end() ->stringNode('namespace')->cannotBeEmpty()->end() ->stringNode('database')->cannotBeEmpty()->end() - ->stringNode('table')->isRequired()->end() - ->stringNode('vector_field')->isRequired()->end() - ->stringNode('strategy')->isRequired()->end() - ->integerNode('dimensions')->isRequired()->end() + ->stringNode('table')->end() + ->stringNode('vector_field') + ->defaultValue('_vectors') + ->end() + ->stringNode('strategy') + ->defaultValue('cosine') + ->end() + ->integerNode('dimensions') + ->defaultValue(1536) + ->end() ->booleanNode('namespaced_user')->end() ->end() ->end() @@ -819,9 +870,13 @@ ->children() ->stringNode('endpoint')->cannotBeEmpty()->end() ->stringNode('api_key')->isRequired()->end() - ->stringNode('collection')->isRequired()->end() - ->stringNode('vector_field')->isRequired()->end() - ->integerNode('dimensions')->isRequired()->end() + ->stringNode('collection')->end() + ->stringNode('vector_field') + ->defaultValue('_vectors') + ->end() + ->integerNode('dimensions') + ->defaultValue(1536) + ->end() ->end() ->end() ->end() @@ -831,7 +886,7 @@ ->children() ->stringNode('endpoint')->cannotBeEmpty()->end() ->stringNode('api_key')->isRequired()->end() - ->stringNode('collection')->isRequired()->end() + ->stringNode('collection')->end() ->end() ->end() ->end() diff --git a/src/ai-bundle/src/AiBundle.php b/src/ai-bundle/src/AiBundle.php index 037baf8fc..a93d7977e 100644 --- a/src/ai-bundle/src/AiBundle.php +++ b/src/ai-bundle/src/AiBundle.php @@ -1049,27 +1049,25 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde if ('cloudflare' === $type) { foreach ($stores as $name => $store) { - $arguments = [ - new Reference('http_client'), - $store['account_id'], - $store['api_key'], - $store['index_name'], - $store['dimensions'], - $store['metric'], - ]; - - if (\array_key_exists('endpoint_url', $store)) { - $arguments[6] = $store['endpoint_url']; - } - $definition = new Definition(CloudflareStore::class); $definition ->setLazy(true) - ->setArguments($arguments) + ->setArguments([ + new Reference('http_client'), + $store['account_id'], + $store['api_key'], + $store['index_name'] ?? $name, + $store['dimensions'], + $store['metric'], + ]) ->addTag('proxy', ['interface' => StoreInterface::class]) ->addTag('proxy', ['interface' => ManagedStoreInterface::class]) ->addTag('ai.store'); + if (\array_key_exists('endpoint', $store)) { + $definition->setArgument(6, $store['endpoint']); + } + $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $type.'_'.$name); @@ -1078,28 +1076,26 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde if ('manticore' === $type) { foreach ($stores as $name => $store) { - $arguments = [ - new Reference('http_client'), - $store['endpoint'], - $store['table'], - $store['field'], - $store['type'], - $store['similarity'], - $store['dimensions'], - ]; - - if (\array_key_exists('quantization', $store)) { - $arguments[7] = $store['quantization']; - } - $definition = new Definition(ManticoreStore::class); $definition ->setLazy(true) - ->setArguments($arguments) + ->setArguments([ + new Reference('http_client'), + $store['endpoint'], + $store['table'] ?? $name, + $store['field'], + $store['type'], + $store['similarity'], + $store['dimensions'], + ]) ->addTag('proxy', ['interface' => StoreInterface::class]) ->addTag('proxy', ['interface' => ManagedStoreInterface::class]) ->addTag('ai.store'); + if (\array_key_exists('quantization', $store)) { + $definition->setArgument(7, $store['quantization']); + } + $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $type.'_'.$name); @@ -1114,7 +1110,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde ->setFactory([MariaDbStore::class, 'fromDbal']) ->setArguments([ new Reference(\sprintf('doctrine.dbal.%s_connection', $store['connection'])), - $store['table_name'], + $store['table_name'] ?? $name, $store['index_name'], $store['vector_field_name'], ]) @@ -1132,24 +1128,19 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde if ('meilisearch' === $type) { foreach ($stores as $name => $store) { - $arguments = [ - new Reference('http_client'), - $store['endpoint'], - $store['api_key'], - $store['index_name'], - $store['embedder'], - $store['vector_field'], - $store['dimensions'], - ]; - - if (\array_key_exists('semantic_ratio', $store)) { - $arguments[7] = $store['semantic_ratio']; - } - $definition = new Definition(MeilisearchStore::class); $definition ->setLazy(true) - ->setArguments($arguments) + ->setArguments([ + new Reference('http_client'), + $store['endpoint'], + $store['api_key'], + $store['index_name'] ?? $name, + $store['embedder'], + $store['vector_field'], + $store['dimensions'], + $store['semantic_ratio'], + ]) ->addTag('proxy', ['interface' => StoreInterface::class]) ->addTag('proxy', ['interface' => ManagedStoreInterface::class]) ->addTag('ai.store'); @@ -1196,7 +1187,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde new Reference('http_client'), $store['endpoint'], $store['api_key'], - $store['database'], + $store['database'] ?? $name, $store['collection'], $store['vector_field'], $store['dimensions'], @@ -1225,7 +1216,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $arguments = [ new Reference($store['client']), $store['database'], - $store['collection'], + $store['collection'] ?? $name, $store['index_name'], $store['vector_field'], ]; @@ -1255,7 +1246,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $store['endpoint'], $store['username'], $store['password'], - $store['database'], + $store['database'] ?? $name, $store['vector_index_name'], $store['node_name'], $store['vector_field'], @@ -1285,7 +1276,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde foreach ($stores as $name => $store) { $arguments = [ new Reference($store['client']), - $store['namespace'], + $store['namespace'] ?? $name, $store['filter'], ]; @@ -1314,7 +1305,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition->setFactory([PostgresStore::class, 'fromDbal']); $arguments = [ new Reference($store['dbal_connection']), - $store['table_name'], + $store['table_name'] ?? $name, $store['vector_field'], ]; } else { @@ -1327,7 +1318,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $arguments = [ $pdo, - $store['table_name'], + $store['table_name'] ?? $name, $store['vector_field'], ]; } @@ -1355,7 +1346,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde new Reference('http_client'), $store['endpoint'], $store['api_key'], - $store['collection_name'], + $store['collection_name'] ?? $name, $store['dimensions'], $store['distance'], ]; @@ -1392,7 +1383,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde ->setLazy(true) ->setArguments([ $redisClient, - $store['index_name'], + $store['index_name'] ?? $name, $store['key_prefix'], $store['distance'], ]) @@ -1476,7 +1467,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde new Reference('http_client'), $store['endpoint'], $store['api_key'], - $store['collection'], + $store['collection'] ?? $name, $store['vector_field'], $store['dimensions'], ]) @@ -1499,7 +1490,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde new Reference('http_client'), $store['endpoint'], $store['api_key'], - $store['collection'], + $store['collection'] ?? $name, ]) ->addTag('proxy', ['interface' => StoreInterface::class]) ->addTag('proxy', ['interface' => ManagedStoreInterface::class]) diff --git a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php index 7c49ac348..9ee2df84c 100644 --- a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php +++ b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php @@ -848,8 +848,6 @@ public function testCloudflareStoreCanBeConfigured() 'account_id' => 'foo', 'api_key' => 'bar', 'index_name' => 'random', - 'dimensions' => 1536, - 'metric' => 'cosine', ], ], ], @@ -884,7 +882,7 @@ public function testCloudflareStoreCanBeConfigured() $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); } - public function testCloudflareStoreWithCustomEndpointCanBeConfigured() + public function testCloudflareStoreWithCustomIndexCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ @@ -894,9 +892,96 @@ public function testCloudflareStoreWithCustomEndpointCanBeConfigured() 'account_id' => 'foo', 'api_key' => 'bar', 'index_name' => 'random', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.cloudflare.my_cloudflare_store')); + + $definition = $container->getDefinition('ai.store.cloudflare.my_cloudflare_store'); + $this->assertSame(CloudflareStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(6, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('foo', $definition->getArgument(1)); + $this->assertSame('bar', $definition->getArgument(2)); + $this->assertSame('random', $definition->getArgument(3)); + $this->assertSame(1536, $definition->getArgument(4)); + $this->assertSame('cosine', $definition->getArgument(5)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_cloudflare_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myCloudflareStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $cloudflareMyCloudflareStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testCloudflareStoreWithCustomDimensionsCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'cloudflare' => [ + 'my_cloudflare_store' => [ + 'account_id' => 'foo', + 'api_key' => 'bar', + 'dimensions' => 768, + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.cloudflare.my_cloudflare_store')); + + $definition = $container->getDefinition('ai.store.cloudflare.my_cloudflare_store'); + $this->assertSame(CloudflareStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(6, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('foo', $definition->getArgument(1)); + $this->assertSame('bar', $definition->getArgument(2)); + $this->assertSame('my_cloudflare_store', $definition->getArgument(3)); + $this->assertSame(768, $definition->getArgument(4)); + $this->assertSame('cosine', $definition->getArgument(5)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_cloudflare_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myCloudflareStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $cloudflareMyCloudflareStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testCloudflareStoreWithCustomEndpointCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'cloudflare' => [ + 'my_cloudflare_store' => [ + 'account_id' => 'foo', + 'api_key' => 'bar', 'dimensions' => 1536, 'metric' => 'cosine', - 'endpoint_url' => 'https://api.cloudflare.com/client/v5/accounts', + 'endpoint' => 'https://api.cloudflare.com/client/v5/accounts', ], ], ], @@ -914,7 +999,7 @@ public function testCloudflareStoreWithCustomEndpointCanBeConfigured() $this->assertSame('http_client', (string) $definition->getArgument(0)); $this->assertSame('foo', $definition->getArgument(1)); $this->assertSame('bar', $definition->getArgument(2)); - $this->assertSame('random', $definition->getArgument(3)); + $this->assertSame('my_cloudflare_store', $definition->getArgument(3)); $this->assertSame(1536, $definition->getArgument(4)); $this->assertSame('cosine', $definition->getArgument(5)); $this->assertSame('https://api.cloudflare.com/client/v5/accounts', $definition->getArgument(6)); @@ -933,6 +1018,49 @@ public function testCloudflareStoreWithCustomEndpointCanBeConfigured() } public function testManticoreStoreCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'manticore' => [ + 'my_manticore_store' => [ + 'endpoint' => 'http://127.0.0.1:9306', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.manticore.my_manticore_store')); + + $definition = $container->getDefinition('ai.store.manticore.my_manticore_store'); + $this->assertSame(ManticoreStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(7, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('http://127.0.0.1:9306', $definition->getArgument(1)); + $this->assertSame('my_manticore_store', $definition->getArgument(2)); + $this->assertSame('_vectors', $definition->getArgument(3)); + $this->assertSame('hnsw', $definition->getArgument(4)); + $this->assertSame('cosine', $definition->getArgument(5)); + $this->assertSame(1536, $definition->getArgument(6)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_manticore_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myManticoreStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $manticoreMyManticoreStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testManticoreStoreWithCustomTableCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ @@ -941,10 +1069,6 @@ public function testManticoreStoreCanBeConfigured() 'my_manticore_store' => [ 'endpoint' => 'http://127.0.0.1:9306', 'table' => 'test', - 'field' => 'foo_vector', - 'type' => 'hnsw', - 'similarity' => 'cosine', - 'dimensions' => 768, ], ], ], @@ -962,7 +1086,95 @@ public function testManticoreStoreCanBeConfigured() $this->assertSame('http_client', (string) $definition->getArgument(0)); $this->assertSame('http://127.0.0.1:9306', $definition->getArgument(1)); $this->assertSame('test', $definition->getArgument(2)); - $this->assertSame('foo_vector', $definition->getArgument(3)); + $this->assertSame('_vectors', $definition->getArgument(3)); + $this->assertSame('hnsw', $definition->getArgument(4)); + $this->assertSame('cosine', $definition->getArgument(5)); + $this->assertSame(1536, $definition->getArgument(6)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_manticore_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myManticoreStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $manticoreMyManticoreStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testManticoreStoreWithCustomFieldCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'manticore' => [ + 'my_manticore_store' => [ + 'endpoint' => 'http://127.0.0.1:9306', + 'field' => '_foo', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.manticore.my_manticore_store')); + + $definition = $container->getDefinition('ai.store.manticore.my_manticore_store'); + $this->assertSame(ManticoreStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(7, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('http://127.0.0.1:9306', $definition->getArgument(1)); + $this->assertSame('my_manticore_store', $definition->getArgument(2)); + $this->assertSame('_foo', $definition->getArgument(3)); + $this->assertSame('hnsw', $definition->getArgument(4)); + $this->assertSame('cosine', $definition->getArgument(5)); + $this->assertSame(1536, $definition->getArgument(6)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_manticore_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myManticoreStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $manticoreMyManticoreStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testManticoreStoreWithCustomDimensionsCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'manticore' => [ + 'my_manticore_store' => [ + 'endpoint' => 'http://127.0.0.1:9306', + 'dimensions' => 768, + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.manticore.my_manticore_store')); + + $definition = $container->getDefinition('ai.store.manticore.my_manticore_store'); + $this->assertSame(ManticoreStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(7, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('http://127.0.0.1:9306', $definition->getArgument(1)); + $this->assertSame('my_manticore_store', $definition->getArgument(2)); + $this->assertSame('_vectors', $definition->getArgument(3)); $this->assertSame('hnsw', $definition->getArgument(4)); $this->assertSame('cosine', $definition->getArgument(5)); $this->assertSame(768, $definition->getArgument(6)); @@ -988,7 +1200,6 @@ public function testManticoreStoreWithQuantizationCanBeConfigured() 'manticore' => [ 'my_manticore_store' => [ 'endpoint' => 'http://127.0.0.1:9306', - 'table' => 'test', 'field' => 'foo_vector', 'type' => 'hnsw', 'similarity' => 'cosine', @@ -1010,7 +1221,7 @@ public function testManticoreStoreWithQuantizationCanBeConfigured() $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); $this->assertSame('http_client', (string) $definition->getArgument(0)); $this->assertSame('http://127.0.0.1:9306', $definition->getArgument(1)); - $this->assertSame('test', $definition->getArgument(2)); + $this->assertSame('my_manticore_store', $definition->getArgument(2)); $this->assertSame('foo_vector', $definition->getArgument(3)); $this->assertSame('hnsw', $definition->getArgument(4)); $this->assertSame('cosine', $definition->getArgument(5)); @@ -1031,6 +1242,51 @@ public function testManticoreStoreWithQuantizationCanBeConfigured() } public function testMariaDbStoreCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'mariadb' => [ + 'my_mariadb_store' => [ + 'connection' => 'default', + 'index_name' => 'vector_idx', + 'vector_field_name' => 'vector', + 'setup_options' => [ + 'dimensions' => 1024, + ], + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.mariadb.my_mariadb_store')); + + $definition = $container->getDefinition('ai.store.mariadb.my_mariadb_store'); + $this->assertSame(MariaDbStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(4, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('doctrine.dbal.default_connection', (string) $definition->getArgument(0)); + $this->assertSame('my_mariadb_store', $definition->getArgument(1)); + $this->assertSame('vector_idx', $definition->getArgument(2)); + $this->assertSame('vector', $definition->getArgument(3)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_mariadb_store')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $mariadb_my_mariadb_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $mariadbMyMariadbStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testMariaDbStoreWithCustomTableCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ @@ -1077,6 +1333,52 @@ public function testMariaDbStoreCanBeConfigured() } public function testMeilisearchStoreCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'meilisearch' => [ + 'custom' => [ + 'endpoint' => 'http://127.0.0.1:7700', + 'api_key' => 'foo', + 'embedder' => 'default', + 'vector_field' => '_vectors', + 'dimensions' => 768, + ], + ], + ], + ], + ]); + + $definition = $container->getDefinition('ai.store.meilisearch.custom'); + $this->assertSame(MeilisearchStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(8, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('http://127.0.0.1:7700', $definition->getArgument(1)); + $this->assertSame('foo', $definition->getArgument(2)); + $this->assertSame('custom', $definition->getArgument(3)); + $this->assertSame('default', $definition->getArgument(4)); + $this->assertSame('_vectors', $definition->getArgument(5)); + $this->assertSame(768, $definition->getArgument(6)); + $this->assertSame(1.0, $definition->getArgument(7)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $custom')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $meilisearch_custom')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $meilisearchCustom')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testMeilisearchStoreWithCustomIndexNameCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ @@ -1248,6 +1550,53 @@ public function testInMemoryStoreWithCustomStrategyCanBeConfigured() } public function testMilvusStoreCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'milvus' => [ + 'my_milvus_store' => [ + 'endpoint' => 'http://127.0.0.1:19530', + 'api_key' => 'foo', + 'collection' => 'default', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.milvus.my_milvus_store')); + + $definition = $container->getDefinition('ai.store.milvus.my_milvus_store'); + $this->assertSame(MilvusStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(8, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('http://127.0.0.1:19530', $definition->getArgument(1)); + $this->assertSame('foo', $definition->getArgument(2)); + $this->assertSame('my_milvus_store', $definition->getArgument(3)); + $this->assertSame('default', $definition->getArgument(4)); + $this->assertSame('_vectors', $definition->getArgument(5)); + $this->assertSame(1536, $definition->getArgument(6)); + $this->assertSame('COSINE', $definition->getArgument(7)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_milvus_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myMilvusStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $milvus_my_milvus_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $milvusMyMilvusStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testMilvusStoreWithCustomDatabaseCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ @@ -1258,8 +1607,6 @@ public function testMilvusStoreCanBeConfigured() 'api_key' => 'foo', 'database' => 'test', 'collection' => 'default', - 'vector_field' => '_vectors', - 'dimensions' => 768, ], ], ], @@ -1272,15 +1619,108 @@ public function testMilvusStoreCanBeConfigured() $this->assertSame(MilvusStore::class, $definition->getClass()); $this->assertTrue($definition->isLazy()); - $this->assertCount(7, $definition->getArguments()); + $this->assertCount(8, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('http://127.0.0.1:19530', $definition->getArgument(1)); + $this->assertSame('foo', $definition->getArgument(2)); + $this->assertSame('test', $definition->getArgument(3)); + $this->assertSame('default', $definition->getArgument(4)); + $this->assertSame('_vectors', $definition->getArgument(5)); + $this->assertSame(1536, $definition->getArgument(6)); + $this->assertSame('COSINE', $definition->getArgument(7)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_milvus_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myMilvusStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $milvus_my_milvus_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $milvusMyMilvusStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testMilvusStoreWithCustomMetricsCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'milvus' => [ + 'my_milvus_store' => [ + 'endpoint' => 'http://127.0.0.1:19530', + 'api_key' => 'foo', + 'collection' => 'default', + 'metric_type' => 'COSINE', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.milvus.my_milvus_store')); + + $definition = $container->getDefinition('ai.store.milvus.my_milvus_store'); + $this->assertSame(MilvusStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(8, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('http://127.0.0.1:19530', $definition->getArgument(1)); + $this->assertSame('foo', $definition->getArgument(2)); + $this->assertSame('my_milvus_store', $definition->getArgument(3)); + $this->assertSame('default', $definition->getArgument(4)); + $this->assertSame('_vectors', $definition->getArgument(5)); + $this->assertSame(1536, $definition->getArgument(6)); + $this->assertSame('COSINE', $definition->getArgument(7)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_milvus_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myMilvusStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $milvus_my_milvus_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $milvusMyMilvusStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testMongoDbStoreCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'mongodb' => [ + 'my_mongo_store' => [ + 'database' => 'my_db', + 'index_name' => 'vector_index', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.mongodb.my_mongo_store')); + + $definition = $container->getDefinition('ai.store.mongodb.my_mongo_store'); + $this->assertSame(MongoDbStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(6, $definition->getArguments()); $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); - $this->assertSame('http_client', (string) $definition->getArgument(0)); - $this->assertSame('http://127.0.0.1:19530', $definition->getArgument(1)); - $this->assertSame('foo', $definition->getArgument(2)); - $this->assertSame('test', $definition->getArgument(3)); - $this->assertSame('default', $definition->getArgument(4)); - $this->assertSame('_vectors', $definition->getArgument(5)); - $this->assertSame(768, $definition->getArgument(6)); + $this->assertSame(MongoDbClient::class, (string) $definition->getArgument(0)); + $this->assertSame('my_db', $definition->getArgument(1)); + $this->assertSame('my_mongo_store', $definition->getArgument(2)); + $this->assertSame('vector_index', $definition->getArgument(3)); + $this->assertSame('vector', $definition->getArgument(4)); + $this->assertFalse($definition->getArgument(5)); $this->assertTrue($definition->hasTag('proxy')); $this->assertSame([ @@ -1289,49 +1729,43 @@ public function testMilvusStoreCanBeConfigured() ], $definition->getTag('proxy')); $this->assertTrue($definition->hasTag('ai.store')); - $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_milvus_store')); - $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myMilvusStore')); - $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $milvus_my_milvus_store')); - $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $milvusMyMilvusStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_mongo_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myMongoStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $mongodb_my_mongo_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $mongodbMyMongoStore')); $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); } - public function testMilvusStoreWithCustomMetricsCanBeConfigured() + public function testMongoDbStoreWithCustomCollectionCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ 'store' => [ - 'milvus' => [ - 'my_milvus_store' => [ - 'endpoint' => 'http://127.0.0.1:19530', - 'api_key' => 'foo', - 'database' => 'test', - 'collection' => 'default', - 'vector_field' => '_vectors', - 'dimensions' => 768, - 'metric_type' => 'COSINE', + 'mongodb' => [ + 'my_mongo_store' => [ + 'database' => 'my_db', + 'collection' => 'my_collection', + 'index_name' => 'vector_index', ], ], ], ], ]); - $this->assertTrue($container->hasDefinition('ai.store.milvus.my_milvus_store')); + $this->assertTrue($container->hasDefinition('ai.store.mongodb.my_mongo_store')); - $definition = $container->getDefinition('ai.store.milvus.my_milvus_store'); - $this->assertSame(MilvusStore::class, $definition->getClass()); + $definition = $container->getDefinition('ai.store.mongodb.my_mongo_store'); + $this->assertSame(MongoDbStore::class, $definition->getClass()); $this->assertTrue($definition->isLazy()); - $this->assertCount(8, $definition->getArguments()); + $this->assertCount(6, $definition->getArguments()); $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); - $this->assertSame('http_client', (string) $definition->getArgument(0)); - $this->assertSame('http://127.0.0.1:19530', $definition->getArgument(1)); - $this->assertSame('foo', $definition->getArgument(2)); - $this->assertSame('test', $definition->getArgument(3)); - $this->assertSame('default', $definition->getArgument(4)); - $this->assertSame('_vectors', $definition->getArgument(5)); - $this->assertSame(768, $definition->getArgument(6)); - $this->assertSame('COSINE', $definition->getArgument(7)); + $this->assertSame(MongoDbClient::class, (string) $definition->getArgument(0)); + $this->assertSame('my_db', $definition->getArgument(1)); + $this->assertSame('my_collection', $definition->getArgument(2)); + $this->assertSame('vector_index', $definition->getArgument(3)); + $this->assertSame('vector', $definition->getArgument(4)); + $this->assertFalse($definition->getArgument(5)); $this->assertTrue($definition->hasTag('proxy')); $this->assertSame([ @@ -1340,14 +1774,14 @@ public function testMilvusStoreWithCustomMetricsCanBeConfigured() ], $definition->getTag('proxy')); $this->assertTrue($definition->hasTag('ai.store')); - $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_milvus_store')); - $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myMilvusStore')); - $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $milvus_my_milvus_store')); - $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $milvusMyMilvusStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_mongo_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myMongoStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $mongodb_my_mongo_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $mongodbMyMongoStore')); $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); } - public function testMongoDbStoreCanBeConfigured() + public function testMongoDbStoreWithCustomVectorFieldCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ @@ -1355,9 +1789,8 @@ public function testMongoDbStoreCanBeConfigured() 'mongodb' => [ 'my_mongo_store' => [ 'database' => 'my_db', - 'collection' => 'my_collection', - 'index_name' => 'vector_index', - 'vector_field' => 'embedding', + 'index_name' => 'foo', + 'vector_field' => 'random', ], ], ], @@ -1370,13 +1803,14 @@ public function testMongoDbStoreCanBeConfigured() $this->assertSame(MongoDbStore::class, $definition->getClass()); $this->assertTrue($definition->isLazy()); - $this->assertCount(5, $definition->getArguments()); + $this->assertCount(6, $definition->getArguments()); $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); $this->assertSame(MongoDbClient::class, (string) $definition->getArgument(0)); $this->assertSame('my_db', $definition->getArgument(1)); - $this->assertSame('my_collection', $definition->getArgument(2)); - $this->assertSame('vector_index', $definition->getArgument(3)); - $this->assertSame('embedding', $definition->getArgument(4)); + $this->assertSame('my_mongo_store', $definition->getArgument(2)); + $this->assertSame('foo', $definition->getArgument(3)); + $this->assertSame('random', $definition->getArgument(4)); + $this->assertFalse($definition->getArgument(5)); $this->assertTrue($definition->hasTag('proxy')); $this->assertSame([ @@ -1401,9 +1835,7 @@ public function testMongoDbStoreWithCustomClientCanBeConfigured() 'my_mongo_store' => [ 'client' => 'foo', 'database' => 'my_db', - 'collection' => 'my_collection', 'index_name' => 'vector_index', - 'vector_field' => 'embedding', ], ], ], @@ -1416,13 +1848,14 @@ public function testMongoDbStoreWithCustomClientCanBeConfigured() $this->assertSame(MongoDbStore::class, $definition->getClass()); $this->assertTrue($definition->isLazy()); - $this->assertCount(5, $definition->getArguments()); + $this->assertCount(6, $definition->getArguments()); $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); $this->assertSame('foo', (string) $definition->getArgument(0)); $this->assertSame('my_db', $definition->getArgument(1)); - $this->assertSame('my_collection', $definition->getArgument(2)); + $this->assertSame('my_mongo_store', $definition->getArgument(2)); $this->assertSame('vector_index', $definition->getArgument(3)); - $this->assertSame('embedding', $definition->getArgument(4)); + $this->assertSame('vector', $definition->getArgument(4)); + $this->assertFalse($definition->getArgument(5)); $this->assertTrue($definition->hasTag('proxy')); $this->assertSame([ @@ -1446,7 +1879,6 @@ public function testMongoDbStoreWithBulkWriteCanBeConfigured() 'mongodb' => [ 'my_mongo_store' => [ 'database' => 'my_db', - 'collection' => 'my_collection', 'index_name' => 'vector_index', 'vector_field' => 'embedding', 'bulk_write' => true, @@ -1466,7 +1898,7 @@ public function testMongoDbStoreWithBulkWriteCanBeConfigured() $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); $this->assertSame(MongoDbClient::class, (string) $definition->getArgument(0)); $this->assertSame('my_db', $definition->getArgument(1)); - $this->assertSame('my_collection', $definition->getArgument(2)); + $this->assertSame('my_mongo_store', $definition->getArgument(2)); $this->assertSame('vector_index', $definition->getArgument(3)); $this->assertSame('embedding', $definition->getArgument(4)); $this->assertTrue($definition->getArgument(5)); @@ -1486,6 +1918,57 @@ public function testMongoDbStoreWithBulkWriteCanBeConfigured() } public function testNeo4jStoreCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'neo4j' => [ + 'my_neo4j_store' => [ + 'endpoint' => 'http://127.0.0.1:8000', + 'username' => 'test', + 'password' => 'test', + 'vector_index_name' => 'test', + 'node_name' => 'foo', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.neo4j.my_neo4j_store')); + + $definition = $container->getDefinition('ai.store.neo4j.my_neo4j_store'); + $this->assertSame(Neo4jStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(10, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('http://127.0.0.1:8000', $definition->getArgument(1)); + $this->assertSame('test', $definition->getArgument(2)); + $this->assertSame('test', $definition->getArgument(3)); + $this->assertSame('my_neo4j_store', $definition->getArgument(4)); + $this->assertSame('test', $definition->getArgument(5)); + $this->assertSame('foo', $definition->getArgument(6)); + $this->assertSame('embeddings', $definition->getArgument(7)); + $this->assertSame(1536, $definition->getArgument(8)); + $this->assertSame('cosine', $definition->getArgument(9)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_neo4j_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myNeo4jStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $neo4j_my_neo4j_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $neo4jMyNeo4jStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testNeo4jStoreWithCustomDatabaseCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ @@ -1498,9 +1981,6 @@ public function testNeo4jStoreCanBeConfigured() 'database' => 'foo', 'vector_index_name' => 'test', 'node_name' => 'foo', - 'vector_field' => '_vectors', - 'dimensions' => 768, - 'distance' => 'cosine', ], ], ], @@ -1522,8 +2002,8 @@ public function testNeo4jStoreCanBeConfigured() $this->assertSame('foo', $definition->getArgument(4)); $this->assertSame('test', $definition->getArgument(5)); $this->assertSame('foo', $definition->getArgument(6)); - $this->assertSame('_vectors', $definition->getArgument(7)); - $this->assertSame(768, $definition->getArgument(8)); + $this->assertSame('embeddings', $definition->getArgument(7)); + $this->assertSame(1536, $definition->getArgument(8)); $this->assertSame('cosine', $definition->getArgument(9)); $this->assertTrue($definition->hasTag('proxy')); @@ -1598,6 +2078,41 @@ public function testNeo4jStoreWithQuantizationCanBeConfigured() } public function testPineconeStoreCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'pinecone' => [ + 'my_pinecone_store' => [], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.pinecone.my_pinecone_store')); + + $definition = $container->getDefinition('ai.store.pinecone.my_pinecone_store'); + $this->assertSame(PineconeStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(3, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame(PineconeClient::class, (string) $definition->getArgument(0)); + $this->assertSame('my_pinecone_store', $definition->getArgument(1)); + $this->assertSame([], $definition->getArgument(2)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([['interface' => StoreInterface::class]], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_pinecone_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myPineconeStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $pinecone_my_pinecone_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $pineconeMyPineconeStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testPineconeStoreWithCustomNamespaceCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ @@ -1605,7 +2120,6 @@ public function testPineconeStoreCanBeConfigured() 'pinecone' => [ 'my_pinecone_store' => [ 'namespace' => 'my_namespace', - 'filter' => ['category' => 'books'], ], ], ], @@ -1622,7 +2136,7 @@ public function testPineconeStoreCanBeConfigured() $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); $this->assertSame(PineconeClient::class, (string) $definition->getArgument(0)); $this->assertSame('my_namespace', $definition->getArgument(1)); - $this->assertSame(['category' => 'books'], $definition->getArgument(2)); + $this->assertSame([], $definition->getArgument(2)); $this->assertTrue($definition->hasTag('proxy')); $this->assertSame([['interface' => StoreInterface::class]], $definition->getTag('proxy')); @@ -1720,8 +2234,6 @@ public function testPostgresStoreWithDifferentConnectionCanBeConfigured() 'postgres' => [ 'db' => [ 'dsn' => 'pgsql:host=localhost;port=5432;dbname=testdb;user=app;password=mypass', - 'table_name' => 'vectors', - 'vector_field' => 'foo', ], ], ], @@ -1737,8 +2249,8 @@ public function testPostgresStoreWithDifferentConnectionCanBeConfigured() $this->assertCount(4, $definition->getArguments()); $this->assertInstanceOf(Definition::class, $definition->getArgument(0)); $this->assertSame(\PDO::class, $definition->getArgument(0)->getClass()); - $this->assertSame('vectors', $definition->getArgument(1)); - $this->assertSame('foo', $definition->getArgument(2)); + $this->assertSame('db', $definition->getArgument(1)); + $this->assertSame('embedding', $definition->getArgument(2)); $this->assertTrue($definition->hasTag('proxy')); $this->assertSame([ @@ -1760,7 +2272,6 @@ public function testPostgresStoreWithDifferentConnectionCanBeConfigured() 'dsn' => 'pgsql:host=localhost;port=5432;dbname=testdb', 'username' => 'foo', 'password' => 'bar', - 'table_name' => 'vectors', 'vector_field' => 'foo', ], ], @@ -1778,7 +2289,7 @@ public function testPostgresStoreWithDifferentConnectionCanBeConfigured() $this->assertInstanceOf(Definition::class, $definition->getArgument(0)); $this->assertSame(\PDO::class, $definition->getArgument(0)->getClass()); $this->assertSame(['pgsql:host=localhost;port=5432;dbname=testdb', 'foo', 'bar'], $definition->getArgument(0)->getArguments()); - $this->assertSame('vectors', $definition->getArgument(1)); + $this->assertSame('db', $definition->getArgument(1)); $this->assertSame('foo', $definition->getArgument(2)); $this->assertTrue($definition->hasTag('proxy')); @@ -1799,7 +2310,6 @@ public function testPostgresStoreWithDifferentConnectionCanBeConfigured() 'postgres' => [ 'db' => [ 'dbal_connection' => 'my_connection', - 'table_name' => 'vectors', 'vector_field' => 'foo', ], ], @@ -1814,7 +2324,7 @@ public function testPostgresStoreWithDifferentConnectionCanBeConfigured() $this->assertCount(4, $definition->getArguments()); $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); $this->assertSame('my_connection', (string) $definition->getArgument(0)); - $this->assertSame('vectors', $definition->getArgument(1)); + $this->assertSame('db', $definition->getArgument(1)); $this->assertSame('foo', $definition->getArgument(2)); $this->assertSame(Distance::L2, $definition->getArgument(3)); @@ -1836,7 +2346,6 @@ public function testPostgresStoreWithDifferentConnectionCanBeConfigured() 'postgres' => [ 'db' => [ 'dbal_connection' => 'my_connection', - 'table_name' => 'vectors', 'vector_field' => 'foo', 'distance' => Distance::L1->value, ], @@ -1852,7 +2361,45 @@ public function testPostgresStoreWithDifferentConnectionCanBeConfigured() $this->assertCount(4, $definition->getArguments()); $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); $this->assertSame('my_connection', (string) $definition->getArgument(0)); - $this->assertSame('vectors', $definition->getArgument(1)); + $this->assertSame('db', $definition->getArgument(1)); + $this->assertSame('foo', $definition->getArgument(2)); + $this->assertSame(Distance::L1, $definition->getArgument(3)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $db')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $postgres_db')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $postgresDb')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'postgres' => [ + 'db' => [ + 'dbal_connection' => 'my_connection', + 'table_name' => 'foo', + 'vector_field' => 'foo', + 'distance' => Distance::L1->value, + ], + ], + ], + ], + ]); + + $definition = $container->getDefinition('ai.store.postgres.db'); + $this->assertSame(PostgresStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(4, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('my_connection', (string) $definition->getArgument(0)); + $this->assertSame('foo', $definition->getArgument(1)); $this->assertSame('foo', $definition->getArgument(2)); $this->assertSame(Distance::L1, $definition->getArgument(3)); @@ -1870,6 +2417,50 @@ public function testPostgresStoreWithDifferentConnectionCanBeConfigured() } public function testQdrantStoreCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'qdrant' => [ + 'my_qdrant_store' => [ + 'endpoint' => 'http://127.0.0.1:8000', + 'api_key' => 'test', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.qdrant.my_qdrant_store')); + + $definition = $container->getDefinition('ai.store.qdrant.my_qdrant_store'); + $this->assertSame(QdrantStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(6, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('http://127.0.0.1:8000', $definition->getArgument(1)); + $this->assertSame('test', $definition->getArgument(2)); + $this->assertSame('my_qdrant_store', $definition->getArgument(3)); + $this->assertSame(1536, $definition->getArgument(4)); + $this->assertSame('Cosine', $definition->getArgument(5)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_qdrant_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myQdrantStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $qdrant_my_qdrant_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $qdrantMyQdrantStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testQdrantStoreWithCustomCollectionCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ @@ -1879,8 +2470,6 @@ public function testQdrantStoreCanBeConfigured() 'endpoint' => 'http://127.0.0.1:8000', 'api_key' => 'test', 'collection_name' => 'foo', - 'dimensions' => 768, - 'distance' => 'Cosine', ], ], ], @@ -1899,7 +2488,7 @@ public function testQdrantStoreCanBeConfigured() $this->assertSame('http://127.0.0.1:8000', $definition->getArgument(1)); $this->assertSame('test', $definition->getArgument(2)); $this->assertSame('foo', $definition->getArgument(3)); - $this->assertSame(768, $definition->getArgument(4)); + $this->assertSame(1536, $definition->getArgument(4)); $this->assertSame('Cosine', $definition->getArgument(5)); $this->assertTrue($definition->hasTag('proxy')); @@ -1926,8 +2515,6 @@ public function testQdrantStoreWithAsyncCanBeConfigured() 'endpoint' => 'http://127.0.0.1:8000', 'api_key' => 'test', 'collection_name' => 'foo', - 'dimensions' => 768, - 'distance' => 'Cosine', 'async' => true, ], ], @@ -1947,7 +2534,7 @@ public function testQdrantStoreWithAsyncCanBeConfigured() $this->assertSame('http://127.0.0.1:8000', $definition->getArgument(1)); $this->assertSame('test', $definition->getArgument(2)); $this->assertSame('foo', $definition->getArgument(3)); - $this->assertSame(768, $definition->getArgument(4)); + $this->assertSame(1536, $definition->getArgument(4)); $this->assertSame('Cosine', $definition->getArgument(5)); $this->assertTrue($definition->getArgument(6)); @@ -1976,7 +2563,6 @@ public function testRedisStoreCanBeConfigured() 'host' => '1.2.3.4', 'port' => 6379, ], - 'index_name' => 'my_vector_index', ], ], ], @@ -1992,7 +2578,52 @@ public function testRedisStoreCanBeConfigured() $this->assertCount(4, $definition->getArguments()); $this->assertInstanceOf(Definition::class, $definition->getArgument(0)); $this->assertSame(\Redis::class, $definition->getArgument(0)->getClass()); - $this->assertSame('my_vector_index', $definition->getArgument(1)); + $this->assertSame('my_redis_store', $definition->getArgument(1)); + $this->assertSame('vector:', $definition->getArgument(2)); + $this->assertSame(RedisDistance::Cosine, $definition->getArgument(3)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_redis_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myRedisStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $redis_my_redis_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $redisMyRedisStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testRedisStoreWithCustomIndexCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'redis' => [ + 'my_redis_store' => [ + 'connection_parameters' => [ + 'host' => '1.2.3.4', + 'port' => 6379, + ], + 'index_name' => 'foo', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.redis.my_redis_store')); + + $definition = $container->getDefinition('ai.store.redis.my_redis_store'); + $this->assertSame(RedisStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(4, $definition->getArguments()); + $this->assertInstanceOf(Definition::class, $definition->getArgument(0)); + $this->assertSame(\Redis::class, $definition->getArgument(0)->getClass()); + $this->assertSame('foo', $definition->getArgument(1)); $this->assertSame('vector:', $definition->getArgument(2)); $this->assertSame(RedisDistance::Cosine, $definition->getArgument(3)); @@ -2139,6 +2770,48 @@ public function testRedisStoreWithCustomDistanceCanBeConfigured() } public function testSupabaseStoreCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'supabase' => [ + 'my_supabase_store' => [ + 'url' => 'https://test.supabase.co', + 'api_key' => 'supabase_test_key', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.supabase.my_supabase_store')); + + $definition = $container->getDefinition('ai.store.supabase.my_supabase_store'); + $this->assertSame(SupabaseStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(7, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('https://test.supabase.co', $definition->getArgument(1)); + $this->assertSame('supabase_test_key', $definition->getArgument(2)); + $this->assertSame('my_supabase_store', $definition->getArgument(3)); + $this->assertSame('embedding', $definition->getArgument(4)); + $this->assertSame(1536, $definition->getArgument(5)); + $this->assertSame('match_documents', $definition->getArgument(6)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([['interface' => StoreInterface::class]], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_supabase_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $mySupabaseStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $supabase_my_supabase_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $supabaseMySupabaseStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testSupabaseStoreWithCustomTableCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ @@ -2148,8 +2821,6 @@ public function testSupabaseStoreCanBeConfigured() 'url' => 'https://test.supabase.co', 'api_key' => 'supabase_test_key', 'table' => 'my_supabase_table', - 'vector_field' => 'my_embedding', - 'vector_dimension' => 1024, ], ], ], @@ -2162,14 +2833,15 @@ public function testSupabaseStoreCanBeConfigured() $this->assertSame(SupabaseStore::class, $definition->getClass()); $this->assertTrue($definition->isLazy()); - $this->assertCount(6, $definition->getArguments()); + $this->assertCount(7, $definition->getArguments()); $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); $this->assertSame('http_client', (string) $definition->getArgument(0)); $this->assertSame('https://test.supabase.co', $definition->getArgument(1)); $this->assertSame('supabase_test_key', $definition->getArgument(2)); $this->assertSame('my_supabase_table', $definition->getArgument(3)); - $this->assertSame('my_embedding', $definition->getArgument(4)); - $this->assertSame(1024, $definition->getArgument(5)); + $this->assertSame('embedding', $definition->getArgument(4)); + $this->assertSame(1536, $definition->getArgument(5)); + $this->assertSame('match_documents', $definition->getArgument(6)); $this->assertTrue($definition->hasTag('proxy')); $this->assertSame([['interface' => StoreInterface::class]], $definition->getTag('proxy')); @@ -2192,9 +2864,6 @@ public function testSupabaseStoreWithCustomHttpClientCanBeConfigured() 'http_client' => 'foo', 'url' => 'https://test.supabase.co', 'api_key' => 'supabase_test_key', - 'table' => 'my_supabase_table', - 'vector_field' => 'my_embedding', - 'vector_dimension' => 1024, ], ], ], @@ -2207,14 +2876,15 @@ public function testSupabaseStoreWithCustomHttpClientCanBeConfigured() $this->assertSame(SupabaseStore::class, $definition->getClass()); $this->assertTrue($definition->isLazy()); - $this->assertCount(6, $definition->getArguments()); + $this->assertCount(7, $definition->getArguments()); $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); $this->assertSame('foo', (string) $definition->getArgument(0)); $this->assertSame('https://test.supabase.co', $definition->getArgument(1)); $this->assertSame('supabase_test_key', $definition->getArgument(2)); - $this->assertSame('my_supabase_table', $definition->getArgument(3)); - $this->assertSame('my_embedding', $definition->getArgument(4)); - $this->assertSame(1024, $definition->getArgument(5)); + $this->assertSame('my_supabase_store', $definition->getArgument(3)); + $this->assertSame('embedding', $definition->getArgument(4)); + $this->assertSame(1536, $definition->getArgument(5)); + $this->assertSame('match_documents', $definition->getArgument(6)); $this->assertTrue($definition->hasTag('proxy')); $this->assertSame([['interface' => StoreInterface::class]], $definition->getTag('proxy')); @@ -2236,9 +2906,6 @@ public function testSupabaseStoreWithCustomFunctionCanBeConfigured() 'my_supabase_store' => [ 'url' => 'https://test.supabase.co', 'api_key' => 'supabase_test_key', - 'table' => 'my_supabase_table', - 'vector_field' => 'my_embedding', - 'vector_dimension' => 1024, 'function_name' => 'my_custom_function', ], ], @@ -2257,9 +2924,9 @@ public function testSupabaseStoreWithCustomFunctionCanBeConfigured() $this->assertSame('http_client', (string) $definition->getArgument(0)); $this->assertSame('https://test.supabase.co', $definition->getArgument(1)); $this->assertSame('supabase_test_key', $definition->getArgument(2)); - $this->assertSame('my_supabase_table', $definition->getArgument(3)); - $this->assertSame('my_embedding', $definition->getArgument(4)); - $this->assertSame(1024, $definition->getArgument(5)); + $this->assertSame('my_supabase_store', $definition->getArgument(3)); + $this->assertSame('embedding', $definition->getArgument(4)); + $this->assertSame(1536, $definition->getArgument(5)); $this->assertSame('my_custom_function', $definition->getArgument(6)); $this->assertTrue($definition->hasTag('proxy')); @@ -2285,10 +2952,6 @@ public function testSurrealDbStoreCanBeConfigured() 'password' => 'test', 'namespace' => 'foo', 'database' => 'bar', - 'table' => 'bar', - 'vector_field' => '_vectors', - 'strategy' => 'cosine', - 'dimensions' => 768, ], ], ], @@ -2309,10 +2972,62 @@ public function testSurrealDbStoreCanBeConfigured() $this->assertSame('test', $definition->getArgument(3)); $this->assertSame('foo', $definition->getArgument(4)); $this->assertSame('bar', $definition->getArgument(5)); - $this->assertSame('bar', $definition->getArgument(6)); + $this->assertSame('my_surrealdb_store', $definition->getArgument(6)); $this->assertSame('_vectors', $definition->getArgument(7)); $this->assertSame('cosine', $definition->getArgument(8)); - $this->assertSame(768, $definition->getArgument(9)); + $this->assertSame(1536, $definition->getArgument(9)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_surrealdb_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $mySurrealdbStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $surrealdb_my_surrealdb_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $surrealdbMySurrealdbStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testSurrealDbStoreWithCustomTableCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'surrealdb' => [ + 'my_surrealdb_store' => [ + 'endpoint' => 'http://127.0.0.1:8000', + 'username' => 'test', + 'password' => 'test', + 'namespace' => 'foo', + 'database' => 'bar', + 'table' => 'custom', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.surrealdb.my_surrealdb_store')); + + $definition = $container->getDefinition('ai.store.surrealdb.my_surrealdb_store'); + $this->assertSame(SurrealDbStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(10, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('http://127.0.0.1:8000', $definition->getArgument(1)); + $this->assertSame('test', $definition->getArgument(2)); + $this->assertSame('test', $definition->getArgument(3)); + $this->assertSame('foo', $definition->getArgument(4)); + $this->assertSame('bar', $definition->getArgument(5)); + $this->assertSame('custom', $definition->getArgument(6)); + $this->assertSame('_vectors', $definition->getArgument(7)); + $this->assertSame('cosine', $definition->getArgument(8)); + $this->assertSame(1536, $definition->getArgument(9)); $this->assertTrue($definition->hasTag('proxy')); $this->assertSame([ @@ -2386,6 +3101,50 @@ public function testSurrealDbStoreWithNamespacedUserCanBeConfigured() } public function testTypesenseStoreCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'typesense' => [ + 'my_typesense_store' => [ + 'endpoint' => 'http://localhost:8108', + 'api_key' => 'foo', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.typesense.my_typesense_store')); + + $definition = $container->getDefinition('ai.store.typesense.my_typesense_store'); + $this->assertSame(TypesenseStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(6, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('http://localhost:8108', $definition->getArgument(1)); + $this->assertSame('foo', $definition->getArgument(2)); + $this->assertSame('my_typesense_store', $definition->getArgument(3)); + $this->assertSame('_vectors', $definition->getArgument(4)); + $this->assertSame(1536, $definition->getArgument(5)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_typesense_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myTypesenseStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $typesense_my_typesense_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $typesenseMyTypesenseStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testTypesenseStoreWithCustomCollectionCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ @@ -2395,8 +3154,6 @@ public function testTypesenseStoreCanBeConfigured() 'endpoint' => 'http://localhost:8108', 'api_key' => 'foo', 'collection' => 'my_collection', - 'vector_field' => 'vector', - 'dimensions' => 768, ], ], ], @@ -2415,8 +3172,8 @@ public function testTypesenseStoreCanBeConfigured() $this->assertSame('http://localhost:8108', $definition->getArgument(1)); $this->assertSame('foo', $definition->getArgument(2)); $this->assertSame('my_collection', $definition->getArgument(3)); - $this->assertSame('vector', $definition->getArgument(4)); - $this->assertSame(768, $definition->getArgument(5)); + $this->assertSame('_vectors', $definition->getArgument(4)); + $this->assertSame(1536, $definition->getArgument(5)); $this->assertTrue($definition->hasTag('proxy')); $this->assertSame([ @@ -2433,6 +3190,48 @@ public function testTypesenseStoreCanBeConfigured() } public function testWevaviateStoreCanBeConfigured() + { + $container = $this->buildContainer([ + 'ai' => [ + 'store' => [ + 'weaviate' => [ + 'my_weaviate_store' => [ + 'endpoint' => 'http://localhost:8080', + 'api_key' => 'bar', + ], + ], + ], + ], + ]); + + $this->assertTrue($container->hasDefinition('ai.store.weaviate.my_weaviate_store')); + + $definition = $container->getDefinition('ai.store.weaviate.my_weaviate_store'); + $this->assertSame(WeaviateStore::class, $definition->getClass()); + + $this->assertTrue($definition->isLazy()); + $this->assertCount(4, $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); + $this->assertSame('http_client', (string) $definition->getArgument(0)); + $this->assertSame('http://localhost:8080', $definition->getArgument(1)); + $this->assertSame('bar', $definition->getArgument(2)); + $this->assertSame('my_weaviate_store', $definition->getArgument(3)); + + $this->assertTrue($definition->hasTag('proxy')); + $this->assertSame([ + ['interface' => StoreInterface::class], + ['interface' => ManagedStoreInterface::class], + ], $definition->getTag('proxy')); + $this->assertTrue($definition->hasTag('ai.store')); + + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_weaviate_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myWeaviateStore')); + $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $weaviate_my_weaviate_store')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $weaviateMyWeaviateStore')); + $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface')); + } + + public function testWevaviateStoreWithCustomCollectionCanBeConfigured() { $container = $this->buildContainer([ 'ai' => [ @@ -5942,7 +6741,25 @@ private function getFullConfig(): array 'index_name' => 'random', 'dimensions' => 1536, 'metric' => 'cosine', - 'endpoint_url' => 'https://api.cloudflare.com/client/v5/accounts', + 'endpoint' => 'https://api.cloudflare.com/client/v5/accounts', + ], + 'my_cloudflare_store_with_dimensions' => [ + 'account_id' => 'foo', + 'api_key' => 'bar', + 'index_name' => 'random', + 'dimensions' => 1536, + ], + 'my_cloudflare_store_with_metric' => [ + 'account_id' => 'foo', + 'api_key' => 'bar', + 'index_name' => 'random', + 'metric' => 'cosine', + ], + 'my_cloudflare_store_with_endpoint' => [ + 'account_id' => 'foo', + 'api_key' => 'bar', + 'index_name' => 'random', + 'endpoint' => 'https://api.cloudflare.com/client/v6/accounts', ], ], 'manticore' => [