'object', 'properties' => array( 'prompt' => array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'description' => esc_html__( 'Prompt used to generate an image.', 'ai' ), ), 'reference' => array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'description' => esc_html__( 'Optional reference image for edits, as a base64-encoded image data URI (e.g. data:image/png;base64,...).', 'ai' ), ), ), 'required' => array( 'prompt' ), ); } /** * {@inheritDoc} * * @since 0.2.0 */ protected function output_schema(): array { return array( 'type' => 'object', 'properties' => array( 'image' => array( 'type' => 'object', 'description' => esc_html__( 'Generated image data.', 'ai' ), 'properties' => array( 'data' => array( 'type' => 'string', 'description' => esc_html__( 'The base64 encoded image data.', 'ai' ), ), 'provider_metadata' => array( 'type' => 'object', 'description' => esc_html__( 'Information about the provider that generated the image.', 'ai' ), 'properties' => array( 'id' => array( 'type' => 'string', 'description' => esc_html__( 'The provider ID.', 'ai' ), ), 'name' => array( 'type' => 'string', 'description' => esc_html__( 'The provider name.', 'ai' ), ), 'type' => array( 'type' => 'string', 'description' => esc_html__( 'The provider type.', 'ai' ), ), ), ), 'model_metadata' => array( 'type' => 'object', 'description' => esc_html__( 'Information about the model that generated the image.', 'ai' ), 'properties' => array( 'id' => array( 'type' => 'string', 'description' => esc_html__( 'The model ID.', 'ai' ), ), 'name' => array( 'type' => 'string', 'description' => esc_html__( 'The model name.', 'ai' ), ), ), ), ), ), ), ); } /** * {@inheritDoc} * * @since 0.2.0 */ protected function execute_callback( $input ) { $reference_image = ! empty( $input['reference'] ) ? (string) $input['reference'] : null; // Generate the image. $result = $this->generate_image( $input['prompt'], $reference_image ); // If we have an error, return it. if ( is_wp_error( $result ) ) { return $result; } // If we have no results, return an error. if ( empty( $result ) ) { return new WP_Error( 'no_results', esc_html__( 'No image was generated.', 'ai' ) ); } // Return the image data in the format the Ability expects. return array( 'image' => $result, ); } /** * {@inheritDoc} * * @since 0.2.0 */ protected function permission_callback( $args ) { // Ensure the user has permission to upload files. if ( ! current_user_can( 'upload_files' ) ) { return new WP_Error( 'insufficient_capabilities', esc_html__( 'You do not have permission to generate images.', 'ai' ) ); } return true; } /** * {@inheritDoc} * * @since 0.2.0 */ protected function meta(): array { return array( 'show_in_rest' => true, ); } /** * Generates an image from the given prompt. * * @since 0.2.0 * * @param string $prompt The prompt to generate an image from. * @param string|null $reference_image Optional base64-encoded image data URI to use as a reference for edits. * @return array{data: string, provider_metadata: array, model_metadata: array}|\WP_Error The generated image data, or a WP_Error on failure. */ protected function generate_image( string $prompt, ?string $reference_image = null ) { // phpcs:ignore Generic.NamingConventions.ConstructorName.OldStyle if ( null !== $reference_image ) { $reference_image = get_image_data_uri( $reference_image ); if ( null === $reference_image ) { return new WP_Error( 'invalid_reference', esc_html__( 'The reference image is not valid base64-encoded data.', 'ai' ) ); } } $prompt = $this->filter_prompt( $prompt, $reference_image ); $prompt_builder = $this->get_prompt_builder( $prompt, $reference_image ); if ( is_wp_error( $prompt_builder ) ) { return $prompt_builder; } // Generate the image using the AI client. $result = $prompt_builder->generate_image_result(); if ( is_wp_error( $result ) ) { return $result; } $data = array( 'data' => '', 'provider_metadata' => array(), 'model_metadata' => array(), ); try { // Get the File from the result. $image_file = $result->toImageFile(); // Extract the base64 encoded image data. $data['data'] = sanitize_text_field( trim( $image_file->getBase64Data() ?? '' ) ); if ( empty( $data['data'] ) ) { return new WP_Error( 'no_image_data', esc_html__( 'No image data was generated.', 'ai' ) ); } // Get details about the provider and model that generated the image. $data['provider_metadata'] = $result->getProviderMetadata()->toArray(); $data['model_metadata'] = $result->getModelMetadata()->toArray(); // Remove data we don't care about. unset( $data['provider_metadata'][ ProviderMetadata::KEY_CREDENTIALS_URL ] ); unset( $data['model_metadata'][ ModelMetadata::KEY_SUPPORTED_OPTIONS ] ); unset( $data['model_metadata'][ ModelMetadata::KEY_SUPPORTED_CAPABILITIES ] ); } catch ( Throwable $t ) { return new WP_Error( 'no_image_data', esc_html__( 'No image data was generated.', 'ai' ), $t ); } return $data; } /** * Gets a prompt builder for generating an image. * * @since 0.6.0 * * @param string $prompt The prompt to generate an image from. * @param string|null $reference_image Optional base64-encoded image data URI to use as a reference for edits. * @return \WP_AI_Client_Prompt_Builder|\WP_Error The prompt builder, or a WP_Error on failure. */ private function get_prompt_builder( string $prompt, ?string $reference_image = null ) { $request_options = new RequestOptions(); $request_options->setTimeout( get_default_request_timeout( Image_Generation_Feature::get_id(), 90 ) ); // Inject guidelines into the prompt. Unlike the other features, we don't // use system instructions here because most image gen models don't support them. $guidelines = $this->get_guidelines_for_prompt(); if ( $guidelines ) { $prompt .= "\n\n" . 'The following guidelines represent the site\'s editorial standards. Apply them where relevant. If guidelines conflict with the above input, prioritize accuracy.'; $prompt .= "\n\n" . $guidelines; } $prompt_builder = wp_ai_client_prompt( $prompt ) ->using_request_options( $request_options ) ->as_output_file_type( FileTypeEnum::inline() ); if ( null !== $reference_image ) { try { $file = new File( $reference_image ); $prompt_builder = $prompt_builder->with_file( $file ); } catch ( Throwable $t ) { return new WP_Error( 'invalid_reference', esc_html__( 'The reference image is not valid base64-encoded data.', 'ai' ) ); } } $error_message = esc_html__( 'Image generation failed. Please ensure you have a connected provider that supports image generation.', 'ai' ); if ( null !== $reference_image ) { $error_message = esc_html__( 'Image refinement failed. Please ensure you have a connected provider that supports image refinement, not just image generation.', 'ai' ); } $prompt_builder = $this->filter_prompt_builder( $prompt_builder, Image_Generation_Feature::class, get_preferred_image_models(), $prompt, $reference_image ); return $this->ensure_image_generation_supported( $prompt_builder, $error_message ); } }