Skip to content

Commit d06d642

Browse files
committed
feature #907 [Chat] Introduce MongoDb as message store (Guikingone)
This PR was squashed before being merged into the main branch. Discussion ---------- [Chat] Introduce `MongoDb` as message store | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Docs? | yes | Issues | #16 | License | MIT Commits ------- e2ab577 [Chat] Introduce `MongoDb` as message store
2 parents 1165a70 + e2ab577 commit d06d642

File tree

13 files changed

+368
-1
lines changed

13 files changed

+368
-1
lines changed

docs/components/chat.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ You can find more advanced usage in combination with an Agent using the store fo
3838
* `Current session context storage with HttpFoundation session`_
3939
* `Current process context storage with InMemory`_
4040
* `Long-term context with Meilisearch`_
41+
* `Long-term context with MongoDb`_
4142
* `Long-term context with Pogocache`_
4243
* `Long-term context with Redis`_
4344
* `Long-term context with SurrealDb`_
@@ -50,6 +51,7 @@ Supported Message stores
5051
* `HttpFoundation session`_
5152
* `InMemory`_
5253
* `Meilisearch`_
54+
* `MongoDb`_
5355
* `Pogocache`_
5456
* `Redis`_
5557
* `SurrealDb`_
@@ -133,6 +135,7 @@ store and ``bin/console ai:message-store:drop`` to clean up the message store:
133135
.. _`Current session context storage with HttpFoundation session`: https://github.com/symfony/ai/blob/main/examples/chat/persistent-chat-session.php
134136
.. _`Current process context storage with InMemory`: https://github.com/symfony/ai/blob/main/examples/chat/persistent-chat.php
135137
.. _`Long-term context with Meilisearch`: https://github.com/symfony/ai/blob/main/examples/chat/persistent-chat-meilisearch.php
138+
.. _`Long-term context with MongoDb`: https://github.com/symfony/ai/blob/main/examples/chat/persistent-chat-mongodb.php
136139
.. _`Long-term context with Pogocache`: https://github.com/symfony/ai/blob/main/examples/chat/persistent-chat-pogocache.php
137140
.. _`Long-term context with Redis`: https://github.com/symfony/ai/blob/main/examples/chat/persistent-chat-redis.php
138141
.. _`Long-term context with SurrealDb`: https://github.com/symfony/ai/blob/main/examples/chat/persistent-chat-surrealdb.php
@@ -141,6 +144,7 @@ store and ``bin/console ai:message-store:drop`` to clean up the message store:
141144
.. _`InMemory`: https://www.php.net/manual/en/language.types.array.php
142145
.. _`HttpFoundation session`: https://developers.cloudflare.com/vectorize/
143146
.. _`Meilisearch`: https://www.meilisearch.com/
147+
.. _`MongoDb`: https://www.mongodb.com/
144148
.. _`Pogocache`: https://pogocache.com/
145149
.. _`Redis`: https://redis.io/
146150
.. _`SurrealDb`: https://surrealdb.com/
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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 MongoDB\Client as MongoDbClient;
13+
use Symfony\AI\Agent\Agent;
14+
use Symfony\AI\Chat\Bridge\MongoDb\MessageStore;
15+
use Symfony\AI\Chat\Chat;
16+
use Symfony\AI\Platform\Bridge\OpenAi\PlatformFactory;
17+
use Symfony\AI\Platform\Message\Message;
18+
use Symfony\AI\Platform\Message\MessageBag;
19+
20+
require_once dirname(__DIR__).'/bootstrap.php';
21+
22+
$platform = PlatformFactory::create(env('OPENAI_API_KEY'), http_client());
23+
24+
$store = new MessageStore(
25+
new MongoDbClient(env('MONGODB_URI')),
26+
'chat',
27+
'symfony',
28+
);
29+
$store->setup();
30+
31+
$agent = new Agent($platform, 'gpt-4o-mini');
32+
$chat = new Chat($agent, $store);
33+
34+
$messages = new MessageBag(
35+
Message::forSystem('You are a helpful assistant. You only answer with short sentences.'),
36+
);
37+
38+
$chat->initiate($messages);
39+
$chat->submit(Message::ofUser('My name is Christopher.'));
40+
$message = $chat->submit(Message::ofUser('What is my name?'));
41+
42+
echo $message->getContent().\PHP_EOL;

examples/commands/message-stores.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
require_once dirname(__DIR__).'/bootstrap.php';
1313

1414
use Doctrine\DBAL\DriverManager;
15+
use MongoDB\Client as MongoDbClient;
1516
use Symfony\AI\Chat\Bridge\Doctrine\DoctrineDbalMessageStore;
1617
use Symfony\AI\Chat\Bridge\HttpFoundation\SessionStore;
1718
use Symfony\AI\Chat\Bridge\Local\CacheStore;
1819
use Symfony\AI\Chat\Bridge\Local\InMemoryStore;
1920
use Symfony\AI\Chat\Bridge\Meilisearch\MessageStore as MeilisearchMessageStore;
21+
use Symfony\AI\Chat\Bridge\MongoDb\MessageStore as MongoDbMessageStore;
2022
use Symfony\AI\Chat\Bridge\Pogocache\MessageStore as PogocacheMessageStore;
2123
use Symfony\AI\Chat\Bridge\Redis\MessageStore as RedisMessageStore;
2224
use Symfony\AI\Chat\Bridge\SurrealDb\MessageStore as SurrealDbMessageStore;
@@ -51,6 +53,11 @@
5153
'symfony',
5254
),
5355
'memory' => static fn (): InMemoryStore => new InMemoryStore('symfony'),
56+
'mongodb' => static fn (): MongoDbMessageStore => new MongoDbMessageStore(
57+
new MongoDbClient(env('MONGODB_URI')),
58+
'chat',
59+
'symfony',
60+
),
5461
'pogocache' => static fn (): PogocacheMessageStore => new PogocacheMessageStore(
5562
http_client(),
5663
env('POGOCACHE_HOST'),

examples/composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
"phpstan/phpstan": "^2.1",
4141
"phpstan/phpstan-strict-rules": "^2.0"
4242
},
43+
"conflict": {
44+
"mongodb/mongodb": "<1.21"
45+
},
4346
"minimum-stability": "dev",
4447
"autoload": {
4548
"psr-4": {

src/ai-bundle/composer.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,17 @@
2828
},
2929
"require-dev": {
3030
"google/auth": "^1.47",
31+
"mongodb/mongodb": "^1.21 || ^2.0",
3132
"phpstan/phpstan": "^2.1",
3233
"phpstan/phpstan-strict-rules": "^2.0",
3334
"phpunit/phpunit": "^11.5",
3435
"symfony/expression-language": "^7.3|^8.0",
3536
"symfony/security-core": "^7.3|^8.0",
3637
"symfony/translation": "^7.3|^8.0"
3738
},
39+
"conflict": {
40+
"mongodb/mongodb": "<1.21"
41+
},
3842
"autoload": {
3943
"psr-4": {
4044
"Symfony\\AI\\AiBundle\\": "src/"

src/ai-bundle/config/options.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,19 @@
862862
->end()
863863
->end()
864864
->end()
865+
->arrayNode('mongodb')
866+
->useAttributeAsKey('name')
867+
->arrayPrototype()
868+
->children()
869+
->stringNode('client')
870+
->cannotBeEmpty()
871+
->defaultValue(MongoDbClient::class)
872+
->end()
873+
->stringNode('database')->isRequired()->end()
874+
->stringNode('collection')->isRequired()->end()
875+
->end()
876+
->end()
877+
->end()
865878
->arrayNode('pogocache')
866879
->useAttributeAsKey('name')
867880
->arrayPrototype()

src/ai-bundle/src/AiBundle.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
use Symfony\AI\Chat\Bridge\HttpFoundation\SessionStore;
4141
use Symfony\AI\Chat\Bridge\Local\CacheStore as CacheMessageStore;
4242
use Symfony\AI\Chat\Bridge\Meilisearch\MessageStore as MeilisearchMessageStore;
43+
use Symfony\AI\Chat\Bridge\MongoDb\MessageStore as MongoDbMessageStore;
4344
use Symfony\AI\Chat\Bridge\Pogocache\MessageStore as PogocacheMessageStore;
4445
use Symfony\AI\Chat\Bridge\Redis\MessageStore as RedisMessageStore;
4546
use Symfony\AI\Chat\Bridge\SurrealDb\MessageStore as SurrealDbMessageStore;
@@ -1604,6 +1605,26 @@ private function processMessageStoreConfig(string $type, array $messageStores, C
16041605
}
16051606
}
16061607

1608+
if ('mongodb' === $type) {
1609+
foreach ($messageStores as $name => $messageStore) {
1610+
$definition = new Definition(MongoDbMessageStore::class);
1611+
$definition
1612+
->setLazy(true)
1613+
->setArguments([
1614+
new Reference($messageStore['client']),
1615+
$messageStore['database'],
1616+
$messageStore['collection'],
1617+
new Reference('serializer'),
1618+
])
1619+
->addTag('proxy', ['interface' => MessageStoreInterface::class])
1620+
->addTag('ai.message_store');
1621+
1622+
$container->setDefinition('ai.message_store.'.$type.'.'.$name, $definition);
1623+
$container->registerAliasForArgument('ai.message_store.'.$type.'.'.$name, StoreInterface::class, $name);
1624+
$container->registerAliasForArgument('ai.message_store.'.$type.'.'.$name, StoreInterface::class, $type.'_'.$name);
1625+
}
1626+
}
1627+
16071628
if ('pogocache' === $type) {
16081629
foreach ($messageStores as $name => $messageStore) {
16091630
$definition = new Definition(PogocacheMessageStore::class);

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

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\AI\AiBundle\Tests\DependencyInjection;
1313

14+
use MongoDB\Client as MongoDbClient;
1415
use PHPUnit\Framework\Attributes\DoesNotPerformAssertions;
1516
use PHPUnit\Framework\Attributes\TestDox;
1617
use PHPUnit\Framework\Attributes\TestWith;
@@ -3087,6 +3088,69 @@ public function testMemoryMessageStoreCanBeConfiguredWithCustomKey()
30873088
$this->assertTrue($memoryMessageStoreDefinition->hasTag('ai.message_store'));
30883089
}
30893090

3091+
public function testMongoDbMessageStoreIsConfigured()
3092+
{
3093+
$container = $this->buildContainer([
3094+
'ai' => [
3095+
'message_store' => [
3096+
'mongodb' => [
3097+
'custom' => [
3098+
'collection' => 'foo',
3099+
'database' => 'bar',
3100+
],
3101+
],
3102+
],
3103+
],
3104+
]);
3105+
3106+
$mongoDbMessageStoreDefinition = $container->getDefinition('ai.message_store.mongodb.custom');
3107+
3108+
$this->assertTrue($mongoDbMessageStoreDefinition->isLazy());
3109+
$this->assertCount(4, $mongoDbMessageStoreDefinition->getArguments());
3110+
$this->assertInstanceOf(Reference::class, $mongoDbMessageStoreDefinition->getArgument(0));
3111+
$this->assertSame(MongoDbClient::class, (string) $mongoDbMessageStoreDefinition->getArgument(0));
3112+
$this->assertSame('bar', $mongoDbMessageStoreDefinition->getArgument(1));
3113+
$this->assertSame('foo', $mongoDbMessageStoreDefinition->getArgument(2));
3114+
$this->assertInstanceOf(Reference::class, $mongoDbMessageStoreDefinition->getArgument(3));
3115+
$this->assertSame('serializer', (string) $mongoDbMessageStoreDefinition->getArgument(3));
3116+
3117+
$this->assertTrue($mongoDbMessageStoreDefinition->hasTag('proxy'));
3118+
$this->assertSame([['interface' => MessageStoreInterface::class]], $mongoDbMessageStoreDefinition->getTag('proxy'));
3119+
$this->assertTrue($mongoDbMessageStoreDefinition->hasTag('ai.message_store'));
3120+
}
3121+
3122+
public function testMongoDbMessageStoreIsConfiguredWithCustomClient()
3123+
{
3124+
$container = $this->buildContainer([
3125+
'ai' => [
3126+
'message_store' => [
3127+
'mongodb' => [
3128+
'custom' => [
3129+
'client' => 'custom',
3130+
'collection' => 'foo',
3131+
'database' => 'bar',
3132+
],
3133+
],
3134+
],
3135+
],
3136+
]);
3137+
3138+
$mongoDbMessageStoreDefinition = $container->getDefinition('ai.message_store.mongodb.custom');
3139+
3140+
$this->assertTrue($mongoDbMessageStoreDefinition->isLazy());
3141+
$this->assertCount(4, $mongoDbMessageStoreDefinition->getArguments());
3142+
$this->assertInstanceOf(Reference::class, $mongoDbMessageStoreDefinition->getArgument(0));
3143+
$this->assertSame('custom', (string) $mongoDbMessageStoreDefinition->getArgument(0));
3144+
$this->assertSame('bar', $mongoDbMessageStoreDefinition->getArgument(1));
3145+
$this->assertSame('foo', $mongoDbMessageStoreDefinition->getArgument(2));
3146+
$this->assertInstanceOf(Reference::class, $mongoDbMessageStoreDefinition->getArgument(3));
3147+
$this->assertSame('serializer', (string) $mongoDbMessageStoreDefinition->getArgument(3));
3148+
3149+
$this->assertTrue($mongoDbMessageStoreDefinition->hasTag('proxy'));
3150+
$this->assertSame([['interface' => MessageStoreInterface::class]], $mongoDbMessageStoreDefinition->getTag('proxy'));
3151+
$this->assertTrue($mongoDbMessageStoreDefinition->hasTag('ai.message_store'));
3152+
}
3153+
30903154
public function testPogocacheMessageStoreIsConfigured()
30913155
{
30923156
$container = $this->buildContainer([
@@ -3812,6 +3876,17 @@ private function getFullConfig(): array
38123876
'index_name' => 'test',
38133877
],
38143878
],
3879+
'mongodb' => [
3880+
'my_mongo_db_message_store' => [
3881+
'collection' => 'foo',
3882+
'database' => 'bar',
3883+
],
3884+
'my_mongo_db_message_store_with_custom_client' => [
3885+
'client' => 'custom',
3886+
'collection' => 'foo',
3887+
'database' => 'bar',
3888+
],
3889+
],
38153890
'pogocache' => [
38163891
'my_pogocache_message_store' => [
38173892
'endpoint' => 'http://127.0.0.1:9401',

src/chat/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ CHANGELOG
66

77
* Introduce the component
88
* Add support for external message stores:
9+
- Doctrine
910
- Meilisearch
11+
- MongoDb
1012
- Pogocache
1113
- Redis
1214
- SurrealDb

src/chat/composer.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"require-dev": {
2828
"ext-redis": "*",
2929
"doctrine/dbal": "^3.3 || ^4.0",
30+
"mongodb/mongodb": "^1.21 || ^2.0",
3031
"phpstan/phpstan": "^2.0",
3132
"phpstan/phpstan-strict-rules": "^2.0",
3233
"phpunit/phpunit": "^11.5.13",
@@ -37,6 +38,9 @@
3738
"symfony/http-foundation": "^7.3|^8.0",
3839
"symfony/serializer": "^7.3|^8.0"
3940
},
41+
"conflict": {
42+
"mongodb/mongodb": "<1.21"
43+
},
4044
"suggest": {
4145
"symfony/clock": "To use stores that requires waiting for actions like Meilisearch"
4246
},

0 commit comments

Comments
 (0)