Add rate-limit handling to bulk AI summary generation

Add 3s delay between successful batches and 10s retry delay with
up to 3 retries when rate-limited. Prevents exhausting free-tier
rate limits (20 req/min) on OpenRouter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Terence Ong
2026-04-07 15:29:32 +07:00
parent 0cea96fd16
commit e1d18ba86e
2 changed files with 44 additions and 10 deletions

View File

@@ -599,9 +599,13 @@ class Peptide_News_Admin {
}); });
// Generate AI Summaries — loops in batches until all articles are done. // Generate AI Summaries — loops in batches until all articles are done.
// Uses a 3-second delay between batches to respect free-tier rate limits,
// and retries up to 3 times when a batch fails before giving up.
$('#peptide-generate-summaries').on('click', function() { $('#peptide-generate-summaries').on('click', function() {
var $btn = $(this), $result = $('#peptide-summary-result'); var $btn = $(this), $result = $('#peptide-summary-result');
var totalProcessed = 0; var totalProcessed = 0;
var retries = 0;
var maxRetries = 3;
$btn.prop('disabled', true); $btn.prop('disabled', true);
$result.text('<?php echo esc_js( __( 'Starting...', 'peptide-news' ) ); ?>'); $result.text('<?php echo esc_js( __( 'Starting...', 'peptide-news' ) ); ?>');
@@ -618,10 +622,17 @@ class Peptide_News_Admin {
$result.text(totalProcessed + ' summarized, ' + remaining + ' remaining...'); $result.text(totalProcessed + ' summarized, ' + remaining + ' remaining...');
if (remaining > 0 && response.data.processed > 0) { if (remaining > 0 && response.data.processed > 0) {
// Continue with next batch. // Success — reset retry counter and continue after a delay
runBatch(); // to respect the free-tier rate limit (20 req/min).
retries = 0;
setTimeout(runBatch, 3000);
} else if (remaining > 0 && retries < maxRetries) {
// No progress — likely rate limited. Wait longer and retry.
retries++;
$result.text(totalProcessed + ' summarized, ' + remaining + ' remaining... (rate limited, retrying in 10s, attempt ' + retries + '/' + maxRetries + ')');
setTimeout(runBatch, 10000);
} else { } else {
// Done — either all processed or no progress (error). // Done — either all processed or exhausted retries.
$btn.prop('disabled', false).text('<?php echo esc_js( __( 'Generate AI Summaries', 'peptide-news' ) ); ?>'); $btn.prop('disabled', false).text('<?php echo esc_js( __( 'Generate AI Summaries', 'peptide-news' ) ); ?>');
if (remaining === 0) { if (remaining === 0) {
$result.text('Done! ' + totalProcessed + ' article(s) summarized.'); $result.text('Done! ' + totalProcessed + ' article(s) summarized.');
@@ -638,8 +649,14 @@ class Peptide_News_Admin {
$result.text('Error: ' + (response.data || 'Unknown error')); $result.text('Error: ' + (response.data || 'Unknown error'));
} }
}).fail(function() { }).fail(function() {
$btn.prop('disabled', false).text('<?php echo esc_js( __( 'Generate AI Summaries', 'peptide-news' ) ); ?>'); if (retries < maxRetries) {
$result.text('Request failed. ' + totalProcessed + ' completed before failure.'); retries++;
$result.text(totalProcessed + ' completed. Request failed, retrying in 10s... (attempt ' + retries + '/' + maxRetries + ')');
setTimeout(runBatch, 10000);
} else {
$btn.prop('disabled', false).text('<?php echo esc_js( __( 'Generate AI Summaries', 'peptide-news' ) ); ?>');
$result.text('Request failed. ' + totalProcessed + ' completed before failure.');
}
}); });
} }

View File

@@ -599,9 +599,13 @@ class Peptide_News_Admin {
}); });
// Generate AI Summaries — loops in batches until all articles are done. // Generate AI Summaries — loops in batches until all articles are done.
// Uses a 3-second delay between batches to respect free-tier rate limits,
// and retries up to 3 times when a batch fails before giving up.
$('#peptide-generate-summaries').on('click', function() { $('#peptide-generate-summaries').on('click', function() {
var $btn = $(this), $result = $('#peptide-summary-result'); var $btn = $(this), $result = $('#peptide-summary-result');
var totalProcessed = 0; var totalProcessed = 0;
var retries = 0;
var maxRetries = 3;
$btn.prop('disabled', true); $btn.prop('disabled', true);
$result.text('<?php echo esc_js( __( 'Starting...', 'peptide-news' ) ); ?>'); $result.text('<?php echo esc_js( __( 'Starting...', 'peptide-news' ) ); ?>');
@@ -618,10 +622,17 @@ class Peptide_News_Admin {
$result.text(totalProcessed + ' summarized, ' + remaining + ' remaining...'); $result.text(totalProcessed + ' summarized, ' + remaining + ' remaining...');
if (remaining > 0 && response.data.processed > 0) { if (remaining > 0 && response.data.processed > 0) {
// Continue with next batch. // Success — reset retry counter and continue after a delay
runBatch(); // to respect the free-tier rate limit (20 req/min).
retries = 0;
setTimeout(runBatch, 3000);
} else if (remaining > 0 && retries < maxRetries) {
// No progress — likely rate limited. Wait longer and retry.
retries++;
$result.text(totalProcessed + ' summarized, ' + remaining + ' remaining... (rate limited, retrying in 10s, attempt ' + retries + '/' + maxRetries + ')');
setTimeout(runBatch, 10000);
} else { } else {
// Done — either all processed or no progress (error). // Done — either all processed or exhausted retries.
$btn.prop('disabled', false).text('<?php echo esc_js( __( 'Generate AI Summaries', 'peptide-news' ) ); ?>'); $btn.prop('disabled', false).text('<?php echo esc_js( __( 'Generate AI Summaries', 'peptide-news' ) ); ?>');
if (remaining === 0) { if (remaining === 0) {
$result.text('Done! ' + totalProcessed + ' article(s) summarized.'); $result.text('Done! ' + totalProcessed + ' article(s) summarized.');
@@ -638,8 +649,14 @@ class Peptide_News_Admin {
$result.text('Error: ' + (response.data || 'Unknown error')); $result.text('Error: ' + (response.data || 'Unknown error'));
} }
}).fail(function() { }).fail(function() {
$btn.prop('disabled', false).text('<?php echo esc_js( __( 'Generate AI Summaries', 'peptide-news' ) ); ?>'); if (retries < maxRetries) {
$result.text('Request failed. ' + totalProcessed + ' completed before failure.'); retries++;
$result.text(totalProcessed + ' completed. Request failed, retrying in 10s... (attempt ' + retries + '/' + maxRetries + ')');
setTimeout(runBatch, 10000);
} else {
$btn.prop('disabled', false).text('<?php echo esc_js( __( 'Generate AI Summaries', 'peptide-news' ) ); ?>');
$result.text('Request failed. ' + totalProcessed + ' completed before failure.');
}
}); });
} }