Skip to content

Commit 72ad069

Browse files
Marco van AngerenMarco van Angeren
authored andcommitted
Added streaming support to Chat API & persistence to storage through accumulator callback chaining
1 parent 5c0268a commit 72ad069

File tree

16 files changed

+295
-21
lines changed

16 files changed

+295
-21
lines changed

src/agent/src/Toolbox/StreamResult.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@ public function getContent(): \Generator
3333
if ($value instanceof ToolCallResult) {
3434
$innerResult = ($this->handleToolCallsCallback)($value, Message::ofAssistant($streamedResult));
3535

36-
// Propagate metadata from inner result to this result
37-
foreach ($innerResult->getMetadata()->all() as $key => $metadataValue) {
38-
$this->getMetadata()->add($key, $metadataValue);
39-
}
36+
$this->getMetadata()->set($innerResult->getMetadata()->all());
4037

4138
$content = $innerResult->getContent();
4239
// Strings are iterable in PHP but yield from would iterate character-by-character.

src/chat/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
CHANGELOG
22
=========
3+
0.2
4+
* Add streaming support to `ChatInterface::submit()`
5+
- Add `StreamableStoreInterface` which indicates `StoreInterface` implementation can be configured with streaming
6+
- Add `AccumulatingStreamResult` wrapper class which adds accumulation logic & callback chaining to `StreamResult` implementations (can wrap both `Agent` and `Platform` variants) to return the full message once `Generator` is exhausted
7+
- Streamed responses now also create `AssistantMessage` & are added to `Store` in `Chat::submit()`
8+
- Bugfixed loss of metadata in `Chat::submit()`
39

410
0.1
511
---

src/chat/src/Bridge/Doctrine/DoctrineDbalMessageStore.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Symfony\AI\Chat\ManagedStoreInterface;
2525
use Symfony\AI\Chat\MessageNormalizer;
2626
use Symfony\AI\Chat\MessageStoreInterface;
27+
use Symfony\AI\Chat\StreamableStoreInterface;
2728
use Symfony\AI\Platform\Message\MessageBag;
2829
use Symfony\AI\Platform\Message\MessageInterface;
2930
use Symfony\Component\Serializer\Encoder\JsonEncoder;
@@ -34,7 +35,7 @@
3435
/**
3536
* @author Guillaume Loulier <personal@guillaumeloulier.fr>
3637
*/
37-
final class DoctrineDbalMessageStore implements ManagedStoreInterface, MessageStoreInterface
38+
final class DoctrineDbalMessageStore implements ManagedStoreInterface, MessageStoreInterface, StreamableStoreInterface
3839
{
3940
public function __construct(
4041
private readonly string $tableName,

src/chat/src/Bridge/Local/CacheStore.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515
use Symfony\AI\Agent\Exception\RuntimeException;
1616
use Symfony\AI\Chat\ManagedStoreInterface;
1717
use Symfony\AI\Chat\MessageStoreInterface;
18+
use Symfony\AI\Chat\StreamableStoreInterface;
1819
use Symfony\AI\Platform\Message\MessageBag;
1920

2021
/**
2122
* @author Christopher Hertel <mail@christopher-hertel.de>
2223
*/
23-
final class CacheStore implements ManagedStoreInterface, MessageStoreInterface
24+
final class CacheStore implements ManagedStoreInterface, MessageStoreInterface, StreamableStoreInterface
2425
{
2526
public function __construct(
2627
private readonly CacheItemPoolInterface $cache,

src/chat/src/Bridge/Local/InMemoryStore.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313

1414
use Symfony\AI\Chat\ManagedStoreInterface;
1515
use Symfony\AI\Chat\MessageStoreInterface;
16+
use Symfony\AI\Chat\StreamableStoreInterface;
1617
use Symfony\AI\Platform\Message\MessageBag;
1718

1819
/**
1920
* @author Christopher Hertel <mail@christopher-hertel.de>
2021
*/
21-
final class InMemoryStore implements ManagedStoreInterface, MessageStoreInterface
22+
final class InMemoryStore implements ManagedStoreInterface, MessageStoreInterface, StreamableStoreInterface
2223
{
2324
/**
2425
* @var MessageBag[]

src/chat/src/Bridge/Meilisearch/MessageStore.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\AI\Chat\ManagedStoreInterface;
1717
use Symfony\AI\Chat\MessageNormalizer;
1818
use Symfony\AI\Chat\MessageStoreInterface;
19+
use Symfony\AI\Chat\StreamableStoreInterface;
1920
use Symfony\AI\Platform\Message\MessageBag;
2021
use Symfony\AI\Platform\Message\MessageInterface;
2122
use Symfony\Component\Clock\ClockInterface;
@@ -31,7 +32,7 @@
3132
/**
3233
* @author Guillaume Loulier <personal@guillaumeloulier.fr>
3334
*/
34-
final class MessageStore implements ManagedStoreInterface, MessageStoreInterface
35+
final class MessageStore implements ManagedStoreInterface, MessageStoreInterface, StreamableStoreInterface
3536
{
3637
public function __construct(
3738
private readonly HttpClientInterface $httpClient,

src/chat/src/Bridge/Pogocache/MessageStore.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\AI\Chat\ManagedStoreInterface;
1616
use Symfony\AI\Chat\MessageNormalizer;
1717
use Symfony\AI\Chat\MessageStoreInterface;
18+
use Symfony\AI\Chat\StreamableStoreInterface;
1819
use Symfony\AI\Platform\Message\MessageBag;
1920
use Symfony\AI\Platform\Message\MessageInterface;
2021
use Symfony\Component\Serializer\Encoder\JsonEncoder;
@@ -28,7 +29,7 @@
2829
/**
2930
* @author Guillaume Loulier <personal@guillaumeloulier.fr>
3031
*/
31-
final class MessageStore implements ManagedStoreInterface, MessageStoreInterface
32+
final class MessageStore implements ManagedStoreInterface, MessageStoreInterface, StreamableStoreInterface
3233
{
3334
public function __construct(
3435
private readonly HttpClientInterface $httpClient,

src/chat/src/Bridge/Redis/MessageStore.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\AI\Chat\ManagedStoreInterface;
1515
use Symfony\AI\Chat\MessageNormalizer;
1616
use Symfony\AI\Chat\MessageStoreInterface;
17+
use Symfony\AI\Chat\StreamableStoreInterface;
1718
use Symfony\AI\Platform\Message\MessageBag;
1819
use Symfony\AI\Platform\Message\MessageInterface;
1920
use Symfony\Component\Serializer\Encoder\JsonEncoder;
@@ -24,7 +25,7 @@
2425
/**
2526
* @author Guillaume Loulier <personal@guillaumeloulier.fr>
2627
*/
27-
final class MessageStore implements ManagedStoreInterface, MessageStoreInterface
28+
final class MessageStore implements ManagedStoreInterface, MessageStoreInterface, StreamableStoreInterface
2829
{
2930
public function __construct(
3031
private readonly \Redis $redis,

src/chat/src/Bridge/SurrealDb/MessageStore.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\AI\Chat\ManagedStoreInterface;
1717
use Symfony\AI\Chat\MessageNormalizer;
1818
use Symfony\AI\Chat\MessageStoreInterface;
19+
use Symfony\AI\Chat\StreamableStoreInterface;
1920
use Symfony\AI\Platform\Message\MessageBag;
2021
use Symfony\AI\Platform\Message\MessageInterface;
2122
use Symfony\Component\Serializer\Encoder\JsonEncoder;
@@ -29,7 +30,7 @@
2930
/**
3031
* @author Guillaume Loulier <personal@guillaumeloulier.fr>
3132
*/
32-
final class MessageStore implements ManagedStoreInterface, MessageStoreInterface
33+
final class MessageStore implements ManagedStoreInterface, MessageStoreInterface, StreamableStoreInterface
3334
{
3435
private string $authenticationToken = '';
3536

src/chat/src/Chat.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@
1212
namespace Symfony\AI\Chat;
1313

1414
use Symfony\AI\Agent\AgentInterface;
15+
use Symfony\AI\Agent\Exception\RuntimeException;
16+
use Symfony\AI\Agent\Toolbox\StreamResult as ToolboxStreamResult;
17+
use Symfony\AI\Chat\Result\AccumulatingStreamResult;
1518
use Symfony\AI\Platform\Message\AssistantMessage;
1619
use Symfony\AI\Platform\Message\Message;
1720
use Symfony\AI\Platform\Message\MessageBag;
1821
use Symfony\AI\Platform\Message\UserMessage;
22+
use Symfony\AI\Platform\Result\StreamResult;
1923
use Symfony\AI\Platform\Result\TextResult;
2024

2125
/**
@@ -35,18 +39,31 @@ public function initiate(MessageBag $messages): void
3539
$this->store->save($messages);
3640
}
3741

38-
public function submit(UserMessage $message): AssistantMessage
42+
public function submit(UserMessage $message): AssistantMessage|AccumulatingStreamResult
3943
{
4044
$messages = $this->store->load();
4145

4246
$messages->add($message);
4347
$result = $this->agent->call($messages);
4448

49+
if ($result instanceof StreamResult || $result instanceof ToolboxStreamResult) {
50+
if (!$this->store instanceof StreamableStoreInterface) {
51+
throw new RuntimeException($this->store::class . ' does not support streaming.');
52+
}
53+
54+
return new AccumulatingStreamResult($result, function (AssistantMessage $assistantMessage) use ($messages) {
55+
$messages->add($assistantMessage);
56+
$this->store->save($messages);
57+
});
58+
}
59+
4560
\assert($result instanceof TextResult);
4661

4762
$assistantMessage = Message::ofAssistant($result->getContent());
48-
$messages->add($assistantMessage);
4963

64+
$assistantMessage->getMetadata()->set($result->getMetadata()->all());
65+
66+
$messages->add($assistantMessage);
5067
$this->store->save($messages);
5168

5269
return $assistantMessage;

0 commit comments

Comments
 (0)