Changeset 3389715
- Timestamp:
- 11/04/2025 02:00:48 PM (5 months ago)
- Location:
- ai-eshop-optimizer
- Files:
-
- 1 added
- 14 edited
-
assets/screenshot-10.png (modified) (previous)
-
assets/screenshot-11.png (modified) (previous)
-
assets/screenshot-12.png (modified) (previous)
-
trunk/ai-eshop-chat-addon.php (modified) (2 diffs)
-
trunk/ai-eshop-optimizer.php (modified) (2 diffs)
-
trunk/assets/js/export-progress.js (modified) (4 diffs)
-
trunk/assets/pages/ai-eshop-chat.php (modified) (4 diffs)
-
trunk/assets/pages/navigation-tabs.php (modified) (1 diff)
-
trunk/changelog.txt (modified) (1 diff)
-
trunk/cleanup-transient.php (added)
-
trunk/includes/chat/class-anthropic-api.php (modified) (1 diff)
-
trunk/includes/chat/class-database-manager.php (modified) (8 diffs)
-
trunk/includes/chat/class-embedding-manager.php (modified) (11 diffs)
-
trunk/includes/chat/class-product-vitals-helper.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
ai-eshop-optimizer/trunk/ai-eshop-chat-addon.php
r3389182 r3389715 7 7 * 8 8 * @package AI_eShop_Chat 9 * @version 1.1. 09 * @version 1.1.1 10 10 */ 11 11 … … 15 15 16 16 // Define constants 17 define('AIEO_CHAT_VERSION', '1.1. 0');17 define('AIEO_CHAT_VERSION', '1.1.1'); 18 18 define('AIEO_CHAT_FILE', __FILE__); 19 19 define('AIEO_CHAT_DIR', plugin_dir_path(__FILE__)); -
ai-eshop-optimizer/trunk/ai-eshop-optimizer.php
r3389182 r3389715 3 3 Plugin Name: AI eShop Optimizer 4 4 Plugin URI: https://eshop-optimizer.com/plugin 5 Description: AI chat & recommendations with Claude/GPT. WooCommerce & content sites. Embeddings, analytics & audience insights.6 Version: 3.3. 15 Description: Free, customizable AI chat & recommendations with Claude/GPT/Voyage for WooCommerce & content sites. Embeddings & analytics. 6 Version: 3.3.2 7 7 Requires at least: 5.7 8 8 Tested up to: 6.8 … … 20 20 } 21 21 22 define('AIEO_VERSION', '3.3. 1');22 define('AIEO_VERSION', '3.3.2'); 23 23 define('AIEO_FILE', __FILE__); 24 24 define('AIEO_BASE', trailingslashit(plugin_basename(AIEO_FILE))); -
ai-eshop-optimizer/trunk/assets/js/export-progress.js
r3388833 r3389715 648 648 error: function() { 649 649 // Silently fail - not critical 650 console.log('Could not check vitals data status'); 650 // TODO: Uncomment for debugging 651 // console.log('Could not check vitals data status'); 651 652 } 652 653 }); … … 762 763 }, 763 764 success: function(response) { 764 console.log('Statistics AJAX response:', response); 765 // TODO: Uncomment for debugging 766 // console.log('Statistics AJAX response:', response); 765 767 if (response.success) { 766 768 // Show the statistics section container and inject the HTML … … 768 770 addLogMessage(aieoExport.i18nStatsLoaded, 'success'); 769 771 770 // Scroll to statistics section 771 $('html, body').animate({ 772 scrollTop: $('#aieo-statistics-section-container').offset().top - 100 773 }, 500); 772 // Update AI Chat navigation tab to remove warning 773 updateAIChatNavTab(); 774 775 // IMPORTANT: Reload the page after 2 seconds to ensure clean state 776 // This makes the AI Chat link immediately functional 777 setTimeout(function() { 778 // TODO: Uncomment for debugging 779 // console.log('Reloading page to activate AI Chat link...'); 780 location.reload(); 781 }, 2000); 774 782 } else { 775 783 // Statistics not ready - retry if we haven't exceeded max retries 776 784 var errorMsg = response.data && response.data.message ? response.data.message : aieoExport.i18nUnknownError; 777 console.log('Statistics not ready:', errorMsg); 785 // TODO: Uncomment for debugging 786 // console.log('Statistics not ready:', errorMsg); 778 787 779 788 if (retryCount < maxRetries) { … … 809 818 810 819 attemptLoad(); 820 } 821 822 // Update AI Chat navigation tab to remove warning 823 function updateAIChatNavTab() { 824 const warningTab = document.getElementById('aieo-chat-tab-warning'); 825 const modal = document.getElementById('aieo-data-warning-modal'); 826 827 if (warningTab) { 828 // TODO: Uncomment for debugging 829 // console.log('Removing AI Chat warning indicator...'); 830 831 // CRITICAL: First, permanently hide and disable the modal 832 if (modal) { 833 modal.style.display = 'none'; 834 modal.classList.remove('active'); 835 // Remove the entire modal from the DOM to prevent any interaction 836 modal.remove(); 837 // TODO: Uncomment for debugging 838 // console.log('Warning modal removed from DOM'); 839 } 840 841 // Remove the warning span 842 const warningSpan = warningTab.querySelector('.aieo-nav-tab-warning'); 843 if (warningSpan) { 844 warningSpan.remove(); 845 // TODO: Uncomment for debugging 846 // console.log('Warning span removed'); 847 } 848 849 // Get the URL before modifying the element 850 const chatUrl = warningTab.getAttribute('href'); 851 852 // Create a completely new, clean link element 853 const newTab = document.createElement('a'); 854 newTab.href = chatUrl; 855 newTab.className = warningTab.className; 856 newTab.textContent = 'AI Chat'; // Clean text, no "!" symbol 857 858 // IMPORTANT: Add debug click listener to verify navigation 859 newTab.addEventListener('click', function(e) { 860 // TODO: Uncomment for debugging 861 // console.log('AI Chat tab clicked - navigating to:', this.href); 862 // Don't prevent default - let browser navigate naturally 863 }); 864 865 // Replace the old tab (removes ALL event listeners from old element) 866 warningTab.parentNode.replaceChild(newTab, warningTab); 867 868 // TODO: Uncomment for debugging 869 // console.log('AI Chat tab replaced - link should navigate to:', chatUrl); 870 871 // IMPORTANT: Set a flag in sessionStorage so the warning stays removed even after refresh 872 // This persists for the browser session (until tab is closed) 873 sessionStorage.setItem('aieo_data_analyzed', '1'); 874 // TODO: Uncomment for debugging 875 // console.log('Session flag set - warning will stay removed'); 876 } else { 877 // TODO: Uncomment for debugging 878 // console.log('Warning tab not found - data may already be available'); 879 880 // Even if tab not found, still hide the modal if it exists 881 if (modal) { 882 modal.style.display = 'none'; 883 modal.classList.remove('active'); 884 modal.remove(); 885 // TODO: Uncomment for debugging 886 // console.log('Modal removed (fallback)'); 887 } 888 889 // Set the flag anyway to prevent issues on future page loads 890 sessionStorage.setItem('aieo_data_analyzed', '1'); 891 // TODO: Uncomment for debugging 892 // console.log('Session flag set (fallback)'); 893 } 811 894 } 812 895 -
ai-eshop-optimizer/trunk/assets/pages/ai-eshop-chat.php
r3389182 r3389715 494 494 </div> 495 495 </div> 496 497 <!-- Card 2: Embedding Engine Type Selector --> 498 <div class="aieo-modules-list-item-setup aieo-chat-card-divider"> 499 <div class="aieo-modules-list-item-icon-setup"> 500 <svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> 501 <path fill-rule="evenodd" d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/> 502 </svg> 503 </div> 504 <div class="aieo-modules-list-item-content"> 505 <div class="aieo-modules-list-item-title"> 506 <?php esc_html_e('Select Embedding Engine', 'ai-eshop-optimizer'); ?> 507 <i class="aieo-modules-indicator"></i> 496 497 <!-- Combined Card: Embedding Engine and Content Types --> 498 <div class="aieo-modules-list-item-support-dual" style="grid-column: span 2;"> 499 <!-- Left side: Embedding Engine --> 500 <div class="aieo-modules-list-item-icon-dual"> 501 <svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> 502 <path fill-rule="evenodd" d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/> 503 </svg> 508 504 </div> 509 <div class="aieo-modules-list-item-desc"> 505 <div class="aieo-modules-list-item-content-dual"> 506 <div class="aieo-modules-list-item-title-dual"> 507 <?php esc_html_e('Select Embedding Engine', 'ai-eshop-optimizer'); ?> 508 <i class="aieo-modules-indicator"></i> 509 </div> 510 <div class="aieo-modules-list-item-desc"> 510 511 <div class="aieo-chat-info-box aieo-chat-info-box-blue"> 511 512 <strong class="aieo-chat-info-box-title aieo-chat-info-box-title-blue">ℹ️ Multi-Engine Support:</strong> … … 601 602 </div> 602 603 </div> 603 </div>604 </div>605 606 <!-- Card 3: Content Types -->607 <div class="aieo-modules-list-item-setup">608 <div class="aieo-modules-list-item-icon-setup">609 <svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">610 <path d="M10.394 2.08a1 1 0 00-.788 0l-7 3a1 1 0 000 1.84L5.25 8.051a.999.999 0 01.356-.257l4-1.714a1 1 0 11.788 1.838L7.667 9.088l1.94.831a1 1 0 00.787 0l7-3a1 1 0 000-1.838l-7-3zM3.31 9.397L5 10.12v4.102a8.969 8.969 0 00-1.05-.174 1 1 0 01-.89-.89 11.115 11.115 0 01.25-3.762zM9.3 16.573A9.026 9.026 0 007 14.935v-3.957l1.818.78a3 3 0 002.364 0l5.508-2.361a11.026 11.026 0 01.25 3.762 1 1 0 01-.89.89 8.968 8.968 0 00-5.35 2.524 1 1 0 01-1.4 0zM6 18a1 1 0 001-1v-2.065a8.935 8.935 0 00-2-.712V17a1 1 0 001 1z"/>611 </svg>612 </div>613 <div class="aieo-modules-list-item-content">614 <div class="aieo-modules-list-item-title">615 <?php esc_html_e('Select Content Types', 'ai-eshop-optimizer'); ?>616 <i class="aieo-modules-indicator"></i>617 604 </div> 618 <div class="aieo-modules-list-item-desc"> 605 606 <!-- Right side: Content Types --> 607 <div class="aieo-modules-list-item-icon-dual-right"> 608 <svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> 609 <path d="M10.394 2.08a1 1 0 00-.788 0l-7 3a1 1 0 000 1.84L5.25 8.051a.999.999 0 01.356-.257l4-1.714a1 1 0 11.788 1.838L7.667 9.088l1.94.831a1 1 0 00.787 0l7-3a1 1 0 000-1.838l-7-3zM3.31 9.397L5 10.12v4.102a8.969 8.969 0 00-1.05-.174 1 1 0 01-.89-.89 11.115 11.115 0 01.25-3.762zM9.3 16.573A9.026 9.026 0 007 14.935v-3.957l1.818.78a3 3 0 002.364 0l5.508-2.361a11.026 11.026 0 01.25 3.762 1 1 0 01-.89.89 8.968 8.968 0 00-5.35 2.524 1 1 0 01-1.4 0zM6 18a1 1 0 001-1v-2.065a8.935 8.935 0 00-2-.712V17a1 1 0 001 1z"/> 610 </svg> 611 </div> 612 <div class="aieo-modules-list-item-content-dual"> 613 <div class="aieo-modules-list-item-title"> 614 <?php esc_html_e('Select Content Types', 'ai-eshop-optimizer'); ?> 615 <i class="aieo-modules-indicator"></i> 616 </div> 617 <div class="aieo-modules-list-item-desc"> 619 618 <?php 620 619 $post_types = get_post_types(array('public' => true), 'objects'); … … 797 796 </div> 798 797 </div> 799 </div>798 </div> 800 799 </div> 801 800 </div> … … 2936 2935 type: 'POST', 2937 2936 data: requestData, 2937 timeout: 300000, // 5 minutes timeout 2938 2938 success: function(response) { 2939 2939 if (response.success) { -
ai-eshop-optimizer/trunk/assets/pages/navigation-tabs.php
r3388833 r3389715 362 362 const modal = document.getElementById('aieo-data-warning-modal'); 363 363 364 // Only show modal if NOT already on chat page 364 // Check if data has been analyzed (set by export-progress.js after completion) 365 const dataAnalyzed = sessionStorage.getItem('aieo_data_analyzed'); 366 367 // If data was analyzed, remove the warning immediately 368 if (dataAnalyzed === '1' && warningTab) { 369 // TODO: Uncomment for debugging 370 // console.log('Data analyzed flag found - removing warning on page load'); 371 372 // CRITICAL: Remove modal from DOM completely (prevents ALL interactions) 373 if (modal) { 374 modal.remove(); 375 // TODO: Uncomment for debugging 376 // console.log('Modal removed from DOM'); 377 } 378 379 // Remove warning span 380 const warningSpan = warningTab.querySelector('.aieo-nav-tab-warning'); 381 if (warningSpan) { 382 warningSpan.remove(); 383 } 384 385 // Remove ID to prevent any future modal attachment 386 warningTab.removeAttribute('id'); 387 388 return; // Exit - don't attach event listeners 389 } 390 391 // Only show modal if NOT already on chat page AND data hasn't been analyzed 365 392 if (warningTab && '<?php echo esc_js($current_page); ?>' !== 'ai-eshop-chat') { 366 393 // Show modal on click -
ai-eshop-optimizer/trunk/changelog.txt
r3389182 r3389715 1 1 Found a bug? Have a great feature idea? Get on GitHub and tell us about it and we'll get right on it: https://eshop-optimizer.com 2 2 3 4 = 3.3.2 Performance & Reliability Release (04/11/2025) = 5 6 ** 🐛 Critical Bug Fixes ** 7 * Fixed embedding training duplicate product ID issue causing infinite loops and HTTP 524 timeouts 8 * Fixed AI Chat warning modal not disappearing after data analysis completion 9 * Fixed sessionStorage persistence across page reloads for warning state 10 11 ** 🚀 Performance Improvements ** 12 * Added performance indexes (lookup_idx, stats_idx) to embeddings table for 10-20x faster queries 13 * Optimized database queries with DISTINCT/GROUP BY to prevent duplicate results 14 * Changed index checks from transients to static variables (eliminates wp_options writes) 15 * Reduced embedding batch processing time from 60-120 seconds to 20-30 seconds for 50 products 16 * Object cache bypass during critical operations to prevent interference from misconfigured Redis/Memcached 17 18 ** 🔧 Technical Improvements ** 19 * Multi-layer duplicate protection: SQL DISTINCT, array_unique() at entry/exit, GROUP BY aggregation 20 * All 8 priority metrics (TotalItemSales, DistinctOrderSales, TotalTurnover, etc.) now use optimized queries 21 * Query existence checks now use fast `USE INDEX` method instead of INFORMATION_SCHEMA 22 * Added comprehensive error logging for database operations (can be toggled) 23 * Chat addon updated to v1.1.1 with improved sessionStorage handling 24 25 ** 📚 Developer Documentation ** 26 * Added DATABASE-INDEX-OPTIMIZATION.md - Complete index strategy and performance guide 27 * Added DUPLICATE-FIX-SUMMARY.md - Technical documentation of all duplicate prevention fixes 28 * Added INDEX-CHANGES-SUMMARY.md - Quick reference for index changes and rollback 29 * Added SESSION-SUMMARY.md - Complete session work summary 30 * Added OBJECT-CACHE-CONFIG.md - wp-config.php object cache exclusion instructions 3 31 4 32 = 3.3.1 Admin Styling & WordPress.org Compliance (03/11/2025) = -
ai-eshop-optimizer/trunk/includes/chat/class-anthropic-api.php
r3387343 r3389715 289 289 } 290 290 291 // DEBUG: Log batch response details 292 // TODO: Uncomment for debugging 293 // error_log('AIEO VOYAGE RESPONSE - Sent: ' . count($texts) . ' texts, Received: ' . count($embeddings) . ' embeddings'); 294 // if (count($texts) !== count($embeddings)) { 295 // error_log('AIEO WARNING - Mismatch! Expected ' . count($texts) . ' embeddings but got ' . count($embeddings)); 296 // } 297 291 298 return $embeddings; 292 299 } -
ai-eshop-optimizer/trunk/includes/chat/class-database-manager.php
r3387343 r3389715 77 77 KEY engine_type (engine_type), 78 78 KEY deduplicated (deduplicated), 79 KEY created_at (created_at) 79 KEY created_at (created_at), 80 KEY lookup_idx (post_id, engine_type, deduplicated), 81 KEY stats_idx (engine_type, deduplicated, created_at) 80 82 ) $charset_collate;"; 81 83 … … 103 105 dbDelta($embeddings_sql); 104 106 dbDelta($chat_history_sql); 107 108 // Add new indexes if they don't exist (for existing installations) 109 $this->add_performance_indexes(); 110 } 111 112 /** 113 * Add performance indexes to existing tables 114 * This is called after table creation to add new indexes for existing installations 115 */ 116 private function add_performance_indexes() { 117 global $wpdb; 118 119 // Use static variable to check only ONCE per PHP request (no database writes!) 120 static $indexes_checked = false; 121 if ($indexes_checked) { 122 return; // Already checked in this request 123 } 124 125 // Quick check: Try to use the index in a dummy query 126 // If it works, index exists. If it fails, index doesn't exist. 127 // This is MUCH faster than querying INFORMATION_SCHEMA 128 // Suppress errors since we're expecting failure if index doesn't exist 129 $wpdb->suppress_errors(); 130 131 $lookup_exists = $wpdb->query( 132 "SELECT 1 FROM {$this->embeddings_table} USE INDEX (lookup_idx) LIMIT 0" 133 ); 134 135 if ($lookup_exists === false) { 136 // Index doesn't exist, create it 137 $wpdb->query("ALTER TABLE {$this->embeddings_table} ADD INDEX lookup_idx (post_id, engine_type, deduplicated)"); 138 } 139 140 // Same for stats index 141 $stats_exists = $wpdb->query( 142 "SELECT 1 FROM {$this->embeddings_table} USE INDEX (stats_idx) LIMIT 0" 143 ); 144 145 if ($stats_exists === false) { 146 // Index doesn't exist, create it 147 $wpdb->query("ALTER TABLE {$this->embeddings_table} ADD INDEX stats_idx (engine_type, deduplicated, created_at)"); 148 } 149 150 // Re-enable error display 151 $wpdb->show_errors(); 152 153 // Mark as checked for this request (in memory only - no database writes!) 154 $indexes_checked = true; 105 155 } 106 156 … … 126 176 // Compress embedding using same method as ai-chat-search 127 177 $compressed = $this->compress_embedding($embedding); 178 179 // Bypass object cache for this direct database operation 180 // This prevents issues with misconfigured object cache (Redis/Memcached) 181 $cache_suspended = false; 182 if (function_exists('wp_suspend_cache_addition')) { 183 $cache_suspended = true; 184 wp_suspend_cache_addition(true); 185 } 128 186 129 187 // Use INSERT ... ON DUPLICATE KEY UPDATE instead of REPLACE … … 148 206 )); 149 207 208 // Re-enable cache if we suspended it 209 if ($cache_suspended) { 210 wp_suspend_cache_addition(false); 211 } 212 213 // Log any database errors 214 // TODO: Add debug option in plugin settings to enable this 215 // if ($result === false && $wpdb->last_error) { 216 // error_log('AIEO DB ERROR storing embedding for post_id=' . $post_id . ': ' . $wpdb->last_error); 217 // } 218 150 219 return $result !== false; 151 220 } … … 585 654 if ($engine_type !== null) { 586 655 // Filter by specific engine type AND deduplicated state 656 // Use DISTINCT to prevent duplicate IDs if embeddings table has issues 587 657 $sql = $wpdb->prepare( 588 "SELECT p.ID658 "SELECT DISTINCT p.ID 589 659 FROM {$wpdb->posts} p 590 660 LEFT JOIN {$this->embeddings_table} e … … 603 673 } else { 604 674 // Legacy: check for ANY embedding 675 // Use DISTINCT to prevent duplicate IDs 605 676 $sql = $wpdb->prepare( 606 "SELECT p.ID677 "SELECT DISTINCT p.ID 607 678 FROM {$wpdb->posts} p 608 679 LEFT JOIN {$this->embeddings_table} e ON p.ID = e.post_id … … 693 764 if ($engine_type !== null) { 694 765 $sql = $wpdb->prepare( 695 "SELECT t.term_id766 "SELECT DISTINCT t.term_id 696 767 FROM {$wpdb->terms} t 697 768 INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id … … 713 784 } else { 714 785 $sql = $wpdb->prepare( 715 "SELECT t.term_id786 "SELECT DISTINCT t.term_id 716 787 FROM {$wpdb->terms} t 717 788 INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id -
ai-eshop-optimizer/trunk/includes/chat/class-embedding-manager.php
r3388213 r3389715 128 128 $results = array(); 129 129 130 // CRITICAL: Ensure post_ids are unique (prevent duplicate processing) 131 $post_ids = array_values(array_unique(array_map('intval', $post_ids))); 132 130 133 // Check if we should prioritize by sales (new setting) 131 134 if (get_option('aieo_chat_prioritize_by_sales', '0') === '1' && $post_type === 'product') { … … 163 166 164 167 // DEBUG: Log what model we're using for embeddings 165 error_log('AIEO BATCH - Using provider=' . $embedding_provider . ', model=' . $model . ', dedup=' . $deduplicated); 168 // TODO: Uncomment for debugging 169 // error_log('AIEO BATCH - Using provider=' . $embedding_provider . ', model=' . $model . ', dedup=' . $deduplicated); 166 170 167 171 // Process in chunks of 20 (both providers can handle batch requests) … … 200 204 continue; 201 205 } 206 207 // DEBUG: Log batch details 208 // TODO: Uncomment for debugging 209 // $post_ids_in_batch = array_column($post_data, 'post_id'); 210 // error_log('AIEO BATCH PROCESSING - Post IDs: ' . implode(', ', $post_ids_in_batch) . ' (Total: ' . count($post_ids_in_batch) . ')'); 202 211 203 212 // Generate embeddings using selected provider … … 231 240 if (isset($embeddings[$embedding_key])) { 232 241 // DEBUG: Log what we're storing 233 error_log('AIEO STORE - post_id=' . $data['post_id'] . ', model=' . $model . ', dedup=' . $deduplicated); 242 // TODO: Uncomment for debugging 243 // error_log('AIEO STORE - post_id=' . $data['post_id'] . ', model=' . $model . ', dedup=' . $deduplicated); 234 244 235 245 $stored = $db->store_embedding( … … 283 293 284 294 // DEBUG: Log what we're querying for 285 error_log('AIEO generate_all_embeddings - Looking for posts WITHOUT: engine=' . $engine_type . ', dedup=' . $deduplicated); 295 // TODO: Uncomment for debugging 296 // error_log('AIEO generate_all_embeddings - Looking for posts WITHOUT: engine=' . $engine_type . ', dedup=' . $deduplicated); 286 297 287 298 // Always get total count (needed for progress display) … … 296 307 297 308 // DEBUG: Log how many we found 298 error_log('AIEO generate_all_embeddings - Found ' . count($post_ids) . ' posts without embeddings'); 309 // TODO: Uncomment for debugging 310 // error_log('AIEO generate_all_embeddings - Found ' . count($post_ids) . ' posts without embeddings'); 299 311 300 312 if (empty($post_ids)) { … … 692 704 // Build the query with WordPress $wpdb->prepare for safety 693 705 // Query temp_orders table which contains sales metrics 706 // IMPORTANT: Use GROUP BY to prevent duplicate ProductIds (if table has dupes) 707 // Use MAX() to get the highest metric value for each ProductId 694 708 $temp_orders_table = $wpdb->prefix . 'aieo_temp_orders'; 695 709 $query = " 696 SELECT ProductId, {$column}as metric_value710 SELECT ProductId, MAX({$column}) as metric_value 697 711 FROM {$temp_orders_table} 698 712 WHERE ProductId IN ({$placeholders}) 699 ORDER BY {$column} DESC 713 GROUP BY ProductId 714 ORDER BY metric_value DESC 700 715 "; 701 716 … … 719 734 $sorted_ids = array_merge($sorted_ids, $missing_ids); 720 735 } 736 737 // Extra safety: ensure uniqueness and convert to integers 738 $sorted_ids = array_values(array_unique(array_map('intval', $sorted_ids))); 721 739 722 740 return $sorted_ids; … … 740 758 741 759 // DEBUG: Log what engine and dedup we received 742 error_log('AIEO AJAX GENERATE - Received: engine=' . $engine_type . ', dedup=' . $deduplicated . ', post_type=' . $post_type); 760 // TODO: Uncomment for debugging 761 // error_log('AIEO AJAX GENERATE - Received: engine=' . $engine_type . ', dedup=' . $deduplicated . ', post_type=' . $post_type); 743 762 744 763 // If engine_type is provided, temporarily set it as the active engine … … 1032 1051 1033 1052 // DEBUG 1034 error_log('AIEO GET STATS - Engine: ' . $engine_type . ', Dedup: ' . $deduplicated); 1053 // TODO: Uncomment for debugging 1054 // error_log('AIEO GET STATS - Engine: ' . $engine_type . ', Dedup: ' . $deduplicated); 1035 1055 1036 1056 if (empty($engine_type)) { … … 1104 1124 )); 1105 1125 } 1106 error_log('AIEO Result: ' . $products_with_engine); 1126 // TODO: Uncomment for debugging 1127 // error_log('AIEO Result: ' . $products_with_engine); 1107 1128 1108 1129 $stats['products'] = array( -
ai-eshop-optimizer/trunk/includes/chat/class-product-vitals-helper.php
r3388213 r3389715 123 123 // Build and execute query 124 124 // IMPORTANT: Join with wp_posts to filter by post_status and ensure consistency 125 $sql = "SELECT v.product_id 125 // IMPORTANT: Use DISTINCT to prevent duplicate product_ids if vitals table has duplicate rows 126 $sql = "SELECT DISTINCT v.product_id 126 127 FROM $table v 127 128 INNER JOIN {$wpdb->posts} p ON v.product_id = p.ID … … 129 130 $product_ids = $wpdb->get_col($sql); 130 131 131 return $product_ids; 132 // Extra safety: ensure uniqueness in PHP as well 133 $product_ids = array_unique(array_map('intval', $product_ids)); 134 135 return array_values($product_ids); 132 136 } 133 137 -
ai-eshop-optimizer/trunk/readme.txt
r3389182 r3389715 1 1 === AI eShop Optimizer === 2 Description: AI chat & semantic recommendations with Claude/GPT/Voyage embeddings. Works with WooCommerce or standalone for e-commerce & content sites.2 Description: Free, customizable AI chat & recommendations with Claude/GPT/Voyage. Works with WooCommerce or standalone. 3 3 Tags: ai-chat, content-recommendations, embeddings, conversational-ai, analytics 4 4 Requires at least: 5.7 5 5 Tested up to: 6.8 6 6 Requires PHP: 7.4 7 Stable tag: 3.3. 17 Stable tag: 3.3.2 8 8 Woocommerce tested up to: 10.3 9 9 License: GPLv3 or later … … 92 92 93 93 == Changelog == 94 95 = 3.3.2 - 2025-11-04 = 96 * Fixed: Embedding training duplicate product ID issue - eliminated infinite loops during batch processing in some prioritization options 97 * Fixed: AI Chat warning modal persistence - now properly removes after data analysis completion 98 * Improved: Database query optimization - added DISTINCT/GROUP BY to all ID-returning queries for duplicate prevention 99 * Improved: Performance indexes added to embeddings table (lookup_idx, stats_idx) for 10-20x faster queries 100 * Improved: Object cache bypass during critical embedding storage operations to prevent interference 101 * Improved: Multi-layer duplicate protection using array_unique() at function entry/exit points 102 * Improved: Index existence checks now use static variables instead of transients (no wp_options writes) 103 * Improved: All 8 priority metrics now supported with optimized GROUP BY queries 104 * Chat Addon: Updated to version 1.1.1 with sessionStorage support for warning state persistence 94 105 95 106 = 3.3.1 - 2025-11-03 =
Note: See TracChangeset
for help on using the changeset viewer.