PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.1.1
MxChat – AI Chatbot & Content Generation for WordPress v2.1.1
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 +227 -774 2.0.32.1.1 View file →
@@ -382,9 +382,9 @@
382 382 //error_log('[DEBUG] ---------- mxchat_handle_save_email_and_response START ----------');
383 383
384 384 // Validate nonce
385 385 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'));
386 + //error_log(esc_html__('[ERROR] Invalid nonce in mxchat_handle_save_email_and_response', 'mxchat'));
387 387 wp_send_json_error(['message' => esc_html__('Invalid nonce.', 'mxchat')]);
388 388 wp_die();
389 389 }
390 390
@@ -703,8 +703,30 @@
703 703
704 704 // Preserve code blocks from markdown conversion
705 705 $message = preg_replace('/```(\w+)?\s*([\s\S]+?)```/s', '<pre><code class="$1">$2</code></pre>', $message);
706 706
707 +// Check if any add-ons want to pre-process this message (for web search etc.)
708 +$pre_processed_result = apply_filters('mxchat_pre_process_message', $message, $user_id, $session_id);
709 +
710 +// If the pre-processing returned a result (not the original message), use it directly
711 +if (is_array($pre_processed_result) && isset($pre_processed_result['text'])) {
712 + // Save the AI response
713 + $this->mxchat_save_chat_message($session_id, 'bot', $pre_processed_result['text']);
714 +
715 + // Save HTML content if provided
716 + if (!empty($pre_processed_result['html'])) {
717 + $this->mxchat_save_chat_message($session_id, 'bot', $pre_processed_result['html']);
718 + }
719 +
720 + // Return the response
721 + wp_send_json([
722 + 'text' => $pre_processed_result['text'],
723 + 'html' => $pre_processed_result['html'] ?? '',
724 + 'session_id' => $session_id
725 + ]);
726 + wp_die();
727 +}
728 +
707 729 // Save the user's message
708 730 $this->mxchat_save_chat_message($session_id, 'user', $message);
709 731
710 732 // Check if the message is an email address
@@ -928,8 +950,11 @@
928 950 }
929 951 $context_content .= "\n";
930 952 }
931 953 }
954 +
955 + $context_content = apply_filters('mxchat_prepare_context', $context_content, $session_id);
956 +
932 957 // Generate the response using the full context
933 958 $response = $this->mxchat_generate_response(
934 959 $context_content,
935 960 $this->options['api_key'],
@@ -960,42 +985,30 @@
960 985 wp_send_json($response_data);
961 986 wp_die();
962 987 }
963 988
964 -
965 -// Helper function to clear PDF and Word document related transients
966 -private function clear_pdf_transients($session_id) {
967 - // PDF transients
968 - delete_transient('mxchat_pdf_url_' . $session_id);
969 - delete_transient('mxchat_pdf_embeddings_' . $session_id);
970 - delete_transient('mxchat_include_pdf_in_context_' . $session_id);
971 - delete_transient('mxchat_waiting_for_pdf_url_' . $session_id);
972 -
973 - // Word document transients
974 - delete_transient('mxchat_word_url_' . $session_id);
975 - delete_transient('mxchat_word_filename_' . $session_id);
976 - delete_transient('mxchat_word_embeddings_' . $session_id);
977 - delete_transient('mxchat_include_word_in_context_' . $session_id);
978 - delete_transient('mxchat_waiting_for_word_' . $session_id);
979 -}
980 -
981 989 // New function to check intents and invoke the callback function
982 990 private function mxchat_check_intent_and_invoke_callback($message, $user_id, $session_id) {
983 991 global $wpdb;
984 992 $chat_mode = get_option("mxchat_mode_{$session_id}", 'ai');
985 993
986 - //error_log("[MxChat] Checking intents for message: " . $message);
994 + //error_log('🔍 MXCHAT DEBUG: Intent Check Started ==================');
995 + //error_log("🔍 MXCHAT DEBUG: Message: '$message'");
996 + //error_log("🔍 MXCHAT DEBUG: Chat Mode: $chat_mode");
987 997
988 998 // Generate the user embedding
999 + //error_log('🔄 MXCHAT DEBUG: Generating user embedding');
989 1000 $user_embedding = $this->mxchat_generate_embedding($message, $this->options['api_key']);
990 1001 if (!is_array($user_embedding)) {
991 - //error_log("[MxChat] Failed to generate user embedding");
1002 + //error_log('❌ MXCHAT DEBUG: Failed to generate user embedding');
992 1003 return false;
993 1004 }
1005 + //error_log('✅ MXCHAT DEBUG: User embedding generated successfully');
994 1006
995 1007 // Fetch intents from the database
996 1008 $table_name = $wpdb->prefix . 'mxchat_intents';
997 1009 if ($chat_mode === 'agent') {
1010 + //error_log('🔍 MXCHAT DEBUG: Agent mode - fetching only chatbot switch intent');
998 1011 $query = $wpdb->prepare(
999 1012 "SELECT * FROM $table_name WHERE callback_function = %s",
1000 1013 'mxchat_handle_switch_to_chatbot_intent'
1001 1014 );
@@ -1000,15 +1013,16 @@
1000 1013 'mxchat_handle_switch_to_chatbot_intent'
1001 1014 );
1002 1015 $intents = $wpdb->get_results($query);
1003 1016 } else {
1017 + //error_log('🔍 MXCHAT DEBUG: AI mode - fetching all intents');
1004 1018 $intents = $wpdb->get_results("SELECT * FROM $table_name");
1005 1019 }
1006 1020
1007 - //error_log("[MxChat] Found " . count($intents) . " intents to check");
1021 + //error_log('🔍 MXCHAT DEBUG: Found ' . count($intents) . ' intents to check');
1008 1022
1009 1023 if (empty($intents)) {
1010 - //error_log("[MxChat] No intents found in database");
1024 + //error_log('❌ MXCHAT DEBUG: No intents found in database');
1011 1025 return false;
1012 1026 }
1013 1027
1014 1028 $highest_similarity = -INF;
@@ -1013,10 +1027,11 @@
1013 1027
1014 1028 $highest_similarity = -INF;
1015 1029 $matched_intent = null;
1016 1030
1031 + //error_log('📊 MXCHAT DEBUG: Intent Similarity Scores ==================');
1017 1032 foreach ($intents as $intent) {
1018 - //error_log("[MxChat] Checking intent: " . $intent->intent_label . " (callback: " . $intent->callback_function . ")");
1033 + //error_log("🔄 MXCHAT DEBUG: Checking intent: '{$intent->intent_label}' (callback: {$intent->callback_function})");
1019 1034
1020 1035 $intent_embedding_serialized = $intent->embedding_vector;
1021 1036 $intent_embedding = $intent_embedding_serialized
1022 1037 ? unserialize($intent_embedding_serialized, ['allowed_classes' => false])
@@ -1022,9 +1037,9 @@
1022 1037 ? unserialize($intent_embedding_serialized, ['allowed_classes' => false])
1023 1038 : null;
1024 1039
1025 1040 if (!is_array($intent_embedding)) {
1026 - //error_log("[MxChat] Invalid embedding for intent: " . $intent->intent_label);
1041 + //error_log("❌ MXCHAT DEBUG: Invalid embedding for intent: {$intent->intent_label}");
1027 1042 continue;
1028 1043 }
1029 1044
1030 1045 $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $intent_embedding);
@@ -1029,30 +1044,34 @@
1029 1044
1030 1045 $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $intent_embedding);
1031 1046 $intent_threshold = isset($intent->similarity_threshold) ? $intent->similarity_threshold : 0.85;
1032 1047
1033 - //error_log("[MxChat] Similarity for {$intent->intent_label}: {$similarity} (threshold: {$intent_threshold})");
1034 1048
1035 1049 if ($similarity >= $intent_threshold && $similarity > $highest_similarity) {
1036 1050 $highest_similarity = $similarity;
1037 1051 $matched_intent = $intent;
1038 - //error_log("[MxChat] New best match: {$intent->intent_label} with similarity {$similarity}");
1052 + //error_log("✅ MXCHAT DEBUG: New best match: '{$intent->intent_label}' with similarity {$similarity}");
1039 1053 }
1040 1054 }
1055 + //error_log('📊 MXCHAT DEBUG: End Intent Scores ==================');
1041 1056
1042 1057 if ($matched_intent) {
1043 - //error_log("[MxChat] Invoking callback: " . $matched_intent->callback_function);
1058 + //error_log("🎯 MXCHAT DEBUG: Final Intent Match: '{$matched_intent->intent_label}'");
1059 + //error_log("🔄 MXCHAT DEBUG: Invoking callback: {$matched_intent->callback_function}");
1044 1060
1045 1061 // If the callback is a method on this instance (core callback), call it directly
1046 1062 if (method_exists($this, $matched_intent->callback_function)) {
1063 + //error_log('🔍 MXCHAT DEBUG: Using direct method call for core callback');
1047 1064 $callback_result = call_user_func(
1048 - [$this, $matched_intent->callback_function],
1049 - $message,
1050 - $user_id,
1051 - $session_id,
1052 - $matched_intent
1053 - );
1065 + [$this, $matched_intent->callback_function],
1066 + $message,
1067 + $user_id,
1068 + $session_id,
1069 + $matched_intent,
1070 + $user_context // Add user context
1071 + );
1054 1072 } else {
1073 + //error_log('🔍 MXCHAT DEBUG: Using apply_filters for add-on callback');
1055 1074 // Otherwise, use apply_filters for add-on callbacks
1056 1075 $callback_result = apply_filters(
1057 1076 $matched_intent->callback_function,
1058 1077 false, // default return value
@@ -1062,159 +1081,42 @@
1062 1081 $matched_intent
1063 1082 );
1064 1083 }
1065 1084
1066 - //error_log("[MxChat] Callback result: " . print_r($callback_result, true));
1085 + //error_log('🔍 MXCHAT DEBUG: Callback result type: ' . gettype($callback_result));
1067 1086 if ($callback_result !== false) {
1087 + //error_log('✅ MXCHAT DEBUG: Intent handled successfully');
1068 1088 $this->fallbackResponse = $callback_result;
1069 1089 return true;
1070 1090 }
1091 + //error_log('❌ MXCHAT DEBUG: Callback returned false');
1092 + } else {
1093 + //error_log('❌ MXCHAT DEBUG: No matching intent found');
1071 1094 }
1072 1095
1073 - //error_log("[MxChat] No matching intent found");
1096 + //error_log('🔍 MXCHAT DEBUG: Intent Check Completed ==================');
1074 1097 return false;
1075 1098 }
1076 1099
1077 1100
1078 -//verified good
1079 -public function mxchat_handle_order_history($message, $user_id, $session_id) {
1080 - 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');
1082 - return true;
1083 - }
1101 +// Helper function to clear PDF and Word document related transients
1102 +private function clear_pdf_transients($session_id) {
1103 + // PDF transients
1104 + delete_transient('mxchat_pdf_url_' . $session_id);
1105 + delete_transient('mxchat_pdf_embeddings_' . $session_id);
1106 + delete_transient('mxchat_include_pdf_in_context_' . $session_id);
1107 + delete_transient('mxchat_waiting_for_pdf_url_' . $session_id);
1084 1108
1085 - $orderDetails = MxChat_WooCommerce::mxchat_fetch_user_orders_details('all');
1086 -
1087 - if (empty($orderDetails)) {
1088 - $this->fallbackResponse['text'] = esc_html__("I don't see any orders associated with your account. Are you logged in?", 'mxchat');
1089 - return true;
1090 - }
1091 -
1092 - // Generate AI prompt with context
1093 - $prompt = __("User asked about their orders:", 'mxchat') . " '{$message}'\n\n";
1094 - $prompt .= __("Order information:", 'mxchat') . "\n";
1095 - foreach ($orderDetails as $order) {
1096 - $items_list = array_map(function($item) {
1097 - return "{$item['name']} ({$item['quantity']})";
1098 - }, $order['items']);
1099 -
1100 - $prompt .= __("Order #", 'mxchat') . "{$order['order_id']}: {$order['formatted_total']} " . __("on", 'mxchat') . " {$order['date']}\n";
1101 - $prompt .= __("Items:", 'mxchat') . " " . implode(', ', $items_list) . "\n";
1102 - }
1103 -
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');
1107 -
1108 - // Get AI response
1109 - $ai_response = $this->mxchat_call_ai_api($prompt);
1110 - $this->fallbackResponse['text'] = $ai_response['text'];
1111 -
1112 - return true;
1109 + // Word document transients
1110 + delete_transient('mxchat_word_url_' . $session_id);
1111 + delete_transient('mxchat_word_filename_' . $session_id);
1112 + delete_transient('mxchat_word_embeddings_' . $session_id);
1113 + delete_transient('mxchat_include_word_in_context_' . $session_id);
1114 + delete_transient('mxchat_waiting_for_word_' . $session_id);
1113 1115 }
1114 1116
1115 1117
1116 -public function mxchat_handle_product_inquiry($message, $user_id, $session_id) {
1117 - //error_log("PRODUCT INQUIRY START - Message: $message, User ID: $user_id");
1118 1118
1119 - // Sanitize user ID
1120 - $sanitized_user_id = sanitize_key($user_id);
1121 - //error_log("Sanitized User ID: $sanitized_user_id");
1122 -
1123 - // Find product
1124 - $product_id = $this->find_product_in_message($message);
1125 - //error_log("Initial product ID from message: " . ($product_id ?? 'null'));
1126 -
1127 - // Check transient if no product found
1128 - if (!$product_id) {
1129 - $product_id = get_transient('mxchat_last_discussed_product_' . $sanitized_user_id);
1130 - //error_log("Product ID from transient: " . ($product_id ?? 'null'));
1131 - }
1132 -
1133 - // Handle product inquiries
1134 - if ($product_id && class_exists('WooCommerce')) {
1135 - $product = wc_get_product($product_id);
1136 - if ($product) {
1137 - //error_log("Found product: " . $product->get_name() . " (ID: $product_id)");
1138 -
1139 - // Prepare product details
1140 - $product_name = esc_html($product->get_name());
1141 - $product_price = $product->get_price_html();
1142 - $product_image_url = esc_url(wp_get_attachment_url($product->get_image_id()));
1143 - $product_url = esc_url(get_permalink($product_id));
1144 - $product_id_attr = esc_attr($product_id);
1145 -
1146 - // Get product description
1147 - $product_description = $product->get_description() ?: $product->get_short_description();
1148 -
1149 - // Log embeddings process
1150 - //error_log("Generating embedding for message: $message");
1151 - $user_message_embedding = $this->mxchat_generate_embedding($message, $this->options['api_key']);
1152 - //error_log("Finding relevant content for product inquiry");
1153 - $relevant_content = $this->mxchat_find_relevant_content($user_message_embedding);
1154 -
1155 - // 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');
1158 -
1159 - if (!empty($relevant_content)) {
1160 - $ai_prompt .= esc_html__("Relevant information from our knowledge base:\n{$relevant_content}\n\n", 'mxchat');
1161 - }
1162 -
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";
1166 - if ($product_description) {
1167 - $ai_prompt .= esc_html__("Description: {$product_description}\n", 'mxchat');
1168 - }
1169 -
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');
1176 -
1177 - //error_log("Sending AI prompt: " . $ai_prompt);
1178 -
1179 - // Build and log AI response
1180 - $ai_response = $this->mxchat_call_ai_api($ai_prompt);
1181 - //error_log("AI Response received for product inquiry: " . print_r($ai_response, true));
1182 -
1183 - // Generate and log product card
1184 - $product_card_html = <<<HTML
1185 -<div class="mxchat-product-card">
1186 - <a href="{$product_url}" target="_blank">
1187 - <img src="{$product_image_url}" alt="{$product_name}" class="mxchat-product-image" />
1188 - <h3 class="mxchat-product-name">{$product_name}</h3>
1189 - </a>
1190 - <div class="mxchat-product-price">{$product_price}</div>
1191 - <button class="mxchat-add-to-cart-button" data-product-id="{$product_id_attr}">Add to Cart</button>
1192 -</div>
1193 -HTML;
1194 -
1195 - // Save response and set transient
1196 - $this->productCardHtml = $product_card_html;
1197 - $this->fallbackResponse = [
1198 - 'text' => $ai_response['text'],
1199 - 'html' => $this->productCardHtml,
1200 - ];
1201 -
1202 - //error_log("Setting transient for product: $product_id with key: mxchat_last_discussed_product_$sanitized_user_id");
1203 - set_transient('mxchat_last_discussed_product_' . $sanitized_user_id, $product_id, HOUR_IN_SECONDS);
1204 -
1205 - // Verify transient was set
1206 - $verify_transient = get_transient('mxchat_last_discussed_product_' . $sanitized_user_id);
1207 - //error_log("Verification - Retrieved transient value: " . ($verify_transient ?? 'null'));
1208 -
1209 - return true;
1210 - }
1211 - }
1212 -
1213 - //error_log("PRODUCT INQUIRY END - No product found or WooCommerce not available");
1214 - return false;
1215 -}
1216 -
1217 1119 //verified good
1218 1120 public function mxchat_handle_email_capture($message, $user_id, $session_id) {
1219 1121 // Log the message safely
1220 1122 //error_log("Triggered email capture intent for message: " . sanitize_text_field($message));
@@ -1254,18 +1156,20 @@
1254 1156 $response_html = '';
1255 1157 //error_log("DALL-E image generation error: " . esc_html($image_response['error'] ?? 'Unknown error.'));
1256 1158 }
1257 1159
1258 - // Save both text and HTML responses
1259 - $this->mxchat_save_chat_message($session_id, 'bot', $response_text . "\n" . $response_html);
1160 + // Where you save the AI response
1161 + if (!empty($response)) {
1162 + $this->mxchat_save_chat_message($session_id, 'bot', $response);
1163 + }
1164 +
1165 + // Similarly, when returning the response data
1166 + $response_data = [
1167 + 'text' => empty($response) ? null : $response, // Use null instead of empty string
1168 + 'html' => !empty($this->productCardHtml) ? $this->productCardHtml : ($this->fallbackResponse['html'] ?? ''),
1169 + 'session_id' => $session_id
1170 + ];
1260 1171
1261 - // Prepare the response data
1262 - $response_data = [
1263 - 'message' => $response_text,
1264 - 'html' => $response_html,
1265 - 'image_url' => $image_url ?? '',
1266 - ];
1267 -
1268 1172 // Send the JSON response
1269 1173 header('Content-Type: application/json; charset=' . get_option('blog_charset'));
1270 1174 echo json_encode($response_data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
1271 1175 wp_die();
@@ -1534,9 +1438,9 @@
1534 1438
1535 1439 if (empty($api_key)) {
1536 1440 /*
1537 1441 if (defined('WP_DEBUG') && WP_DEBUG) {
1538 - error_log("Brave API key is missing.");
1442 + //error_log("Brave API key is missing.");
1539 1443 }
1540 1444 */
1541 1445
1542 1446 $this->fallbackResponse = [
@@ -1558,9 +1462,9 @@
1558 1462
1559 1463 /*
1560 1464 // Log the final API URL for the search
1561 1465 if (defined('WP_DEBUG') && WP_DEBUG) {
1562 - error_log("Final API URL for Brave Image Search: " . esc_url_raw($api_url));
1466 + //error_log("Final API URL for Brave Image Search: " . esc_url_raw($api_url));
1563 1467 }
1564 1468 */
1565 1469
1566 1470
@@ -1582,9 +1486,9 @@
1582 1486
1583 1487 if (is_wp_error($response)) {
1584 1488 /*
1585 1489 if (defined('WP_DEBUG') && WP_DEBUG) {
1586 - error_log("Brave Image API request failed: " . $response->get_error_message());
1490 + //error_log("Brave Image API request failed: " . $response->get_error_message());
1587 1491 }
1588 1492 */
1589 1493
1590 1494 $this->fallbackResponse = [
@@ -1628,9 +1532,9 @@
1628 1532
1629 1533 } else {
1630 1534 /*
1631 1535 if (defined('WP_DEBUG') && WP_DEBUG) {
1632 - error_log("Brave Image API response did not contain expected data structure or was empty: " . print_r($body, true));
1536 + //error_log("Brave Image API response did not contain expected data structure or was empty: " . print_r($body, true));
1633 1537 }
1634 1538 */
1635 1539
1636 1540 $this->fallbackResponse = [
@@ -1647,9 +1551,9 @@
1647 1551
1648 1552 /*
1649 1553 // Log the API key check, without exposing the key
1650 1554 if (defined('WP_DEBUG') && WP_DEBUG) {
1651 - error_log("Retrieved OpenAI API Key: " . ($api_key ? "Present" : "Missing"));
1555 + //error_log("Retrieved OpenAI API Key: " . ($api_key ? "Present" : "Missing"));
1652 1556 }
1653 1557 */
1654 1558
1655 1559 if (empty($api_key)) {
@@ -1690,9 +1594,9 @@
1690 1594
1691 1595 /*
1692 1596 // Log the interpreted query for debugging
1693 1597 if (defined('WP_DEBUG') && WP_DEBUG) {
1694 - error_log("Interpreted search query: " . $interpreted_query);
1598 + //error_log("Interpreted search query: " . $interpreted_query);
1695 1599 }
1696 1600 */
1697 1601
1698 1602 return $interpreted_query;
@@ -1701,100 +1605,10 @@
1701 1605 return sanitize_text_field($user_query);
1702 1606 }
1703 1607 }
1704 1608
1705 -public function mxchat_handle_add_to_cart_intent($message, $user_id, $session_id) {
1706 - //error_log("ADD TO CART START - Message: $message, User ID: $user_id");
1707 1609
1708 - if (!class_exists('WooCommerce')) {
1709 - //error_log("WooCommerce not available");
1710 - return $this->generate_intent_response([
1711 - 'intent' => 'add_to_cart',
1712 - 'status' => 'error',
1713 - 'reason' => esc_html__('woocommerce_not_available', 'mxchat')
1714 - ], $session_id);
1715 - }
1716 1610
1717 - $sanitized_user_id = sanitize_key($user_id);
1718 - // error_log("Sanitized User ID: $sanitized_user_id");
1719 - $product_id = null;
1720 -
1721 - // If button click, use transient
1722 - if ($message === '!addtocart') {
1723 - //error_log("Button click detected - using transient");
1724 - $product_id = get_transient('mxchat_last_discussed_product_' . $sanitized_user_id);
1725 - //error_log("Product ID from transient: " . ($product_id ?? 'null'));
1726 - } else {
1727 - // For text commands, search message first
1728 - //error_log("Text command detected - searching message first");
1729 - $product_id = $this->find_product_in_message($message);
1730 - //error_log("Product ID from message search: " . ($product_id ?? 'null'));
1731 -
1732 - // Only use transient as fallback if no product found in message
1733 - if (!$product_id) {
1734 - //error_log("No product found in message, checking transient as fallback");
1735 - $product_id = get_transient('mxchat_last_discussed_product_' . $sanitized_user_id);
1736 - //error_log("Product ID from transient fallback: " . ($product_id ?? 'null'));
1737 - }
1738 - }
1739 -
1740 - // Handle no product found
1741 - if (!$product_id) {
1742 - //error_log("No product ID found through any method");
1743 - return $this->generate_intent_response([
1744 - 'intent' => 'add_to_cart',
1745 - 'status' => 'error',
1746 - 'reason' => esc_html__('no_product_context', 'mxchat'),
1747 - 'action_needed' => esc_html__('request_product_name', 'mxchat'),
1748 - 'searched_message' => $message
1749 - ], $session_id);
1750 - }
1751 -
1752 - // Get and verify product
1753 - $product = wc_get_product($product_id);
1754 - if (!$product) {
1755 - //error_log("Product not found with ID: $product_id");
1756 - return $this->generate_intent_response([
1757 - 'intent' => 'add_to_cart',
1758 - 'status' => 'error',
1759 - 'reason' => esc_html__('product_not_found', 'mxchat'),
1760 - 'product_id' => $product_id
1761 - ], $session_id);
1762 - }
1763 -
1764 - //error_log("Found product: " . $product->get_name() . " (ID: $product_id)");
1765 -
1766 - // Add to cart
1767 - $added = WC()->cart->add_to_cart($product_id);
1768 - if ($added) {
1769 - //error_log("Successfully added to cart: " . $product->get_name());
1770 - return $this->generate_intent_response([
1771 - 'intent' => 'add_to_cart',
1772 - 'status' => 'success',
1773 - 'product' => [
1774 - 'name' => $product->get_name(),
1775 - 'id' => $product_id
1776 - ],
1777 - 'cart_url' => wc_get_cart_url(),
1778 - 'available_actions' => [
1779 - esc_html__('view_cart', 'mxchat'),
1780 - esc_html__('checkout', 'mxchat'),
1781 - esc_html__('continue_shopping', 'mxchat')
1782 - ]
1783 - ], $session_id);
1784 - } else {
1785 - //error_log("Failed to add to cart: " . $product->get_name());
1786 - return $this->generate_intent_response([
1787 - 'intent' => 'add_to_cart',
1788 - 'status' => 'error',
1789 - 'reason' => esc_html__('add_to_cart_failed', 'mxchat'),
1790 - 'product' => [
1791 - 'name' => $product->get_name(),
1792 - 'id' => $product_id
1793 - ]
1794 - ], $session_id);
1795 - }
1796 -}
1797 1611 private function find_product_in_message($message) {
1798 1612 global $wpdb;
1799 1613
1800 1614 // Get embedding for the search query
@@ -1920,44 +1734,8 @@
1920 1734 return $context_string;
1921 1735 }
1922 1736
1923 1737
1924 -public function mxchat_handle_checkout_intent($message, $user_id, $session_id) {
1925 - if (!class_exists('WooCommerce')) {
1926 - $this->fallbackResponse['text'] = esc_html__("I apologize, but the checkout feature isn't available at the moment.", 'mxchat');
1927 - return true;
1928 - }
1929 -
1930 - // Check if cart has items
1931 - 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');
1933 - return true;
1934 - }
1935 -
1936 - // Get cart summary
1937 - $cart_count = WC()->cart->get_cart_contents_count();
1938 - $cart_total = WC()->cart->get_total();
1939 -
1940 - // Get and validate checkout URL
1941 - $checkout_url = wc_get_checkout_url();
1942 - if (!$checkout_url) {
1943 - $this->fallbackResponse['text'] = esc_html__("I'm having trouble accessing the checkout page. Please try again in a moment.", 'mxchat');
1944 - return true;
1945 - }
1946 -
1947 - wp_send_json([
1948 - 'text' => sprintf(
1949 - esc_html__("You have %d item%s in your cart totaling %s. I'll redirect you to checkout now.", 'mxchat'),
1950 - $cart_count,
1951 - $cart_count > 1 ? esc_html__('s', 'mxchat') : '',
1952 - strip_tags($cart_total)
1953 - ),
1954 - 'redirect_url' => esc_url_raw($checkout_url)
1955 - ]);
1956 - wp_die();
1957 -}
1958 -
1959 -
1960 1738 //very good
1961 1739 private function add_email_to_loops($email) {
1962 1740 // Sanitize the email
1963 1741 $email = sanitize_email($email);
@@ -2118,9 +1896,9 @@
2118 1896
2119 1897 return $embeddings;
2120 1898
2121 1899 } catch (\Exception $e) {
2122 - // error_log(esc_html__("Error parsing or processing PDF: ", 'mxchat') . $e->getMessage());
1900 + // //error_log(esc_html__("Error parsing or processing PDF: ", 'mxchat') . $e->getMessage());
2123 1901
2124 1902 // Cleanup in case of exception
2125 1903 if (filter_var($pdf_source, FILTER_VALIDATE_URL) && $temp_file && file_exists($temp_file)) {
2126 1904 unlink($temp_file);
@@ -2250,408 +2028,10 @@
2250 2028 wp_die();
2251 2029 }
2252 2030
2253 2031
2254 -/**
2255 - * Calls the AI API with the provided prompt.
2256 - *
2257 - * @param string $prompt The prompt to send to the AI.
2258 - * @return array An array containing the AI's response text.
2259 - */
2260 -private function mxchat_call_ai_api( $prompt ) {
2261 - //error_log( esc_html__( 'Calling AI API with the provided prompt.', 'mxchat' ) );
2262 2032
2263 - $api_key = $this->options['api_key'];
2264 - 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' ) ];
2267 - }
2268 2033
2269 - $url = 'https://api.openai.com/v1/chat/completions';
2270 - $messages = [
2271 - [
2272 - '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' ),
2274 - ],
2275 - [
2276 - 'role' => 'user',
2277 - 'content' => $prompt,
2278 - ],
2279 - ];
2280 -
2281 - $args = [
2282 - 'headers' => [
2283 - 'Authorization' => 'Bearer ' . $api_key,
2284 - 'Content-Type' => 'application/json',
2285 - ],
2286 - 'body' => wp_json_encode(
2287 - [
2288 - 'model' => 'gpt-4o',
2289 - 'messages' => $messages,
2290 - 'temperature' => 0.7,
2291 - ]
2292 - ),
2293 - 'timeout' => 10,
2294 - 'method' => 'POST',
2295 - ];
2296 -
2297 - $response = wp_remote_post( $url, $args );
2298 -
2299 - 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' ) ];
2302 - }
2303 -
2304 - $body = wp_remote_retrieve_body( $response );
2305 - //error_log( esc_html__( 'API response body: ', 'mxchat' ) . $body );
2306 -
2307 - $decoded_body = json_decode( $body, true );
2308 - if ( isset( $decoded_body['choices'][0]['message']['content'] ) ) {
2309 - return [ 'text' => $decoded_body['choices'][0]['message']['content'] ];
2310 - } 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' ) ];
2313 - }
2314 -}
2315 -
2316 -/**
2317 - * Fetches the AI response for the given prompt.
2318 - *
2319 - * @param string $prompt The prompt to send to the AI.
2320 - * @return string|null The AI's response text or null if not available.
2321 - */
2322 -private function mxchat_fetch_ai_response( $prompt ) {
2323 - $response = $this->mxchat_call_ai_api( $prompt );
2324 - return isset( $response['text'] ) ? $response['text'] : null;
2325 -}
2326 -/**
2327 - * Generates the AI prompt for product recommendations.
2328 - *
2329 - * @param array $recommendations An array of product recommendations.
2330 - * @return string The generated AI prompt.
2331 - */
2332 -private function mxchat_generate_ai_recommendation_prompt($recommendations) {
2333 - $recommendation_list = '';
2334 - foreach ($recommendations as $index => $rec) {
2335 - $number = $index + 1;
2336 - $name = $rec['name'];
2337 - $price = $rec['price'];
2338 - $url = $rec['url'];
2339 - $image = $rec['image'];
2340 - $recommendation_list .= "{$number}. " . esc_html__('Product:', 'mxchat') . " {$name} (" . esc_html__('Price:', 'mxchat') . " \\${$price})\n";
2341 - $recommendation_list .= " [Link]({$url})\n";
2342 - $recommendation_list .= " ![Image]({$image})\n\n";
2343 - }
2344 -
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');
2351 -
2352 - return $prompt;
2353 -}
2354 -private function mxchat_generate_recommendations($user_id, $message) {
2355 - // 1. Gather user context
2356 - $user_context = $this->mxchat_get_user_context($user_id);
2357 -
2358 - // Get cart item IDs for filtering
2359 - $cart_item_ids = [];
2360 - if (!empty($user_context['cart_items'])) {
2361 - $cart_item_ids = array_map(function($item) {
2362 - return $item['product_id'];
2363 - }, $user_context['cart_items']);
2364 - }
2365 -
2366 - // 2. Get AI recommendation based on context
2367 - $ai_suggestion = $this->mxchat_get_ai_shopping_suggestion($message, $user_context);
2368 - //error_log("AI Shopping Suggestion: " . $ai_suggestion);
2369 -
2370 - // 3. Generate embedding for the AI suggestion
2371 - $suggestion_embedding = $this->mxchat_generate_embedding($ai_suggestion, $this->options['api_key']);
2372 - if (!$suggestion_embedding) {
2373 - //error_log("Could not generate embedding for AI suggestion");
2374 - return ['recommendations' => [], 'sources' => []];
2375 - }
2376 -
2377 - // 4. Use existing relevant content function to find matches
2378 - $relevant_content = $this->mxchat_find_relevant_content($suggestion_embedding);
2379 -
2380 - // 5. Extract product URLs from the relevant content
2381 - preg_match_all('/https?:\/\/[^\s<>"\']+?\/product\/[^\s<>"\']+/', $relevant_content, $matches);
2382 -
2383 - $found_products = [];
2384 - if (!empty($matches[0])) {
2385 - foreach ($matches[0] as $url) {
2386 - // Clean the URL
2387 - $url = rtrim($url, '/."\']');
2388 -
2389 - // Get the product slug
2390 - $path = parse_url($url, PHP_URL_PATH);
2391 - $slug = basename(rtrim($path, '/'));
2392 -
2393 - // Find product by slug
2394 - $args = array(
2395 - 'post_type' => 'product',
2396 - 'post_status' => 'publish',
2397 - 'name' => $slug,
2398 - 'posts_per_page' => 1
2399 - );
2400 -
2401 - $products = get_posts($args);
2402 -
2403 - if (!empty($products)) {
2404 - $product_id = $products[0]->ID;
2405 -
2406 - // Skip if product is in cart
2407 - if (in_array($product_id, $cart_item_ids)) {
2408 - //error_log("Skipping product ID $product_id - already in cart");
2409 - continue;
2410 - }
2411 -
2412 - $product = wc_get_product($product_id);
2413 -
2414 - if ($product && $product->is_purchasable() && !in_array($product_id, array_map(function($p) {
2415 - return $p->get_id();
2416 - }, $found_products))) {
2417 - $found_products[] = $product;
2418 - }
2419 - }
2420 - }
2421 - }
2422 -
2423 - // If no products found by URLs, look for product names in the content
2424 - if (empty($found_products)) {
2425 - $products = wc_get_products([
2426 - 'status' => 'publish',
2427 - 'limit' => -1,
2428 - 'return' => 'objects'
2429 - ]);
2430 -
2431 - foreach ($products as $product) {
2432 - // Skip if product is in cart
2433 - if (in_array($product->get_id(), $cart_item_ids)) {
2434 - continue;
2435 - }
2436 -
2437 - $name = $product->get_name();
2438 - if (stripos($relevant_content, $name) !== false) {
2439 - if ($product->is_purchasable() && !in_array($product->get_id(), array_map(function($p) {
2440 - return $p->get_id();
2441 - }, $found_products))) {
2442 - $found_products[] = $product;
2443 - }
2444 - }
2445 - }
2446 - }
2447 -
2448 - // Keep searching until we have 4 products or run out of options
2449 - $formatted_recommendations = [];
2450 - foreach ($found_products as $product) {
2451 - if (count($formatted_recommendations) >= 4) break;
2452 -
2453 - $formatted_recommendations[] = [
2454 - 'name' => $product->get_name(),
2455 - 'price' => $product->get_price(),
2456 - 'url' => get_permalink($product->get_id()),
2457 - 'image' => wp_get_attachment_url($product->get_image_id())
2458 - ];
2459 - }
2460 -
2461 - return [
2462 - 'recommendations' => $formatted_recommendations,
2463 - 'sources' => [__('AI-powered personalized recommendations', 'mxchat')]
2464 - ];
2465 -}
2466 -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');
2469 -
2470 - if (!empty($user_context['order_history'])) {
2471 - $prompt .= esc_html__("Their recent orders include:\n", 'mxchat');
2472 - foreach ($user_context['order_history'] as $order) {
2473 - $prompt .= esc_html__("- {$order['name']} ({$order['date']})\n", 'mxchat');
2474 - }
2475 - }
2476 -
2477 - if (!empty($user_context['cart_items'])) {
2478 - $prompt .= esc_html__("\nThey currently have in their cart:\n", 'mxchat');
2479 - foreach ($user_context['cart_items'] as $item) {
2480 - $prompt .= esc_html__("- {$item['name']}\n", 'mxchat');
2481 - }
2482 - }
2483 -
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');
2486 -
2487 - $response = $this->mxchat_call_ai_api($prompt);
2488 - return isset($response['text']) ? trim($response['text']) : $message;
2489 -}
2490 -public function mxchat_handle_product_recommendations($message, $user_id, $session_id) {
2491 - try {
2492 - //error_log("Starting product recommendations for user: $user_id, session: $session_id");
2493 -
2494 - // Pass the message to get context-aware recommendations
2495 - $recommendation_data = $this->mxchat_generate_recommendations($user_id, $message);
2496 - //error_log('Generated recommendation data: ' . wp_json_encode($recommendation_data));
2497 -
2498 - if (empty($recommendation_data['recommendations'])) {
2499 - //error_log("No recommendations found for user: $user_id");
2500 - $this->fallbackResponse = [
2501 - 'text' => __("I couldn't find any product recommendations for you right now. Please try again later!", 'mxchat'),
2502 - ];
2503 - return;
2504 - }
2505 -
2506 - // Remove duplicates and limit to top 4 recommendations
2507 - $unique_recommendations = [];
2508 - foreach ($recommendation_data['recommendations'] as $rec) {
2509 - $unique_recommendations[$rec['url']] = $rec;
2510 - }
2511 -
2512 - $unique_recommendations = array_slice($unique_recommendations, 0, 4);
2513 - //error_log('Top 4 recommendations: ' . wp_json_encode($unique_recommendations));
2514 -
2515 - $recommendations_summary = [];
2516 - foreach ($unique_recommendations as $rec) {
2517 - $recommendations_summary[] = [
2518 - 'name' => $rec['name'],
2519 - 'price' => strip_tags($rec['price']),
2520 - 'url' => $rec['url'],
2521 - 'image' => $rec['image'],
2522 - ];
2523 - }
2524 -
2525 - // Generate AI prompt and fetch response
2526 - $ai_prompt = $this->mxchat_generate_ai_recommendation_prompt($recommendations_summary);
2527 - $ai_response = $this->mxchat_fetch_ai_response($ai_prompt);
2528 -
2529 - if (empty($ai_response)) {
2530 - $this->fallbackResponse = [
2531 - 'text' => __('An unexpected error occurred while generating recommendations. Please try again later.', 'mxchat'),
2532 - ];
2533 - return;
2534 - }
2535 -
2536 - // Split the AI's response into lines
2537 - $ai_lines = preg_split('/\r\n|\r|\n/', $ai_response);
2538 -
2539 - // Initialize variables
2540 - $formatted_response = '';
2541 - $justifications = [];
2542 - $current_number = 0;
2543 - $in_introduction = true;
2544 - $introduction = '';
2545 -
2546 - // Parse the AI response to separate the introduction and the justifications
2547 - foreach ($ai_lines as $line) {
2548 - if (preg_match('/^\s*(\d+)\.\s*(.*)$/', $line, $matches)) {
2549 - // This line is a numbered justification
2550 - $current_number = intval($matches[1]) - 1;
2551 - $justifications[$current_number] = $matches[2];
2552 - $in_introduction = false;
2553 - } elseif ($in_introduction) {
2554 - // This line is part of the introduction
2555 - $introduction .= $line . ' ';
2556 - } else {
2557 - // This line is a continuation of the current justification
2558 - if (isset($justifications[$current_number])) {
2559 - $justifications[$current_number] .= ' ' . $line;
2560 - }
2561 - }
2562 - }
2563 -
2564 - // Build the formatted response
2565 - if (!empty($introduction)) {
2566 - $formatted_response .= esc_html(trim($introduction)) . "<br><br>";
2567 - }
2568 -
2569 - foreach ($recommendations_summary as $index => $rec) {
2570 - $name = esc_html($rec['name']);
2571 - $price = number_format((float)$rec['price'], 2, '.', '');
2572 - $url = esc_url($rec['url']);
2573 - $image = esc_url($rec['image']);
2574 -
2575 - $formatted_response .= ($index + 1) . ". <strong>" . $name . "</strong> - <strong>$" . $price . "</strong><br>";
2576 - $formatted_response .= "<img src=\"{$image}\" alt=\"{$name}\" style=\"max-width: 200px; height: auto; display: block; margin: 10px 0;\" /><br>";
2577 -
2578 - if (isset($justifications[$index])) {
2579 - $formatted_response .= "<em>" . esc_html(trim($justifications[$index])) . "</em><br><br>";
2580 - }
2581 - }
2582 -
2583 - $this->fallbackResponse = [
2584 - 'text' => $formatted_response,
2585 - ];
2586 - //error_log('Final formatted response set.');
2587 - } catch (Exception $e) {
2588 - //error_log('Error in mxchat_handle_product_recommendations: ' . $e->getMessage());
2589 - $this->fallbackResponse = [
2590 - 'text' => __('An unexpected error occurred while generating recommendations. Please try again later.', 'mxchat'),
2591 - ];
2592 - }
2593 -}
2594 -private function mxchat_get_user_context($user_id) {
2595 - $context = [
2596 - 'order_history' => [],
2597 - 'cart_items' => [],
2598 - 'recently_viewed' => [],
2599 - ];
2600 -
2601 - // Get order history
2602 - if (is_user_logged_in() && $user_id) {
2603 - $orders = wc_get_orders([
2604 - 'customer_id' => $user_id,
2605 - 'limit' => 5, // Last 5 orders
2606 - 'orderby' => 'date',
2607 - 'order' => 'DESC',
2608 - ]);
2609 -
2610 - foreach ($orders as $order) {
2611 - foreach ($order->get_items() as $item) {
2612 - $context['order_history'][] = [
2613 - 'name' => $item->get_name(),
2614 - 'product_id' => $item->get_product_id(),
2615 - 'date' => $order->get_date_created()->format('Y-m-d')
2616 - ];
2617 - }
2618 - }
2619 - }
2620 -
2621 - // Get cart items
2622 - if (WC()->cart && WC()->cart->get_cart_contents_count() > 0) {
2623 - foreach (WC()->cart->get_cart() as $cart_item) {
2624 - $product = wc_get_product($cart_item['product_id']);
2625 - if ($product) {
2626 - $context['cart_items'][] = [
2627 - 'name' => $product->get_name(),
2628 - 'product_id' => $product->get_id()
2629 - ];
2630 - }
2631 - }
2632 - }
2633 -
2634 - // Get recently viewed items from session
2635 - $viewed_products = WC()->session->get('recently_viewed_products', []);
2636 - foreach ($viewed_products as $product_id) {
2637 - $product = wc_get_product($product_id);
2638 - if ($product) {
2639 - $context['recently_viewed'][] = [
2640 - 'name' => $product->get_name(),
2641 - 'product_id' => $product->get_id()
2642 - ];
2643 - }
2644 - }
2645 -
2646 - return $context;
2647 -}
2648 -
2649 -
2650 -
2651 -
2652 -
2653 -
2654 2034 function mxchat_fetch_new_messages() {
2655 2035 $session_id = sanitize_text_field($_POST['session_id']);
2656 2036 $last_seen_id = sanitize_text_field($_POST['last_seen_id']);
2657 2037 $persistence_enabled = $_POST['persistence_enabled'] === 'true';
@@ -2988,13 +2368,13 @@
2988 2368
2989 2369 public function mxchat_handle_agent_response(WP_REST_Request $request) {
2990 2370 //error_log('Received agent response request');
2991 2371 //error_log('Request data: ' . print_r($request->get_params(), true));
2992 - // error_log('Raw body: ' . file_get_contents('php://input'));
2372 + // //error_log('Raw body: ' . file_get_contents('php://input'));
2993 2373
2994 2374 // Get the data from Slack's slash command format
2995 2375 $command_text = $request->get_param('text');
2996 - // error_log('Command text: ' . $command_text);
2376 + // //error_log('Command text: ' . $command_text);
2997 2377
2998 2378 if (empty($command_text)) {
2999 2379 //error_log(esc_html__('Agent response error: No command text received', 'mxchat'));
3000 2380 return new WP_REST_Response([
@@ -3019,9 +2399,9 @@
3019 2399 // Save the message
3020 2400 $message_id = $this->mxchat_save_chat_message($session_id, 'agent', $message);
3021 2401
3022 2402 if (!$message_id) {
3023 - // error_log('Failed to save agent message');
2403 + // //error_log('Failed to save agent message');
3024 2404 return new WP_REST_Response([
3025 2405 'error' => esc_html__('Failed to save message', 'mxchat')
3026 2406 ], 500);
3027 2407 }
@@ -3076,42 +2456,64 @@
3076 2456 return MxChat_User::mxchat_get_user_identifier();
3077 2457 }
3078 2458
3079 2459 private function mxchat_generate_embedding($text, $api_key) {
2460 + // Get options and selected model
2461 + $options = get_option('mxchat_options');
2462 + $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
2463 +
2464 + // Determine endpoint and API key based on model
2465 + if (strpos($selected_model, 'voyage') === 0) {
2466 + $endpoint = 'https://api.voyageai.com/v1/embeddings';
2467 + $api_key = $options['voyage_api_key'] ?? '';
2468 + } else {
3080 2469 $endpoint = 'https://api.openai.com/v1/embeddings';
3081 -
3082 - $body = wp_json_encode([
3083 - 'input' => $text,
3084 - 'model' => 'text-embedding-ada-002'
3085 - ]);
3086 -
3087 - $args = [
3088 - 'body' => $body,
3089 - 'headers' => [
3090 - 'Content-Type' => 'application/json',
3091 - 'Authorization' => 'Bearer ' . $api_key,
3092 - ],
3093 - 'timeout' => 60,
3094 - 'redirection' => 5,
3095 - 'blocking' => true,
3096 - 'httpversion' => '1.0',
3097 - 'sslverify' => true,
3098 - ];
3099 -
3100 - $response = wp_remote_post($endpoint, $args);
3101 -
3102 - if (is_wp_error($response)) {
3103 - return null;
3104 - }
3105 -
3106 - $response_body = json_decode(wp_remote_retrieve_body($response), true);
3107 -
3108 - if (isset($response_body['data'][0]['embedding']) && is_array($response_body['data'][0]['embedding'])) {
3109 - return $response_body['data'][0]['embedding'];
3110 - } else {
3111 - return null;
3112 - }
2470 + // Use the passed API key for OpenAI
3113 2471 }
2472 +
2473 + // Prepare request body with conditional output_dimension
2474 + $request_body = [
2475 + 'input' => $text,
2476 + 'model' => $selected_model
2477 + ];
2478 +
2479 + // Add output_dimension for voyage-3-large
2480 + if ($selected_model === 'voyage-3-large') {
2481 + $request_body['output_dimension'] = 2048;
2482 + }
2483 +
2484 + // Prepare request arguments
2485 + $args = [
2486 + 'body' => wp_json_encode($request_body),
2487 + 'headers' => [
2488 + 'Content-Type' => 'application/json',
2489 + 'Authorization' => 'Bearer ' . $api_key,
2490 + ],
2491 + 'timeout' => 60,
2492 + 'redirection' => 5,
2493 + 'blocking' => true,
2494 + 'httpversion' => '1.0',
2495 + 'sslverify' => true,
2496 + ];
2497 +
2498 + // Make the request
2499 + $response = wp_remote_post($endpoint, $args);
2500 +
2501 + // Rest of your existing code...
2502 + if (is_wp_error($response)) {
2503 + //error_log('Embedding Generation Error: ' . $response->get_error_message());
2504 + return null;
2505 + }
2506 +
2507 + $response_body = json_decode(wp_remote_retrieve_body($response), true);
2508 +
2509 + if (isset($response_body['data'][0]['embedding']) && is_array($response_body['data'][0]['embedding'])) {
2510 + return $response_body['data'][0]['embedding'];
2511 + } else {
2512 + //error_log('Invalid embedding response: ' . wp_json_encode($response_body));
2513 + return null;
2514 + }
2515 +}
3114 2516
3115 2517
3116 2518 private function mxchat_find_relevant_content($user_embedding) {
3117 2519 //error_log('MXChat Vector Search: Starting content search...');
@@ -3133,8 +2535,9 @@
3133 2535 return $this->find_relevant_content_wordpress($user_embedding);
3134 2536 }
3135 2537 }
3136 2538
2539 +
3137 2540 private function find_relevant_content_wordpress($user_embedding) {
3138 2541 global $wpdb;
3139 2542 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
3140 2543 $cache_key = 'mxchat_system_prompt_embeddings';
@@ -3139,11 +2542,15 @@
3139 2542 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
3140 2543 $cache_key = 'mxchat_system_prompt_embeddings';
3141 2544 $batch_size = 500;
3142 2545
2546 + // Log start of matching process
2547 + //error_log('[MXCHAT] Starting similarity matching process');
2548 +
3143 2549 // Retrieve embeddings from cache or database
3144 2550 $embeddings = wp_cache_get($cache_key, 'mxchat_system_prompts');
3145 2551 if ($embeddings === false) {
2552 + //error_log('[MXCHAT] Cache miss - loading embeddings from database');
3146 2553 $embeddings = [];
3147 2554 $offset = 0;
3148 2555
3149 2556 // Load in batches and build cache
@@ -3169,15 +2576,28 @@
3169 2576
3170 2577 } while (true);
3171 2578
3172 2579 if (empty($embeddings)) {
2580 + //error_log('[MXCHAT] No embeddings found in database');
3173 2581 return ''; // Return an empty string if no embeddings found
3174 2582 }
3175 2583 wp_cache_set($cache_key, $embeddings, 'mxchat_system_prompts', 3600);
2584 + //error_log('[MXCHAT] Cached ' . count($embeddings) . ' embeddings');
2585 + } else {
2586 + //error_log('[MXCHAT] Using ' . count($embeddings) . ' cached embeddings');
3176 2587 }
3177 2588
3178 2589 // Initialize array to store relevant results with similarity scores
3179 2590 $relevant_results = [];
2591 +
2592 + // Get the similarity threshold from the main options array only
2593 + $main_options = get_option('mxchat_options', []);
2594 + $similarity_threshold = isset($main_options['similarity_threshold'])
2595 + ? ((int) $main_options['similarity_threshold']) / 100
2596 + : 0.8; // Default to 80%
2597 +
2598 + //error_log('[MXCHAT] Using similarity threshold: ' . ($similarity_threshold * 100) . '%');
2599 +
3180 2600 // Iterate through embeddings to calculate similarity
3181 2601 foreach ($embeddings as $embedding) {
3182 2602 $database_embedding = $embedding->embedding_vector
3183 2603 ? unserialize($embedding->embedding_vector, ['allowed_classes' => false])
@@ -3183,8 +2603,14 @@
3183 2603 ? unserialize($embedding->embedding_vector, ['allowed_classes' => false])
3184 2604 : null;
3185 2605 if (is_array($database_embedding) && is_array($user_embedding)) {
3186 2606 $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $database_embedding);
2607 +
2608 + // Log each similarity score over 0.5 to reduce log spam
2609 + if ($similarity > 0.1) {
2610 + //error_log(sprintf('[MXCHAT] ID: %d | Similarity Score: %.4f', $embedding->id, $similarity));
2611 + }
2612 +
3187 2613 $relevant_results[] = [
3188 2614 'id' => $embedding->id,
3189 2615 'similarity' => $similarity
3190 2616 ];
@@ -3192,11 +2618,8 @@
3192 2618 // Free memory
3193 2619 unset($database_embedding);
3194 2620 }
3195 2621
3196 - // Retrieve the similarity threshold
3197 - $similarity_threshold = ((int) get_option('mxchat_similarity_threshold', 80)) / 100;
3198 -
3199 2622 // Filter and sort relevant results by similarity
3200 2623 $relevant_results = array_filter($relevant_results, function ($result) use ($similarity_threshold) {
3201 2624 return $result['similarity'] >= $similarity_threshold;
3202 2625 });
@@ -3203,11 +2626,20 @@
3203 2626 usort($relevant_results, function ($a, $b) {
3204 2627 return $b['similarity'] <=> $a['similarity'];
3205 2628 });
3206 2629
2630 + // Log number of results that met threshold
2631 + //error_log('[MXCHAT] ' . count($relevant_results) . ' results met the similarity threshold');
2632 +
3207 2633 // Limit to the top 5 results
3208 2634 $top_results = array_slice($relevant_results, 0, 5);
3209 2635
2636 + // Log the top matches
2637 + //error_log('[MXCHAT] Top matching results:');
2638 + foreach ($top_results as $index => $result) {
2639 +
2640 + }
2641 +
3210 2642 // Initialize the final content
3211 2643 $content = '';
3212 2644
3213 2645 // Fetch and combine content for the top results
@@ -3214,10 +2646,11 @@
3214 2646 foreach ($top_results as $result) {
3215 2647 $chunk_content = $this->fetch_content_with_product_links($result['id']);
3216 2648 // Check if the content is PDF-related and add surrounding pages
3217 2649 if (strpos($chunk_content, '{"document_type":"pdf"') !== false) {
2650 + //error_log('[MXCHAT] ID ' . $result['id'] . ' is PDF content, adding surrounding pages');
3218 2651 $surrounding_content = $wpdb->get_results($wpdb->prepare(
3219 - "SELECT article_content FROM {$system_prompt_table}
2652 + "SELECT id, article_content FROM {$system_prompt_table}
3220 2653 WHERE id IN (
3221 2654 (SELECT id FROM {$system_prompt_table} WHERE id < %d ORDER BY id DESC LIMIT 1),
3222 2655 (SELECT id FROM {$system_prompt_table} WHERE id > %d ORDER BY id ASC LIMIT 1)
3223 2656 )",
@@ -3239,29 +2672,38 @@
3239 2672 $content .= $chunk_content . "\n\n";
3240 2673 }
3241 2674 }
3242 2675
2676 + // Log content length
2677 + //error_log('[MXCHAT] Retrieved content length: ' . strlen(trim($content)) . ' characters');
2678 +
3243 2679 return trim($content);
3244 2680 }
3245 -/**
3246 - * Find relevant content in Pinecone vector database
3247 - */
2681 +
2682 +
3248 2683 private function find_relevant_content_pinecone($user_embedding) {
3249 2684 $options = get_option('mxchat_pinecone_addon_options', array());
3250 2685 $api_key = $options['mxchat_pinecone_api_key'] ?? '';
3251 2686 $host = $options['mxchat_pinecone_host'] ?? '';
3252 -
2687 +
3253 2688 if (empty($host) || empty($api_key)) {
3254 - //error_log('Pinecone credentials not properly configured');
2689 + //error_log('[MXCHAT Debug] Pinecone credentials not properly configured');
3255 2690 return '';
3256 2691 }
3257 -
3258 - // Get similarity threshold from WordPress settings
3259 - $similarity_threshold = ((int) get_option('mxchat_similarity_threshold', 80)) / 100;
3260 -
2692 +
2693 + // Get the similarity threshold from the main options array only
2694 + $main_options = get_option('mxchat_options', []);
2695 + $similarity_threshold = isset($main_options['similarity_threshold'])
2696 + ? ((int) $main_options['similarity_threshold']) / 100
2697 + : 0.8; // Default to 80%
2698 +
2699 + //error_log('[MXCHAT Debug] Using similarity threshold: ' . ($similarity_threshold * 100) . '%');
2700 +
3261 2701 // Prepare the query request for Pinecone
3262 2702 $api_endpoint = "https://{$host}/query";
3263 -
2703 +
2704 + //error_log('[MXCHAT Debug] Querying Pinecone at: ' . $api_endpoint);
2705 +
3264 2706 $request_body = array(
3265 2707 'vector' => $user_embedding,
3266 2708 'topK' => 5,
3267 2709 'includeMetadata' => true,
@@ -3266,9 +2708,9 @@
3266 2708 'topK' => 5,
3267 2709 'includeMetadata' => true,
3268 2710 'includeValues' => true
3269 2711 );
3270 -
2712 +
3271 2713 $response = wp_remote_post($api_endpoint, array(
3272 2714 'headers' => array(
3273 2715 'Api-Key' => $api_key,
3274 2716 'accept' => 'application/json',
@@ -3276,35 +2718,43 @@
3276 2718 ),
3277 2719 'body' => wp_json_encode($request_body),
3278 2720 'timeout' => 30
3279 2721 ));
3280 -
2722 +
3281 2723 if (is_wp_error($response)) {
3282 - //error_log('Pinecone query error: ' . $response->get_error_message());
2724 + //error_log('[MXCHAT Debug] Pinecone query error: ' . $response->get_error_message());
3283 2725 return '';
3284 2726 }
3285 -
2727 +
3286 2728 $response_code = wp_remote_retrieve_response_code($response);
3287 2729 if ($response_code !== 200) {
3288 - //error_log('Pinecone API error: ' . wp_remote_retrieve_body($response));
2730 + //error_log('[MXCHAT Debug] Pinecone API error: ' . wp_remote_retrieve_body($response));
3289 2731 return '';
3290 2732 }
3291 -
2733 +
3292 2734 $results = json_decode(wp_remote_retrieve_body($response), true);
3293 2735 if (empty($results['matches'])) {
2736 + //error_log('[MXCHAT Debug] No matches found in Pinecone response');
3294 2737 return '';
3295 2738 }
3296 -
2739 +
2740 + //error_log('[MXCHAT Debug] Found ' . count($results['matches']) . ' matches in Pinecone');
2741 +
3297 2742 // Initialize the final content
3298 2743 $content = '';
2744 + $matches_above_threshold = 0;
2745 +
2746 + // Process each match
2747 + foreach ($results['matches'] as $index => $match) {
2748 + // Log score for each match
3299 2749
3300 - // Process each match
3301 - foreach ($results['matches'] as $match) {
3302 2750 // Skip if similarity is below threshold
3303 2751 if ($match['score'] < $similarity_threshold) {
3304 2752 continue;
3305 2753 }
3306 -
2754 +
2755 + $matches_above_threshold++;
2756 +
3307 2757 if (!empty($match['metadata']['text']) && !empty($match['metadata']['source_url'])) {
3308 2758 // Add content with citation
3309 2759 $content .= $match['metadata']['text'] . "\n";
3310 2760 $content .= "Source: " . $match['metadata']['source_url'] . "\n\n";
@@ -3309,13 +2759,15 @@
3309 2759 $content .= $match['metadata']['text'] . "\n";
3310 2760 $content .= "Source: " . $match['metadata']['source_url'] . "\n\n";
3311 2761 }
3312 2762 }
3313 -
2763 +
2764 + //error_log('[MXCHAT Debug] Total matches used (above threshold): ' . $matches_above_threshold);
2765 + //error_log('[MXCHAT Debug] Content length returned: ' . strlen(trim($content)) . ' characters');
2766 +
3314 2767 return trim($content);
3315 2768 }
3316 2769
3317 -
3318 2770 private function mxchat_find_relevant_products($user_embedding) {
3319 2771 //error_log('MXChat Vector Search: Starting product search...');
3320 2772
3321 2773 // Retrieve the add-on settings from the database
@@ -3671,9 +3123,8 @@
3671 3123 //error_log('DeepSeek API Response Format Error: ' . print_r($decoded_response, true));
3672 3124 return "Sorry, I couldn't process that request.";
3673 3125 }
3674 3126 }
3675 -
3676 3127 private function mxchat_generate_response_openai($selected_model, $api_key, $conversation_history, $relevant_content) {
3677 3128 // Ensure conversation_history is an array
3678 3129 if (!is_array($conversation_history)) {
3679 3130 $conversation_history = array();
@@ -3817,9 +3268,8 @@
3817 3268 } else {
3818 3269 return "Sorry, I couldn't process that request.";
3819 3270 }
3820 3271 }
3821 -
3822 3272 private function mxchat_generate_response_claude($selected_model, $claude_api_key, $conversation_history, $relevant_content) {
3823 3273 // Get system prompt instructions from options
3824 3274 $system_prompt_instructions = isset($this->options['system_prompt_instructions']) ? $this->options['system_prompt_instructions'] : '';
3825 3275
@@ -3918,8 +3368,11 @@
3918 3368 // Log unexpected response format
3919 3369 //error_log("Claude API unexpected response format: " . print_r($response_body, true));
3920 3370 return "Sorry, I received an unexpected response format from the API.";
3921 3371 }
3372 +
3373 +
3374 +
3922 3375 public function mxchat_dismiss_pre_chat_message() {
3923 3376 // Get and sanitize the user identifier
3924 3377 $user_id = $this->mxchat_get_user_identifier();
3925 3378 $user_id = sanitize_key($user_id);
@@ -3975,10 +3428,10 @@
3975 3428 }
3976 3429
3977 3430 public function mxchat_enqueue_scripts_styles() {
3978 3431 // 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
3432 + $chat_style_version = '2.1.1'; // Replace with your actual version
3433 + $chat_script_version = '2.1.1'; // Replace with your actual version
3981 3434
3982 3435 // Enqueue the script
3983 3436 wp_enqueue_script(
3984 3437 'mxchat-chat-js',