From 839b4867a0c04eef92db0b21e0e46a6d2dab89be Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Thu, 10 Jul 2025 18:07:19 +0200 Subject: [PATCH] fix: use intersection type AbstractUid&TimeBasedUidInterface for flexible message IDs This PR updates the message ID type from TimeBasedUidInterface to AbstractUid&TimeBasedUidInterface using PHP's intersection types. Problem: - The original implementation returned TimeBasedUidInterface from getId(), but tests were calling toRfc4122() which is not part of that interface - This caused PHPStan errors Solution: - Using the intersection type AbstractUid&TimeBasedUidInterface ensures that both toRfc4122() (from AbstractUid) and getDateTime() (from TimeBasedUidInterface) are available - This allows implementations to use any time-based UID type (UUID v1/v6/v7 or ULID) Changes: - Updated MessageInterface::getId() return type - Updated all message implementations (AssistantMessage, SystemMessage, UserMessage, ToolCallMessage) - Added tests to verify interface implementations - Updated PHPStan configuration Benefits: - Flexibility: Users can now implement messages with either UUID or ULID - Type Safety: The intersection type guarantees all necessary methods are available - Future Proof: Any new Symfony UID types that extend AbstractUid and implement TimeBasedUidInterface will work - Backwards Compatible: All existing UUID implementations already satisfy the new type constraint --- src/Platform/Message/AssistantMessage.php | 6 ++++-- src/Platform/Message/MessageInterface.php | 5 +++-- src/Platform/Message/SystemMessage.php | 6 ++++-- src/Platform/Message/ToolCallMessage.php | 6 ++++-- src/Platform/Message/UserMessage.php | 6 ++++-- tests/Platform/ContractTest.php | 4 +++- tests/Platform/Message/AssistantMessageTest.php | 12 ++++++++++++ tests/Platform/Message/SystemMessageTest.php | 12 ++++++++++++ tests/Platform/Message/ToolCallMessageTest.php | 13 +++++++++++++ tests/Platform/Message/UserMessageTest.php | 12 ++++++++++++ 10 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/Platform/Message/AssistantMessage.php b/src/Platform/Message/AssistantMessage.php index 7590f909..e0dfee7f 100644 --- a/src/Platform/Message/AssistantMessage.php +++ b/src/Platform/Message/AssistantMessage.php @@ -5,6 +5,8 @@ namespace PhpLlm\LlmChain\Platform\Message; use PhpLlm\LlmChain\Platform\Response\ToolCall; +use Symfony\Component\Uid\AbstractUid; +use Symfony\Component\Uid\TimeBasedUidInterface; use Symfony\Component\Uid\Uuid; /** @@ -12,7 +14,7 @@ */ final readonly class AssistantMessage implements MessageInterface { - public Uuid $id; + public AbstractUid&TimeBasedUidInterface $id; /** * @param ?ToolCall[] $toolCalls @@ -29,7 +31,7 @@ public function getRole(): Role return Role::Assistant; } - public function getId(): Uuid + public function getId(): AbstractUid&TimeBasedUidInterface { return $this->id; } diff --git a/src/Platform/Message/MessageInterface.php b/src/Platform/Message/MessageInterface.php index 0b9f0865..e7a77c95 100644 --- a/src/Platform/Message/MessageInterface.php +++ b/src/Platform/Message/MessageInterface.php @@ -4,7 +4,8 @@ namespace PhpLlm\LlmChain\Platform\Message; -use Symfony\Component\Uid\Uuid; +use Symfony\Component\Uid\AbstractUid; +use Symfony\Component\Uid\TimeBasedUidInterface; /** * @author Denis Zunke @@ -13,5 +14,5 @@ interface MessageInterface { public function getRole(): Role; - public function getId(): Uuid; + public function getId(): AbstractUid&TimeBasedUidInterface; } diff --git a/src/Platform/Message/SystemMessage.php b/src/Platform/Message/SystemMessage.php index 9c53aa87..d62428bf 100644 --- a/src/Platform/Message/SystemMessage.php +++ b/src/Platform/Message/SystemMessage.php @@ -4,6 +4,8 @@ namespace PhpLlm\LlmChain\Platform\Message; +use Symfony\Component\Uid\AbstractUid; +use Symfony\Component\Uid\TimeBasedUidInterface; use Symfony\Component\Uid\Uuid; /** @@ -11,7 +13,7 @@ */ final readonly class SystemMessage implements MessageInterface { - public Uuid $id; + public AbstractUid&TimeBasedUidInterface $id; public function __construct(public string $content) { @@ -23,7 +25,7 @@ public function getRole(): Role return Role::System; } - public function getId(): Uuid + public function getId(): AbstractUid&TimeBasedUidInterface { return $this->id; } diff --git a/src/Platform/Message/ToolCallMessage.php b/src/Platform/Message/ToolCallMessage.php index 6667546d..d29b4019 100644 --- a/src/Platform/Message/ToolCallMessage.php +++ b/src/Platform/Message/ToolCallMessage.php @@ -5,6 +5,8 @@ namespace PhpLlm\LlmChain\Platform\Message; use PhpLlm\LlmChain\Platform\Response\ToolCall; +use Symfony\Component\Uid\AbstractUid; +use Symfony\Component\Uid\TimeBasedUidInterface; use Symfony\Component\Uid\Uuid; /** @@ -12,7 +14,7 @@ */ final readonly class ToolCallMessage implements MessageInterface { - public Uuid $id; + public AbstractUid&TimeBasedUidInterface $id; public function __construct( public ToolCall $toolCall, @@ -26,7 +28,7 @@ public function getRole(): Role return Role::ToolCall; } - public function getId(): Uuid + public function getId(): AbstractUid&TimeBasedUidInterface { return $this->id; } diff --git a/src/Platform/Message/UserMessage.php b/src/Platform/Message/UserMessage.php index ec034e4e..697e7fe6 100644 --- a/src/Platform/Message/UserMessage.php +++ b/src/Platform/Message/UserMessage.php @@ -8,6 +8,8 @@ use PhpLlm\LlmChain\Platform\Message\Content\ContentInterface; use PhpLlm\LlmChain\Platform\Message\Content\Image; use PhpLlm\LlmChain\Platform\Message\Content\ImageUrl; +use Symfony\Component\Uid\AbstractUid; +use Symfony\Component\Uid\TimeBasedUidInterface; use Symfony\Component\Uid\Uuid; /** @@ -20,7 +22,7 @@ */ public array $content; - public Uuid $id; + public AbstractUid&TimeBasedUidInterface $id; public function __construct( ContentInterface ...$content, @@ -34,7 +36,7 @@ public function getRole(): Role return Role::User; } - public function getId(): Uuid + public function getId(): AbstractUid&TimeBasedUidInterface { return $this->id; } diff --git a/tests/Platform/ContractTest.php b/tests/Platform/ContractTest.php index 85f0ab55..6906a469 100644 --- a/tests/Platform/ContractTest.php +++ b/tests/Platform/ContractTest.php @@ -35,6 +35,8 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; +use Symfony\Component\Uid\AbstractUid; +use Symfony\Component\Uid\TimeBasedUidInterface; use Symfony\Component\Uid\Uuid; #[Large] @@ -201,7 +203,7 @@ public function getRole(): Role return Role::User; } - public function getId(): Uuid + public function getId(): AbstractUid&TimeBasedUidInterface { return Uuid::v7(); } diff --git a/tests/Platform/Message/AssistantMessageTest.php b/tests/Platform/Message/AssistantMessageTest.php index 68f9ee18..46e754a4 100644 --- a/tests/Platform/Message/AssistantMessageTest.php +++ b/tests/Platform/Message/AssistantMessageTest.php @@ -13,6 +13,8 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; +use Symfony\Component\Uid\AbstractUid; +use Symfony\Component\Uid\TimeBasedUidInterface; use Symfony\Component\Uid\UuidV7; #[CoversClass(AssistantMessage::class)] @@ -79,4 +81,14 @@ public function sameMessagesHaveDifferentUids(): void self::assertIsUuidV7($message1->getId()->toRfc4122()); self::assertIsUuidV7($message2->getId()->toRfc4122()); } + + #[Test] + public function messageIdImplementsRequiredInterfaces(): void + { + $message = new AssistantMessage('test'); + + self::assertInstanceOf(AbstractUid::class, $message->getId()); + self::assertInstanceOf(TimeBasedUidInterface::class, $message->getId()); + self::assertInstanceOf(UuidV7::class, $message->getId()); + } } diff --git a/tests/Platform/Message/SystemMessageTest.php b/tests/Platform/Message/SystemMessageTest.php index eaab4dde..6ad4caf5 100644 --- a/tests/Platform/Message/SystemMessageTest.php +++ b/tests/Platform/Message/SystemMessageTest.php @@ -11,6 +11,8 @@ use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; +use Symfony\Component\Uid\AbstractUid; +use Symfony\Component\Uid\TimeBasedUidInterface; use Symfony\Component\Uid\UuidV7; #[CoversClass(SystemMessage::class)] @@ -59,4 +61,14 @@ public function sameMessagesHaveDifferentUids(): void self::assertIsUuidV7($message1->getId()->toRfc4122()); self::assertIsUuidV7($message2->getId()->toRfc4122()); } + + #[Test] + public function messageIdImplementsRequiredInterfaces(): void + { + $message = new SystemMessage('test'); + + self::assertInstanceOf(AbstractUid::class, $message->getId()); + self::assertInstanceOf(TimeBasedUidInterface::class, $message->getId()); + self::assertInstanceOf(UuidV7::class, $message->getId()); + } } diff --git a/tests/Platform/Message/ToolCallMessageTest.php b/tests/Platform/Message/ToolCallMessageTest.php index 046d68b4..5b42ae87 100644 --- a/tests/Platform/Message/ToolCallMessageTest.php +++ b/tests/Platform/Message/ToolCallMessageTest.php @@ -12,6 +12,8 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; +use Symfony\Component\Uid\AbstractUid; +use Symfony\Component\Uid\TimeBasedUidInterface; use Symfony\Component\Uid\UuidV7; #[CoversClass(ToolCallMessage::class)] @@ -65,4 +67,15 @@ public function sameMessagesHaveDifferentUids(): void self::assertIsUuidV7($message1->getId()->toRfc4122()); self::assertIsUuidV7($message2->getId()->toRfc4122()); } + + #[Test] + public function messageIdImplementsRequiredInterfaces(): void + { + $toolCall = new ToolCall('foo', 'bar'); + $message = new ToolCallMessage($toolCall, 'test'); + + self::assertInstanceOf(AbstractUid::class, $message->getId()); + self::assertInstanceOf(TimeBasedUidInterface::class, $message->getId()); + self::assertInstanceOf(UuidV7::class, $message->getId()); + } } diff --git a/tests/Platform/Message/UserMessageTest.php b/tests/Platform/Message/UserMessageTest.php index 8ed5a6e7..899b5500 100644 --- a/tests/Platform/Message/UserMessageTest.php +++ b/tests/Platform/Message/UserMessageTest.php @@ -15,6 +15,8 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; +use Symfony\Component\Uid\AbstractUid; +use Symfony\Component\Uid\TimeBasedUidInterface; use Symfony\Component\Uid\UuidV7; #[CoversClass(UserMessage::class)] @@ -109,4 +111,14 @@ public function sameMessagesHaveDifferentUids(): void self::assertIsUuidV7($message1->getId()->toRfc4122()); self::assertIsUuidV7($message2->getId()->toRfc4122()); } + + #[Test] + public function messageIdImplementsRequiredInterfaces(): void + { + $message = new UserMessage(new Text('test')); + + self::assertInstanceOf(AbstractUid::class, $message->getId()); + self::assertInstanceOf(TimeBasedUidInterface::class, $message->getId()); + self::assertInstanceOf(UuidV7::class, $message->getId()); + } }