PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.10.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.10.0
2.10.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 All 51 releases
← All changes | includes/ai/class-openrouter-client.php +21 -3 1.26.0 → 2.10.0 View file →
@@ -69,9 +69,9 @@
69 69 * @param string $api_key OpenRouter API key
70 70 * @param string $model Default model to use
71 71 * @param int $timeout Request timeout
72 72 */
73 - public function __construct(string $api_key, string $model = 'openai/gpt-4o-mini', int $timeout = 30) {
73 + public function __construct(string $api_key, string $model = \ThinkRank\Core\Settings::DEFAULT_OPENROUTER_MODEL, int $timeout = 30) {
74 74 $this->api_key = $api_key;
75 75 $this->model = $model;
76 76 $this->timeout = $timeout;
77 77 }
@@ -158,9 +158,10 @@
158 158 $content_type = $options['content_type'] ?? 'blog_post';
159 159 $tone = $options['tone'] ?? 'professional';
160 160
161 161 $prompt_builder = $this->get_prompt_builder();
162 - $prompt = $prompt_builder->build_seo_prompt($content, $target_keyword, $content_type, $tone, 'openrouter');
162 + $language = is_string($options['language'] ?? null) ? $options['language'] : '';
163 + $prompt = $prompt_builder->build_seo_prompt($content, $target_keyword, $content_type, $tone, 'openrouter', $language);
163 164
164 165 $response = $this->generate_completion($prompt, [
165 166 'max_tokens' => $this->get_recommended_tokens('seo_metadata'),
166 167 'temperature' => 0.3, // Lower temperature for more consistent SEO output
@@ -195,8 +196,19 @@
195 196 * OpenRouter normalises `max_tokens` across very different underlying
196 197 * models, so we apply a single conservative ceiling rather than per-model
197 198 * limits.
198 199 *
200 + * Deliberately NOT given the per-model table Claude_Client gained in #665,
201 + * and still subject to the same truncation symptom as a result.
202 + *
203 + * That table is only safe for Claude because its model list is short, known
204 + * and verifiable. OpenRouter routes to arbitrary models from many providers
205 + * with no curated list here, and a ceiling guessed too high returns a
206 + * provider 400 rather than a smaller answer — a worse failure than the one
207 + * it would be fixing. Raising this needs either a per-model table sourced
208 + * from OpenRouter's own model metadata endpoint, or streaming so a large
209 + * budget cannot time out. Tracked separately; see #665.
210 + *
199 211 * @param string $model Model name
200 212 * @return int Maximum completion tokens
201 213 */
202 214 private function get_max_completion_tokens(string $model): int {
@@ -298,8 +310,14 @@
298 310 * @return array Response data
299 311 * @throws \Exception If request fails
300 312 */
301 313 private function make_request(string $endpoint, array $body = []): array {
314 + // The user's daily ceiling and kill switch are enforced here, at the
315 + // one place every outbound OpenRouter call passes through, so no feature
316 + // path can bypass them by forgetting to ask first (#448).
317 + Spend_Guard::guard();
318 + Spend_Guard::record();
319 +
302 320 $url = self::API_BASE_URL . '/' . ltrim($endpoint, '/');
303 321
304 322 $args = [
305 323 'timeout' => $this->timeout,
@@ -667,9 +685,9 @@
667 685 return [
668 686 'optimized_data' => [
669 687 'site_name' => sanitize_text_field($optimization['optimized_data']['site_name'] ?? ''),
670 688 'project_overview' => sanitize_textarea_field($optimization['optimized_data']['project_overview'] ?? ''),
671 - 'key_features' => sanitize_textarea_field($optimization['optimized_data']['key_features'] ?? ''),
689 + 'key_features' => \ThinkRank\SEO\LLMs_Txt_Manager::normalize_ai_key_features($optimization['optimized_data']['key_features'] ?? ''),
672 690 'architecture' => sanitize_textarea_field($optimization['optimized_data']['architecture'] ?? ''),
673 691 'development_guidelines' => sanitize_textarea_field($optimization['optimized_data']['development_guidelines'] ?? ''),
674 692 'ai_context' => sanitize_textarea_field($optimization['optimized_data']['ai_context'] ?? ''),
675 693 ],