| @@ -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'], |
| @@ -1131,18 +1156,20 @@ | ||
| 1131 | 1156 | $response_html = ''; |
| 1132 | 1157 | //error_log("DALL-E image generation error: " . esc_html($image_response['error'] ?? 'Unknown error.')); |
| 1133 | 1158 | } |
| 1134 | 1159 | |
| 1135 | - // Save both text and HTML responses | |
| 1136 | - $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 | + ]; | |
| 1137 | 1171 | |
| 1138 | - // Prepare the response data | |
| 1139 | - $response_data = [ | |
| 1140 | - 'message' => $response_text, | |
| 1141 | - 'html' => $response_html, | |
| 1142 | - 'image_url' => $image_url ?? '', | |
| 1143 | - ]; | |
| 1144 | - | |
| 1145 | 1172 | // Send the JSON response |
| 1146 | 1173 | header('Content-Type: application/json; charset=' . get_option('blog_charset')); |
| 1147 | 1174 | echo json_encode($response_data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); |
| 1148 | 1175 | wp_die(); |
| @@ -2429,42 +2456,64 @@ | ||
| 2429 | 2456 | return MxChat_User::mxchat_get_user_identifier(); |
| 2430 | 2457 | } |
| 2431 | 2458 | |
| 2432 | 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 { | |
| 2433 | 2469 | $endpoint = 'https://api.openai.com/v1/embeddings'; |
| 2434 | - | |
| 2435 | - $body = wp_json_encode([ | |
| 2436 | - 'input' => $text, | |
| 2437 | - 'model' => 'text-embedding-ada-002' | |
| 2438 | - ]); | |
| 2439 | - | |
| 2440 | - $args = [ | |
| 2441 | - 'body' => $body, | |
| 2442 | - 'headers' => [ | |
| 2443 | - 'Content-Type' => 'application/json', | |
| 2444 | - 'Authorization' => 'Bearer ' . $api_key, | |
| 2445 | - ], | |
| 2446 | - 'timeout' => 60, | |
| 2447 | - 'redirection' => 5, | |
| 2448 | - 'blocking' => true, | |
| 2449 | - 'httpversion' => '1.0', | |
| 2450 | - 'sslverify' => true, | |
| 2451 | - ]; | |
| 2452 | - | |
| 2453 | - $response = wp_remote_post($endpoint, $args); | |
| 2454 | - | |
| 2455 | - if (is_wp_error($response)) { | |
| 2456 | - return null; | |
| 2457 | - } | |
| 2458 | - | |
| 2459 | - $response_body = json_decode(wp_remote_retrieve_body($response), true); | |
| 2460 | - | |
| 2461 | - if (isset($response_body['data'][0]['embedding']) && is_array($response_body['data'][0]['embedding'])) { | |
| 2462 | - return $response_body['data'][0]['embedding']; | |
| 2463 | - } else { | |
| 2464 | - return null; | |
| 2465 | - } | |
| 2470 | + // Use the passed API key for OpenAI | |
| 2466 | 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 | +} | |
| 2467 | 2516 | |
| 2468 | 2517 | |
| 2469 | 2518 | private function mxchat_find_relevant_content($user_embedding) { |
| 2470 | 2519 | //error_log('MXChat Vector Search: Starting content search...'); |
| @@ -2486,8 +2535,9 @@ | ||
| 2486 | 2535 | return $this->find_relevant_content_wordpress($user_embedding); |
| 2487 | 2536 | } |
| 2488 | 2537 | } |
| 2489 | 2538 | |
| 2539 | + | |
| 2490 | 2540 | private function find_relevant_content_wordpress($user_embedding) { |
| 2491 | 2541 | global $wpdb; |
| 2492 | 2542 | $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content'; |
| 2493 | 2543 | $cache_key = 'mxchat_system_prompt_embeddings'; |
| @@ -2492,11 +2542,15 @@ | ||
| 2492 | 2542 | $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content'; |
| 2493 | 2543 | $cache_key = 'mxchat_system_prompt_embeddings'; |
| 2494 | 2544 | $batch_size = 500; |
| 2495 | 2545 | |
| 2546 | + // Log start of matching process | |
| 2547 | + error_log('[MXCHAT] Starting similarity matching process'); | |
| 2548 | + | |
| 2496 | 2549 | // Retrieve embeddings from cache or database |
| 2497 | 2550 | $embeddings = wp_cache_get($cache_key, 'mxchat_system_prompts'); |
| 2498 | 2551 | if ($embeddings === false) { |
| 2552 | + error_log('[MXCHAT] Cache miss - loading embeddings from database'); | |
| 2499 | 2553 | $embeddings = []; |
| 2500 | 2554 | $offset = 0; |
| 2501 | 2555 | |
| 2502 | 2556 | // Load in batches and build cache |
| @@ -2522,15 +2576,24 @@ | ||
| 2522 | 2576 | |
| 2523 | 2577 | } while (true); |
| 2524 | 2578 | |
| 2525 | 2579 | if (empty($embeddings)) { |
| 2580 | + error_log('[MXCHAT] No embeddings found in database'); | |
| 2526 | 2581 | return ''; // Return an empty string if no embeddings found |
| 2527 | 2582 | } |
| 2528 | 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'); | |
| 2529 | 2587 | } |
| 2530 | 2588 | |
| 2531 | 2589 | // Initialize array to store relevant results with similarity scores |
| 2532 | 2590 | $relevant_results = []; |
| 2591 | + | |
| 2592 | + // Retrieve the similarity threshold | |
| 2593 | + $similarity_threshold = ((int) get_option('mxchat_similarity_threshold', 80)) / 100; | |
| 2594 | + error_log('[MXCHAT] Using similarity threshold: ' . ($similarity_threshold * 100) . '%'); | |
| 2595 | + | |
| 2533 | 2596 | // Iterate through embeddings to calculate similarity |
| 2534 | 2597 | foreach ($embeddings as $embedding) { |
| 2535 | 2598 | $database_embedding = $embedding->embedding_vector |
| 2536 | 2599 | ? unserialize($embedding->embedding_vector, ['allowed_classes' => false]) |
| @@ -2536,8 +2599,14 @@ | ||
| 2536 | 2599 | ? unserialize($embedding->embedding_vector, ['allowed_classes' => false]) |
| 2537 | 2600 | : null; |
| 2538 | 2601 | if (is_array($database_embedding) && is_array($user_embedding)) { |
| 2539 | 2602 | $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $database_embedding); |
| 2603 | + | |
| 2604 | + // Log each similarity score over 0.5 to reduce log spam | |
| 2605 | + if ($similarity > 0.1) { | |
| 2606 | + error_log(sprintf('[MXCHAT] ID: %d | Similarity Score: %.4f', $embedding->id, $similarity)); | |
| 2607 | + } | |
| 2608 | + | |
| 2540 | 2609 | $relevant_results[] = [ |
| 2541 | 2610 | 'id' => $embedding->id, |
| 2542 | 2611 | 'similarity' => $similarity |
| 2543 | 2612 | ]; |
| @@ -2545,11 +2614,8 @@ | ||
| 2545 | 2614 | // Free memory |
| 2546 | 2615 | unset($database_embedding); |
| 2547 | 2616 | } |
| 2548 | 2617 | |
| 2549 | - // Retrieve the similarity threshold | |
| 2550 | - $similarity_threshold = ((int) get_option('mxchat_similarity_threshold', 80)) / 100; | |
| 2551 | - | |
| 2552 | 2618 | // Filter and sort relevant results by similarity |
| 2553 | 2619 | $relevant_results = array_filter($relevant_results, function ($result) use ($similarity_threshold) { |
| 2554 | 2620 | return $result['similarity'] >= $similarity_threshold; |
| 2555 | 2621 | }); |
| @@ -2556,11 +2622,24 @@ | ||
| 2556 | 2622 | usort($relevant_results, function ($a, $b) { |
| 2557 | 2623 | return $b['similarity'] <=> $a['similarity']; |
| 2558 | 2624 | }); |
| 2559 | 2625 | |
| 2626 | + // Log number of results that met threshold | |
| 2627 | + error_log('[MXCHAT] ' . count($relevant_results) . ' results met the similarity threshold'); | |
| 2628 | + | |
| 2560 | 2629 | // Limit to the top 5 results |
| 2561 | 2630 | $top_results = array_slice($relevant_results, 0, 5); |
| 2562 | 2631 | |
| 2632 | + // Log the top matches | |
| 2633 | + error_log('[MXCHAT] Top matching results:'); | |
| 2634 | + foreach ($top_results as $index => $result) { | |
| 2635 | + error_log(sprintf('[MXCHAT] %d. ID: %d | Score: %.4f', | |
| 2636 | + $index + 1, | |
| 2637 | + $result['id'], | |
| 2638 | + $result['similarity'] | |
| 2639 | + )); | |
| 2640 | + } | |
| 2641 | + | |
| 2563 | 2642 | // Initialize the final content |
| 2564 | 2643 | $content = ''; |
| 2565 | 2644 | |
| 2566 | 2645 | // Fetch and combine content for the top results |
| @@ -2567,10 +2646,11 @@ | ||
| 2567 | 2646 | foreach ($top_results as $result) { |
| 2568 | 2647 | $chunk_content = $this->fetch_content_with_product_links($result['id']); |
| 2569 | 2648 | // Check if the content is PDF-related and add surrounding pages |
| 2570 | 2649 | if (strpos($chunk_content, '{"document_type":"pdf"') !== false) { |
| 2650 | + error_log('[MXCHAT] ID ' . $result['id'] . ' is PDF content, adding surrounding pages'); | |
| 2571 | 2651 | $surrounding_content = $wpdb->get_results($wpdb->prepare( |
| 2572 | - "SELECT article_content FROM {$system_prompt_table} | |
| 2652 | + "SELECT id, article_content FROM {$system_prompt_table} | |
| 2573 | 2653 | WHERE id IN ( |
| 2574 | 2654 | (SELECT id FROM {$system_prompt_table} WHERE id < %d ORDER BY id DESC LIMIT 1), |
| 2575 | 2655 | (SELECT id FROM {$system_prompt_table} WHERE id > %d ORDER BY id ASC LIMIT 1) |
| 2576 | 2656 | )", |
| @@ -2592,10 +2672,15 @@ | ||
| 2592 | 2672 | $content .= $chunk_content . "\n\n"; |
| 2593 | 2673 | } |
| 2594 | 2674 | } |
| 2595 | 2675 | |
| 2676 | + // Log content length | |
| 2677 | + error_log('[MXCHAT] Retrieved content length: ' . strlen(trim($content)) . ' characters'); | |
| 2678 | + | |
| 2596 | 2679 | return trim($content); |
| 2597 | 2680 | } |
| 2681 | + | |
| 2682 | + | |
| 2598 | 2683 | /** |
| 2599 | 2684 | * Find relevant content in Pinecone vector database |
| 2600 | 2685 | */ |
| 2601 | 2686 | private function find_relevant_content_pinecone($user_embedding) { |
| @@ -3024,9 +3109,8 @@ | ||
| 3024 | 3109 | //error_log('DeepSeek API Response Format Error: ' . print_r($decoded_response, true)); |
| 3025 | 3110 | return "Sorry, I couldn't process that request."; |
| 3026 | 3111 | } |
| 3027 | 3112 | } |
| 3028 | - | |
| 3029 | 3113 | private function mxchat_generate_response_openai($selected_model, $api_key, $conversation_history, $relevant_content) { |
| 3030 | 3114 | // Ensure conversation_history is an array |
| 3031 | 3115 | if (!is_array($conversation_history)) { |
| 3032 | 3116 | $conversation_history = array(); |
| @@ -3170,9 +3254,8 @@ | ||
| 3170 | 3254 | } else { |
| 3171 | 3255 | return "Sorry, I couldn't process that request."; |
| 3172 | 3256 | } |
| 3173 | 3257 | } |
| 3174 | - | |
| 3175 | 3258 | private function mxchat_generate_response_claude($selected_model, $claude_api_key, $conversation_history, $relevant_content) { |
| 3176 | 3259 | // Get system prompt instructions from options |
| 3177 | 3260 | $system_prompt_instructions = isset($this->options['system_prompt_instructions']) ? $this->options['system_prompt_instructions'] : ''; |
| 3178 | 3261 | |
| @@ -3271,8 +3354,11 @@ | ||
| 3271 | 3354 | // Log unexpected response format |
| 3272 | 3355 | //error_log("Claude API unexpected response format: " . print_r($response_body, true)); |
| 3273 | 3356 | return "Sorry, I received an unexpected response format from the API."; |
| 3274 | 3357 | } |
| 3358 | + | |
| 3359 | + | |
| 3360 | + | |
| 3275 | 3361 | public function mxchat_dismiss_pre_chat_message() { |
| 3276 | 3362 | // Get and sanitize the user identifier |
| 3277 | 3363 | $user_id = $this->mxchat_get_user_identifier(); |
| 3278 | 3364 | $user_id = sanitize_key($user_id); |
| @@ -3328,10 +3414,10 @@ | ||
| 3328 | 3414 | } |
| 3329 | 3415 | |
| 3330 | 3416 | public function mxchat_enqueue_scripts_styles() { |
| 3331 | 3417 | // Define version numbers for the styles and scripts |
| 3332 | - $chat_style_version = '2.0.4'; // Replace with your actual version | |
| 3333 | - $chat_script_version = '2.0.4'; // Replace with your actual version | |
| 3418 | + $chat_style_version = '2.0.7'; // Replace with your actual version | |
| 3419 | + $chat_script_version = '2.0.7'; // Replace with your actual version | |
| 3334 | 3420 | |
| 3335 | 3421 | // Enqueue the script |
| 3336 | 3422 | wp_enqueue_script( |
| 3337 | 3423 | 'mxchat-chat-js', |