{"repo": "bcardi0427/wp-smart-linker", "files": {"includes/class-deepseek-integration.php": {"90fc6dbf4f2e121d2fbcc2a09234082ee836ea59": {"t": 1785745767.3569927, "n": 9747, "h": "73288c96424dc1a2", "c": " 'DeepSeek Chat',\n 'deepseek-coder' => 'DeepSeek Coder',\n ];\n\n /**\n * Set up API credentials from settings\n */\n protected function setup_credentials() {\n $settings = get_option('wsl_settings');\n $this->api_key = $settings['deepseek_api_key'] ?? '';\n $this->model = $settings['deepseek_model'] ?? 'deepseek-chat';\n }\n\n /**\n * Validate if a model ID is valid\n *\n * @param string $model_id Model ID to check\n * @return bool Whether the model is valid\n */\n public function is_valid_model($model_id) {\n return isset($this->valid_models[$model_id]);\n }\n\n /**\n * Get list of available models\n *\n * @return array Array of model ID => display name pairs\n */\n public function get_available_models() {\n return $this->valid_models;\n }\n\n /**\n * Generate suggestions using the DeepSeek API\n *\n * @param string $prompt The prompt to send to the AI\n * @return array The AI response\n */\n protected function get_ai_suggestions($prompt) {\n // Try to get cached response first\n $cached = $this->get_cached_response($prompt);\n if ($cached !== false) {\n return $cached;\n }\n\n $request_body = [\n 'model' => $this->model,\n 'messages' => [\n [\n 'role' => 'system',\n 'content' => 'You are an expert content editor and SEO specialist who understands how to create natural, engaging internal links that enhance readability while improving SEO. Focus on creating links that feel like a natural part of the conversation, adding value to the reader\\'s experience. Respond with valid JSON only.'\n ],\n [\n 'role' => 'user',\n 'content' => $prompt\n ]\n ],\n 'temperature' => 0.7,\n 'max_tokens' => 1500\n ];\n\n error_log('WSL Debug - DeepSeek Request: ' . print_r($request_body, true));\n\n $response = wp_remote_post($this->api_endpoint, [\n 'headers' => [\n 'Authorization' => 'Bearer ' . $this->api_key,\n 'Content-Type' => 'application/json',\n ],\n 'body' => json_encode($request_body),\n 'timeout' => 30\n ]);\n\n if (is_wp_error($response)) {\n throw new AI_Exception(\n 'API Request Error: ' . $response->get_error_message(),\n AI_Exception::ERROR_API_ERROR\n );\n }\n\n $response_code = wp_remote_retrieve_response_code($response);\n $response_body = wp_remote_retrieve_body($response);\n\n error_log('WSL Debug - DeepSeek Response Code: ' . $response_code);\n error_log('WSL Debug - DeepSeek Response Body: ' . $response_body);\n\n if ($response_code !== 200) {\n $error_data = json_decode($response_body, true);\n $error_message = isset($error_data['error']['message'])\n ? $error_data['error']['message']\n : 'Unknown API error occurred';\n \n throw new AI_Exception(\n 'DeepSeek API Error: ' . $error_message,\n AI_Exception::ERROR_API_ERROR\n );\n }\n\n $body = json_decode($response_body, true);\n if (empty($body['choices'][0]['message']['content'])) {\n throw new AI_Exception(\n 'Invalid API response: No content returned from DeepSeek',\n AI_Exception::ERROR_INVALID_RESPONSE\n );\n }\n\n // Clean up the response content\n $content = $body['choices'][0]['message']['content'];\n \n // Remove markdown code blocks if present\n $content = preg_replace('/```json\\s*/', '', $content);\n $content = preg_replace('/```\\s*/', '', $content);\n \n // Try to parse the JSON\n $suggestions = json_decode($content, true);\n \n if (json_last_error() !== JSON_ERROR_NONE) {\n error_log('WSL Debug - Failed to parse JSON: ' . $content);\n throw new AI_Exception(\n 'Invalid JSON in API response: ' . json_last_error_msg(),\n AI_Exception::ERROR_INVALID_RESPONSE\n );\n }\n\n // Validate response structure\n if (!isset($suggestions['suggestions']) || !is_array($suggestions['suggestions'])) {\n error_log('WSL Debug - Invalid response structure: missing suggestions array');\n throw new AI_Exception(\n 'Invalid response structure: missing suggestions array',\n AI_Exception::ERROR_INVALID_RESPONSE\n );\n }\n\n error_log('WSL Debug - Parsed suggestions: ' . print_r($suggestions, true));\n \n // Cache successful response\n $this->cache_response($prompt, $suggestions);\n \n return $suggestions;\n }\n\n /**\n * Process and validate API response\n *\n * @param array $response Raw API response\n * @param array $sections Original content sections\n * @return array Processed suggestions\n */\n protected function process_ai_response($response, $sections) {\n if (!isset($response['suggestions']) || !is_array($response['suggestions'])) {\n error_log('WSL Debug - No suggestions found in AI response');\n return [];\n }\n\n error_log('WSL Debug - Processing ' . count($response['suggestions']) . ' suggestions');\n $processed = [];\n $used_sections = [];\n\n foreach ($response['suggestions'] as $suggestion) {\n try {\n // Basic validation\n if (!$this->validate_suggestion($suggestion, count($sections))) {\n continue;\n }\n\n $section_index = intval($suggestion['section_index']);\n \n // Prevent multiple links in the same section\n if (isset($used_sections[$section_index])) {\n error_log('WSL Debug - Section already has a link: ' . $section_index);\n continue;\n }\n\n // Find matching section\n foreach ($sections as $section) {\n if ($section['type'] === 'p') {\n $content = strip_tags($section['content']);\n $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8');\n \n if (empty($suggestion['anchor_text']) || stripos($content, $suggestion['anchor_text']) === false) {\n continue;\n }\n\n $processed[] = [\n 'section_index' => $section_index,\n 'target_post_id' => $suggestion['target_post_id'],\n 'anchor_text' => sanitize_text_field($suggestion['anchor_text']),\n 'relevance_score' => (float) $suggestion['relevance_score'],\n 'section_content' => $content\n ];\n \n $used_sections[$section_index] = true;\n error_log('WSL Debug - Added suggestion for section ' . $section_index .\n ' with anchor text: ' . $suggestion['anchor_text']);\n break;\n }\n }\n\n } catch (\\Exception $e) {\n error_log('WSL Debug - Error processing suggestion: ' . $e->getMessage());\n continue;\n }\n }\n\n return $processed;\n }\n\n /**\n * Validate a single suggestion\n *\n * @param array $suggestion Suggestion to validate\n * @param int $section_count Total number of sections\n * @return bool Whether suggestion is valid\n */\n private function validate_suggestion($suggestion, $section_count) {\n // Check required fields\n if (!isset($suggestion['section_index']) ||\n !isset($suggestion['target_post_id']) ||\n !isset($suggestion['anchor_text']) ||\n !isset($suggestion['relevance_score'])) {\n error_log('WSL Debug - Missing required fields in suggestion');\n return false;\n }\n\n // Validate section index\n if (!is_numeric($suggestion['section_index']) ||\n $suggestion['section_index'] < 0 ||\n $suggestion['section_index'] >= $section_count) {\n error_log('WSL Debug - Invalid section index: ' . $suggestion['section_index']);\n return false;\n }\n\n // Validate anchor text exists and isn't empty\n if (empty($suggestion['anchor_text'])) {\n error_log('WSL Debug - Empty anchor text');\n return false;\n }\n\n // Validate relevance score\n if (!is_numeric($suggestion['relevance_score']) ||\n $suggestion['relevance_score'] < 0 ||\n $suggestion['relevance_score'] > 1) {\n error_log('WSL Debug - Invalid relevance score: ' . $suggestion['relevance_score']);\n return false;\n }\n\n // Validate post exists and is published\n $post = get_post($suggestion['target_post_id']);\n if (!$post || $post->post_status !== 'publish') {\n error_log('WSL Debug - Invalid post ID or unpublished post: ' . $suggestion['target_post_id']);\n return false;\n }\n\n return true;\n }\n}"}, "7feb4bc599e766dabb6a30fb2579cd35af8f502e": {"t": 1785756317.9595897, "n": 9747, "h": "73288c96424dc1a2", "c": " 'DeepSeek Chat',\n 'deepseek-coder' => 'DeepSeek Coder',\n ];\n\n /**\n * Set up API credentials from settings\n */\n protected function setup_credentials() {\n $settings = get_option('wsl_settings');\n $this->api_key = $settings['deepseek_api_key'] ?? '';\n $this->model = $settings['deepseek_model'] ?? 'deepseek-chat';\n }\n\n /**\n * Validate if a model ID is valid\n *\n * @param string $model_id Model ID to check\n * @return bool Whether the model is valid\n */\n public function is_valid_model($model_id) {\n return isset($this->valid_models[$model_id]);\n }\n\n /**\n * Get list of available models\n *\n * @return array Array of model ID => display name pairs\n */\n public function get_available_models() {\n return $this->valid_models;\n }\n\n /**\n * Generate suggestions using the DeepSeek API\n *\n * @param string $prompt The prompt to send to the AI\n * @return array The AI response\n */\n protected function get_ai_suggestions($prompt) {\n // Try to get cached response first\n $cached = $this->get_cached_response($prompt);\n if ($cached !== false) {\n return $cached;\n }\n\n $request_body = [\n 'model' => $this->model,\n 'messages' => [\n [\n 'role' => 'system',\n 'content' => 'You are an expert content editor and SEO specialist who understands how to create natural, engaging internal links that enhance readability while improving SEO. Focus on creating links that feel like a natural part of the conversation, adding value to the reader\\'s experience. Respond with valid JSON only.'\n ],\n [\n 'role' => 'user',\n 'content' => $prompt\n ]\n ],\n 'temperature' => 0.7,\n 'max_tokens' => 1500\n ];\n\n error_log('WSL Debug - DeepSeek Request: ' . print_r($request_body, true));\n\n $response = wp_remote_post($this->api_endpoint, [\n 'headers' => [\n 'Authorization' => 'Bearer ' . $this->api_key,\n 'Content-Type' => 'application/json',\n ],\n 'body' => json_encode($request_body),\n 'timeout' => 30\n ]);\n\n if (is_wp_error($response)) {\n throw new AI_Exception(\n 'API Request Error: ' . $response->get_error_message(),\n AI_Exception::ERROR_API_ERROR\n );\n }\n\n $response_code = wp_remote_retrieve_response_code($response);\n $response_body = wp_remote_retrieve_body($response);\n\n error_log('WSL Debug - DeepSeek Response Code: ' . $response_code);\n error_log('WSL Debug - DeepSeek Response Body: ' . $response_body);\n\n if ($response_code !== 200) {\n $error_data = json_decode($response_body, true);\n $error_message = isset($error_data['error']['message'])\n ? $error_data['error']['message']\n : 'Unknown API error occurred';\n \n throw new AI_Exception(\n 'DeepSeek API Error: ' . $error_message,\n AI_Exception::ERROR_API_ERROR\n );\n }\n\n $body = json_decode($response_body, true);\n if (empty($body['choices'][0]['message']['content'])) {\n throw new AI_Exception(\n 'Invalid API response: No content returned from DeepSeek',\n AI_Exception::ERROR_INVALID_RESPONSE\n );\n }\n\n // Clean up the response content\n $content = $body['choices'][0]['message']['content'];\n \n // Remove markdown code blocks if present\n $content = preg_replace('/```json\\s*/', '', $content);\n $content = preg_replace('/```\\s*/', '', $content);\n \n // Try to parse the JSON\n $suggestions = json_decode($content, true);\n \n if (json_last_error() !== JSON_ERROR_NONE) {\n error_log('WSL Debug - Failed to parse JSON: ' . $content);\n throw new AI_Exception(\n 'Invalid JSON in API response: ' . json_last_error_msg(),\n AI_Exception::ERROR_INVALID_RESPONSE\n );\n }\n\n // Validate response structure\n if (!isset($suggestions['suggestions']) || !is_array($suggestions['suggestions'])) {\n error_log('WSL Debug - Invalid response structure: missing suggestions array');\n throw new AI_Exception(\n 'Invalid response structure: missing suggestions array',\n AI_Exception::ERROR_INVALID_RESPONSE\n );\n }\n\n error_log('WSL Debug - Parsed suggestions: ' . print_r($suggestions, true));\n \n // Cache successful response\n $this->cache_response($prompt, $suggestions);\n \n return $suggestions;\n }\n\n /**\n * Process and validate API response\n *\n * @param array $response Raw API response\n * @param array $sections Original content sections\n * @return array Processed suggestions\n */\n protected function process_ai_response($response, $sections) {\n if (!isset($response['suggestions']) || !is_array($response['suggestions'])) {\n error_log('WSL Debug - No suggestions found in AI response');\n return [];\n }\n\n error_log('WSL Debug - Processing ' . count($response['suggestions']) . ' suggestions');\n $processed = [];\n $used_sections = [];\n\n foreach ($response['suggestions'] as $suggestion) {\n try {\n // Basic validation\n if (!$this->validate_suggestion($suggestion, count($sections))) {\n continue;\n }\n\n $section_index = intval($suggestion['section_index']);\n \n // Prevent multiple links in the same section\n if (isset($used_sections[$section_index])) {\n error_log('WSL Debug - Section already has a link: ' . $section_index);\n continue;\n }\n\n // Find matching section\n foreach ($sections as $section) {\n if ($section['type'] === 'p') {\n $content = strip_tags($section['content']);\n $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8');\n \n if (empty($suggestion['anchor_text']) || stripos($content, $suggestion['anchor_text']) === false) {\n continue;\n }\n\n $processed[] = [\n 'section_index' => $section_index,\n 'target_post_id' => $suggestion['target_post_id'],\n 'anchor_text' => sanitize_text_field($suggestion['anchor_text']),\n 'relevance_score' => (float) $suggestion['relevance_score'],\n 'section_content' => $content\n ];\n \n $used_sections[$section_index] = true;\n error_log('WSL Debug - Added suggestion for section ' . $section_index .\n ' with anchor text: ' . $suggestion['anchor_text']);\n break;\n }\n }\n\n } catch (\\Exception $e) {\n error_log('WSL Debug - Error processing suggestion: ' . $e->getMessage());\n continue;\n }\n }\n\n return $processed;\n }\n\n /**\n * Validate a single suggestion\n *\n * @param array $suggestion Suggestion to validate\n * @param int $section_count Total number of sections\n * @return bool Whether suggestion is valid\n */\n private function validate_suggestion($suggestion, $section_count) {\n // Check required fields\n if (!isset($suggestion['section_index']) ||\n !isset($suggestion['target_post_id']) ||\n !isset($suggestion['anchor_text']) ||\n !isset($suggestion['relevance_score'])) {\n error_log('WSL Debug - Missing required fields in suggestion');\n return false;\n }\n\n // Validate section index\n if (!is_numeric($suggestion['section_index']) ||\n $suggestion['section_index'] < 0 ||\n $suggestion['section_index'] >= $section_count) {\n error_log('WSL Debug - Invalid section index: ' . $suggestion['section_index']);\n return false;\n }\n\n // Validate anchor text exists and isn't empty\n if (empty($suggestion['anchor_text'])) {\n error_log('WSL Debug - Empty anchor text');\n return false;\n }\n\n // Validate relevance score\n if (!is_numeric($suggestion['relevance_score']) ||\n $suggestion['relevance_score'] < 0 ||\n $suggestion['relevance_score'] > 1) {\n error_log('WSL Debug - Invalid relevance score: ' . $suggestion['relevance_score']);\n return false;\n }\n\n // Validate post exists and is published\n $post = get_post($suggestion['target_post_id']);\n if (!$post || $post->post_status !== 'publish') {\n error_log('WSL Debug - Invalid post ID or unpublished post: ' . $suggestion['target_post_id']);\n return false;\n }\n\n return true;\n }\n}"}}}}