diff --git a/src/ai-bundle/config/options.php b/src/ai-bundle/config/options.php index 09c123af..a344f643 100644 --- a/src/ai-bundle/config/options.php +++ b/src/ai-bundle/config/options.php @@ -171,6 +171,28 @@ ->end() ->end() ->end() + ->arrayNode('meilisearch') + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->arrayPrototype() + ->children() + ->scalarNode('api_key')->cannotBeEmpty()->end() + ->scalarNode('index_name')->cannotBeEmpty()->end() + ->scalarNode('embedder')->end() + ->scalarNode('vector_field')->end() + ->scalarNode('dimensions')->end() + ->end() + ->end() + ->end() + ->arrayNode('memory') + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->arrayPrototype() + ->children() + ->scalarNode('distance')->cannotBeEmpty()->end() + ->end() + ->end() + ->end() ->arrayNode('mongodb') ->normalizeKeys(false) ->useAttributeAsKey('name') @@ -185,6 +207,24 @@ ->end() ->end() ->end() + ->arrayNode('neo4j') + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->arrayPrototype() + ->children() + ->scalarNode('endpoint')->cannotBeEmpty()->end() + ->scalarNode('username')->cannotBeEmpty()->end() + ->scalarNode('password')->cannotBeEmpty()->end() + ->scalarNode('database')->cannotBeEmpty()->end() + ->scalarNode('vector_index_name')->cannotBeEmpty()->end() + ->scalarNode('node_name')->cannotBeEmpty()->end() + ->scalarNode('vector_field')->end() + ->scalarNode('dimensions')->end() + ->scalarNode('distance')->end() + ->booleanNode('quantization')->end() + ->end() + ->end() + ->end() ->arrayNode('pinecone') ->normalizeKeys(false) ->useAttributeAsKey('name') @@ -199,6 +239,37 @@ ->end() ->end() ->end() + ->arrayNode('qdrant') + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->arrayPrototype() + ->children() + ->scalarNode('endpoint')->cannotBeEmpty()->end() + ->scalarNode('api_key')->cannotBeEmpty()->end() + ->scalarNode('collection_name')->cannotBeEmpty()->end() + ->scalarNode('dimensions')->end() + ->scalarNode('distance')->end() + ->end() + ->end() + ->end() + ->arrayNode('surreal_db') + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->arrayPrototype() + ->children() + ->scalarNode('endpoint')->cannotBeEmpty()->end() + ->scalarNode('username')->cannotBeEmpty()->end() + ->scalarNode('password')->cannotBeEmpty()->end() + ->scalarNode('namespace')->cannotBeEmpty()->end() + ->scalarNode('database')->cannotBeEmpty()->end() + ->scalarNode('table')->end() + ->scalarNode('vector_field')->end() + ->scalarNode('strategy')->end() + ->scalarNode('dimensions')->end() + ->booleanNode('namespaced_user')->end() + ->end() + ->end() + ->end() ->end() ->end() ->arrayNode('indexer') diff --git a/src/ai-bundle/doc/index.rst b/src/ai-bundle/doc/index.rst index 7a22b92d..f1076126 100644 --- a/src/ai-bundle/doc/index.rst +++ b/src/ai-bundle/doc/index.rst @@ -86,7 +86,7 @@ Configuration - 'Symfony\AI\Agent\Toolbox\Tool\Wikipedia' fault_tolerant_toolbox: false # Disables fault tolerant toolbox, default is true store: - # also azure_search, mongodb and pinecone are supported as store type + # also azure_search, meilisearch, memory, mongodb, pinecone, qdrant and surrealdb are supported as store type chroma_db: # multiple collections possible per type default: diff --git a/src/ai-bundle/src/AiBundle.php b/src/ai-bundle/src/AiBundle.php index bfd00a1f..4230044a 100644 --- a/src/ai-bundle/src/AiBundle.php +++ b/src/ai-bundle/src/AiBundle.php @@ -40,10 +40,15 @@ use Symfony\AI\Platform\ResultConverterInterface; use Symfony\AI\Store\Bridge\Azure\SearchStore as AzureSearchStore; use Symfony\AI\Store\Bridge\ChromaDb\Store as ChromaDbStore; +use Symfony\AI\Store\Bridge\Meilisearch\Store as MeilisearchStore; use Symfony\AI\Store\Bridge\MongoDb\Store as MongoDbStore; +use Symfony\AI\Store\Bridge\Neo4j\Store as Neo4jStore; use Symfony\AI\Store\Bridge\Pinecone\Store as PineconeStore; +use Symfony\AI\Store\Bridge\Qdrant\Store as QdrantStore; +use Symfony\AI\Store\Bridge\SurrealDb\Store as SurrealDbStore; use Symfony\AI\Store\Document\Vectorizer; use Symfony\AI\Store\Indexer; +use Symfony\AI\Store\InMemoryStore; use Symfony\AI\Store\StoreInterface; use Symfony\AI\Store\VectorStoreInterface; use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator; @@ -476,6 +481,50 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde } } + if ('meilisearch' === $type) { + foreach ($stores as $name => $store) { + $arguments = [ + new Reference('http_client'), + $store['api_key'], + $store['index_name'], + ]; + + if (\array_key_exists('embedder', $store)) { + $arguments[3] = $store['embedder']; + } + + if (\array_key_exists('vector_field', $store)) { + $arguments[4] = $store['vector_field']; + } + + if (\array_key_exists('dimensions', $store)) { + $arguments[5] = $store['dimensions']; + } + + $definition = new Definition(MeilisearchStore::class); + $definition + ->addTag('ai.store') + ->setArguments($arguments); + + $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); + } + } + + if ('memory' === $type) { + foreach ($stores as $name => $store) { + $arguments = [ + $store['distance'], + ]; + + $definition = new Definition(InMemoryStore::class); + $definition + ->addTag('ai.store') + ->setArguments($arguments); + + $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); + } + } + if ('mongodb' === $type) { foreach ($stores as $name => $store) { $arguments = [ @@ -502,6 +551,43 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde } } + if ('neo4j' === $type) { + foreach ($stores as $name => $store) { + $arguments = [ + new Reference('http_client'), + $store['endpoint'], + $store['username'], + $store['password'], + $store['database'], + $store['vector_index_name'], + $store['node_name'], + ]; + + if (\array_key_exists('vector_field', $store)) { + $arguments[7] = $store['vector_field']; + } + + if (\array_key_exists('dimensions', $store)) { + $arguments[8] = $store['dimensions']; + } + + if (\array_key_exists('distance', $store)) { + $arguments[9] = $store['distance']; + } + + if (\array_key_exists('quantization', $store)) { + $arguments[10] = $store['quantization']; + } + + $definition = new Definition(Neo4jStore::class); + $definition + ->addTag('ai.store') + ->setArguments($arguments); + + $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); + } + } + if ('pinecone' === $type) { foreach ($stores as $name => $store) { $arguments = [ @@ -525,6 +611,72 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); } } + + if ('qdrant' === $type) { + foreach ($stores as $name => $store) { + $arguments = [ + new Reference('http_client'), + $store['endpoint'], + $store['api_key'], + $store['collection_name'], + ]; + + if (\array_key_exists('dimensions', $store)) { + $arguments[4] = $store['dimensions']; + } + + if (\array_key_exists('distance', $store)) { + $arguments[5] = $store['distance']; + } + + $definition = new Definition(QdrantStore::class); + $definition + ->addTag('ai.store') + ->setArguments($arguments); + + $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); + } + } + + if ('surreal_db' === $type) { + foreach ($stores as $name => $store) { + $arguments = [ + new Reference('http_client'), + $store['endpoint'], + $store['username'], + $store['password'], + $store['namespace'], + $store['database'], + ]; + + if (\array_key_exists('table', $store)) { + $arguments[6] = $store['table']; + } + + if (\array_key_exists('vector_field', $store)) { + $arguments[7] = $store['vector_field']; + } + + if (\array_key_exists('strategy', $store)) { + $arguments[8] = $store['strategy']; + } + + if (\array_key_exists('dimensions', $store)) { + $arguments[9] = $store['dimensions']; + } + + if (\array_key_exists('namespaced_user', $store)) { + $arguments[10] = $store['namespaced_user']; + } + + $definition = new Definition(SurrealDbStore::class); + $definition + ->addTag('ai.store') + ->setArguments($arguments); + + $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); + } + } } /** diff --git a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php index f19ef3ef..1e6d4131 100644 --- a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php +++ b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php @@ -206,6 +206,20 @@ private function getFullConfig(): array 'collection' => 'my_collection', ], ], + 'meilisearch' => [ + 'my_meilisearch_store' => [ + 'api_key' => 'foo', + 'index_name' => 'test', + 'embedder' => 'default', + 'vector_field' => '_vectors', + 'dimensions' => 768, + ], + ], + 'memory' => [ + 'my_memory_store' => [ + 'distance' => 'cosine', + ], + ], 'mongodb' => [ 'my_mongo_store' => [ 'database' => 'my_db', @@ -215,6 +229,20 @@ private function getFullConfig(): array 'bulk_write' => true, ], ], + 'neo4j' => [ + 'my_neo4j_store' => [ + 'endpoint' => 'http://127.0.0.1:8000', + 'username' => 'test', + 'password' => 'test', + 'database' => 'foo', + 'vector_index_name' => 'test', + 'node_name' => 'foo', + 'vector_field' => '_vectors', + 'dimensions' => 768, + 'distance' => 'cosine', + 'quantization' => true, + ], + ], 'pinecone' => [ 'my_pinecone_store' => [ 'namespace' => 'my_namespace', @@ -222,6 +250,29 @@ private function getFullConfig(): array 'top_k' => 10, ], ], + 'qdrant' => [ + 'my_qdrant_store' => [ + 'endpoint' => 'http://127.0.0.1:8000', + 'api_key' => 'test', + 'collection_name' => 'foo', + 'dimensions' => 768, + 'distance' => 'Cosine', + ], + ], + 'surreal_db' => [ + 'my_surreal_db_store' => [ + 'endpoint' => 'http://127.0.0.1:8000', + 'username' => 'test', + 'password' => 'test', + 'namespace' => 'foo', + 'database' => 'bar', + 'table' => 'bar', + 'vector_field' => '_vectors', + 'strategy' => 'cosine', + 'dimensions' => 768, + 'namespaced_user' => true, + ], + ], ], 'indexer' => [ 'my_text_indexer' => [