PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.9.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.9.0
2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 All 50 releases
← All changes | includes/ai/class-gemini-client.php +19 -1 1.28.0 → 2.9.0 View file →
@@ -13,13 +13,17 @@
13 13 declare(strict_types=1);
14 14
15 15 namespace ThinkRank\AI;
16 16
17 +use ThinkRank\AI\Traits\Request_Timeout;
18 +
17 19 // Prevent direct access
18 20 if (!defined('ABSPATH')) {
19 21 exit;
20 22 }
21 23
24 +require_once __DIR__ . '/traits/trait-request-timeout.php';
25 +
22 26 /**
23 27 * Gemini AI Client Class
24 28 *
25 29 * Provides interface to Google Gemini API for SEO optimization,
@@ -28,8 +32,11 @@
28 32 * @since 1.0.0
29 33 */
30 34 class Gemini_Client {
31 35
36 + use Request_Timeout;
37 +
38 +
32 39 /**
33 40 * API key for Gemini
34 41 *
35 42 * @since 1.0.0
@@ -409,8 +416,14 @@
409 416 * @return array Response data
410 417 * @throws \Exception If request fails
411 418 */
412 419 private function make_request(string $endpoint, array $data): array {
420 + // The user's daily ceiling and kill switch are enforced here, at the
421 + // one place every outbound Gemini call passes through, so no feature
422 + // path can bypass them by forgetting to ask first (#448).
423 + Spend_Guard::guard();
424 + Spend_Guard::record();
425 +
413 426 // Send the API key in the x-goog-api-key header rather than the URL
414 427 // query string, which is logged by servers, proxies and referrers.
415 428 $url = "https://generativelanguage.googleapis.com/v1beta/models/{$this->model}:{$endpoint}";
416 429
@@ -494,9 +507,14 @@
494 507
495 508 $is_transient = false;
496 509 $retry_after = 0;
497 510 if (is_wp_error($response)) {
498 - $is_transient = true;
511 + // A client-side timeout means the work genuinely needs longer
512 + // than the budget we allowed; re-running the identical prompt,
513 + // model and budget just times out again and multiplies the
514 + // wait (issue #288). Do not retry a timeout. Other WP_Error
515 + // results — DNS, connection refused, TLS — stay retryable.
516 + $is_transient = !$this->is_timeout_error($response);
499 517 } else {
500 518 $status = wp_remote_retrieve_response_code($response);
501 519 if (429 === $status || $status >= 500) {
502 520 $is_transient = true;