base_url = $this->getBaseUrl(); $this->use_ai_agent = $this->shouldUseAiAgentFromSettings(); } /** * Get base URL - Trả về AIAUTOTOOL_API_GEM (dùng cho các method không qua sendPostRequestFull) * @return string Base URL */ private function getBaseUrl() { return defined('AIAUTOTOOL_API_GEM') ? AIAUTOTOOL_API_GEM : ''; } /** * Set whether to use AI agent or not * @param bool $use_ai_agent True to use AI agent (mặc định), false to use direct API call */ public function setUseAiAgent($use_ai_agent = true) { $this->use_ai_agent = $use_ai_agent; } /** * Read AI execution mode from AI Provider Settings. * * @return bool True when Agent mode is enabled, false for direct provider API. */ private function shouldUseAiAgentFromSettings() { if (!function_exists('get_option')) { return true; } $mode = get_option('aiautotool_ai_execution_mode', 'agent'); return $mode !== 'direct_api'; } /** * Get current base URL * @return string Current base URL */ public function getCurrentBaseUrl() { return $this->base_url; } /** * Send POST request via AI Agent instead of direct API call * @param string $url Original URL (for endpoint detection) * @param array $data Request data * @return array Response with code, msg, result format */ private function sendPostRequestFullViaAiAgent($url, $data) { if (!class_exists('AIAutotool_AI_Agent')) { return $this->sendPostRequestFullViaAiProvider($url, $data); } // Debug log if (defined('WP_DEBUG') && WP_DEBUG) { } try { // Lấy AI Agent instance $agent = AIAutotool_AI_Agent::get_instance(); // Chuẩn bị prompt từ data $question = isset($data['question']) ? $data['question'] : ''; if (empty($question)) { return array( 'code' => '400', 'msg' => 'Missing question parameter', 'result' => false ); } // Chuẩn bị options cho AI Agent $options = array(); // Map lang nếu có if (isset($data['lang'])) { // AI Agent không cần lang parameter, nhưng có thể dùng trong system prompt $lang = $data['lang']; if ($lang !== 'English' && $lang !== 'english') { $options['system'] = "Please respond in {$lang} language."; } } // Map model nếu có if (isset($data['model'])) { $options['model'] = $data['model']; } // Map toneOfVoice nếu có (cho bardrewrite) if (isset($data['toneOfVoice']) && !empty($data['toneOfVoice'])) { $tone = $data['toneOfVoice']; $system_prompt = isset($options['system']) ? $options['system'] . "\n" : ''; $system_prompt .= "Please write in {$tone} tone of voice."; $options['system'] = $system_prompt; } // Detect use case từ URL endpoint $use_case = $this->detectUseCaseFromUrl($url); if ($use_case) { $options['use_case'] = $use_case; } if ($this->ensureAiProviderLoaded()) { $manager = AIAutotool_AI_Provider_Manager::get_instance(); $requested_provider_id = isset($data['provider_id']) ? sanitize_text_field((string) $data['provider_id']) : ''; $provider = $requested_provider_id !== '' ? $manager->get_provider($requested_provider_id) : $manager->get_active_provider(); if ($provider && $provider->is_configured()) { $options['provider'] = $provider->get_id(); $options['model'] = $this->resolveProviderModel($provider, isset($data['model']) ? (string) $data['model'] : ''); } } // Gọi AI Agent. Planning mode gives the agent access to its tool/action layer; // generate() remains a fallback for simple providers or incomplete planner setup. $result = $agent->execute_with_planning($question, array_merge($options, array( 'skip_clarification' => true, ))); if (empty($result['success']) || empty($result['content'])) { $fallback_result = $agent->generate($question, $options); if (!empty($fallback_result['success']) && !empty($fallback_result['content'])) { $result = $fallback_result; } } // Format response giống với API cũ if ($result['success'] && isset($result['content']) && !empty($result['content'])) { if (defined('WP_DEBUG') && WP_DEBUG) { } return array( 'code' => '200', 'msg' => 'Success', 'result' => $result['content'] ); } else { $error_msg = isset($result['error']) ? $result['error'] : 'AI Agent failed to generate content'; // Log chi tiết tất cả failed attempts if (defined('WP_DEBUG') && WP_DEBUG) { // Log tất cả failed attempts nếu có if (isset($result['failed_attempts']) && is_array($result['failed_attempts']) && !empty($result['failed_attempts'])) { foreach ($result['failed_attempts'] as $attempt_key => $attempt_error) { // Parse attempt_key để lấy provider và model $parts = explode(':', $attempt_key, 2); $provider = isset($parts[0]) ? $parts[0] : 'unknown'; $model = isset($parts[1]) ? $parts[1] : 'unknown'; // Parse attempt_error để lấy error message $error_detail = is_array($attempt_error) ? (isset($attempt_error['error']) ? $attempt_error['error'] : json_encode($attempt_error)) : (string)$attempt_error; // Kiểm tra nếu là HuggingFace và có lỗi về credits if ($provider === 'huggingface' && (stripos($error_detail, 'exceeded') !== false || stripos($error_detail, 'credits') !== false || stripos($error_detail, 'Inference Providers') !== false)) { } } } } return array( 'code' => '500', 'msg' => $error_msg, 'result' => false ); } } catch (Exception $e) { if (defined('WP_DEBUG') && WP_DEBUG) { } return array( 'code' => '500', 'msg' => 'AI Agent error: ' . $e->getMessage(), 'result' => false ); } } /** * Detect use case from URL endpoint * @param string $url URL endpoint * @return string|null Use case or null */ private function detectUseCaseFromUrl($url) { // Detect use case từ endpoint if (strpos($url, '/bardrewrite') !== false) { return 'general'; // Rewrite thường là general } elseif (strpos($url, '/bardcontentmore') !== false) { return 'long_context'; // Content more thường cần long context } elseif (strpos($url, '/bardcontent') !== false) { return 'general'; // Content generation } return null; // Để AI Agent tự detect } /** * Send text generation requests through the configured AI Provider. * * This replaces the legacy bard.aitoolseo.com text endpoints for AI Post. * * @param string $url Original legacy endpoint URL. * @param array $data Request data. * @return array Response with code, msg, result. */ private function sendPostRequestFullViaAiProvider($url, $data) { if (!$this->ensureAiProviderLoaded()) { return array( 'code' => '500', 'msg' => 'AI Provider module is not available', 'result' => false ); } try { $manager = AIAutotool_AI_Provider_Manager::get_instance(); $requested_provider_id = isset($data['provider_id']) ? sanitize_text_field((string) $data['provider_id']) : ''; $route = false; $provider = false; if ($requested_provider_id !== '') { $provider = $manager->get_provider($requested_provider_id); if (!$provider || !$provider->is_configured()) { return array( 'code' => '500', 'msg' => __('Selected AI provider is not configured. Please choose another provider or update AI Provider settings.', 'ai-auto-tool'), 'result' => false ); } } else { $route = method_exists($manager, 'get_provider_for_request') ? $manager->get_provider_for_request() : false; $provider = $route && isset($route['provider']) ? $route['provider'] : $manager->get_active_provider(); } if (!$provider || !$provider->is_configured()) { $manager->auto_detect_provider(); $route = method_exists($manager, 'get_provider_for_request') ? $manager->get_provider_for_request() : false; $provider = $route && isset($route['provider']) ? $route['provider'] : $manager->get_active_provider(); } if (!$provider || !$provider->is_configured()) { return array( 'code' => '500', 'msg' => __('No configured AI provider. Please add an API key and select an active AI Provider first.', 'ai-auto-tool'), 'result' => false ); } $question = isset($data['question']) ? trim((string) $data['question']) : ''; if ($question === '') { return array( 'code' => '400', 'msg' => 'Missing question parameter', 'result' => false ); } $lang = isset($data['lang']) ? trim((string) $data['lang']) : 'Vietnamese'; $messages = array(); $system_parts = array(); if ($lang !== '') { $system_parts[] = "Respond in {$lang}."; } if (isset($data['toneOfVoice']) && trim((string) $data['toneOfVoice']) !== '') { $system_parts[] = 'Use a ' . trim((string) $data['toneOfVoice']) . ' tone of voice.'; } if (!empty($system_parts)) { $messages[] = array( 'role' => 'system', 'content' => implode(' ', $system_parts) ); } $messages[] = array( 'role' => 'user', 'content' => $question ); $model = $this->resolveProviderModel($provider, isset($data['model']) ? (string) $data['model'] : '', $route && isset($route['model']) ? $route['model'] : ''); $max_tokens = (strpos($url, '/bardcontentmore') !== false || strpos($url, '/bardcontentfull') !== false) ? 4000 : 2000; $response = $provider->chat_completion($messages, array( 'model' => $model, 'temperature' => 0.7, 'max_tokens' => $max_tokens, 'top_p' => 1.0, )); if (!is_array($response) || empty($response['success'])) { return array( 'code' => '500', 'msg' => isset($response['error']) ? $response['error'] : 'AI Provider failed to generate content', 'result' => false ); } $content = isset($response['content']) ? (string) $response['content'] : ''; if ($content === '') { return array( 'code' => '500', 'msg' => 'AI Provider returned empty content', 'result' => false ); } return array( 'code' => '200', 'msg' => 'Success', 'result' => $content, 'provider' => $provider->get_id(), 'model' => isset($response['model']) ? $response['model'] : $model ); } catch (Exception $e) { return array( 'code' => '500', 'msg' => 'AI Provider error: ' . $e->getMessage(), 'result' => false ); } } private function ensureAiProviderLoaded() { if (class_exists('AIAutotool_AI_Provider_Manager')) { return true; } $ai_providers_index = dirname(__DIR__) . '/inc/ai-providers/index.php'; if (file_exists($ai_providers_index)) { require_once $ai_providers_index; } return class_exists('AIAutotool_AI_Provider_Manager'); } private function resolveProviderModel($provider, $requested_model = '', $routed_model = '') { $available_models = $provider->get_available_models(); $default_model = $provider->get_default_model(); $active_model = ''; if (class_exists('AIAutotool_AI_Provider_Manager')) { $active_model = AIAutotool_AI_Provider_Manager::get_instance()->get_active_model(); } if ($requested_model !== '' && isset($available_models[$requested_model])) { return $requested_model; } if ($routed_model !== '' && isset($available_models[$routed_model])) { return $routed_model; } if ($active_model !== '' && isset($available_models[$active_model])) { return $active_model; } return $default_model; } public function generateRandomDocId() { $url = $this->base_url . '/generateRandomDocId'; return $this->sendGetRequest($url); } public function generateRandomKey() { $url = $this->base_url . '/generateRandomKey'; return $this->sendGetRequest($url); } public function maskdown($text) { // Check if text is null or empty if ($text === null || $text === '') { return ''; } $Parsedown = new Parsedown(); $cleaned_text = $this->removeImageTags($text); // Ensure cleaned text is not null if ($cleaned_text === null || $cleaned_text === '') { return ''; } $html = $Parsedown->text($cleaned_text); return $html ?: ''; } public function removeImageTags($inputString) { // Handle null or non-string input if ($inputString === null) { return ''; } // Convert to string if not already if (!is_string($inputString)) { $inputString = (string) $inputString; } if(!empty($inputString)){ $pattern = '/\[Image[^\]]*\]/'; $replacement = ''; $outputString = preg_replace($pattern, $replacement, $inputString); return $outputString ?: ''; }else{ return ''; } } public function bardrewrite($question, $lang = 'Vietnamese',$toneOfVoice='', $model = 'gpt-4.1-nano-2025-04-14', $provider_id = '') { $url = $this->base_url . '/bardrewrite'; $data = ['question' => $question, 'lang' => $lang,'toneOfVoice'=>$toneOfVoice, 'model' => $model, 'provider_id' => $provider_id]; $response = $this->sendPostRequestFull($url, $data); // Check if API returned error code if (isset($response['code']) && $response['code'] != '200' && $response['code'] != 200) { // Return full response with error code and message return $response; } // If successful, return masked result if (isset($response['result']) && $response['result'] !== false) { return $this->maskdown($response['result']); } return $response; } public function bardcontentfull($question, $lang = 'Vietnamese', $model = 'gpt-4.1-nano-2025-04-14', $provider_id = '') { $url = $this->base_url . '/bardcontentfull'; $data = ['question' => $question, 'lang' => $lang, 'model' => $model, 'provider_id' => $provider_id]; $result = $this->sendPostRequest($url, $data); // If API call failed, return error array instead of passing null to maskdown if ($result === null || $result === false) { return array( 'code' => '500', 'msg' => 'API call failed or returned no result', 'result' => false ); } return $this->maskdown($result); } public function bardcontent($question, $lang = 'Vietnamese', $model = 'gpt-4.1-nano-2025-04-14', $provider_id = '') { $url = $this->base_url . '/bardcontent'; $data = ['question' => $question, 'lang' => $lang, 'model' => $model, 'provider_id' => $provider_id]; $response = $this->sendPostRequestFull($url, $data); // Check if API returned error code if (isset($response['code']) && $response['code'] != '200' && $response['code'] != 200) { // Return full response with error code and message return $response; } // If successful, return masked result if (isset($response['result']) && $response['result'] !== false) { return $this->maskdown($response['result']); } return $response; } public function bardcontentRaw($question, $lang = 'Vietnamese') { $url = $this->base_url . '/bardcontent'; $data = ['question' => $question, 'lang' => $lang]; return $this->sendPostRequest($url, $data); } public function bardcontentmore($question, $lang = 'Vietnamese', $model = 'gpt-4.1-nano-2025-04-14', $provider_id = ''){ $url = $this->base_url . '/bardcontentmore'; $data = ['question' => $question, 'lang' => $lang, 'model' => $model, 'provider_id' => $provider_id]; $response = $this->sendPostRequestFull($url, $data); // Check if API returned error code if (isset($response['code']) && $response['code'] != '200' && $response['code'] != 200) { // Return full response with error code and message return $response; } // If successful, return masked result if (isset($response['result']) && $response['result'] !== false) { return $this->maskdown($response['result']); } return $response; } /** // Tạo prompt cho bài viết hoàn chỉnh $prompt = $this->generateFullArticlePrompt($keywords, $lang, $chapters, $words); // Sử dụng API bardcontentmore hiện có $url = $this->base_url . '/bardcontentmore'; $data = [ 'question' => $prompt, 'lang' => $lang ]; $result = $this->sendPostRequest($url, $data); // Xử lý kết quả để tạo cấu trúc bài viết return $this->processFullArticleResult($result, $keywords, $chapters, $words); } /** * Tạo prompt cho bài viết hoàn chỉnh */ private function generateFullArticlePrompt($keywords, $lang, $chapters, $words) { $wordsPerChapter = round($words / $chapters); $prompt = "Viết một bài viết hoàn chỉnh về chủ đề: {$keywords}\n\n"; $prompt .= "Yêu cầu:\n"; $prompt .= "- Tạo {$chapters} chapters với cấu trúc logic\n"; $prompt .= "- Mỗi chapter khoảng {$wordsPerChapter} từ\n"; $prompt .= "- Tổng cộng khoảng {$words} từ\n"; $prompt .= "- Bao gồm introduction và conclusion\n"; $prompt .= "- Sử dụng HTML formatting (h2, p, ul, li, strong, em)\n"; $prompt .= "- Viết bằng {$lang}\n"; $prompt .= "- Tối ưu SEO\n"; $prompt .= "- Nội dung chất lượng cao và hữu ích\n\n"; $prompt .= "Cấu trúc bài viết:\n"; $prompt .= "1. Introduction (giới thiệu chủ đề)\n"; for ($i = 1; $i <= $chapters; $i++) { $prompt .= ($i + 1) . ". Chapter {$i} (nội dung chính)\n"; } $prompt .= ($chapters + 2) . ". Conclusion (kết luận)\n\n"; $prompt .= "Hãy viết bài viết hoàn chỉnh với đầy đủ các phần trên."; return $prompt; } /** * Xử lý kết quả để tạo cấu trúc bài viết */ private function processFullArticleResult($result, $keywords, $chapters, $words) { // Tạo title từ keywords $title = $this->generateTitleFromKeywords($keywords); // Tạo tags từ keywords $tags = $this->generateTagsFromKeywords($keywords); return [ 'title' => $title, 'article' => $result, 'tags' => $tags, 'keywords' => $keywords, 'chapters' => $chapters, 'words' => $words ]; } /** * Tạo title từ keywords */ /** * Tạo tags từ keywords */ public function gentitle($question, $lang = 'Vietnamese', $model = 'gpt-4.1-nano-2025-04-14', $provider_id = '') { $url = $this->base_url . '/gentitle'; $data = ['question' => $question, 'lang' => $lang, 'model' => $model, 'provider_id' => $provider_id]; return $this->sendPostRequest($url, $data); } /** * Generate ideas/titles for blog posts * @param string $lang Language for generation * @param int $count Number of ideas to generate (default: 10) * @return array Array of idea objects with 'title' property */ public function generateIdeas($lang = 'Vietnamese', $count = 10, $category = '') { // Try to use dedicated API endpoint if available $url = $this->base_url . '/generateIdeas'; $data = ['lang' => $lang, 'count' => $count]; // Add category to data if provided if (!empty($category)) { $data['category'] = $category; } try { $result = $this->sendPostRequest($url, $data); // Check if result is valid if ($result === null || $result === false) { return $this->generateIdeasFallback($lang, $count, $category); } // If result is array, return as is if (is_array($result)) { // Ensure each item has 'title' property $ideas = array_map(function($item) { if (is_string($item)) { return ['title' => $item]; } elseif (is_array($item) && isset($item['title'])) { return $item; } elseif (is_array($item) && isset($item[0])) { return ['title' => $item[0]]; } return ['title' => is_string($item) ? $item : '']; }, $result); if (!empty($ideas)) { return array_slice($ideas, 0, $count); } } // If result is string, try to parse it as JSON or split by newlines if (is_string($result) && !empty($result)) { $decoded = json_decode($result, true); if (is_array($decoded)) { $ideas = array_map(function($item) { return ['title' => is_array($item) ? ($item['title'] ?? $item[0] ?? '') : $item]; }, $decoded); if (!empty($ideas)) { return array_slice($ideas, 0, $count); } } // Split by newlines if it's a text string $lines = array_filter(array_map('trim', explode("\n", $result))); if (!empty($lines)) { $ideas = array_map(function($line) { // Remove numbering (1., 2., etc.) $line = preg_replace('/^\d+[\.\)]\s*/', '', $line); $line = trim($line); return ['title' => $line]; }, array_slice($lines, 0, $count)); if (!empty($ideas)) { return $ideas; } } } } catch (Exception $e) { // Fallback: use bardcontentmore to generate ideas if (defined('WP_DEBUG') && WP_DEBUG) { } } catch (Error $e) { // Fallback: use bardcontentmore to generate ideas if (defined('WP_DEBUG') && WP_DEBUG) { } } // Fallback if API doesn't work return $this->generateIdeasFallback($lang, $count, $category); } /** * Fallback method to generate ideas using bardcontentmore */ private function generateIdeasFallback($lang = 'Vietnamese', $count = 10, $category = '') { $prompt = "Hãy tạo danh sách {$count} ý tưởng tiêu đề bài viết blog hấp dẫn và SEO-friendly. "; // Add category context if provided if (!empty($category)) { $prompt .= "Các tiêu đề nên liên quan đến chủ đề/chuyên mục: {$category}. "; } $prompt .= "Mỗi tiêu đề nên ngắn gọn, hấp dẫn và dễ hiểu. "; $prompt .= "Trả về danh sách dạng:\n"; $prompt .= "1. Tiêu đề 1\n"; $prompt .= "2. Tiêu đề 2\n"; $prompt .= "...\n"; $prompt .= "Viết bằng {$lang}"; try { $result = $this->bardcontentmore($prompt, $lang); if (!empty($result)) { // Parse the result to extract titles $lines = array_filter(array_map('trim', explode("\n", strip_tags($result)))); $ideas = []; foreach ($lines as $line) { // Remove numbering and bullet points $line = preg_replace('/^[\d\-\*\•\◦]\s*/', '', $line); $line = preg_replace('/^\d+[\.\)]\s*/', '', $line); $line = trim($line); if (!empty($line) && strlen($line) > 10) { $ideas[] = ['title' => $line]; if (count($ideas) >= $count) { break; } } } if (!empty($ideas)) { return $ideas; } } } catch (Exception $e) { // Continue to default fallback } // Default fallback: return some generic ideas $categoryPrefix = !empty($category) ? $category . ': ' : ''; $defaultIdeas = [ ['title' => $categoryPrefix . 'Cách bắt đầu với ' . $lang], ['title' => $categoryPrefix . 'Hướng dẫn chi tiết về ' . $lang], ['title' => $categoryPrefix . 'Tips và tricks cho ' . $lang], ['title' => $categoryPrefix . 'Những điều cần biết về ' . $lang], ['title' => $categoryPrefix . 'Best practices cho ' . $lang], ]; return array_slice($defaultIdeas, 0, min($count, count($defaultIdeas))); } public function searchimg($question) { $url = $this->base_url . '/searchimg'; $data = ['question' => $question]; return $this->sendPostRequest($url, $data); } /** * Get list of available models from API (with caching) * @param bool $use_v1_endpoint Whether to use /v1/models endpoint (OpenAI-compatible) or /api/models * @param bool $force_refresh Force refresh cache and fetch from API * @param int $cache_duration Cache duration in seconds (default: 24 hours) * @return array|false Array of models or false on error */ public function getModels($use_v1_endpoint = true, $force_refresh = false, $cache_duration = 86400) { // Cache key for storing models $cache_key = 'aiautotool_bard_models_1_' . ($use_v1_endpoint ? 'v1' : 'api'); // Try to get from cache first (unless force refresh) if (!$force_refresh) { $cached_models = get_transient($cache_key); if ($cached_models !== false && is_array($cached_models)) { // Return cached models if (defined('WP_DEBUG') && WP_DEBUG) { } return $cached_models; } } // Try /v1/models first (OpenAI-compatible), fallback to /api/models $url = $use_v1_endpoint ? $this->base_url . '/v1/models' : $this->base_url . '/api/models'; $response = $this->sendGetRequest($url); if ($response === false || empty($response)) { // Fallback to /api/models if /v1/models fails if ($use_v1_endpoint) { return $this->getModels(false, $force_refresh, $cache_duration); } // If API fails, try to return cached data even if expired $cached_models = get_transient($cache_key); if ($cached_models !== false && is_array($cached_models)) { if (defined('WP_DEBUG') && WP_DEBUG) { } return $cached_models; } return false; } $decoded = json_decode($response, true); if ($decoded === null || json_last_error() !== JSON_ERROR_NONE) { if (defined('WP_DEBUG') && WP_DEBUG) { } // Fallback to /api/models if /v1/models fails if ($use_v1_endpoint) { return $this->getModels(false, $force_refresh, $cache_duration); } // If decode fails, try to return cached data even if expired $cached_models = get_transient($cache_key); if ($cached_models !== false && is_array($cached_models)) { if (defined('WP_DEBUG') && WP_DEBUG) { } return $cached_models; } return false; } // Cache the successful response set_transient($cache_key, $decoded, $cache_duration); if (defined('WP_DEBUG') && WP_DEBUG) { } return $decoded; } /** * Clear cached models * @param bool $use_v1_endpoint Whether to clear v1 or api cache * @return bool True on success, false on failure */ public function clearModelsCache($use_v1_endpoint = true) { $cache_key = 'aiautotool_bard_models_' . ($use_v1_endpoint ? 'v1' : 'api'); return delete_transient($cache_key); } /** * Clear all models cache (both v1 and api) * @return bool True if all caches cleared successfully */ public function clearAllModelsCache() { $result1 = $this->clearModelsCache(true); $result2 = $this->clearModelsCache(false); return $result1 && $result2; } private function sendGetRequest($url) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 5); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); $response = curl_exec($ch); $curl_error = curl_error($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // Check for curl errors if ($response === false || !empty($curl_error)) { if (defined('WP_DEBUG') && WP_DEBUG) { } return false; } // Check HTTP status code if ($http_code !== 200) { if (defined('WP_DEBUG') && WP_DEBUG) { } return false; } return $response; } private function sendPostRequest($url, $data) { if ($this->isTextGenerationEndpoint($url)) { $response = ($this->use_ai_agent && class_exists('AIAutotool_AI_Agent')) ? $this->sendPostRequestFullViaAiAgent($url, $data) : $this->sendPostRequestFullViaAiProvider($url, $data); return (isset($response['code']) && (string) $response['code'] === '200' && isset($response['result'])) ? $response['result'] : null; } $data['info'] = rendersetting::aiautotool_getdata(); $data['domain'] = get_option('home'); // Debug log: URL and request data if (defined('WP_DEBUG') && WP_DEBUG) { } $json_data = json_encode($data); $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $json_data); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); $response = curl_exec($ch); $curl_error = curl_error($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // Debug log: Response info if (defined('WP_DEBUG') && WP_DEBUG) { if ($response !== false && strlen($response) > 0) { // Log first 500 chars of response for debugging (truncate if too long) $response_preview = strlen($response) > 500 ? substr($response, 0, 500) . '...' : $response; } } // Check for curl errors if ($response === false || !empty($curl_error)) { if (defined('WP_DEBUG') && WP_DEBUG) { } return null; } // Check HTTP status code if ($http_code !== 200) { if (defined('WP_DEBUG') && WP_DEBUG) { } return null; } // Decode JSON response $decoded = json_decode($response); // Check if decode was successful and response has result property if ($decoded === null || json_last_error() !== JSON_ERROR_NONE) { if (defined('WP_DEBUG') && WP_DEBUG) { } return null; } // Debug log: Decoded response structure if (defined('WP_DEBUG') && WP_DEBUG) { } // Check if result property exists if (!isset($decoded->result)) { if (defined('WP_DEBUG') && WP_DEBUG) { } return null; } // Debug log: Success if (defined('WP_DEBUG') && WP_DEBUG) { $result_preview = is_string($decoded->result) ? (strlen($decoded->result) > 200 ? substr($decoded->result, 0, 200) . '...' : $decoded->result) : 'N/A'; } return $decoded->result; } private function isTextGenerationEndpoint($url) { return strpos($url, '/bardcontent') !== false || strpos($url, '/bardcontentmore') !== false || strpos($url, '/bardcontentfull') !== false || strpos($url, '/bardrewrite') !== false; } /** * Send POST request and return full response including code and msg * Mặc định sử dụng AI Agent thay vì gọi API trực tiếp */ private function sendPostRequestFull($url, $data, $max_retries = 3) { // Nếu sử dụng AI Agent (mặc định), thay vì gọi API trực tiếp, sử dụng AI Agent if ($this->use_ai_agent && class_exists('AIAutotool_AI_Agent')) { return $this->sendPostRequestFullViaAiAgent($url, $data); } if ($this->isTextGenerationEndpoint($url)) { return $this->sendPostRequestFullViaAiProvider($url, $data); } // Nếu không sử dụng AI Agent, gọi API trực tiếp như cũ $data['info'] = rendersetting::aiautotool_getdata(); $data['domain'] = get_option('home'); // Debug log: URL and request data if (defined('WP_DEBUG') && WP_DEBUG) { } $json_data = json_encode($data); $attempt = 0; while ($attempt < $max_retries) { $attempt++; if (defined('WP_DEBUG') && WP_DEBUG) { } $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $json_data); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); $response = curl_exec($ch); $curl_error = curl_error($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // Debug log: Response info if (defined('WP_DEBUG') && WP_DEBUG) { if ($response !== false && is_string($response) && strlen($response) > 0) { $response_preview = strlen($response) > 500 ? substr($response, 0, 500) . '...' : $response; } } // Check for curl errors (timeout, connection errors, etc.) $is_timeout = false; $is_retryable_error = false; if ($response === false || !empty($curl_error)) { // Check if it's a timeout error if (strpos($curl_error, 'timeout') !== false || strpos($curl_error, 'timed out') !== false) { $is_timeout = true; $is_retryable_error = true; } else { $is_retryable_error = true; } } // Check HTTP status code - retry on 100 (Continue) or other retryable errors if ($http_code == 100 || ($http_code >= 500 && $http_code < 600)) { $is_retryable_error = true; } // If it's a retryable error and we have retries left, retry if ($is_retryable_error && $attempt < $max_retries) { // Exponential backoff: wait 1s, 2s, 4s... $wait_time = pow(2, $attempt - 1); if (defined('WP_DEBUG') && WP_DEBUG) { if ($is_timeout) { } elseif ($http_code == 100) { } else { } } sleep($wait_time); continue; // Retry the request } // If we've exhausted retries or it's a non-retryable error, return error if ($response === false || !empty($curl_error)) { if (defined('WP_DEBUG') && WP_DEBUG) { } return array( 'code' => '500', 'msg' => 'Network error: ' . $curl_error . ' (after ' . $attempt . ' attempts)', 'result' => false ); } // Check HTTP status code if ($http_code !== 200) { // Try to parse error response from server $error_msg = 'HTTP error: ' . $http_code; $error_code = '500'; if (!empty($response) && is_string($response)) { $error_response = json_decode($response, true); if ($error_response && isset($error_response['msg'])) { $error_msg = $error_response['msg']; } if ($error_response && isset($error_response['code'])) { $error_code = $error_response['code']; } } if (defined('WP_DEBUG') && WP_DEBUG) { } return array( 'code' => $error_code, 'msg' => $error_msg . ' (after ' . $attempt . ' attempts)', 'result' => false ); } // Success - break out of retry loop break; } // Decode JSON response $decoded = json_decode($response, true); // Use true to get associative array // Check if decode was successful if ($decoded === null || json_last_error() !== JSON_ERROR_NONE) { if (defined('WP_DEBUG') && WP_DEBUG) { } return array( 'code' => '500', 'msg' => 'Invalid response from server', 'result' => false ); } // Debug log: Decoded response structure if (defined('WP_DEBUG') && WP_DEBUG) { } // Decode HTML entities in result if it's a string $decoded_result = isset($decoded['result']) ? $decoded['result'] : false; if (is_string($decoded_result) && !empty($decoded_result)) { // Decode HTML entities (like < to <, > to >, etc.) $decoded_result = html_entity_decode($decoded_result, ENT_QUOTES | ENT_HTML5, 'UTF-8'); } // Return full response array with code, msg, and result $result = array( 'code' => isset($decoded['code']) ? $decoded['code'] : '200', 'msg' => isset($decoded['msg']) ? $decoded['msg'] : 'Success', 'result' => $decoded_result ); return $result; } /** try { // Bước 1: Tạo cấu trúc bài viết $structure = $this->generateArticleStructure($keywords, $lang, $chapters); // Bước 2: Tạo introduction $introduction = $this->generateIntroduction($keywords, $lang); // Bước 3: Tạo từng chapter $chapterContent = []; for ($i = 1; $i <= $chapters; $i++) { $chapterPrompt = $structure['chapters'][$i - 1]; $chapterContent[] = $this->generateChapter($chapterPrompt, $lang, $i); } // Bước 4: Tạo conclusion $conclusion = $this->generateConclusion($keywords, $lang); // Bước 5: Ghép tất cả lại $fullArticle = $this->assembleFullArticle($introduction, $chapterContent, $conclusion); // Bước 6: Tạo title và tags $title = $this->generateTitleFromKeywords($keywords); $tags = $this->generateTagsFromKeywords($keywords); return [ 'title' => $title, 'article' => $fullArticle, 'tags' => $tags, 'keywords' => $keywords, 'chapters' => $chapters, 'words' => $words, 'structure' => $structure ]; } catch (Exception $e) { // Fallback về method đơn giản nếu có lỗi return $this->writefullarticle($keywords, $lang, $chapters, $words); } } /** * Tạo cấu trúc bài viết */ private function generateArticleStructure($keywords, $lang, $chapters) { $prompt = "Tạo cấu trúc chi tiết cho bài viết về: {$keywords}\n\n"; $prompt .= "Yêu cầu:\n"; $prompt .= "- Tạo {$chapters} chapters với tiêu đề cụ thể\n"; $prompt .= "- Mỗi chapter cần có prompt để viết nội dung\n"; $prompt .= "- Cấu trúc logic và mạch lạc\n"; $prompt .= "- Viết bằng {$lang}\n\n"; $prompt .= "Format trả về JSON:\n"; $prompt .= '{"title": "Tiêu đề bài viết", "subtitle": "Phụ đề", "chapters": [{"title": "Tiêu đề chapter 1", "prompt": "Prompt để viết chapter 1"}, ...]}'; $url = $this->base_url . '/bardcontentmore'; $data = ['question' => $prompt, 'lang' => $lang]; $result = $this->sendPostRequest($url, $data); // Parse JSON result $structure = json_decode($result, true); if (!$structure) { // Fallback structure $structure = [ 'title' => ucfirst($keywords), 'subtitle' => 'Hướng dẫn chi tiết', 'chapters' => [] ]; for ($i = 1; $i <= $chapters; $i++) { $structure['chapters'][] = [ 'title' => "Chapter {$i}", 'prompt' => "Viết nội dung cho chapter {$i} về {$keywords}" ]; } } return $structure; } /** * Tạo introduction */ private function generateIntroduction($keywords, $lang) { $prompt = "Viết phần giới thiệu (introduction) cho bài viết về: {$keywords}\n\n"; $prompt .= "Yêu cầu:\n"; $prompt .= "- Khoảng 100-150 từ\n"; $prompt .= "- Giới thiệu chủ đề một cách hấp dẫn\n"; $prompt .= "- Sử dụng HTML formatting\n"; $prompt .= "- Viết bằng {$lang}\n"; $prompt .= "- Không cần tiêu đề 'Introduction'"; $url = $this->base_url . '/bardcontentmore'; $data = ['question' => $prompt, 'lang' => $lang]; return $this->sendPostRequest($url, $data); } /** * Tạo chapter */ private function generateChapter($chapterPrompt, $lang, $chapterNumber) { $prompt = "Viết nội dung cho chapter {$chapterNumber}:\n\n"; $prompt .= "Tiêu đề: {$chapterPrompt['title']}\n"; $prompt .= "Prompt: {$chapterPrompt['prompt']}\n\n"; $prompt .= "Yêu cầu:\n"; $prompt .= "- Khoảng 300-400 từ\n"; $prompt .= "- Sử dụng HTML formatting (h2, p, ul, li, strong, em)\n"; $prompt .= "- Viết bằng {$lang}\n"; $prompt .= "- Nội dung chất lượng cao và hữu ích"; $url = $this->base_url . '/bardcontentmore'; $data = ['question' => $prompt, 'lang' => $lang]; return $this->sendPostRequest($url, $data); } /** * Tạo conclusion */ private function generateConclusion($keywords, $lang) { $prompt = "Viết phần kết luận (conclusion) cho bài viết về: {$keywords}\n\n"; $prompt .= "Yêu cầu:\n"; $prompt .= "- Khoảng 100-150 từ\n"; $prompt .= "- Tóm tắt những điểm chính\n"; $prompt .= "- Đưa ra kết luận hoặc gợi ý\n"; $prompt .= "- Sử dụng HTML formatting\n"; $prompt .= "- Viết bằng {$lang}\n"; $prompt .= "- Không cần tiêu đề 'Conclusion'"; $url = $this->base_url . '/bardcontentmore'; $data = ['question' => $prompt, 'lang' => $lang]; return $this->sendPostRequest($url, $data); } /** * Ghép bài viết hoàn chỉnh */ private function assembleFullArticle($introduction, $chapterContent, $conclusion) { $article = ''; // Thêm introduction $article .= $introduction . "\n\n"; // Thêm từng chapter foreach ($chapterContent as $index => $content) { $article .= $content . "\n\n"; } // Thêm conclusion $article .= $conclusion; return $article; } /** // Tạo prompt tối ưu cho bài viết hoàn chỉnh $prompt = $this->createOptimizedPrompt($keywords, $lang, $chapters, $words); // Sử dụng API bardcontentmore hiện có $url = $this->base_url . '/bardcontentmore'; $data = [ 'question' => $prompt, 'lang' => $lang ]; $result = $this->sendPostRequest($url, $data); // Xử lý kết quả return $this->processSimpleResult($result, $keywords, $chapters, $words); } /** * Tạo prompt tối ưu cho bài viết hoàn chỉnh */ private function createOptimizedPrompt($keywords, $lang, $chapters, $words) { $wordsPerChapter = round($words / $chapters); $prompt = "Viết một bài viết hoàn chỉnh và chuyên nghiệp về chủ đề: {$keywords}\n\n"; $prompt .= "YÊU CẦU CHI TIẾT:\n"; $prompt .= "1. Cấu trúc bài viết:\n"; $prompt .= " - Phần giới thiệu (Introduction) - khoảng 150 từ\n"; $prompt .= " - {$chapters} chapters chính - mỗi chapter khoảng {$wordsPerChapter} từ\n"; $prompt .= " - Phần kết luận (Conclusion) - khoảng 150 từ\n"; $prompt .= " - Tổng cộng khoảng {$words} từ\n\n"; $prompt .= "2. Format và style:\n"; $prompt .= " - Sử dụng HTML tags:

cho tiêu đề chapters,

cho đoạn văn\n"; $prompt .= " - Sử dụng và cho nhấn mạnh\n"; $prompt .= " - Sử dụng