[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/php-ai-client/src/Results/DTO/ -> GenerativeAiResult.php (source)

   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  }


Generated : Sat Jun 13 09:38:55 2026 Cross-referenced by PHPXref