Skip to content

Commit 1f1e432

Browse files
committed
feature #55 feat!: add UUID to all messages (OskarStark)
This PR was merged into the main branch. Discussion ---------- feat!: add UUID to all messages | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Docs? | yes | Issues | | License | MIT Cherry picking php-llm/llm-chain#364 Commits ------- 4fa1279 feat!: add UUID to all messages (#364)
2 parents 7c527ab + 4fa1279 commit 1f1e432

12 files changed

+242
-0
lines changed

src/platform/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"symfony/property-info": "^6.4 || ^7.1",
3333
"symfony/serializer": "^6.4 || ^7.1",
3434
"symfony/type-info": "^7.2.3",
35+
"symfony/uid": "^6.4 || ^7.1",
3536
"webmozart/assert": "^1.11"
3637
},
3738
"require-dev": {

src/platform/doc/index.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,31 @@ have different content types, like ``Text``, ``Image`` or ``Audio``, and can be
124124
Message::ofUser('Please describe this picture?', Image::fromFile('/path/to/image.jpg')),
125125
);
126126

127+
**Message Unique IDs**
128+
129+
Each message automatically receives a unique identifier (UUID v7) upon creation.
130+
This provides several benefits:
131+
132+
- **Traceability**: Track individual messages through your application
133+
- **Time-ordered**: UUIDs are naturally sortable by creation time
134+
- **Timestamp extraction**: Get the exact creation time from the ID
135+
- **Database-friendly**: Sequential nature improves index performance
136+
137+
.. code-block:: php
138+
use PhpLlm\LlmChain\Platform\Message\Message;
139+
140+
$message = Message::ofUser('Hello, AI!');
141+
142+
// Access the unique ID
143+
$id = $message->getId(); // Returns Symfony\Component\Uid\Uuid instance
144+
145+
// Extract creation timestamp
146+
$createdAt = $id->getDateTime(); // Returns \DateTimeImmutable
147+
echo $createdAt->format('Y-m-d H:i:s.u'); // e.g., "2025-06-29 15:30:45.123456"
148+
149+
// Get string representation
150+
echo $id->toRfc4122(); // e.g., "01928d1f-6f2e-7123-a456-123456789abc"
151+
127152
Response Streaming
128153
------------------
129154

src/platform/src/Message/AssistantMessage.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,35 @@
1212
namespace Symfony\AI\Platform\Message;
1313

1414
use Symfony\AI\Platform\Response\ToolCall;
15+
use Symfony\Component\Uid\Uuid;
1516

1617
/**
1718
* @author Denis Zunke <[email protected]>
1819
*/
1920
final readonly class AssistantMessage implements MessageInterface
2021
{
22+
public Uuid $id;
23+
2124
/**
2225
* @param ?ToolCall[] $toolCalls
2326
*/
2427
public function __construct(
2528
public ?string $content = null,
2629
public ?array $toolCalls = null,
2730
) {
31+
$this->id = Uuid::v7();
2832
}
2933

3034
public function getRole(): Role
3135
{
3236
return Role::Assistant;
3337
}
3438

39+
public function getId(): Uuid
40+
{
41+
return $this->id;
42+
}
43+
3544
public function hasToolCalls(): bool
3645
{
3746
return null !== $this->toolCalls && 0 !== \count($this->toolCalls);

src/platform/src/Message/MessageInterface.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@
1111

1212
namespace Symfony\AI\Platform\Message;
1313

14+
use Symfony\Component\Uid\Uuid;
15+
1416
/**
1517
* @author Denis Zunke <[email protected]>
1618
*/
1719
interface MessageInterface
1820
{
1921
public function getRole(): Role;
22+
23+
public function getId(): Uuid;
2024
}

src/platform/src/Message/SystemMessage.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,27 @@
1111

1212
namespace Symfony\AI\Platform\Message;
1313

14+
use Symfony\Component\Uid\Uuid;
15+
1416
/**
1517
* @author Denis Zunke <[email protected]>
1618
*/
1719
final readonly class SystemMessage implements MessageInterface
1820
{
21+
public Uuid $id;
22+
1923
public function __construct(public string $content)
2024
{
25+
$this->id = Uuid::v7();
2126
}
2227

2328
public function getRole(): Role
2429
{
2530
return Role::System;
2631
}
32+
33+
public function getId(): Uuid
34+
{
35+
return $this->id;
36+
}
2737
}

src/platform/src/Message/ToolCallMessage.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,29 @@
1212
namespace Symfony\AI\Platform\Message;
1313

1414
use Symfony\AI\Platform\Response\ToolCall;
15+
use Symfony\Component\Uid\Uuid;
1516

1617
/**
1718
* @author Denis Zunke <[email protected]>
1819
*/
1920
final readonly class ToolCallMessage implements MessageInterface
2021
{
22+
public Uuid $id;
23+
2124
public function __construct(
2225
public ToolCall $toolCall,
2326
public string $content,
2427
) {
28+
$this->id = Uuid::v7();
2529
}
2630

2731
public function getRole(): Role
2832
{
2933
return Role::ToolCall;
3034
}
35+
36+
public function getId(): Uuid
37+
{
38+
return $this->id;
39+
}
3140
}

src/platform/src/Message/UserMessage.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\AI\Platform\Message\Content\ContentInterface;
1616
use Symfony\AI\Platform\Message\Content\Image;
1717
use Symfony\AI\Platform\Message\Content\ImageUrl;
18+
use Symfony\Component\Uid\Uuid;
1819

1920
/**
2021
* @author Denis Zunke <[email protected]>
@@ -26,17 +27,25 @@
2627
*/
2728
public array $content;
2829

30+
public Uuid $id;
31+
2932
public function __construct(
3033
ContentInterface ...$content,
3134
) {
3235
$this->content = $content;
36+
$this->id = Uuid::v7();
3337
}
3438

3539
public function getRole(): Role
3640
{
3741
return Role::User;
3842
}
3943

44+
public function getId(): Uuid
45+
{
46+
return $this->id;
47+
}
48+
4049
public function hasAudioContent(): bool
4150
{
4251
foreach ($this->content as $content) {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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\Platform\Tests\Helper;
13+
14+
trait UuidAssertionTrait
15+
{
16+
/**
17+
* Asserts that a value is a valid UUID v7 string.
18+
*/
19+
public static function assertIsUuidV7(mixed $actual, string $message = ''): void
20+
{
21+
self::assertIsString($actual, $message ?: 'Failed asserting that value is a string.');
22+
self::assertMatchesRegularExpression(
23+
'/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i',
24+
$actual,
25+
$message ?: 'Failed asserting that value is a valid UUID v7.'
26+
);
27+
}
28+
}

src/platform/tests/Message/AssistantMessageTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@
1919
use Symfony\AI\Platform\Message\AssistantMessage;
2020
use Symfony\AI\Platform\Message\Role;
2121
use Symfony\AI\Platform\Response\ToolCall;
22+
use Symfony\AI\Platform\Tests\Helper\UuidAssertionTrait;
23+
use Symfony\Component\Uid\UuidV7;
2224

2325
#[CoversClass(AssistantMessage::class)]
2426
#[UsesClass(ToolCall::class)]
2527
#[Small]
2628
final class AssistantMessageTest extends TestCase
2729
{
30+
use UuidAssertionTrait;
31+
2832
#[Test]
2933
public function theRoleOfTheMessageIsAsExpected(): void
3034
{
@@ -50,4 +54,36 @@ public function constructionWithoutContentIsPossible(): void
5054
self::assertSame([$toolCall], $message->toolCalls);
5155
self::assertTrue($message->hasToolCalls());
5256
}
57+
58+
#[Test]
59+
public function messageHasUid(): void
60+
{
61+
$message = new AssistantMessage('foo');
62+
63+
self::assertInstanceOf(UuidV7::class, $message->id);
64+
self::assertInstanceOf(UuidV7::class, $message->getId());
65+
self::assertSame($message->id, $message->getId());
66+
}
67+
68+
#[Test]
69+
public function differentMessagesHaveDifferentUids(): void
70+
{
71+
$message1 = new AssistantMessage('foo');
72+
$message2 = new AssistantMessage('bar');
73+
74+
self::assertNotSame($message1->getId()->toRfc4122(), $message2->getId()->toRfc4122());
75+
self::assertIsUuidV7($message1->getId()->toRfc4122());
76+
self::assertIsUuidV7($message2->getId()->toRfc4122());
77+
}
78+
79+
#[Test]
80+
public function sameMessagesHaveDifferentUids(): void
81+
{
82+
$message1 = new AssistantMessage('foo');
83+
$message2 = new AssistantMessage('foo');
84+
85+
self::assertNotSame($message1->getId()->toRfc4122(), $message2->getId()->toRfc4122());
86+
self::assertIsUuidV7($message1->getId()->toRfc4122());
87+
self::assertIsUuidV7($message2->getId()->toRfc4122());
88+
}
5389
}

src/platform/tests/Message/SystemMessageTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@
1717
use PHPUnit\Framework\TestCase;
1818
use Symfony\AI\Platform\Message\Role;
1919
use Symfony\AI\Platform\Message\SystemMessage;
20+
use Symfony\AI\Platform\Tests\Helper\UuidAssertionTrait;
21+
use Symfony\Component\Uid\UuidV7;
2022

2123
#[CoversClass(SystemMessage::class)]
2224
#[Small]
2325
final class SystemMessageTest extends TestCase
2426
{
27+
use UuidAssertionTrait;
28+
2529
#[Test]
2630
public function constructionIsPossible(): void
2731
{
@@ -30,4 +34,36 @@ public function constructionIsPossible(): void
3034
self::assertSame(Role::System, $message->getRole());
3135
self::assertSame('foo', $message->content);
3236
}
37+
38+
#[Test]
39+
public function messageHasUid(): void
40+
{
41+
$message = new SystemMessage('foo');
42+
43+
self::assertInstanceOf(UuidV7::class, $message->id);
44+
self::assertInstanceOf(UuidV7::class, $message->getId());
45+
self::assertSame($message->id, $message->getId());
46+
}
47+
48+
#[Test]
49+
public function differentMessagesHaveDifferentUids(): void
50+
{
51+
$message1 = new SystemMessage('foo');
52+
$message2 = new SystemMessage('bar');
53+
54+
self::assertNotSame($message1->getId()->toRfc4122(), $message2->getId()->toRfc4122());
55+
self::assertIsUuidV7($message1->getId()->toRfc4122());
56+
self::assertIsUuidV7($message2->getId()->toRfc4122());
57+
}
58+
59+
#[Test]
60+
public function sameMessagesHaveDifferentUids(): void
61+
{
62+
$message1 = new SystemMessage('foo');
63+
$message2 = new SystemMessage('foo');
64+
65+
self::assertNotSame($message1->getId()->toRfc4122(), $message2->getId()->toRfc4122());
66+
self::assertIsUuidV7($message1->getId()->toRfc4122());
67+
self::assertIsUuidV7($message2->getId()->toRfc4122());
68+
}
3369
}

0 commit comments

Comments
 (0)