| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 3 declare (strict_types=1); 4 namespace WordPress\AiClient\Builders; 5 6 use InvalidArgumentException; 7 use WordPress\AiClient\Files\DTO\File; 8 use WordPress\AiClient\Messages\DTO\Message; 9 use WordPress\AiClient\Messages\DTO\MessagePart; 10 use WordPress\AiClient\Messages\Enums\MessageRoleEnum; 11 use WordPress\AiClient\Tools\DTO\FunctionCall; 12 use WordPress\AiClient\Tools\DTO\FunctionResponse; 13 /** 14 * Fluent builder for constructing AI messages. 15 * 16 * This class provides a fluent interface for building messages with various 17 * content types including text, files, function calls, and function responses. 18 * 19 * @since 0.2.0 20 * 21 * @phpstan-import-type MessagePartArrayShape from MessagePart 22 * 23 * @phpstan-type Input string|MessagePart|MessagePartArrayShape|File|FunctionCall|FunctionResponse|null 24 */ 25 class MessageBuilder 26 { 27 /** 28 * @var MessageRoleEnum|null The role of the message sender. 29 */ 30 protected ?MessageRoleEnum $role = null; 31 /** 32 * @var list<MessagePart> The parts that make up the message. 33 */ 34 protected array $parts = []; 35 /** 36 * Constructor. 37 * 38 * @since 0.2.0 39 * 40 * @param Input $input Optional initial content. 41 * @param MessageRoleEnum|null $role Optional role. 42 */ 43 public function __construct($input = null, ?MessageRoleEnum $role = null) 44 { 45 $this->role = $role; 46 if ($input === null) { 47 return; 48 } 49 // Handle different input types 50 if ($input instanceof MessagePart) { 51 $this->parts[] = $input; 52 } elseif (is_string($input)) { 53 $this->withText($input); 54 } elseif ($input instanceof File) { 55 $this->withFile($input); 56 } elseif ($input instanceof FunctionCall) { 57 $this->withFunctionCall($input); 58 } elseif ($input instanceof FunctionResponse) { 59 $this->withFunctionResponse($input); 60 } elseif (is_array($input) && MessagePart::isArrayShape($input)) { 61 $this->parts[] = MessagePart::fromArray($input); 62 } else { 63 throw new InvalidArgumentException('Input must be a string, MessagePart, MessagePartArrayShape, File, FunctionCall, or FunctionResponse.'); 64 } 65 } 66 /** 67 * Creates a deep clone of this builder. 68 * 69 * Clones all MessagePart objects in the parts array to ensure 70 * the cloned builder is independent of the original. 71 * 72 * @since 0.4.2 73 */ 74 public function __clone() 75 { 76 // Deep clone parts array (MessagePart has __clone) 77 $clonedParts = []; 78 foreach ($this->parts as $part) { 79 $clonedParts[] = clone $part; 80 } 81 $this->parts = $clonedParts; 82 // Note: $role is an enum value object and can be safely shared 83 } 84 /** 85 * Sets the role of the message sender. 86 * 87 * @since 0.2.0 88 * 89 * @param MessageRoleEnum $role The role to set. 90 * @return self 91 */ 92 public function usingRole(MessageRoleEnum $role): self 93 { 94 $this->role = $role; 95 return $this; 96 } 97 /** 98 * Sets the role to user. 99 * 100 * @since 0.2.0 101 * 102 * @return self 103 */ 104 public function usingUserRole(): self 105 { 106 return $this->usingRole(MessageRoleEnum::user()); 107 } 108 /** 109 * Sets the role to model. 110 * 111 * @since 0.2.0 112 * 113 * @return self 114 */ 115 public function usingModelRole(): self 116 { 117 return $this->usingRole(MessageRoleEnum::model()); 118 } 119 /** 120 * Adds text content to the message. 121 * 122 * @since 0.2.0 123 * 124 * @param string $text The text to add. 125 * @return self 126 * @throws InvalidArgumentException If the text is empty. 127 */ 128 public function withText(string $text): self 129 { 130 if (trim($text) === '') { 131 throw new InvalidArgumentException('Text content cannot be empty.'); 132 } 133 $this->parts[] = new MessagePart($text); 134 return $this; 135 } 136 /** 137 * Adds a file to the message. 138 * 139 * Accepts: 140 * - File object 141 * - URL string (remote file) 142 * - Base64-encoded data string 143 * - Data URI string (data:mime/type;base64,data) 144 * - Local file path string 145 * 146 * @since 0.2.0 147 * 148 * @param string|File $file The file to add. 149 * @param string|null $mimeType Optional MIME type (ignored if File object provided). 150 * @return self 151 * @throws InvalidArgumentException If the file is invalid. 152 */ 153 public function withFile($file, ?string $mimeType = null): self 154 { 155 $file = $file instanceof File ? $file : new File($file, $mimeType); 156 $this->parts[] = new MessagePart($file); 157 return $this; 158 } 159 /** 160 * Adds a function call to the message. 161 * 162 * @since 0.2.0 163 * 164 * @param FunctionCall $functionCall The function call to add. 165 * @return self 166 */ 167 public function withFunctionCall(FunctionCall $functionCall): self 168 { 169 $this->parts[] = new MessagePart($functionCall); 170 return $this; 171 } 172 /** 173 * Adds a function response to the message. 174 * 175 * @since 0.2.0 176 * 177 * @param FunctionResponse $functionResponse The function response to add. 178 * @return self 179 */ 180 public function withFunctionResponse(FunctionResponse $functionResponse): self 181 { 182 $this->parts[] = new MessagePart($functionResponse); 183 return $this; 184 } 185 /** 186 * Adds multiple message parts to the message. 187 * 188 * @since 0.2.0 189 * 190 * @param MessagePart ...$parts The message parts to add. 191 * @return self 192 */ 193 public function withMessageParts(MessagePart ...$parts): self 194 { 195 foreach ($parts as $part) { 196 $this->parts[] = $part; 197 } 198 return $this; 199 } 200 /** 201 * Builds and returns the Message object. 202 * 203 * @since 0.2.0 204 * 205 * @return Message The built message. 206 * @throws InvalidArgumentException If the message validation fails. 207 */ 208 public function get(): Message 209 { 210 if (empty($this->parts)) { 211 throw new InvalidArgumentException('Cannot build an empty message. Add content using withText() or similar methods.'); 212 } 213 if ($this->role === null) { 214 throw new InvalidArgumentException('Cannot build a message with no role. Set a role using usingRole() or similar methods.'); 215 } 216 // At this point, we've validated that $this->role is not null 217 /** @var MessageRoleEnum $role */ 218 $role = $this->role; 219 return new Message($role, $this->parts); 220 } 221 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sat Jun 13 09:38:55 2026 | Cross-referenced by PHPXref |