Skip to content

Commit 39e2ef3

Browse files
committed
fix
1 parent 14945ab commit 39e2ef3

File tree

14 files changed

+356
-21
lines changed

14 files changed

+356
-21
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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\Chat;
14+
use Symfony\AI\Agent\Chat\MessageStore\InMemoryStore;
15+
use Symfony\AI\Platform\Bridge\OpenAi\Gpt;
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+
$llm = new Gpt(Gpt::GPT_4O_MINI);
24+
25+
$firstAgent = new Agent($platform, $llm, logger: logger());
26+
$secondAgent = new Agent($platform, $llm, logger: logger());
27+
28+
$store = new InMemoryStore();
29+
30+
$firstChat = new Chat($firstAgent, $store);
31+
$secondChat = new Chat($secondAgent, $store);
32+
33+
$firstChat->initiate(new MessageBag(
34+
Message::forSystem('You are a helpful assistant. You only answer with short sentences.'),
35+
));
36+
$secondChat->initiate(new MessageBag(
37+
Message::forSystem('You are a helpful assistant. You only answer with short sentences.'),
38+
));
39+
40+
$firstChat->submit(Message::ofUser('My name is Christopher.'));
41+
$firstChatMessage = $firstChat->submit(Message::ofUser('What is my name?'));
42+
$secondChatMessage = $secondChat->submit(Message::ofUser('What is my name?'));
43+
44+
echo $firstChatMessage->content.\PHP_EOL;
45+
echo $secondChatMessage->content.\PHP_EOL;

src/agent/src/Chat.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,16 @@
1717
use Symfony\AI\Platform\Message\MessageBagInterface;
1818
use Symfony\AI\Platform\Message\UserMessage;
1919
use Symfony\AI\Platform\Result\TextResult;
20+
use Symfony\Component\Uid\AbstractUid;
21+
use Symfony\Component\Uid\TimeBasedUidInterface;
2022

23+
/**
24+
* @author Christopher Hertel <[email protected]>
25+
*/
2126
final readonly class Chat implements ChatInterface
2227
{
28+
private AbstractUid&TimeBasedUidInterface $currentMessageBag;
29+
2330
public function __construct(
2431
private AgentInterface $agent,
2532
private MessageStoreInterface $store,
@@ -30,21 +37,23 @@ public function initiate(MessageBagInterface $messages): void
3037
{
3138
$this->store->clear();
3239
$this->store->save($messages);
40+
41+
$this->currentMessageBag = $messages->getId();
3342
}
3443

3544
public function submit(UserMessage $message): AssistantMessage
3645
{
37-
$messages = $this->store->load();
46+
$messagesBag = $this->store->load($this->currentMessageBag);
3847

39-
$messages->add($message);
40-
$result = $this->agent->call($messages);
48+
$messagesBag->add($message);
49+
$result = $this->agent->call($messagesBag);
4150

4251
\assert($result instanceof TextResult);
4352

4453
$assistantMessage = Message::ofAssistant($result->getContent());
45-
$messages->add($assistantMessage);
54+
$messagesBag->add($assistantMessage);
4655

47-
$this->store->save($messages);
56+
$this->store->save($messagesBag);
4857

4958
return $assistantMessage;
5059
}

src/agent/src/Chat/MessageStore/CacheStore.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
use Symfony\AI\Agent\Exception\RuntimeException;
1717
use Symfony\AI\Platform\Message\MessageBag;
1818
use Symfony\AI\Platform\Message\MessageBagInterface;
19+
use Symfony\Component\Uid\AbstractUid;
20+
use Symfony\Component\Uid\TimeBasedUidInterface;
1921

2022
final readonly class CacheStore implements MessageStoreInterface
2123
{
2224
public function __construct(
2325
private CacheItemPoolInterface $cache,
24-
private string $cacheKey,
2526
private int $ttl = 86400,
2627
) {
2728
if (!interface_exists(CacheItemPoolInterface::class)) {
@@ -31,23 +32,23 @@ public function __construct(
3132

3233
public function save(MessageBagInterface $messages): void
3334
{
34-
$item = $this->cache->getItem($this->cacheKey);
35+
$item = $this->cache->getItem($messages->getId()->toRfc4122());
3536

3637
$item->set($messages);
3738
$item->expiresAfter($this->ttl);
3839

3940
$this->cache->save($item);
4041
}
4142

42-
public function load(): MessageBag
43+
public function load(AbstractUid&TimeBasedUidInterface $id): MessageBagInterface
4344
{
44-
$item = $this->cache->getItem($this->cacheKey);
45+
$item = $this->cache->getItem($id->toRfc4122());
4546

4647
return $item->isHit() ? $item->get() : new MessageBag();
4748
}
4849

4950
public function clear(): void
5051
{
51-
$this->cache->deleteItem($this->cacheKey);
52+
$this->cache->clear();
5253
}
5354
}

src/agent/src/Chat/MessageStore/InMemoryStore.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,28 @@
1414
use Symfony\AI\Agent\Chat\MessageStoreInterface;
1515
use Symfony\AI\Platform\Message\MessageBag;
1616
use Symfony\AI\Platform\Message\MessageBagInterface;
17+
use Symfony\Component\Uid\AbstractUid;
18+
use Symfony\Component\Uid\TimeBasedUidInterface;
1719

1820
final class InMemoryStore implements MessageStoreInterface
1921
{
20-
private MessageBagInterface $messages;
22+
/**
23+
* @var MessageBagInterface[]
24+
*/
25+
private array $messageBags;
2126

2227
public function save(MessageBagInterface $messages): void
2328
{
24-
$this->messages = $messages;
29+
$this->messageBags[$messages->getId()->toRfc4122()] = $messages;
2530
}
2631

27-
public function load(): MessageBagInterface
32+
public function load(AbstractUid&TimeBasedUidInterface $id): MessageBagInterface
2833
{
29-
return $this->messages ?? new MessageBag();
34+
return $this->messageBags[$id->toRfc4122()] ?? new MessageBag();
3035
}
3136

3237
public function clear(): void
3338
{
34-
$this->messages = new MessageBag();
39+
$this->messageBags = [];
3540
}
3641
}

src/agent/src/Chat/MessageStore/SessionStore.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,35 @@
1717
use Symfony\AI\Platform\Message\MessageBagInterface;
1818
use Symfony\Component\HttpFoundation\RequestStack;
1919
use Symfony\Component\HttpFoundation\Session\SessionInterface;
20+
use Symfony\Component\Uid\AbstractUid;
21+
use Symfony\Component\Uid\TimeBasedUidInterface;
2022

2123
final readonly class SessionStore implements MessageStoreInterface
2224
{
2325
private SessionInterface $session;
2426

2527
public function __construct(
2628
RequestStack $requestStack,
27-
private string $sessionKey = 'messages',
2829
) {
2930
if (!class_exists(RequestStack::class)) {
3031
throw new RuntimeException('For using the SessionStore as message store, the symfony/http-foundation package is required. Try running "composer require symfony/http-foundation".');
3132
}
33+
3234
$this->session = $requestStack->getSession();
3335
}
3436

3537
public function save(MessageBagInterface $messages): void
3638
{
37-
$this->session->set($this->sessionKey, $messages);
39+
$this->session->set($messages->getId()->toRfc4122(), $messages);
3840
}
3941

40-
public function load(): MessageBagInterface
42+
public function load(AbstractUid&TimeBasedUidInterface $id): MessageBagInterface
4143
{
42-
return $this->session->get($this->sessionKey, new MessageBag());
44+
return $this->session->get($id->toRfc4122(), new MessageBag());
4345
}
4446

4547
public function clear(): void
4648
{
47-
$this->session->remove($this->sessionKey);
49+
$this->session->clear();
4850
}
4951
}

src/agent/src/Chat/MessageStoreInterface.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
namespace Symfony\AI\Agent\Chat;
1313

1414
use Symfony\AI\Platform\Message\MessageBagInterface;
15+
use Symfony\Component\Uid\AbstractUid;
16+
use Symfony\Component\Uid\TimeBasedUidInterface;
1517

1618
interface MessageStoreInterface
1719
{
1820
public function save(MessageBagInterface $messages): void;
1921

20-
public function load(): MessageBagInterface;
22+
public function load(AbstractUid&TimeBasedUidInterface $id): MessageBagInterface;
2123

2224
public function clear(): void;
2325
}

src/agent/tests/AgentTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
use Symfony\AI\Agent\Agent;
2020
use Symfony\AI\Agent\AgentAwareInterface;
2121
use Symfony\AI\Agent\AgentInterface;
22+
use Symfony\AI\Agent\Chat;
23+
use Symfony\AI\Agent\Chat\MessageStore\InMemoryStore;
2224
use Symfony\AI\Agent\Exception\InvalidArgumentException;
2325
use Symfony\AI\Agent\Exception\MissingModelSupportException;
2426
use Symfony\AI\Agent\Exception\RuntimeException;
@@ -30,6 +32,7 @@
3032
use Symfony\AI\Platform\Message\Content\Audio;
3133
use Symfony\AI\Platform\Message\Content\Image;
3234
use Symfony\AI\Platform\Message\Content\Text;
35+
use Symfony\AI\Platform\Message\Message;
3336
use Symfony\AI\Platform\Message\MessageBag;
3437
use Symfony\AI\Platform\Message\UserMessage;
3538
use Symfony\AI\Platform\Model;
@@ -49,6 +52,8 @@
4952
#[UsesClass(Text::class)]
5053
#[UsesClass(Audio::class)]
5154
#[UsesClass(Image::class)]
55+
#[UsesClass(InMemoryStore::class)]
56+
#[UsesClass(Chat::class)]
5257
#[Small]
5358
final class AgentTest extends TestCase
5459
{
@@ -395,4 +400,32 @@ public function testConstructorAcceptsTraversableProcessors()
395400

396401
$this->assertInstanceOf(AgentInterface::class, $agent);
397402
}
403+
404+
public function testDoubleAgentCanUseSameMessageStore()
405+
{
406+
$platform = $this->createMock(PlatformInterface::class);
407+
$model = $this->createMock(Model::class);
408+
409+
$firstAgent = new Agent($platform, $model);
410+
$secondAgent = new Agent($platform, $model);
411+
412+
$store = new InMemoryStore();
413+
414+
$firstChat = new Chat($firstAgent, $store);
415+
$secondChat = new Chat($secondAgent, $store);
416+
417+
$messages = new MessageBag(
418+
Message::forSystem('You are a helpful assistant. You only answer with short sentences.'),
419+
);
420+
421+
$firstChat->initiate($messages);
422+
$firstChat->submit(new UserMessage(new Text('Hello')));
423+
424+
$secondChat->initiate($messages);
425+
$secondChat->submit(new UserMessage(new Text('Hello')));
426+
$secondChat->submit(new UserMessage(new Text('Hello there')));
427+
428+
$this->assertCount(1, $store->load($firstAgent->getId()));
429+
$this->assertCount(2, $store->load($secondAgent->getId()));
430+
}
398431
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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\Agent\Tests\Chat;
13+
14+
use PHPUnit\Framework\Attributes\CoversClass;
15+
use PHPUnit\Framework\Attributes\UsesClass;
16+
use PHPUnit\Framework\TestCase;
17+
use Symfony\AI\Agent\Chat\MessageStore\InMemoryStore;
18+
use Symfony\AI\Platform\Message\Message;
19+
use Symfony\AI\Platform\Message\MessageBag;
20+
21+
#[CoversClass(InMemoryStore::class)]
22+
#[UsesClass(MessageBag::class)]
23+
#[UsesClass(Message::class)]
24+
final class InMemoryStoreTest extends TestCase
25+
{
26+
public function testItCanStore()
27+
{
28+
$messageBag = new MessageBag();
29+
$messageBag->add(Message::ofUser('Hello'));
30+
31+
$store = new InMemoryStore();
32+
$store->save($messageBag);
33+
34+
$this->assertCount(1, $store->load($messageBag->getId()));
35+
}
36+
37+
public function testItCanStoreMultipleMessageBags()
38+
{
39+
$firstMessageBag = new MessageBag();
40+
$firstMessageBag->add(Message::ofUser('Hello'));
41+
42+
$secondMessageBag = new MessageBag();
43+
44+
$store = new InMemoryStore();
45+
$store->save($firstMessageBag);
46+
$store->save($secondMessageBag);
47+
48+
$this->assertCount(1, $store->load($firstMessageBag->getId()));
49+
$this->assertCount(0, $store->load($secondMessageBag->getId()));
50+
}
51+
52+
public function testItCanClear()
53+
{
54+
$firstMessageBag = new MessageBag();
55+
$firstMessageBag->add(Message::ofUser('Hello'));
56+
57+
$secondMessageBag = new MessageBag();
58+
59+
$store = new InMemoryStore();
60+
$store->save($firstMessageBag);
61+
$store->save($secondMessageBag);
62+
63+
$this->assertCount(1, $store->load($firstMessageBag->getId()));
64+
$this->assertCount(0, $store->load($secondMessageBag->getId()));
65+
66+
$store->clear();
67+
68+
$this->assertCount(0, $store->load($firstMessageBag->getId()));
69+
$this->assertCount(0, $store->load($secondMessageBag->getId()));
70+
}
71+
}

src/ai-bundle/config/options.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,33 @@
359359
->end()
360360
->end()
361361
->end()
362+
->arrayNode('message_store')
363+
->children()
364+
->arrayNode('cache')
365+
->normalizeKeys(false)
366+
->useAttributeAsKey('name')
367+
->arrayPrototype()
368+
->children()
369+
->scalarNode('service')->cannotBeEmpty()->defaultValue('cache.app')->end()
370+
->end()
371+
->end()
372+
->end()
373+
->end()
374+
->end()
375+
->arrayNode('chat')
376+
->normalizeKeys(false)
377+
->useAttributeAsKey('name')
378+
->arrayPrototype()
379+
->children()
380+
->scalarNode('agent')
381+
->info('Name of the agent used for the chat')
382+
->end()
383+
->scalarNode('message_store')
384+
->info('Name of the message store')
385+
->end()
386+
->end()
387+
->end()
388+
->end()
362389
->end()
363390
;
364391
};

0 commit comments

Comments
 (0)