| [ 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\Messages\DTO; 5 6 use WordPress\AiClient\Common\AbstractDataTransferObject; 7 use WordPress\AiClient\Common\Exception\InvalidArgumentException; 8 use WordPress\AiClient\Common\Exception\RuntimeException; 9 use WordPress\AiClient\Files\DTO\File; 10 use WordPress\AiClient\Messages\Enums\MessagePartChannelEnum; 11 use WordPress\AiClient\Messages\Enums\MessagePartTypeEnum; 12 use WordPress\AiClient\Tools\DTO\FunctionCall; 13 use WordPress\AiClient\Tools\DTO\FunctionResponse; 14 /** 15 * Represents a part of a message. 16 * 17 * Messages can contain multiple parts of different types, such as text, files, 18 * function calls, etc. This DTO encapsulates one such part. 19 * 20 * @since 0.1.0 21 * 22 * @phpstan-import-type FileArrayShape from File 23 * @phpstan-import-type FunctionCallArrayShape from FunctionCall 24 * @phpstan-import-type FunctionResponseArrayShape from FunctionResponse 25 * 26 * @phpstan-type MessagePartArrayShape array{ 27 * channel: string, 28 * type: string, 29 * thoughtSignature?: string, 30 * text?: string, 31 * file?: FileArrayShape, 32 * functionCall?: FunctionCallArrayShape, 33 * functionResponse?: FunctionResponseArrayShape 34 * } 35 * 36 * @extends AbstractDataTransferObject<MessagePartArrayShape> 37 */ 38 class MessagePart extends AbstractDataTransferObject 39 { 40 public const KEY_CHANNEL = 'channel'; 41 public const KEY_TYPE = 'type'; 42 public const KEY_THOUGHT_SIGNATURE = 'thoughtSignature'; 43 public const KEY_TEXT = 'text'; 44 public const KEY_FILE = 'file'; 45 public const KEY_FUNCTION_CALL = 'functionCall'; 46 public const KEY_FUNCTION_RESPONSE = 'functionResponse'; 47 /** 48 * @var MessagePartChannelEnum The channel this message part belongs to. 49 */ 50 private MessagePartChannelEnum $channel; 51 /** 52 * @var MessagePartTypeEnum The type of this message part. 53 */ 54 private MessagePartTypeEnum $type; 55 /** 56 * @var string|null Thought signature for extended thinking. 57 */ 58 private ?string $thoughtSignature = null; 59 /** 60 * @var string|null Text content (when type is TEXT). 61 */ 62 private ?string $text = null; 63 /** 64 * @var File|null File data (when type is FILE). 65 */ 66 private ?File $file = null; 67 /** 68 * @var FunctionCall|null Function call request (when type is FUNCTION_CALL). 69 */ 70 private ?FunctionCall $functionCall = null; 71 /** 72 * @var FunctionResponse|null Function response (when type is FUNCTION_RESPONSE). 73 */ 74 private ?FunctionResponse $functionResponse = null; 75 /** 76 * Constructor that accepts various content types and infers the message part type. 77 * 78 * @since 0.1.0 79 * 80 * @param mixed $content The content of this message part. 81 * @param MessagePartChannelEnum|null $channel The channel this part belongs to. Defaults to CONTENT. 82 * @param string|null $thoughtSignature Optional thought signature for extended thinking. 83 * @throws InvalidArgumentException If an unsupported content type is provided. 84 */ 85 public function __construct($content, ?MessagePartChannelEnum $channel = null, ?string $thoughtSignature = null) 86 { 87 $this->channel = $channel ?? MessagePartChannelEnum::content(); 88 $this->thoughtSignature = $thoughtSignature; 89 if (is_string($content)) { 90 $this->type = MessagePartTypeEnum::text(); 91 $this->text = $content; 92 } elseif ($content instanceof File) { 93 $this->type = MessagePartTypeEnum::file(); 94 $this->file = $content; 95 } elseif ($content instanceof FunctionCall) { 96 $this->type = MessagePartTypeEnum::functionCall(); 97 $this->functionCall = $content; 98 } elseif ($content instanceof FunctionResponse) { 99 $this->type = MessagePartTypeEnum::functionResponse(); 100 $this->functionResponse = $content; 101 } else { 102 $type = is_object($content) ? get_class($content) : gettype($content); 103 throw new InvalidArgumentException(sprintf('Unsupported content type %s. Expected string, File, ' . 'FunctionCall, or FunctionResponse.', $type)); 104 } 105 } 106 /** 107 * Gets the channel this message part belongs to. 108 * 109 * @since 0.1.0 110 * 111 * @return MessagePartChannelEnum The channel. 112 */ 113 public function getChannel(): MessagePartChannelEnum 114 { 115 return $this->channel; 116 } 117 /** 118 * Gets the type of this message part. 119 * 120 * @since 0.1.0 121 * 122 * @return MessagePartTypeEnum The type. 123 */ 124 public function getType(): MessagePartTypeEnum 125 { 126 return $this->type; 127 } 128 /** 129 * Gets the thought signature. 130 * 131 * @since 1.3.0 132 * 133 * @return string|null The thought signature or null if not set. 134 */ 135 public function getThoughtSignature(): ?string 136 { 137 return $this->thoughtSignature; 138 } 139 /** 140 * Gets the text content. 141 * 142 * @since 0.1.0 143 * 144 * @return string|null The text content or null if not a text part. 145 */ 146 public function getText(): ?string 147 { 148 return $this->text; 149 } 150 /** 151 * Gets the file. 152 * 153 * @since 0.1.0 154 * 155 * @return File|null The file or null if not a file part. 156 */ 157 public function getFile(): ?File 158 { 159 return $this->file; 160 } 161 /** 162 * Gets the function call. 163 * 164 * @since 0.1.0 165 * 166 * @return FunctionCall|null The function call or null if not a function call part. 167 */ 168 public function getFunctionCall(): ?FunctionCall 169 { 170 return $this->functionCall; 171 } 172 /** 173 * Gets the function response. 174 * 175 * @since 0.1.0 176 * 177 * @return FunctionResponse|null The function response or null if not a function response part. 178 */ 179 public function getFunctionResponse(): ?FunctionResponse 180 { 181 return $this->functionResponse; 182 } 183 /** 184 * {@inheritDoc} 185 * 186 * @since 0.1.0 187 */ 188 public static function getJsonSchema(): array 189 { 190 $channelSchema = ['type' => 'string', 'enum' => MessagePartChannelEnum::getValues(), 'description' => 'The channel this message part belongs to.']; 191 $thoughtSignatureSchema = ['type' => 'string', 'description' => 'Thought signature for extended thinking.']; 192 return ['oneOf' => [['type' => 'object', 'properties' => [self::KEY_CHANNEL => $channelSchema, self::KEY_TYPE => ['type' => 'string', 'const' => MessagePartTypeEnum::text()->value], self::KEY_TEXT => ['type' => 'string', 'description' => 'Text content.'], self::KEY_THOUGHT_SIGNATURE => $thoughtSignatureSchema], 'required' => [self::KEY_TYPE, self::KEY_TEXT], 'additionalProperties' => \false], ['type' => 'object', 'properties' => [self::KEY_CHANNEL => $channelSchema, self::KEY_TYPE => ['type' => 'string', 'const' => MessagePartTypeEnum::file()->value], self::KEY_FILE => File::getJsonSchema(), self::KEY_THOUGHT_SIGNATURE => $thoughtSignatureSchema], 'required' => [self::KEY_TYPE, self::KEY_FILE], 'additionalProperties' => \false], ['type' => 'object', 'properties' => [self::KEY_CHANNEL => $channelSchema, self::KEY_TYPE => ['type' => 'string', 'const' => MessagePartTypeEnum::functionCall()->value], self::KEY_FUNCTION_CALL => FunctionCall::getJsonSchema(), self::KEY_THOUGHT_SIGNATURE => $thoughtSignatureSchema], 'required' => [self::KEY_TYPE, self::KEY_FUNCTION_CALL], 'additionalProperties' => \false], ['type' => 'object', 'properties' => [self::KEY_CHANNEL => $channelSchema, self::KEY_TYPE => ['type' => 'string', 'const' => MessagePartTypeEnum::functionResponse()->value], self::KEY_FUNCTION_RESPONSE => FunctionResponse::getJsonSchema(), self::KEY_THOUGHT_SIGNATURE => $thoughtSignatureSchema], 'required' => [self::KEY_TYPE, self::KEY_FUNCTION_RESPONSE], 'additionalProperties' => \false]]]; 193 } 194 /** 195 * {@inheritDoc} 196 * 197 * @since 0.1.0 198 * 199 * @return MessagePartArrayShape 200 */ 201 public function toArray(): array 202 { 203 $data = [self::KEY_CHANNEL => $this->channel->value, self::KEY_TYPE => $this->type->value]; 204 if ($this->text !== null) { 205 $data[self::KEY_TEXT] = $this->text; 206 } elseif ($this->file !== null) { 207 $data[self::KEY_FILE] = $this->file->toArray(); 208 } elseif ($this->functionCall !== null) { 209 $data[self::KEY_FUNCTION_CALL] = $this->functionCall->toArray(); 210 } elseif ($this->functionResponse !== null) { 211 $data[self::KEY_FUNCTION_RESPONSE] = $this->functionResponse->toArray(); 212 } else { 213 throw new RuntimeException('MessagePart requires one of: text, file, functionCall, or functionResponse. ' . 'This should not be a possible condition.'); 214 } 215 if ($this->thoughtSignature !== null) { 216 $data[self::KEY_THOUGHT_SIGNATURE] = $this->thoughtSignature; 217 } 218 return $data; 219 } 220 /** 221 * {@inheritDoc} 222 * 223 * @since 0.1.0 224 */ 225 public static function fromArray(array $array): self 226 { 227 if (isset($array[self::KEY_CHANNEL])) { 228 $channel = MessagePartChannelEnum::from($array[self::KEY_CHANNEL]); 229 } else { 230 $channel = null; 231 } 232 $thoughtSignature = $array[self::KEY_THOUGHT_SIGNATURE] ?? null; 233 // Check which properties are set to determine how to construct the MessagePart 234 if (isset($array[self::KEY_TEXT])) { 235 return new self($array[self::KEY_TEXT], $channel, $thoughtSignature); 236 } elseif (isset($array[self::KEY_FILE])) { 237 return new self(File::fromArray($array[self::KEY_FILE]), $channel, $thoughtSignature); 238 } elseif (isset($array[self::KEY_FUNCTION_CALL])) { 239 return new self(FunctionCall::fromArray($array[self::KEY_FUNCTION_CALL]), $channel, $thoughtSignature); 240 } elseif (isset($array[self::KEY_FUNCTION_RESPONSE])) { 241 return new self(FunctionResponse::fromArray($array[self::KEY_FUNCTION_RESPONSE]), $channel, $thoughtSignature); 242 } else { 243 throw new InvalidArgumentException('MessagePart requires one of: text, file, functionCall, or functionResponse.'); 244 } 245 } 246 /** 247 * Performs a deep clone of the message part. 248 * 249 * This method ensures that nested objects (file, function call, function response) 250 * are cloned to prevent modifications to the cloned part from affecting the original. 251 * 252 * @since 0.4.2 253 */ 254 public function __clone() 255 { 256 if ($this->file !== null) { 257 $this->file = clone $this->file; 258 } 259 if ($this->functionCall !== null) { 260 $this->functionCall = clone $this->functionCall; 261 } 262 if ($this->functionResponse !== null) { 263 $this->functionResponse = clone $this->functionResponse; 264 } 265 } 266 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sat Jun 13 09:38:55 2026 | Cross-referenced by PHPXref |