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