From 67f932a9e5124abf50db1360de14749b4e220872 Mon Sep 17 00:00:00 2001 From: Terence Ong Date: Tue, 7 Apr 2026 15:21:49 +0700 Subject: [PATCH] Add error reporting to bulk AI summary generation Surface actual API error messages in the admin UI instead of just showing a generic failure count. This helps diagnose issues with the OpenRouter API configuration. Co-Authored-By: Claude Opus 4.6 --- admin/class-peptide-news-admin.php | 6 +++++- includes/class-peptide-news-llm.php | 19 +++++++++++++++++++ .../admin/class-peptide-news-admin.php | 6 +++++- .../includes/class-peptide-news-llm.php | 19 +++++++++++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/admin/class-peptide-news-admin.php b/admin/class-peptide-news-admin.php index 7ccffa8..d10c7d5 100644 --- a/admin/class-peptide-news-admin.php +++ b/admin/class-peptide-news-admin.php @@ -626,7 +626,11 @@ class Peptide_News_Admin { if (remaining === 0) { $result.text('Done! ' + totalProcessed + ' article(s) summarized.'); } else { - $result.text(totalProcessed + ' summarized. ' + remaining + ' could not be processed — check the debug log.'); + var errMsg = totalProcessed + ' summarized. ' + remaining + ' could not be processed.'; + if (response.data.errors && response.data.errors.length > 0) { + errMsg += ' Error: ' + response.data.errors[0]; + } + $result.text(errMsg); } } } else { diff --git a/includes/class-peptide-news-llm.php b/includes/class-peptide-news-llm.php index 47cc17f..e2eb257 100644 --- a/includes/class-peptide-news-llm.php +++ b/includes/class-peptide-news-llm.php @@ -58,6 +58,7 @@ class Peptide_News_LLM { 'keywords' => '', 'summary' => '', 'success' => false, + 'errors' => array(), ); $api_key = get_option( 'peptide_news_openrouter_api_key', '' ); @@ -75,9 +76,12 @@ class Peptide_News_LLM { $results['keywords'] = self::sanitize_keywords( $keywords_result ); $any_success = true; } else { + $err_msg = 'Keywords (' . $keywords_model . '): ' . $keywords_result->get_error_message(); + $results['errors'][] = $err_msg; self::log_error( 'Keyword extraction failed for article ' . $article->id . ': ' . $keywords_result->get_error_message() ); } } elseif ( $need_keywords ) { + $results['errors'][] = 'Invalid keywords model ID: ' . $keywords_model; self::log_error( 'Invalid keywords model ID: ' . $keywords_model ); } @@ -94,9 +98,12 @@ class Peptide_News_LLM { $results['summary'] = sanitize_textarea_field( wp_strip_all_tags( $summary_result ) ); $any_success = true; } else { + $err_msg = 'Summary (' . $summary_model . '): ' . $summary_result->get_error_message(); + $results['errors'][] = $err_msg; self::log_error( 'Summarization failed for article ' . $article->id . ': ' . $summary_result->get_error_message() ); } } elseif ( $need_summary ) { + $results['errors'][] = 'Invalid summary model ID: ' . $summary_model; self::log_error( 'Invalid summary model ID: ' . $summary_model ); } @@ -150,6 +157,7 @@ class Peptide_News_LLM { ) ); $processed = 0; + $last_errors = array(); $start_time = time(); foreach ( $articles as $article ) { @@ -166,6 +174,11 @@ class Peptide_News_LLM { $processed++; } + // Track errors for debugging. + if ( ! empty( $result['errors'] ) ) { + $last_errors = $result['errors']; + } + // Small delay to avoid rate limiting. if ( next( $articles ) !== false ) { usleep( 500000 ); // 0.5s between articles. @@ -182,6 +195,7 @@ class Peptide_News_LLM { 'time' => current_time( 'mysql' ), 'processed' => $processed, 'attempted' => count( $articles ), + 'errors' => $last_errors, ) ); return $processed; @@ -431,10 +445,15 @@ class Peptide_News_LLM { AND ( ai_summary = '' OR ai_summary IS NULL )" ); + // Include error details from the last processing run for debugging. + $last_run = get_option( 'peptide_news_last_llm_process', array() ); + $errors = isset( $last_run['errors'] ) ? $last_run['errors'] : array(); + wp_send_json_success( array( 'processed' => $processed, 'remaining' => $still_remaining, 'message' => sprintf( '%d article(s) summarized, %d remaining.', $processed, $still_remaining ), + 'errors' => $errors, ) ); } diff --git a/peptide-news-plugin/admin/class-peptide-news-admin.php b/peptide-news-plugin/admin/class-peptide-news-admin.php index 7ccffa8..d10c7d5 100644 --- a/peptide-news-plugin/admin/class-peptide-news-admin.php +++ b/peptide-news-plugin/admin/class-peptide-news-admin.php @@ -626,7 +626,11 @@ class Peptide_News_Admin { if (remaining === 0) { $result.text('Done! ' + totalProcessed + ' article(s) summarized.'); } else { - $result.text(totalProcessed + ' summarized. ' + remaining + ' could not be processed — check the debug log.'); + var errMsg = totalProcessed + ' summarized. ' + remaining + ' could not be processed.'; + if (response.data.errors && response.data.errors.length > 0) { + errMsg += ' Error: ' + response.data.errors[0]; + } + $result.text(errMsg); } } } else { diff --git a/peptide-news-plugin/includes/class-peptide-news-llm.php b/peptide-news-plugin/includes/class-peptide-news-llm.php index 47cc17f..e2eb257 100644 --- a/peptide-news-plugin/includes/class-peptide-news-llm.php +++ b/peptide-news-plugin/includes/class-peptide-news-llm.php @@ -58,6 +58,7 @@ class Peptide_News_LLM { 'keywords' => '', 'summary' => '', 'success' => false, + 'errors' => array(), ); $api_key = get_option( 'peptide_news_openrouter_api_key', '' ); @@ -75,9 +76,12 @@ class Peptide_News_LLM { $results['keywords'] = self::sanitize_keywords( $keywords_result ); $any_success = true; } else { + $err_msg = 'Keywords (' . $keywords_model . '): ' . $keywords_result->get_error_message(); + $results['errors'][] = $err_msg; self::log_error( 'Keyword extraction failed for article ' . $article->id . ': ' . $keywords_result->get_error_message() ); } } elseif ( $need_keywords ) { + $results['errors'][] = 'Invalid keywords model ID: ' . $keywords_model; self::log_error( 'Invalid keywords model ID: ' . $keywords_model ); } @@ -94,9 +98,12 @@ class Peptide_News_LLM { $results['summary'] = sanitize_textarea_field( wp_strip_all_tags( $summary_result ) ); $any_success = true; } else { + $err_msg = 'Summary (' . $summary_model . '): ' . $summary_result->get_error_message(); + $results['errors'][] = $err_msg; self::log_error( 'Summarization failed for article ' . $article->id . ': ' . $summary_result->get_error_message() ); } } elseif ( $need_summary ) { + $results['errors'][] = 'Invalid summary model ID: ' . $summary_model; self::log_error( 'Invalid summary model ID: ' . $summary_model ); } @@ -150,6 +157,7 @@ class Peptide_News_LLM { ) ); $processed = 0; + $last_errors = array(); $start_time = time(); foreach ( $articles as $article ) { @@ -166,6 +174,11 @@ class Peptide_News_LLM { $processed++; } + // Track errors for debugging. + if ( ! empty( $result['errors'] ) ) { + $last_errors = $result['errors']; + } + // Small delay to avoid rate limiting. if ( next( $articles ) !== false ) { usleep( 500000 ); // 0.5s between articles. @@ -182,6 +195,7 @@ class Peptide_News_LLM { 'time' => current_time( 'mysql' ), 'processed' => $processed, 'attempted' => count( $articles ), + 'errors' => $last_errors, ) ); return $processed; @@ -431,10 +445,15 @@ class Peptide_News_LLM { AND ( ai_summary = '' OR ai_summary IS NULL )" ); + // Include error details from the last processing run for debugging. + $last_run = get_option( 'peptide_news_last_llm_process', array() ); + $errors = isset( $last_run['errors'] ) ? $last_run['errors'] : array(); + wp_send_json_success( array( 'processed' => $processed, 'remaining' => $still_remaining, 'message' => sprintf( '%d article(s) summarized, %d remaining.', $processed, $still_remaining ), + 'errors' => $errors, ) ); }