[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/php-ai-client/src/Builders/ -> MessageBuilder.php (source)

   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  }


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