Skip to content

Commit d9aa07e

Browse files
committed
AI: Improve AI Client error handling.
Prior to this changeset, the WordPress AI Client would handle every exception thrown in the PHP AI Client in the same way, losing meaningful nuance about different kinds of errors along the way. Now, the `WP_Error` objects returned by the WordPress AI Client come with more helpful error codes, and they include semantic HTTP status codes as well so that they can be used in relevant contexts out of the box, like e.g. the REST API. Props flixos90, gziolo. Fixes #64867. See #64591. git-svn-id: https://develop.svn.wordpress.org/trunk@62037 602fd350-edb4-49c9-b593-d223f7449a82
1 parent dea662b commit d9aa07e

2 files changed

Lines changed: 265 additions & 28 deletions

File tree

src/wp-includes/ai-client/class-wp-ai-client-prompt-builder.php

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,18 @@
88
*/
99

1010
use WordPress\AiClient\Builders\PromptBuilder;
11+
use WordPress\AiClient\Common\Exception\InvalidArgumentException;
12+
use WordPress\AiClient\Common\Exception\TokenLimitReachedException;
1113
use WordPress\AiClient\Files\DTO\File;
1214
use WordPress\AiClient\Files\Enums\FileTypeEnum;
1315
use WordPress\AiClient\Files\Enums\MediaOrientationEnum;
1416
use WordPress\AiClient\Messages\DTO\Message;
1517
use WordPress\AiClient\Messages\DTO\MessagePart;
1618
use WordPress\AiClient\Messages\Enums\ModalityEnum;
1719
use WordPress\AiClient\Providers\Http\DTO\RequestOptions;
20+
use WordPress\AiClient\Providers\Http\Exception\ClientException;
21+
use WordPress\AiClient\Providers\Http\Exception\NetworkException;
22+
use WordPress\AiClient\Providers\Http\Exception\ServerException;
1823
use WordPress\AiClient\Providers\Models\Contracts\ModelInterface;
1924
use WordPress\AiClient\Providers\Models\DTO\ModelConfig;
2025
use WordPress\AiClient\Providers\Models\Enums\CapabilityEnum;
@@ -179,13 +184,7 @@ public function __construct( ProviderRegistry $registry, $prompt = null ) {
179184
$this->builder = new PromptBuilder( $registry, $prompt );
180185
} catch ( Exception $e ) {
181186
$this->builder = new PromptBuilder( $registry );
182-
$this->error = new WP_Error(
183-
'prompt_builder_error',
184-
$e->getMessage(),
185-
array(
186-
'exception_class' => get_class( $e ),
187-
)
188-
);
187+
$this->error = $this->exception_to_wp_error( $e );
189188
}
190189

191190
/**
@@ -311,7 +310,7 @@ public function __call( string $name, array $arguments ) {
311310
'prompt_prevented',
312311
__( 'Prompt execution was prevented by a filter.' ),
313312
array(
314-
'exception_class' => 'WP_AI_Client_Prompt_Prevented',
313+
'status' => 503,
315314
)
316315
);
317316

@@ -333,13 +332,7 @@ public function __call( string $name, array $arguments ) {
333332

334333
return $result;
335334
} catch ( Exception $e ) {
336-
$this->error = new WP_Error(
337-
'prompt_builder_error',
338-
$e->getMessage(),
339-
array(
340-
'exception_class' => get_class( $e ),
341-
)
342-
);
335+
$this->error = $this->exception_to_wp_error( $e );
343336

344337
if ( self::is_generating_method( $name ) ) {
345338
return $this->error;
@@ -348,6 +341,51 @@ public function __call( string $name, array $arguments ) {
348341
}
349342
}
350343

344+
/**
345+
* Converts an exception into a WP_Error with a structured error code and message.
346+
*
347+
* This method maps different exception types to specific WP_Error codes and HTTP status codes.
348+
* The presence of the status codes means these WP_Error objects can be easily used in REST API responses
349+
* or other contexts where HTTP semantics are relevant.
350+
*
351+
* @since 7.0.0
352+
*
353+
* @param Exception $e The exception to convert.
354+
* @return WP_Error The resulting WP_Error object.
355+
*/
356+
private function exception_to_wp_error( Exception $e ): WP_Error {
357+
if ( $e instanceof NetworkException ) {
358+
$error_code = 'prompt_network_error';
359+
$status_code = 503;
360+
} elseif ( $e instanceof ClientException ) {
361+
// `ClientException` uses HTTP status codes as exception codes, so we can rely on them.
362+
$error_code = 'prompt_client_error';
363+
$status_code = $e->getCode() ? $e->getCode() : 400;
364+
} elseif ( $e instanceof ServerException ) {
365+
// `ServerException` uses HTTP status codes as exception codes, so we can rely on them.
366+
$error_code = 'prompt_upstream_server_error';
367+
$status_code = $e->getCode() ? $e->getCode() : 500;
368+
} elseif ( $e instanceof TokenLimitReachedException ) {
369+
$error_code = 'prompt_token_limit_reached';
370+
$status_code = 400;
371+
} elseif ( $e instanceof InvalidArgumentException ) {
372+
$error_code = 'prompt_invalid_argument';
373+
$status_code = 400;
374+
} else {
375+
$error_code = 'prompt_builder_error';
376+
$status_code = 500;
377+
}
378+
379+
return new WP_Error(
380+
$error_code,
381+
$e->getMessage(),
382+
array(
383+
'status' => $status_code,
384+
'exception_class' => get_class( $e ),
385+
)
386+
);
387+
}
388+
351389
/**
352390
* Checks if a method name is a support check method (is_supported*).
353391
*

0 commit comments

Comments
 (0)