PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.1.6
MxChat – AI Chatbot & Content Generation for WordPress v2.1.6
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
mxchat-basic / includes / class-mxchat-utils.php

class-mxchat-utils.php in MxChat – AI Chatbot & Content Generation for WordPress 2.1.6, at includes/class-mxchat-utils.php

197 lines 7.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit; // Exit if accessed directly
4 }
5
6 class MxChat_Utils {
7
8 /**
9 * Submit or update content (and its embedding) in the database.
10 *
11 * If the source_url already exists, update that row.
12 * Otherwise, insert a new row.
13 *
14 * @param string $content The content to be embedded.
15 * @param string $source_url The source URL of the content.
16 * @param string $api_key The API key used for generating embeddings.
17 * @return bool|string True on success, error message on failure
18 */
19 public static function submit_content_to_db($content, $source_url, $api_key) {
20 global $wpdb;
21 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
22
23 error_log('[MXCHAT-DB] Starting database submission for URL: ' . $source_url);
24 error_log('[MXCHAT-DB] Content length: ' . strlen($content) . ' bytes');
25
26 // Sanitize the source URL
27 $source_url = esc_url_raw($source_url);
28
29 // Use wpdb's prepare to ensure content is properly escaped for database
30 $prepared_content = $wpdb->prepare('%s', $content);
31 // Remove the quotes that wpdb->prepare adds
32 $safe_content = substr($prepared_content, 1, -1);
33
34 // Additional content safety measures
35 $safe_content = wp_check_invalid_utf8($safe_content);
36 $safe_content = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $safe_content);
37
38 // Generate the embedding using the API key
39 $embedding_vector = self::generate_embedding($content, $api_key);
40
41 if (!is_array($embedding_vector)) {
42 error_log('[MXCHAT-DB] Error: Embedding generation failed');
43 return false;
44 }
45
46 error_log('[MXCHAT-DB] Embedding generated successfully');
47
48 $embedding_vector_serialized = maybe_serialize($embedding_vector);
49 error_log('[MXCHAT-DB] Serialized embedding length: ' . strlen($embedding_vector_serialized) . ' bytes');
50
51 // Check if a row already exists for this URL
52 $existing_id = $wpdb->get_var(
53 $wpdb->prepare(
54 "SELECT id FROM {$table_name} WHERE source_url = %s LIMIT 1",
55 $source_url
56 )
57 );
58
59 // Progressive fallback mechanism for problematic content
60 $attempt = 1;
61 $max_attempts = 3;
62 $current_content = $safe_content;
63 $result = false;
64
65 while ($attempt <= $max_attempts && $result === false) {
66 try {
67 if ($existing_id) {
68 error_log('[MXCHAT-DB] Found existing entry (ID: ' . $existing_id . '). Updating... (Attempt ' . $attempt . ')');
69
70 // Update the existing row
71 $result = $wpdb->update(
72 $table_name,
73 array(
74 'url' => $source_url,
75 'article_content' => $current_content,
76 'embedding_vector' => $embedding_vector_serialized,
77 'source_url' => $source_url,
78 'timestamp' => current_time('mysql'),
79 ),
80 array('id' => $existing_id),
81 array('%s','%s','%s','%s','%s'),
82 array('%d')
83 );
84 } else {
85 error_log('[MXCHAT-DB] No existing entry found. Inserting new row... (Attempt ' . $attempt . ')');
86 error_log('[MXCHAT-DB] Content sample: ' . substr($current_content, 0, 1000));
87
88 // Insert a new row
89 $result = $wpdb->insert(
90 $table_name,
91 array(
92 'url' => $source_url,
93 'article_content' => $current_content,
94 'embedding_vector' => $embedding_vector_serialized,
95 'source_url' => $source_url,
96 'timestamp' => current_time('mysql'),
97 ),
98 array('%s','%s','%s','%s','%s')
99 );
100 }
101
102 if ($result === false) {
103 error_log('[MXCHAT-DB] Database operation failed (Attempt ' . $attempt . '): ' . $wpdb->last_error);
104
105 // Progressively apply more aggressive sanitization on failure
106 if ($attempt === 1) {
107 // First fallback: Use a more aggressive character filter and shorten
108 $current_content = preg_replace('/[^\p{L}\p{N}\s.,;:!?()-]/u', '', $current_content);
109 $current_content = substr($current_content, 0, 50000);
110 } else if ($attempt === 2) {
111 // Second fallback: Keep only alphanumeric and basic punctuation, shorten further
112 $current_content = preg_replace('/[^a-zA-Z0-9\s.,;:!?()-]/u', '', $current_content);
113 $current_content = substr($current_content, 0, 30000);
114 }
115
116 $attempt++;
117 }
118 } catch (Exception $e) {
119 error_log('[MXCHAT-DB] Exception during database operation: ' . $e->getMessage());
120 $attempt++;
121 }
122 }
123
124 if ($result === false) {
125 error_log('[MXCHAT-DB] All database operation attempts failed');
126 return false;
127 }
128
129 error_log('[MXCHAT-DB] Database operation completed successfully (Attempt ' . ($attempt - 1) . ')');
130 return true;
131 }
132
133 /**
134 * Generate an embedding for the given text using the specified API key.
135 *
136 * @param string $text The text to be embedded.
137 * @param string $api_key The API key used for generating embeddings.
138 * @return array|null The embedding vector or null on failure.
139 */
140 private static function generate_embedding($text, $api_key) {
141 // Get options and selected model
142 $options = get_option('mxchat_options');
143 $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
144
145 // Determine endpoint and API key based on model
146 if (strpos($selected_model, 'voyage') === 0) {
147 $endpoint = 'https://api.voyageai.com/v1/embeddings';
148 $api_key = $options['voyage_api_key'] ?? '';
149 } else {
150 $endpoint = 'https://api.openai.com/v1/embeddings';
151 // Use the passed API key for OpenAI
152 }
153
154 // Prepare request body
155 $request_body = [
156 'input' => $text,
157 'model' => $selected_model
158 ];
159
160 // Add output_dimension for voyage-3-large
161 if ($selected_model === 'voyage-3-large') {
162 $request_body['output_dimension'] = 2048;
163 }
164
165 $args = [
166 'body' => wp_json_encode($request_body),
167 'headers' => [
168 'Content-Type' => 'application/json',
169 'Authorization' => 'Bearer ' . $api_key,
170 ],
171 'timeout' => 60,
172 'redirection' => 5,
173 'blocking' => true,
174 'httpversion' => '1.0',
175 'sslverify' => true,
176 ];
177
178 $response = wp_remote_post($endpoint, $args);
179
180 if (is_wp_error($response)) {
181 error_log('Error generating embedding: ' . $response->get_error_message());
182 return null;
183 }
184
185 $response_body = json_decode(wp_remote_retrieve_body($response), true);
186
187 if (isset($response_body['data'][0]['embedding']) && is_array($response_body['data'][0]['embedding'])) {
188 return $response_body['data'][0]['embedding'];
189 } else {
190 error_log('Invalid response received from embedding API: ' . wp_json_encode($response_body));
191 return null;
192 }
193 }
194
195
196 }
197