claude_api_key = get_option('ai_seo_article_generator_api_key', ''); $this->openai_api_key = get_option('ai_seo_article_generator_openai_api_key', ''); $this->ai_provider = get_option('ai_seo_article_generator_ai_provider', 'claude'); } /** * Determine content language based on user selection */ public function determine_content_language($article_language) { switch ($article_language) { case 'hebrew': return true; case 'english': return false; case 'auto': default: // Use WordPress locale detection $locale = get_locale(); $hebrew_locales = array('he_IL', 'he', 'hebrew', 'Hebrew', 'HE_IL', 'HE'); return in_array($locale, $hebrew_locales) || strpos($locale, 'he') === 0; } } /** * Get English length category for article type */ private function get_length_category_english($target_words) { if ($target_words >= 3000) return 'very comprehensive'; if ($target_words >= 2500) return 'comprehensive'; if ($target_words >= 2000) return 'detailed'; if ($target_words >= 1500) return 'long'; if ($target_words >= 1000) return 'medium'; return 'short'; } /** * Debug logging helper function */ private function debug_log($message, $data = null) { if (get_option('ai_seo_article_generator_debug_logging', 0)) { $log_message = 'AI SEO Article Generator API: ' . $message; if ($data !== null && is_array($data)) { $log_message .= ' - ' . wp_json_encode($data); } elseif ($data !== null) { $log_message .= ' - ' . $data; } if (function_exists('error_log')) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Conditional debug logging error_log($log_message); } } } public function test_connection() { if ($this->ai_provider === 'openai') { $response = $this->make_openai_request('Test connection', 'Please respond with "Connection successful"', 50, 30); } else { $response = $this->make_claude_request('Test connection', 'Please respond with "Connection successful"', 50, 30); } if (is_wp_error($response)) { return array('success' => false, 'message' => $response->get_error_message()); } if (strpos(strtolower($response), 'connection successful') !== false) { update_option('ai_seo_article_generator_api_connected', true); $provider_name = ($this->ai_provider === 'openai') ? 'OpenAI' : 'Claude'; return array('success' => true, 'message' => "Connection successful with {$provider_name}!"); } return array('success' => false, 'message' => 'Unexpected response from API'); } public function generate_article_structure($main_keyword, $sub_keywords, $target_words, $article_language = 'auto') { $this->debug_log("šŸŽÆ AI SEO API: Starting structure generation for keyword: $main_keyword (language: $article_language)"); $prompt = $this->build_structure_prompt($main_keyword, $sub_keywords, $target_words, $article_language); $this->debug_log("šŸ“ AI SEO API: Built prompt, length: " . strlen($prompt)); // Check the appropriate API key based on provider $api_key = ($this->ai_provider === 'openai') ? $this->openai_api_key : $this->claude_api_key; if (empty($api_key)) { $this->debug_log("āŒ AI SEO API: API key is empty for provider: " . $this->ai_provider); return array('success' => false, 'message' => 'API key לא הוגדר'); } $this->debug_log("šŸ”‘ AI SEO API: API key is set, making request..."); // Calculate dynamic timeout matching frontend calculation // Frontend: 30s + 30s per 1000 words (60s-180s range) $structure_timeout = max(60, min(180, 30 + ($target_words / 1000) * 30)); $this->debug_log("ā° AI SEO API: Structure timeout calculated: {$structure_timeout}s for {$target_words} words (matching frontend)"); $response = $this->make_request('Generate Article Structure', $prompt, 2500, $structure_timeout); if (is_wp_error($response)) { $this->debug_log("āŒ AI SEO API: Request failed with error: " . $response->get_error_message()); return array('success' => false, 'message' => $response->get_error_message()); } $this->debug_log("āœ… AI SEO API: Got response, length: " . strlen($response)); $this->debug_log("šŸ“„ AI SEO API: Response preview: " . substr($response, 0, 200) . "..."); // Check if response appears truncated if ($this->is_response_truncated($response)) { $this->debug_log("āš ļø AI SEO API: Response appears truncated, retrying with higher max_tokens..."); // Use extended timeout for retry (add 50% more time) $retry_timeout = min(270, ceil($structure_timeout * 1.5)); // Cap at 4.5 minutes $this->debug_log("ā° AI SEO API: Structure retry timeout: {$retry_timeout}s"); $retry_response = $this->make_request('Generate Article Structure - Retry', $prompt, 4000, $retry_timeout); if (!is_wp_error($retry_response) && !$this->is_response_truncated($retry_response)) { $this->debug_log("āœ… AI SEO API: Retry successful, using retry response"); $response = $retry_response; } else { $this->debug_log("āš ļø AI SEO API: Retry also truncated or failed, proceeding with original response"); } } $structure = $this->parse_structure_response($response); $this->debug_log("šŸ—ļø AI SEO API: Parsed structure", $structure); return array('success' => true, 'structure' => $structure); } public function generate_article_content($structure, $main_keyword, $sub_keywords, $target_words, $article_language = 'auto') { $this->debug_log("šŸš€ AI SEO API: Starting content generation for {$target_words} words (language: $article_language)"); $prompt = $this->build_content_prompt($structure, $main_keyword, $sub_keywords, $target_words, $article_language); $this->debug_log("šŸ“ AI SEO API: Content prompt built, length: " . strlen($prompt)); // Calculate max_tokens based on target words (approximately 7-8 tokens per Hebrew word) // Claude-3.5-Sonnet has a max of 8192 tokens output, but we'll use dynamic scaling $tokens_per_word = 7; // More accurate for Hebrew content $max_tokens = min(8100, max(4000, $target_words * $tokens_per_word)); // For very long articles, we'll use section-by-section generation if ($target_words > 2500) { $this->debug_log("šŸ”„ AI SEO API: Long article detected ({$target_words} words), implementing enhanced generation strategy"); return $this->generate_long_article_content($structure, $main_keyword, $sub_keywords, $target_words); } // Calculate timeout based on target words (more words = longer timeout) // Use admin-configurable maximum timeout for Hebrew content generation $max_content_timeout = get_option('ai_seo_article_generator_max_content_timeout', 600); $base_content_timeout = min(180, $max_content_timeout * 0.3); // 30% of max as base $content_timeout = max($base_content_timeout, min($max_content_timeout, $target_words / 4)); // Dynamic based on word count $this->debug_log("āš™ļø AI SEO API: Using max_tokens: {$max_tokens}, timeout: {$content_timeout}s (max: {$max_content_timeout}s)"); $response = $this->make_request('Generate Article Content', $prompt, $max_tokens, $content_timeout); if (is_wp_error($response)) { $this->debug_log("āŒ AI SEO API: Content generation failed: " . $response->get_error_message()); return array('success' => false, 'message' => $response->get_error_message()); } $this->debug_log("āœ… AI SEO API: Content generation completed, response length: " . strlen($response)); // Validate content structure and quality $structure_validation = $this->validate_content_structure($response, $main_keyword, $target_words); // If structure validation fails, try to fix the content if (!$structure_validation['valid']) { $this->debug_log("āš ļø AI SEO API: Content structure validation failed: " . implode(', ', $structure_validation['issues'])); // Try to enhance the content with missing elements $enhanced_content = $this->enhance_content_structure($response, $main_keyword, $structure_validation['issues']); if ($enhanced_content) { $response = $enhanced_content; $this->debug_log("šŸ”§ AI SEO API: Enhanced content structure automatically"); // Re-validate after enhancement $structure_validation = $this->validate_content_structure($response, $main_keyword, $target_words); } } // Validate content length with improved logic $validation_result = $this->validate_content_length($response, $target_words); // Apply keyword density optimization if needed if ($structure_validation['valid'] && isset($structure_validation['stats']['keyword_density'])) { $keyword_density = $structure_validation['stats']['keyword_density']; if ($keyword_density < 1.0) { $this->debug_log("āš ļø AI SEO API: Low keyword density ({$keyword_density}%), attempting optimization"); $optimized_content = $this->optimize_keyword_density($response, $main_keyword, $target_words); if ($optimized_content) { $response = $optimized_content; $this->debug_log("šŸ”§ AI SEO API: Applied keyword density optimization"); } } } // Much stricter acceptance criteria - require at least 90% of target words $min_acceptable = $target_words * 0.9; // Require 90% of target $should_retry = false; if ($validation_result['word_count'] >= $min_acceptable) { $this->debug_log("āœ… AI SEO API: Content accepted - {$validation_result['word_count']} words (target: {$target_words})"); // Even more aggressive retry logic - always retry if not 95% of target $should_retry = $validation_result['word_count'] < ($target_words * 0.95); if ($should_retry && $target_words <= 3500) { $this->debug_log("āš ļø AI SEO API: Content short ({$validation_result['word_count']} words), attempting retry..."); $percentage_achieved = round(($validation_result['word_count'] / $target_words) * 100); $words_needed = $target_words - $validation_result['word_count']; $enhanced_prompt = $this->build_content_prompt($structure, $main_keyword, $sub_keywords, $target_words) . "\n\n🚨 ניהיון שני - ×”×ž××ž×Ø הקודם קצר מדי!\n" . "šŸ“Š הישג נוכחי: {$validation_result['word_count']} מילים ({$percentage_achieved}% מהיעד)\n" . "šŸŽÆ נדרש להוהיף: {$words_needed} מילים נוהפות\n" . "šŸ“ יעד הופי: {$target_words} מילים ×ž×“×•×™×§×•×Ŗ\n\n" . "šŸ”„ הוראות ×ž×™×•×—×“×•×Ŗ למילוי החהר:\n" . "- הוהף פהקאות נוהפות לכל פרק (×œ×¤×—×•×Ŗ 6-8 פהקאות לפרק)\n" . "- כלול ×“×•×’×ž××•×Ŗ ×ž×¢×©×™×•×Ŗ ×ž×¤×•×Ø×˜×•×Ŗ ×•×ž×§×Ø×™ מבחן\n" . "- הוהף ×Ø×©×™×ž×•×Ŗ של 8-12 נקודות עם ×”×”×‘×Ø×™×\n" . "- כלול ×©××œ×•×Ŗ ותשובות נפוצות בכל פרק\n" . "- הרחב עם ×¤×Ø×˜×™× טכניים, × ×Ŗ×•× ×™× ×•×”×˜×˜×™×”×˜×™×§×•×Ŗ\n" . "- הוהף טיפים ×ž×Ŗ×§×“×ž×™× ×•×˜×›× ×™×§×•×Ŗ מומחה\n" . "- כתוב במלוא ההרחבה - אין גבול עליון!\n" . "- אל תעצור עד שתגיע בדיוק ל-{$target_words} מילים!"; $retry_max_tokens = min(8000, $target_words * 5); $retry_response = $this->make_request('Generate Article Content - Retry', $enhanced_prompt, $retry_max_tokens, $content_timeout + 60); if (!is_wp_error($retry_response)) { $retry_validation = $this->validate_content_length($retry_response, $target_words); // Use retry if it's significantly better, otherwise stick with original if ($retry_validation['word_count'] > $validation_result['word_count'] * 1.3) { $this->debug_log("āœ… AI SEO API: Retry successful - using {$retry_validation['word_count']} words instead of {$validation_result['word_count']}"); return array('success' => true, 'content' => $retry_response, 'word_count' => $retry_validation['word_count']); } else { $this->debug_log("āš ļø AI SEO API: Retry didn't improve significantly, using original content"); } } else { $this->debug_log("āš ļø AI SEO API: Retry failed, using original content"); } } // Return the original content (it's good enough) return array('success' => true, 'content' => $response, 'word_count' => $validation_result['word_count']); } // For articles that are still too short, try content extension (now more aggressive) if ($validation_result['word_count'] < $target_words * 0.95) { $this->debug_log("āš ļø AI SEO API: Article significantly short, attempting content extension"); $extended_content = $this->extend_article_content($response, $main_keyword, $target_words, $validation_result['word_count']); if ($extended_content) { $extended_validation = $this->validate_content_length($extended_content, $target_words); if ($extended_validation['word_count'] > $validation_result['word_count']) { $this->debug_log("āœ… AI SEO API: Content extended successfully to {$extended_validation['word_count']} words"); return array('success' => true, 'content' => $extended_content, 'word_count' => $extended_validation['word_count']); } } } return array('success' => true, 'content' => $response, 'word_count' => $validation_result['word_count']); } public function enhance_section($section_content, $main_keyword, $enhancement_type = 'improve') { $prompt = $this->build_enhancement_prompt($section_content, $main_keyword, $enhancement_type); $response = $this->make_request('Enhance Section', $prompt, 1000, 45); if (is_wp_error($response)) { return array('success' => false, 'message' => $response->get_error_message()); } return array('success' => true, 'content' => $response); } private function make_claude_request($purpose, $prompt, $max_tokens = 1000, $timeout = 60) { $this->debug_log("🌐 AI SEO API: Making request for: $purpose"); $this->debug_log("šŸ“ AI SEO API: Max tokens: $max_tokens"); $this->debug_log("ā° AI SEO API: Timeout: {$timeout}s"); // Set appropriate execution time limit for long-running operations if ($timeout > 300) { // For very long operations (5+ minutes), remove execution time limit if (function_exists('set_time_limit') && !ini_get('safe_mode')) { // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required for long API operations @set_time_limit(0); } $this->debug_log("ā° AI SEO API: Removed execution time limit for long operation ({$timeout}s timeout)"); } else { // Set execution time limit to 50% more than request timeout to allow for processing $execution_limit = ceil($timeout * 1.5); if (function_exists('set_time_limit') && !ini_get('safe_mode')) { // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required for API timeout control @set_time_limit($execution_limit); } $this->debug_log("ā° AI SEO API: Set execution time limit to {$execution_limit}s (request timeout: {$timeout}s)"); } // Log start time for debugging timeout issues $start_time = microtime(true); $this->debug_log("šŸš€ AI SEO API: Starting request at " . gmdate('Y-m-d H:i:s') . " UTC with {$timeout}s timeout"); if (empty($this->claude_api_key)) { $this->debug_log("āŒ AI SEO API: Claude API key is empty in make_claude_request"); return new WP_Error('no_api_key', 'API key not configured'); } $this->debug_log("šŸ”‘ AI SEO API: Claude API key length: " . strlen($this->claude_api_key)); $headers = array( 'Content-Type' => 'application/json', 'x-api-key' => $this->claude_api_key, 'anthropic-version' => '2023-06-01' ); $this->debug_log("šŸ“‹ AI SEO API: Headers prepared"); // Determine system prompt language based on content $is_hebrew_content = strpos($prompt, 'בעברית') !== false || strpos($prompt, '×ž××ž×Ø') !== false; $system_prompt = $is_hebrew_content ? 'אתה כותב ×Ŗ×•×›×Ÿ מקצועי בעברית ×”×ž×Ŗ×ž×—×” ב-SEO, AEO ו-GEO. אתה ×ž×™×™×¦×Ø ×Ŗ×•×›×Ÿ איכותי, ×ž×¤×•×Ø×˜ ומקיף בעברית ×˜×‘×¢×™×Ŗ.' : 'You are a professional content writer specializing in SEO, AEO, and GEO. You produce high-quality, detailed, and comprehensive content in natural English.'; $body = array( 'model' => 'claude-sonnet-4-20250514', 'max_tokens' => $max_tokens, 'temperature' => 0.7, 'system' => $system_prompt, 'messages' => array( array( 'role' => 'user', 'content' => $prompt ) ) ); $this->debug_log("šŸ“¦ AI SEO API: Request body prepared, JSON size: " . strlen(json_encode($body))); $this->debug_log("šŸŽÆ AI SEO API: Request URL: " . $this->claude_api_url); $this->debug_log("ā° AI SEO API: Request timeout: {$timeout}s"); $response = wp_remote_post($this->claude_api_url, array( 'headers' => $headers, 'body' => json_encode($body), 'timeout' => $timeout, )); // Log completion time for debugging $end_time = microtime(true); $actual_duration = round($end_time - $start_time, 2); $this->debug_log("šŸ AI SEO API: Request completed in {$actual_duration}s (timeout was {$timeout}s)"); $this->debug_log("šŸ“” AI SEO API: Request sent, checking response..."); if (is_wp_error($response)) { $error_message = $response->get_error_message(); $this->debug_log("āŒ AI SEO API: WP_Error in response: " . $error_message); // Handle timeout specifically with improved messaging if (strpos($error_message, 'timed out') !== false || strpos($error_message, 'timeout') !== false) { $this->debug_log("ā° AI SEO API: Request timed out after {$timeout}s"); // Provide specific timeout messages based on operation type if ($purpose === 'Generate Article Content' || $purpose === 'Generate Article Content - Retry') { $timeout_minutes = ceil($timeout / 60); return new WP_Error('content_timeout', "יצירת ×”×ž××ž×Ø ×œ×•×§×—×Ŗ זמן רב מהצפוי ({$timeout_minutes} דקות). זה יכול ×œ×§×Ø×•×Ŗ עם ×ž××ž×Ø×™× ××Ø×•×›×™× או ×ž×•×Ø×›×‘×™×. אנא × ×”×” שוב או הקטן את ××•×Ø×š ×”×ž××ž×Ø."); } elseif (strpos($purpose, 'Generate Article Structure') !== false) { $timeout_minutes = ceil($timeout / 60); return new WP_Error('structure_timeout', "יצירת מבנה ×”×ž××ž×Ø ×œ×•×§×—×Ŗ זמן רב מהצפוי ({$timeout_minutes} דקות). זה יכול ×œ×§×Ø×•×Ŗ עם ×ž××ž×Ø×™× ×ž×•×Ø×›×‘×™×. אנא × ×”×” שוב או הפשט את הדרישות."); } else { return new WP_Error('operation_timeout', "הפעולה ×œ×•×§×—×Ŗ זמן רב מהצפוי ({$timeout} שניות). אנא × ×”×” שוב ×ž××•×—×Ø יותר."); } } return $response; } $response_body = wp_remote_retrieve_body($response); $response_code = wp_remote_retrieve_response_code($response); $this->debug_log("šŸ“Š AI SEO API: Response code: $response_code"); $this->debug_log("šŸ“„ AI SEO API: Response body length: " . strlen($response_body)); if ($response_code !== 200) { $this->debug_log("āŒ AI SEO API: Non-200 response code: $response_code"); $this->debug_log("šŸ” AI SEO API: Error response body: " . $response_body); $error_data = json_decode($response_body, true); $error_message = $error_data['error']['message'] ?? 'Unknown error'; $error_type = $error_data['error']['type'] ?? 'unknown_error'; $this->debug_log("šŸ’„ AI SEO API: Parsed error - Type: $error_type, Message: $error_message"); // Handle specific error types if ($error_type === 'invalid_request_error' && strpos($error_message, 'max_tokens') !== false) { return new WP_Error('token_limit_exceeded', 'הבקשה ארוכה מדי. × ×”×” להקטין את ××•×Ø×š ×”×ž××ž×Ø המבוקש.'); } elseif ($response_code === 429) { return new WP_Error('rate_limit_exceeded', '×”×’×¢×Ŗ ×œ×ž×’×‘×œ×Ŗ הבקשות. × ×”×” שוב בעוד כמה דקות.'); } elseif ($response_code >= 500) { return new WP_Error('server_error', 'שגיאת שרת ב-Claude API. × ×”×” שוב בעוד כמה דקות.'); } return new WP_Error('api_error', 'API Error: ' . $error_message); } $data = json_decode($response_body, true); $this->debug_log("šŸ” AI SEO API: JSON decode result: " . (is_array($data) ? 'Success' : 'Failed')); if (!isset($data['content'][0]['text'])) { $this->debug_log("āŒ AI SEO API: Invalid response structure"); $this->debug_log("šŸ” AI SEO API: Response structure", $data); return new WP_Error('invalid_response', 'Invalid API response format'); } $response_text = $data['content'][0]['text']; $final_duration = round(microtime(true) - $start_time, 2); $this->debug_log("āœ… AI SEO API: Successfully extracted response text, length: " . strlen($response_text) . " (total time: {$final_duration}s)"); return $response_text; } private function make_openai_request($purpose, $prompt, $max_tokens = 1000, $timeout = 60) { $this->debug_log("🌐 AI SEO API (OpenAI): Making request for: $purpose"); $this->debug_log("šŸ“ AI SEO API (OpenAI): Max tokens: $max_tokens"); $this->debug_log("ā° AI SEO API (OpenAI): Timeout: {$timeout}s"); // Set appropriate execution time limit for long-running operations if ($timeout > 300) { if (function_exists('set_time_limit') && !ini_get('safe_mode')) { // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required for long API operations @set_time_limit(0); } $this->debug_log("ā° AI SEO API (OpenAI): Removed execution time limit for long operation ({$timeout}s timeout)"); } else { $execution_limit = ceil($timeout * 1.5); if (function_exists('set_time_limit') && !ini_get('safe_mode')) { // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required for API timeout control @set_time_limit($execution_limit); } $this->debug_log("ā° AI SEO API (OpenAI): Set execution time limit to {$execution_limit}s (request timeout: {$timeout}s)"); } $start_time = microtime(true); $this->debug_log("šŸš€ AI SEO API (OpenAI): Starting request at " . gmdate('Y-m-d H:i:s') . " UTC with {$timeout}s timeout"); if (empty($this->openai_api_key)) { $this->debug_log("āŒ AI SEO API (OpenAI): OpenAI API key is empty in make_openai_request"); return new WP_Error('no_api_key', 'OpenAI API key not configured'); } $this->debug_log("šŸ”‘ AI SEO API (OpenAI): OpenAI API key length: " . strlen($this->openai_api_key)); $headers = array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $this->openai_api_key, ); $this->debug_log("šŸ“‹ AI SEO API (OpenAI): Headers prepared"); // Get selected OpenAI model $openai_model = get_option('ai_seo_article_generator_openai_model', 'gpt-4o'); $body = array( 'model' => $openai_model, 'max_tokens' => $max_tokens, 'temperature' => 0.7, 'messages' => array( array( 'role' => 'system', 'content' => 'אתה כותב ×Ŗ×•×›×Ÿ מקצועי בעברית ×”×ž×Ŗ×ž×—×” ב-SEO, AEO ו-GEO. אתה ×ž×™×™×¦×Ø ×Ŗ×•×›×Ÿ איכותי, ×ž×¤×•×Ø×˜ ומקיף בעברית ×˜×‘×¢×™×Ŗ.' ), array( 'role' => 'user', 'content' => $prompt ) ) ); $this->debug_log("šŸ“¦ AI SEO API (OpenAI): Request body prepared, JSON size: " . strlen(json_encode($body))); $this->debug_log("šŸŽÆ AI SEO API (OpenAI): Request URL: " . $this->openai_api_url); $this->debug_log("šŸ¤– AI SEO API (OpenAI): Using model: " . $openai_model); $this->debug_log("ā° AI SEO API (OpenAI): Request timeout: {$timeout}s"); $response = wp_remote_post($this->openai_api_url, array( 'headers' => $headers, 'body' => json_encode($body), 'timeout' => $timeout, )); $end_time = microtime(true); $actual_duration = round($end_time - $start_time, 2); $this->debug_log("šŸ AI SEO API (OpenAI): Request completed in {$actual_duration}s (timeout was {$timeout}s)"); $this->debug_log("šŸ“” AI SEO API (OpenAI): Request sent, checking response..."); if (is_wp_error($response)) { $error_message = $response->get_error_message(); $this->debug_log("āŒ AI SEO API (OpenAI): WP_Error in response: " . $error_message); if (strpos($error_message, 'timed out') !== false || strpos($error_message, 'timeout') !== false) { $this->debug_log("ā° AI SEO API (OpenAI): Request timed out after {$timeout}s"); if ($purpose === 'Generate Article Content' || $purpose === 'Generate Article Content - Retry') { $timeout_minutes = ceil($timeout / 60); return new WP_Error('content_timeout', "יצירת ×”×ž××ž×Ø ×‘××ž×¦×¢×•×Ŗ OpenAI ×œ×•×§×—×Ŗ זמן רב מהצפוי ({$timeout_minutes} דקות). זה יכול ×œ×§×Ø×•×Ŗ עם ×ž××ž×Ø×™× ××Ø×•×›×™× או ×ž×•×Ø×›×‘×™×. אנא × ×”×” שוב או הקטן את ××•×Ø×š ×”×ž××ž×Ø."); } elseif (strpos($purpose, 'Generate Article Structure') !== false) { $timeout_minutes = ceil($timeout / 60); return new WP_Error('structure_timeout', "יצירת מבנה ×”×ž××ž×Ø ×‘××ž×¦×¢×•×Ŗ OpenAI ×œ×•×§×—×Ŗ זמן רב מהצפוי ({$timeout_minutes} דקות). זה יכול ×œ×§×Ø×•×Ŗ עם ×ž××ž×Ø×™× ×ž×•×Ø×›×‘×™×. אנא × ×”×” שוב או הפשט את הדרישות."); } else { return new WP_Error('operation_timeout', "הפעולה ×‘××ž×¦×¢×•×Ŗ OpenAI ×œ×•×§×—×Ŗ זמן רב מהצפוי ({$timeout} שניות). אנא × ×”×” שוב ×ž××•×—×Ø יותר."); } } return $response; } $response_body = wp_remote_retrieve_body($response); $response_code = wp_remote_retrieve_response_code($response); $this->debug_log("šŸ“Š AI SEO API (OpenAI): Response code: $response_code"); $this->debug_log("šŸ“„ AI SEO API (OpenAI): Response body length: " . strlen($response_body)); if ($response_code !== 200) { $this->debug_log("āŒ AI SEO API (OpenAI): Non-200 response code: $response_code"); $this->debug_log("šŸ” AI SEO API (OpenAI): Error response body: " . $response_body); $error_data = json_decode($response_body, true); $error_message = $error_data['error']['message'] ?? 'Unknown error'; $error_type = $error_data['error']['type'] ?? 'unknown_error'; $this->debug_log("šŸ’„ AI SEO API (OpenAI): Parsed error - Type: $error_type, Message: $error_message"); if ($response_code === 429) { return new WP_Error('rate_limit_exceeded', '×”×’×¢×Ŗ ×œ×ž×’×‘×œ×Ŗ הבקשות של OpenAI. × ×”×” שוב בעוד כמה דקות.'); } elseif ($response_code >= 500) { return new WP_Error('server_error', 'שגיאת שרת ב-OpenAI API. × ×”×” שוב בעוד כמה דקות.'); } elseif ($response_code === 401) { return new WP_Error('invalid_api_key', '×ž×¤×Ŗ×— ה-API של OpenAI לא ×Ŗ×§×™×Ÿ. אנא בדוק את ההגדרות.'); } return new WP_Error('api_error', 'OpenAI API Error: ' . $error_message); } $data = json_decode($response_body, true); $this->debug_log("šŸ” AI SEO API (OpenAI): JSON decode result: " . (is_array($data) ? 'Success' : 'Failed')); if (!isset($data['choices'][0]['message']['content'])) { $this->debug_log("āŒ AI SEO API (OpenAI): Invalid response structure"); $this->debug_log("šŸ” AI SEO API (OpenAI): Response structure", $data); return new WP_Error('invalid_response', 'Invalid OpenAI API response format'); } $response_text = $data['choices'][0]['message']['content']; $final_duration = round(microtime(true) - $start_time, 2); $this->debug_log("āœ… AI SEO API (OpenAI): Successfully extracted response text, length: " . strlen($response_text) . " (total time: {$final_duration}s)"); return $response_text; } /** * Helper method to route requests to the appropriate AI provider */ private function make_request($purpose, $prompt, $max_tokens = 1000, $timeout = 60) { if ($this->ai_provider === 'openai') { return $this->make_openai_request($purpose, $prompt, $max_tokens, $timeout); } else { return $this->make_claude_request($purpose, $prompt, $max_tokens, $timeout); } } private function build_structure_prompt($main_keyword, $sub_keywords, $target_words, $article_language = 'auto') { $sub_keywords_list = is_array($sub_keywords) ? implode(', ', $sub_keywords) : $sub_keywords; // Determine if content should be Hebrew $is_hebrew = $this->determine_content_language($article_language); $data = array('main_keyword' => $main_keyword, 'is_hebrew' => $is_hebrew); // Apply AEO enhancement filter if ($is_hebrew) { $base_prompt = "אתה כותב ×Ŗ×•×›×Ÿ מקצועי בעברית ×”×ž×Ŗ×ž×—×” ב-SEO, AEO ו-GEO. צור מבנה ×ž×¤×•×Ø×˜ ×œ×ž××ž×Ø אופטימלי בעברית. ×ž×™×œ×Ŗ ×”×ž×¤×Ŗ×— הראשית: {$main_keyword} ×ž×™×œ×•×Ŗ ×ž×¤×Ŗ×— ×ž×©× ×™×•×Ŗ: {$sub_keywords_list} ××•×Ø×š יעד: {$target_words} מילים צור מבנה ×ž××ž×Ø הכולל: 1. כותרת ראשית ×ž×•×©×›×Ŗ וידידותית ×œ×¢×™×˜×•×Ø 2. ×Ŗ×Ŗ-כותרות (H2, H3) עם שילוב טבעי של ×ž×™×œ×•×Ŗ ×”×ž×¤×Ŗ×— 3. פהקאות מבוא והיכום 4. ×Ø×©×™×ž×Ŗ נושאים ×¢×™×§×Ø×™×™× לכל פרק 5. הצעות ×œ×Ŗ×•×›×Ÿ מולטימדיה (×Ŗ×ž×•× ×•×Ŗ, וידאו) šŸŽÆ דרישות AEO (אופטימיזציה למנועי תשובות) 2025: - כל כותרת פרק חייבת ×œ×”×™×•×Ŗ בצורת שאלה (מה זה {$main_keyword}? איך עובד {$main_keyword}? למה חשוב {$main_keyword}?) - כל פרק ×™×Ŗ×—×™×œ בתשובה ישירה של 40-60 מילים - כלול קטעי הגדרה ×‘×Ø×•×Ø×™× לכל מונח ×ž×Ø×›×–×™ - ×Ŗ×›× ×Ÿ מקום ×œ×“×•×’×ž××•×Ŗ ועובדות ×ž×”×¤×Ø×™×•×Ŗ - צור מבנה שקל לחלׄ ממנו מידע (בלוקי מידע ×§×¦×Ø×™×) - הוהף קטעי ×©××œ×•×Ŗ נפוצות בכל פרק ×ž×Ø×›×–×™ המבנה ×¦×Ø×™×š ×œ×”×™×•×Ŗ: - ידידותי למנועי חיפוש (SEO) - ×ž×•×Ŗ×× למנועי תשובות (AEO) - תשובות ישירות וברורות - אופטימלי למנועי יצירה (GEO) - בשפה עברית ×˜×‘×¢×™×Ŗ ×•×ž×§×¦×•×¢×™×Ŗ - ×ž××•×Ø×’×Ÿ בצורה הגיונית וקריאה āš ļø חשוב מאוד: החזר רק JSON ×Ŗ×§×™×Ÿ ללא טקהט נוהף! אל ×Ŗ×©×Ŗ×ž×© בבלוק קוד markdown (```json) החזר רק את ה-JSON הזה ושום דבר אחר: { \"title\": \"כותרת ראשית ×ž×§×¦×•×¢×™×Ŗ עם ×ž×™×œ×Ŗ ×”×ž×¤×Ŗ×—\", \"introduction\": \"תיאור ×ž×¤×•×Ø×˜ של המבוא שיכלול ×ž×™×œ×•×Ŗ ×ž×¤×Ŗ×—\", \"sections\": [ { \"heading\": \"פרק 1: כותרת H2 עם ×ž×™×œ×Ŗ ×ž×¤×Ŗ×—\", \"subheadings\": [\"×Ŗ×Ŗ-נושא 1\", \"×Ŗ×Ŗ-נושא 2\", \"×Ŗ×Ŗ-נושא 3\"], \"topics\": [\"נושא ×ž×¤×•×Ø×˜ 1\", \"נושא ×ž×¤×•×Ø×˜ 2\", \"נושא ×ž×¤×•×Ø×˜ 3\"], \"word_count\": 400 }, { \"heading\": \"פרק 2: כותרת H2 נוהפת\", \"subheadings\": [\"×Ŗ×Ŗ-נושא 1\", \"×Ŗ×Ŗ-נושא 2\"], \"topics\": [\"נושא ×ž×¤×•×Ø×˜ 1\", \"נושא ×ž×¤×•×Ø×˜ 2\"], \"word_count\": 400 } ], \"conclusion\": \"תיאור ×ž×¤×•×Ø×˜ של ההיכום\", \"multimedia_suggestions\": [\"×Ŗ×ž×•× ×” המדגימה את הנושא\", \"אינפוגרפיקה עם × ×Ŗ×•× ×™×\", \"וידאו ההבר\"] } שים לב: - ×œ×ž××ž×Ø של 1000 מילים: צור 4-5 ×¤×Ø×§×™× ×¢×™×§×Ø×™×™× - ×œ×ž××ž×Ø של 1500 מילים: צור 5-6 ×¤×Ø×§×™× ×¢×™×§×Ø×™×™× - ×œ×ž××ž×Ø של 2000 מילים: צור 6-7 ×¤×Ø×§×™× ×¢×™×§×Ø×™×™× - ×œ×ž××ž×Ø של 2500+ מילים: צור 7-9 ×¤×Ø×§×™× ×¢×™×§×Ø×™×™× ×ž×¤×•×Ø×˜×™× עבור ×ž××ž×Ø של {$target_words} מילים - הקפד ×œ×™×¦×•×Ø ×ž×”×¤×Ø ×¤×Ø×§×™× ×ž×Ŗ××™× ×•×ž×¤×•×Ø×˜×™×!"; } else { // English prompt $base_prompt = "You are a professional content writer specializing in SEO, AEO & GEO. Create a detailed structure for an optimal English article. Main keyword: {$main_keyword} Secondary keywords: {$sub_keywords_list} Target length: {$target_words} words Create an article structure including: 1. Engaging main title with keyword integration 2. Subheadings (H2, H3) with natural keyword integration 3. Introduction and conclusion paragraphs 4. List of main topics for each chapter 5. Multimedia content suggestions (images, video) šŸŽÆ AEO (Answer Engine Optimization) Requirements 2025: - Every chapter heading must be in question format (What is {$main_keyword}? How does {$main_keyword} work? Why is {$main_keyword} important?) - Each chapter starts with a direct answer of 40-60 words - Include clear definition sections for all key terms - Plan space for examples and numerical facts - Create structure that's easy to extract information from (short info blocks) - Add FAQ sections in each main chapter The structure should be: - Search engine friendly (SEO) - Optimized for answer engines (AEO) - direct and clear answers - Optimal for generative engines (GEO) - In natural, professional English - Logically organized and readable āš ļø Very important: Return only valid JSON without additional text! Do not use markdown code blocks (```json) Return only this JSON and nothing else: { \"title\": \"Professional main title with keyword\", \"introduction\": \"Detailed description of introduction including keywords\", \"sections\": [ { \"heading\": \"Chapter 1: H2 heading with keyword\", \"subheadings\": [\"Sub-topic 1\", \"Sub-topic 2\", \"Sub-topic 3\"], \"topics\": [\"Detailed topic 1\", \"Detailed topic 2\", \"Detailed topic 3\"], \"word_count\": 400 }, { \"heading\": \"Chapter 2: Another H2 heading\", \"subheadings\": [\"Sub-topic 1\", \"Sub-topic 2\"], \"topics\": [\"Detailed topic 1\", \"Detailed topic 2\"], \"word_count\": 400 } ], \"conclusion\": \"Detailed description of conclusion section\", \"faq\": [ { \"question\": \"Common question about {$main_keyword}\", \"answer_preview\": \"Brief answer preview\" } ], \"total_word_count\": {$target_words} } šŸ“Š Article Length Guidelines: - For 500-1000 word articles: Create 3-4 main chapters - For 1000-1500 word articles: Create 4-5 main chapters - For 1500-2000 word articles: Create 5-6 detailed chapters - For 2000-2500 word articles: Create 6-7 comprehensive chapters - For 2500+ word articles: Create 7-9 detailed main chapters For an article of {$target_words} words - ensure appropriate number of detailed chapters!"; } // Apply AEO enhancement filter return apply_filters('ai_seo_article_generator_structure_prompt', $base_prompt, $data); } private function build_content_prompt($structure, $main_keyword, $sub_keywords, $target_words, $article_language = 'auto') { $structure_json = json_encode($structure, JSON_UNESCAPED_UNICODE); $sub_keywords_list = is_array($sub_keywords) ? implode(', ', $sub_keywords) : $sub_keywords; // Determine content language $is_hebrew = $this->determine_content_language($article_language); // Enhanced word distribution for longer articles $sections_count = isset($structure['sections']) ? count($structure['sections']) : 3; $intro_words = min(300, $target_words * 0.15); // 10-15% for intro $conclusion_words = min(250, $target_words * 0.12); // 10-12% for conclusion $remaining_words = $target_words - $intro_words - $conclusion_words; $words_per_section = floor($remaining_words / $sections_count); // For longer articles, increase minimum words per section if ($target_words >= 2500) { $words_per_section = max($words_per_section, 400); } // Calculate optimal keyword density (1.5-2.5%) $target_keyword_density = 2.0; $target_keyword_occurrences = ceil(($target_words * $target_keyword_density) / 100); // Build detailed section requirements with expansion instructions $section_requirements = ""; if (isset($structure['sections'])) { foreach ($structure['sections'] as $index => $section) { $section_words = isset($section['word_count']) ? max($section['word_count'], $words_per_section) : $words_per_section; $section_keywords_needed = ceil($section_words * 0.02); // 2% density per section if ($is_hebrew) { $section_requirements .= "פרק " . ($index + 1) . " ('{$section['heading']}'): ×œ×¤×—×•×Ŗ {$section_words} מילים ×ž×¤×•×Ø×˜×•×Ŗ\n"; $section_requirements .= " • כלול ×œ×¤×—×•×Ŗ 4-5 פהקאות\n"; $section_requirements .= " • הוהף 2-3 ×“×•×’×ž××•×Ŗ ×ž×¢×©×™×•×Ŗ\n"; $section_requirements .= " • כלול ×Ø×©×™×ž×” של 5-7 נקודות\n"; $section_requirements .= " • ההבר ×ž×¤×•×Ø×˜ וטכני\n"; $section_requirements .= " • שלב את ×ž×™×œ×Ŗ ×”×ž×¤×Ŗ×— '{$main_keyword}' {$section_keywords_needed} פעמים באופן טבעי\n\n"; } else { $section_requirements .= "Chapter " . ($index + 1) . " ('{$section['heading']}'): at least {$section_words} detailed words\n"; $section_requirements .= " • Include at least 4-5 paragraphs\n"; $section_requirements .= " • Add 2-3 practical examples\n"; $section_requirements .= " • Include a list of 5-7 points\n"; $section_requirements .= " • Provide detailed technical explanation\n"; $section_requirements .= " • Integrate the keyword '{$main_keyword}' {$section_keywords_needed} times naturally\n\n"; } } } // Enhanced instructions for longer articles if ($is_hebrew) { $length_category = $target_words >= 2500 ? '××Ø×•×š' : ($target_words >= 1500 ? 'בינוני-××Ø×•×š' : 'בינוני'); $expansion_level = $target_words >= 2500 ? '×ž×§×”×™×ž×œ×™×Ŗ' : 'גבוהה'; $base_prompt = "🚨 כתוב ×ž××ž×Ø מלא ומקיף בעברית - {$target_words} מילים חובה ×ž×•×—×œ×˜×Ŗ! šŸ“š הוג ×ž××ž×Ø: {$length_category} (דרישת הרחבה: {$expansion_level}) āš ļø חשוב ביותר: ×”×ž××ž×Ø חייב להכיל ×œ×¤×—×•×Ŗ {$target_words} מילים! אל תעצור לפני שהגעת ליעד! šŸŽÆ ×¤×Ø×˜×™ ×”×ž××ž×Ø: ×ž×™×œ×Ŗ ×”×ž×¤×Ŗ×— הראשית: {$main_keyword} ×ž×™×œ×•×Ŗ ×ž×¤×Ŗ×— ×ž×©× ×™×•×Ŗ: {$sub_keywords_list} ××•×Ø×š יעד מחייב: {$target_words} מילים בדיוק (לא פחות!) מבנה ×”×ž××ž×Ø: {$structure_json} šŸ“ דרישות ××•×Ø×š ×ž×“×•×™×§×•×Ŗ (×”×”\"כ {$target_words} מילים): - מבוא ×ž×¤×•×Ø×˜: ×œ×¤×—×•×Ŗ {$intro_words} מילים - היכום מקיף: ×œ×¤×—×•×Ŗ {$conclusion_words} מילים {$section_requirements} šŸŽÆ דרישות SEO ×§×Ø×™×˜×™×•×Ŗ: - שילוב ×ž×™×œ×Ŗ ×”×ž×¤×Ŗ×— '{$main_keyword}' בדיוק {$target_keyword_occurrences} פעמים (צפיפות {$target_keyword_density}%) - חייב ×œ×”×™×•×Ŗ H1 אחד בלבד עם ×ž×™×œ×Ŗ ×”×ž×¤×Ŗ×— בכותרת הראשית - ×œ×¤×—×•×Ŗ {$sections_count} כותרות H2 עם שילוב ×ž×™×œ×•×Ŗ ×ž×¤×Ŗ×— - ×œ×¤×—×•×Ŗ " . ($sections_count * 2) . " כותרות H3 לתת-נושאים - שילוב טבעי של ×ž×™×œ×•×Ŗ ×”×ž×¤×Ŗ×— ×”×ž×©× ×™×•×Ŗ בכל פרק šŸ”„ דרישות HTML וקידום: - ×”×©×Ŗ×ž×© בכותרות היררכיות: H1→H2→H3→H4 בלבד - H1: רק ×œ×›×•×Ŗ×Ø×Ŗ הראשית (חייב לכלול ×ž×™×œ×Ŗ ×ž×¤×Ŗ×—) - H2: לכל פרק ראשי (5-7 ×¤×Ø×§×™× מומלׄ) - H3: לתת-נושאים ×‘×Ŗ×•×š כל פרק (2-4 לכל פרק) - H4: ×œ×¤×™×Ø×•×˜×™× נוהפים אם נדרש - שימוש ×‘×Ø×©×™×ž×•×Ŗ