diff --git a/src/ai-bundle/config/options.php b/src/ai-bundle/config/options.php index 9e09b5226..a21b35562 100644 --- a/src/ai-bundle/config/options.php +++ b/src/ai-bundle/config/options.php @@ -442,7 +442,7 @@ ->treatNullLike(['enabled' => true]) ->beforeNormalization() ->ifArray() - ->then(function (array $v) { + ->then(function (array $v): array { return [ 'enabled' => $v['enabled'] ?? true, 'services' => $v['services'] ?? $v, @@ -564,7 +564,7 @@ ->stringNode('table')->isRequired()->cannotBeEmpty()->end() ->end() ->validate() - ->ifTrue(static fn ($v) => !isset($v['dsn']) && !isset($v['http_client'])) + ->ifTrue(static fn ($v): bool => !isset($v['dsn']) && !isset($v['http_client'])) ->thenInvalid('Either "dsn" or "http_client" must be configured.') ->end() ->end() @@ -819,11 +819,11 @@ ->stringNode('dbal_connection')->cannotBeEmpty()->end() ->end() ->validate() - ->ifTrue(static fn ($v) => !isset($v['dsn']) && !isset($v['dbal_connection'])) + ->ifTrue(static fn (array $v): bool => !isset($v['dsn']) && !isset($v['dbal_connection'])) ->thenInvalid('Either "dsn" or "dbal_connection" must be configured.') ->end() ->validate() - ->ifTrue(static fn ($v) => isset($v['dsn'], $v['dbal_connection'])) + ->ifTrue(static fn (array $v): bool => isset($v['dsn'], $v['dbal_connection'])) ->thenInvalid('Either "dsn" or "dbal_connection" can be configured, but not both.') ->end() ->end() diff --git a/src/ai-bundle/src/AiBundle.php b/src/ai-bundle/src/AiBundle.php index 4922e79f6..0228d8c1b 100644 --- a/src/ai-bundle/src/AiBundle.php +++ b/src/ai-bundle/src/AiBundle.php @@ -929,7 +929,7 @@ private function processAgentConfig(string $name, array $config, ContainerBuilde * @param array $stores * @param array $setupStoresOptions */ - private function processStoreConfig(string $type, array $stores, ContainerBuilder $container, &$setupStoresOptions): void + private function processStoreConfig(string $type, array $stores, ContainerBuilder $container, array &$setupStoresOptions): void { if ('azure_search' === $type) { foreach ($stores as $name => $store) { @@ -947,8 +947,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(AzureSearchStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -961,6 +963,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $arguments = [ new Reference($store['service']), new Definition(DistanceCalculator::class), + $store['cache_key'] ?? $name, ]; if (\array_key_exists('strategy', $store) && null !== $store['strategy']) { @@ -974,13 +977,11 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $arguments[1] = new Reference('ai.store.distance_calculator.'.$name); } - $arguments[2] = \array_key_exists('cache_key', $store) && null !== $store['cache_key'] - ? $store['cache_key'] - : $name; - $definition = new Definition(CacheStore::class); $definition + ->setLazy(true) ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); @@ -993,10 +994,12 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde foreach ($stores as $name => $store) { $definition = new Definition(ChromaDbStore::class); $definition + ->setLazy(true) ->setArguments([ new Reference($store['client']), $store['collection'], ]) + ->addTag('proxy', ['interface' => StoreInterface::class]) ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); @@ -1019,11 +1022,13 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(ClickHouseStore::class); $definition + ->setLazy(true) ->setArguments([ $httpClient, $store['database'], $store['table'], ]) + ->addTag('proxy', ['interface' => StoreInterface::class]) ->addTag('ai.store') ; @@ -1056,8 +1061,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(CloudflareStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -1095,7 +1102,9 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(ManticoreStore::class); $definition + ->setLazy(true) ->addTag('ai.store') + ->addTag('proxy', ['interface' => StoreInterface::class]) ->setArguments($arguments); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); @@ -1116,8 +1125,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(MariaDbStore::class); $definition->setFactory([MariaDbStore::class, 'fromDbal']); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $serviceId = 'ai.store.'.$type.'.'.$name; $container->setDefinition($serviceId, $definition); @@ -1155,8 +1166,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(MeilisearchStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -1166,7 +1179,9 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde if ('memory' === $type) { foreach ($stores as $name => $store) { - $arguments = []; + $arguments = [ + new Definition(DistanceCalculator::class), + ]; if (\array_key_exists('strategy', $store) && null !== $store['strategy']) { if (!$container->hasDefinition('ai.store.distance_calculator.'.$name)) { @@ -1181,8 +1196,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(InMemoryStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -1214,8 +1231,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(MilvusStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -1242,8 +1261,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(MongoDbStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -1281,8 +1302,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(Neo4jStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -1307,8 +1330,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(PineconeStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -1339,8 +1364,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(QdrantStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -1359,14 +1386,15 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(RedisStore::class); $definition - ->addTag('ai.store') + ->setLazy(true) ->setArguments([ $redisClient, $store['index_name'], $store['key_prefix'], $store['distance'], ]) - ; + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); } @@ -1405,8 +1433,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(SurrealDbStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -1433,8 +1463,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(TypesenseStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -1444,17 +1476,17 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde if ('weaviate' === $type) { foreach ($stores as $name => $store) { - $arguments = [ - new Reference('http_client'), - $store['endpoint'], - $store['api_key'], - $store['collection'], - ]; - $definition = new Definition(WeaviateStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments([ + new Reference('http_client'), + $store['endpoint'], + $store['api_key'], + $store['collection'], + ]) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -1495,8 +1527,10 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde } $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -1530,11 +1564,14 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(SupabaseStore::class); $definition - ->addTag('ai.store') - ->setArguments($arguments); + ->setLazy(true) + ->setArguments($arguments) + ->addTag('proxy', ['interface' => StoreInterface::class]) + ->addTag('ai.store'); $container->setDefinition('ai.store.supabase.'.$name, $definition); - $container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName()); + $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); + $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $type.'_'.$name); } } } @@ -1750,10 +1787,12 @@ private function processChatConfig(string $name, array $configuration, Container { $definition = new Definition(Chat::class); $definition + ->setLazy(true) ->setArguments([ new Reference($configuration['agent']), new Reference($configuration['message_store']), ]) + ->addTag('proxy', ['interface' => ChatInterface::class]) ->addTag('ai.chat'); $container->setDefinition('ai.chat.'.$name, $definition); diff --git a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php index 239ef5cc0..abce25d2e 100644 --- a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php +++ b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php @@ -27,6 +27,7 @@ use Symfony\AI\Platform\Bridge\Ollama\OllamaApiCatalog; use Symfony\AI\Platform\Capability; use Symfony\AI\Platform\Model; +use Symfony\AI\Store\Bridge\Local\DistanceCalculator; use Symfony\AI\Store\Document\Filter\TextContainsFilter; use Symfony\AI\Store\Document\Loader\InMemoryLoader; use Symfony\AI\Store\Document\Transformer\TextTrimTransformer; @@ -483,7 +484,9 @@ public function testInMemoryStoreWithoutCustomStrategyCanBeConfigured() $this->assertTrue($container->hasDefinition('ai.store.memory.my_memory_store_with_custom_strategy')); $definition = $container->getDefinition('ai.store.memory.my_memory_store_with_custom_strategy'); - $this->assertCount(0, $definition->getArguments()); + $this->assertCount(1, $definition->getArguments()); + $this->assertInstanceOf(Definition::class, $definition->getArgument(0)); + $this->assertSame(DistanceCalculator::class, $definition->getArgument(0)->getClass()); } public function testInMemoryStoreWithCustomStrategyCanBeConfigured()