Changeset 63346
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/ai-client/class-wp-ai-client-prompt-builder.php
r63299 r63346 188 188 try { 189 189 $this->builder = new PromptBuilder( $registry, $prompt, AiClient::getEventDispatcher() ); 190 } catch ( Exception$e ) {190 } catch ( Throwable $e ) { 191 191 $this->builder = new PromptBuilder( $registry, null, AiClient::getEventDispatcher() ); 192 $this->error = $this-> exception_to_wp_error( $e );192 $this->error = $this->throwable_to_wp_error( $e ); 193 193 } 194 194 … … 378 378 379 379 return $result; 380 } catch ( Exception$e ) {381 $this->error = $this-> exception_to_wp_error( $e );380 } catch ( Throwable $e ) { 381 $this->error = $this->throwable_to_wp_error( $e ); 382 382 383 383 if ( self::is_generating_method( $name ) ) { … … 392 392 393 393 /** 394 * Converts a n exceptioninto a WP_Error with a structured error code and message.395 * 396 * This method maps different exceptiontypes to specific WP_Error codes and HTTP status codes.394 * Converts a throwable into a WP_Error with a structured error code and message. 395 * 396 * This method maps different throwable types to specific WP_Error codes and HTTP status codes. 397 397 * The presence of the status codes means these WP_Error objects can be easily used in REST API responses 398 398 * or other contexts where HTTP semantics are relevant. … … 400 400 * @since 7.0.0 401 401 * 402 * @param Exception $e The exceptionto convert.402 * @param Throwable $throwable The throwable to convert. 403 403 * @return WP_Error The resulting WP_Error object. 404 404 */ 405 private function exception_to_wp_error( Exception $e ): WP_Error {406 if ( $ e instanceof NetworkException ) {405 private function throwable_to_wp_error( Throwable $throwable ): WP_Error { 406 if ( $throwable instanceof NetworkException ) { 407 407 $error_code = 'prompt_network_error'; 408 408 $status_code = 503; 409 } elseif ( $ e instanceof ClientException ) {409 } elseif ( $throwable instanceof ClientException ) { 410 410 // `ClientException` uses HTTP status codes as exception codes, so we can rely on them. 411 411 $error_code = 'prompt_client_error'; 412 $status_code = $ e->getCode() ? $e->getCode() : 400;413 } elseif ( $ e instanceof ServerException ) {412 $status_code = $throwable->getCode() ? $throwable->getCode() : 400; 413 } elseif ( $throwable instanceof ServerException ) { 414 414 // `ServerException` uses HTTP status codes as exception codes, so we can rely on them. 415 415 $error_code = 'prompt_upstream_server_error'; 416 $status_code = $ e->getCode() ? $e->getCode() : 500;417 } elseif ( $ e instanceof TokenLimitReachedException ) {416 $status_code = $throwable->getCode() ? $throwable->getCode() : 500; 417 } elseif ( $throwable instanceof TokenLimitReachedException ) { 418 418 $error_code = 'prompt_token_limit_reached'; 419 419 $status_code = 400; 420 } elseif ( $ e instanceof InvalidArgumentException ) {420 } elseif ( $throwable instanceof InvalidArgumentException ) { 421 421 $error_code = 'prompt_invalid_argument'; 422 422 $status_code = 400; … … 428 428 return new WP_Error( 429 429 $error_code, 430 $ e->getMessage(),430 $throwable->getMessage(), 431 431 array( 432 432 'status' => $status_code, 433 'exception_class' => get_class( $ e ),433 'exception_class' => get_class( $throwable ), 434 434 ) 435 435 ); -
trunk/tests/phpunit/tests/ai-client/wpAiClientPromptBuilder.php
r63299 r63346 2912 2912 2913 2913 /** 2914 * Invokes the private exception_to_wp_error method via reflection.2914 * Invokes the private throwable_to_wp_error method via reflection. 2915 2915 * 2916 2916 * @param WP_AI_Client_Prompt_Builder $builder The builder instance. 2917 * @param Exception $exception The exceptionto convert.2917 * @param Throwable $throwable The throwable to convert. 2918 2918 * @return WP_Error The resulting WP_Error. 2919 2919 */ 2920 private function invoke_ exception_to_wp_error( WP_AI_Client_Prompt_Builder $builder, Exception $exception): WP_Error {2920 private function invoke_throwable_to_wp_error( WP_AI_Client_Prompt_Builder $builder, Throwable $throwable ): WP_Error { 2921 2921 $reflection = new ReflectionClass( WP_AI_Client_Prompt_Builder::class ); 2922 $method = $reflection->getMethod( ' exception_to_wp_error' );2922 $method = $reflection->getMethod( 'throwable_to_wp_error' ); 2923 2923 self::set_accessible( $method ); 2924 2924 2925 return $method->invoke( $builder, $ exception);2925 return $method->invoke( $builder, $throwable ); 2926 2926 } 2927 2927 … … 2933 2933 public function test_exception_to_wp_error_network_exception() { 2934 2934 $builder = new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry() ); 2935 $error = $this->invoke_ exception_to_wp_error(2935 $error = $this->invoke_throwable_to_wp_error( 2936 2936 $builder, 2937 2937 new NetworkException( 'Connection timed out' ) … … 2951 2951 public function test_exception_to_wp_error_client_exception_with_code() { 2952 2952 $builder = new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry() ); 2953 $error = $this->invoke_ exception_to_wp_error(2953 $error = $this->invoke_throwable_to_wp_error( 2954 2954 $builder, 2955 2955 new ClientException( 'Unauthorized', 401 ) … … 2969 2969 public function test_exception_to_wp_error_client_exception_without_code() { 2970 2970 $builder = new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry() ); 2971 $error = $this->invoke_ exception_to_wp_error(2971 $error = $this->invoke_throwable_to_wp_error( 2972 2972 $builder, 2973 2973 new ClientException( 'Bad request' ) … … 2986 2986 public function test_exception_to_wp_error_server_exception_with_code() { 2987 2987 $builder = new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry() ); 2988 $error = $this->invoke_ exception_to_wp_error(2988 $error = $this->invoke_throwable_to_wp_error( 2989 2989 $builder, 2990 2990 new ServerException( 'Bad gateway', 502 ) … … 3004 3004 public function test_exception_to_wp_error_server_exception_without_code() { 3005 3005 $builder = new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry() ); 3006 $error = $this->invoke_ exception_to_wp_error(3006 $error = $this->invoke_throwable_to_wp_error( 3007 3007 $builder, 3008 3008 new ServerException( 'Internal server error' ) … … 3021 3021 public function test_exception_to_wp_error_token_limit_reached_exception() { 3022 3022 $builder = new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry() ); 3023 $error = $this->invoke_ exception_to_wp_error(3023 $error = $this->invoke_throwable_to_wp_error( 3024 3024 $builder, 3025 3025 new TokenLimitReachedException( 'Token limit exceeded', 4096 ) … … 3039 3039 public function test_exception_to_wp_error_invalid_argument_exception() { 3040 3040 $builder = new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry() ); 3041 $error = $this->invoke_ exception_to_wp_error(3041 $error = $this->invoke_throwable_to_wp_error( 3042 3042 $builder, 3043 3043 new AiClientInvalidArgumentException( 'Invalid model parameter' ) … … 3057 3057 public function test_exception_to_wp_error_generic_exception() { 3058 3058 $builder = new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry() ); 3059 $error = $this->invoke_ exception_to_wp_error(3059 $error = $this->invoke_throwable_to_wp_error( 3060 3060 $builder, 3061 3061 new Exception( 'Something went wrong' ) … … 3069 3069 3070 3070 /** 3071 * Tests exception_to_wp_error maps an Error (e.g. TypeError) to a generic builder error. 3072 * 3073 * A TypeError extends Error, not Exception, so this guards against the 3074 * conversion only accepting Exception instances. 3075 * 3076 * @ticket 65505 3077 */ 3078 public function test_exception_to_wp_error_type_error() { 3079 $builder = new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry() ); 3080 $error = $this->invoke_throwable_to_wp_error( 3081 $builder, 3082 new TypeError( 'Argument must be of type float' ) 3083 ); 3084 3085 $this->assertSame( 'prompt_builder_error', $error->get_error_code() ); 3086 $this->assertSame( 'Argument must be of type float', $error->get_error_message() ); 3087 $this->assertSame( 500, $error->get_error_data()['status'] ); 3088 $this->assertSame( 'TypeError', $error->get_error_data()['exception_class'] ); 3089 } 3090 3091 /** 3092 * Tests that a TypeError thrown by the wrapped SDK is caught and returned as a WP_Error. 3093 * 3094 * Passing an argument of the wrong type to a strict-typed SDK method throws a 3095 * TypeError (which extends Error, not Exception). The builder must catch it and 3096 * place itself in an error state instead of letting it fatal the request. 3097 * 3098 * @ticket 65505 3099 */ 3100 public function test_call_catches_type_error_from_invalid_argument_type() { 3101 $builder = new WP_AI_Client_Prompt_Builder( $this->registry, 'Test prompt' ); 3102 3103 // usingTemperature() expects a float; an array can never be coerced and throws a TypeError. 3104 $result = $builder->using_temperature( array( 0.7 ) ); 3105 3106 // The builder is returned (fluent interface preserved), now in an error state. 3107 $this->assertInstanceOf( WP_AI_Client_Prompt_Builder::class, $result ); 3108 3109 // A generating method now surfaces the stored WP_Error rather than fataling. 3110 $error = $result->generate_text(); 3111 $this->assertWPError( $error ); 3112 $this->assertSame( 'prompt_builder_error', $error->get_error_code() ); 3113 } 3114 3115 /** 3071 3116 * Tests exception_to_wp_error always includes status and exception_class in error data. 3072 3117 * … … 3079 3124 public function test_exception_to_wp_error_error_data_structure( Exception $exception ) { 3080 3125 $builder = new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry() ); 3081 $error = $this->invoke_ exception_to_wp_error( $builder, $exception );3126 $error = $this->invoke_throwable_to_wp_error( $builder, $exception ); 3082 3127 3083 3128 $data = $error->get_error_data();
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)