Plugin Directory

Changeset 3392455


Ignore:
Timestamp:
11/09/2025 04:56:59 PM (3 months ago)
Author:
mxchat
Message:

Version 2.5.3 - GPT-5 performance fix, API key management improvements, and link encoding fixes

Location:
mxchat-basic
Files:
103 added
9 edited

Legend:

Unmodified
Added
Removed
  • mxchat-basic/trunk/admin/class-ajax-handler.php

    r3379814 r3392455  
    3939    add_action('wp_ajax_mxchat_toggle_action', array($this, 'mxchat_toggle_action'));
    4040    add_action('wp_ajax_mxchat_update_intent_threshold', array($this, 'mxchat_update_intent_threshold'));
    41    
     41
    4242    add_action('wp_ajax_mxchat_save_selected_bot', array($this, 'mxchat_save_selected_bot'));
     43    add_action('wp_ajax_mxchat_check_api_keys', array($this, 'mxchat_check_api_keys'));
    4344}
    4445
     
    873874     }
    874875
     876    /**
     877     * Check API key status for all providers
     878     */
     879    public function mxchat_check_api_keys() {
     880        // Check nonce
     881        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'mxchat_save_setting_nonce')) {
     882            wp_send_json_error('Invalid nonce');
     883        }
     884
     885        // Check permissions
     886        if (!current_user_can('manage_options')) {
     887            wp_send_json_error('Unauthorized');
     888        }
     889
     890        // Get current options
     891        $options = get_option('mxchat_options', array());
     892
     893        // Check which API keys are present
     894        $api_key_status = array(
     895            'openai' => !empty($options['api_key']),
     896            'claude' => !empty($options['claude_api_key']),
     897            'xai' => !empty($options['xai_api_key']),
     898            'deepseek' => !empty($options['deepseek_api_key']),
     899            'gemini' => !empty($options['gemini_api_key']),
     900            'openrouter' => !empty($options['openrouter_api_key']),
     901            'voyage' => !empty($options['voyage_api_key'])
     902        );
     903
     904        wp_send_json_success($api_key_status);
     905    }
     906
    875907}
    876908
  • mxchat-basic/trunk/includes/class-mxchat-addons.php

    r3389316 r3392455  
    177177     */
    178178    public function enqueue_styles() {
    179         $plugin_version = '2.5.2';
    180 
    181179        wp_enqueue_style(
    182180            'mxchat-addons',
    183181            plugin_dir_url(__FILE__) . '../css/admin-add-ons.css',
    184182            array(),
    185             $plugin_version,
     183            MXCHAT_VERSION,
    186184            'all'
    187185        );
  • mxchat-basic/trunk/includes/class-mxchat-admin.php

    r3389316 r3392455  
    610610            <div class="mxchat-tabs">
    611611                <button class="mxchat-tab-button active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></button>
     612                <button class="mxchat-tab-button" data-tab="api-keys"><?php echo esc_html__('API Keys', 'mxchat'); ?></button>
    612613                <button class="mxchat-tab-button" data-tab="embed"><?php echo esc_html__('Toolbar & Components', 'mxchat'); ?></button>
    613614                <button class="mxchat-tab-button" data-tab="general"><?php echo esc_html__('YouTube Tutorials', 'mxchat'); ?></button>
     
    619620                    <div class="mxchat-autosave-section">
    620621                        <?php do_settings_sections('mxchat-chatbot'); ?>
     622                    </div>
     623                </div>
     624            </div>
     625
     626            <div id="api-keys" class="mxchat-tab-content">
     627                <div class="mxchat-card">
     628                    <div class="mxchat-autosave-section">
     629                        <table class="form-table">
     630                            <?php do_settings_fields('mxchat-api-keys', 'mxchat_api_keys_section'); ?>
     631                        </table>
    621632                    </div>
    622633                </div>
     
    17411752                    </div>
    17421753                </div>
    1743                
    1744                 <script>
    1745 jQuery(document).ready(function($) {
    1746     var saveTimer;
    1747    
    1748     // Function to update bot_id in forms
    1749     function updateBotIdInForm(formSelector) {
    1750         var botId = $('#mxchat-bot-selector').val();
    1751         var form = $(formSelector);
    1752        
    1753         if (form.length > 0) {
    1754             // Remove existing bot_id hidden input
    1755             form.find('input[name="bot_id"]').remove();
    1756            
    1757             // Add new bot_id hidden input if not default
    1758             if (botId && botId !== 'default') {
    1759                 form.append('<input type="hidden" name="bot_id" value="' + botId + '">');
    1760             }
    1761         }
    1762     }
    1763    
    1764     $('#mxchat-bot-selector').on('change', function() {
    1765         var botId = $(this).val();
    1766        
    1767         // Clear any existing timer
    1768         clearTimeout(saveTimer);
    1769        
    1770         // Update all forms with new bot_id when bot selection changes
    1771         updateBotIdInForm('#mxchat-url-form');
    1772         updateBotIdInForm('#mxchat-content-form');
    1773        
    1774         // Save the selection via AJAX
    1775         $.ajax({
    1776             url: ajaxurl,
    1777             type: 'POST',
    1778             data: {
    1779                 action: 'mxchat_save_selected_bot',
    1780                 bot_id: botId,
    1781                 nonce: '<?php echo wp_create_nonce('mxchat_save_setting_nonce'); ?>'
    1782             },
    1783             success: function(response) {
    1784                 if (response.success) {
    1785                     // Show saved indicator
    1786                     $('#mxchat-bot-save-status').fadeIn().delay(2000).fadeOut();
    1787                    
    1788                     // Reload the page after a short delay to refresh content
    1789                     saveTimer = setTimeout(function() {
    1790                         var currentUrl = new URL(window.location.href);
    1791                         currentUrl.searchParams.set('bot_id', botId);
    1792                         currentUrl.searchParams.set('page', 'mxchat-prompts');
    1793                         window.location.href = currentUrl.toString();
    1794                     }, 500);
    1795                 }
    1796             },
    1797             error: function() {
    1798                 console.error('Failed to save bot selection');
    1799             }
    1800         });
    1801     });
    1802    
    1803     // Initialize forms with current bot_id when the page loads
    1804     setTimeout(function() {
    1805         updateBotIdInForm('#mxchat-url-form');
    1806         updateBotIdInForm('#mxchat-content-form');
    1807     }, 100);
    1808 });
    1809 </script>
    18101754
    18111755            <?php
     
    43554299    );
    43564300
     4301    // API Keys Settings Section
     4302    add_settings_section(
     4303        'mxchat_api_keys_section',
     4304        esc_html__('API Keys', 'mxchat'),
     4305        array($this, 'mxchat_api_keys_section_callback'),
     4306        'mxchat-api-keys'
     4307    );
     4308
     4309    // OpenAI API Key
     4310    add_settings_field(
     4311        'api_key',
     4312        esc_html__('OpenAI API Key', 'mxchat'),
     4313        array($this, 'api_key_callback'),
     4314        'mxchat-api-keys',
     4315        'mxchat_api_keys_section'
     4316    );
     4317
     4318    // X.AI API Key
     4319    add_settings_field(
     4320        'xai_api_key',
     4321        esc_html__('X.AI API Key', 'mxchat'),
     4322        array($this, 'xai_api_key_callback'),
     4323        'mxchat-api-keys',
     4324        'mxchat_api_keys_section'
     4325    );
     4326
     4327    // Claude API Key
     4328    add_settings_field(
     4329        'claude_api_key',
     4330        esc_html__('Claude API Key', 'mxchat'),
     4331        array($this, 'claude_api_key_callback'),
     4332        'mxchat-api-keys',
     4333        'mxchat_api_keys_section'
     4334    );
     4335
     4336    // DeepSeek API Key
     4337    add_settings_field(
     4338        'deepseek_api_key',
     4339        esc_html__('DeepSeek API Key', 'mxchat'),
     4340        array($this, 'deepseek_api_key_callback'),
     4341        'mxchat-api-keys',
     4342        'mxchat_api_keys_section'
     4343    );
     4344
     4345    // Google Gemini API Key
     4346    add_settings_field(
     4347        'gemini_api_key',
     4348        esc_html__('Google Gemini API Key', 'mxchat'),
     4349        array($this, 'gemini_api_key_callback'),
     4350        'mxchat-api-keys',
     4351        'mxchat_api_keys_section'
     4352    );
     4353
     4354    // Voyage AI API Key
     4355    add_settings_field(
     4356        'voyage_api_key',
     4357        esc_html__('Voyage AI API Key', 'mxchat'),
     4358        array($this, 'voyage_api_key_callback'),
     4359        'mxchat-api-keys',
     4360        'mxchat_api_keys_section'
     4361    );
     4362
     4363    // OpenRouter API Key
     4364    add_settings_field(
     4365        'openrouter_api_key',
     4366        esc_html__('OpenRouter API Key', 'mxchat'),
     4367        array($this, 'openrouter_api_key_callback'),
     4368        'mxchat-api-keys',
     4369        'mxchat_api_keys_section'
     4370    );
     4371
     4372    // Loops API Key
     4373    add_settings_field(
     4374        'loops_api_key',
     4375        esc_html__('Loops API Key', 'mxchat'),
     4376        array($this, 'mxchat_loops_api_key_callback'),
     4377        'mxchat-api-keys',
     4378        'mxchat_api_keys_section'
     4379    );
     4380
     4381    // Brave Search API Key
     4382    add_settings_field(
     4383        'brave_api_key',
     4384        __('Brave API Key', 'mxchat'),
     4385        array($this, 'mxchat_brave_api_key_callback'),
     4386        'mxchat-api-keys',
     4387        'mxchat_api_keys_section'
     4388    );
     4389
    43574390    // Similarity Threshold Slider
    43584391    add_settings_field(
     
    43794412        'mxchat_chatbot_section'
    43804413    );
    4381 
    4382         add_settings_field(
    4383             'api_key',
    4384             esc_html__('OpenAI API Key', 'mxchat'),
    4385             array($this, 'api_key_callback'),
    4386             'mxchat-chatbot',
    4387             'mxchat_chatbot_section',
    4388             array(
    4389                 'class' => 'mxchat-setting-row',
    4390                 'data-provider' => 'openai'
    4391             )
    4392         );
    4393 
    4394         add_settings_field(
    4395             'xai_api_key',
    4396             esc_html__('X.AI API Key', 'mxchat'),
    4397             array($this, 'xai_api_key_callback'),
    4398             'mxchat-chatbot',
    4399             'mxchat_chatbot_section',
    4400             array(
    4401                 'class' => 'mxchat-setting-row',
    4402                 'data-provider' => 'xai'
    4403             )
    4404         );
    4405 
    4406         add_settings_field(
    4407             'claude_api_key',
    4408             esc_html__('Claude API Key', 'mxchat'),
    4409             array($this, 'claude_api_key_callback'),
    4410             'mxchat-chatbot',
    4411             'mxchat_chatbot_section',
    4412             array(
    4413                 'class' => 'mxchat-setting-row',
    4414                 'data-provider' => 'claude'
    4415             )
    4416         );
    4417 
    4418         add_settings_field(
    4419             'deepseek_api_key',
    4420             esc_html__('DeepSeek API Key', 'mxchat'),
    4421             array($this, 'deepseek_api_key_callback'),
    4422             'mxchat-chatbot',
    4423             'mxchat_chatbot_section',
    4424             array(
    4425                 'class' => 'mxchat-setting-row',
    4426                 'data-provider' => 'deepseek'
    4427             )
    4428         );
    4429 
    4430         add_settings_field(
    4431             'gemini_api_key',
    4432             esc_html__('Google Gemini API Key', 'mxchat'),
    4433             array($this, 'gemini_api_key_callback'),
    4434             'mxchat-chatbot',
    4435             'mxchat_chatbot_section',
    4436             array(
    4437                 'class' => 'mxchat-setting-row',
    4438                 'data-provider' => 'gemini'
    4439             )
    4440         );
    4441 
    4442         add_settings_field(
    4443             'voyage_api_key',
    4444             esc_html__('Voyage AI API Key', 'mxchat'),
    4445             array($this, 'voyage_api_key_callback'),
    4446             'mxchat-chatbot',
    4447             'mxchat_chatbot_section',
    4448             array(
    4449                 'class' => 'mxchat-setting-row',
    4450                 'data-provider' => 'voyage'
    4451             )
    4452         );
    44534414
    44544415    add_settings_field(
     
    44724433        'mxchat_chatbot_section'
    44734434    );
    4474    
    4475    
    4476     add_settings_field(
    4477         'openrouter_api_key',
    4478         esc_html__('OpenRouter API Key', 'mxchat'),
    4479         array($this, 'openrouter_api_key_callback'),
    4480         'mxchat-chatbot',
    4481         'mxchat_chatbot_section',
    4482         array(
    4483             'class' => 'mxchat-setting-row mxchat-openrouter-key-row', // Special class so we can always show it
    4484             'data-provider' => 'openrouter'
    4485         )
    4486     );
    44874435
    44884436    add_settings_field(
     
    46644612    );
    46654613
    4666     // Loops Settings Fields
    4667     add_settings_field(
    4668         'loops_api_key',
    4669         esc_html__('Loops API Key', 'mxchat'),
    4670         array($this, 'mxchat_loops_api_key_callback'),
    4671         'mxchat-embed',
    4672         'mxchat_loops_section'
    4673     );
    4674 
     4614    // Loops Settings Fields (API Key moved to API Keys tab)
    46754615    add_settings_field(
    46764616        'loops_mailing_list',
     
    47054645    );
    47064646
    4707     add_settings_field(
    4708         'brave_api_key',
    4709         __('Brave API Key', 'mxchat'),
    4710         array($this, 'mxchat_brave_api_key_callback'),
    4711         'mxchat-embed',
    4712         'mxchat_brave_section'
    4713     );
    4714 
     4647    // Brave API Key moved to API Keys tab
    47154648    add_settings_field(
    47164649        'brave_image_count',
     
    51755108    }
    51765109
     5110// API Keys Section Callback
     5111public function mxchat_api_keys_section_callback() {
     5112    echo '<p>' . esc_html__('Manage all your API keys in one place. Add the API keys for the services you want to use with your chatbot.', 'mxchat') . '</p>';
     5113}
     5114
    51775115// OpenAI API Key
    51785116public function api_key_callback() {
    51795117    $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
    51805118
    5181     echo '<div class="api-key-wrapper" data-provider="openai">';
     5119    echo '<div class="api-key-wrapper">';
    51825120    echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
    51835121    echo '<button type="button" id="toggleApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
    5184     echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
     5122    echo '<p class="description">' . esc_html__('Required for OpenAI GPT models and OpenAI embeddings. Get your API key from OpenAI Platform.', 'mxchat') . '</p>';
    51855123    echo '</div>';
    51865124}
     
    51905128    $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
    51915129
    5192     echo '<div class="api-key-wrapper" data-provider="xai">';
     5130    echo '<div class="api-key-wrapper">';
    51935131    echo '<input type="password" id="xai_api_key" name="xai_api_key" value="' . $xaiApiKey . '" class="regular-text" autocomplete="off" />';
    51945132    echo '<button type="button" id="toggleXaiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
    5195     echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
     5133    echo '<p class="description">' . esc_html__('Required for X.AI Grok models. Get your API key from X.AI Console.', 'mxchat') . '</p>';
    51965134    echo '</div>';
    51975135}
     
    52005138    $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
    52015139
    5202     echo '<div class="api-key-wrapper" data-provider="claude">';
     5140    echo '<div class="api-key-wrapper">';
    52035141    echo '<input type="password" id="claude_api_key" name="claude_api_key" value="' . $claudeApiKey . '" class="regular-text" autocomplete="off" />';
    52045142    echo '<button type="button" id="toggleClaudeApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
    5205     echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
     5143    echo '<p class="description">' . esc_html__('Required for Anthropic Claude models. Get your API key from Anthropic Console.', 'mxchat') . '</p>';
    52065144    echo '</div>';
    52075145}
     
    52115149    $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : '';
    52125150
    5213     echo '<div class="api-key-wrapper" data-provider="deepseek">';
     5151    echo '<div class="api-key-wrapper">';
    52145152    echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
    52155153    echo '<button type="button" id="toggleDeepSeekApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
    5216     echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
     5154    echo '<p class="description">' . esc_html__('Required for DeepSeek models. Get your API key from DeepSeek Platform.', 'mxchat') . '</p>';
    52175155    echo '</div>';
    52185156}
     
    52225160    $geminiApiKey = isset($this->options['gemini_api_key']) ? esc_attr($this->options['gemini_api_key']) : '';
    52235161
    5224     echo '<div class="api-key-wrapper" data-provider="gemini">';
     5162    echo '<div class="api-key-wrapper">';
    52255163    echo '<input type="password" id="gemini_api_key" name="gemini_api_key" value="' . $geminiApiKey . '" class="regular-text" autocomplete="off" />';
    52265164    echo '<button type="button" id="toggleGeminiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
    5227     echo '<p class="description api-key-notice">' . esc_html__('Required for Google Gemini models. Get your API key from Google AI Studio.', 'mxchat') . '</p>';
     5165    echo '<p class="description">' . esc_html__('Required for Google Gemini models and embeddings. Get your API key from Google AI Studio.', 'mxchat') . '</p>';
    52285166    echo '</div>';
    52295167}
     
    52335171public function openrouter_api_key_callback() {
    52345172    $openrouterApiKey = isset($this->options['openrouter_api_key']) ? esc_attr($this->options['openrouter_api_key']) : '';
    5235     echo '<div class="api-key-wrapper" data-provider="openrouter">';
     5173    echo '<div class="api-key-wrapper">';
    52365174    echo '<input type="password" id="openrouter_api_key" name="openrouter_api_key" value="' . $openrouterApiKey . '" class="regular-text" autocomplete="off" />';
    52375175    echo '<button type="button" id="toggleOpenRouterApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
    5238     echo '<p class="description api-key-notice">' . esc_html__('Required only if using OpenRouter. Get your API key from OpenRouter.ai', 'mxchat') . '</p>';
     5176    echo '<p class="description">' . esc_html__('Required for OpenRouter models. Get your API key from OpenRouter.ai', 'mxchat') . '</p>';
    52395177    echo '</div>';
    52405178}
     
    52445182    $apiKey = isset($this->options['voyage_api_key']) ? esc_attr($this->options['voyage_api_key']) : '';
    52455183
    5246     echo '<div class="api-key-wrapper" data-provider="voyage">';
     5184    echo '<div class="api-key-wrapper">';
    52475185    echo '<input type="password" id="voyage_api_key" name="voyage_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
    52485186    echo '<button type="button" id="toggleVoyageAPIKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
    5249     echo '<p class="description api-key-notice">' . esc_html__('Required for your selected embedding model. Important: You must add credits before use.', 'mxchat') . '</p>';
     5187    echo '<p class="description">' . esc_html__('Required for Voyage AI embedding models. Get your API key from Voyage AI.', 'mxchat') . '</p>';
    52505188    echo '</div>';
    52515189}
     
    52585196    echo '<input type="password" style="display:none" autocomplete="current-password" />';
    52595197
    5260     echo '<div class="api-key-wrapper" data-provider="loops">';
     5198    echo '<div class="api-key-wrapper">';
    52615199    echo sprintf(
    52625200        '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" autocomplete="new-password" />',
     
    52655203    echo '<button type="button" id="toggleLoopsApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
    52665204    echo '</div>';
    5267     echo '<p class="description">' . esc_html__('Enter your Loops API Key here. Once entered, refreshed page to load list (See FAQ for details)', 'mxchat') . '</p>';
     5205    echo '<p class="description">' . esc_html__('Required for Loops email integration. Get your API key from Loops.so', 'mxchat') . '</p>';
    52685206}
    52695207public function mxchat_loops_mailing_list_callback() {
     
    55105448
    55115449    echo '<p class="description">' . esc_html__('Select the AI model your chatbot will use for chatting.', 'mxchat') . '</p>';
    5512    
     5450
    55135451    // Add a note for OpenRouter
    55145452    echo '<p class="description" id="openrouter-model-note" style="display:none; color: #d63638; font-weight: 500;">';
     
    55165454    echo esc_html__('After entering your OpenRouter API key above, click the button below to load available models.', 'mxchat');
    55175455    echo '</p>';
     5456
     5457    // API Key Status Messages (hidden by default, shown by JS based on selected model)
     5458    $has_openai_key = !empty($this->options['api_key']);
     5459    $has_claude_key = !empty($this->options['claude_api_key']);
     5460    $has_xai_key = !empty($this->options['xai_api_key']);
     5461    $has_deepseek_key = !empty($this->options['deepseek_api_key']);
     5462    $has_gemini_key = !empty($this->options['gemini_api_key']);
     5463    $has_openrouter_key = !empty($this->options['openrouter_api_key']);
     5464
     5465    // OpenAI/GPT models
     5466    echo '<p class="mxchat-api-status" data-provider="openai" style="display:none;">';
     5467    if ($has_openai_key) {
     5468        echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for OpenAI detected', 'mxchat') . '</span>';
     5469    } else {
     5470        echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for OpenAI detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
     5471    }
     5472    echo '</p>';
     5473
     5474    // Claude models
     5475    echo '<p class="mxchat-api-status" data-provider="claude" style="display:none;">';
     5476    if ($has_claude_key) {
     5477        echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for Anthropic (Claude) detected', 'mxchat') . '</span>';
     5478    } else {
     5479        echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for Anthropic (Claude) detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
     5480    }
     5481    echo '</p>';
     5482
     5483    // X.AI models
     5484    echo '<p class="mxchat-api-status" data-provider="xai" style="display:none;">';
     5485    if ($has_xai_key) {
     5486        echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for X.AI (Grok) detected', 'mxchat') . '</span>';
     5487    } else {
     5488        echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for X.AI (Grok) detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
     5489    }
     5490    echo '</p>';
     5491
     5492    // DeepSeek models
     5493    echo '<p class="mxchat-api-status" data-provider="deepseek" style="display:none;">';
     5494    if ($has_deepseek_key) {
     5495        echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for DeepSeek detected', 'mxchat') . '</span>';
     5496    } else {
     5497        echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for DeepSeek detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
     5498    }
     5499    echo '</p>';
     5500
     5501    // Gemini models
     5502    echo '<p class="mxchat-api-status" data-provider="gemini" style="display:none;">';
     5503    if ($has_gemini_key) {
     5504        echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for Google Gemini detected', 'mxchat') . '</span>';
     5505    } else {
     5506        echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for Google Gemini detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
     5507    }
     5508    echo '</p>';
     5509
     5510    // OpenRouter models
     5511    echo '<p class="mxchat-api-status" data-provider="openrouter" style="display:none;">';
     5512    if ($has_openrouter_key) {
     5513        echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for OpenRouter detected', 'mxchat') . '</span>';
     5514    } else {
     5515        echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for OpenRouter detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
     5516    }
     5517    echo '</p>';
     5518
    55185519 // ADD THESE HIDDEN FIELDS RIGHT HERE:
    55195520    $openrouter_model = isset($this->options['openrouter_selected_model']) ? esc_attr($this->options['openrouter_selected_model']) : '';
    55205521    $openrouter_model_name = isset($this->options['openrouter_selected_model_name']) ? esc_attr($this->options['openrouter_selected_model_name']) : '';
    5521    
     5522
    55225523    echo '<input type="hidden" id="openrouter_selected_model" name="openrouter_selected_model" value="' . $openrouter_model . '" />';
    55235524    echo '<input type="hidden" id="openrouter_selected_model_name" name="openrouter_selected_model_name" value="' . $openrouter_model_name . '" />';
     
    56235624    echo '</select>';
    56245625    echo '<p class="description"><span class="red-warning">IMPORTANT:</span> A vector embedding model is required for MxChat to function. Changing models is not recommended; if you do, you must delete all existing knowledge & intent data and reconfigure them.</p>';
     5626
     5627    // API Key Status Messages for Embedding Models
     5628    $has_openai_key = !empty($this->options['api_key']);
     5629    $has_voyage_key = !empty($this->options['voyage_api_key']);
     5630    $has_gemini_key = !empty($this->options['gemini_api_key']);
     5631
     5632    // OpenAI Embeddings
     5633    echo '<p class="mxchat-embedding-api-status" data-provider="openai" style="display:none;">';
     5634    if ($has_openai_key) {
     5635        echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for OpenAI detected', 'mxchat') . '</span>';
     5636    } else {
     5637        echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for OpenAI detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
     5638    }
     5639    echo '</p>';
     5640
     5641    // Voyage AI Embeddings
     5642    echo '<p class="mxchat-embedding-api-status" data-provider="voyage" style="display:none;">';
     5643    if ($has_voyage_key) {
     5644        echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for Voyage AI detected', 'mxchat') . '</span>';
     5645    } else {
     5646        echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for Voyage AI detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
     5647    }
     5648    echo '</p>';
     5649
     5650    // Gemini Embeddings
     5651    echo '<p class="mxchat-embedding-api-status" data-provider="gemini" style="display:none;">';
     5652    if ($has_gemini_key) {
     5653        echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for Google Gemini detected', 'mxchat') . '</span>';
     5654    } else {
     5655        echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for Google Gemini detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
     5656    }
     5657    echo '</p>';
    56255658}
    56265659
     
    64466479    echo '<button type="button" id="toggleBraveApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
    64476480    echo '</div>';
    6448     echo '<p class="description">' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '</p>';
     6481    echo '<p class="description">' . __('Required for Brave Search integration. Get your API key from Brave Search API.', 'mxchat') . '</p>';
    64496482}
    64506483
     
    67716804
    67726805public function mxchat_enqueue_admin_assets() {
    6773     // Get plugin version (define this in your main plugin file)
    6774     $version = defined('MXCHAT_VERSION') ? MXCHAT_VERSION : '2.5.2';
     6806    // Get plugin version
     6807    $version = MXCHAT_VERSION;
    67756808
    67766809    // Use file modification time for development (remove in production)
     
    68906923                'ajax_url' => admin_url('admin-ajax.php'),
    68916924                'status_nonce' => wp_create_nonce('mxchat_status_nonce'),
    6892                 'queue_nonce' => wp_create_nonce('mxchat_queue_nonce'), // ADD THIS LINE
     6925                'queue_nonce' => wp_create_nonce('mxchat_queue_nonce'),
    68936926                'stop_nonce' => wp_create_nonce('mxchat_stop_processing_action'),
    68946927                'settings_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
     6928                'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
    68956929                'admin_url' => admin_url(),
    68966930                'ajaxurl' => admin_url('admin-ajax.php'),
  • mxchat-basic/trunk/includes/class-mxchat-integrator.php

    r3389316 r3392455  
    12591259                    'session_id' => $session_id
    12601260                ];
    1261                
     1261
     1262                // IMPORTANT: Include chat_mode if present (for WhatsApp, Slack, etc.)
     1263                if (isset($intent_result['chat_mode'])) {
     1264                    $response_data['chat_mode'] = $intent_result['chat_mode'];
     1265                }
     1266
    12621267                if ($testing_data !== null) {
    12631268                    $response_data['testing_data'] = $testing_data;
     
    28452850    $history = get_option("mxchat_history_{$session_id}", []);
    28462851
     2852    error_log("MxChat WhatsApp DEBUG: Fetch new messages for session {$session_id}");
     2853    error_log("MxChat WhatsApp DEBUG: last_seen_id = " . var_export($last_seen_id, true));
     2854    error_log("MxChat WhatsApp DEBUG: History count = " . count($history));
     2855    error_log("MxChat WhatsApp DEBUG: Full history = " . print_r($history, true));
     2856
    28472857    $new_messages = array_filter($history, function ($message) use ($last_seen_id, $persistence_enabled, $initial_timestamp) {
     2858        error_log("MxChat WhatsApp DEBUG: Checking message - ID: " . ($message['id'] ?? 'NO_ID') . ", Role: " . ($message['role'] ?? 'NO_ROLE'));
     2859
    28482860        // If persistence is enabled, show all new messages
    28492861        if ($persistence_enabled) {
    2850             return !empty($message['id']) &&
    2851                    strcmp($message['id'], $last_seen_id) > 0 &&
    2852                    $message['role'] === 'agent';
     2862            $has_id = !empty($message['id']);
     2863            $is_agent = $message['role'] === 'agent';
     2864
     2865            // If last_seen_id is empty, 'NaN', or invalid, show all agent messages
     2866            if (empty($last_seen_id) || $last_seen_id === 'NaN' || $last_seen_id === 'undefined') {
     2867                $is_newer = true;
     2868            } else {
     2869                $is_newer = strcmp($message['id'] ?? '', $last_seen_id) > 0;
     2870            }
     2871
     2872            error_log("MxChat WhatsApp DEBUG: has_id={$has_id}, is_newer={$is_newer}, is_agent={$is_agent}");
     2873
     2874            return $has_id && $is_newer && $is_agent;
    28532875        }
    28542876
     
    28592881    });
    28602882
    2861     //error_log(esc_html__("New agent messages fetched for session $session_id. Last seen ID: $last_seen_id", 'mxchat'));
     2883    error_log("MxChat WhatsApp DEBUG: Filtered messages count = " . count($new_messages));
    28622884
    28632885    wp_send_json_success([
     
    50085030        }
    50095031
    5010         // Prepare the request body with stream: true
    5011         $body = json_encode([
     5032        // Check if this is a GPT-5 model (supports reasoning_effort parameter)
     5033        $is_gpt5_model = (
     5034            strpos($selected_model, 'gpt-5') === 0 ||
     5035            $selected_model === 'gpt-5' ||
     5036            $selected_model === 'gpt-5-mini' ||
     5037            $selected_model === 'gpt-5-nano'
     5038        );
     5039
     5040        // Build request body with optimal settings for fast streaming
     5041        $request_body = [
    50125042            'model' => $selected_model,
    50135043            'messages' => $formatted_conversation,
    50145044            'temperature' => 1,
    50155045            'stream' => true
    5016         ]);
     5046        ];
     5047
     5048        // Add reasoning_effort only for GPT-5 models
     5049        if ($is_gpt5_model) {
     5050            $request_body['reasoning_effort'] = 'minimal'; // Fastest response for GPT-5
     5051        }
     5052
     5053        $body = json_encode($request_body);
    50175054
    50185055        // Use cURL for streaming support
     
    60446081
    60456082        // Get bot ID from session or request
    6046         $bot_id = $this->get_current_bot_id($session_id);
     6083        $bot_id = $this->get_current_bot_id('');
    60476084       
    60486085        // Get system prompt instructions using centralized function
     
    60786115        }
    60796116
    6080         $body = json_encode([
     6117        // Check if this is a GPT-5 model (supports reasoning_effort parameter)
     6118        $is_gpt5_model = (
     6119            strpos($selected_model, 'gpt-5') === 0 ||
     6120            $selected_model === 'gpt-5' ||
     6121            $selected_model === 'gpt-5-mini' ||
     6122            $selected_model === 'gpt-5-nano'
     6123        );
     6124
     6125        // Build request body with optimal settings for fast responses
     6126        $request_body = [
    60816127            'model' => $selected_model,
    60826128            'messages' => $formatted_conversation,
    60836129            'temperature' => 1,
    60846130            'stream' => false
    6085         ]);
     6131        ];
     6132
     6133        // Add reasoning_effort only for GPT-5 models
     6134        if ($is_gpt5_model) {
     6135            $request_body['reasoning_effort'] = 'minimal'; // Fastest response for GPT-5
     6136        }
     6137
     6138        $body = json_encode($request_body);
    60866139
    60876140        $args = [
     
    61026155        if (is_wp_error($response)) {
    61036156            $error_message = $response->get_error_message();
    6104             //error_log('OpenAI API Error: ' . $error_message);
    61056157            return [
    61066158                'error' => esc_html__('Connection error when contacting OpenAI: ', 'mxchat') . esc_html($error_message),
     
    61226174                ? $decoded_response['error']['type']
    61236175                : 'unknown';
    6124            
    6125             //error_log('OpenAI API HTTP Error: ' . $status_code . ' - ' . $error_message);
    61266176           
    61276177            // Handle specific error types
     
    61746224            return trim($decoded_response['choices'][0]['message']['content']);
    61756225        } else {
    6176             //error_log('OpenAI API Response Format Error: ' . print_r($decoded_response, true));
    61776226            return [
    61786227                'error' => esc_html__('Unexpected response format from OpenAI.', 'mxchat'),
     
    61826231        }
    61836232    } catch (Exception $e) {
    6184         //error_log('OpenAI Exception: ' . $e->getMessage());
    61856233        return [
    61866234            'error' => esc_html__('System error when processing OpenAI request: ', 'mxchat') . esc_html($e->getMessage()),
     
    61906238    }
    61916239}
     6240
    61926241private function mxchat_generate_response_xai($selected_model, $xai_api_key, $conversation_history, $relevant_content) {
    61936242    try {
     
    68816930
    68826931public function mxchat_enqueue_scripts_styles() {
    6883     // Define version numbers for the styles and scripts
    6884     $chat_style_version = '2.5.2';
    6885     $chat_script_version = '2.5.2';
    68866932    // Enqueue the script
    68876933    wp_enqueue_script(
     
    68896935        plugin_dir_url(__FILE__) . '../js/chat-script.js',
    68906936        array('jquery'),
    6891         $chat_script_version,
     6937        MXCHAT_VERSION,
    68926938        true
    68936939    );
     
    68976943        plugin_dir_url(__FILE__) . '../css/chat-style.css',
    68986944        array(),
    6899         $chat_style_version
     6945        MXCHAT_VERSION
    69006946    );
    69016947    // Fetch options from the database
  • mxchat-basic/trunk/js/chat-script.js

    r3389316 r3392455  
    13551355    }
    13561356   
    1357     function formatTextStyling(text) {
    1358         // Handle bold text (**text**)
    1359         text = text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
    1360        
    1361         // Handle italic text (*text* or _text_) - avoid conflicts with bold
    1362         text = text.replace(/(?<!\*)\*([^*\n]+)\*(?!\*)/g, '<em>$1</em>');
    1363         text = text.replace(/(?<!_)_([^_\n]+)_(?!_)/g, '<em>$1</em>');
    1364        
    1365         // Handle strikethrough (~~text~~)
    1366         text = text.replace(/~~(.*?)~~/g, '<del>$1</del>');
    1367        
    1368         return text;
    1369     }
    1370    
     1357function formatTextStyling(text) {
     1358    // IMPORTANT: Protect BOTH HTML href and Markdown URLs from formatting
     1359    const protectedSegments = [];
     1360    let protectedText = text;
     1361   
     1362    // Step 1a: Protect HTML href="..." attributes
     1363    protectedText = protectedText.replace(/href\s*=\s*["']([^"']+)["']/gi, function(match) {
     1364        const placeholder = `__PROTECTED_${protectedSegments.length}__`;
     1365        protectedSegments.push(match);
     1366        return placeholder;
     1367    });
     1368   
     1369    // Step 1b: Protect Markdown links [text](url)
     1370    // This is crucial - we need to protect the URLs in markdown format
     1371    protectedText = protectedText.replace(/\[([^\]]*)\]\(([^)]+)\)/g, function(match) {
     1372        const placeholder = `__PROTECTED_${protectedSegments.length}__`;
     1373        protectedSegments.push(match);
     1374        return placeholder;
     1375    });
     1376   
     1377    // Step 1c: Also protect bare URLs that might exist
     1378    protectedText = protectedText.replace(/(https?:\/\/[^\s<>"]+)/gi, function(match) {
     1379        const placeholder = `__PROTECTED_${protectedSegments.length}__`;
     1380        protectedSegments.push(match);
     1381        return placeholder;
     1382    });
     1383   
     1384    // Step 2: Now apply text styling to the protected text
     1385    // Handle bold text (**text**)
     1386    protectedText = protectedText.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
     1387   
     1388    // Handle italic text (*text* or _text_) - avoid conflicts with bold
     1389    protectedText = protectedText.replace(/(?<!\*)\*([^*\n]+)\*(?!\*)/g, '<em>$1</em>');
     1390   
     1391    // Handle underscores for italic - this won't touch URLs now since they're protected
     1392    protectedText = protectedText.replace(/(?<!_)_([^_\n]+)_(?!_)/g, '<em>$1</em>');
     1393   
     1394    // Handle strikethrough (~~text~~)
     1395    protectedText = protectedText.replace(/~~(.*?)~~/g, '<del>$1</del>');
     1396   
     1397    // Step 3: Restore all protected segments
     1398    protectedSegments.forEach((original, index) => {
     1399        const placeholder = `__PROTECTED_${index}__`;
     1400        protectedText = protectedText.replace(placeholder, original);
     1401    });
     1402   
     1403    return protectedText;
     1404}
    13711405    function formatBoldText(text) {
    13721406        // This function is kept for compatibility but now uses formatTextStyling
  • mxchat-basic/trunk/js/knowledge-processing.js

    r3389316 r3392455  
    77    let currentQueueId = null;
    88    let currentQueueType = null;
    9    
     9
     10    // Process 5 items at a time
     11    const BATCH_SIZE = 5;
     12
    1013    // Check if we should start queue processing on page load
    1114    checkForActiveQueues();
     
    115118        // Create or update status card
    116119        createOrUpdateStatusCard(queueType);
    117        
    118         // Start the processing loop
    119         processNextQueueItem();
    120     }
    121    
    122     /**
    123      * Process the next item in the queue
    124      */
    125     function processNextQueueItem() {
     120
     121        // Start the batch processing loop
     122        processNextBatch();
     123    }
     124
     125    /**
     126     * Process the next batch of items (5 at a time)
     127     */
     128    function processNextBatch() {
    126129        if (!isProcessingQueue) {
    127130            //console.log('MxChat: Processing stopped');
    128131            return;
    129132        }
    130        
    131         // Get next item from queue
    132         $.ajax({
    133             url: ajaxurl,
    134             type: 'POST',
    135             data: {
    136                 action: 'mxchat_get_next_queue_item',
    137                 nonce: mxchatAdmin.queue_nonce,
    138                 queue_id: currentQueueId
    139             },
    140             success: function(response) {
    141                 if (!response.success) {
    142                     console.error('MxChat: Error getting next queue item:', response.data);
    143                     // Check if queue is actually complete despite error
     133
     134        // Fetch the next batch of items
     135        const fetchPromises = [];
     136
     137        for (let i = 0; i < BATCH_SIZE; i++) {
     138            const promise = $.ajax({
     139                url: ajaxurl,
     140                type: 'POST',
     141                data: {
     142                    action: 'mxchat_get_next_queue_item',
     143                    nonce: mxchatAdmin.queue_nonce,
     144                    queue_id: currentQueueId
     145                }
     146            });
     147            fetchPromises.push(promise);
     148        }
     149
     150        // Wait for all fetch requests to complete
     151        Promise.all(fetchPromises).then(function(responses) {
     152            // Filter out completed/error responses and extract items
     153            const items = [];
     154            let queueComplete = false;
     155
     156            for (let response of responses) {
     157                if (response.success && response.data && !response.data.complete) {
     158                    items.push(response.data.item);
     159                } else if (response.data && response.data.complete) {
     160                    queueComplete = true;
     161                }
     162            }
     163
     164            // If no items to process, queue is done
     165            if (items.length === 0) {
     166                if (queueComplete) {
     167                    handleQueueComplete();
     168                } else {
    144169                    verifyQueueCompletion();
    145                     return;
    146                 }
    147                
    148                 if (response.data.complete) {
    149                     // Queue is complete!
    150                     //console.log('MxChat: Queue processing complete!');
    151                     handleQueueComplete();
    152                     return;
    153                 }
    154                
    155                 // Process this item
    156                 const item = response.data.item;
    157                 //console.log('MxChat: Processing item:', item.type, item.id);
    158                
    159                 processQueueItem(item);
    160             },
    161             error: function(xhr, status, error) {
    162                 console.error('MxChat: AJAX error getting next item:', error);
    163                 // Network error - verify queue status before retrying
     170                }
     171                return;
     172            }
     173
     174            // Process all items in this batch simultaneously
     175            const processPromises = items.map(item => processQueueItem(item));
     176
     177            // Wait for all items to finish processing
     178            Promise.all(processPromises).then(function() {
     179                // Update progress after batch completes
     180                updateQueueProgress();
     181
     182                // If we got fewer items than batch size, queue might be done
     183                if (items.length < BATCH_SIZE || queueComplete) {
     184                    verifyQueueCompletion();
     185                } else {
     186                    // Process next batch immediately
     187                    processNextBatch();
     188                }
     189            }).catch(function(error) {
     190                console.error('MxChat: Error processing batch:', error);
     191                // Continue anyway
     192                updateQueueProgress();
    164193                setTimeout(function() {
    165                     verifyQueueCompletion();
    166                 }, 2000);
    167             }
     194                    processNextBatch();
     195                }, 1000);
     196            });
     197
     198        }).catch(function(error) {
     199            console.error('MxChat: Error fetching batch:', error);
     200            // Verify queue status before retrying
     201            setTimeout(function() {
     202                verifyQueueCompletion();
     203            }, 2000);
    168204        });
    169205    }
     
    195231                        // Still has items, try to continue
    196232                        //console.log('MxChat: Queue still has pending items, continuing...');
    197                         processNextQueueItem();
     233                        processNextBatch();
    198234                    }
    199235                } else {
     
    213249    /**
    214250     * Process a single queue item
    215      *  Never stops the queue - always continues regardless of success/failure
     251     * Returns a Promise that resolves when processing is complete
    216252     */
    217253    function processQueueItem(item) {
    218         $.ajax({
     254        return $.ajax({
    219255            url: ajaxurl,
    220256            type: 'POST',
     
    226262                item_data: item.data,
    227263                bot_id: item.bot_id
    228             },
    229             success: function(response) {
    230                 if (response.success) {
    231                     // Item processed successfully
    232                     //console.log('MxChat: Item processed successfully:', item.id);
    233                    
    234                     // Update progress
    235                     updateQueueProgress();
    236                    
    237                     // Small delay to prevent server overload, then process next
    238                     setTimeout(function() {
    239                         processNextQueueItem();
    240                     }, 500); // 500ms delay between items
    241                    
    242                 } else {
    243                     // Item failed but we KEEP GOING
    244                     console.warn('MxChat: Item processing failed (will continue):', item.type, item.id);
    245                     console.warn('MxChat: Error details:', response.data);
    246                    
    247                     // Update progress to reflect the attempt
    248                     updateQueueProgress();
    249                    
    250                     // Continue to next item regardless
    251                     setTimeout(function() {
    252                         processNextQueueItem();
    253                     }, 500);
    254                 }
    255             },
    256             error: function(xhr, status, error) {
    257                 // Network error - log it but KEEP GOING
    258                 console.error('MxChat: AJAX/Network error processing item:', item.id, error);
    259                
    260                 // Update progress
    261                 updateQueueProgress();
    262                
    263                 // Wait a bit longer for network errors, then continue
    264                 setTimeout(function() {
    265                     processNextQueueItem();
    266                 }, 1000);
    267             }
     264            }
     265        }).then(function(response) {
     266            if (response.success) {
     267                // Item processed successfully
     268                //console.log('MxChat: Item processed successfully:', item.id);
     269                return true;
     270            } else {
     271                // Item failed but we KEEP GOING
     272                console.warn('MxChat: Item processing failed (will continue):', item.type, item.id);
     273                console.warn('MxChat: Error details:', response.data);
     274                return false;
     275            }
     276        }).catch(function(xhr, status, error) {
     277            // Network error - log it but KEEP GOING
     278            console.error('MxChat: AJAX/Network error processing item:', item.id, error);
     279            return false;
    268280        });
    269281    }
  • mxchat-basic/trunk/js/mxchat-admin.js

    r3386482 r3392455  
    486486                    });
    487487                });
    488                
     488
     489                // *** ADD THIS: Refresh API key status after saving an API key ***
     490                const isApiKeyField = name && (
     491                    name === 'api_key' ||
     492                    name === 'xai_api_key' ||
     493                    name === 'claude_api_key' ||
     494                    name === 'voyage_api_key' ||
     495                    name === 'gemini_api_key' ||
     496                    name === 'deepseek_api_key' ||
     497                    name === 'openrouter_api_key' ||
     498                    name.indexOf('_api_key') !== -1
     499                );
     500
     501                if (isApiKeyField && typeof window.mxchatRefreshAPIKeyStatus === 'function') {
     502                    window.mxchatRefreshAPIKeyStatus();
     503                }
     504
    489505                // *** ADD THIS: Update Pinecone checkbox state after successful save ***
    490506                if (name && name.indexOf('mxchat_pinecone_addon_options[mxchat_use_pinecone]') !== -1) {
     
    740756].forEach(toggleVisibility);
    741757
    742 // Handle API key visibility based on model selection
    743 function setupAPIKeyVisibility() {
    744     // Cache the selectors
    745     const $chatModelSelect = $('#model');
    746     const $embeddingModelSelect = $('#embedding_model');
    747    
    748     // First, locate and mark the API key rows
    749     setupAPIKeyRows();
    750    
    751     // Initial setup based on current selections
    752     updateApiKeyVisibility();
    753    
    754     // Listen for changes to the model selectors
    755     $chatModelSelect.on('change', updateApiKeyVisibility);
    756     $embeddingModelSelect.on('change', updateApiKeyVisibility);
    757    
    758     /**
    759      * Locate and mark rows that contain API key fields
    760      */
    761     function setupAPIKeyRows() {
    762         // Find key rows by their field IDs
    763         const providerMap = {
    764             'api_key': 'openai',
    765             'xai_api_key': 'xai',
    766             'claude_api_key': 'claude',
    767             'deepseek_api_key': 'deepseek',
    768             'voyage_api_key': 'voyage',
    769             'gemini_api_key': 'gemini' // Added Gemini API key mapping
    770         };
    771        
    772         $.each(providerMap, function(fieldId, provider) {
    773             const $field = $('#' + fieldId);
    774             if ($field.length) {
    775                 const $row = $field.closest('tr');
    776                 $row.addClass('mxchat-setting-row');
    777                 $row.attr('data-provider', provider);
    778             }
    779         });
    780     }
    781    
    782 /**
    783  * Updates the visibility of API key fields based on current model selections
    784  */
    785 function updateApiKeyVisibility() {
    786     const chatModel = $chatModelSelect.val();
    787     const embeddingModel = $embeddingModelSelect.val();
    788    
    789     // Determine which providers are needed
    790     const isOpenAIChat = chatModel && chatModel.startsWith('gpt-');
    791     const isXAI = chatModel && chatModel.startsWith('grok-');
    792     const isClaude = chatModel && chatModel.startsWith('claude-');
    793     const isDeepSeek = chatModel && chatModel.startsWith('deepseek-');
    794     const isGemini = chatModel && chatModel.startsWith('gemini-'); // Added Gemini detection
    795    
    796     const isOpenAIEmbedding = embeddingModel && embeddingModel.startsWith('text-embedding-');
    797     const isVoyage = embeddingModel && embeddingModel.startsWith('voyage-');
    798     const isGeminiEmbedding = embeddingModel && embeddingModel.startsWith('gemini-embedding-');
    799    
    800     // Update API key visibility for each provider
    801     updateWrapperVisibility('openai', isOpenAIChat || isOpenAIEmbedding);
    802     updateWrapperVisibility('xai', isXAI);
    803     updateWrapperVisibility('claude', isClaude);
    804     updateWrapperVisibility('deepseek', isDeepSeek);
    805     updateWrapperVisibility('voyage', isVoyage);
    806     updateWrapperVisibility('gemini', isGemini || isGeminiEmbedding); // Updated Gemini visibility for both chat and embedding
    807    
    808     // Update provider-specific notices for OpenAI
    809     if (isOpenAIChat && isOpenAIEmbedding) {
    810         $('div[data-provider="openai"] .api-key-notice').text(
    811             'Required for your selected chat model and embedding model. Important: You must add credits before use.'
    812         );
    813     } else if (isOpenAIChat) {
    814         $('div[data-provider="openai"] .api-key-notice').text(
    815             'Required for your selected chat model. Important: You must add credits before use.'
    816         );
    817     } else if (isOpenAIEmbedding) {
    818         $('div[data-provider="openai"] .api-key-notice').text(
    819             'Required for your selected embedding model. Important: You must add credits before use.'
    820         );
    821     }
    822    
    823     // Update provider-specific notices for Gemini
    824     if (isGemini && isGeminiEmbedding) {
    825         $('div[data-provider="gemini"] .api-key-notice').text(
    826             'Required for your selected chat model and embedding model.'
    827         );
    828     } else if (isGemini) {
    829         $('div[data-provider="gemini"] .api-key-notice').text(
    830             'Required for your selected chat model.'
    831         );
    832     } else if (isGeminiEmbedding) {
    833         $('div[data-provider="gemini"] .api-key-notice').text(
    834             'Required for your selected embedding model.'
    835         );
    836     }
    837 }
    838    
    839     /**
    840      * Updates visibility of a specific provider's API key wrapper
    841      */
    842     function updateWrapperVisibility(provider, isVisible) {
    843         const $row = $('tr.mxchat-setting-row[data-provider="' + provider + '"]');
    844        
    845         if (!$row.length) {
    846             //console.warn('API key row not found for provider: ' + provider);
    847             return;
    848         }
    849        
    850         if (isVisible) {
    851             $row.show();
    852             if (!$row.hasClass('highlighted')) {
    853                 $row.addClass('highlighted');
    854                 setTimeout(() => {
    855                     $row.removeClass('highlighted');
    856                 }, 1500);
    857             }
    858         } else {
    859             $row.hide();
    860         }
    861     }
    862 }
    863 
    864758function setupMxChatModelSelector() {
    865759    const $modelSelect = $('#model');
     
    17491643    setupMxChatEmbeddingModelSelector();
    17501644});
    1751    
    1752     // Initialize API key visibility
    1753     setupAPIKeyVisibility();
    1754    
     1645
    17551646    // Add Intent Form Submission
    17561647    $('#mxchat-add-intent-form').on('submit', function(event) {
     
    26882579    });
    26892580});
     2581
     2582// Bot Selector Handler
     2583jQuery(document).ready(function($) {
     2584    var saveTimer;
     2585
     2586    // Function to update bot_id in forms
     2587    function updateBotIdInForm(formSelector) {
     2588        var botId = $('#mxchat-bot-selector').val();
     2589        var form = $(formSelector);
     2590
     2591        if (form.length > 0) {
     2592            // Remove existing bot_id hidden input
     2593            form.find('input[name="bot_id"]').remove();
     2594
     2595            // Add new bot_id hidden input if not default
     2596            if (botId && botId !== 'default') {
     2597                form.append('<input type="hidden" name="bot_id" value="' + botId + '">');
     2598            }
     2599        }
     2600    }
     2601
     2602    $('#mxchat-bot-selector').on('change', function() {
     2603        var botId = $(this).val();
     2604
     2605        // Clear any existing timer
     2606        clearTimeout(saveTimer);
     2607
     2608        // Update all forms with new bot_id when bot selection changes
     2609        updateBotIdInForm('#mxchat-url-form');
     2610        updateBotIdInForm('#mxchat-content-form');
     2611
     2612        // Save the selection via AJAX
     2613        $.ajax({
     2614            url: ajaxurl,
     2615            type: 'POST',
     2616            data: {
     2617                action: 'mxchat_save_selected_bot',
     2618                bot_id: botId,
     2619                nonce: mxchatAdmin.setting_nonce
     2620            },
     2621            success: function(response) {
     2622                if (response.success) {
     2623                    // Show saved indicator
     2624                    $('#mxchat-bot-save-status').fadeIn().delay(2000).fadeOut();
     2625
     2626                    // Reload the page after a short delay to refresh content
     2627                    saveTimer = setTimeout(function() {
     2628                        var currentUrl = new URL(window.location.href);
     2629                        currentUrl.searchParams.set('bot_id', botId);
     2630                        currentUrl.searchParams.set('page', 'mxchat-prompts');
     2631                        window.location.href = currentUrl.toString();
     2632                    }, 500);
     2633                }
     2634            },
     2635            error: function() {
     2636                console.error('Failed to save bot selection');
     2637            }
     2638        });
     2639    });
     2640
     2641    // Initialize forms with current bot_id when the page loads
     2642    setTimeout(function() {
     2643        updateBotIdInForm('#mxchat-url-form');
     2644        updateBotIdInForm('#mxchat-content-form');
     2645    }, 100);
     2646});
     2647
     2648// API Key Status Indicator for Chat Models
     2649jQuery(document).ready(function($) {
     2650    function updateChatModelAPIStatus(apiKeyStatuses) {
     2651        var selectedModel = $('#model').val();
     2652
     2653        // Hide all status messages
     2654        $('.mxchat-api-status').hide();
     2655
     2656        // Return early if no model is selected
     2657        if (!selectedModel) {
     2658            return;
     2659        }
     2660
     2661        // Map models to providers
     2662        var provider = null;
     2663
     2664        if (selectedModel.startsWith('gpt-')) {
     2665            provider = 'openai';
     2666        } else if (selectedModel.startsWith('claude-')) {
     2667            provider = 'claude';
     2668        } else if (selectedModel.startsWith('grok-')) {
     2669            provider = 'xai';
     2670        } else if (selectedModel.startsWith('deepseek-')) {
     2671            provider = 'deepseek';
     2672        } else if (selectedModel.startsWith('gemini-')) {
     2673            provider = 'gemini';
     2674        } else if (selectedModel === 'openrouter') {
     2675            provider = 'openrouter';
     2676        }
     2677
     2678        // If we have fresh API key data, update the messages
     2679        if (apiKeyStatuses && provider && apiKeyStatuses[provider] !== undefined) {
     2680            var $statusElement = $('.mxchat-api-status[data-provider="' + provider + '"]');
     2681            var hasKey = apiKeyStatuses[provider];
     2682
     2683            if (hasKey) {
     2684                $statusElement.html('<span style="color: #00a32a;">✓ API key for ' + getProviderName(provider) + ' detected</span>');
     2685            } else {
     2686                $statusElement.html('<span style="color: #d63638;">⚠ No API key for ' + getProviderName(provider) + ' detected. Please enter API key in API Keys tab.</span>');
     2687            }
     2688        }
     2689
     2690        // Show the appropriate status message
     2691        if (provider) {
     2692            $('.mxchat-api-status[data-provider="' + provider + '"]').show();
     2693        }
     2694    }
     2695
     2696    function updateEmbeddingModelAPIStatus(apiKeyStatuses) {
     2697        var selectedModel = $('#embedding_model').val();
     2698
     2699        // Hide all status messages
     2700        $('.mxchat-embedding-api-status').hide();
     2701
     2702        // Return early if no model is selected
     2703        if (!selectedModel) {
     2704            return;
     2705        }
     2706
     2707        // Map models to providers
     2708        var provider = null;
     2709
     2710        if (selectedModel.startsWith('text-embedding-')) {
     2711            provider = 'openai';
     2712        } else if (selectedModel.startsWith('voyage-')) {
     2713            provider = 'voyage';
     2714        } else if (selectedModel.startsWith('gemini-embedding-')) {
     2715            provider = 'gemini';
     2716        }
     2717
     2718        // If we have fresh API key data, update the messages
     2719        if (apiKeyStatuses && provider && apiKeyStatuses[provider] !== undefined) {
     2720            var $statusElement = $('.mxchat-embedding-api-status[data-provider="' + provider + '"]');
     2721            var hasKey = apiKeyStatuses[provider];
     2722
     2723            if (hasKey) {
     2724                $statusElement.html('<span style="color: #00a32a;">✓ API key for ' + getProviderName(provider) + ' detected</span>');
     2725            } else {
     2726                $statusElement.html('<span style="color: #d63638;">⚠ No API key for ' + getProviderName(provider) + ' detected. Please enter API key in API Keys tab.</span>');
     2727            }
     2728        }
     2729
     2730        // Show the appropriate status message
     2731        if (provider) {
     2732            $('.mxchat-embedding-api-status[data-provider="' + provider + '"]').show();
     2733        }
     2734    }
     2735
     2736    function getProviderName(provider) {
     2737        var names = {
     2738            'openai': 'OpenAI',
     2739            'claude': 'Anthropic (Claude)',
     2740            'xai': 'X.AI (Grok)',
     2741            'deepseek': 'DeepSeek',
     2742            'gemini': 'Google Gemini',
     2743            'openrouter': 'OpenRouter',
     2744            'voyage': 'Voyage AI'
     2745        };
     2746        return names[provider] || provider;
     2747    }
     2748
     2749    function refreshAPIKeyStatus() {
     2750        $.ajax({
     2751            url: ajaxurl,
     2752            type: 'POST',
     2753            data: {
     2754                action: 'mxchat_check_api_keys',
     2755                nonce: mxchatAdmin.setting_nonce
     2756            },
     2757            success: function(response) {
     2758                if (response.success && response.data) {
     2759                    updateChatModelAPIStatus(response.data);
     2760                    updateEmbeddingModelAPIStatus(response.data);
     2761                }
     2762            }
     2763        });
     2764    }
     2765
     2766    // Expose refresh function globally so auto-save can call it
     2767    window.mxchatRefreshAPIKeyStatus = refreshAPIKeyStatus;
     2768
     2769    // Run on page load
     2770    updateChatModelAPIStatus();
     2771    updateEmbeddingModelAPIStatus();
     2772
     2773    // Check if we just saved settings (WordPress redirects with ?settings-updated=true)
     2774    var urlParams = new URLSearchParams(window.location.search);
     2775    if (urlParams.get('settings-updated') === 'true') {
     2776        // Page was just reloaded after save, fetch fresh API key status
     2777        refreshAPIKeyStatus();
     2778    }
     2779
     2780    // Run when model changes
     2781    $('#model').on('change', function() { updateChatModelAPIStatus(); });
     2782    $('#embedding_model').on('change', function() { updateEmbeddingModelAPIStatus(); });
     2783});
  • mxchat-basic/trunk/mxchat-basic.php

    r3389316 r3392455  
    44 * Plugin URI: https://mxchat.ai/
    55 * Description: AI chatbot for WordPress with OpenAI, Claude, xAI, DeepSeek, live agent, PDF uploads, WooCommerce, and training on website data.
    6  * Version: 2.5.2
     6 * Version: 2.5.3
    77 * Author: MxChat
    88 * Author URI: https://mxchat.ai
     
    1818
    1919// Define plugin version constant for asset versioning
    20 define('MXCHAT_VERSION', '2.5.2');
     20// Reads version from plugin header automatically
     21if (!defined('MXCHAT_VERSION')) {
     22    $plugin_data = get_file_data(__FILE__, array('Version' => 'Version'), 'plugin');
     23    define('MXCHAT_VERSION', $plugin_data['Version']);
     24}
    2125
    2226function mxchat_load_textdomain() {
     
    811815       
    812816        if (class_exists('MxChat_Integrator')) {
     817            global $mxchat_integrator;
    813818            $mxchat_integrator = new MxChat_Integrator();
    814819        }
  • mxchat-basic/trunk/readme.txt

    r3389316 r3392455  
    66Tested up to: 6.8
    77Requires PHP: 7.2
    8 Stable tag: 2.5.2
     8Stable tag: 2.5.3
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    1515
    1616[Documentation](https://mxchat.ai/documentation/)
    17 
    18 ### 📦 Optional Premium Add-Ons 
    19 Extend the free plugin with these optional premium features: 
    20 
    21 - [Forms Action Add-On](https://mxchat.ai/forms-action/) – Capture leads and data with custom in-chat forms 
    22 - [WooCommerce Add-On](https://mxchat.ai/woocommerce/) – AI-powered shopping assistance and product recommendations 
    23 - [AI Theme Customizer Add-On](https://mxchat.ai/ai-theme-customizer/) – Instantly design chatbot themes with natural language 
    24 - [Multi-Bot Add-On](https://mxchat.ai/multi-bot/) – Run unlimited AI chatbots with custom knowledge and personalities 
    25 - [Image Analysis Add-On](https://mxchat.ai/image-analysis/) – Enable AI-powered image analysis and OCR inside chats 
    26 
    27 👉 [Visit our website to view all add-ons](https://mxchat.ai) 
    28 
    29 ### 🎥 Product Demo Videos 
    30 [Explore all product videos on YouTube](https://www.youtube.com/@MxChat/videos) 
    3117
    3218## Why Choose MxChat AI Chatbot for Your WordPress Website?
     
    3925✅ **Extensive Add-On Ecosystem**: Forms, moderation, recommendations, theme customization, and more 
    4026
    41 ## 🔥 What's New in Version 2.5.2
     27### 📦 Optional Premium Add-Ons 
     28Extend the free plugin with these optional premium features: 
     29
     30- [Forms Action Add-On](https://mxchat.ai/forms-action/) – Capture leads and data with custom in-chat forms 
     31- [WooCommerce Add-On](https://mxchat.ai/woocommerce/) – AI-powered shopping assistance and product recommendations 
     32- [AI Theme Customizer Add-On](https://mxchat.ai/ai-theme-customizer/) – Instantly design chatbot themes with natural language 
     33- [Multi-Bot Add-On](https://mxchat.ai/multi-bot/) – Run unlimited AI chatbots with custom knowledge and personalities 
     34- [Image Analysis Add-On](https://mxchat.ai/image-analysis/) – Enable AI-powered image analysis and OCR inside chats 
     35
     36👉 [Visit our website to view all add-ons](https://mxchat.ai) 
     37
     38### 🎥 Product Demo Videos 
     39[Explore all product videos on YouTube](https://www.youtube.com/@MxChat/videos) 
     40
     41## 🔥 What's New in Version 2.5.3
     42
    4243🚀 **New Features** 
    43 - **URL Hallucination Prevention System** - The AI now only cites URLs that exist in your knowledge base or system instructions. Any hallucinated or invented URLs are automatically removed from responses, ensuring users only receive valid, working links. Admins can view approved URLs in the debug panel for complete transparency.
    44 - **Improved Background Processing System** - Replaced unreliable cron-based processing with a robust queue system for sitemap and PDF imports. Processing now runs directly in your browser tab with real-time progress tracking, ensuring reliable completion of large imports without server timeouts or missed jobs.
     44- **Dedicated API Key Management Tab** - All API keys now organized in one central tab for easier management and overview.
    4545
    4646🔧 **Improvements** 
    47 - **Faster Response Times** - Optimized knowledge retrieval to use the top 3 most relevant documents (down from 5), resulting in quicker chatbot responses and improved answer accuracy by reducing information overload.
     47- **GPT-5 Performance Fix** - Significantly faster response times for all GPT-5 models by implementing minimal reasoning effort mode.
     48- **URL & PDF Link Fix** - Resolved encoding issues causing broken or unclickable links in chatbot responses.
     49- **Performance Enhancements** - Various optimizations for improved stability and faster processing.
    4850
    4951## Core Features That Set MxChat Apart
     
    8789🟢 **Chat Moderation** – Advanced security with email/IP banning and content filtering 
    8890🟢 **Smart Recommender** – Intelligent recommendation flows with customizable matching algorithms 
    89 🟢 **Perplexity Integration** – Real-time web search with authoritative, well-sourced responses
     91🟢 **Perplexity Integration** – Real-time web search with authoritative, well-sourced responses 
     92🟢 **Migration Tool** – Seamlessly switch embedding models and vector databases without losing data, with batch processing and real-time progress tracking
    9093
    9194## 📱 Mobile-Friendly & Fully Customizable
     
    181184
    182185== Changelog ==
     186
     187= 2.5.3 - November 9, 2025 =
     188- New: Dedicated API Key Management Tab - All API keys organized in one central location for easier management
     189- Improvement: GPT-5 performance optimization with minimal reasoning effort mode for significantly faster response times
     190- Fix: Resolved URL and PDF link encoding issues causing broken or unclickable links in responses
     191- Improvement: Various performance enhancements for improved stability and faster processing
    183192
    184193= 2.5.2 - November 3, 2025 =
     
    579588== Upgrade Notice ==
    580589
    581 = 2.5.2 =
    582 - New: URL Hallucination Prevention System - AI only cites URLs from knowledge base or system instructions, with automatic removal of invalid links
    583 - Improvement: Brand new robust queue-based processing system for sitemap and PDF imports with real-time progress tracking
    584 - Improvement: Optimized knowledge retrieval from 5 to 3 documents for faster response times and improved accuracy
    585 - Improvement: Increased URL column size to TEXT type for support of long URLs and special characters (Hebrew, Arabic, etc.)
    586 - Fix: Resolved URL double-encoding issue causing malformed PDF and document links
    587 - Fix: Corrected citation-style bracket handling for AI responses containing [URL] format
    588 - Fix: Fixed empty markdown link processing to prevent broken link generation
     590= 2.5.3 =
     591- New: Dedicated API Key Management Tab - All API keys organized in one central location for easier management
     592- Improvement: GPT-5 performance optimization with minimal reasoning effort mode for significantly faster response times
     593- Fix: Resolved URL and PDF link encoding issues causing broken or unclickable links in responses
     594- Improvement: Various performance enhancements for improved stability and faster processing
    589595
    590596== License & Warranty ==
Note: See TracChangeset for help on using the changeset viewer.