Skip to content

Commit facd849

Browse files
committed
[Store] Add support for Redis
1 parent d742c66 commit facd849

File tree

10 files changed

+884
-1
lines changed

10 files changed

+884
-1
lines changed

examples/compose.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ services:
126126
ports:
127127
- '6333:6333'
128128

129+
redis:
130+
image: redis:8.0.3
131+
ports:
132+
- '6379:6379'
133+
129134
surrealdb:
130135
image: surrealdb/surrealdb:v2
131136
command: [ 'start', '--user', 'symfony', '--pass', 'symfony' ]

examples/rag/redis.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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\Toolbox\AgentProcessor;
14+
use Symfony\AI\Agent\Toolbox\Tool\SimilaritySearch;
15+
use Symfony\AI\Agent\Toolbox\Toolbox;
16+
use Symfony\AI\Fixtures\Movies;
17+
use Symfony\AI\Platform\Bridge\OpenAi\Embeddings;
18+
use Symfony\AI\Platform\Bridge\OpenAi\Gpt;
19+
use Symfony\AI\Platform\Bridge\OpenAi\PlatformFactory;
20+
use Symfony\AI\Platform\Message\Message;
21+
use Symfony\AI\Platform\Message\MessageBag;
22+
use Symfony\AI\Store\Bridge\Redis\Store;
23+
use Symfony\AI\Store\Document\Loader\InMemoryLoader;
24+
use Symfony\AI\Store\Document\Metadata;
25+
use Symfony\AI\Store\Document\TextDocument;
26+
use Symfony\AI\Store\Document\Vectorizer;
27+
use Symfony\AI\Store\Indexer;
28+
use Symfony\Component\Uid\Uuid;
29+
30+
require_once dirname(__DIR__).'/bootstrap.php';
31+
32+
// initialize the store
33+
$redis = new Redis([
34+
'host' => 'localhost',
35+
'port' => 6379,
36+
]);
37+
$store = new Store(
38+
redis: $redis,
39+
indexName: 'my_index',
40+
);
41+
42+
// create embeddings and documents
43+
$documents = [];
44+
foreach (Movies::all() as $i => $movie) {
45+
$documents[] = new TextDocument(
46+
id: Uuid::v4(),
47+
content: 'Title: '.$movie['title'].\PHP_EOL.'Director: '.$movie['director'].\PHP_EOL.'Description: '.$movie['description'],
48+
metadata: new Metadata($movie),
49+
);
50+
}
51+
52+
// initialize the table
53+
$store->setup(['vector_size' => 1536]);
54+
55+
// create embeddings for documents
56+
$platform = PlatformFactory::create(env('OPENAI_API_KEY'), http_client());
57+
$vectorizer = new Vectorizer($platform, $embeddings = new Embeddings(Embeddings::TEXT_3_SMALL), logger());
58+
$indexer = new Indexer(new InMemoryLoader($documents), $vectorizer, $store, logger: logger());
59+
$indexer->index($documents);
60+
61+
$model = new Gpt(Gpt::GPT_4O_MINI);
62+
63+
$similaritySearch = new SimilaritySearch($vectorizer, $store);
64+
$toolbox = new Toolbox([$similaritySearch], logger: logger());
65+
$processor = new AgentProcessor($toolbox);
66+
$agent = new Agent($platform, $model, [$processor], [$processor], logger: logger());
67+
68+
$messages = new MessageBag(
69+
Message::forSystem('Please answer all user questions only using SimilaritySearch function.'),
70+
Message::ofUser('Which movie fits the theme of technology?')
71+
);
72+
$result = $agent->call($messages);
73+
74+
echo $result->getContent().\PHP_EOL;
75+
76+
$store->drop();

src/ai-bundle/config/options.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\AI\Platform\Bridge\OpenAi\PlatformFactory;
1818
use Symfony\AI\Platform\Model;
1919
use Symfony\AI\Platform\PlatformInterface;
20+
use Symfony\AI\Store\Bridge\Redis\Distance;
2021
use Symfony\AI\Store\Document\VectorizerInterface;
2122
use Symfony\AI\Store\StoreInterface;
2223
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -470,6 +471,36 @@
470471
->end()
471472
->end()
472473
->end()
474+
->arrayNode('redis')
475+
->useAttributeAsKey('name')
476+
->arrayPrototype()
477+
->children()
478+
->variableNode('connection_parameters')
479+
->info('see https://github.com/phpredis/phpredis?tab=readme-ov-file#example-1')
480+
->cannotBeEmpty()
481+
->end()
482+
->stringNode('client')
483+
->info('a service id of a Redis client')
484+
->cannotBeEmpty()
485+
->end()
486+
->stringNode('index_name')->isRequired()->cannotBeEmpty()->end()
487+
->stringNode('key_prefix')->defaultValue('vector:')->end()
488+
->enumNode('distance')
489+
->info('Distance metric to use for vector similarity search')
490+
->values(Distance::cases())
491+
->defaultValue(Distance::Cosine)
492+
->end()
493+
->end()
494+
->validate()
495+
->ifTrue(static fn ($v) => !isset($v['connection_parameters']) && !isset($v['client']))
496+
->thenInvalid('Either "connection_parameters" or "client" must be configured.')
497+
->end()
498+
->validate()
499+
->ifTrue(static fn ($v) => isset($v['connection_parameters']) && isset($v['client']))
500+
->thenInvalid('Either "connection_parameters" or "client" can be configured, but not both.')
501+
->end()
502+
->end()
503+
->end()
473504
->arrayNode('surreal_db')
474505
->useAttributeAsKey('name')
475506
->arrayPrototype()

src/ai-bundle/src/AiBundle.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
use Symfony\AI\Platform\Bridge\VertexAi\PlatformFactory as VertexAiPlatformFactory;
4848
use Symfony\AI\Platform\Bridge\Voyage\PlatformFactory as VoyagePlatformFactory;
4949
use Symfony\AI\Platform\Exception\RuntimeException;
50-
use Symfony\AI\Platform\Model;
5150
use Symfony\AI\Platform\ModelClientInterface;
5251
use Symfony\AI\Platform\Platform;
5352
use Symfony\AI\Platform\PlatformInterface;
@@ -66,6 +65,7 @@
6665
use Symfony\AI\Store\Bridge\Neo4j\Store as Neo4jStore;
6766
use Symfony\AI\Store\Bridge\Pinecone\Store as PineconeStore;
6867
use Symfony\AI\Store\Bridge\Qdrant\Store as QdrantStore;
68+
use Symfony\AI\Store\Bridge\Redis\Store as RedisStore;
6969
use Symfony\AI\Store\Bridge\SurrealDb\Store as SurrealDbStore;
7070
use Symfony\AI\Store\Bridge\Typesense\Store as TypesenseStore;
7171
use Symfony\AI\Store\Bridge\Weaviate\Store as WeaviateStore;
@@ -1010,6 +1010,32 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
10101010
}
10111011
}
10121012

1013+
if ('redis' === $type) {
1014+
foreach ($stores as $name => $store) {
1015+
if (isset($store['client'])) {
1016+
$redisClient = new Reference($store['client']);
1017+
} else {
1018+
$redisClient = new Definition(\Redis::class);
1019+
$redisClient->setArguments([$store['connection_parameters']]);
1020+
}
1021+
1022+
$arguments = [
1023+
$redisClient,
1024+
$store['index_name'],
1025+
$store['key_prefix'],
1026+
$store['distance'],
1027+
];
1028+
1029+
$definition = new Definition(RedisStore::class);
1030+
$definition
1031+
->addTag('ai.store')
1032+
->setArguments($arguments)
1033+
;
1034+
1035+
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
1036+
}
1037+
}
1038+
10131039
if ('surreal_db' === $type) {
10141040
foreach ($stores as $name => $store) {
10151041
$arguments = [

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2167,6 +2167,15 @@ private function getFullConfig(): array
21672167
'distance' => 'Cosine',
21682168
],
21692169
],
2170+
'redis' => [
2171+
'my_redis_store' => [
2172+
'connection_parameters' => [
2173+
'host' => '1.2.3.4',
2174+
'port' => 6379,
2175+
],
2176+
'index_name' => 'my_vector_index',
2177+
],
2178+
],
21702179
'surreal_db' => [
21712180
'my_surreal_db_store' => [
21722181
'endpoint' => 'http://127.0.0.1:8000',

src/store/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ CHANGELOG
4545
- Pinecone
4646
- PostgreSQL with pgvector extension
4747
- Qdrant
48+
- Redis
4849
- SurrealDB
4950
- Typesense
5051
- Weaviate

src/store/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"symfony/ai-platform": "@dev",
3939
"symfony/clock": "^7.3|^8.0",
4040
"symfony/http-client": "^7.3|^8.0",
41+
"symfony/polyfill-php83": "^1.32",
4142
"symfony/uid": "^7.3|^8.0"
4243
},
4344
"require-dev": {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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\AI\Store\Bridge\Redis;
13+
14+
use OskarStark\Enum\Trait\Comparable;
15+
16+
/**
17+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
18+
*/
19+
enum Distance: string
20+
{
21+
use Comparable;
22+
23+
case Cosine = 'COSINE';
24+
case L2 = 'L2';
25+
case Ip = 'IP';
26+
27+
public function getRedisMetric(): string
28+
{
29+
return $this->value;
30+
}
31+
}

0 commit comments

Comments
 (0)