Replace mixed constructor with abstract class pattern for MessagePart#37
Replace mixed constructor with abstract class pattern for MessagePart#37JosephGabito wants to merge 34 commits intoWordPress:trunkfrom
Conversation
Introduces MessageContentInterface to define a contract for message content value objects, ensuring consistent behavior and type safety. The interface requires implementations to provide a message part type and an array representation of the content.
Introduces the FileContent class implementing MessageContentInterface to encapsulate file content as a value object. Provides methods for retrieving the file, its type, and converting the content to an array.
Introduces the FunctionCallContent class implementing MessageContentInterface to encapsulate function call message parts. Provides methods for retrieving the message part type, accessing the FunctionCall object, and converting the content to an array.
Introduces the TextContent class implementing MessageContentInterface, encapsulating immutable text content with methods for type retrieval and array conversion. This supports structured handling of message text parts.
Introduces getText, getFile, getFunctionCall, and getFunctionResponse methods to MessageContentInterface, allowing retrieval of specific content types. This enhances type safety and consistency for message content handling.
MessagePart now delegates content handling to a MessageContentInterface implementation, simplifying construction and serialization. This change improves extensibility and encapsulation by removing type-specific logic from MessagePart and moving it to dedicated content value objects.
Introduces ContentGettersTrait with default getter methods for text, file, function call, and function response content in message value objects. This provides a consistent interface for accessing different message part types.
The FunctionCallContent class now uses the ContentGettersTrait, enabling shared content getter functionality. This change improves code reuse and consistency across message content value objects.
Included the ContentGettersTrait in the FunctionResponseContent value object to provide additional content-related methods. This enhances the functionality and consistency of content handling within the class.
The TextContent class now uses the ContentGettersTrait, providing additional getter methods or shared logic for content retrieval. This change improves code reuse and consistency across value objects handling message content.
The toArray method now adds the type key to the returned array, ensuring the message part type is always included in the serialized output.
Replaces the use of getMessagePartType()->value with the constant MessagePart::KEY_FILE in the FileContent::toArray() method for improved consistency and clarity.
Replaces the use of MessagePartTypeEnum value with the MessagePart::KEY_FUNCTION_CALL constant in the toArray() method for consistency and maintainability.
Replaces the hardcoded message part type key in FunctionResponseContent::toArray() with the MessagePart::KEY_FUNCTION_RESPONSE constant for improved consistency and maintainability.
Replaces the use of MessagePartTypeEnum value with MessagePart::KEY_TEXT as the array key in the TextContent::toArray() method for consistency and clarity.
Replaces direct string and File usage in MessagePart constructors with explicit TextContent, FileContent, and FunctionCallContent objects in GenerativeAiResultTest. This aligns the tests with the updated MessagePart API and ensures type safety and clarity in test cases.
Refactors CandidateTest to wrap message part content in appropriate value objects such as TextContent, FileContent, and FunctionCallContent. This aligns the test with recent changes to the message part structure, ensuring type safety and consistency with the updated implementation.
Replaces string arguments with TextContent objects when creating MessagePart instances in GenerativeAiOperationTest. This aligns the test with recent changes to the MessagePart constructor, ensuring compatibility and correctness.
Refactors all MessagePart instantiations in UserMessageTest to explicitly use TextContent for text and FileContent for files. This aligns the tests with the updated MessagePart API and ensures correct type usage in test cases.
Refactors all MessagePart instantiations in SystemMessageTest to wrap string content in TextContent objects. This aligns the tests with the updated MessagePart constructor requirements and ensures consistency with the new value object usage.
Replaces string and DTO arguments in MessagePart instantiations with appropriate content value objects (TextContent, FileContent, FunctionCallContent, FunctionResponseContent) to align with updated MessagePart constructor requirements. Ensures tests reflect the new structure for message parts.
Refactors MessageTest to construct MessagePart instances with explicit content value objects such as TextContent, FileContent, FunctionCallContent, and FunctionResponseContent. This aligns the tests with the updated MessagePart API and improves type safety and clarity.
Refactors MessagePartTest to construct MessagePart instances using explicit content value objects (TextContent, FileContent, FunctionCallContent, FunctionResponseContent) instead of raw values. Updates tests for exception handling to focus on fromArray input validation, and removes the unsupported content provider. This aligns the tests with the new MessagePart constructor requirements.
Eliminated the @mixin ContentGettersTrait annotation from the MessageContentInterface docblock as it is not applicable to interfaces. This improves code documentation clarity.
Simplifies MessagePart by removing individual content properties and using a single MessageContentInterface property. Updates the constructor and fromArray logic to directly handle content types, improving maintainability and clarity.
Refines the docblock for the toArray() method in FileContent to specify a more precise return type. Also removes a redundant @use annotation for ContentGettersTrait.
Refines the return type annotation for the toArray() method to specify array<string, array<string, mixed>>. Also removes a redundant @use annotation for ContentGettersTrait.
Refines the return type annotation for the toArray() method to specify array<string, array<string, mixed>>. Also removes a redundant @use annotation for ContentGettersTrait.
Removed the unnecessary @use annotation for ContentGettersTrait and updated the return type in the toArray() docblock to specify array<string, string> for improved clarity.
Replaced MessageContentInterface with an abstract MessageContent class providing default content getter implementations. Updated MessagePart to use the new MessageContent class and removed the now-unnecessary ContentGettersTrait.
Updated FileContent, FunctionCallContent, FunctionResponseContent, and TextContent to extend the MessageContent base class instead of implementing MessageContentInterface and using ContentGettersTrait. Also changed getter return types to nullable for better type safety and consistency.
Updated getter methods in FileContent, FunctionCallContent, and FunctionResponseContent to return non-nullable types. This change enforces that these methods always return a value, improving type safety.
|
@JosephGabito Thank you for opening this PR. I'm not sure yet that this is something we'll want to do though. The current architecture is built with portability in mind, with a focus on DTOs that can easily be expressed in other formats like JSON or other languages like TypeScript, all to have a uniform API across the different layers. I generally understand and agree with the points you're making regarding use of abstract classes, but I think for this concrete case the implementation as it is is more aligned with the above philosophy and easier to follow. The manual type checks are certainly something you can argue against from a software architecture "cleanliness" perspective, but it works flawlessly, is pragmatic and simple. I think introducing separate classes just to wrap the different types of message part content, while looking more elegant, adds more complexity in practice. Aside: In general, for larger proposed changes like this opening an issue to discuss first would be ideal, so that you don't spend too much time writing code without there being consensus yet. |
|
Thanks for the thoughtful feedback, Felix 🙌 That all makes sense. I understand the focus on portability and consistency across layers. I approached this from a pure PHP design lens, but I see now that alignment across systems needs to come first. I'll close this out for now. Next time, I'll raise an issue first before proposing changes at this level. Appreciate the perspective. |
Overview
This PR refactors the
MessagePartDTO to improve type safety, simplify the codebase, and make the system easier to extend. It replaces the previous mixed constructor approach with a value object-based architecture, while keeping everything backward compatible. All relevant tests have been updated.Problem
mixed $content, requiring manual type checks$text,$file, etc.)if/else) was needed to determine the content typeSolution
MessageContentclass defines common behavior and default accessorsTextContent,FileContent, etc. implement only what they needMessagePartdelegates to a single content object, rather than managing multiple propertiesfromArray()handles content deserializationBefore vs After
Before
After
Why Abstract Class Instead of Interface?
Using an interface would require every content type to implement all possible accessors (
getText(),getFile(), etc.), even if they returnnull. This creates:By using an abstract class, we:
nullimplementations for optional methodsThis keeps the design simpler, more flexible, and easier to maintain.
Benefits
fromArray()Files Changed
Added
TextContent.phpFileContent.phpFunctionCallContent.phpFunctionResponseContent.phpMessageContent.php(base class)Modified
MessagePart.php- updated to use new structureNotes
Appreciate any feedback on this change. Happy to adjust anything to better fit the overall direction or future plans.