[ 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\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  }


Generated : Tue Nov 3 08:20:36 2026 Cross-referenced by PHPXref