prefix . 'peptide_news_articles'; $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 ONE article per request to stay within PHP's // max_execution_time (often 30s on shared hosting). $processed = Peptide_News_LLM::process_unanalyzed( 1, 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 )" ); $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, ) ); } }