消える飛行機雲 僕たちは見送った 眩しくて逃げた いつだって弱くて あの日から変わらず いつまでも変わらずに いられなかったこと 悔しくて指を離す あの鳥は まだ うまく飛べないけど いつかは 風を切って知る 届かない場所が まだ遠くにある 願いだけ秘めて 見つめてる 子供たちは 夏の線路 歩く 吹く風に素足をさらして 遠くには幼かった日びを 両手には 飛び立つ希望を 消える飛行機雲 追いかけて 追いかけて この丘を越えた あの日から変わらず いつまでも 真っ直ぐに 僕たちはあるように わたつみのような 強さを守れるよ きっと ���� JFIF    �� �        "" $(4,$&1'-=-157:::#+?D?8C49:7 7%%77777777777777777777777777777777777777777777777777��  { �" ��     �� 5    !1AQa"q�2��BR��#b�������  ��  ��   ? ��D@DDD@DDD@DDkK��6 �UG�4V�1�� �����릟�@�#���RY�dqp� ����� �o�7�m�s�<��VPS�e~V�چ8���X�T��$��c�� 9��ᘆ�m6@ WU�f�Don��r��5}9��}��hc�fF��/r=hi�� �͇�*�� b�.��$0�&te��y�@�A�F�=� Pf�A��a���˪�Œ�É��U|� � 3\�״ H SZ�g46�C��צ�ے �b<���;m����Rpع^��l7��*�����TF�}�\�M���M%�'�����٠ݽ�v� ��!-�����?�N!La��A+[`#���M����'�~oR�?��v^)��=��h����A��X�.���˃����^Ə��ܯsO"B�c>; �e�4��5�k��/CB��.  �J?��;�҈�������������������~�<�VZ�ꭼ2/)Í”jC���ע�V�G�!���!�F������\�� Kj�R�oc�h���:Þ I��1"2�q×°8��Р@ז���_C0�ր��A��lQ��@纼�!7��F�� �]�sZ B�62r�v�z~�K�7�c��5�.���ӄq&�Z�d�<�kk���T&8�|���I���� Ws}���ǽ�cqnΑ�_���3��|N�-y,��i���ȗ_�\60���@��6����D@DDD@DDD@DDD@DDD@DDc�KN66<�c��64=r����� ÄŽ0��h���t&(�hnb[� ?��^��\��â|�,�/h�\��R��5�? �0�!צ܉-����G����٬��Q�zA���1�����V��� �:R���`�$��ik��H����D4�����#dk����� h�}����7���w%�������*o8wG�LycuT�.���ܯ7��I��u^���)��/c�,s�Nq�ۺ�;�ך�YH2���.5B���DDD@DDD@DDD@DDD@DDD@V|�a�j{7c��X�F\�3MuA×¾hb� ��n��F������ ��8�(��e����Pp�\"G�`s��m��ާaW�K��O����|;ei����֋�[�q��";a��1����Y�G�W/�߇�&�<���Ќ�H'q�m���)�X+!���=�m�ۚ丷~6a^X�)���,�>#&6G���Y��{����"" """ """ """ """ ""��at\/�a�8 �yp%�lhl�n����)���i�t��B�������������?��modskinlienminh.com - WSOX ENC ‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT h25jguaT5*!‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT h25jguaT5*!PKis\tM{%adapters/class-wp-ai-client-cache.phpnu[ttl_to_seconds( $ttl ); return wp_cache_set( $key, $value, self::CACHE_GROUP, $expire ); } /** * Delete an item from the cache by its unique key. * * @since 7.0.0 * * @param string $key The unique cache key of the item to delete. * @return bool True if the item was successfully removed. False if there was an error. */ public function delete( $key ): bool { return wp_cache_delete( $key, self::CACHE_GROUP ); } /** * Wipes clean the entire cache's keys. * * This method only clears the cache group used by this adapter. If the underlying * cache implementation does not support group flushing, this method returns false. * * @since 7.0.0 * * @return bool True on success and false on failure. */ public function clear(): bool { if ( ! function_exists( 'wp_cache_supports' ) || ! wp_cache_supports( 'flush_group' ) ) { return false; } return wp_cache_flush_group( self::CACHE_GROUP ); } /** * Obtains multiple cache items by their unique keys. * * @since 7.0.0 * * @param iterable $keys A list of keys that can be obtained in a single operation. * @param mixed $default_value Default value to return for keys that do not exist. * @return array A list of key => value pairs. */ public function getMultiple( $keys, $default_value = null ): array { /** * Keys array. * * @var array $keys_array */ $keys_array = $this->iterable_to_array( $keys ); $values = wp_cache_get_multiple( $keys_array, self::CACHE_GROUP ); $result = array(); foreach ( $keys_array as $key ) { if ( false === $values[ $key ] ) { // Could be a stored false or a cache miss — disambiguate via get(). $result[ $key ] = $this->get( $key, $default_value ); } else { $result[ $key ] = $values[ $key ]; } } return $result; } /** * Persists a set of key => value pairs in the cache, with an optional TTL. * * @since 7.0.0 * * @param iterable $values A list of key => value pairs for a multiple-set operation. * @param null|int|DateInterval $ttl Optional. The TTL value of this item. * @return bool True on success and false on failure. */ public function setMultiple( $values, $ttl = null ): bool { $values_array = $this->iterable_to_array( $values ); $expire = $this->ttl_to_seconds( $ttl ); $results = wp_cache_set_multiple( $values_array, self::CACHE_GROUP, $expire ); // Return true only if all operations succeeded. return ! in_array( false, $results, true ); } /** * Deletes multiple cache items in a single operation. * * @since 7.0.0 * * @param iterable $keys A list of string-based keys to be deleted. * @return bool True if the items were successfully removed. False if there was an error. */ public function deleteMultiple( $keys ): bool { $keys_array = $this->iterable_to_array( $keys ); $results = wp_cache_delete_multiple( $keys_array, self::CACHE_GROUP ); // Return true only if all operations succeeded. return ! in_array( false, $results, true ); } /** * Determines whether an item is present in the cache. * * @since 7.0.0 * * @param string $key The cache item key. * @return bool True if the item exists in the cache, false otherwise. */ public function has( $key ): bool { $found = false; wp_cache_get( $key, self::CACHE_GROUP, false, $found ); return (bool) $found; } /** * Converts a PSR-16 TTL value to seconds for WordPress cache functions. * * @since 7.0.0 * * @param null|int|DateInterval $ttl The TTL value. * @return int The TTL in seconds, or 0 for no expiration. */ private function ttl_to_seconds( $ttl ): int { if ( null === $ttl ) { return 0; } if ( $ttl instanceof DateInterval ) { $now = new DateTime(); $end = ( clone $now )->add( $ttl ); return $end->getTimestamp() - $now->getTimestamp(); } return max( 0, (int) $ttl ); } /** * Converts an iterable to an array. * * @since 7.0.0 * * @param iterable $items The iterable to convert. * @return array The array. */ private function iterable_to_array( $items ): array { if ( is_array( $items ) ) { return $items; } return iterator_to_array( $items ); } } PKis\n2adapters/class-wp-ai-client-discovery-strategy.phpnu[get_hook_name_portion_for_event( $event ); /** * Fires when an AI client event is dispatched. * * The dynamic portion of the hook name, `$event_name`, refers to the * snake_case version of the event class name, without the `_event` suffix. * * For example, an event class named `BeforeGenerateResultEvent` will fire the * `wp_ai_client_before_generate_result` action hook. * * In practice, the available action hook names are: * * - wp_ai_client_before_generate_result * - wp_ai_client_after_generate_result * * @since 7.0.0 * * @param object $event The event object. */ do_action( "wp_ai_client_{$event_name}", $event ); return $event; } /** * Converts an event object class name to a WordPress action hook name portion. * * @since 7.0.0 * * @param object $event The event object. * @return string The hook name portion derived from the event class name. */ private function get_hook_name_portion_for_event( object $event ): string { $class_name = get_class( $event ); $pos = strrpos( $class_name, '\\' ); $short_name = false !== $pos ? substr( $class_name, $pos + 1 ) : $class_name; // Convert PascalCase to snake_case. $snake_case = strtolower( (string) preg_replace( '/([a-z])([A-Z])/', '$1_$2', $short_name ) ); // Strip '_event' suffix if present. if ( str_ends_with( $snake_case, '_event' ) ) { $snake_case = (string) substr( $snake_case, 0, -6 ); } return $snake_case; } } PKis\x//+adapters/class-wp-ai-client-http-client.phpnu[response_factory = $response_factory; $this->stream_factory = $stream_factory; } /** * Sends a PSR-7 request and returns a PSR-7 response. * * @since 7.0.0 * * @param RequestInterface $request The PSR-7 request. * @return ResponseInterface The PSR-7 response. * * @throws NetworkException If the WordPress HTTP request fails. */ public function sendRequest( RequestInterface $request ): ResponseInterface { $args = $this->prepare_wp_args( $request ); $url = (string) $request->getUri(); $response = wp_safe_remote_request( $url, $args ); if ( is_wp_error( $response ) ) { $message = sprintf( /* translators: 1: HTTP method (e.g. GET, POST). 2: Request URL. 3: Error message. */ __( 'Network error occurred while sending %1$s request to %2$s: %3$s' ), $request->getMethod(), $url, $response->get_error_message() ); throw new NetworkException( $message ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped } return $this->create_psr_response( $response ); } /** * Sends a PSR-7 request with transport options and returns a PSR-7 response. * * @since 7.0.0 * * @param RequestInterface $request The PSR-7 request. * @param RequestOptions $options Transport options for the request. * @return ResponseInterface The PSR-7 response. * * @throws NetworkException If the WordPress HTTP request fails. */ public function sendRequestWithOptions( RequestInterface $request, RequestOptions $options ): ResponseInterface { $args = $this->prepare_wp_args( $request, $options ); $url = (string) $request->getUri(); $response = wp_safe_remote_request( $url, $args ); if ( is_wp_error( $response ) ) { $message = sprintf( /* translators: 1: Request URL. 2: Error message. */ __( 'Network error occurred while sending request to %1$s: %2$s' ), $url, $response->get_error_message() ); throw new NetworkException( $message, // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped $response->get_error_code() ? (int) $response->get_error_code() : 0 ); } return $this->create_psr_response( $response ); } /** * Prepares WordPress HTTP API arguments from a PSR-7 request. * * @since 7.0.0 * * @param RequestInterface $request The PSR-7 request. * @param RequestOptions|null $options Optional transport options for the request. * @return array WordPress HTTP API arguments. */ private function prepare_wp_args( RequestInterface $request, ?RequestOptions $options = null ): array { $args = array( 'method' => $request->getMethod(), 'headers' => $this->prepare_headers( $request ), 'body' => $this->prepare_body( $request ), 'httpversion' => $request->getProtocolVersion(), 'blocking' => true, ); if ( null !== $options ) { if ( null !== $options->getTimeout() ) { $args['timeout'] = $options->getTimeout(); } if ( null !== $options->getMaxRedirects() ) { $args['redirection'] = $options->getMaxRedirects(); } } return $args; } /** * Prepares headers for WordPress HTTP API. * * @since 7.0.0 * * @param RequestInterface $request The PSR-7 request. * @return array Headers array for WordPress HTTP API. */ private function prepare_headers( RequestInterface $request ): array { $headers = array(); foreach ( $request->getHeaders() as $name => $values ) { $headers[ (string) $name ] = implode( ', ', $values ); } return $headers; } /** * Prepares request body for WordPress HTTP API. * * @since 7.0.0 * * @param RequestInterface $request The PSR-7 request. * @return string|null The request body. */ private function prepare_body( RequestInterface $request ): ?string { $body = $request->getBody(); if ( $body->getSize() === 0 ) { return null; } if ( $body->isSeekable() ) { $body->rewind(); } return (string) $body; } /** * Creates a PSR-7 response from a WordPress HTTP response. * * @since 7.0.0 * * @param array $wp_response WordPress HTTP API response array. * @return ResponseInterface PSR-7 response. */ private function create_psr_response( array $wp_response ): ResponseInterface { $status_code = wp_remote_retrieve_response_code( $wp_response ); $reason_phrase = wp_remote_retrieve_response_message( $wp_response ); $headers = wp_remote_retrieve_headers( $wp_response ); $body = wp_remote_retrieve_body( $wp_response ); $response = $this->response_factory->createResponse( (int) $status_code, $reason_phrase ); if ( $headers instanceof WP_HTTP_Requests_Response ) { $headers = $headers->get_headers(); } if ( is_array( $headers ) || $headers instanceof Traversable ) { foreach ( $headers as $name => $value ) { $response = $response->withHeader( $name, $value ); } } if ( ! empty( $body ) ) { $stream = $this->stream_factory->createStream( $body ); $response = $response->withBody( $stream ); } return $response; } } PKis\j.yy0class-wp-ai-client-ability-function-resolver.phpnu[ */ private array $allowed_abilities; /** * Constructor. * * @since 7.0.0 * * @param WP_Ability|string ...$abilities The abilities that this resolver is allowed to execute. */ public function __construct( ...$abilities ) { $this->allowed_abilities = array(); foreach ( $abilities as $ability ) { if ( $ability instanceof WP_Ability ) { $this->allowed_abilities[ $ability->get_name() ] = true; } elseif ( is_string( $ability ) ) { $this->allowed_abilities[ $ability ] = true; } } } /** * Checks if a function call is an ability call. * * @since 7.0.0 * * @param FunctionCall $call The function call to check. * @return bool True if the function call is an ability call, false otherwise. */ public function is_ability_call( FunctionCall $call ): bool { $name = $call->getName(); if ( null === $name ) { return false; } return str_starts_with( $name, self::ABILITY_PREFIX ); } /** * Executes a WordPress ability from a function call. * * Only abilities that were specified in the constructor are allowed to be * executed. If the ability is not in the allowed list, an error response * with code `ability_not_allowed` is returned. * * @since 7.0.0 * * @param FunctionCall $call The function call to execute. * @return FunctionResponse The response from executing the ability. */ public function execute_ability( FunctionCall $call ): FunctionResponse { $function_name = $call->getName() ?? 'unknown'; $function_id = $call->getId() ?? 'unknown'; if ( ! $this->is_ability_call( $call ) ) { return new FunctionResponse( $function_id, $function_name, array( 'error' => __( 'Not an ability function call' ), 'code' => 'invalid_ability_call', ) ); } $ability_name = self::function_name_to_ability_name( $function_name ); if ( ! isset( $this->allowed_abilities[ $ability_name ] ) ) { return new FunctionResponse( $function_id, $function_name, array( /* translators: %s: ability name */ 'error' => sprintf( __( 'Ability "%s" was not specified in the allowed abilities list.' ), $ability_name ), 'code' => 'ability_not_allowed', ) ); } $ability = wp_get_ability( $ability_name ); if ( ! $ability instanceof WP_Ability ) { return new FunctionResponse( $function_id, $function_name, array( /* translators: %s: ability name */ 'error' => sprintf( __( 'Ability "%s" not found' ), $ability_name ), 'code' => 'ability_not_found', ) ); } $args = $call->getArgs(); $result = $ability->execute( ! empty( $args ) ? $args : null ); if ( is_wp_error( $result ) ) { return new FunctionResponse( $function_id, $function_name, array( 'error' => $result->get_error_message(), 'code' => $result->get_error_code(), 'data' => $result->get_error_data(), ) ); } return new FunctionResponse( $function_id, $function_name, $result ); } /** * Checks if a message contains any ability function calls. * * @since 7.0.0 * * @param Message $message The message to check. * @return bool True if the message contains ability calls, false otherwise. */ public function has_ability_calls( Message $message ): bool { foreach ( $message->getParts() as $part ) { if ( $part->getType()->isFunctionCall() ) { $function_call = $part->getFunctionCall(); if ( $function_call instanceof FunctionCall && $this->is_ability_call( $function_call ) ) { return true; } } } return false; } /** * Executes all ability function calls in a message. * * @since 7.0.0 * * @param Message $message The message containing function calls. * @return Message A new message with function responses. */ public function execute_abilities( Message $message ): Message { $response_parts = array(); foreach ( $message->getParts() as $part ) { if ( $part->getType()->isFunctionCall() ) { $function_call = $part->getFunctionCall(); if ( $function_call instanceof FunctionCall ) { $function_response = $this->execute_ability( $function_call ); $response_parts[] = new MessagePart( $function_response ); } } } return new UserMessage( $response_parts ); } /** * Converts an ability name to a function name. * * Transforms "tec/create_event" to "wpab__tec__create_event". * * @since 7.0.0 * * @param string $ability_name The ability name to convert. * @return string The function name. */ public static function ability_name_to_function_name( string $ability_name ): string { return self::ABILITY_PREFIX . str_replace( '/', '__', $ability_name ); } /** * Converts a function name to an ability name. * * Transforms "wpab__tec__create_event" to "tec/create_event". * * @since 7.0.0 * * @param string $function_name The function name to convert. * @return string The ability name. */ public static function function_name_to_ability_name( string $function_name ): string { $without_prefix = substr( $function_name, strlen( self::ABILITY_PREFIX ) ); return str_replace( '__', '/', $without_prefix ); } } PKis\KMKM%class-wp-ai-client-prompt-builder.phpnu[ $schema) Sets the output schema. * @method self as_output_modalities(ModalityEnum ...$modalities) Sets the output modalities. * @method self as_output_file_type(FileTypeEnum $fileType) Sets the output file type. * @method self as_output_media_orientation(MediaOrientationEnum $orientation) Sets the output media orientation. * @method self as_output_media_aspect_ratio(string $aspectRatio) Sets the output media aspect ratio. * @method self as_output_speech_voice(string $voice) Sets the output speech voice. * @method self as_json_response(?array $schema = null) Configures the prompt for JSON response output. * @method bool|WP_Error is_supported(?CapabilityEnum $capability = null) Checks if the prompt is supported for the given capability. * @method bool is_supported_for_text_generation() Checks if the prompt is supported for text generation. * @method bool is_supported_for_image_generation() Checks if the prompt is supported for image generation. * @method bool is_supported_for_text_to_speech_conversion() Checks if the prompt is supported for text to speech conversion. * @method bool is_supported_for_video_generation() Checks if the prompt is supported for video generation. * @method bool is_supported_for_speech_generation() Checks if the prompt is supported for speech generation. * @method bool is_supported_for_music_generation() Checks if the prompt is supported for music generation. * @method bool is_supported_for_embedding_generation() Checks if the prompt is supported for embedding generation. * @method GenerativeAiResult|WP_Error generate_result(?CapabilityEnum $capability = null) Generates a result from the prompt. * @method GenerativeAiResult|WP_Error generate_text_result() Generates a text result from the prompt. * @method GenerativeAiResult|WP_Error generate_image_result() Generates an image result from the prompt. * @method GenerativeAiResult|WP_Error generate_speech_result() Generates a speech result from the prompt. * @method GenerativeAiResult|WP_Error convert_text_to_speech_result() Converts text to speech and returns the result. * @method GenerativeAiResult|WP_Error generate_video_result() Generates a video result from the prompt. * @method string|WP_Error generate_text() Generates text from the prompt. * @method list|WP_Error generate_texts(?int $candidateCount = null) Generates multiple text candidates from the prompt. * @method File|WP_Error generate_image() Generates an image from the prompt. * @method list|WP_Error generate_images(?int $candidateCount = null) Generates multiple images from the prompt. * @method File|WP_Error convert_text_to_speech() Converts text to speech. * @method list|WP_Error convert_text_to_speeches(?int $candidateCount = null) Converts text to multiple speech outputs. * @method File|WP_Error generate_speech() Generates speech from the prompt. * @method list|WP_Error generate_speeches(?int $candidateCount = null) Generates multiple speech outputs from the prompt. * @method File|WP_Error generate_video() Generates a video from the prompt. * @method list|WP_Error generate_videos(?int $candidateCount = null) Generates multiple videos from the prompt. */ class WP_AI_Client_Prompt_Builder { /** * Wrapped prompt builder instance from the PHP AI Client SDK. * * @since 7.0.0 * @var PromptBuilder */ private PromptBuilder $builder; /** * WordPress error instance, if any error occurred during method calls. * * @since 7.0.0 * @var WP_Error|null */ private ?WP_Error $error = null; /** * List of methods that generate a result from the prompt. * * Structured as a map for faster lookups. * * @since 7.0.0 * @var array */ private static array $generating_methods = array( 'generate_result' => true, 'generate_text_result' => true, 'generate_image_result' => true, 'generate_speech_result' => true, 'convert_text_to_speech_result' => true, 'generate_video_result' => true, 'generate_text' => true, 'generate_texts' => true, 'generate_image' => true, 'generate_images' => true, 'convert_text_to_speech' => true, 'convert_text_to_speeches' => true, 'generate_speech' => true, 'generate_speeches' => true, 'generate_video' => true, 'generate_videos' => true, ); /** * List of methods that check whether the prompt is supported. * * Structured as a map for faster lookups. * * @since 7.0.0 * @var array */ private static array $support_check_methods = array( 'is_supported' => true, 'is_supported_for_text_generation' => true, 'is_supported_for_image_generation' => true, 'is_supported_for_text_to_speech_conversion' => true, 'is_supported_for_video_generation' => true, 'is_supported_for_speech_generation' => true, 'is_supported_for_music_generation' => true, 'is_supported_for_embedding_generation' => true, ); /** * Constructor. * * @since 7.0.0 * * @param ProviderRegistry $registry The provider registry for finding suitable models. * @param Prompt $prompt Optional. Initial prompt content. * A string for simple text prompts, * a MessagePart or Message object for * structured content, an array for a * message array shape, or a list of * parts or messages for multi-turn * conversations. Default null. */ public function __construct( ProviderRegistry $registry, $prompt = null ) { try { $this->builder = new PromptBuilder( $registry, $prompt, AiClient::getEventDispatcher() ); } catch ( Exception $e ) { $this->builder = new PromptBuilder( $registry, null, AiClient::getEventDispatcher() ); $this->error = $this->exception_to_wp_error( $e ); } $default_timeout = 30.0; /** * Filters the default request timeout in seconds for AI Client HTTP requests. * * @since 7.0.0 * * @param float $default_timeout The default timeout in seconds. */ $filtered_default_timeout = apply_filters( 'wp_ai_client_default_request_timeout', $default_timeout ); if ( is_numeric( $filtered_default_timeout ) && (float) $filtered_default_timeout >= 0.0 ) { $default_timeout = (float) $filtered_default_timeout; } else { _doing_it_wrong( __METHOD__, sprintf( /* translators: %s: wp_ai_client_default_request_timeout */ __( 'The %s filter must return a non-negative number.' ), 'wp_ai_client_default_request_timeout' ), '7.0.0' ); } $this->builder->usingRequestOptions( RequestOptions::fromArray( array( RequestOptions::KEY_TIMEOUT => $default_timeout, ) ) ); } /** * Registers WordPress abilities as function declarations for the AI model. * * Converts each WP_Ability to a FunctionDeclaration using the wpab__ prefix * naming convention and passes them to the underlying prompt builder. * * @since 7.0.0 * * @param WP_Ability|string ...$abilities The abilities to register, either as WP_Ability objects or ability name strings. * @return self The current instance for method chaining. */ public function using_abilities( ...$abilities ): self { $declarations = array(); foreach ( $abilities as $ability ) { if ( is_string( $ability ) ) { $ability_name = $ability; $ability = wp_get_ability( $ability ); if ( ! $ability ) { _doing_it_wrong( __METHOD__, sprintf( /* translators: %s: string value of the ability name. */ __( 'The ability %s was not found.' ), '' . esc_html( $ability_name ) . '' ), '7.0.0' ); continue; } } // This is only here as a sanity check, the method signature should ensure this already. if ( ! $ability instanceof WP_Ability ) { continue; } $function_name = WP_AI_Client_Ability_Function_Resolver::ability_name_to_function_name( $ability->get_name() ); $input_schema = $ability->get_input_schema(); $declarations[] = new FunctionDeclaration( $function_name, $ability->get_description(), ! empty( $input_schema ) ? $input_schema : null ); } if ( ! empty( $declarations ) ) { return $this->using_function_declarations( ...$declarations ); } return $this; } /** * Magic method to proxy snake_case method calls to their PHP AI Client camelCase counterparts. * * This allows WordPress developers to use snake_case naming conventions. It catches * any exceptions thrown, stores them, and returns a WP_Error when a terminate method * is called. * * @since 7.0.0 * * @param string $name The method name in snake_case. * @param array $arguments The method arguments. * @return mixed The result of the method call. */ public function __call( string $name, array $arguments ) { /* * If an error occurred in a previous method call, either return the error for terminate methods, * or return the same instance for other methods to maintain the fluent interface. */ if ( null !== $this->error ) { if ( self::is_generating_method( $name ) ) { return $this->error; } if ( self::is_support_check_method( $name ) ) { return false; } return $this; } // Check if the prompt should be prevented for is_supported* and generate_*/convert_text_to_speech* methods. if ( self::is_support_check_method( $name ) || self::is_generating_method( $name ) ) { // If AI is not supported, then there's no need to apply the filter as the prompt will be prevented anyway. $is_ai_disabled = ! wp_supports_ai(); $prevent = $is_ai_disabled; if ( ! $prevent ) { /** * Filters whether to prevent the prompt from being executed. * * @since 7.0.0 * * @param bool $prevent Whether to prevent the prompt. Default false. * @param WP_AI_Client_Prompt_Builder $builder A clone of the prompt builder instance (read-only). */ $prevent = (bool) apply_filters( 'wp_ai_client_prevent_prompt', false, clone $this ); } if ( $prevent ) { // For is_supported* methods, return false. if ( self::is_support_check_method( $name ) ) { return false; } $error_message = $is_ai_disabled ? __( 'AI features are not supported in this environment.' ) : __( 'Prompt execution was prevented by a filter.' ); // For generate_* and convert_text_to_speech* methods, create a WP_Error. $this->error = new WP_Error( 'prompt_prevented', $error_message, array( 'status' => 503, ) ); if ( self::is_generating_method( $name ) ) { return $this->error; } return $this; } } try { $callable = $this->get_builder_callable( $name ); $result = $callable( ...$arguments ); // If the result is a PromptBuilder, return the current instance to allow method chaining. if ( $result instanceof PromptBuilder ) { return $this; } return $result; } catch ( Exception $e ) { $this->error = $this->exception_to_wp_error( $e ); if ( self::is_generating_method( $name ) ) { return $this->error; } return $this; } } /** * Converts an exception into a WP_Error with a structured error code and message. * * This method maps different exception types to specific WP_Error codes and HTTP status codes. * The presence of the status codes means these WP_Error objects can be easily used in REST API responses * or other contexts where HTTP semantics are relevant. * * @since 7.0.0 * * @param Exception $e The exception to convert. * @return WP_Error The resulting WP_Error object. */ private function exception_to_wp_error( Exception $e ): WP_Error { if ( $e instanceof NetworkException ) { $error_code = 'prompt_network_error'; $status_code = 503; } elseif ( $e instanceof ClientException ) { // `ClientException` uses HTTP status codes as exception codes, so we can rely on them. $error_code = 'prompt_client_error'; $status_code = $e->getCode() ? $e->getCode() : 400; } elseif ( $e instanceof ServerException ) { // `ServerException` uses HTTP status codes as exception codes, so we can rely on them. $error_code = 'prompt_upstream_server_error'; $status_code = $e->getCode() ? $e->getCode() : 500; } elseif ( $e instanceof TokenLimitReachedException ) { $error_code = 'prompt_token_limit_reached'; $status_code = 400; } elseif ( $e instanceof InvalidArgumentException ) { $error_code = 'prompt_invalid_argument'; $status_code = 400; } else { $error_code = 'prompt_builder_error'; $status_code = 500; } return new WP_Error( $error_code, $e->getMessage(), array( 'status' => $status_code, 'exception_class' => get_class( $e ), ) ); } /** * Checks if a method name is a support check method (is_supported*). * * @since 7.0.0 * * @param string $name The method name. * @return bool True if the method is a support check method, false otherwise. */ private static function is_support_check_method( string $name ): bool { return isset( self::$support_check_methods[ $name ] ); } /** * Checks if a method name is a generating method (generate_*, convert_text_to_speech*). * * @since 7.0.0 * * @param string $name The method name. * @return bool True if the method is a generating method, false otherwise. */ private static function is_generating_method( string $name ): bool { return isset( self::$generating_methods[ $name ] ); } /** * Retrieves a callable for a given PHP AI Client SDK prompt builder method name. * * @since 7.0.0 * * @param string $name The method name in snake_case. * @return callable The callable for the specified method. * * @throws BadMethodCallException If the method does not exist. */ protected function get_builder_callable( string $name ): callable { $camel_case_name = $this->snake_to_camel_case( $name ); $method = array( $this->builder, $camel_case_name ); if ( ! is_callable( $method ) ) { throw new BadMethodCallException( sprintf( /* translators: 1: Method name. 2: Class name. */ __( 'Method %1$s does not exist on %2$s.' ), $name, // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped get_class( $this->builder ) // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped ) ); } return $method; } /** * Converts snake_case to camelCase. * * @since 7.0.0 * * @param string $snake_case The snake_case string. * @return string The camelCase string. */ private function snake_to_camel_case( string $snake_case ): string { $parts = explode( '_', $snake_case ); $camel_case = $parts[0]; $parts_count = count( $parts ); for ( $i = 1; $i < $parts_count; $i++ ) { $camel_case .= ucfirst( $parts[ $i ] ); } return $camel_case; } } PKis\tM{%adapters/class-wp-ai-client-cache.phpnu[PKis\n2adapters/class-wp-ai-client-discovery-strategy.phpnu[PKis\ 0adapters/class-wp-ai-client-event-dispatcher.phpnu[PKis\x//+S'adapters/class-wp-ai-client-http-client.phpnu[PKis\j.yy0Aclass-wp-ai-client-ability-function-resolver.phpnu[PKis\KMKM%Zclass-wp-ai-client-prompt-builder.phpnu[PKV