PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.5.8
MxChat – AI Chatbot & Content Generation for WordPress v1.5.8
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
← All changes | includes/class-mxchat-integrator.php +480 -1009 2.0.31.5.8 View file →
@@ -4,9 +4,8 @@
4 4 }
5 5
6 6 class MxChat_Integrator {
7 7 private $options;
8 - private $prompts_options;
9 8 private $chat_count;
10 9 private $fallbackResponse;
11 10 private $productCardHtml;
12 11 private $word_handler;
@@ -12,13 +11,18 @@
12 11 private $word_handler;
13 12
14 13 public function __construct() {
15 14 $this->options = get_option('mxchat_options');
16 - $this->prompts_options = get_option('mxchat_prompts_options', array());
17 -
18 15 $this->chat_count = get_option('mxchat_chat_count', 0);
19 16 $this->word_handler = new MXChat_Word_Handler($this->options);
20 17
18 + // Add WooCommerce hooks
19 + add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);
20 +
21 + // Ensure embeddings are removed when a product is moved to trash or permanently deleted
22 + add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
23 + add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));
24 +
21 25 add_action('wp_enqueue_scripts', array($this, 'mxchat_enqueue_scripts_styles'));
22 26 add_action('wp_ajax_mxchat_handle_chat_request', array($this, 'mxchat_handle_chat_request'));
23 27 add_action('wp_ajax_nopriv_mxchat_handle_chat_request', array($this, 'mxchat_handle_chat_request'));
24 28
@@ -43,8 +47,9 @@
43 47
44 48 add_action('wp_ajax_mxchat_fetch_new_messages', array($this, 'mxchat_fetch_new_messages'));
45 49 add_action('wp_ajax_nopriv_mxchat_fetch_new_messages', array($this, 'mxchat_fetch_new_messages'));
46 50
51 +
47 52 add_action('mxchat_reset_rate_limits', array($this, 'mxchat_reset_rate_limits'));
48 53
49 54 add_action('wp_ajax_mxchat_upload_pdf', [$this, 'handle_pdf_upload']);
50 55 add_action('wp_ajax_nopriv_mxchat_upload_pdf', [$this, 'handle_pdf_upload']);
@@ -64,9 +69,70 @@
64 69 add_action('wp_ajax_nopriv_mxchat_check_email_provided', [$this, 'mxchat_check_email_provided']);
65 70 add_action('wp_ajax_mxchat_check_email_provided', [$this, 'mxchat_check_email_provided']);
66 71 }
67 72
73 +public function mxchat_handle_product_change($post_id, $post, $update) {
74 + // Ensure this is a product post type
75 + if ($post->post_type !== 'product') {
76 + return;
77 + }
68 78
79 + // Only generate embeddings if the product is published
80 + if ($post->post_status === 'publish') {
81 + // Delay the embedding slightly to ensure all product data is available
82 + add_action('shutdown', function() use ($post_id) {
83 + $product = wc_get_product($post_id);
84 + if ($product && $product->get_price() !== '') {
85 + $this->mxchat_store_product_embedding($product);
86 + } else {
87 + // Optionally, log or handle the case where product data is incomplete
88 + // error_log("Product {$post_id} does not have complete data. Embedding not generated.");
89 + }
90 + });
91 + }
92 +}
93 +
94 +public function mxchat_handle_product_delete($post_id) {
95 + if (get_post_type($post_id) !== 'product') {
96 + return;
97 + }
98 +
99 + global $wpdb;
100 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
101 +
102 + // Delete the embedding associated with this product
103 + $wpdb->delete($table_name, array('source_url' => get_permalink($post_id)), array('%s'));
104 +}
105 +
106 +private function mxchat_store_product_embedding($product) {
107 + if (isset($this->options['enable_woocommerce_integration']) && $this->options['enable_woocommerce_integration'] === '1') {
108 +
109 + $source_url = get_permalink($product->get_id());
110 + $regular_price = $product->get_regular_price();
111 + $sale_price = $product->get_sale_price();
112 + $price = $sale_price ?: $regular_price;
113 +
114 + $description = $product->get_description() . "\n\n" .
115 + "Short Description: " . $product->get_short_description() . "\n" .
116 + "Price: " . $regular_price . "\n" .
117 + "Sale Price: " . ($sale_price ?: 'N/A') . "\n" .
118 + "SKU: " . $product->get_sku();
119 +
120 + global $wpdb;
121 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
122 +
123 + // Delete any existing embedding for this product
124 + $wpdb->delete($table_name, array('source_url' => $source_url), array('%s'));
125 +
126 + // Submit the new content and embedding to the database
127 + MxChat_Utils::submit_content_to_db($description, $source_url, $this->options['api_key']);
128 + }
129 +}
130 +
131 +
132 +
133 +
134 +
69 135 private function mxchat_increment_chat_count() {
70 136 $chat_count = get_option('mxchat_chat_count', 0);
71 137 $chat_count++;
72 138 update_option('mxchat_chat_count', $chat_count);
@@ -73,9 +139,9 @@
73 139 }
74 140
75 141 function mxchat_fetch_conversation_history() {
76 142 if (empty($_POST['session_id'])) {
77 - wp_send_json_error(['message' => esc_html__('Session ID missing.', 'mxchat')]);
143 + wp_send_json_error(['message' => 'Session ID missing.']);
78 144 wp_die();
79 145 }
80 146
81 147 $session_id = sanitize_text_field($_POST['session_id']);
@@ -114,82 +180,41 @@
114 180
115 181
116 182 private function mxchat_fetch_conversation_history_for_ai($session_id) {
117 183 $history = get_option("mxchat_history_{$session_id}", []);
184 +
118 185 $formatted_history = [];
119 -
120 - // Adjusted for code-heavy conversations
121 - $max_tokens = 120000; // Context window size
122 - $reserved_tokens = 5000; // Space for system prompts + current query
186 + $max_tokens = 120000; // Adjust based on your model's context window
187 + $reserved_tokens = 4000; // Reserve tokens for current query and system/context prompt
123 188 $current_token_count = 0;
124 189
125 - // Allowed HTML tags for content sanitization
126 - $allowed_tags = [
127 - 'pre' => ['class' => true],
128 - 'code' => ['class' => true],
129 - 'span' => ['class' => true],
130 - 'div' => ['class' => true],
131 - 'strong' => [],
132 - 'em' => []
133 - ];
134 -
135 - foreach (array_reverse($history) as $entry) {
136 - // Preserve code blocks while sanitizing other HTML
137 - $clean_content = wp_kses($entry['content'], $allowed_tags);
138 -
139 - // Detect code blocks in content
140 - $has_code = false;
141 -// Replace the HTML check with:
142 -// Allow messages that contain code blocks or are plain text
143 -if (strpos($clean_content, '<pre') === false &&
144 - strpos($clean_content, '<code') === false &&
145 - $clean_content !== strip_tags($entry['content'])) {
146 - continue;
147 -}
148 -
149 - // Skip entries that lost significant content during sanitization
150 - if (!$has_code && $clean_content !== strip_tags($entry['content'])) {
190 + foreach ($history as $entry) {
191 + // Skip messages containing HTML
192 + if ($entry['content'] !== strip_tags($entry['content'])) {
151 193 continue;
152 194 }
153 195
154 - // More accurate token estimation (1 token ≈ 4 characters)
155 - $token_estimate = ceil(mb_strlen($clean_content, 'UTF-8') / 4);
196 + // Calculate tokens for this entry
197 + $tokens_in_entry = str_word_count($entry['content']) * 1.5; // Rough estimate: ~1.5 tokens per word
198 + $current_token_count += $tokens_in_entry;
156 199
157 - // Check token budget with the new estimate
158 - if (($current_token_count + $token_estimate + $reserved_tokens) > $max_tokens) {
159 - // Try to fit partial content if it's the first entry
160 - if (empty($formatted_history)) {
161 - $clean_content = mb_substr($clean_content, 0, ($max_tokens - $reserved_tokens) * 4);
162 - $token_estimate = ceil(mb_strlen($clean_content, 'UTF-8') / 4);
163 - } else {
164 - break;
165 - }
200 + // If adding this entry exceeds the limit, stop
201 + if ($current_token_count + $reserved_tokens > $max_tokens) {
202 + break;
166 203 }
167 204
168 - // Add to formatted history
169 205 $formatted_history[] = [
170 206 'role' => $entry['role'],
171 - 'content' => $clean_content
207 + 'content' => $entry['content']
172 208 ];
173 -
174 - $current_token_count += $token_estimate;
175 209 }
176 210
177 - // Reverse back to maintain chronological order
178 - $formatted_history = array_reverse($formatted_history);
179 -
180 - // Add system message about code context
181 - array_unshift($formatted_history, [
182 - 'role' => 'system',
183 - 'content' => 'Preserved code blocks are marked with [CODE BLOCK PRESERVED]. '
184 - . 'Maintain formatting and syntax highlighting when referencing code.'
185 - ]);
186 -
187 211 return $formatted_history;
188 212 }
189 213
214 +
190 215 public function register_routes() {
191 - //error_log(esc_html__('Registering MxChat REST routes', 'mxchat'));
216 + //error_log('Registering MxChat REST routes');
192 217
193 218 register_rest_route('mxchat/v1', '/stream', [
194 219 'methods' => 'GET',
195 220 'callback' => [$this, 'mxchat_stream_events'],
@@ -207,9 +232,9 @@
207 232 'callback' => [$this, 'handle_slack_interaction'],
208 233 'permission_callback' => [$this, 'verify_slack_request'],
209 234 ]);
210 235
211 - //error_log(esc_html__('MxChat REST routes registered', 'mxchat'));
236 + //error_log('MxChat REST routes registered');
212 237 }
213 238
214 239 /**
215 240 * Verify valid chat session
@@ -216,9 +241,9 @@
216 241 */
217 242 public function verify_chat_session($request) {
218 243 $session_id = $request->get_param('session_id');
219 244 if (empty($session_id)) {
220 - //error_log(esc_html__('Empty session ID in chat request', 'mxchat'));
245 + //error_log('Empty session ID in chat request');
221 246 return false;
222 247 }
223 248
224 249 $chat_mode = get_option("mxchat_mode_{$session_id}", 'ai');
@@ -235,9 +260,9 @@
235 260 // Get the Slack signing secret from your plugin options
236 261 $valid_key = $this->options['live_agent_secret_key'] ?? '';
237 262
238 263 if (empty($valid_key)) {
239 - //error_log(esc_html__('Slack signing secret not configured', 'mxchat'));
264 + //error_log('Slack signing secret not configured');
240 265 return false;
241 266 }
242 267
243 268 $timestamp = $request->get_header('X-Slack-Request-Timestamp');
@@ -244,9 +269,9 @@
244 269 $slack_signature = $request->get_header('X-Slack-Signature');
245 270
246 271 // Verify timestamp to prevent replay attacks
247 272 if (abs(time() - intval($timestamp)) > 300) {
248 - //error_log(esc_html__('Slack request timestamp too old', 'mxchat'));
273 + //error_log('Slack request timestamp too old');
249 274 return false;
250 275 }
251 276
252 277 // Get raw request body
@@ -269,9 +294,9 @@
269 294 $session_id = sanitize_text_field($request->get_param('session_id'));
270 295 $last_seen_id = sanitize_text_field($request->get_param('last_seen_id')) ?: '';
271 296
272 297 if (empty($session_id)) {
273 - echo esc_html__("event: error\ndata: ", 'mxchat') . esc_html__('Missing session_id', 'mxchat') . "\n\n";
298 + echo "event: error\ndata: Missing session_id\n\n";
274 299 flush();
275 300 exit;
276 301 }
277 302
@@ -283,12 +308,12 @@
283 308 });
284 309
285 310 // Send new messages if available
286 311 if (!empty($new_messages)) {
287 - echo esc_html__("event: newMessages\ndata: ", 'mxchat') . json_encode(array_values($new_messages)) . "\n\n";
312 + echo "event: newMessages\ndata: " . json_encode(array_values($new_messages)) . "\n\n";
288 313 } else {
289 314 // Keep the connection alive
290 - echo esc_html__("event: keepAlive\ndata: ", 'mxchat') . "{}\n\n";
315 + echo "event: keepAlive\ndata: {}\n\n";
291 316 }
292 317 flush();
293 318 exit;
294 319 }
@@ -382,10 +407,10 @@
382 407 //error_log('[DEBUG] ---------- mxchat_handle_save_email_and_response START ----------');
383 408
384 409 // Validate nonce
385 410 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'mxchat_chat_nonce')) {
386 - error_log(esc_html__('[ERROR] Invalid nonce in mxchat_handle_save_email_and_response', 'mxchat'));
387 - wp_send_json_error(['message' => esc_html__('Invalid nonce.', 'mxchat')]);
411 + error_log('[ERROR] Invalid nonce in mxchat_handle_save_email_and_response');
412 + wp_send_json_error(['message' => 'Invalid nonce.']);
388 413 wp_die();
389 414 }
390 415
391 416 $session_id = isset($_POST['session_id']) ? sanitize_text_field($_POST['session_id']) : '';
@@ -394,9 +419,9 @@
394 419 //error_log("[DEBUG] handle_save_email_and_response -> session_id: {$session_id}, email: {$email}");
395 420
396 421 if (empty($session_id) || empty($email)) {
397 422 //error_log("[ERROR] Missing session_id or email: session_id={$session_id}, email={$email}");
398 - wp_send_json_error(['message' => esc_html__('Session ID or email is missing.', 'mxchat')]);
423 + wp_send_json_error(['message' => 'Session ID or email is missing.']);
399 424 wp_die();
400 425 }
401 426
402 427 // 1) Always store in wp_options
@@ -427,9 +452,9 @@
427 452 //error_log("[INFO] handle_save_email_and_response -> No DB entry for {$session_id}, so email is only in wp_options.");
428 453 }
429 454
430 455 // Provide success response
431 - $bot_message = __('Thanks for providing your email! You can continue chatting now.', 'mxchat');
456 + $bot_message = 'Thanks for providing your email! You can continue chatting now.';
432 457 //error_log("[DEBUG] handle_save_email_and_response -> success, returning bot_message: {$bot_message}");
433 458 wp_send_json_success(['message' => $bot_message]);
434 459 wp_die();
435 460 }
@@ -438,15 +463,15 @@
438 463 //error_log('[DEBUG] ---------- mxchat_check_email_provided START ----------');
439 464
440 465 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'mxchat_chat_nonce')) {
441 466 //error_log('[ERROR] Invalid nonce in mxchat_check_email_provided');
442 - wp_send_json_error(['message' => esc_html__('Invalid nonce', 'mxchat')]);
467 + wp_send_json_error(['message' => 'Invalid nonce']);
443 468 }
444 469
445 470 $session_id = isset($_POST['session_id']) ? sanitize_text_field($_POST['session_id']) : '';
446 471 if (empty($session_id)) {
447 472 //error_log('[ERROR] No session ID provided in mxchat_check_email_provided');
448 - wp_send_json_error(['message' => esc_html__('No session ID provided', 'mxchat')]);
473 + wp_send_json_error(['message' => 'No session ID provided']);
449 474 }
450 475
451 476 // Check if the user is logged in
452 477 if (is_user_logged_in()) {
@@ -464,182 +489,53 @@
464 489 //error_log("[DEBUG] mxchat_check_email_provided -> Email found, returning success");
465 490 wp_send_json_success(['email' => $stored_email]);
466 491 } else {
467 492 //error_log("[DEBUG] mxchat_check_email_provided -> No email found, returning error");
468 - wp_send_json_error(['message' => esc_html__('No email found', 'mxchat')]);
493 + wp_send_json_error(['message' => 'No email found']);
469 494 }
470 495 }
471 496
472 497
473 -// First, add this helper function to get the highest rate limit for a user's roles
474 -private function get_user_role_rate_limit($user_id) {
475 - //error_log(esc_html__("Checking rate limit for user ID: ", 'mxchat') . $user_id);
476 -
477 - if (!$user_id) {
478 - //error_log(esc_html__("No user ID provided, using logged-out limit: ", 'mxchat') . ($this->options['rate_limit_logged_out'] ?? '10'));
479 - return $this->options['rate_limit_logged_out'] ?? '10';
480 - }
481 -
482 - $user = get_userdata($user_id);
483 - if (!$user || !$user->roles) {
484 - //error_log(esc_html__("No user data or roles found, using logged-out limit: ", 'mxchat') . ($this->options['rate_limit_logged_out'] ?? '10'));
485 - return $this->options['rate_limit_logged_out'] ?? '10';
486 - }
487 -
488 - //error_log(esc_html__("User roles: ", 'mxchat') . print_r($user->roles, true));
489 - //error_log(esc_html__("Available role rate limits: ", 'mxchat') . print_r($this->options['role_rate_limits'] ?? [], true));
490 -
491 - $max_limit = 0;
492 - foreach ($user->roles as $role) {
493 - //error_log(esc_html__("Checking limit for role: ", 'mxchat') . $role);
494 - if (isset($this->options['role_rate_limits'][$role])) {
495 - $role_limit = $this->options['role_rate_limits'][$role];
496 - //error_log(esc_html__("Found limit for role ", 'mxchat') . $role . esc_html__(": ", 'mxchat') . $role_limit);
497 -
498 - if ($role_limit === 'unlimited') {
499 - //error_log(esc_html__("Returning unlimited for role: ", 'mxchat') . $role);
500 - return 'unlimited';
501 - }
502 -
503 - $max_limit = max($max_limit, (int)$role_limit);
504 - //error_log(esc_html__("Current max limit: ", 'mxchat') . $max_limit);
505 - } else {
506 - //error_log(esc_html__("No limit found for role: ", 'mxchat') . $role);
507 - }
508 - }
509 -
510 - $final_limit = $max_limit > 0 ? (string)$max_limit : '100';
511 - //error_log(esc_html__("Final rate limit: ", 'mxchat') . $final_limit);
512 - return $final_limit;
513 -}
514 -
515 -
516 -// Add this to your plugin's main PHP file
517 -public function mxchat_check_new_messages() {
518 - if (!isset($_POST['session_id']) || !isset($_POST['last_seen_id'])) {
519 - wp_send_json_error(['message' => 'Missing required parameters']);
520 - wp_die();
521 - }
522 -
523 - $session_id = sanitize_text_field($_POST['session_id']);
524 - $last_seen_id = sanitize_text_field($_POST['last_seen_id']);
525 -
526 - // Get chat history
527 - $history = get_option("mxchat_history_{$session_id}", []);
528 -
529 - if (empty($history)) {
530 - wp_send_json_success([
531 - 'hasNewMessages' => false,
532 - 'new_messages' => []
533 - ]);
534 - wp_die();
535 - }
536 -
537 - // Filter new messages
538 - $new_messages = array_filter($history, function($message) use ($last_seen_id) {
539 - return isset($message['id']) && $message['id'] > $last_seen_id;
540 - });
541 -
542 - // Sort by ID to ensure proper order
543 - usort($new_messages, function($a, $b) {
544 - return $a['id'] <=> $b['id'];
545 - });
546 -
547 - wp_send_json_success([
548 - 'hasNewMessages' => !empty($new_messages),
549 - 'new_messages' => array_values($new_messages),
550 - 'latestMessageId' => end($new_messages)['id'] ?? $last_seen_id
551 - ]);
552 - wp_die();
553 -}
554 -
555 498 public function mxchat_handle_chat_request() {
556 499 global $wpdb;
557 500
558 -
559 - // Check if MX Chat Moderation is active
560 - if (class_exists('MX_Chat_Moderation')) {
561 - // Get user email and IP
562 - $user_email = '';
563 - $user_ip = $_SERVER['REMOTE_ADDR'];
564 -
565 - // If user is logged in, get their email
566 - if (is_user_logged_in()) {
567 - $current_user = wp_get_current_user();
568 - $user_email = $current_user->user_email;
569 - }
570 -
571 - // Create ban handler instance
572 - $ban_handler = new MX_Chat_Ban_Handler();
573 -
574 - // Check if user is banned by IP
575 - if ($ban_handler->check_ban($user_ip, 'ip')) {
576 - wp_send_json([
577 - 'success' => false,
578 - 'message' => esc_html__('Access denied. Your IP address has been banned.', 'mxchat'),
579 - 'status' => 'banned'
580 - ]);
581 - wp_die();
582 - }
583 -
584 - // If user is logged in, also check email
585 - if (!empty($user_email) && $ban_handler->check_ban($user_email, 'email')) {
586 - wp_send_json([
587 - 'success' => false,
588 - 'message' => esc_html__('Access denied. Your email address has been banned.', 'mxchat'),
589 - 'status' => 'banned'
590 - ]);
591 - wp_die();
592 - }
593 - }
594 -
595 -
596 501 // Reset fallback response at the start of each request
597 502 $this->fallbackResponse = ['text' => '', 'html' => '', 'images' => []];
598 503 $this->productCardHtml = '';
599 504
600 - // Get the actual WordPress user ID if logged in
601 - $is_logged_in = is_user_logged_in();
602 - if ($is_logged_in) {
603 - $user_id = get_current_user_id(); // This will get the actual WordPress user ID
604 - } else {
605 - // For logged-out users, use your existing identifier method
606 - $user_id = $this->mxchat_get_user_identifier();
607 - }
608 -
609 505 // Get and sanitize the user identifier
506 + $user_id = $this->mxchat_get_user_identifier();
610 507 $user_id = sanitize_key($user_id);
611 508
612 509 // Determine if user is logged in
613 510 $is_logged_in = is_user_logged_in();
614 - //error_log("User logged in status: " . ($is_logged_in ? 'true' : 'false'));
615 511
616 512 // Get rate limit based on user status
617 - // Get rate limit based on user status
618 513 $rate_limit = $is_logged_in
619 - ? $this->get_user_role_rate_limit($user_id)
620 - : ($this->options['rate_limit_logged_out'] ?? '10');
514 + ? $this->options['rate_limit_logged_in'] ?? '100'
515 + : $this->options['rate_limit_logged_out'] ?? '10';
621 516
622 - //error_log("Selected rate limit: " . $rate_limit);
623 -
624 - // Rest of your code remains the same
625 517 // If rate limit is 'unlimited', skip rate limiting checks
626 518 if ($rate_limit !== 'unlimited') {
627 519 // Convert rate limit to integer
628 520 $rate_limit = intval($rate_limit);
521 +
629 522 // Setup rate limiting
630 523 $rate_limit_transient_key = 'mxchat_chat_limit_' . $user_id;
631 524 $chat_count = get_transient($rate_limit_transient_key);
525 +
632 526 if ($chat_count === false) {
633 527 // Initialize new counter if none exists
634 528 $chat_count = 0;
635 529 }
530 +
636 531 // Check if user has exceeded their rate limit
637 532 if ($chat_count >= $rate_limit) {
638 533 // Get custom rate limit message or use default
639 534 $rate_limit_message = isset($this->options['rate_limit_message'])
640 535 ? $this->options['rate_limit_message']
641 - : esc_html__('Rate limit exceeded. Please try again later.', 'mxchat');
536 + : 'Rate limit exceeded. Please try again later.';
537 +
642 538 // Replace placeholder if it exists in the message
643 539 $rate_limit_message = str_replace(
644 540 array('{limit}', '{count}', '{remaining}'),
645 541 array($rate_limit, $chat_count, max(0, $rate_limit - $chat_count)),
@@ -644,8 +540,9 @@
644 540 array('{limit}', '{count}', '{remaining}'),
645 541 array($rate_limit, $chat_count, max(0, $rate_limit - $chat_count)),
646 542 $rate_limit_message
647 543 );
544 +
648 545 wp_send_json([
649 546 'success' => false,
650 547 'message' => $rate_limit_message,
651 548 'status' => 'rate_limit_exceeded',
@@ -653,10 +550,12 @@
653 550 'count' => $chat_count
654 551 ]);
655 552 wp_die();
656 553 }
554 +
657 555 // Increment the counter
658 556 $chat_count++;
557 +
659 558 // Store the updated count with 24-hour expiration
660 559 set_transient($rate_limit_transient_key, $chat_count, DAY_IN_SECONDS);
661 560 }
662 561
@@ -665,9 +564,9 @@
665 564 //error_log("Session ID: $session_id");
666 565
667 566 if (empty($session_id)) {
668 567 //error_log("Error: Session ID is missing.");
669 - wp_send_json_error(esc_html__('Session ID is missing.', 'mxchat'));
568 + wp_send_json_error('Session ID is missing.');
670 569 wp_die();
671 570 }
672 571
673 572 // Validate and sanitize the incoming message
@@ -672,39 +571,16 @@
672 571
673 572 // Validate and sanitize the incoming message
674 573 if (empty($_POST['message'])) {
675 574 //error_log("Error: No message received.");
676 - wp_send_json_error(esc_html__('No message received.', 'mxchat'));
575 + wp_send_json_error('No message received.');
677 576 wp_die();
678 577 }
679 578
680 579
681 -// Modify the message sanitization to preserve PHP tags in code blocks
682 -$allowed_tags = [
683 - 'pre' => [],
684 - 'code' => ['class' => true],
685 - 'span' => ['class' => true],
686 - 'div' => ['class' => true],
687 -];
580 + $message = wp_strip_all_tags($_POST['message'], false);
581 + $message = trim($message);
688 582
689 -// First preserve code blocks
690 -$message = preg_replace_callback('/<pre><code.*?>.*?<\/code><\/pre>/s', function($matches) {
691 - return htmlspecialchars_decode($matches[0]);
692 -}, $_POST['message']);
693 -
694 -// Then apply sanitization
695 -$message = wp_kses($message, $allowed_tags);
696 -
697 -// Decode code blocks
698 -$message = preg_replace_callback('/(&lt;pre&gt;&lt;code.*?&gt;.*?&lt;\/code&gt;&lt;\/pre&gt;)/s', function($matches) {
699 - return htmlspecialchars_decode($matches[1]);
700 -}, $message);
701 -
702 -$message = trim($message);
703 -
704 -// Preserve code blocks from markdown conversion
705 -$message = preg_replace('/```(\w+)?\s*([\s\S]+?)```/s', '<pre><code class="$1">$2</code></pre>', $message);
706 -
707 583 // Save the user's message
708 584 $this->mxchat_save_chat_message($session_id, 'user', $message);
709 585
710 586 // Check if the message is an email address
@@ -713,9 +589,9 @@
713 589 $this->add_email_to_loops($message);
714 590
715 591 // Send success response
716 592 $response_message = $this->options['email_capture_response'] ??
717 - esc_html__('Thank you! Your coupon is on the way!', 'mxchat');
593 + 'Thank you! Your coupon is on the way!';
718 594
719 595 wp_send_json([
720 596 'success' => true,
721 597 'status' => 'email_captured',
@@ -753,9 +629,9 @@
753 629 'chat_mode' => 'ai'
754 630 ];
755 631
756 632 // Save the mode switch message
757 - $this->mxchat_save_chat_message($session_id, 'system', esc_html__('Switched to AI chat mode', 'mxchat'));
633 + $this->mxchat_save_chat_message($session_id, 'system', 'Switched to AI chat mode');
758 634 $this->mxchat_save_chat_message($session_id, 'bot', $this->fallbackResponse['text']);
759 635
760 636 // Send response and exit
761 637 wp_send_json($response_data);
@@ -767,13 +643,13 @@
767 643 //error_log("Message sent to agent.");
768 644
769 645 wp_send_json_success([
770 646 'status' => 'waiting_for_agent',
771 - 'message' => esc_html__('Message sent to live agent.', 'mxchat')
647 + 'message' => 'Message sent to live agent.'
772 648 ]);
773 649 } catch (\Exception $e) {
774 650 //error_log("Error sending message to agent: " . $e->getMessage());
775 - wp_send_json_error(esc_html__('Failed to send message to agent', 'mxchat'));
651 + wp_send_json_error('Failed to send message to agent');
776 652 }
777 653 wp_die();
778 654 }
779 655 }
@@ -809,9 +685,9 @@
809 685
810 686 if ($embeddings === 'too_many_pages') {
811 687 $error_text = sprintf(
812 688 $this->options['pdf_intent_error_text'] ??
813 - esc_html__("The provided PDF exceeds the maximum allowed limit of %d pages. Please provide a smaller document.", 'mxchat'),
689 + "The provided PDF exceeds the maximum allowed limit of %d pages. Please provide a smaller document.",
814 690 $max_pages
815 691 );
816 692 $this->fallbackResponse['text'] = $error_text;
817 693 } elseif ($embeddings) {
@@ -831,9 +707,9 @@
831 707 set_transient('mxchat_pdf_embeddings_' . $session_id, $embeddings, HOUR_IN_SECONDS);
832 708 set_transient('mxchat_include_pdf_in_context_' . $session_id, true, HOUR_IN_SECONDS);
833 709
834 710 $success_text = $this->options['pdf_intent_success_text'] ??
835 - esc_html__("I've processed the new PDF '{$pdf_filename}'. What questions do you have about it?", 'mxchat');
711 + "I've processed the new PDF '{$pdf_filename}'. What questions do you have about it?";
836 712
837 713 // Return success with filename for UI update
838 714 wp_send_json([
839 715 'success' => true,
@@ -844,9 +720,9 @@
844 720 ]);
845 721 wp_die();
846 722 } else {
847 723 $error_text = $this->options['pdf_intent_error_text'] ??
848 - esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat');
724 + "Sorry, I couldn't process the PDF. Please ensure it's a valid file.";
849 725 $this->fallbackResponse['text'] = $error_text;
850 726 }
851 727
852 728 wp_send_json([
@@ -885,9 +761,9 @@
885 761 // Generate embedding for the user's query
886 762 $user_message_embedding = $this->mxchat_generate_embedding($message, $this->options['api_key']);
887 763 if (!is_array($user_message_embedding)) {
888 764 //error_log("Failed to generate message embedding for session $session_id");
889 - wp_send_json_error(esc_html__('Error processing your message.', 'mxchat'));
765 + wp_send_json_error('Error processing your message.');
890 766 wp_die();
891 767 }
892 768
893 769 // Build context with both knowledge base and PDF content if available
@@ -934,9 +810,8 @@
934 810 $context_content,
935 811 $this->options['api_key'],
936 812 $this->options['xai_api_key'],
937 813 $this->options['claude_api_key'],
938 - $this->options['deepseek_api_key'],
939 814 $conversation_history
940 815 );
941 816
942 817 $this->mxchat_save_chat_message($session_id, 'bot', $response);
@@ -980,106 +855,81 @@
980 855
981 856 // New function to check intents and invoke the callback function
982 857 private function mxchat_check_intent_and_invoke_callback($message, $user_id, $session_id) {
983 858 global $wpdb;
859 +
860 + // Check chat mode
984 861 $chat_mode = get_option("mxchat_mode_{$session_id}", 'ai');
985 -
986 - //error_log("[MxChat] Checking intents for message: " . $message);
987 -
862 + //error_log("Checking intents for mode: " . $chat_mode);
863 +
988 864 // Generate the user embedding
989 865 $user_embedding = $this->mxchat_generate_embedding($message, $this->options['api_key']);
990 866 if (!is_array($user_embedding)) {
991 - //error_log("[MxChat] Failed to generate user embedding");
867 + //error_log("Failed to generate user embedding");
992 868 return false;
993 869 }
994 -
870 +
995 871 // Fetch intents from the database
996 872 $table_name = $wpdb->prefix . 'mxchat_intents';
873 +
997 874 if ($chat_mode === 'agent') {
875 + // Only fetch the switch intent when in agent mode
998 876 $query = $wpdb->prepare(
999 877 "SELECT * FROM $table_name WHERE callback_function = %s",
1000 878 'mxchat_handle_switch_to_chatbot_intent'
1001 879 );
880 + //error_log("Searching for switch intent with query: " . $query);
1002 881 $intents = $wpdb->get_results($query);
882 + //error_log("Found " . count($intents) . " switch intents");
1003 883 } else {
1004 884 $intents = $wpdb->get_results("SELECT * FROM $table_name");
1005 885 }
1006 -
1007 - //error_log("[MxChat] Found " . count($intents) . " intents to check");
1008 -
886 +
1009 887 if (empty($intents)) {
1010 - //error_log("[MxChat] No intents found in database");
888 + //error_log("No intents found in database");
1011 889 return false;
1012 890 }
1013 -
891 +
1014 892 $highest_similarity = -INF;
1015 893 $matched_intent = null;
1016 -
894 +
1017 895 foreach ($intents as $intent) {
1018 - //error_log("[MxChat] Checking intent: " . $intent->intent_label . " (callback: " . $intent->callback_function . ")");
1019 -
896 + //error_log("Checking intent: " . $intent->intent_label);
1020 897 $intent_embedding_serialized = $intent->embedding_vector;
1021 898 $intent_embedding = $intent_embedding_serialized
1022 899 ? unserialize($intent_embedding_serialized, ['allowed_classes' => false])
1023 900 : null;
1024 -
901 +
1025 902 if (!is_array($intent_embedding)) {
1026 - //error_log("[MxChat] Invalid embedding for intent: " . $intent->intent_label);
903 + //error_log("Invalid embedding for intent: " . $intent->intent_label);
1027 904 continue;
1028 905 }
1029 -
906 +
1030 907 $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $intent_embedding);
1031 908 $intent_threshold = isset($intent->similarity_threshold) ? $intent->similarity_threshold : 0.85;
1032 -
1033 - //error_log("[MxChat] Similarity for {$intent->intent_label}: {$similarity} (threshold: {$intent_threshold})");
1034 -
909 + //error_log("Similarity for " . $intent->intent_label . ": " . $similarity . " (threshold: " . $intent_threshold . ")");
910 +
1035 911 if ($similarity >= $intent_threshold && $similarity > $highest_similarity) {
1036 912 $highest_similarity = $similarity;
1037 913 $matched_intent = $intent;
1038 - //error_log("[MxChat] New best match: {$intent->intent_label} with similarity {$similarity}");
914 + //error_log("New best match: " . $intent->intent_label . " with similarity " . $similarity);
1039 915 }
1040 916 }
1041 -
1042 - if ($matched_intent) {
1043 - //error_log("[MxChat] Invoking callback: " . $matched_intent->callback_function);
1044 -
1045 - // If the callback is a method on this instance (core callback), call it directly
1046 - if (method_exists($this, $matched_intent->callback_function)) {
1047 - $callback_result = call_user_func(
1048 - [$this, $matched_intent->callback_function],
1049 - $message,
1050 - $user_id,
1051 - $session_id,
1052 - $matched_intent
1053 - );
1054 - } else {
1055 - // Otherwise, use apply_filters for add-on callbacks
1056 - $callback_result = apply_filters(
1057 - $matched_intent->callback_function,
1058 - false, // default return value
1059 - $message,
1060 - $user_id,
1061 - $session_id,
1062 - $matched_intent
1063 - );
1064 - }
1065 -
1066 - //error_log("[MxChat] Callback result: " . print_r($callback_result, true));
1067 - if ($callback_result !== false) {
1068 - $this->fallbackResponse = $callback_result;
1069 - return true;
1070 - }
917 +
918 + if ($matched_intent && method_exists($this, $matched_intent->callback_function)) {
919 + //error_log("Calling callback function: " . $matched_intent->callback_function);
920 + call_user_func([$this, $matched_intent->callback_function], $message, $user_id, $session_id);
921 + return true;
1071 922 }
1072 -
1073 - //error_log("[MxChat] No matching intent found");
923 +
924 + //error_log("No matching intent found");
1074 925 return false;
1075 926 }
1076 927
1077 -
1078 928 //verified good
1079 929 public function mxchat_handle_order_history($message, $user_id, $session_id) {
1080 930 if (!class_exists('WooCommerce')) {
1081 - $this->fallbackResponse['text'] = esc_html__("I can't access order information right now. The order system seems to be unavailable.", 'mxchat');
931 + $this->fallbackResponse['text'] = "I can't access order information right now. The order system seems to be unavailable.";
1082 932 return true;
1083 933 }
1084 934
1085 935 $orderDetails = MxChat_WooCommerce::mxchat_fetch_user_orders_details('all');
@@ -1084,27 +934,27 @@
1084 934
1085 935 $orderDetails = MxChat_WooCommerce::mxchat_fetch_user_orders_details('all');
1086 936
1087 937 if (empty($orderDetails)) {
1088 - $this->fallbackResponse['text'] = esc_html__("I don't see any orders associated with your account. Are you logged in?", 'mxchat');
938 + $this->fallbackResponse['text'] = "I don't see any orders associated with your account. Are you logged in?";
1089 939 return true;
1090 940 }
1091 941
1092 942 // Generate AI prompt with context
1093 - $prompt = __("User asked about their orders:", 'mxchat') . " '{$message}'\n\n";
1094 - $prompt .= __("Order information:", 'mxchat') . "\n";
943 + $prompt = "User asked about their orders: '{$message}'\n\n";
944 + $prompt .= "Order information:\n";
1095 945 foreach ($orderDetails as $order) {
1096 946 $items_list = array_map(function($item) {
1097 947 return "{$item['name']} ({$item['quantity']})";
1098 948 }, $order['items']);
1099 949
1100 - $prompt .= __("Order #", 'mxchat') . "{$order['order_id']}: {$order['formatted_total']} " . __("on", 'mxchat') . " {$order['date']}\n";
1101 - $prompt .= __("Items:", 'mxchat') . " " . implode(', ', $items_list) . "\n";
950 + $prompt .= "Order #{$order['order_id']}: {$order['formatted_total']} on {$order['date']}\n";
951 + $prompt .= "Items: " . implode(', ', $items_list) . "\n";
1102 952 }
1103 953
1104 - $prompt .= __("\nProvide a natural, conversational response focusing on the specific information the user asked about. ", 'mxchat');
1105 - $prompt .= __("If they ask about a specific order or detail, provide just that information. ", 'mxchat');
1106 - $prompt .= __("If they ask about license keys or sensitive information, inform them to check their email or contact support.", 'mxchat');
954 + $prompt .= "\nProvide a natural, conversational response focusing on the specific information the user asked about. ";
955 + $prompt .= "If they ask about a specific order or detail, provide just that information. ";
956 + $prompt .= "If they ask about license keys or sensitive information, inform them to check their email or contact support.";
1107 957
1108 958 // Get AI response
1109 959 $ai_response = $this->mxchat_call_ai_api($prompt);
1110 960 $this->fallbackResponse['text'] = $ai_response['text'];
@@ -1152,28 +1002,28 @@
1152 1002 //error_log("Finding relevant content for product inquiry");
1153 1003 $relevant_content = $this->mxchat_find_relevant_content($user_message_embedding);
1154 1004
1155 1005 // Build AI prompt
1156 - $ai_prompt = esc_html__("You are a knowledgeable product assistant. ", 'mxchat');
1157 - $ai_prompt .= esc_html__("Respond to this user query: '{$message}'\n\n", 'mxchat');
1006 + $ai_prompt = "You are a knowledgeable product assistant. ";
1007 + $ai_prompt .= "Respond to this user query: '{$message}'\n\n";
1158 1008
1159 1009 if (!empty($relevant_content)) {
1160 - $ai_prompt .= esc_html__("Relevant information from our knowledge base:\n{$relevant_content}\n\n", 'mxchat');
1010 + $ai_prompt .= "Relevant information from our knowledge base:\n{$relevant_content}\n\n";
1161 1011 }
1162 1012
1163 - $ai_prompt .= esc_html__("Product details:\n", 'mxchat');
1164 - $ai_prompt .= esc_html__("Name: {$product_name}\n", 'mxchat');
1165 - $ai_prompt .= esc_html__("Price: ", 'mxchat') . strip_tags($product_price) . "\n";
1013 + $ai_prompt .= "Product details:\n";
1014 + $ai_prompt .= "Name: {$product_name}\n";
1015 + $ai_prompt .= "Price: " . strip_tags($product_price) . "\n";
1166 1016 if ($product_description) {
1167 - $ai_prompt .= esc_html__("Description: {$product_description}\n", 'mxchat');
1017 + $ai_prompt .= "Description: {$product_description}\n";
1168 1018 }
1169 1019
1170 - $ai_prompt .= esc_html__("\nInstructions:\n", 'mxchat');
1171 - $ai_prompt .= esc_html__("1. Address the user's specific question or concern about the product\n", 'mxchat');
1172 - $ai_prompt .= esc_html__("2. Incorporate relevant information from our knowledge base if provided\n", 'mxchat');
1173 - $ai_prompt .= esc_html__("3. Highlight key product features that relate to their query\n", 'mxchat');
1174 - $ai_prompt .= esc_html__("4. Include a natural suggestion to check out the product\n", 'mxchat');
1175 - $ai_prompt .= esc_html__("5. Keep the response conversational and helpful\n", 'mxchat');
1020 + $ai_prompt .= "\nInstructions:\n";
1021 + $ai_prompt .= "1. Address the user's specific question or concern about the product\n";
1022 + $ai_prompt .= "2. Incorporate relevant information from our knowledge base if provided\n";
1023 + $ai_prompt .= "3. Highlight key product features that relate to their query\n";
1024 + $ai_prompt .= "4. Include a natural suggestion to check out the product\n";
1025 + $ai_prompt .= "5. Keep the response conversational and helpful\n";
1176 1026
1177 1027 //error_log("Sending AI prompt: " . $ai_prompt);
1178 1028
1179 1029 // Build and log AI response
@@ -1219,9 +1069,9 @@
1219 1069 // Log the message safely
1220 1070 //error_log("Triggered email capture intent for message: " . sanitize_text_field($message));
1221 1071
1222 1072 // Initiate email capture flow
1223 - $response = esc_html($this->options['triggered_phrase_response'] ?? esc_html__("Would you like to join our mailing list? Please provide your email below.", 'mxchat'));
1073 + $response = esc_html($this->options['triggered_phrase_response'] ?? "Would you like to join our mailing list? Please provide your email below.");
1224 1074
1225 1075 set_transient('mxchat_email_capture_' . $user_id, true, 5 * MINUTE_IN_SECONDS);
1226 1076 $this->mxchat_save_chat_message($session_id, 'bot', $response);
1227 1077
@@ -1232,9 +1082,9 @@
1232 1082
1233 1083 //very good
1234 1084 public function mxchat_generate_image($message, $user_id, $session_id) {
1235 1085 // Prepare a prompt for DALL-E
1236 - $prompt = esc_html__('Create an image of ', 'mxchat') . sanitize_text_field($message);
1086 + $prompt = "Create an image of " . sanitize_text_field($message);
1237 1087
1238 1088 // Use the existing OpenAI API key
1239 1089 $openai_api_key = sanitize_text_field($this->options['api_key']);
1240 1090
@@ -1245,13 +1095,13 @@
1245 1095 if (isset($image_response['imageUrl'])) {
1246 1096 $image_url = esc_url_raw($image_response['imageUrl']);
1247 1097
1248 1098 // Construct the HTML with a CSS class instead of inline styles
1249 - $response_html = '<img src="' . esc_url($image_url) . '" alt="' . esc_attr__('Generated Image', 'mxchat') . '" class="mxchat-generated-image" />';
1099 + $response_html = '<img src="' . esc_url($image_url) . '" alt="Generated Image" class="mxchat-generated-image" />';
1250 1100
1251 - $response_text = esc_html__('Here is the image I generated:', 'mxchat');
1101 + $response_text = "Here is the image I generated:";
1252 1102 } else {
1253 - $response_text = esc_html__("I'm sorry, but I couldn't generate an image based on your request.", 'mxchat');
1103 + $response_text = "I'm sorry, but I couldn't generate an image based on your request.";
1254 1104 $response_html = '';
1255 1105 //error_log("DALL-E image generation error: " . esc_html($image_response['error'] ?? 'Unknown error.'));
1256 1106 }
1257 1107
@@ -1292,9 +1142,9 @@
1292 1142 $response = wp_remote_post($api_url, $args);
1293 1143
1294 1144 if (is_wp_error($response)) {
1295 1145 //error_log("DALL-E request failed: " . $response->get_error_message());
1296 - return ['error' => esc_html__('Error generating image: ', 'mxchat') . $response->get_error_message()];
1146 + return ['error' => "Error generating image: " . $response->get_error_message()];
1297 1147 }
1298 1148
1299 1149 $response_body = json_decode(wp_remote_retrieve_body($response), true);
1300 1150
@@ -1301,9 +1151,9 @@
1301 1151 if (isset($response_body['data'][0]['url'])) {
1302 1152 return ['imageUrl' => esc_url_raw($response_body['data'][0]['url'])];
1303 1153 } else {
1304 1154 //error_log("DALL-E response error: " . wp_remote_retrieve_body($response));
1305 - return ['error' => esc_html__('Failed to generate image.', 'mxchat')];
1155 + return ['error' => "Failed to generate image."];
1306 1156 }
1307 1157 }
1308 1158
1309 1159 /**
@@ -1604,9 +1454,9 @@
1604 1454
1605 1455 foreach ($body['results'] as $image) {
1606 1456 $image_url = isset($image['url']) ? esc_url($image['url']) : '';
1607 1457 $thumbnail_url = isset($image['thumbnail']['src']) ? esc_url($image['thumbnail']['src']) : '';
1608 - $title = isset($image['title']) ? esc_html($image['title']) : esc_html__('Image', 'mxchat');
1458 + $title = isset($image['title']) ? esc_html($image['title']) : __('Image', 'mxchat');
1609 1459
1610 1460 if ($image_url && $thumbnail_url) {
1611 1461 $html_output .= '<div class="mxchat-image-item">';
1612 1462 $html_output .= '<strong class="mxchat-image-title">' . $title . '</strong>';
@@ -1639,20 +1489,21 @@
1639 1489 ];
1640 1490 }
1641 1491 }
1642 1492 public function mxchat_interpret_search_query($user_query) {
1643 - $system_prompt = esc_html__("Interpret the user's request to provide only the essential keywords or phrases for image searching. Remove conversational language, politeness, or extra context. Return a concise search query that doesn't lose any of the original meaning.", 'mxchat');
1493 + $system_prompt = "Interpret the user's request to provide only the essential keywords or phrases for image searching. Remove conversational language, politeness, or extra context. Return a concise search query that doesn't lose any of the original meaning.";
1644 1494
1645 1495 // Retrieve OpenAI API key using 'api_key' as the option key
1646 1496 $api_key = isset($this->options['api_key']) ? sanitize_text_field($this->options['api_key']) : sanitize_text_field(get_option('mxchat_options')['api_key']);
1647 1497
1648 - /*
1498 +/*
1649 1499 // Log the API key check, without exposing the key
1650 1500 if (defined('WP_DEBUG') && WP_DEBUG) {
1651 1501 error_log("Retrieved OpenAI API Key: " . ($api_key ? "Present" : "Missing"));
1652 1502 }
1653 - */
1503 +*/
1654 1504
1505 +
1655 1506 if (empty($api_key)) {
1656 1507 //error_log("OpenAI API key is missing.");
1657 1508 return sanitize_text_field($user_query); // Default to the original query if API key is missing
1658 1509 }
@@ -1687,14 +1538,14 @@
1687 1538 // Check for a valid response and sanitize output
1688 1539 if (isset($body['choices'][0]['message']['content'])) {
1689 1540 $interpreted_query = sanitize_text_field(trim($body['choices'][0]['message']['content']));
1690 1541
1691 - /*
1542 +/*
1692 1543 // Log the interpreted query for debugging
1693 1544 if (defined('WP_DEBUG') && WP_DEBUG) {
1694 1545 error_log("Interpreted search query: " . $interpreted_query);
1695 1546 }
1696 - */
1547 +*/
1697 1548
1698 1549 return $interpreted_query;
1699 1550 } else {
1700 1551 //error_log("Unexpected API response format: " . print_r($body, true));
@@ -1709,9 +1560,9 @@
1709 1560 //error_log("WooCommerce not available");
1710 1561 return $this->generate_intent_response([
1711 1562 'intent' => 'add_to_cart',
1712 1563 'status' => 'error',
1713 - 'reason' => esc_html__('woocommerce_not_available', 'mxchat')
1564 + 'reason' => 'woocommerce_not_available'
1714 1565 ], $session_id);
1715 1566 }
1716 1567
1717 1568 $sanitized_user_id = sanitize_key($user_id);
@@ -1742,10 +1593,10 @@
1742 1593 //error_log("No product ID found through any method");
1743 1594 return $this->generate_intent_response([
1744 1595 'intent' => 'add_to_cart',
1745 1596 'status' => 'error',
1746 - 'reason' => esc_html__('no_product_context', 'mxchat'),
1747 - 'action_needed' => esc_html__('request_product_name', 'mxchat'),
1597 + 'reason' => 'no_product_context',
1598 + 'action_needed' => 'request_product_name',
1748 1599 'searched_message' => $message
1749 1600 ], $session_id);
1750 1601 }
1751 1602
@@ -1755,9 +1606,9 @@
1755 1606 //error_log("Product not found with ID: $product_id");
1756 1607 return $this->generate_intent_response([
1757 1608 'intent' => 'add_to_cart',
1758 1609 'status' => 'error',
1759 - 'reason' => esc_html__('product_not_found', 'mxchat'),
1610 + 'reason' => 'product_not_found',
1760 1611 'product_id' => $product_id
1761 1612 ], $session_id);
1762 1613 }
1763 1614
@@ -1775,11 +1626,11 @@
1775 1626 'id' => $product_id
1776 1627 ],
1777 1628 'cart_url' => wc_get_cart_url(),
1778 1629 'available_actions' => [
1779 - esc_html__('view_cart', 'mxchat'),
1780 - esc_html__('checkout', 'mxchat'),
1781 - esc_html__('continue_shopping', 'mxchat')
1630 + 'view_cart',
1631 + 'checkout',
1632 + 'continue_shopping'
1782 1633 ]
1783 1634 ], $session_id);
1784 1635 } else {
1785 1636 //error_log("Failed to add to cart: " . $product->get_name());
@@ -1785,9 +1636,9 @@
1785 1636 //error_log("Failed to add to cart: " . $product->get_name());
1786 1637 return $this->generate_intent_response([
1787 1638 'intent' => 'add_to_cart',
1788 1639 'status' => 'error',
1789 - 'reason' => esc_html__('add_to_cart_failed', 'mxchat'),
1640 + 'reason' => 'add_to_cart_failed',
1790 1641 'product' => [
1791 1642 'name' => $product->get_name(),
1792 1643 'id' => $product_id
1793 1644 ]
@@ -1806,9 +1657,9 @@
1806 1657 // Get relevant content as string
1807 1658 $relevant_content = $this->mxchat_find_relevant_products($query_embedding);
1808 1659 if (empty($relevant_content)) {
1809 1660 // Return null to indicate no results and set fallback response
1810 - $this->fallbackResponse['text'] = esc_html__("I couldn't find any relevant products based on your query. Could you please be more specific about the product you're looking for?", 'mxchat');
1661 + $this->fallbackResponse['text'] = "I couldn't find any relevant products based on your query. Could you please be more specific about the product you're looking for?";
1811 1662 return null;
1812 1663 }
1813 1664
1814 1665 // Extract product URLs from the content
@@ -1861,9 +1712,9 @@
1861 1712 }
1862 1713 }
1863 1714
1864 1715 // If no product is found after all checks, set the fallback response
1865 - $this->fallbackResponse['text'] = esc_html__("I couldn't find any relevant products based on your query. Try to be more specific", 'mxchat');
1716 + $this->fallbackResponse['text'] = "I couldn't find any relevant products based on your query. Try to be more specific";
1866 1717 return null;
1867 1718 }
1868 1719
1869 1720 // New method to handle intent responses
@@ -1876,9 +1727,8 @@
1876 1727 $context_string,
1877 1728 $this->options['api_key'],
1878 1729 $this->options['xai_api_key'],
1879 1730 $this->options['claude_api_key'],
1880 - $this->options['deepseek_api_key'],
1881 1731 $this->mxchat_fetch_conversation_history_for_ai($session_id)
1882 1732 );
1883 1733
1884 1734 $this->fallbackResponse['text'] = $response;
@@ -1885,33 +1735,33 @@
1885 1735 return true;
1886 1736 }
1887 1737 // Helper method to format intent context
1888 1738 private function format_intent_context($context) {
1889 - $context_string = esc_html__("INTENT CONTEXT:\n", 'mxchat');
1739 + $context_string = "INTENT CONTEXT:\n";
1890 1740
1891 1741 switch ($context['intent']) {
1892 1742 case 'add_to_cart':
1893 1743 if ($context['status'] === 'success') {
1894 - $context_string .= esc_html__("Action: Successfully added product to cart\n", 'mxchat');
1895 - $context_string .= sprintf(esc_html__("Product: %s\n", 'mxchat'), $context['product']['name']);
1896 - $context_string .= esc_html__("Available actions: ", 'mxchat') . implode(', ', $context['available_actions']) . "\n";
1897 - $context_string .= sprintf(esc_html__("Cart URL: %s\n", 'mxchat'), $context['cart_url']);
1898 - $context_string .= esc_html__("\nPlease inform the user of the successful addition and their available options.", 'mxchat');
1744 + $context_string .= "Action: Successfully added product to cart\n";
1745 + $context_string .= "Product: {$context['product']['name']}\n";
1746 + $context_string .= "Available actions: " . implode(', ', $context['available_actions']) . "\n";
1747 + $context_string .= "Cart URL: {$context['cart_url']}\n";
1748 + $context_string .= "\nPlease inform the user of the successful addition and their available options.";
1899 1749 } else {
1900 - $context_string .= esc_html__("Action: Failed to add product to cart\n", 'mxchat');
1901 - $context_string .= sprintf(esc_html__("Reason: %s\n", 'mxchat'), $context['reason']);
1750 + $context_string .= "Action: Failed to add product to cart\n";
1751 + $context_string .= "Reason: {$context['reason']}\n";
1902 1752 switch ($context['reason']) {
1903 1753 case 'woocommerce_not_available':
1904 - $context_string .= esc_html__("\nPlease inform the user that shopping features are not available.", 'mxchat');
1754 + $context_string .= "\nPlease inform the user that shopping features are not available.";
1905 1755 break;
1906 1756 case 'no_product_context':
1907 - $context_string .= esc_html__("\nPlease ask the user to specify which product they want to add.", 'mxchat');
1757 + $context_string .= "\nPlease ask the user to specify which product they want to add.";
1908 1758 break;
1909 1759 case 'product_not_found':
1910 - $context_string .= esc_html__("\nPlease inform the user that the product couldn't be found and ask them to try again.", 'mxchat');
1760 + $context_string .= "\nPlease inform the user that the product couldn't be found and ask them to try again.";
1911 1761 break;
1912 1762 case 'add_to_cart_failed':
1913 - $context_string .= esc_html__("\nPlease apologize to the user and suggest they try again or ask for assistance.", 'mxchat');
1763 + $context_string .= "\nPlease apologize to the user and suggest they try again or ask for assistance.";
1914 1764 break;
1915 1765 }
1916 1766 }
1917 1767 break;
@@ -1922,15 +1772,15 @@
1922 1772
1923 1773
1924 1774 public function mxchat_handle_checkout_intent($message, $user_id, $session_id) {
1925 1775 if (!class_exists('WooCommerce')) {
1926 - $this->fallbackResponse['text'] = esc_html__("I apologize, but the checkout feature isn't available at the moment.", 'mxchat');
1776 + $this->fallbackResponse['text'] = "I apologize, but the checkout feature isn't available at the moment.";
1927 1777 return true;
1928 1778 }
1929 1779
1930 1780 // Check if cart has items
1931 1781 if (WC()->cart->is_empty()) {
1932 - $this->fallbackResponse['text'] = esc_html__("Your cart is empty at the moment. Would you like to see our products?", 'mxchat');
1782 + $this->fallbackResponse['text'] = "Your cart is empty at the moment. Would you like to see our products?";
1933 1783 return true;
1934 1784 }
1935 1785
1936 1786 // Get cart summary
@@ -1939,17 +1789,17 @@
1939 1789
1940 1790 // Get and validate checkout URL
1941 1791 $checkout_url = wc_get_checkout_url();
1942 1792 if (!$checkout_url) {
1943 - $this->fallbackResponse['text'] = esc_html__("I'm having trouble accessing the checkout page. Please try again in a moment.", 'mxchat');
1793 + $this->fallbackResponse['text'] = "I'm having trouble accessing the checkout page. Please try again in a moment.";
1944 1794 return true;
1945 1795 }
1946 1796
1947 1797 wp_send_json([
1948 1798 'text' => sprintf(
1949 - esc_html__("You have %d item%s in your cart totaling %s. I'll redirect you to checkout now.", 'mxchat'),
1799 + "You have %d item%s in your cart totaling %s. I'll redirect you to checkout now.",
1950 1800 $cart_count,
1951 - $cart_count > 1 ? esc_html__('s', 'mxchat') : '',
1801 + $cart_count > 1 ? 's' : '',
1952 1802 strip_tags($cart_total)
1953 1803 ),
1954 1804 'redirect_url' => esc_url_raw($checkout_url)
1955 1805 ]);
@@ -1967,9 +1817,9 @@
1967 1817 $mailing_list_id = isset($this->options['loops_mailing_list']) ? sanitize_text_field($this->options['loops_mailing_list']) : '';
1968 1818
1969 1819 // Check for missing API key or mailing list ID
1970 1820 if (empty($api_key) || empty($mailing_list_id)) {
1971 - //error_log(esc_html__('Loops API key or mailing list ID is missing.', 'mxchat'));
1821 + //error_log('Loops API key or mailing list ID is missing.');
1972 1822 return;
1973 1823 }
1974 1824
1975 1825 $data = array(
@@ -1974,9 +1824,9 @@
1974 1824
1975 1825 $data = array(
1976 1826 'email' => $email,
1977 1827 'subscribed' => true,
1978 - 'source' => __('MxChat AI Chatbot', 'mxchat'),
1828 + 'source' => 'MxChat AI Chatbot',
1979 1829 'mailingLists' => array($mailing_list_id => true),
1980 1830 );
1981 1831
1982 1832 $url = 'https://app.loops.so/api/v1/contacts/create';
@@ -1993,9 +1843,9 @@
1993 1843 $response = wp_remote_post($url, $args);
1994 1844
1995 1845 // Handle errors in the API request
1996 1846 if (is_wp_error($response)) {
1997 - //error_log(esc_html__('Error adding email to Loops: ', 'mxchat') . $response->get_error_message());
1847 + //error_log('Error adding email to Loops: ' . $response->get_error_message());
1998 1848 return;
1999 1849 }
2000 1850
2001 1851 // Check for non-200 HTTP responses
@@ -2001,9 +1851,9 @@
2001 1851 // Check for non-200 HTTP responses
2002 1852 $response_code = wp_remote_retrieve_response_code($response);
2003 1853 if ($response_code != 200) {
2004 1854 $response_body = wp_remote_retrieve_body($response);
2005 - //error_log(esc_html__('Loops API responded with code ', 'mxchat') . $response_code . ': ' . $response_body);
1855 + //error_log('Loops API responded with code ' . $response_code . ': ' . $response_body);
2006 1856 }
2007 1857 }
2008 1858
2009 1859 public function mxchat_handle_pdf_discussion($message, $user_id, $session_id) {
@@ -2010,11 +1860,11 @@
2010 1860 // Get the maximum number of pages allowed from admin settings
2011 1861 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
2012 1862
2013 1863 // Retrieve options for dynamic texts
2014 - $trigger_text = $this->options['pdf_intent_trigger_text'] ?? __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat');
2015 - $success_text = $this->options['pdf_intent_success_text'] ?? __("I've processed the PDF. What questions do you have about it?", 'mxchat');
2016 - $error_text = $this->options['pdf_intent_error_text'] ?? __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat');
1864 + $trigger_text = $this->options['pdf_intent_trigger_text'] ?? "Please provide the URL to the PDF you'd like to discuss.";
1865 + $success_text = $this->options['pdf_intent_success_text'] ?? "I've processed the PDF. What questions do you have about it?";
1866 + $error_text = $this->options['pdf_intent_error_text'] ?? "Sorry, I couldn't process the PDF. Please ensure it's a valid file.";
2017 1867
2018 1868 // Check for explicit request for new PDF
2019 1869 $new_pdf_requested = stripos($message, 'new') !== false ||
2020 1870 stripos($message, 'another') !== false ||
@@ -2053,9 +1903,9 @@
2053 1903 $temp_file = wp_tempnam($pdf_source); // Safe temporary file name
2054 1904 $response = wp_remote_get($pdf_source, ['timeout' => 60]);
2055 1905
2056 1906 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
2057 - //error_log(esc_html__("Failed to download PDF. Error: ", 'mxchat') . print_r($response, true));
1907 + //error_log("Failed to download PDF. Error: " . print_r($response, true));
2058 1908 return false;
2059 1909 }
2060 1910
2061 1911 file_put_contents($temp_file, wp_remote_retrieve_body($response));
@@ -2062,9 +1912,9 @@
2062 1912
2063 1913 // Validate that the downloaded file is a PDF
2064 1914 $mime_type = mime_content_type($temp_file);
2065 1915 if ($mime_type !== 'application/pdf') {
2066 - //error_log(esc_html__("Invalid MIME type detected for PDF: ", 'mxchat') . $mime_type);
1916 + //error_log("Invalid MIME type detected for PDF: $mime_type");
2067 1917 unlink($temp_file);
2068 1918 return false;
2069 1919 }
2070 1920 } else {
@@ -2077,13 +1927,13 @@
2077 1927 $pdf = $parser->parseFile($temp_file);
2078 1928 $pages = $pdf->getPages();
2079 1929
2080 1930 if (count($pages) > $max_pages) {
2081 - //error_log(esc_html__("PDF exceeds the maximum allowed pages: ", 'mxchat') . count($pages));
1931 + //error_log("PDF exceeds the maximum allowed pages: " . count($pages));
2082 1932 if (filter_var($pdf_source, FILTER_VALIDATE_URL)) {
2083 1933 unlink($temp_file);
2084 1934 }
2085 - return esc_html__('too_many_pages', 'mxchat');
1935 + return 'too_many_pages';
2086 1936 }
2087 1937
2088 1938 $embeddings = [];
2089 1939 foreach ($pages as $page_number => $page) {
@@ -2090,14 +1940,14 @@
2090 1940 $text = $page->getText();
2091 1941
2092 1942 // Ensure text is non-empty before generating embeddings
2093 1943 if (empty(trim($text))) {
2094 - //error_log(esc_html__("Skipping empty page: ", 'mxchat') . ($page_number + 1));
1944 + //error_log("Skipping empty page: " . ($page_number + 1));
2095 1945 continue;
2096 1946 }
2097 1947
2098 1948 $embedding = $this->mxchat_generate_embedding(
2099 - esc_html__("Page ", 'mxchat') . ($page_number + 1) . ": " . $text,
1949 + "Page " . ($page_number + 1) . ": " . $text,
2100 1950 $this->options['api_key']
2101 1951 );
2102 1952
2103 1953 if ($embedding) {
@@ -2106,9 +1956,9 @@
2106 1956 'embedding' => $embedding,
2107 1957 'text' => $text,
2108 1958 ];
2109 1959 } else {
2110 - //error_log(esc_html__("Failed to generate embedding for page ", 'mxchat') . ($page_number + 1));
1960 + //error_log("Failed to generate embedding for page " . ($page_number + 1));
2111 1961 }
2112 1962 }
2113 1963
2114 1964 // Clean up downloaded file if it was from URL
@@ -2118,9 +1968,9 @@
2118 1968
2119 1969 return $embeddings;
2120 1970
2121 1971 } catch (\Exception $e) {
2122 - // error_log(esc_html__("Error parsing or processing PDF: ", 'mxchat') . $e->getMessage());
1972 + // error_log("Error parsing or processing PDF: " . $e->getMessage());
2123 1973
2124 1974 // Cleanup in case of exception
2125 1975 if (filter_var($pdf_source, FILTER_VALIDATE_URL) && $temp_file && file_exists($temp_file)) {
2126 1976 unlink($temp_file);
@@ -2129,9 +1979,9 @@
2129 1979 return false;
2130 1980 }
2131 1981 }
2132 1982 private function find_relevant_pdf_pages($query_embedding, $embeddings) {
2133 - //error_log(esc_html__("find_relevant_pdf_pages called.", 'mxchat'));
1983 + //error_log("find_relevant_pdf_pages called.");
2134 1984
2135 1985 $most_relevant = null;
2136 1986 $highest_similarity = -INF;
2137 1987
@@ -2157,9 +2007,9 @@
2157 2007 public function handle_pdf_upload() {
2158 2008 check_ajax_referer('mxchat_chat_nonce', 'nonce');
2159 2009
2160 2010 if (!isset($_FILES['pdf_file']) || !isset($_POST['session_id'])) {
2161 - wp_send_json_error(esc_html__('Missing required parameters.', 'mxchat'));
2011 + wp_send_json_error('Missing required parameters.');
2162 2012 return;
2163 2013 }
2164 2014
2165 2015 $file = $_FILES['pdf_file'];
@@ -2167,9 +2017,9 @@
2167 2017 $original_filename = sanitize_text_field($file['name']);
2168 2018
2169 2019 $file_type = wp_check_filetype($file['name'], ['pdf' => 'application/pdf']);
2170 2020 if ($file_type['type'] !== 'application/pdf') {
2171 - wp_send_json_error(esc_html__('Invalid file type. Only PDF files are allowed.', 'mxchat'));
2021 + wp_send_json_error('Invalid file type. Only PDF files are allowed.');
2172 2022 return;
2173 2023 }
2174 2024
2175 2025 $upload_dir = wp_upload_dir();
@@ -2176,9 +2026,9 @@
2176 2026 $pdf_filename = 'mxchat_' . $session_id . '_' . time() . '.pdf';
2177 2027 $pdf_path = $upload_dir['path'] . '/' . $pdf_filename;
2178 2028
2179 2029 if (!move_uploaded_file($file['tmp_name'], $pdf_path)) {
2180 - wp_send_json_error(esc_html__('Failed to upload file.', 'mxchat'));
2030 + wp_send_json_error('Failed to upload file.');
2181 2031 return;
2182 2032 }
2183 2033
2184 2034 $this->clear_pdf_transients($session_id);
@@ -2189,9 +2039,9 @@
2189 2039 if ($embeddings === 'too_many_pages') {
2190 2040 unlink($pdf_path);
2191 2041 $error_message = sprintf(
2192 2042 $this->options['pdf_intent_error_text'] ??
2193 - esc_html__("The provided PDF exceeds the maximum allowed limit of %d pages. Please provide a smaller document.", 'mxchat'),
2043 + "The provided PDF exceeds the maximum allowed limit of %d pages. Please provide a smaller document.",
2194 2044 $max_pages
2195 2045 );
2196 2046 wp_send_json_error($error_message);
2197 2047 return;
@@ -2199,9 +2049,9 @@
2199 2049
2200 2050 if ($embeddings === false || empty($embeddings)) {
2201 2051 unlink($pdf_path);
2202 2052 $error_message = $this->options['pdf_intent_error_text'] ??
2203 - esc_html__('The uploaded PDF appears to be empty or contains unsupported content.', 'mxchat');
2053 + 'The uploaded PDF appears to be empty or contains unsupported content.';
2204 2054 wp_send_json_error($error_message);
2205 2055 return;
2206 2056 }
2207 2057
@@ -2211,9 +2061,9 @@
2211 2061 set_transient('mxchat_pdf_embeddings_' . $session_id, $embeddings, HOUR_IN_SECONDS);
2212 2062 set_transient('mxchat_include_pdf_in_context_' . $session_id, true, HOUR_IN_SECONDS);
2213 2063
2214 2064 $success_message = $this->options['pdf_intent_success_text'] ??
2215 - esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat');
2065 + "I've processed the PDF. What questions do you have about it?";
2216 2066
2217 2067 wp_send_json_success([
2218 2068 'message' => $success_message,
2219 2069 'filename' => $original_filename
@@ -2222,9 +2072,9 @@
2222 2072 }
2223 2073
2224 2074 unlink($pdf_path);
2225 2075 $error_message = $this->options['pdf_intent_error_text'] ??
2226 - esc_html__('Sorry, I couldn\'t process the PDF. Please ensure it\'s a valid file.', 'mxchat');
2076 + 'Sorry, I couldn\'t process the PDF. Please ensure it\'s a valid file.';
2227 2077 wp_send_json_error($error_message);
2228 2078 return;
2229 2079 }
2230 2080 public function handle_pdf_remove() {
@@ -2230,9 +2080,9 @@
2230 2080 public function handle_pdf_remove() {
2231 2081 check_ajax_referer('mxchat_chat_nonce', 'nonce');
2232 2082
2233 2083 if (empty($_POST['session_id'])) {
2234 - wp_send_json_error(esc_html__('Session ID missing.', 'mxchat'));
2084 + wp_send_json_error('Session ID missing.');
2235 2085 wp_die();
2236 2086 }
2237 2087
2238 2088 $session_id = sanitize_text_field($_POST['session_id']);
@@ -2244,9 +2094,9 @@
2244 2094
2245 2095 $this->clear_pdf_transients($session_id);
2246 2096
2247 2097 wp_send_json_success([
2248 - 'message' => esc_html__('PDF removed successfully.', 'mxchat')
2098 + 'message' => 'PDF removed successfully.'
2249 2099 ]);
2250 2100 wp_die();
2251 2101 }
2252 2102
@@ -2257,14 +2107,14 @@
2257 2107 * @param string $prompt The prompt to send to the AI.
2258 2108 * @return array An array containing the AI's response text.
2259 2109 */
2260 2110 private function mxchat_call_ai_api( $prompt ) {
2261 - //error_log( esc_html__( 'Calling AI API with the provided prompt.', 'mxchat' ) );
2111 + //error_log( 'Calling AI API with the provided prompt.' );
2262 2112
2263 2113 $api_key = $this->options['api_key'];
2264 2114 if ( empty( $api_key ) ) {
2265 - //error_log( esc_html__( 'API key is not set.', 'mxchat' ) );
2266 - return [ 'text' => esc_html__( 'API key is not set.', 'mxchat' ) ];
2115 + //error_log( 'API key is not set.' );
2116 + return [ 'text' => 'API key is not set.' ];
2267 2117 }
2268 2118
2269 2119 $url = 'https://api.openai.com/v1/chat/completions';
2270 2120 $messages = [
@@ -2269,9 +2119,9 @@
2269 2119 $url = 'https://api.openai.com/v1/chat/completions';
2270 2120 $messages = [
2271 2121 [
2272 2122 'role' => 'system',
2273 - 'content' => __( 'You are a helpful assistant that provides concise and personalized product recommendations. Someone has asked you for some recommendations please respond very concisely appropriate for an AI chatbot.', 'mxchat' ),
2123 + 'content' => 'You are a helpful assistant that provides concise and personalized product recommendations. Someone has asked you for some recommendations please respond very concisely appropriate for an AI chatbot.',
2274 2124 ],
2275 2125 [
2276 2126 'role' => 'user',
2277 2127 'content' => $prompt,
@@ -2296,24 +2146,23 @@
2296 2146
2297 2147 $response = wp_remote_post( $url, $args );
2298 2148
2299 2149 if ( is_wp_error( $response ) ) {
2300 - //error_log( esc_html__( 'Error communicating with AI API: ', 'mxchat' ) . $response->get_error_message() );
2301 - return [ 'text' => esc_html__( 'Error communicating with AI API.', 'mxchat' ) ];
2150 + //error_log( 'Error communicating with AI API: ' . $response->get_error_message() );
2151 + return [ 'text' => 'Error communicating with AI API.' ];
2302 2152 }
2303 2153
2304 2154 $body = wp_remote_retrieve_body( $response );
2305 - //error_log( esc_html__( 'API response body: ', 'mxchat' ) . $body );
2155 + //error_log( 'API response body: ' . $body );
2306 2156
2307 2157 $decoded_body = json_decode( $body, true );
2308 2158 if ( isset( $decoded_body['choices'][0]['message']['content'] ) ) {
2309 2159 return [ 'text' => $decoded_body['choices'][0]['message']['content'] ];
2310 2160 } else {
2311 - //error_log( esc_html__( 'Unexpected API response format: ', 'mxchat' ) . wp_json_encode( $decoded_body ) );
2312 - return [ 'text' => esc_html__( 'No response received from AI.', 'mxchat' ) ];
2161 + //error_log( 'Unexpected API response format: ' . wp_json_encode( $decoded_body ) );
2162 + return [ 'text' => 'No response received from AI.' ];
2313 2163 }
2314 2164 }
2315 -
2316 2165 /**
2317 2166 * Fetches the AI response for the given prompt.
2318 2167 *
2319 2168 * @param string $prompt The prompt to send to the AI.
@@ -2336,19 +2185,19 @@
2336 2185 $name = $rec['name'];
2337 2186 $price = $rec['price'];
2338 2187 $url = $rec['url'];
2339 2188 $image = $rec['image'];
2340 - $recommendation_list .= "{$number}. " . esc_html__('Product:', 'mxchat') . " {$name} (" . esc_html__('Price:', 'mxchat') . " \\${$price})\n";
2189 + $recommendation_list .= "{$number}. Product: {$name} (Price: \\${$price})\n";
2341 2190 $recommendation_list .= " [Link]({$url})\n";
2342 2191 $recommendation_list .= " ![Image]({$image})\n\n";
2343 2192 }
2344 2193
2345 - $prompt = esc_html__('Based on the following list of products, generate a unique, friendly, and personalized response to a user. ', 'mxchat');
2346 - $prompt .= esc_html__('If some products aren\'t exactly what the user asked for but share similar styles or patterns, acknowledge this and explain why you\'re suggesting them. ', 'mxchat');
2347 - $prompt .= esc_html__('For each product, provide a brief justification that clearly explains why it\'s relevant, especially if it\'s a different type of product than requested. ', 'mxchat');
2348 - $prompt .= esc_html__('Please number your responses to match the product numbers.', 'mxchat') . "\n\n";
2349 - $prompt .= esc_html__('Products:', 'mxchat') . "\n\n{$recommendation_list}";
2350 - $prompt .= esc_html__('Please ensure that the number of each product matches the order in which the products are listed.', 'mxchat');
2194 + $prompt = "Based on the following list of products, generate a unique, friendly, and personalized response to a user. ";
2195 + $prompt .= "If some products aren't exactly what the user asked for but share similar styles or patterns, acknowledge this and explain why you're suggesting them. ";
2196 + $prompt .= "For each product, provide a brief justification that clearly explains why it's relevant, especially if it's a different type of product than requested. ";
2197 + $prompt .= "Please number your responses to match the product numbers.\n\n";
2198 + $prompt .= "Products:\n\n{$recommendation_list}";
2199 + $prompt .= "Please ensure that the number of each product matches the order in which the products are listed.";
2351 2200
2352 2201 return $prompt;
2353 2202 }
2354 2203 private function mxchat_generate_recommendations($user_id, $message) {
@@ -2459,31 +2308,31 @@
2459 2308 }
2460 2309
2461 2310 return [
2462 2311 'recommendations' => $formatted_recommendations,
2463 - 'sources' => [__('AI-powered personalized recommendations', 'mxchat')]
2312 + 'sources' => ['AI-powered personalized recommendations']
2464 2313 ];
2465 2314 }
2466 2315 private function mxchat_get_ai_shopping_suggestion($message, $user_context) {
2467 - $prompt = esc_html__("As a shopping assistant, analyze this user's context and generate a specific product search suggestion. ", 'mxchat');
2468 - $prompt .= esc_html__("User's Question: \"{$message}\"\n\n", 'mxchat');
2316 + $prompt = "As a shopping assistant, analyze this user's context and generate a specific product search suggestion. ";
2317 + $prompt .= "User's Question: \"{$message}\"\n\n";
2469 2318
2470 2319 if (!empty($user_context['order_history'])) {
2471 - $prompt .= esc_html__("Their recent orders include:\n", 'mxchat');
2320 + $prompt .= "Their recent orders include:\n";
2472 2321 foreach ($user_context['order_history'] as $order) {
2473 - $prompt .= esc_html__("- {$order['name']} ({$order['date']})\n", 'mxchat');
2322 + $prompt .= "- {$order['name']} ({$order['date']})\n";
2474 2323 }
2475 2324 }
2476 2325
2477 2326 if (!empty($user_context['cart_items'])) {
2478 - $prompt .= esc_html__("\nThey currently have in their cart:\n", 'mxchat');
2327 + $prompt .= "\nThey currently have in their cart:\n";
2479 2328 foreach ($user_context['cart_items'] as $item) {
2480 - $prompt .= esc_html__("- {$item['name']}\n", 'mxchat');
2329 + $prompt .= "- {$item['name']}\n";
2481 2330 }
2482 2331 }
2483 2332
2484 - $prompt .= esc_html__("\nBased on their question and history, suggest a specific search query that would help find the most relevant products. ", 'mxchat');
2485 - $prompt .= esc_html__("Respond with ONLY the search query, nothing else.", 'mxchat');
2333 + $prompt .= "\nBased on their question and history, suggest a specific search query that would help find the most relevant products. ";
2334 + $prompt .= "Respond with ONLY the search query, nothing else.";
2486 2335
2487 2336 $response = $this->mxchat_call_ai_api($prompt);
2488 2337 return isset($response['text']) ? trim($response['text']) : $message;
2489 2338 }
@@ -2657,10 +2506,10 @@
2657 2506 $persistence_enabled = $_POST['persistence_enabled'] === 'true';
2658 2507 $initial_timestamp = isset($_POST['initial_timestamp']) ? intval($_POST['initial_timestamp']) : 0;
2659 2508
2660 2509 if (empty($session_id)) {
2661 - //error_log(esc_html__('Fetch new messages error: Session ID missing.', 'mxchat'));
2662 - wp_send_json_error(['message' => esc_html__('Session ID missing.', 'mxchat')]);
2510 + //error_log('Fetch new messages error: Session ID missing.');
2511 + wp_send_json_error(['message' => 'Session ID missing.']);
2663 2512 wp_die();
2664 2513 }
2665 2514
2666 2515 $history = get_option("mxchat_history_{$session_id}", []);
@@ -2678,9 +2527,9 @@
2678 2527 $message['role'] === 'agent' &&
2679 2528 $message['timestamp'] > $initial_timestamp;
2680 2529 });
2681 2530
2682 - //error_log(esc_html__("New agent messages fetched for session $session_id. Last seen ID: $last_seen_id", 'mxchat'));
2531 + //error_log("New agent messages fetched for session $session_id. Last seen ID: $last_seen_id");
2683 2532
2684 2533 wp_send_json_success([
2685 2534 'new_messages' => array_values($new_messages)
2686 2535 ]);
@@ -2689,11 +2538,13 @@
2689 2538
2690 2539
2691 2540 public function mxchat_live_agent_handover($message, $user_id, $session_id) {
2692 2541 // First check if live agents are available
2693 - $live_agent_available = $this->options['live_agent_status'] ?? 'off';
2694 - if ($live_agent_available !== 'on') {
2542 + $live_agent_available = $this->options['live_agent_status'] ?? 'offline';
2543 +
2544 + if ($live_agent_available !== 'online') {
2695 2545 $away_message = $this->options['live_agent_away_message'] ?? 'Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.';
2546 +
2696 2547 $this->fallbackResponse = [
2697 2548 'text' => $away_message,
2698 2549 'html' => '',
2699 2550 'images' => [],
@@ -2698,8 +2549,11 @@
2698 2549 'html' => '',
2699 2550 'images' => [],
2700 2551 'chat_mode' => 'ai'
2701 2552 ];
2553 +
2554 + //error_log('Live agent handover attempted but agents are offline');
2555 +
2702 2556 wp_send_json([
2703 2557 'text' => $away_message,
2704 2558 'html' => '',
2705 2559 'chat_mode' => 'ai',
@@ -2708,27 +2562,15 @@
2708 2562 wp_die();
2709 2563 }
2710 2564
2711 2565 $slack_webhook_url = $this->options['live_agent_webhook_url'] ?? '';
2566 + //error_log('Slack Webhook URL retrieved: ' . $slack_webhook_url);
2567 +
2712 2568 if (empty($slack_webhook_url)) {
2569 + //error_log('Slack Webhook URL is not configured.');
2713 2570 return false;
2714 2571 }
2715 2572
2716 - // Get recent chat history (last 5 messages)
2717 - $history = get_option("mxchat_history_{$session_id}", []);
2718 - $recent_history = array_slice($history, -5); // Get last 5 messages
2719 -
2720 - // Format conversation history
2721 - $conversation_context = "";
2722 - if (!empty($recent_history)) {
2723 - $conversation_context = "*Recent Conversation:*\n";
2724 - foreach ($recent_history as $hist_message) {
2725 - $role_display = $hist_message['role'] === 'user' ? 'User' : 'AI';
2726 - $conversation_context .= ">{$role_display}: {$hist_message['content']}\n";
2727 - }
2728 - $conversation_context .= "\n";
2729 - }
2730 -
2731 2573 update_option("mxchat_mode_{$session_id}", 'agent');
2732 2574
2733 2575 $webhook_data = [
2734 2576 'blocks' => [
@@ -2744,53 +2586,38 @@
2744 2586 'type' => 'section',
2745 2587 'fields' => [
2746 2588 [
2747 2589 'type' => 'mrkdwn',
2748 - 'text' => sprintf('*User ID:*\n`%s`', $user_id)
2590 + 'text' => "*User ID:*\n`$user_id`"
2749 2591 ],
2750 2592 [
2751 2593 'type' => 'mrkdwn',
2752 - 'text' => sprintf('*Session ID:*\n`%s`', $session_id)
2594 + 'text' => "*Session ID:*\n`$session_id`"
2753 2595 ]
2754 2596 ]
2755 - ]
2756 - ]
2757 - ];
2758 -
2759 - // Add conversation history if exists
2760 - if (!empty($conversation_context)) {
2761 - $webhook_data['blocks'][] = [
2762 - 'type' => 'section',
2763 - 'text' => [
2764 - 'type' => 'mrkdwn',
2765 - 'text' => $conversation_context
2766 - ]
2767 - ];
2768 - }
2769 -
2770 - // Add the current message
2771 - $webhook_data['blocks'][] = [
2772 - 'type' => 'section',
2773 - 'text' => [
2774 - 'type' => 'mrkdwn',
2775 - 'text' => sprintf('*Current Message:*\n%s', $message)
2776 - ]
2777 - ];
2778 -
2779 - // Add the reply button
2780 - $webhook_data['blocks'][] = [
2781 - 'type' => 'actions',
2782 - 'elements' => [
2597 + ],
2783 2598 [
2784 - 'type' => 'button',
2599 + 'type' => 'section',
2785 2600 'text' => [
2786 - 'type' => 'plain_text',
2787 - 'text' => '✍️ Reply',
2788 - 'emoji' => true
2789 - ],
2790 - 'value' => $session_id,
2791 - 'action_id' => 'reply_to_user',
2792 - 'style' => 'primary'
2601 + 'type' => 'mrkdwn',
2602 + 'text' => "*Initial Message:*\n$message"
2603 + ]
2604 + ],
2605 + [
2606 + 'type' => 'actions',
2607 + 'elements' => [
2608 + [
2609 + 'type' => 'button',
2610 + 'text' => [
2611 + 'type' => 'plain_text',
2612 + 'text' => '✍️ Reply',
2613 + 'emoji' => true
2614 + ],
2615 + 'value' => $session_id,
2616 + 'action_id' => 'reply_to_user',
2617 + 'style' => 'primary'
2618 + ]
2619 + ]
2793 2620 ]
2794 2621 ]
2795 2622 ];
2796 2623
@@ -2801,12 +2628,16 @@
2801 2628 ],
2802 2629 ]);
2803 2630
2804 2631 if (is_wp_error($response)) {
2632 + //error_log('Error sending live agent handover: ' . $response->get_error_message());
2805 2633 return false;
2806 2634 }
2807 2635
2636 + //error_log('Live agent handover triggered successfully.');
2637 +
2808 2638 $success_message = $this->options['live_agent_notification_message'] ?? 'Live agent has been notified.';
2639 +
2809 2640 $this->mxchat_save_chat_message($session_id, 'bot', $success_message);
2810 2641
2811 2642 $this->fallbackResponse = [
2812 2643 'text' => $success_message,
@@ -2828,9 +2659,9 @@
2828 2659 public function mxchat_send_user_message_to_agent($message, $user_id, $session_id) {
2829 2660 $slack_webhook_url = $this->options['live_agent_webhook_url'] ?? '';
2830 2661
2831 2662 if (empty($slack_webhook_url)) {
2832 - //error_log(esc_html__('Slack Webhook URL is not configured.', 'mxchat'));
2663 + //error_log('Slack Webhook URL is not configured.');
2833 2664 return false;
2834 2665 }
2835 2666
2836 2667 $webhook_data = [
@@ -2838,9 +2669,9 @@
2838 2669 [
2839 2670 'type' => 'header',
2840 2671 'text' => [
2841 2672 'type' => 'plain_text',
2842 - 'text' => esc_html__('📩 New Chat Message', 'mxchat'),
2673 + 'text' => '📩 New Chat Message',
2843 2674 'emoji' => true
2844 2675 ]
2845 2676 ],
2846 2677 [
@@ -2847,13 +2678,13 @@
2847 2678 'type' => 'section',
2848 2679 'fields' => [
2849 2680 [
2850 2681 'type' => 'mrkdwn',
2851 - 'text' => sprintf(esc_html__('*User ID:*\n`%s`', 'mxchat'), $user_id)
2682 + 'text' => "*User ID:*\n`$user_id`"
2852 2683 ],
2853 2684 [
2854 2685 'type' => 'mrkdwn',
2855 - 'text' => sprintf(esc_html__('*Session ID:*\n`%s`', 'mxchat'), $session_id)
2686 + 'text' => "*Session ID:*\n`$session_id`"
2856 2687 ]
2857 2688 ]
2858 2689 ],
2859 2690 [
@@ -2859,9 +2690,9 @@
2859 2690 [
2860 2691 'type' => 'section',
2861 2692 'text' => [
2862 2693 'type' => 'mrkdwn',
2863 - 'text' => sprintf(esc_html__('*Message:*\n%s', 'mxchat'), $message)
2694 + 'text' => "*Message:*\n$message"
2864 2695 ]
2865 2696 ],
2866 2697 [
2867 2698 'type' => 'actions',
@@ -2869,9 +2700,9 @@
2869 2700 [
2870 2701 'type' => 'button',
2871 2702 'text' => [
2872 2703 'type' => 'plain_text',
2873 - 'text' => esc_html__('✍️ Reply', 'mxchat'),
2704 + 'text' => '✍️ Reply',
2874 2705 'emoji' => true
2875 2706 ],
2876 2707 'value' => $session_id,
2877 2708 'action_id' => 'reply_to_user',
@@ -2889,13 +2720,13 @@
2889 2720 ],
2890 2721 ]);
2891 2722
2892 2723 if (is_wp_error($response)) {
2893 - //error_log(esc_html__('Error sending message to Slack: ', 'mxchat') . $response->get_error_message());
2724 + //error_log('Error sending message to Slack: ' . $response->get_error_message());
2894 2725 return false;
2895 2726 }
2896 2727
2897 - //error_log(esc_html__('Message sent to Slack successfully.', 'mxchat'));
2728 + //error_log('Message sent to Slack successfully.');
2898 2729 return true;
2899 2730 }
2900 2731 public function handle_slack_interaction(WP_REST_Request $request) {
2901 2732 //error_log('Received Slack interaction');
@@ -2912,9 +2743,9 @@
2912 2743 $slack_token = $this->options['live_agent_bot_token'] ?? '';
2913 2744
2914 2745 if (empty($slack_token)) {
2915 2746 //error_log('Slack Bot Token not configured');
2916 - return new WP_REST_Response(['error' => esc_html__('Bot token not configured', 'mxchat')], 400);
2747 + return new WP_REST_Response(['error' => 'Bot token not configured'], 400);
2917 2748 }
2918 2749 $response = wp_remote_post('https://slack.com/api/views.open', [
2919 2750 'headers' => [
2920 2751 'Content-Type' => 'application/json',
@@ -2926,17 +2757,17 @@
2926 2757 'type' => 'modal',
2927 2758 'callback_id' => 'reply_modal',
2928 2759 'title' => [
2929 2760 'type' => 'plain_text',
2930 - 'text' => __('Reply to User', 'mxchat')
2761 + 'text' => 'Reply to User'
2931 2762 ],
2932 2763 'submit' => [
2933 2764 'type' => 'plain_text',
2934 - 'text' => __('Send', 'mxchat')
2765 + 'text' => 'Send'
2935 2766 ],
2936 2767 'close' => [
2937 2768 'type' => 'plain_text',
2938 - 'text' => __('Cancel', 'mxchat')
2769 + 'text' => 'Cancel'
2939 2770 ],
2940 2771 'blocks' => [
2941 2772 [
2942 2773 'type' => 'input',
@@ -2942,9 +2773,9 @@
2942 2773 'type' => 'input',
2943 2774 'block_id' => 'reply_block',
2944 2775 'label' => [
2945 2776 'type' => 'plain_text',
2946 - 'text' => sprintf(__('Reply to session: %s', 'mxchat'), $session_id)
2777 + 'text' => "Reply to session: $session_id"
2947 2778 ],
2948 2779 'element' => [
2949 2780 'type' => 'plain_text_input',
2950 2781 'action_id' => 'message',
@@ -2950,9 +2781,9 @@
2950 2781 'action_id' => 'message',
2951 2782 'multiline' => true,
2952 2783 'placeholder' => [
2953 2784 'type' => 'plain_text',
2954 - 'text' => __('Type your message here...', 'mxchat')
2785 + 'text' => 'Type your message here...'
2955 2786 ]
2956 2787 ]
2957 2788 ]
2958 2789 ],
@@ -2967,21 +2798,19 @@
2967 2798 return new WP_REST_Response(['ok' => true]);
2968 2799 }
2969 2800
2970 2801 // Handle modal submission
2971 -// Handle modal submission
2972 -if ($payload['type'] === 'view_submission') {
2973 - $session_id = $payload['view']['private_metadata'];
2974 - $message = $payload['view']['state']['values']['reply_block']['message']['value'];
2802 + if ($payload['type'] === 'view_submission') {
2803 + $session_id = $payload['view']['private_metadata'];
2804 + $message = $payload['view']['state']['values']['reply_block']['message']['value'];
2975 2805
2976 - // Save the message (keep the message_id but don't include in response)
2977 - $this->mxchat_save_chat_message($session_id, 'agent', $message);
2806 + // Save the message
2807 + $this->mxchat_save_chat_message($session_id, 'agent', $message);
2978 2808
2979 - // Keep the original response format for Slack
2980 - return new WP_REST_Response([
2981 - 'response_action' => 'clear'
2982 - ]);
2983 -}
2809 + return new WP_REST_Response([
2810 + 'response_action' => 'clear'
2811 + ]);
2812 + }
2984 2813
2985 2814 // Default acknowledgment
2986 2815 return new WP_REST_Response(['ok' => true]);
2987 2816 }
@@ -2995,11 +2824,11 @@
2995 2824 $command_text = $request->get_param('text');
2996 2825 // error_log('Command text: ' . $command_text);
2997 2826
2998 2827 if (empty($command_text)) {
2999 - //error_log(esc_html__('Agent response error: No command text received', 'mxchat'));
2828 + error_log('Agent response error: No command text received');
3000 2829 return new WP_REST_Response([
3001 - 'error' => esc_html__('Command text is required. Format: /reply session_id message', 'mxchat')
2830 + 'error' => 'Command text is required. Format: /reply session_id message'
3002 2831 ], 400);
3003 2832 }
3004 2833
3005 2834 // Split the command text into session_id and message
@@ -3006,9 +2835,9 @@
3006 2835 $parts = explode(' ', $command_text, 2);
3007 2836 if (count($parts) !== 2) {
3008 2837 //error_log('Agent response error: Invalid command format');
3009 2838 return new WP_REST_Response([
3010 - 'error' => esc_html__('Invalid format. Use: /reply session_id message', 'mxchat')
2839 + 'error' => 'Invalid format. Use: /reply session_id message'
3011 2840 ], 400);
3012 2841 }
3013 2842
3014 2843 $session_id = sanitize_text_field($parts[0]);
@@ -3021,9 +2850,9 @@
3021 2850
3022 2851 if (!$message_id) {
3023 2852 // error_log('Failed to save agent message');
3024 2853 return new WP_REST_Response([
3025 - 'error' => esc_html__('Failed to save message', 'mxchat')
2854 + 'error' => 'Failed to save message'
3026 2855 ], 500);
3027 2856 }
3028 2857
3029 2858 // Return success response in Slack's expected format
@@ -3028,15 +2857,15 @@
3028 2857
3029 2858 // Return success response in Slack's expected format
3030 2859 return new WP_REST_Response([
3031 2860 'response_type' => 'in_channel',
3032 - 'text' => esc_html__("Message sent successfully to session $session_id", 'mxchat')
2861 + 'text' => "Message sent successfully to session $session_id"
3033 2862 ], 200);
3034 2863 }
3035 2864
3036 2865
3037 2866 public function mxchat_handle_switch_to_chatbot_intent($message, $user_id, $session_id) {
3038 - //error_log(esc_html__("Switching back to chatbot mode via intent.", 'mxchat'));
2867 + //error_log("Switching back to chatbot mode via intent.");
3039 2868
3040 2869 // Just update mode to AI
3041 2870 update_option("mxchat_mode_{$session_id}", 'ai');
3042 2871
@@ -3044,9 +2873,9 @@
3044 2873 $this->fallbackResponse = ['text' => '', 'html' => '', 'images' => []];
3045 2874 $this->productCardHtml = '';
3046 2875
3047 2876 // Set the response message
3048 - $this->fallbackResponse['text'] = esc_html__('You are now chatting with the AI chatbot.', 'mxchat');
2877 + $this->fallbackResponse['text'] = 'You are now chatting with the AI chatbot.';
3049 2878
3050 2879 return true; // Intent was handled
3051 2880 }
3052 2881
@@ -3111,66 +2940,19 @@
3111 2940 return null;
3112 2941 }
3113 2942 }
3114 2943
3115 -
3116 2944 private function mxchat_find_relevant_content($user_embedding) {
3117 - //error_log('MXChat Vector Search: Starting content search...');
3118 -
3119 - // Retrieve the add-on settings from the database.
3120 - $addon_options = get_option('mxchat_pinecone_addon_options', array());
3121 -
3122 - // Determine whether Pinecone is enabled.
3123 - // We expect the sanitized setting to be a string '1' if enabled, otherwise '0'.
3124 - $use_pinecone = (isset($addon_options['mxchat_use_pinecone']) && $addon_options['mxchat_use_pinecone'] === '1') ? 1 : 0;
3125 -
3126 - //error_log('Pinecone enabled flag: ' . $use_pinecone);
3127 -
3128 - if ($use_pinecone === 1) {
3129 - //error_log('MXChat Vector Search: Using Pinecone database');
3130 - return $this->find_relevant_content_pinecone($user_embedding);
3131 - } else {
3132 - //error_log('MXChat Vector Search: Using WordPress database');
3133 - return $this->find_relevant_content_wordpress($user_embedding);
3134 - }
3135 -}
3136 -
3137 -private function find_relevant_content_wordpress($user_embedding) {
3138 2945 global $wpdb;
3139 2946 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
3140 2947 $cache_key = 'mxchat_system_prompt_embeddings';
3141 - $batch_size = 500;
3142 2948
3143 2949 // Retrieve embeddings from cache or database
3144 2950 $embeddings = wp_cache_get($cache_key, 'mxchat_system_prompts');
3145 2951 if ($embeddings === false) {
3146 - $embeddings = [];
3147 - $offset = 0;
3148 -
3149 - // Load in batches and build cache
3150 - do {
3151 - $query = $wpdb->prepare(
3152 - "SELECT id, embedding_vector
3153 - FROM {$system_prompt_table}
3154 - LIMIT %d OFFSET %d",
3155 - $batch_size,
3156 - $offset
3157 - );
3158 -
3159 - $batch = $wpdb->get_results($query);
3160 - if (empty($batch)) {
3161 - break;
3162 - }
3163 -
3164 - $embeddings = array_merge($embeddings, $batch);
3165 - $offset += $batch_size;
3166 -
3167 - // Free memory
3168 - unset($batch);
3169 -
3170 - } while (true);
3171 -
3172 - if (empty($embeddings)) {
2952 + $query = "SELECT id, embedding_vector FROM {$system_prompt_table}";
2953 + $embeddings = $wpdb->get_results($query);
2954 + if ($embeddings === null || empty($embeddings)) {
3173 2955 return ''; // Return an empty string if no embeddings found
3174 2956 }
3175 2957 wp_cache_set($cache_key, $embeddings, 'mxchat_system_prompts', 3600);
3176 2958 }
@@ -3176,13 +2958,15 @@
3176 2958 }
3177 2959
3178 2960 // Initialize array to store relevant results with similarity scores
3179 2961 $relevant_results = [];
2962 +
3180 2963 // Iterate through embeddings to calculate similarity
3181 2964 foreach ($embeddings as $embedding) {
3182 2965 $database_embedding = $embedding->embedding_vector
3183 2966 ? unserialize($embedding->embedding_vector, ['allowed_classes' => false])
3184 2967 : null;
2968 +
3185 2969 if (is_array($database_embedding) && is_array($user_embedding)) {
3186 2970 $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $database_embedding);
3187 2971 $relevant_results[] = [
3188 2972 'id' => $embedding->id,
@@ -3188,10 +2972,8 @@
3188 2972 'id' => $embedding->id,
3189 2973 'similarity' => $similarity
3190 2974 ];
3191 2975 }
3192 - // Free memory
3193 - unset($database_embedding);
3194 2976 }
3195 2977
3196 2978 // Retrieve the similarity threshold
3197 2979 $similarity_threshold = ((int) get_option('mxchat_similarity_threshold', 80)) / 100;
@@ -3212,8 +2994,9 @@
3212 2994
3213 2995 // Fetch and combine content for the top results
3214 2996 foreach ($top_results as $result) {
3215 2997 $chunk_content = $this->fetch_content_with_product_links($result['id']);
2998 +
3216 2999 // Check if the content is PDF-related and add surrounding pages
3217 3000 if (strpos($chunk_content, '{"document_type":"pdf"') !== false) {
3218 3001 $surrounding_content = $wpdb->get_results($wpdb->prepare(
3219 3002 "SELECT article_content FROM {$system_prompt_table}
@@ -3223,14 +3006,17 @@
3223 3006 )",
3224 3007 $result['id'],
3225 3008 $result['id']
3226 3009 ));
3010 +
3227 3011 // Add previous content if it exists
3228 3012 if (!empty($surrounding_content[0])) {
3229 3013 $content .= $surrounding_content[0]->article_content . "\n\n";
3230 3014 }
3015 +
3231 3016 // Add the main chunk content
3232 3017 $content .= $chunk_content . "\n\n";
3018 +
3233 3019 // Add next content if it exists
3234 3020 if (!empty($surrounding_content[1])) {
3235 3021 $content .= $surrounding_content[1]->article_content . "\n\n";
3236 3022 }
@@ -3239,148 +3025,36 @@
3239 3025 $content .= $chunk_content . "\n\n";
3240 3026 }
3241 3027 }
3242 3028
3243 - return trim($content);
3029 + return trim($content); // Return the combined content
3244 3030 }
3245 -/**
3246 - * Find relevant content in Pinecone vector database
3247 - */
3248 -private function find_relevant_content_pinecone($user_embedding) {
3249 - $options = get_option('mxchat_pinecone_addon_options', array());
3250 - $api_key = $options['mxchat_pinecone_api_key'] ?? '';
3251 - $host = $options['mxchat_pinecone_host'] ?? '';
3252 3031
3253 - if (empty($host) || empty($api_key)) {
3254 - //error_log('Pinecone credentials not properly configured');
3255 - return '';
3256 - }
3257 -
3258 - // Get similarity threshold from WordPress settings
3259 - $similarity_threshold = ((int) get_option('mxchat_similarity_threshold', 80)) / 100;
3260 -
3261 - // Prepare the query request for Pinecone
3262 - $api_endpoint = "https://{$host}/query";
3263 -
3264 - $request_body = array(
3265 - 'vector' => $user_embedding,
3266 - 'topK' => 5,
3267 - 'includeMetadata' => true,
3268 - 'includeValues' => true
3269 - );
3270 -
3271 - $response = wp_remote_post($api_endpoint, array(
3272 - 'headers' => array(
3273 - 'Api-Key' => $api_key,
3274 - 'accept' => 'application/json',
3275 - 'content-type' => 'application/json'
3276 - ),
3277 - 'body' => wp_json_encode($request_body),
3278 - 'timeout' => 30
3279 - ));
3280 -
3281 - if (is_wp_error($response)) {
3282 - //error_log('Pinecone query error: ' . $response->get_error_message());
3283 - return '';
3284 - }
3285 -
3286 - $response_code = wp_remote_retrieve_response_code($response);
3287 - if ($response_code !== 200) {
3288 - //error_log('Pinecone API error: ' . wp_remote_retrieve_body($response));
3289 - return '';
3290 - }
3291 -
3292 - $results = json_decode(wp_remote_retrieve_body($response), true);
3293 - if (empty($results['matches'])) {
3294 - return '';
3295 - }
3296 -
3297 - // Initialize the final content
3298 - $content = '';
3299 -
3300 - // Process each match
3301 - foreach ($results['matches'] as $match) {
3302 - // Skip if similarity is below threshold
3303 - if ($match['score'] < $similarity_threshold) {
3304 - continue;
3305 - }
3306 -
3307 - if (!empty($match['metadata']['text']) && !empty($match['metadata']['source_url'])) {
3308 - // Add content with citation
3309 - $content .= $match['metadata']['text'] . "\n";
3310 - $content .= "Source: " . $match['metadata']['source_url'] . "\n\n";
3311 - }
3312 - }
3313 -
3314 - return trim($content);
3315 -}
3316 -
3317 -
3318 3032 private function mxchat_find_relevant_products($user_embedding) {
3319 - //error_log('MXChat Vector Search: Starting product search...');
3320 -
3321 - // Retrieve the add-on settings from the database
3322 - $addon_options = get_option('mxchat_pinecone_addon_options', array());
3323 -
3324 - // Determine whether Pinecone is enabled
3325 - $use_pinecone = (isset($addon_options['mxchat_use_pinecone']) && $addon_options['mxchat_use_pinecone'] === '1') ? 1 : 0;
3326 -
3327 - //error_log('Pinecone enabled flag: ' . $use_pinecone);
3328 -
3329 - if ($use_pinecone === 1) {
3330 - //error_log('MXChat Vector Search: Using Pinecone database for products');
3331 - return $this->find_relevant_products_pinecone($user_embedding);
3332 - } else {
3333 - //error_log('MXChat Vector Search: Using WordPress database for products');
3334 - return $this->find_relevant_products_wordpress($user_embedding);
3335 - }
3336 -}
3337 -
3338 -private function find_relevant_products_wordpress($user_embedding) {
3339 3033 global $wpdb;
3340 3034 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
3341 3035 $cache_key = 'mxchat_system_prompt_embeddings';
3342 - $batch_size = 500;
3343 3036
3344 - // Original WordPress database search logic
3345 - // [Previous implementation remains the same]
3037 + // Retrieve embeddings from cache or database
3346 3038 $embeddings = wp_cache_get($cache_key, 'mxchat_system_prompts');
3347 3039 if ($embeddings === false) {
3348 - $embeddings = [];
3349 - $offset = 0;
3350 -
3351 - do {
3352 - $query = $wpdb->prepare(
3353 - "SELECT id, embedding_vector
3354 - FROM {$system_prompt_table}
3355 - LIMIT %d OFFSET %d",
3356 - $batch_size,
3357 - $offset
3358 - );
3359 -
3360 - $batch = $wpdb->get_results($query);
3361 - if (empty($batch)) {
3362 - break;
3363 - }
3364 -
3365 - $embeddings = array_merge($embeddings, $batch);
3366 - $offset += $batch_size;
3367 -
3368 - unset($batch);
3369 -
3370 - } while (true);
3371 -
3372 - if (empty($embeddings)) {
3373 - return '';
3040 + $query = "SELECT id, embedding_vector FROM {$system_prompt_table}";
3041 + $embeddings = $wpdb->get_results($query);
3042 + if ($embeddings === null || empty($embeddings)) {
3043 + return ''; // Return an empty string if no embeddings found
3374 3044 }
3375 3045 wp_cache_set($cache_key, $embeddings, 'mxchat_system_prompts', 3600);
3376 3046 }
3377 3047
3048 + // Initialize array to store relevant results with similarity scores
3378 3049 $relevant_results = [];
3050 +
3051 + // Iterate through embeddings to calculate similarity
3379 3052 foreach ($embeddings as $embedding) {
3380 3053 $database_embedding = $embedding->embedding_vector
3381 3054 ? unserialize($embedding->embedding_vector, ['allowed_classes' => false])
3382 3055 : null;
3056 +
3383 3057 if (is_array($database_embedding) && is_array($user_embedding)) {
3384 3058 $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $database_embedding);
3385 3059 $relevant_results[] = [
3386 3060 'id' => $embedding->id,
@@ -3386,14 +3060,14 @@
3386 3060 'id' => $embedding->id,
3387 3061 'similarity' => $similarity
3388 3062 ];
3389 3063 }
3390 - unset($database_embedding);
3391 3064 }
3392 3065
3393 - // Use fixed threshold for products
3066 +// Use a fixed similarity threshold of 0.85
3394 3067 $similarity_threshold = 0.85;
3395 3068
3069 + // Filter and sort relevant results by similarity
3396 3070 $relevant_results = array_filter($relevant_results, function ($result) use ($similarity_threshold) {
3397 3071 return $result['similarity'] >= $similarity_threshold;
3398 3072 });
3399 3073 usort($relevant_results, function ($a, $b) {
@@ -3399,98 +3073,51 @@
3399 3073 usort($relevant_results, function ($a, $b) {
3400 3074 return $b['similarity'] <=> $a['similarity'];
3401 3075 });
3402 3076
3077 + // Limit to the top 5 results
3403 3078 $top_results = array_slice($relevant_results, 0, 5);
3079 +
3080 + // Initialize the final content
3404 3081 $content = '';
3405 3082
3083 + // Fetch and combine content for the top results
3406 3084 foreach ($top_results as $result) {
3407 3085 $chunk_content = $this->fetch_content_with_product_links($result['id']);
3408 - $content .= $chunk_content . "\n\n";
3409 - }
3410 3086
3411 - return trim($content);
3412 -}
3087 + // Check if the content is PDF-related and add surrounding pages
3088 + if (strpos($chunk_content, '{"document_type":"pdf"') !== false) {
3089 + $surrounding_content = $wpdb->get_results($wpdb->prepare(
3090 + "SELECT article_content FROM {$system_prompt_table}
3091 + WHERE id IN (
3092 + (SELECT id FROM {$system_prompt_table} WHERE id < %d ORDER BY id DESC LIMIT 1),
3093 + (SELECT id FROM {$system_prompt_table} WHERE id > %d ORDER BY id ASC LIMIT 1)
3094 + )",
3095 + $result['id'],
3096 + $result['id']
3097 + ));
3413 3098
3414 -// Modified search function with correct filter syntax
3415 -private function find_relevant_products_pinecone($user_embedding) {
3416 - //error_log('Starting Pinecone product search...');
3099 + // Add previous content if it exists
3100 + if (!empty($surrounding_content[0])) {
3101 + $content .= $surrounding_content[0]->article_content . "\n\n";
3102 + }
3417 3103
3418 - $options = get_option('mxchat_pinecone_addon_options', array());
3419 - $api_key = $options['mxchat_pinecone_api_key'] ?? '';
3420 - $host = $options['mxchat_pinecone_host'] ?? '';
3104 + // Add the main chunk content
3105 + $content .= $chunk_content . "\n\n";
3421 3106
3422 - if (empty($host) || empty($api_key)) {
3423 - //error_log('Pinecone credentials not properly configured for product search');
3424 - return '';
3425 - }
3426 -
3427 - $similarity_threshold = 0.85;
3428 - $api_endpoint = "https://{$host}/query";
3429 -
3430 - $request_body = array(
3431 - 'vector' => $user_embedding,
3432 - 'topK' => 5,
3433 - 'includeMetadata' => true,
3434 - 'includeValues' => true,
3435 - 'filter' => array(
3436 - 'type' => 'product'
3437 - )
3438 - );
3439 -
3440 - //error_log('Sending request to Pinecone with body: ' . wp_json_encode($request_body));
3441 -
3442 - $response = wp_remote_post($api_endpoint, array(
3443 - 'headers' => array(
3444 - 'Api-Key' => $api_key,
3445 - 'accept' => 'application/json',
3446 - 'content-type' => 'application/json'
3447 - ),
3448 - 'body' => wp_json_encode($request_body),
3449 - 'timeout' => 30
3450 - ));
3451 -
3452 - if (is_wp_error($response)) {
3453 - //error_log('Pinecone product query error: ' . $response->get_error_message());
3454 - return '';
3455 - }
3456 -
3457 - $response_code = wp_remote_retrieve_response_code($response);
3458 - //error_log('Pinecone response code: ' . $response_code);
3459 -
3460 - if ($response_code !== 200) {
3461 - //error_log('Pinecone API error during product search: ' . wp_remote_retrieve_body($response));
3462 - return '';
3463 - }
3464 -
3465 - $results = json_decode(wp_remote_retrieve_body($response), true);
3466 - //error_log('Pinecone raw response: ' . wp_remote_retrieve_body($response));
3467 -
3468 - if (empty($results['matches'])) {
3469 - //error_log('No matches found in Pinecone response');
3470 - return '';
3471 - }
3472 -
3473 - $content = '';
3474 - foreach ($results['matches'] as $match) {
3475 - if ($match['score'] < $similarity_threshold) {
3476 - //error_log("Match below threshold: " . $match['score']);
3477 - continue;
3478 - }
3479 -
3480 - if (!empty($match['metadata']['text'])) {
3481 - $content .= $match['metadata']['text'];
3482 - if (!empty($match['metadata']['source_url'])) {
3483 - $content .= "\n\nFor more details, check out this product: " . esc_url($match['metadata']['source_url']);
3107 + // Add next content if it exists
3108 + if (!empty($surrounding_content[1])) {
3109 + $content .= $surrounding_content[1]->article_content . "\n\n";
3484 3110 }
3485 - $content .= "\n\n";
3111 + } else {
3112 + // For non-PDF content, add directly
3113 + $content .= $chunk_content . "\n\n";
3486 3114 }
3487 3115 }
3488 3116
3489 - return trim($content);
3117 + return trim($content); // Return the combined content
3490 3118 }
3491 3119
3492 -
3493 3120 private function fetch_content_with_product_links($most_relevant_id) {
3494 3121 global $wpdb;
3495 3122 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
3496 3123
@@ -3509,216 +3136,76 @@
3509 3136
3510 3137 return null;
3511 3138 }
3512 3139
3513 -// Function definition
3514 -private function mxchat_generate_response($relevant_content, $api_key, $xai_api_key, $claude_api_key, $deepseek_api_key, $conversation_history) {
3515 - try {
3516 - if (!$relevant_content) {
3517 - return esc_html__("I'm sorry, I couldn't find relevant information on that topic.", 'mxchat');
3518 - }
3519 -
3520 - // Ensure conversation_history is an array
3521 - if (!is_array($conversation_history)) {
3522 - $conversation_history = array();
3523 - }
3524 -
3525 - // Get selected model with default fallback
3526 - $selected_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-4o';
3527 -
3528 - // Extract model prefix to determine the provider
3529 - $model_parts = explode('-', $selected_model);
3530 - $provider = strtolower($model_parts[0]);
3531 -
3532 - // Handle model selection based on provider prefix
3533 - switch ($provider) {
3534 - case 'claude':
3535 - if (empty($claude_api_key)) {
3536 - throw new Exception(esc_html__('Claude API key is not configured', 'mxchat'));
3537 - }
3538 - return $this->mxchat_generate_response_claude(
3539 - $selected_model,
3540 - $claude_api_key,
3541 - $conversation_history,
3542 - $relevant_content
3543 - );
3544 -
3545 - case 'grok':
3546 - if (empty($xai_api_key)) {
3547 - throw new Exception(esc_html__('X.AI API key is not configured', 'mxchat'));
3548 - }
3549 - return $this->mxchat_generate_response_xai(
3550 - $selected_model,
3551 - $xai_api_key,
3552 - $conversation_history,
3553 - $relevant_content
3554 - );
3555 -
3556 - case 'deepseek':
3557 - if (empty($deepseek_api_key)) {
3558 - throw new Exception(esc_html__('DeepSeek API key is not configured', 'mxchat'));
3559 - }
3560 - return $this->mxchat_generate_response_deepseek(
3561 - $selected_model,
3562 - $deepseek_api_key,
3563 - $conversation_history,
3564 - $relevant_content
3565 - );
3566 -
3567 - case 'gpt':
3568 - if (empty($api_key)) {
3569 - throw new Exception(esc_html__('OpenAI API key is not configured', 'mxchat'));
3570 - }
3571 - return $this->mxchat_generate_response_openai(
3572 - $selected_model,
3573 - $api_key,
3574 - $conversation_history,
3575 - $relevant_content
3576 - );
3577 -
3578 - default:
3579 - // Default to OpenAI for custom models or unrecognized prefixes
3580 - if (empty($api_key)) {
3581 - throw new Exception(esc_html__('OpenAI API key is not configured', 'mxchat'));
3582 - }
3583 - return $this->mxchat_generate_response_openai(
3584 - $selected_model,
3585 - $api_key,
3586 - $conversation_history,
3587 - $relevant_content
3588 - );
3589 - }
3590 - } catch (Exception $e) {
3591 - //error_log('MXChat Error: ' . $e->getMessage());
3592 - return sprintf(
3593 - esc_html__('An error occurred: %s', 'mxchat'),
3594 - esc_html($e->getMessage())
3595 - );
3140 +private function mxchat_generate_response($relevant_content, $api_key, $xai_api_key, $claude_api_key, $conversation_history) {
3141 + if (!$relevant_content) {
3142 + return "I'm sorry, I couldn't find relevant information on that topic.";
3596 3143 }
3597 -}
3598 3144
3145 + // Check the selected model
3146 + $selected_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5-turbo';
3599 3147
3600 -private function mxchat_generate_response_deepseek($selected_model, $deepseek_api_key, $conversation_history, $relevant_content) {
3601 - // Ensure conversation_history is an array
3602 - if (!is_array($conversation_history)) {
3603 - $conversation_history = array();
3604 - }
3605 -
3606 - // Get system prompt instructions from options
3607 - $system_prompt_instructions = isset($this->options['system_prompt_instructions']) ? $this->options['system_prompt_instructions'] : '';
3608 -
3609 - // Create a new array for the formatted conversation
3610 - $formatted_conversation = array();
3611 -
3612 - // Add system message first
3613 - $formatted_conversation[] = array(
3614 - 'role' => 'system',
3615 - 'content' => $system_prompt_instructions . " " . $relevant_content
3616 - );
3617 -
3618 - // Add the rest of the conversation history
3619 - foreach ($conversation_history as $message) {
3620 - if (is_array($message) && isset($message['role']) && isset($message['content'])) {
3621 - $role = $message['role'];
3622 -
3623 - // Convert roles to supported format
3624 - if ($role === 'bot' || $role === 'agent') {
3625 - $role = 'assistant';
3626 - }
3627 - if (!in_array($role, ['system', 'assistant', 'user', 'function', 'tool'])) {
3628 - $role = 'user';
3629 - }
3630 -
3631 - $formatted_conversation[] = array(
3632 - 'role' => $role,
3633 - 'content' => $message['content']
3634 - );
3635 - }
3636 - }
3637 -
3638 - $body = json_encode([
3639 - 'model' => $selected_model,
3640 - 'messages' => $formatted_conversation,
3641 - 'temperature' => 0.8,
3642 - 'stream' => false
3643 - ]);
3644 -
3645 - $args = [
3646 - 'body' => $body,
3647 - 'headers' => [
3648 - 'Content-Type' => 'application/json',
3649 - 'Authorization' => 'Bearer ' . $deepseek_api_key,
3650 - ],
3651 - 'timeout' => 60,
3652 - 'redirection' => 5,
3653 - 'blocking' => true,
3654 - 'httpversion' => '1.0',
3655 - 'sslverify' => true,
3656 - ];
3657 -
3658 - $response = wp_remote_post('https://api.deepseek.com/v1/chat/completions', $args);
3659 -
3660 - if (is_wp_error($response)) {
3661 - //error_log('DeepSeek API Error: ' . $response->get_error_message());
3662 - return "Sorry, there was an error processing your request.";
3663 - }
3664 -
3665 - $response_body = wp_remote_retrieve_body($response);
3666 - $decoded_response = json_decode($response_body, true);
3667 -
3668 - if (isset($decoded_response['choices'][0]['message']['content'])) {
3669 - return trim($decoded_response['choices'][0]['message']['content']);
3148 + // Call the appropriate function based on the selected model
3149 + if (strpos($selected_model, 'claude') !== false) {
3150 + return $this->mxchat_generate_response_claude($selected_model, $claude_api_key, $conversation_history, $relevant_content);
3151 + } elseif ($selected_model === 'grok-beta') {
3152 + return $this->mxchat_generate_response_xai($selected_model, $xai_api_key, $conversation_history, $relevant_content);
3670 3153 } else {
3671 - //error_log('DeepSeek API Response Format Error: ' . print_r($decoded_response, true));
3672 - return "Sorry, I couldn't process that request.";
3154 + return $this->mxchat_generate_response_openai($selected_model, $api_key, $conversation_history, $relevant_content);
3673 3155 }
3674 3156 }
3675 3157
3676 3158 private function mxchat_generate_response_openai($selected_model, $api_key, $conversation_history, $relevant_content) {
3677 - // Ensure conversation_history is an array
3678 - if (!is_array($conversation_history)) {
3679 - $conversation_history = array();
3680 - }
3681 -
3682 3159 // Get system prompt instructions from options
3683 3160 $system_prompt_instructions = isset($this->options['system_prompt_instructions']) ? $this->options['system_prompt_instructions'] : '';
3684 3161
3685 - // Create a new array for the formatted conversation
3686 - $formatted_conversation = array();
3162 + // Add system prompt to relevant content
3163 + $content_with_instructions = $system_prompt_instructions . " " . $relevant_content;
3687 3164
3688 - // Add system message first
3689 - $formatted_conversation[] = array(
3165 + // Prepend system instructions to the conversation history
3166 + array_unshift($conversation_history, [
3690 3167 'role' => 'system',
3691 - 'content' => $system_prompt_instructions . " " . $relevant_content
3692 - );
3168 + 'content' => "Here are your instructions: " . $content_with_instructions
3169 + ]);
3693 3170
3694 - // Add the rest of the conversation history
3695 - foreach ($conversation_history as $message) {
3696 - if (is_array($message) && isset($message['role']) && isset($message['content'])) {
3697 - $role = $message['role'];
3171 + // Log the system prompt and relevant content
3172 + //error_log("System Prompt Instructions: " . $system_prompt_instructions);
3173 + //error_log("Relevant Content: " . substr($relevant_content, 0, 500)); // Log first 500 characters for brevity
3698 3174
3699 - // Convert roles to supported format
3700 - if ($role === 'bot' || $role === 'agent') {
3701 - $role = 'assistant';
3175 + // Ensure consistency: Replace 'bot' and 'agent' roles with supported values
3176 + foreach ($conversation_history as &$message) {
3177 + if ($message['role'] === 'bot') {
3178 + $message['role'] = 'assistant';
3179 + } elseif ($message['role'] === 'agent') {
3180 + // Tag the message as coming from a live agent
3181 + $message['role'] = 'assistant';
3182 + if (!isset($message['metadata'])) {
3183 + $message['metadata'] = ['source' => 'live_agent'];
3702 3184 }
3703 - if (!in_array($role, ['system', 'assistant', 'user', 'function', 'tool'])) {
3704 - $role = 'user';
3705 - }
3185 + }
3706 3186
3707 - $formatted_conversation[] = array(
3708 - 'role' => $role,
3709 - 'content' => $message['content']
3710 - );
3187 + // Ensure all roles are valid
3188 + if (!in_array($message['role'], ['system', 'assistant', 'user', 'function', 'tool'])) {
3189 + $message['role'] = 'user'; // Default to 'user'
3711 3190 }
3712 3191 }
3713 3192
3193 + // Log the formatted conversation history
3194 + //error_log("Conversation History: " . json_encode($conversation_history, JSON_PRETTY_PRINT));
3195 +
3196 + // Build the request body
3714 3197 $body = json_encode([
3715 3198 'model' => $selected_model,
3716 - 'messages' => $formatted_conversation,
3199 + 'messages' => $conversation_history,
3717 3200 'temperature' => 0.8,
3718 3201 'stream' => false
3719 3202 ]);
3720 3203
3204 + // Log the full API request body
3205 + //error_log("OpenAI API Request Body: " . $body);
3206 +
3207 + // Set up the API request
3721 3208 $args = [
3722 3209 'body' => $body,
3723 3210 'headers' => [
3724 3211 'Content-Type' => 'application/json',
@@ -3730,25 +3217,37 @@
3730 3217 'httpversion' => '1.0',
3731 3218 'sslverify' => true,
3732 3219 ];
3733 3220
3221 + // Make the API request
3734 3222 $response = wp_remote_post('https://api.openai.com/v1/chat/completions', $args);
3735 3223
3224 + // Log the response or error
3736 3225 if (is_wp_error($response)) {
3737 - //error_log('OpenAI API Error: ' . $response->get_error_message());
3226 + //error_log("OpenAI API Error: " . $response->get_error_message());
3738 3227 return "Sorry, there was an error processing your request.";
3739 3228 }
3740 3229
3230 + // Log the raw API response
3231 + //error_log("OpenAI API Raw Response: " . print_r($response, true));
3232 +
3741 3233 $response_body = wp_remote_retrieve_body($response);
3742 3234 $decoded_response = json_decode($response_body, true);
3743 3235
3236 + // Log the decoded API response
3237 + error_log("OpenAI API Decoded Response: " . json_encode($decoded_response, JSON_PRETTY_PRINT));
3238 +
3744 3239 if (isset($decoded_response['choices'][0]['message']['content'])) {
3745 - return trim($decoded_response['choices'][0]['message']['content']);
3240 + $response_text = trim($decoded_response['choices'][0]['message']['content']);
3241 + //error_log("OpenAI API Final Response: " . $response_text);
3242 + return $response_text;
3746 3243 } else {
3747 - //error_log('OpenAI API Response Format Error: ' . print_r($decoded_response, true));
3244 + // Log an error if the expected response format is missing
3245 + //error_log("OpenAI API Response Format Error: Expected 'choices[0][message][content]' not found.");
3748 3246 return "Sorry, I couldn't process that request.";
3749 3247 }
3750 3248 }
3249 +
3751 3250 private function mxchat_generate_response_xai($selected_model, $xai_api_key, $conversation_history, $relevant_content) {
3752 3251 // Get system prompt instructions from options
3753 3252 $system_prompt_instructions = isset($this->options['system_prompt_instructions']) ? $this->options['system_prompt_instructions'] : '';
3754 3253
@@ -3819,107 +3318,83 @@
3819 3318 }
3820 3319 }
3821 3320
3822 3321 private function mxchat_generate_response_claude($selected_model, $claude_api_key, $conversation_history, $relevant_content) {
3823 - // Get system prompt instructions from options
3322 + // Get system prompt instructions from options for Claude's top-level system parameter
3824 3323 $system_prompt_instructions = isset($this->options['system_prompt_instructions']) ? $this->options['system_prompt_instructions'] : '';
3825 3324
3826 - // Clean and validate conversation history
3325 + // Ensure consistency: Replace 'bot' and 'agent' roles with supported values
3827 3326 foreach ($conversation_history as &$message) {
3828 - // Convert bot and agent roles to assistant
3829 - if ($message['role'] === 'bot' || $message['role'] === 'agent') {
3327 + if ($message['role'] === 'bot') {
3830 3328 $message['role'] = 'assistant';
3329 + } elseif ($message['role'] === 'agent') {
3330 + // Tag the message as coming from a live agent
3331 + $message['role'] = 'assistant';
3332 + if (!isset($message['metadata'])) {
3333 + $message['metadata'] = ['source' => 'live_agent'];
3334 + }
3831 3335 }
3832 -
3833 - // Remove unsupported roles - Claude only supports 'assistant' and 'user'
3834 - if (!in_array($message['role'], ['assistant', 'user'])) {
3835 - $message['role'] = 'user';
3836 - }
3837 3336
3838 - // Ensure content field exists
3839 - if (!isset($message['content']) || empty($message['content'])) {
3840 - $message['content'] = '';
3337 + // Ensure all roles are valid
3338 + if (!in_array($message['role'], ['system', 'assistant', 'user', 'function', 'tool'])) {
3339 + $message['role'] = 'user'; // Default to 'user'
3841 3340 }
3842 -
3843 - // Remove any unsupported fields
3844 - $message = array_intersect_key($message, array_flip(['role', 'content']));
3845 3341 }
3846 3342
3847 - // Add relevant content as the latest user message
3343 + // Add relevant content as the latest user message in conversation history
3848 3344 $conversation_history[] = [
3849 3345 'role' => 'user',
3850 3346 'content' => $relevant_content
3851 3347 ];
3852 3348
3853 - // Build request body
3349 + // Build the request body with Claude's expected structure, using system instructions as a top-level parameter
3854 3350 $body = json_encode([
3855 3351 'model' => $selected_model,
3856 3352 'max_tokens' => 1000,
3857 3353 'temperature' => 0.8,
3858 - 'messages' => $conversation_history,
3859 - 'system' => $system_prompt_instructions
3354 + 'system' => $system_prompt_instructions, // Set the system prompt at the top level as required
3355 + 'messages' => $conversation_history
3860 3356 ]);
3861 3357
3862 - // Set up API request
3358 + // Set up the API request with the necessary headers
3863 3359 $args = [
3864 - 'body' => $body,
3865 - 'headers' => [
3866 - 'Content-Type' => 'application/json',
3867 - 'x-api-key' => $claude_api_key,
3868 - 'anthropic-version' => '2023-06-01'
3869 - ],
3870 - 'timeout' => 60,
3360 + 'body' => $body,
3361 + 'headers' => [
3362 + 'Content-Type' => 'application/json',
3363 + 'x-api-key' => $claude_api_key,
3364 + 'anthropic-version' => '2023-06-01',
3365 + ],
3366 + 'timeout' => 60,
3871 3367 'redirection' => 5,
3872 - 'blocking' => true,
3368 + 'blocking' => true,
3873 3369 'httpversion' => '1.0',
3874 - 'sslverify' => true,
3370 + 'sslverify' => true,
3875 3371 ];
3876 3372
3877 - // Make API request
3373 + // Make the API request
3878 3374 $response = wp_remote_post('https://api.anthropic.com/v1/messages', $args);
3879 3375
3880 - // Check for WordPress errors
3376 +/*
3377 + // Check for errors and log the response for debugging
3881 3378 if (is_wp_error($response)) {
3882 - //error_log("Claude API request error: " . $response->get_error_message());
3883 - return "Sorry, there was an error connecting to the API.";
3379 + error_log("Claude API request error: " . print_r($response->get_error_message(), true));
3380 + return "Sorry, there was an error processing your request.";
3884 3381 }
3382 +*/
3885 3383
3886 - // Check HTTP response code
3887 - $http_code = wp_remote_retrieve_response_code($response);
3888 - if ($http_code !== 200) {
3889 - $error_body = wp_remote_retrieve_body($response);
3890 - //error_log("Claude API HTTP error: " . $http_code . " - " . $error_body);
3891 -
3892 - // Try to extract error message from response
3893 - $error_data = json_decode($error_body, true);
3894 - $error_message = isset($error_data['error']['message']) ?
3895 - $error_data['error']['message'] :
3896 - "HTTP error " . $http_code;
3897 -
3898 - return "Sorry, the API returned an error: " . $error_message;
3899 - }
3900 3384
3901 - // Parse response
3385 + // Decode the response and parse according to the expected Claude response structure
3902 3386 $response_body = json_decode(wp_remote_retrieve_body($response), true);
3903 -
3904 - // Check for JSON decode errors
3905 - if (json_last_error() !== JSON_ERROR_NONE) {
3906 - //error_log("Claude API JSON decode error: " . json_last_error_msg());
3907 - return "Sorry, there was an error processing the API response.";
3908 - }
3387 + //error_log("Claude API response: " . print_r($response_body, true));
3909 3388
3910 - // Extract and validate response content
3911 - if (isset($response_body['content']) &&
3912 - is_array($response_body['content']) &&
3913 - !empty($response_body['content']) &&
3914 - isset($response_body['content'][0]['text'])) {
3389 + // Check if the response has the expected 'content' array with 'text' blocks
3390 + if (isset($response_body['content'][0]['text'])) {
3915 3391 return trim($response_body['content'][0]['text']);
3392 + } else {
3393 + return "Sorry, I couldn't process that request.";
3916 3394 }
3395 +}
3917 3396
3918 - // Log unexpected response format
3919 - //error_log("Claude API unexpected response format: " . print_r($response_body, true));
3920 - return "Sorry, I received an unexpected response format from the API.";
3921 -}
3922 3397 public function mxchat_dismiss_pre_chat_message() {
3923 3398 // Get and sanitize the user identifier
3924 3399 $user_id = $this->mxchat_get_user_identifier();
3925 3400 $user_id = sanitize_key($user_id);
@@ -3975,10 +3450,10 @@
3975 3450 }
3976 3451
3977 3452 public function mxchat_enqueue_scripts_styles() {
3978 3453 // Define version numbers for the styles and scripts
3979 - $chat_style_version = '2.0.3'; // Replace with your actual version
3980 - $chat_script_version = '2.0.3'; // Replace with your actual version
3454 + $chat_style_version = '1.5.8'; // Replace with your actual version
3455 + $chat_script_version = '1.5.8'; // Replace with your actual version
3981 3456
3982 3457 // Enqueue the script
3983 3458 wp_enqueue_script(
3984 3459 'mxchat-chat-js',
@@ -3997,9 +3472,8 @@
3997 3472 );
3998 3473
3999 3474 // Fetch options from the database
4000 3475 $this->options = get_option('mxchat_options');
4001 - $prompts_options = get_option('mxchat_prompts_options', array());
4002 3476
4003 3477 // Prepare settings for JavaScript
4004 3478 $style_settings = array(
4005 3479 'ajax_url' => admin_url('admin-ajax.php'),
@@ -4026,11 +3500,8 @@
4026 3500 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
4027 3501 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
4028 3502 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
4029 3503 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
4030 -
4031 - 'use_pinecone' => $prompts_options['mxchat_use_pinecone'] ?? '0',
4032 - 'pinecone_enabled' => isset($prompts_options['mxchat_use_pinecone']) && $prompts_options['mxchat_use_pinecone'] === '1'
4033 3504 );
4034 3505
4035 3506 // Pass the settings to the script
4036 3507 wp_localize_script('mxchat-chat-js', 'mxchatChat', $style_settings);