Skip to content

Commit 9911d50

Browse files
committed
[Store] Add support for Redis
1 parent 4a6f72b commit 9911d50

File tree

10 files changed

+869
-1
lines changed

10 files changed

+869
-1
lines changed

examples/compose.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,10 @@ services:
5656
ports:
5757
- '8108:8108'
5858

59+
redis:
60+
image: redis:8.0.3
61+
ports:
62+
- '6379:6379'
63+
5964
volumes:
6065
typesense_data:

examples/rag/redis.php

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

src/ai-bundle/config/options.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use MongoDB\Client as MongoDbClient;
1616
use Probots\Pinecone\Client as PineconeClient;
1717
use Symfony\AI\Platform\PlatformInterface;
18+
use Symfony\AI\Store\Bridge\Redis\Distance;
1819
use Symfony\AI\Store\StoreInterface;
1920

2021
return static function (DefinitionConfigurator $configurator): void {
@@ -278,6 +279,33 @@
278279
->end()
279280
->end()
280281
->end()
282+
->arrayNode('redis')
283+
->normalizeKeys(false)
284+
->useAttributeAsKey('name')
285+
->arrayPrototype()
286+
->children()
287+
->variableNode('connection_parameters')
288+
->info('see https://github.com/phpredis/phpredis?tab=readme-ov-file#example-1')
289+
->cannotBeEmpty()
290+
->end()
291+
->scalarNode('client')
292+
->info('a service id of a Redis client')
293+
->cannotBeEmpty()
294+
->end()
295+
->scalarNode('index_name')->isRequired()->cannotBeEmpty()->end()
296+
->scalarNode('key_prefix')->defaultValue('vector:')->end()
297+
->enumNode('distance')
298+
->info('Distance metric to use for vector similarity search')
299+
->values(Distance::cases())
300+
->defaultValue(Distance::Cosine)
301+
->end()
302+
->end()
303+
->validate()
304+
->ifTrue(static fn ($v) => !isset($v['connection_parameters']) && !isset($v['client']))
305+
->thenInvalid('Either "connection_parameters" or "client" must be configured.')
306+
->end()
307+
->end()
308+
->end()
281309
->arrayNode('surreal_db')
282310
->normalizeKeys(false)
283311
->useAttributeAsKey('name')

src/ai-bundle/src/AiBundle.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@
2727
use Symfony\AI\AiBundle\Security\Attribute\IsGrantedTool;
2828
use Symfony\AI\Platform\Bridge\Anthropic\PlatformFactory as AnthropicPlatformFactory;
2929
use Symfony\AI\Platform\Bridge\Azure\OpenAi\PlatformFactory as AzureOpenAiPlatformFactory;
30+
use Symfony\AI\Platform\Bridge\Cerebras\PlatformFactory as CerebrasPlatformFactory;
3031
use Symfony\AI\Platform\Bridge\Gemini\PlatformFactory as GeminiPlatformFactory;
3132
use Symfony\AI\Platform\Bridge\LmStudio\PlatformFactory as LmStudioPlatformFactory;
3233
use Symfony\AI\Platform\Bridge\Mistral\PlatformFactory as MistralPlatformFactory;
3334
use Symfony\AI\Platform\Bridge\Ollama\PlatformFactory as OllamaPlatformFactory;
3435
use Symfony\AI\Platform\Bridge\OpenAi\PlatformFactory as OpenAiPlatformFactory;
3536
use Symfony\AI\Platform\Bridge\OpenRouter\PlatformFactory as OpenRouterPlatformFactory;
36-
use Symfony\AI\Platform\Bridge\Cerebras\PlatformFactory as CerebrasPlatformFactory;
3737
use Symfony\AI\Platform\Model;
3838
use Symfony\AI\Platform\ModelClientInterface;
3939
use Symfony\AI\Platform\Platform;
@@ -47,6 +47,7 @@
4747
use Symfony\AI\Store\Bridge\Neo4j\Store as Neo4jStore;
4848
use Symfony\AI\Store\Bridge\Pinecone\Store as PineconeStore;
4949
use Symfony\AI\Store\Bridge\Qdrant\Store as QdrantStore;
50+
use Symfony\AI\Store\Bridge\Redis\Store as RedisStore;
5051
use Symfony\AI\Store\Bridge\SurrealDb\Store as SurrealDbStore;
5152
use Symfony\AI\Store\Bridge\Typesense\Store as TypesenseStore;
5253
use Symfony\AI\Store\CacheStore;
@@ -703,6 +704,32 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
703704
}
704705
}
705706

707+
if ('redis' === $type) {
708+
foreach ($stores as $name => $store) {
709+
if (isset($store['http_client'])) {
710+
$redisClient = new Reference($store['redis_client']);
711+
} else {
712+
$redisClient = new Definition(\Redis::class);
713+
$redisClient->setArguments([$store['connection_parameters']]);
714+
}
715+
716+
$arguments = [
717+
$redisClient,
718+
$store['index_name'],
719+
$store['key_prefix'],
720+
$store['distance'],
721+
];
722+
723+
$definition = new Definition(RedisStore::class);
724+
$definition
725+
->addTag('ai.store')
726+
->setArguments($arguments)
727+
;
728+
729+
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
730+
}
731+
}
732+
706733
if ('surreal_db' === $type) {
707734
foreach ($stores as $name => $store) {
708735
$arguments = [

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,15 @@ private function getFullConfig(): array
272272
'distance' => 'Cosine',
273273
],
274274
],
275+
'redis' => [
276+
'my_redis_store' => [
277+
'connection_parameters' => [
278+
'host' => '1.2.3.4',
279+
'port' => 6379,
280+
],
281+
'index_name' => 'my_vector_index',
282+
],
283+
],
275284
'surreal_db' => [
276285
'my_surreal_db_store' => [
277286
'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
* Add Retrieval Augmented Generation (RAG) support:

src/store/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"symfony/ai-platform": "@dev",
3333
"symfony/clock": "^6.4 || ^7.1",
3434
"symfony/http-client": "^6.4 || ^7.1",
35+
"symfony/polyfill-php83": "^1.32",
3536
"symfony/uid": "^6.4 || ^7.1"
3637
},
3738
"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 <[email protected]>
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 <[email protected]>
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)