'Write a short, clear product/content description (2-3 sentences, no fluff) for: ', 'seo_title' => 'Write one SEO title, max 55 chars, no quotes, for: ', 'seo_desc' => 'Write one meta description, max 155 chars, no quotes, for: ', ); $prompt = ( $prompts[ $task ] ?? $prompts['description'] ) . $post->post_title . '. ' . wp_trim_words( wp_strip_all_tags( $post->post_content ), 60 ); if ( self::core_available() ) { $text = wp_ai_client_prompt( $prompt )->generate_text(); if ( ! is_wp_error( $text ) || ! self::fallback_configured() ) { return self::clean( $text ); } } if ( self::fallback_configured() ) { return self::clean( self::fallback( $prompt ) ); } return new \WP_Error( 'rafgrid_no_ai', __( 'No AI provider is configured. Set one up in the WordPress AI settings, or enter a fallback AI connection in Rafgrid settings.', 'rafgrid' ) ); } /** * Is the WordPress AI Client present, allowed and able to generate text? * * @return bool */ public static function core_available(): bool { if ( ! function_exists( 'wp_ai_client_prompt' ) || ( function_exists( 'wp_supports_ai' ) && ! wp_supports_ai() ) ) { return false; } return (bool) wp_ai_client_prompt( 'ping' )->is_supported_for_text_generation(); } /** * Did an administrator enter a fallback OpenAI-compatible connection? * * @return bool */ public static function fallback_configured(): bool { $settings = new Settings(); return '' !== (string) $settings->get( 'ai_api_key', '' ) && '' !== (string) $settings->get( 'ai_base_url', '' ) && '' !== (string) $settings->get( 'ai_model', '' ); } /** * Fallback: chat completions on the administrator's own endpoint and key. * * @param string $prompt Prompt. * @return string|\WP_Error */ private static function fallback( string $prompt ) { $settings = new Settings(); $base = untrailingslashit( (string) $settings->get( 'ai_base_url', '' ) ); $valid = Security::validate_outbound_url( $base ); if ( is_wp_error( $valid ) ) { return $valid; } $res = wp_remote_post( $base . '/chat/completions', array( 'timeout' => 45, 'headers' => array( 'Authorization' => 'Bearer ' . (string) $settings->get( 'ai_api_key', '' ), 'Content-Type' => 'application/json', ), 'body' => wp_json_encode( array( 'model' => (string) $settings->get( 'ai_model', '' ), 'messages' => array( array( 'role' => 'user', 'content' => $prompt ) ), ) ), ) ); if ( is_wp_error( $res ) ) { return $res; } $data = json_decode( (string) wp_remote_retrieve_body( $res ), true ); return (string) ( $data['choices'][0]['message']['content'] ?? '' ); } /** * Normalize a generation result. * * @param string|\WP_Error $text Text. * @return string|\WP_Error */ private static function clean( $text ) { if ( is_wp_error( $text ) ) { return $text; } if ( '' === trim( (string) $text ) ) { return new \WP_Error( 'rafgrid_ai_empty', __( 'AI returned an empty answer.', 'rafgrid' ) ); } return trim( (string) $text ); } }