Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@ $imageFile = AiClient::prompt('Generate an illustration of the PHP elephant in t

See the [`PromptBuilder` class](https://github.com/WordPress/php-ai-client/blob/trunk/src/Builders/PromptBuilder.php) and its public methods for all the ways you can configure the prompt.

### Configuring request options

You can configure HTTP transport options like timeout and maximum redirects using the `RequestOptions` DTO:

```php
use WordPress\AiClient\Providers\Http\DTO\RequestOptions;

// Set custom timeout for long-running requests
$options = new RequestOptions(120, 10);

// Or use defaults and modify
$options = RequestOptions::defaults()->withTimeout(60);
```

For implementation ideas in different environments (WordPress, Guzzle, cURL), check the transporter-specific examples in the SDK source and tests.

Comment on lines +81 to +96
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems far too specific to document in the overall README.md. Let's not add any documentation for this yet, because we're still missing an overall structure for documentation.

**More documentation is coming soon.**

## Further reading
Expand Down
66 changes: 62 additions & 4 deletions src/Providers/Http/DTO/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
* method: string,
* uri: string,
* headers: array<string, list<string>>,
* body?: string|null
* body?: string|null,
* options?: array<string, mixed>
* }
*
* @extends AbstractDataTransferObject<RequestArrayShape>
Expand All @@ -34,6 +35,7 @@ class Request extends AbstractDataTransferObject
public const KEY_URI = 'uri';
public const KEY_HEADERS = 'headers';
public const KEY_BODY = 'body';
public const KEY_OPTIONS = 'options';

/**
* @var HttpMethodEnum The HTTP method.
Expand All @@ -60,6 +62,11 @@ class Request extends AbstractDataTransferObject
*/
protected ?string $body = null;

/**
* @var RequestOptions|null The request options.
*/
protected ?RequestOptions $options = null;

/**
* Constructor.
*
Expand All @@ -69,18 +76,25 @@ class Request extends AbstractDataTransferObject
* @param string $uri The request URI.
* @param array<string, string|list<string>> $headers The request headers.
* @param string|array<string, mixed>|null $data The request data.
* @param RequestOptions|null $options The request options.
*
* @throws InvalidArgumentException If the URI is empty.
*/
public function __construct(HttpMethodEnum $method, string $uri, array $headers = [], $data = null)
{
public function __construct(
HttpMethodEnum $method,
string $uri,
array $headers = [],
$data = null,
?RequestOptions $options = null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some open discussion in #99 (comment) on whether this is the preferred approach, so please feel free to share your thoughts there. I'm only flagging this here - maybe what you have here is what we should be doing, but it's still undecided.

) {
if (empty($uri)) {
throw new InvalidArgumentException('URI cannot be empty.');
}

$this->method = $method;
$this->uri = $uri;
$this->headers = new HeadersCollection($headers);
$this->options = $options;

// Separate data and body based on type
if (is_string($data)) {
Expand Down Expand Up @@ -281,6 +295,33 @@ public function getData(): ?array
return $this->data;
}

/**
* Gets the request options.
*
* @since n.e.x.t
*
* @return RequestOptions|null The request options or null if not set.
*/
public function getOptions(): ?RequestOptions
{
return $this->options;
}

/**
* Returns a new instance with the specified options.
*
* @since n.e.x.t
*
* @param RequestOptions|null $options The request options.
* @return self A new instance with the options.
*/
public function withOptions(?RequestOptions $options): self
{
$new = clone $this;
$new->options = $options;
return $new;
}

/**
* {@inheritDoc}
*
Expand Down Expand Up @@ -311,6 +352,10 @@ public static function getJsonSchema(): array
'type' => ['string'],
'description' => 'The request body.',
],
self::KEY_OPTIONS => [
'type' => 'object',
'description' => 'The request options.',
],
],
'required' => [self::KEY_METHOD, self::KEY_URI, self::KEY_HEADERS],
];
Expand All @@ -337,6 +382,11 @@ public function toArray(): array
$array[self::KEY_BODY] = $body;
}

// Include options if present
if ($this->options !== null) {
$array[self::KEY_OPTIONS] = $this->options->toArray();
}

return $array;
}

Expand All @@ -349,11 +399,19 @@ public static function fromArray(array $array): self
{
static::validateFromArrayData($array, [self::KEY_METHOD, self::KEY_URI, self::KEY_HEADERS]);

$options = null;
if (isset($array[self::KEY_OPTIONS]) && is_array($array[self::KEY_OPTIONS])) {
/** @var array{timeout?: int|null, max_redirects?: int|null} $optionsArray */
$optionsArray = $array[self::KEY_OPTIONS];
$options = RequestOptions::fromArray($optionsArray);
}

return new self(
HttpMethodEnum::from($array[self::KEY_METHOD]),
$array[self::KEY_URI],
$array[self::KEY_HEADERS] ?? [],
$array[self::KEY_BODY] ?? null
$array[self::KEY_BODY] ?? null,
$options
);
}

Expand Down
Loading