| [ 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\Results\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\DTO\Message; 11 use WordPress\AiClient\Providers\DTO\ProviderMetadata; 12 use WordPress\AiClient\Providers\Models\DTO\ModelMetadata; 13 use WordPress\AiClient\Results\Contracts\ResultInterface; 14 /** 15 * Represents the result of a generative AI operation. 16 * 17 * This DTO contains the generated candidates along with usage statistics 18 * and metadata from the AI provider. 19 * 20 * @since 0.1.0 21 * 22 * @phpstan-import-type CandidateArrayShape from Candidate 23 * @phpstan-import-type TokenUsageArrayShape from TokenUsage 24 * @phpstan-import-type ProviderMetadataArrayShape from ProviderMetadata 25 * @phpstan-import-type ModelMetadataArrayShape from ModelMetadata 26 * 27 * @phpstan-type GenerativeAiResultArrayShape array{ 28 * id: string, 29 * candidates: array<CandidateArrayShape>, 30 * tokenUsage: TokenUsageArrayShape, 31 * providerMetadata: ProviderMetadataArrayShape, 32 * modelMetadata: ModelMetadataArrayShape, 33 * additionalData?: array<string, mixed> 34 * } 35 * 36 * @extends AbstractDataTransferObject<GenerativeAiResultArrayShape> 37 */ 38 class GenerativeAiResult extends AbstractDataTransferObject implements ResultInterface 39 { 40 public const KEY_ID = 'id'; 41 public const KEY_CANDIDATES = 'candidates'; 42 public const KEY_TOKEN_USAGE = 'tokenUsage'; 43 public const KEY_PROVIDER_METADATA = 'providerMetadata'; 44 public const KEY_MODEL_METADATA = 'modelMetadata'; 45 public const KEY_ADDITIONAL_DATA = 'additionalData'; 46 /** 47 * @var string Unique identifier for this result. 48 */ 49 private string $id; 50 /** 51 * @var Candidate[] The generated candidates. 52 */ 53 private array $candidates; 54 /** 55 * @var TokenUsage Token usage statistics. 56 */ 57 private \WordPress\AiClient\Results\DTO\TokenUsage $tokenUsage; 58 /** 59 * @var ProviderMetadata Provider metadata. 60 */ 61 private ProviderMetadata $providerMetadata; 62 /** 63 * @var ModelMetadata Model metadata. 64 */ 65 private ModelMetadata $modelMetadata; 66 /** 67 * @var array<string, mixed> Additional data. 68 */ 69 private array $additionalData; 70 /** 71 * Constructor. 72 * 73 * @since 0.1.0 74 * 75 * @param string $id Unique identifier for this result. 76 * @param Candidate[] $candidates The generated candidates. 77 * @param TokenUsage $tokenUsage Token usage statistics. 78 * @param ProviderMetadata $providerMetadata Provider metadata. 79 * @param ModelMetadata $modelMetadata Model metadata. 80 * @param array<string, mixed> $additionalData Additional data. 81 * @throws InvalidArgumentException If no candidates provided. 82 */ 83 public function __construct(string $id, array $candidates, \WordPress\AiClient\Results\DTO\TokenUsage $tokenUsage, ProviderMetadata $providerMetadata, ModelMetadata $modelMetadata, array $additionalData = []) 84 { 85 if (empty($candidates)) { 86 throw new InvalidArgumentException('At least one candidate must be provided'); 87 } 88 $this->id = $id; 89 $this->candidates = $candidates; 90 $this->tokenUsage = $tokenUsage; 91 $this->providerMetadata = $providerMetadata; 92 $this->modelMetadata = $modelMetadata; 93 $this->additionalData = $additionalData; 94 } 95 /** 96 * {@inheritDoc} 97 * 98 * @since 0.1.0 99 */ 100 public function getId(): string 101 { 102 return $this->id; 103 } 104 /** 105 * Gets the generated candidates. 106 * 107 * @since 0.1.0 108 * 109 * @return Candidate[] The candidates. 110 */ 111 public function getCandidates(): array 112 { 113 return $this->candidates; 114 } 115 /** 116 * {@inheritDoc} 117 * 118 * @since 0.1.0 119 */ 120 public function getTokenUsage(): \WordPress\AiClient\Results\DTO\TokenUsage 121 { 122 return $this->tokenUsage; 123 } 124 /** 125 * Gets the provider metadata. 126 * 127 * @since 0.1.0 128 * 129 * @return ProviderMetadata The provider metadata. 130 */ 131 public function getProviderMetadata(): ProviderMetadata 132 { 133 return $this->providerMetadata; 134 } 135 /** 136 * Gets the model metadata. 137 * 138 * @since 0.1.0 139 * 140 * @return ModelMetadata The model metadata. 141 */ 142 public function getModelMetadata(): ModelMetadata 143 { 144 return $this->modelMetadata; 145 } 146 /** 147 * {@inheritDoc} 148 * 149 * @since 0.1.0 150 */ 151 public function getAdditionalData(): array 152 { 153 return $this->additionalData; 154 } 155 /** 156 * Gets the total number of candidates. 157 * 158 * @since 0.1.0 159 * 160 * @return int The total number of candidates. 161 */ 162 public function getCandidateCount(): int 163 { 164 return count($this->candidates); 165 } 166 /** 167 * Checks if the result has multiple candidates. 168 * 169 * @since 0.1.0 170 * 171 * @return bool True if there are multiple candidates, false otherwise. 172 */ 173 public function hasMultipleCandidates(): bool 174 { 175 return $this->getCandidateCount() > 1; 176 } 177 /** 178 * Converts the first candidate to text. 179 * 180 * Only text from the content channel is considered. Text within model thought or reasoning is ignored. 181 * 182 * @since 0.1.0 183 * 184 * @return string The text content. 185 * @throws RuntimeException If no text content. 186 */ 187 public function toText(): string 188 { 189 $message = $this->candidates[0]->getMessage(); 190 foreach ($message->getParts() as $part) { 191 $channel = $part->getChannel(); 192 $text = $part->getText(); 193 if ($channel->isContent() && $text !== null) { 194 return $text; 195 } 196 } 197 throw new RuntimeException('No text content found in first candidate'); 198 } 199 /** 200 * Converts the first candidate to a file. 201 * 202 * Only files from the content channel are considered. Files within model thought or reasoning are ignored. 203 * 204 * @since 0.1.0 205 * 206 * @return File The file. 207 * @throws RuntimeException If no file content. 208 */ 209 public function toFile(): File 210 { 211 $message = $this->candidates[0]->getMessage(); 212 foreach ($message->getParts() as $part) { 213 $channel = $part->getChannel(); 214 $file = $part->getFile(); 215 if ($channel->isContent() && $file !== null) { 216 return $file; 217 } 218 } 219 throw new RuntimeException('No file content found in first candidate'); 220 } 221 /** 222 * Converts the first candidate to an image file. 223 * 224 * @since 0.1.0 225 * 226 * @return File The image file. 227 * @throws RuntimeException If no image content. 228 */ 229 public function toImageFile(): File 230 { 231 $file = $this->toFile(); 232 if (!$file->isImage()) { 233 throw new RuntimeException(sprintf('File is not an image. MIME type: %s', $file->getMimeType())); 234 } 235 return $file; 236 } 237 /** 238 * Converts the first candidate to an audio file. 239 * 240 * @since 0.1.0 241 * 242 * @return File The audio file. 243 * @throws RuntimeException If no audio content. 244 */ 245 public function toAudioFile(): File 246 { 247 $file = $this->toFile(); 248 if (!$file->isAudio()) { 249 throw new RuntimeException(sprintf('File is not an audio file. MIME type: %s', $file->getMimeType())); 250 } 251 return $file; 252 } 253 /** 254 * Converts the first candidate to a video file. 255 * 256 * @since 0.1.0 257 * 258 * @return File The video file. 259 * @throws RuntimeException If no video content. 260 */ 261 public function toVideoFile(): File 262 { 263 $file = $this->toFile(); 264 if (!$file->isVideo()) { 265 throw new RuntimeException(sprintf('File is not a video file. MIME type: %s', $file->getMimeType())); 266 } 267 return $file; 268 } 269 /** 270 * Converts the first candidate to a message. 271 * 272 * @since 0.1.0 273 * 274 * @return Message The message. 275 */ 276 public function toMessage(): Message 277 { 278 return $this->candidates[0]->getMessage(); 279 } 280 /** 281 * Converts all candidates to text. 282 * 283 * @since 0.1.0 284 * 285 * @return list<string> Array of text content. 286 */ 287 public function toTexts(): array 288 { 289 $texts = []; 290 foreach ($this->candidates as $candidate) { 291 $message = $candidate->getMessage(); 292 foreach ($message->getParts() as $part) { 293 $channel = $part->getChannel(); 294 $text = $part->getText(); 295 if ($channel->isContent() && $text !== null) { 296 $texts[] = $text; 297 break; 298 } 299 } 300 } 301 return $texts; 302 } 303 /** 304 * Converts all candidates to files. 305 * 306 * @since 0.1.0 307 * 308 * @return list<File> Array of files. 309 */ 310 public function toFiles(): array 311 { 312 $files = []; 313 foreach ($this->candidates as $candidate) { 314 $message = $candidate->getMessage(); 315 foreach ($message->getParts() as $part) { 316 $channel = $part->getChannel(); 317 $file = $part->getFile(); 318 if ($channel->isContent() && $file !== null) { 319 $files[] = $file; 320 break; 321 } 322 } 323 } 324 return $files; 325 } 326 /** 327 * Converts all candidates to image files. 328 * 329 * @since 0.1.0 330 * 331 * @return list<File> Array of image files. 332 */ 333 public function toImageFiles(): array 334 { 335 return array_values(array_filter($this->toFiles(), fn(File $file) => $file->isImage())); 336 } 337 /** 338 * Converts all candidates to audio files. 339 * 340 * @since 0.1.0 341 * 342 * @return list<File> Array of audio files. 343 */ 344 public function toAudioFiles(): array 345 { 346 return array_values(array_filter($this->toFiles(), fn(File $file) => $file->isAudio())); 347 } 348 /** 349 * Converts all candidates to video files. 350 * 351 * @since 0.1.0 352 * 353 * @return list<File> Array of video files. 354 */ 355 public function toVideoFiles(): array 356 { 357 return array_values(array_filter($this->toFiles(), fn(File $file) => $file->isVideo())); 358 } 359 /** 360 * Converts all candidates to messages. 361 * 362 * @since 0.1.0 363 * 364 * @return list<Message> Array of messages. 365 */ 366 public function toMessages(): array 367 { 368 return array_values(array_map(fn(\WordPress\AiClient\Results\DTO\Candidate $candidate) => $candidate->getMessage(), $this->candidates)); 369 } 370 /** 371 * {@inheritDoc} 372 * 373 * @since 0.1.0 374 */ 375 public static function getJsonSchema(): array 376 { 377 return ['type' => 'object', 'properties' => [self::KEY_ID => ['type' => 'string', 'description' => 'Unique identifier for this result.'], self::KEY_CANDIDATES => ['type' => 'array', 'items' => \WordPress\AiClient\Results\DTO\Candidate::getJsonSchema(), 'minItems' => 1, 'description' => 'The generated candidates.'], self::KEY_TOKEN_USAGE => \WordPress\AiClient\Results\DTO\TokenUsage::getJsonSchema(), self::KEY_PROVIDER_METADATA => ProviderMetadata::getJsonSchema(), self::KEY_MODEL_METADATA => ModelMetadata::getJsonSchema(), self::KEY_ADDITIONAL_DATA => ['type' => 'object', 'additionalProperties' => \true, 'description' => 'Additional data included in the API response.']], 'required' => [self::KEY_ID, self::KEY_CANDIDATES, self::KEY_TOKEN_USAGE, self::KEY_PROVIDER_METADATA, self::KEY_MODEL_METADATA]]; 378 } 379 /** 380 * {@inheritDoc} 381 * 382 * @since 0.1.0 383 * 384 * @return GenerativeAiResultArrayShape 385 */ 386 public function toArray(): array 387 { 388 return [self::KEY_ID => $this->id, self::KEY_CANDIDATES => array_map(fn(\WordPress\AiClient\Results\DTO\Candidate $candidate) => $candidate->toArray(), $this->candidates), self::KEY_TOKEN_USAGE => $this->tokenUsage->toArray(), self::KEY_PROVIDER_METADATA => $this->providerMetadata->toArray(), self::KEY_MODEL_METADATA => $this->modelMetadata->toArray(), self::KEY_ADDITIONAL_DATA => $this->additionalData]; 389 } 390 /** 391 * {@inheritDoc} 392 * 393 * @since 0.1.0 394 */ 395 public static function fromArray(array $array): self 396 { 397 static::validateFromArrayData($array, [self::KEY_ID, self::KEY_CANDIDATES, self::KEY_TOKEN_USAGE, self::KEY_PROVIDER_METADATA, self::KEY_MODEL_METADATA]); 398 $candidates = array_map(fn(array $candidateData) => \WordPress\AiClient\Results\DTO\Candidate::fromArray($candidateData), $array[self::KEY_CANDIDATES]); 399 return new self($array[self::KEY_ID], $candidates, \WordPress\AiClient\Results\DTO\TokenUsage::fromArray($array[self::KEY_TOKEN_USAGE]), ProviderMetadata::fromArray($array[self::KEY_PROVIDER_METADATA]), ModelMetadata::fromArray($array[self::KEY_MODEL_METADATA]), $array[self::KEY_ADDITIONAL_DATA] ?? []); 400 } 401 /** 402 * Performs a deep clone of the result. 403 * 404 * This method ensures that all nested objects (candidates, token usage, metadata) 405 * are cloned to prevent modifications to the cloned result from affecting the original. 406 * 407 * @since 0.4.2 408 */ 409 public function __clone() 410 { 411 $clonedCandidates = []; 412 foreach ($this->candidates as $candidate) { 413 $clonedCandidates[] = clone $candidate; 414 } 415 $this->candidates = $clonedCandidates; 416 $this->tokenUsage = clone $this->tokenUsage; 417 $this->providerMetadata = clone $this->providerMetadata; 418 $this->modelMetadata = clone $this->modelMetadata; 419 } 420 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sat Jun 13 09:38:55 2026 | Cross-referenced by PHPXref |