Skip to content

Commit 74d4164

Browse files
committed
feat(store): support for Vektor
1 parent 3fd26c2 commit 74d4164

File tree

22 files changed

+695
-0
lines changed

22 files changed

+695
-0
lines changed

deptrac.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,10 @@ deptrac:
325325
collectors:
326326
- type: classLike
327327
value: Symfony\\AI\\Store\\Bridge\\Typesense\\.*
328+
- name: VektorStore
329+
collectors:
330+
- type: classLike
331+
value: Symfony\\AI\\Store\\Bridge\\Vektor\\.*
328332
- name: WeaviateStore
329333
collectors:
330334
- type: classLike
@@ -555,6 +559,9 @@ deptrac:
555559
TypesenseStore:
556560
- StoreComponent
557561
- PlatformComponent
562+
VektorStore:
563+
- StoreComponent
564+
- PlatformComponent
558565
WeaviateStore:
559566
- StoreComponent
560567
- PlatformComponent

examples/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"symfony/ai-tavily-tool": "^0.5",
7171
"symfony/ai-transformers-php-platform": "^0.5",
7272
"symfony/ai-typesense-store": "^0.5",
73+
"symfony/ai-vektor-store": "^0.6",
7374
"symfony/ai-vertex-ai-platform": "^0.5",
7475
"symfony/ai-voyage-platform": "^0.5",
7576
"symfony/ai-weaviate-store": "^0.5",

examples/rag/vektor.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
use Symfony\AI\Agent\Agent;
13+
use Symfony\AI\Agent\Bridge\SimilaritySearch\SimilaritySearch;
14+
use Symfony\AI\Agent\Toolbox\AgentProcessor;
15+
use Symfony\AI\Agent\Toolbox\Toolbox;
16+
use Symfony\AI\Fixtures\Movies;
17+
use Symfony\AI\Platform\Bridge\OpenAi\PlatformFactory;
18+
use Symfony\AI\Platform\Message\Message;
19+
use Symfony\AI\Platform\Message\MessageBag;
20+
use Symfony\AI\Store\Bridge\Vektor\Store;
21+
use Symfony\AI\Store\Document\Metadata;
22+
use Symfony\AI\Store\Document\TextDocument;
23+
use Symfony\AI\Store\Document\Vectorizer;
24+
use Symfony\AI\Store\Indexer\DocumentIndexer;
25+
use Symfony\AI\Store\Indexer\DocumentProcessor;
26+
use Symfony\Component\Uid\Uuid;
27+
28+
require_once dirname(__DIR__).'/bootstrap.php';
29+
30+
// initialize the store
31+
$store = new Store(sys_get_temp_dir());
32+
33+
// initialize the index
34+
$store->setup();
35+
36+
// create embeddings and documents
37+
$documents = [];
38+
foreach (Movies::all() as $i => $movie) {
39+
$documents[] = new TextDocument(
40+
id: Uuid::v4(),
41+
content: 'Title: '.$movie['title'].\PHP_EOL.'Director: '.$movie['director'].\PHP_EOL.'Description: '.$movie['description'],
42+
metadata: new Metadata($movie),
43+
);
44+
}
45+
46+
// create embeddings for documents
47+
$platform = PlatformFactory::create(env('OPENAI_API_KEY'), http_client());
48+
$vectorizer = new Vectorizer($platform, 'text-embedding-3-small', logger());
49+
$indexer = new DocumentIndexer(new DocumentProcessor($vectorizer, $store, logger: logger()));
50+
$indexer->index($documents);
51+
52+
$similaritySearch = new SimilaritySearch($vectorizer, $store);
53+
$toolbox = new Toolbox([$similaritySearch], logger: logger());
54+
$processor = new AgentProcessor($toolbox);
55+
$agent = new Agent($platform, 'gpt-5-mini', [$processor], [$processor]);
56+
57+
$messages = new MessageBag(
58+
Message::forSystem('Please answer all user questions only using SimilaritySearch function.'),
59+
Message::ofUser('Which movie fits the theme of technology?')
60+
);
61+
$result = $agent->call($messages);
62+
63+
echo $result->getContent().\PHP_EOL;

splitsh.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"ai-supabase-store": "src/store/src/Bridge/Supabase",
9595
"ai-surreal-db-store": "src/store/src/Bridge/SurrealDb",
9696
"ai-typesense-store": "src/store/src/Bridge/Typesense",
97+
"ai-vektor-store": "src/store/src/Bridge/Vektor",
9798
"ai-weaviate-store": "src/store/src/Bridge/Weaviate",
9899
"ai-s3vectors-store": "src/store/src/Bridge/S3Vectors"
99100
}

src/ai-bundle/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"symfony/ai-surreal-db-store": "^0.5",
9595
"symfony/ai-transformers-php-platform": "^0.5",
9696
"symfony/ai-typesense-store": "^0.5",
97+
"symfony/ai-vektor-store": "^0.6",
9798
"symfony/ai-vertex-ai-platform": "^0.5",
9899
"symfony/ai-voyage-platform": "^0.5",
99100
"symfony/ai-weaviate-store": "^0.5",

src/ai-bundle/config/options.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@
365365
->append($import('store/surrealdb'))
366366
->append($import('store/typesense'))
367367
->append($import('store/weaviate'))
368+
->append($import('store/vektor'))
368369
->end()
369370
->end()
370371
->arrayNode('message_store')
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Config\Definition\Configurator;
13+
14+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
15+
16+
return (new ArrayNodeDefinition('vektor'))
17+
->useAttributeAsKey('name')
18+
->arrayPrototype()
19+
->children()
20+
->stringNode('storage_path')
21+
->defaultValue('%kernel.project_dir%/var/share')
22+
->end()
23+
->integerNode('dimensions')
24+
->defaultValue(1536)
25+
->end()
26+
->end()
27+
->end();

src/ai-bundle/src/AiBundle.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
use Symfony\AI\Store\Bridge\Supabase\Store as SupabaseStore;
110110
use Symfony\AI\Store\Bridge\SurrealDb\Store as SurrealDbStore;
111111
use Symfony\AI\Store\Bridge\Typesense\Store as TypesenseStore;
112+
use Symfony\AI\Store\Bridge\Vektor\Store as VektorStore;
112113
use Symfony\AI\Store\Bridge\Weaviate\Store as WeaviateStore;
113114
use Symfony\AI\Store\Distance\DistanceCalculator;
114115
use Symfony\AI\Store\Distance\DistanceStrategy;
@@ -1999,6 +2000,30 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
19992000
$container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $type.'_'.$name);
20002001
}
20012002
}
2003+
2004+
if ('vektor' === $type) {
2005+
if (!ContainerBuilder::willBeAvailable('symfony/ai-vektor-store', VektorStore::class, ['symfony/ai-bundle'])) {
2006+
throw new RuntimeException('Vektor store configuration requires "symfony/ai-vektor-store" package. Try running "composer require symfony/ai-vektor-store".');
2007+
}
2008+
2009+
foreach ($stores as $name => $store) {
2010+
$definition = new Definition(VektorStore::class);
2011+
$definition
2012+
->setLazy(true)
2013+
->setArguments([
2014+
$store['storage_path'],
2015+
$store['dimensions'],
2016+
new Reference('filesystem'),
2017+
])
2018+
->addTag('proxy', ['interface' => StoreInterface::class])
2019+
->addTag('proxy', ['interface' => ManagedStoreInterface::class])
2020+
->addTag('ai.store');
2021+
2022+
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
2023+
$container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name);
2024+
$container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $type.'_'.$name);
2025+
}
2026+
}
20022027
}
20032028

20042029
/**

src/ai-bundle/tests/DependencyInjection/AiBundleTest.php

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
use Symfony\AI\Store\Bridge\Supabase\Store as SupabaseStore;
7272
use Symfony\AI\Store\Bridge\SurrealDb\Store as SurrealDbStore;
7373
use Symfony\AI\Store\Bridge\Typesense\Store as TypesenseStore;
74+
use Symfony\AI\Store\Bridge\Vektor\Store as VektorStore;
7475
use Symfony\AI\Store\Bridge\Weaviate\Store as WeaviateStore;
7576
use Symfony\AI\Store\Distance\DistanceCalculator;
7677
use Symfony\AI\Store\Distance\DistanceStrategy;
@@ -3507,6 +3508,115 @@ public function testWevaviateStoreWithCustomCollectionCanBeConfigured()
35073508
$this->assertTrue($container->hasAlias(StoreInterface::class));
35083509
}
35093510

3511+
public function testVektorStoreCanBeConfigured()
3512+
{
3513+
$container = $this->buildContainer([
3514+
'ai' => [
3515+
'store' => [
3516+
'vektor' => [
3517+
'main' => [],
3518+
],
3519+
],
3520+
],
3521+
]);
3522+
3523+
$this->assertTrue($container->hasDefinition('ai.store.vektor.main'));
3524+
3525+
$definition = $container->getDefinition('ai.store.vektor.main');
3526+
$this->assertSame(VektorStore::class, $definition->getClass());
3527+
$this->assertTrue($definition->isLazy());
3528+
3529+
$this->assertCount(3, $definition->getArguments());
3530+
$this->assertSame('%kernel.project_dir%/var/share', $definition->getArgument(0));
3531+
$this->assertSame(1536, $definition->getArgument(1));
3532+
$this->assertInstanceOf(Reference::class, $definition->getArgument(2));
3533+
$this->assertSame('filesystem', (string) $definition->getArgument(2));
3534+
3535+
$this->assertTrue($definition->hasTag('proxy'));
3536+
$this->assertSame([
3537+
['interface' => StoreInterface::class],
3538+
['interface' => ManagedStoreInterface::class],
3539+
], $definition->getTag('proxy'));
3540+
$this->assertTrue($definition->hasTag('ai.store'));
3541+
3542+
$this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $main'));
3543+
$this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $vektor_main'));
3544+
$this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $vektorMain'));
3545+
$this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface'));
3546+
3547+
$container = $this->buildContainer([
3548+
'ai' => [
3549+
'store' => [
3550+
'vektor' => [
3551+
'main' => [
3552+
'storage_path' => '%kernel.project_dir%/var/share/vektor',
3553+
],
3554+
],
3555+
],
3556+
],
3557+
]);
3558+
3559+
$this->assertTrue($container->hasDefinition('ai.store.vektor.main'));
3560+
3561+
$definition = $container->getDefinition('ai.store.vektor.main');
3562+
$this->assertSame(VektorStore::class, $definition->getClass());
3563+
$this->assertTrue($definition->isLazy());
3564+
3565+
$this->assertCount(3, $definition->getArguments());
3566+
$this->assertSame('%kernel.project_dir%/var/share/vektor', $definition->getArgument(0));
3567+
$this->assertSame(1536, $definition->getArgument(1));
3568+
$this->assertInstanceOf(Reference::class, $definition->getArgument(2));
3569+
$this->assertSame('filesystem', (string) $definition->getArgument(2));
3570+
3571+
$this->assertTrue($definition->hasTag('proxy'));
3572+
$this->assertSame([
3573+
['interface' => StoreInterface::class],
3574+
['interface' => ManagedStoreInterface::class],
3575+
], $definition->getTag('proxy'));
3576+
$this->assertTrue($definition->hasTag('ai.store'));
3577+
3578+
$this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $main'));
3579+
$this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $vektor_main'));
3580+
$this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $vektorMain'));
3581+
$this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface'));
3582+
3583+
$container = $this->buildContainer([
3584+
'ai' => [
3585+
'store' => [
3586+
'vektor' => [
3587+
'main' => [
3588+
'dimensions' => 764,
3589+
],
3590+
],
3591+
],
3592+
],
3593+
]);
3594+
3595+
$this->assertTrue($container->hasDefinition('ai.store.vektor.main'));
3596+
3597+
$definition = $container->getDefinition('ai.store.vektor.main');
3598+
$this->assertSame(VektorStore::class, $definition->getClass());
3599+
$this->assertTrue($definition->isLazy());
3600+
3601+
$this->assertCount(3, $definition->getArguments());
3602+
$this->assertSame('%kernel.project_dir%/var/share', $definition->getArgument(0));
3603+
$this->assertSame(764, $definition->getArgument(1));
3604+
$this->assertInstanceOf(Reference::class, $definition->getArgument(2));
3605+
$this->assertSame('filesystem', (string) $definition->getArgument(2));
3606+
3607+
$this->assertTrue($definition->hasTag('proxy'));
3608+
$this->assertSame([
3609+
['interface' => StoreInterface::class],
3610+
['interface' => ManagedStoreInterface::class],
3611+
], $definition->getTag('proxy'));
3612+
$this->assertTrue($definition->hasTag('ai.store'));
3613+
3614+
$this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $main'));
3615+
$this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $vektor_main'));
3616+
$this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $vektorMain'));
3617+
$this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface'));
3618+
}
3619+
35103620
public function testConfigurationWithUseAttributeAsKeyWorksWithoutNormalizeKeys()
35113621
{
35123622
// Test that configurations using useAttributeAsKey work correctly
@@ -8033,6 +8143,15 @@ private function getFullConfig(): array
80338143
'collection' => 'my_weaviate_collection',
80348144
],
80358145
],
8146+
'vektor' => [
8147+
'my_vektor_store' => [],
8148+
'my_vektor_store_with_custom_path' => [
8149+
'storage_path' => 'foo',
8150+
],
8151+
'my_vektor_store_with_custom_dimensions' => [
8152+
'dimensions' => 764,
8153+
],
8154+
],
80368155
],
80378156
'message_store' => [
80388157
'cache' => [
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/Tests export-ignore
2+
/phpunit.xml.dist export-ignore
3+
/.git* export-ignore

0 commit comments

Comments
 (0)