diff --git a/admin/class-peptide-news-admin.php b/admin/class-peptide-news-admin.php index 177dbd8..7ccffa8 100644 --- a/admin/class-peptide-news-admin.php +++ b/admin/class-peptide-news-admin.php @@ -568,6 +568,13 @@ class Peptide_News_Admin { echo ''; echo ''; + // Generate AI Summaries button. + echo '
'; + echo ''; + echo ''; + ?> prefix . 'peptide_news_articles'; @@ -370,6 +374,63 @@ class Peptide_News_LLM { } } + /** + * AJAX handler: bulk-generate AI summaries for articles that are missing them. + * + * Processes up to 50 articles per request. Returns the count of articles + * processed and remaining so the admin UI can loop until done. + * + * @since 2.0.1 + */ + public static function ajax_generate_summaries() { + check_ajax_referer( 'peptide_news_admin', 'nonce' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( 'Unauthorized', 403 ); + } + + if ( ! self::is_enabled() ) { + wp_send_json_error( 'AI Analysis is not enabled. Please enable it and set an OpenRouter API key in Peptide News → Settings.' ); + } + + global $wpdb; + $table = $wpdb->prefix . 'peptide_news_articles'; + + // Count how many still need processing. + $remaining = (int) $wpdb->get_var( + "SELECT COUNT(*) FROM {$table} + WHERE is_active = 1 + AND ( ai_summary = '' OR ai_summary IS NULL )" + ); + + if ( $remaining < 1 ) { + wp_send_json_success( array( + 'processed' => 0, + 'remaining' => 0, + 'message' => 'All articles already have AI summaries.', + ) ); + } + + // Process a generous batch — the 120-second timeout guard inside + // process_unanalyzed() will stop early if needed. Override the + // per-cycle limit since this is a manual admin action. + $batch_size = min( 50, $remaining ); + $processed = self::process_unanalyzed( $batch_size, true ); + + // Recount remaining after this batch. + $still_remaining = (int) $wpdb->get_var( + "SELECT COUNT(*) FROM {$table} + WHERE is_active = 1 + AND ( ai_summary = '' OR ai_summary IS NULL )" + ); + + wp_send_json_success( array( + 'processed' => $processed, + 'remaining' => $still_remaining, + 'message' => sprintf( '%d article(s) summarized, %d remaining.', $processed, $still_remaining ), + ) ); + } + /** * Clear all article transient caches. */ diff --git a/includes/class-peptide-news.php b/includes/class-peptide-news.php index 7dee51b..25a0cc3 100644 --- a/includes/class-peptide-news.php +++ b/includes/class-peptide-news.php @@ -81,6 +81,7 @@ class Peptide_News { $this->loader->add_action( 'peptide_news_cron_fetch', $fetcher, 'fetch_all_sources' ); $this->loader->add_filter( 'cron_schedules', $fetcher, 'add_custom_cron_schedules' ); $this->loader->add_action( 'wp_ajax_peptide_news_backfill_sources', $fetcher, 'ajax_backfill_sources' ); + $this->loader->add_action( 'wp_ajax_peptide_news_generate_summaries', 'Peptide_News_LLM', 'ajax_generate_summaries' ); } /**