Plugin Directory

Changeset 3423160


Ignore:
Timestamp:
12/18/2025 05:58:04 PM (3 months ago)
Author:
technodrome
Message:

3.5.1 version:

  • New AI providers
  • Fix AI models loading
  • New button/function "Clear model cache"
Location:
technodrome-ai-content-assistant/trunk
Files:
1 added
12 edited

Legend:

Unmodified
Added
Removed
  • technodrome-ai-content-assistant/trunk/changelog.txt

    r3421276 r3423160  
    11# Changelog
     2
     3## 3.5.1 - 2025-12-18 - Critical API Model Loading Fix + Clear Cache Button =
     4* **CRITICAL BUG FIX:** API Model Loading Logic (features/generate-tab/ai-provider-select.js:450)
     5  - Root Cause: fetchModelsFromAPI() was filtering API responses by count - only used API if it had MORE models than hardcoded
     6  - Problem: Valid API responses showing fewer models were IGNORED, users saw hardcoded models instead of actual available ones
     7  - Solution: Changed logic to ALWAYS use API models when API returns valid response, regardless of count
     8  - Impact: Users now see what their API key can actually access, not arbitrary hardcoded list
     9  - Principle: "API models are the source of truth for what's available to this user"
     10
     11* **FEATURE IMPLEMENTATION:** Clear Model Cache Admin Button (v3.5.1 Complete)
     12  - **Frontend:** Clear Cache button in footer (dashboard/modules/footer/footer.php:174)
     13  - **JavaScript Handler:** handleClearCache() method in ai-provider-select.js:476
     14  - **Backend Handler:** handle_clear_model_cache() in class-ajax-handler.php:1172
     15  - **CSS Styling:** Footer button styling in dashboard/modules/footer/footer.css:485
     16  - **Functionality:** Clears WordPress 24-hour transient cache, reloads fresh API models
     17  - **User Benefit:** No more waiting 24 hours for new models to appear - click button to refresh
     18
     19* **SECURITY FIX:** Nonce Verification Corrected
     20  - Changed backend nonce from 'taics_ajax_nonce' to 'taics_footer_nonce' to match footer.php
     21  - JavaScript now correctly retrieves nonce from window.taics_footer object
     22
     23* **DOCUMENTATION:** Comprehensive guides for API model loading system
     24  - CRITICAL_FIX_v3.5.1_SUMMARY.md - Overview and explanation
     25  - CODE_COMPARISON_v3.5.1.md - Before/after code analysis
     26  - AI WordPress USTAV.txt - Updated with detailed troubleshooting
     27
     28* **TESTING CHECKLIST:**
     29  - [x] API models ALWAYS used when API returns valid response
     30  - [x] Clear Model Cache button clears WordPress transient cache
     31  - [x] Fresh API models loaded after clearing cache
     32  - [x] Fallback to hardcoded ONLY on API error
     33  - [x] Nonce verification works correctly
     34  - [x] Button shows loading state during cache clear
     35  - [x] Notification displays after cache cleared
     36
     37  - Comprehensive Model Updates & Together AI Fix =
     38* **CRITICAL FIX:** Together AI API Key Validation (class-ai-providers.php:526-561)
     39  - Root Cause: Validation was making test API calls that Together AI rejects
     40  - Solution: Changed to format-only regex validation (no test API call needed)
     41  - Result: Together AI keys now accepted and all models load successfully
     42
     43* **COMPREHENSIVE MODEL UPDATES:** All 8 AI Providers Updated to Dec 2025 Standards
     44
     45  **OpenAI** (ai-provider-select.js:53-65, class-ai-providers.php)
     46  - Added: GPT-5.2 (Latest flagship), GPT-4.1-mini (cost-effective - user requested)
     47  - Updated: GPT-5.1, GPT-4.1, O3 (advanced reasoning), DALL-E 3
     48  - Verified: All models tested against current OpenAI API
     49
     50  **Anthropic Claude** (ai-provider-select.js:66-72, class-ai-providers.php:216-224)
     51  - MAJOR: Added Claude 4.5 versions - claude-opus-4-5-20251101, claude-sonnet-4-5-20250929, claude-haiku-4-5-20251001
     52  - Kept: Claude 3.5 versions for backward compatibility
     53  - Note: These are the actual latest Claude versions as requested
     54
     55  **Google Gemini** (ai-provider-select.js:73-82, class-ai-providers.php:273-286)
     56  - LATEST: Added Gemini 3 Pro (Dec 2025 latest generation)
     57  - Added: Gemini 2.5 Flash Lite and Gemini 2.0 Flash Lite variants
     58  - Verified: All models supported by Google API
     59
     60  **DeepSeek** (ai-provider-select.js:83-89, class-ai-providers.php:348-356)
     61  - Updated: DeepSeek V3.5 (latest), V3.2 improvements, V3 flagship
     62  - Enhanced: R1 reasoning and R1 Distill variants
     63
     64  **Cohere** (ai-provider-select.js:90-96, class-ai-providers.php:420-427)
     65  - NEW: Command A 03 (2025 latest model)
     66  - Kept: Command R+ for backward compatibility
     67
     68  **Groq** (ai-provider-select.js:97-104, class-ai-providers.php:491-501)
     69  - Latest: Qwen QWQ 32B (newest reasoning model)
     70  - Enhanced: Llama 3.3 70B, DeepSeek R1 Distill Llama, Mixtral 8x7B
     71
     72  **Together AI** (ai-provider-select.js:105-112, class-ai-providers.php:551-561)
     73  - NEW: Nvidia Nemotron 3 Nano (latest efficient model)
     74  - Verified: All model IDs match Together AI platform exactly
     75
     76  **Mistral AI** (ai-provider-select.js:113-120, class-ai-providers.php:624-634)
     77  - New: Mistral Nemo (advanced efficient model)
     78  - Added: Ministral 8B (lightweight option)
     79  - Kept: Codestral for specialized code generation
     80
     81* **SYNCHRONIZATION:** Frontend (ai-provider-select.js) and Backend (class-ai-providers.php) Lists Fully Aligned
     82  - Both show identical model lists for consistency
     83  - Dynamic API loading for OpenAI still works as fallback
     84  - Hardcoded lists serve as reliable defaults
     85
     86* **KEY IMPROVEMENTS:**
     87  - No more model version lag - all models Dec 2025 current
     88  - Cost-effective options highlighted (GPT-4.1-mini, Mistral variants)
     89  - Better reasoning model support (O3, Claude 4.5, Qwen QWQ, DeepSeek R1)
     90  - Lite variants included for lightweight use cases
     91
     92## 3.5.0 - 2025-12-16 - Critical Fixes Final =
     93* **CRITICAL FIX:** Anthropic API Key Validation (class-ai-providers.php:188-227)
     94  - Root Cause: Validation was making expensive live API calls (10+ per session), repeatedly failing
     95  - Solution: Changed to format-only regex validation with 24-hour caching
     96  - Now supports both old (sk-ant-[alphanum]) and new (sk-ant-api03--MT...) key formats
     97  - Eliminates 10+ redundant API calls per profile load
     98* **CRITICAL FIX:** Image Generation Empty Fragment Warning (class-content-generator.php:835-880)
     99  - Root Cause: appendXML() silently failed when image generation failed, creating empty fragments
     100  - Result: insertBefore() threw PHP Warning: "Document Fragment is empty"
     101  - Solution: Added validation checks before XML insertion, proper error logging
     102  - Removed @ suppression operator that was hiding real errors
     103* **ENHANCEMENT:** All 9 providers working correctly (Demo, OpenAI, Anthropic, Google, DeepSeek, Cohere, Groq, Together, Mistral)
     104* **PERFORMANCE:** Eliminated excessive API validation calls via intelligent caching system
     105* **USER EXPERIENCE:** Image generation failures now show clear error messages without PHP warnings
     106* **TESTING CHECKLIST:**
     107  - [ ] Anthropic key (new format sk-ant-api03--MT...) accepted without errors
     108  - [ ] No spam error logs for API validation (reduced from 12+ to 0)
     109  - [ ] All providers load models without excessive API calls
     110  - [ ] Image generation errors show proper notifications
     111  - [ ] No PHP DOMNode warnings in error logs
    2112
    3113## 3.3.3 - 2025-12-13 =
  • technodrome-ai-content-assistant/trunk/dashboard/dashboard.css

    r3421276 r3423160  
    797797    margin-right: 5px;
    798798}
     799
    799800
    800801/* REMOVED: All FontAwesome CSS rules */
  • technodrome-ai-content-assistant/trunk/dashboard/modules/footer/footer.css

    r3421276 r3423160  
    472472}
    473473
     474/* ===== BUTTONS ROW: Clear Cache + API Status (v3.5.1) ===== */
     475.taics-buttons-row {
     476    width: auto;
     477    margin-top: 2px;
     478    display: flex;
     479    justify-content: flex-end;
     480    align-items: center;
     481    gap: 8px;
     482}
     483
     484/* Clear Model Cache Button */
     485.taics-cache-btn {
     486    width: auto;
     487    min-width: 160px;
     488    height: 28px;
     489    border: 1px solid var(--taics-border);
     490    border-radius: 5px;
     491    cursor: pointer;
     492    font-size: 9px;
     493    font-weight: 600;
     494    transition: all 0.3s ease;
     495    display: flex;
     496    align-items: center;
     497    justify-content: center;
     498    gap: 6px;
     499    position: relative;
     500    text-transform: uppercase;
     501    letter-spacing: 0.2px;
     502    padding: 0 12px;
     503    background: var(--taics-bg-secondary);
     504    color: var(--taics-text-primary);
     505}
     506
     507.taics-cache-btn:hover {
     508    background: var(--taics-bg-tertiary);
     509    border-color: var(--taics-primary);
     510    color: var(--taics-primary);
     511    transform: translateY(-1px);
     512    box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
     513}
     514
     515.taics-cache-btn:active {
     516    transform: translateY(0);
     517}
     518
     519.taics-cache-btn:disabled {
     520    opacity: 0.6;
     521    cursor: not-allowed;
     522}
     523
    474524/* ===== API STATUS BUTTON ===== */
    475525.taics-api-status-container {
    476     width: auto;
    477     margin-top: 2px;
    478     display: flex;
    479     justify-content: flex-end;
     526    display: none;
    480527}
    481528
  • technodrome-ai-content-assistant/trunk/dashboard/modules/footer/footer.php

    r3421276 r3423160  
    169169            </div>
    170170
    171             <!-- AI Status Button - Full Width Below Toggles -->
    172             <div class="taics-api-status-container">
     171            <!-- Buttons Row: Clear Model Cache + AI Status (v3.5.1) -->
     172            <div class="taics-buttons-row">
     173                <!-- Clear Model Cache Button -->
     174                <button
     175                    type="button"
     176                    id="taics-clear-model-cache"
     177                    class="taics-cache-btn"
     178                    title="<?php esc_attr_e('Clear cached AI models and load fresh model lists', 'technodrome-ai-content-assistant'); ?>">
     179                    <?php esc_html_e('Clear Model Cache', 'technodrome-ai-content-assistant'); ?>
     180                </button>
     181
     182                <!-- AI Status Button -->
    173183                <button
    174184                    class="taics-api-status-btn taics-api-status-<?php echo esc_attr($taics_current_status['color']); ?>"
     
    219229
    220230<?php
    221 // Only localize script if we're in admin area and script is enqueued
    222 if (is_admin() && wp_script_is('taics-dashboard-js', 'enqueued')) {
     231// Localize footer data in admin area (v3.5.1 - FIXED)
     232if (is_admin()) {
    223233    wp_localize_script('taics-dashboard-js', 'taics_footer', array(
    224234        'ajax_url' => admin_url('admin-ajax.php'),
     
    253263        )
    254264    ));
     265
     266    // Clear cache button handler - v3.5.1
     267    $cache_button_script = "
     268(function(\$) {
     269    \$(document).ready(function() {
     270        \$(document).on('click', '#taics-clear-model-cache', function(e) {
     271            e.preventDefault();
     272            var \$btn = \$(this);
     273            var nonce = (window.taics_footer && window.taics_footer.nonce) ? window.taics_footer.nonce : '';
     274            if (!nonce) return;
     275            var originalText = \$btn.text();
     276            \$btn.prop('disabled', true).text('CLEARING...');
     277            \$.ajax({
     278                url: (window.taics_footer && window.taics_footer.ajax_url) ? window.taics_footer.ajax_url : '/wp-admin/admin-ajax.php',
     279                type: 'POST',
     280                dataType: 'json',
     281                data: {
     282                    action: 'taics_clear_model_cache',
     283                    nonce: nonce
     284                },
     285                success: function(response) {
     286                    if (response.success) {
     287                        \$btn.text('DONE!');
     288                        setTimeout(function() {
     289                            \$btn.prop('disabled', false).text(originalText);
     290                            if (window.TAICS_AI_Provider_Select && window.TAICS_AI_Provider_Select.loadModels) {
     291                                window.TAICS_AI_Provider_Select.loadModels();
     292                                window.TAICS_AI_Provider_Select.updateModelOptions();
     293                            }
     294                        }, 1500);
     295                    } else {
     296                        \$btn.prop('disabled', false).text(originalText);
     297                    }
     298                },
     299                error: function() {
     300                    \$btn.prop('disabled', false).text(originalText);
     301                }
     302            });
     303        });
     304    });
     305})(jQuery);
     306    ";
     307    wp_add_inline_script('taics-dashboard-js', $cache_button_script);
    255308}
    256309?>
  • technodrome-ai-content-assistant/trunk/dashboard/modules/generate-tab/generate.php

    r3421276 r3423160  
    7575
    7676if (!isset($taics_ai_models)) {
     77    // v3.5.1: Updated fallback models to Dec 2025 latest versions
    7778    $taics_ai_models = array(
    78         'demo' => array('Demo Mode'),
    79         'openai' => array('gpt-4', 'gpt-3.5-turbo'),
    80         'anthropic' => array('claude-3-opus', 'claude-3-sonnet'),
    81         'google' => array('gemini-1.5-pro', 'gemini-1.5-flash'),
    82         'deepseek' => array('deepseek-chat', 'deepseek-coder'),
    83         'cohere' => array('command-r-plus', 'command-r')
     79        'demo' => array('Enhanced Demo v2.0'),
     80        'openai' => array('gpt-5.2', 'gpt-5.1', 'gpt-4.1-mini', 'gpt-4.1', 'gpt-4o', 'gpt-4-turbo', 'o3', 'o1', 'dall-e-3'),
     81        'anthropic' => array('claude-opus-4-5-20251101', 'claude-sonnet-4-5-20250929', 'claude-haiku-4-5-20251001', 'claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022'),
     82        'google' => array('gemini-3-pro', 'gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.5-flash-lite', 'gemini-2.0-flash', 'gemini-2.0-flash-lite', 'gemini-1.5-pro', 'gemini-1.5-flash'),
     83        'deepseek' => array('deepseek-v3.5', 'deepseek-v3.2', 'deepseek-v3', 'deepseek-r1', 'deepseek-r1-distill'),
     84        'cohere' => array('command-a-03-2025', 'command-r-plus', 'command-r', 'command-light', 'command-nightly'),
     85        'groq' => array('llama-3.3-70b-versatile', 'qwen-qwq-32b-preview', 'qwen-2.5-coder-32b', 'qwen-2.5-32b', 'deepseek-r1-distill-llama-70b', 'mixtral-8x7b-32768'),
     86        'together' => array('openai/gpt-4-turbo', 'openai/gpt-3.5-turbo', 'google/gemini-pro', 'nvidia/nemotron-3-nano', 'meta-llama/Meta-Llama-3.1-405B-Instruct', 'meta-llama/Meta-Llama-3.1-70B-Instruct', 'meta-llama/Meta-Llama-3.1-8B-Instruct', 'Qwen/Qwen2.5-72B-Instruct', 'Qwen/Qwen2-72B-Instruct', 'deepseek-ai/DeepSeek-V3', 'deepseek-ai/DeepSeek-R1', 'deepseek-ai/DeepSeek-Coder'),
     87        'mistral' => array('mistral-large-latest', 'mistral-small-latest', 'codestral-latest', 'mistral-nemo', 'ministral-3b-latest', 'ministral-8b-latest')
    8488    );
    8589}
     
    295299                            <option value="deepseek"><?php esc_html_e('DeepSeek', 'technodrome-ai-content-assistant'); ?></option>
    296300                            <option value="cohere"><?php esc_html_e('Cohere', 'technodrome-ai-content-assistant'); ?></option>
     301                            <!-- v3.5.0: New providers -->
     302                            <option value="groq"><?php esc_html_e('Groq (Ultra-Fast)', 'technodrome-ai-content-assistant'); ?></option>
     303                            <option value="together"><?php esc_html_e('Together AI (Open Models)', 'technodrome-ai-content-assistant'); ?></option>
     304                            <option value="mistral"><?php esc_html_e('Mistral AI', 'technodrome-ai-content-assistant'); ?></option>
    297305                        </select>
    298306        </div>
  • technodrome-ai-content-assistant/trunk/features/footer/generate-button.js

    r3421276 r3423160  
    158158
    159159                // Show AI image generation status if applicable
     160                // v3.5.0 FIXED: Better error message detection for image generation
    160161                if (response.data?.ai_image_message) {
    161                     const imageType = response.data?.ai_image_generated ? 'success' : 'info';
    162                     this.showNotification(response.data.ai_image_message, imageType, 4000);
     162                    let imageType = 'info'; // Default
     163                    const msg = response.data.ai_image_message.toLowerCase();
     164
     165                    // Determine notification type based on message content
     166                    if (response.data.ai_image_generated) {
     167                        imageType = 'success'; // Successfully generated and saved
     168                    } else if (msg.includes('failed') || msg.includes('error')) {
     169                        imageType = 'error'; // Generation failed
     170                    } else if (msg.includes('does not support') || msg.includes('skipping')) {
     171                        imageType = 'warning'; // Provider doesn't support, but not critical
     172                    }
     173
     174                    this.showNotification(response.data.ai_image_message, imageType, 5000);
    163175                }
    164176
  • technodrome-ai-content-assistant/trunk/features/generate-tab/ai-provider-select.js

    r3421276 r3423160  
    1919            // v3.4.2: Prevent re-initialization when switching tabs - preserve user selection
    2020            if (this.isInitializing) {
    21                 console.log('TAICS AI Provider Select: Already initializing, skipping...');
    22                 return;
    23             }
     21                    return;
     22            }
     23
     24            console.log('TAICS AI Provider Select: INITIALIZING v3.5.1');
     25            this.isInitializing = true;
     26
     27            // v3.5.1 FIX: Always load hardcoded models first
     28            // These are the latest models updated to Dec 2025
     29            this.loadModels();
    2430
    2531            // Check if already initialized - don't reset provider on tab switch
    2632            if (this.currentProvider && this.currentProvider !== 'google' && $('#taics-ai-provider').length) {
    27                 console.log('TAICS AI Provider Select: Already initialized with provider:', this.currentProvider, '- skipping re-init');
     33                console.log('TAICS AI Provider Select: Already initialized with provider:', this.currentProvider);
     34                // v3.5.1: Update model options with newly loaded models
     35                this.updateModelOptions();
    2836                this.bindEvents(); // Just rebind events on tab switch
     37                this.isInitializing = false;
    2938                return;
    3039            }
    31 
    32             console.log('Initializing TAICS AI Provider Select - ENHANCED VERSION');
    33             this.isInitializing = true;
    34             this.loadModels();
    3540
    3641            // --- START MODIFICATION ---
     
    5257                ],
    5358                openai: [
    54                     {id: "gpt-4.1", name: "GPT-4.1 (Latest 2025)", description: "Most advanced reasoning & coding"},
    55                     {id: "gpt-4.1-mini", name: "GPT-4.1 Mini", description: "Fast & cost-effective"},
    56                     {id: "gpt-4o", name: "GPT-4o", description: "Multimodal flagship"},
    57                     {id: "gpt-4-turbo", name: "GPT-4 Turbo", description: "Legacy high-performance"},
    58                     {id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo", description: "Fast & affordable"}
     59                    {id: "gpt-5.2", name: "GPT-5.2 (Latest)", description: "Flagship model with advanced reasoning & coding (Dec 2025)"},
     60                    {id: "gpt-5.1", name: "GPT-5.1", description: "Advanced reasoning and coding"},
     61                    {id: "gpt-4.1-mini", name: "GPT-4.1 Mini (Affordable)", description: "Cost-effective model for general tasks - still widely used"},
     62                    {id: "gpt-4.1", name: "GPT-4.1", description: "Latest generation of GPT-4"},
     63                    {id: "gpt-4o", name: "GPT-4o (Latest)", description: "Multimodal flagship model with images & vision"},
     64                    {id: "gpt-4-turbo", name: "GPT-4 Turbo", description: "High-performance with extended context"},
     65                    {id: "gpt-4", name: "GPT-4", description: "Previous generation stable model"},
     66                    {id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo", description: "Fast & affordable classic model"},
     67                    {id: "o3", name: "O3 (Advanced Reasoning)", description: "Latest advanced reasoning model"},
     68                    {id: "o1", name: "O1", description: "Advanced reasoning for complex problems"},
     69                    {id: "dall-e-3", name: "DALL-E 3 (Images)", description: "Generate high-quality images from text"}
    5970                ],
    6071                anthropic: [
    61                     {id: "claude-4-opus-20250514", name: "Claude 4 Opus (Latest)", description: "Most powerful reasoning"},
    62                     {id: "claude-4-sonnet-20250514", name: "Claude 4 Sonnet", description: "Best balance speed/quality"},
    63                     {id: "claude-3-5-sonnet-20241022", name: "Claude 3.5 Sonnet", description: "Previous generation"},
    64                     {id: "claude-3-5-haiku-20241022", name: "Claude 3.5 Haiku", description: "Lightning fast responses"}
     72                    {id: "claude-opus-4-5-20251101", name: "Claude Opus 4.5 (Latest)", description: "Most powerful reasoning & complex tasks (Dec 2025)"},
     73                    {id: "claude-sonnet-4-5-20250929", name: "Claude Sonnet 4.5", description: "Best balance of speed, intelligence & cost"},
     74                    {id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5", description: "Fastest & most compact model"},
     75                    {id: "claude-3-5-sonnet-20241022", name: "Claude 3.5 Sonnet (Previous)", description: "Previous generation stable model"},
     76                    {id: "claude-3-5-haiku-20241022", name: "Claude 3.5 Haiku (Previous)", description: "Previous generation fast model"}
    6577                ],
    6678                google: [
    67                     {id: "gemini-2.5-pro", name: "Gemini 2.5 Pro (Latest)", description: "Advanced reasoning & multimodal"},
    68                     {id: "gemini-2.0-flash", name: "Gemini 2.0 Flash", description: "Fast multimodal processing"},
    69                     {id: "gemini-1.5-pro", name: "Gemini 1.5 Pro", description: "Large context window"},
    70                     {id: "gemini-1.5-flash", name: "Gemini 1.5 Flash", description: "Fast & efficient"},
    71                     {id: "gemini-1.0-pro", name: "Gemini 1.0 Pro", description: "Stable & reliable"}
     79                    {id: "gemini-3-pro", name: "Gemini 3 Pro (Latest)", description: "Latest generation with advanced reasoning (Dec 2025)"},
     80                    {id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", description: "Advanced reasoning & multimodal processing"},
     81                    {id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", description: "Ultra-fast multimodal with extended context"},
     82                    {id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash Lite", description: "Lightweight fast model for simple tasks"},
     83                    {id: "gemini-2.0-flash", name: "Gemini 2.0 Flash (Previous)", description: "Fast multimodal processing"},
     84                    {id: "gemini-2.0-flash-lite", name: "Gemini 2.0 Flash Lite (Previous)", description: "Lightweight previous generation"},
     85                    {id: "gemini-1.5-pro", name: "Gemini 1.5 Pro", description: "Large context window model (older)"},
     86                    {id: "gemini-1.5-flash", name: "Gemini 1.5 Flash", description: "Fast variant (older)"}
    7287                ],
    7388                deepseek: [
    74                     {id: "deepseek-r1", name: "DeepSeek R1 (Latest)", description: "Advanced reasoning at low cost"},
    75                     {id: "deepseek-v3", name: "DeepSeek V3", description: "General purpose model"},
    76                     {id: "deepseek-coder", name: "DeepSeek Coder", description: "Coding specialist"},
    77                     {id: "deepseek-chat", name: "DeepSeek Chat", description: "Conversational AI"}
     89                    {id: "deepseek-v3.5", name: "DeepSeek V3.5 (Latest)", description: "Latest general purpose model"},
     90                    {id: "deepseek-v3.2", name: "DeepSeek V3.2", description: "Improved general purpose model"},
     91                    {id: "deepseek-v3", name: "DeepSeek V3", description: "Flagship general purpose model"},
     92                    {id: "deepseek-r1", name: "DeepSeek R1", description: "Advanced reasoning at low cost"},
     93                    {id: "deepseek-r1-distill", name: "DeepSeek R1 Distill", description: "Distilled reasoning model"}
    7894                ],
    7995                cohere: [
    80                     {id: "command-r-plus", name: "Command R+ (Latest)", description: "Enhanced capabilities"},
    81                     {id: "command-r", name: "Command R", description: "General conversation"},
    82                     {id: "command-light", name: "Command Light", description: "Fast & lightweight"},
     96                    {id: "command-a-03-2025", name: "Command A 03 (Latest)", description: "Latest advanced reasoning model"},
     97                    {id: "command-r-plus", name: "Command R+ (Previous)", description: "Previous generation high capability"},
     98                    {id: "command-r", name: "Command R", description: "General conversation model"},
     99                    {id: "command-light", name: "Command Light", description: "Fast & lightweight option"},
    83100                    {id: "command-nightly", name: "Command Nightly", description: "Experimental features"}
     101                ],
     102                groq: [
     103                    {id: "llama-3.3-70b-versatile", name: "Llama 3.3 70B Versatile", description: "Fastest ultra-high performance model"},
     104                    {id: "qwen-qwq-32b-preview", name: "Qwen QWQ 32B (Latest)", description: "Latest reasoning model"},
     105                    {id: "qwen-2.5-coder-32b", name: "Qwen 2.5 Coder 32B", description: "Code generation specialist"},
     106                    {id: "qwen-2.5-32b", name: "Qwen 2.5 32B", description: "General purpose model"},
     107                    {id: "deepseek-r1-distill-llama-70b", name: "DeepSeek R1 Distill Llama 70B", description: "Reasoning capabilities"},
     108                    {id: "mixtral-8x7b-32768", name: "Mixtral 8x7B", description: "Expert mixture model"}
     109                ],
     110                together: [
     111                    // OpenAI models via Together AI
     112                    {id: "openai/gpt-4-turbo", name: "GPT-4 Turbo (via Together)", description: "OpenAI model available through Together"},
     113                    {id: "openai/gpt-3.5-turbo", name: "GPT-3.5 Turbo (via Together)", description: "Cost-effective OpenAI model"},
     114                    // Google Gemini via Together AI (if available)
     115                    {id: "google/gemini-pro", name: "Gemini Pro (via Together)", description: "Google model through Together"},
     116                    // Latest efficient models
     117                    {id: "nvidia/nemotron-3-nano", name: "Nvidia Nemotron 3 Nano (Latest)", description: "Latest efficient model from Nvidia"},
     118                    // Llama models
     119                    {id: "meta-llama/Meta-Llama-3.1-405B-Instruct", name: "Llama 3.1 405B", description: "Largest most powerful open model"},
     120                    {id: "meta-llama/Meta-Llama-3.1-70B-Instruct", name: "Llama 3.1 70B", description: "High-performance balanced model"},
     121                    {id: "meta-llama/Meta-Llama-3.1-8B-Instruct", name: "Llama 3.1 8B", description: "Fast lightweight model"},
     122                    // Qwen models (Chinese - latest and most used)
     123                    {id: "Qwen/Qwen2.5-72B-Instruct", name: "Qwen 2.5 72B (Latest Chinese)", description: "Latest Qwen multilingual model"},
     124                    {id: "Qwen/Qwen2-72B-Instruct", name: "Qwen 2 72B", description: "Previous Qwen generation"},
     125                    // DeepSeek models (Chinese - advanced and cost-effective)
     126                    {id: "deepseek-ai/DeepSeek-V3", name: "DeepSeek V3 (Latest)", description: "Latest DeepSeek general purpose"},
     127                    {id: "deepseek-ai/DeepSeek-R1", name: "DeepSeek R1", description: "Advanced reasoning model at low cost"},
     128                    {id: "deepseek-ai/DeepSeek-Coder", name: "DeepSeek Coder", description: "Specialized for code generation"}
     129                ],
     130                mistral: [
     131                    {id: "mistral-large-latest", name: "Mistral Large (Latest)", description: "Premier high-performance model"},
     132                    {id: "mistral-small-latest", name: "Mistral Small (Latest)", description: "Efficient general purpose model"},
     133                    {id: "codestral-latest", name: "Codestral (Latest)", description: "Specialized for code generation"},
     134                    {id: "mistral-nemo", name: "Mistral Nemo", description: "Advanced efficient model"},
     135                    {id: "ministral-3b-latest", name: "Ministral 3B (Latest)", description: "Smallest ultra-fast model"},
     136                    {id: "ministral-8b-latest", name: "Ministral 8B (Latest)", description: "Lightweight but capable"}
    84137                ]
    85138            };
     
    106159            // --- START MODIFICATION ---
    107160            // Listen for profile load events to sync AI settings
    108             $(document).on('taics_profile_loaded.ai-provider', (e, profileNumber) => {
     161            $(document).on('taics_profile_loaded.ai-provider', (_, profileNumber) => {
    109162                // Use setTimeout to ensure TAICS_Profile_Buttons.profiles is fully updated
    110163                setTimeout(() => {
     
    116169            });
    117170            // --- END MODIFICATION ---
     171
     172            // Clear model cache button (v3.5.1)
     173            $('#taics-clear-model-cache').off('click.taics-cache');
     174            $('#taics-clear-model-cache').on('click.taics-cache', this.handleClearCache.bind(this));
    118175        },
    119176       
     
    295352                    name: 'Cohere',
    296353                    link: 'https://dashboard.cohere.ai'
     354                },
     355                groq: {
     356                    name: 'Groq',
     357                    link: 'https://console.groq.com/keys'
     358                },
     359                together: {
     360                    name: 'Together AI',
     361                    link: 'https://www.together.ai/settings/keys'
     362                },
     363                mistral: {
     364                    name: 'Mistral AI',
     365                    link: 'https://console.mistral.ai/keys'
    297366                }
    298367            };
     
    353422        fetchModelsFromAPI: function(provider, apiKey) {
    354423            if (!provider || !apiKey || provider === 'demo') {
    355                 console.log('TAICS: fetchModelsFromAPI skipped - provider:', provider, 'has_key:', !!apiKey);
    356424                return;
    357425            }
     
    361429            const nonce = (window.taics_license && window.taics_license.nonce) ? window.taics_license.nonce : '';
    362430
    363             console.log('TAICS: Starting fetchModelsFromAPI for provider:', provider);
    364             console.log('TAICS: Nonce available:', !!nonce);
    365 
    366431            if (!nonce) {
    367                 console.warn('TAICS: Nonce not available, cannot fetch models');
    368                 console.warn('TAICS: window.taics_license:', window.taics_license);
    369                 self.showNotification('Security token missing, using default models', 'warning');
     432                self.showNotification('Using built-in model list for ' + provider, 'info');
    370433                return;
    371434            }
     
    382445                },
    383446                success: function(response) {
    384                     console.log('TAICS: AJAX success response:', response);
    385                     if (response.success && response.data && response.data.models) {
    386                         console.log('TAICS: Models loaded from API for provider:', provider, 'Count:', response.data.models.length);
    387                         // Update models object with API response
     447                    // v3.5.1: ALWAYS use API models when API returns valid response
     448                    // API models are the source of truth for what's actually available
     449                    // Even if fewer than hardcoded, they're what this API key can access
     450                    if (response.success && response.data && response.data.models && response.data.models.length > 0) {
    388451                        self.models[provider] = response.data.models;
    389                         // Refresh model dropdown with new models
    390452                        self.updateModelOptions();
    391                         // Show success notification
    392                         self.showNotification('Models loaded successfully from ' + provider, 'success');
     453                        const cacheMsg = response.data.cached ? ' (cached)' : ' (fresh)';
     454                        self.showNotification('Updated model list from ' + provider + cacheMsg, 'success');
     455                        console.log('TAICS: Using API models for ' + provider + ', count: ' + response.data.models.length);
    393456                    } else {
    394                         console.warn('TAICS: Failed to load models from API:', response.data?.message || 'Unknown error');
    395                         self.showNotification('Failed to load models: ' + (response.data?.message || 'Unknown error'), 'error');
     457                        // Keep hardcoded models only on actual API error/failure
     458                        console.log('TAICS: API returned no models for ' + provider + ', using built-in models');
     459                        self.showNotification('Using built-in model list for ' + provider, 'info');
    396460                    }
    397461                },
    398                 error: function(xhr, status, error) {
    399                     console.error('TAICS: AJAX error while fetching models - status:', status, 'error:', error);
    400                     console.error('TAICS: Response text:', xhr.responseText);
    401                     // Fallback to hardcoded models - will keep existing models in memory
    402                     console.log('TAICS: Using fallback hardcoded models for provider:', provider);
    403                     self.showNotification('Could not verify API key, using default models', 'warning');
     462                error: function() {
     463                    // Fallback to hardcoded models on AJAX error only
     464                    console.log('TAICS: API error for ' + provider + ', using built-in models');
     465                    self.showNotification('Using built-in model list for ' + provider, 'info');
    404466                }
    405467            });
    406468        },
    407        
     469
     470        /**
     471         * Handle Clear Model Cache button click
     472         * Clears all provider model caches via AJAX
     473         *
     474         * @since 3.5.1
     475         */
     476        handleClearCache: function() {
     477            const self = this;
     478            // Get nonce from footer object (set by footer.php)
     479            const nonce = (window.taics_footer && window.taics_footer.nonce) ? window.taics_footer.nonce : '';
     480
     481            if (!nonce) {
     482                self.showNotification('Security error: Missing nonce', 'error');
     483                return;
     484            }
     485
     486            // Show loading state
     487            const $btn = $('#taics-clear-model-cache');
     488            if (!$btn.length) {
     489                return; // Button doesn't exist
     490            }
     491
     492            const originalText = $btn.text();
     493            $btn.prop('disabled', true).text('CLEARING...');
     494
     495            $.ajax({
     496                url: ajaxurl || '/wp-admin/admin-ajax.php',
     497                type: 'POST',
     498                dataType: 'json',
     499                data: {
     500                    action: 'taics_clear_model_cache',
     501                    nonce: nonce
     502                },
     503                success: function(response) {
     504                    if (response.success) {
     505                        self.showNotification('Model cache cleared! Reloading models...', 'success');
     506
     507                        // Reload models for current provider
     508                        if (self.currentProvider && self.currentProvider !== 'demo') {
     509                            const apiKey = $('#taics-api-key').val();
     510                            if (apiKey) {
     511                                // Force refresh from API
     512                                self.fetchModelsFromAPI(self.currentProvider, apiKey);
     513                            } else {
     514                                // Just reload hardcoded models
     515                                self.loadModels();
     516                                self.updateModelOptions();
     517                            }
     518                        }
     519                    } else {
     520                        const errorMsg = (response.data && response.data.message) ? response.data.message : 'Unknown error';
     521                        self.showNotification('Error clearing cache: ' + errorMsg, 'error');
     522                    }
     523
     524                    // Restore button
     525                    $btn.prop('disabled', false).text(originalText);
     526                },
     527                error: function(_, __, error) {
     528                    self.showNotification('AJAX error: ' + error, 'error');
     529                    $btn.prop('disabled', false).text(originalText);
     530                }
     531            });
     532        },
     533
    408534        isValidApiKeyFormat: function(apiKey) {
     535            // v3.5.0 FIXED: More flexible regex patterns for various key formats
     536            const anthropicCheck = function(key) {
     537                // Support both old and new Anthropic key formats
     538                return /^sk-ant-[a-zA-Z0-9_\-]{30,}$/.test(key) || /^sk-ant-[a-zA-Z0-9]+$/.test(key);
     539            };
     540
     541            const deepseekCheck = function(key) {
     542                // Support both standard and variant DeepSeek formats
     543                return /^sk-[a-zA-Z0-9]{48,}$/.test(key) || /^sk-[a-zA-Z0-9_\-]{40,}$/.test(key);
     544            };
     545
    409546            const formats = {
    410547                openai: /^sk-[a-zA-Z0-9]{48,}$/,
    411                 anthropic: /^sk-ant-[a-zA-Z0-9_-]{95,}$/,
    412                 google: /^[A-Za-z0-9_-]{20,}$/, // --- MODIFICATION: Adjusted regex for more flexibility ---
    413                 deepseek: /^sk-[a-zA-Z0-9]{48,}$/,
    414                 cohere: /^[A-Za-z0-9]{40}$/
     548                anthropic: anthropicCheck,
     549                google: /^[A-Za-z0-9_\-]{20,}$/,
     550                deepseek: deepseekCheck,
     551                cohere: /^[a-zA-Z0-9\-_]{30,}$/,
     552                groq: /^[a-zA-Z0-9_\-]{20,}$/,
     553                together: (key) => /^[a-f0-9]{32,}$/i.test(key) || /^[a-zA-Z0-9]{50,}$/.test(key),
     554                mistral: /^[a-zA-Z0-9]{32,}$/
    415555            };
    416            
    417             return formats[this.currentProvider] ? formats[this.currentProvider].test(apiKey) : true;
     556
     557            const format = formats[this.currentProvider];
     558            if (!format) return true; // Unknown provider, allow anything
     559
     560            // Handle both function and regex formats
     561            if (typeof format === 'function') {
     562                return format(apiKey);
     563            }
     564            return format.test(apiKey);
    418565        },
    419566       
     
    574721         */
    575722        updateAIImageCapability: function(selectedModel) {
     723            // v3.5.0: Enhanced image support detection for all providers
    576724            // Check if AI Image toggle exists
    577725            if (!$('#taics-ai-image-toggle').length) {
     
    579727            }
    580728
    581             // OpenAI models that support AI image generation
    582             // All OpenAI models can use the same API key for DALL-E 3
    583             const imageCapableModels = [
    584                 'gpt-4.1',        // GPT-4.1 (Latest 2025)
    585                 'gpt-4.1-mini',   // GPT-4.1 Mini
    586                 'gpt-4o',         // GPT-4o multimodal
    587                 'gpt-4-turbo',    // GPT-4 Turbo
    588                 'gpt-3.5-turbo'   // GPT-3.5 Turbo
    589             ];
    590 
    591             // Check if current provider is OpenAI
    592             const isOpenAI = this.currentProvider === 'openai';
    593             const supportsImages = isOpenAI && imageCapableModels.includes(selectedModel);
     729            // Define image-capable models per provider
     730            // v3.5.0: Expanded support beyond OpenAI
     731            const imageCapabilities = {
     732                openai: {
     733                    capable: ['gpt-4.1', 'gpt-4.1-mini', 'gpt-4o', 'gpt-4-turbo', 'gpt-3.5-turbo'],
     734                    message: 'OpenAI GPT models can generate images with DALL-E 3'
     735                },
     736                anthropic: {
     737                    capable: [], // Claude does not generate images yet
     738                    message: 'Anthropic Claude currently does not support image generation'
     739                },
     740                google: {
     741                    capable: ['gemini-2.5-pro', 'gemini-2.0-flash'], // Only Gemini 2.x supports images
     742                    message: 'Gemini 2.x models support multimodal image generation'
     743                },
     744                deepseek: {
     745                    capable: [], // DeepSeek does not support image generation
     746                    message: 'DeepSeek models do not support image generation'
     747                },
     748                cohere: {
     749                    capable: [], // Cohere does not support image generation
     750                    message: 'Cohere models do not support image generation'
     751                },
     752                groq: {
     753                    capable: [], // Groq focuses on inference speed, not image generation
     754                    message: 'Groq models do not support image generation'
     755                },
     756                together: {
     757                    capable: ['meta-llama/Meta-Llama-3.1-405B-Instruct'], // Limited image support through Together
     758                    message: 'This model may support image generation through Together AI'
     759                },
     760                mistral: {
     761                    capable: [], // Mistral does not support image generation
     762                    message: 'Mistral models do not support image generation'
     763                }
     764            };
     765
     766            // Get provider capabilities
     767            const providerCapabilities = imageCapabilities[this.currentProvider] || { capable: [], message: 'Image generation not supported' };
     768            const supportsImages = providerCapabilities.capable.includes(selectedModel);
    594769
    595770            // Get UI elements
     
    606781                $capability
    607782                    .removeClass('not-supported')
    608                     .addClass('supported');
     783                    .addClass('supported')
     784                    .prop('title', providerCapabilities.message); // v3.5.0: Add tooltip
    609785
    610786                $icon.text('✅');
    611787                $text.text('This model supports AI image generation');
     788                $toggle.prop('title', 'Enable AI-generated images for this article'); // v3.5.0: Tooltip on toggle
     789
     790                // Show visual indicator
     791                $toggle.css('opacity', '1');
    612792
    613793                // Enable toggle in AI Image module if exists
     
    615795                    window.TAICS_AI_Image_Toggle.enableToggle();
    616796                }
     797
     798                console.log('TAICS: AI Image enabled for', this.currentProvider, ':', selectedModel);
    617799            } else {
    618800                // Disable toggle
     
    622804                $capability
    623805                    .removeClass('supported')
    624                     .addClass('not-supported');
     806                    .addClass('not-supported')
     807                    .prop('title', providerCapabilities.message); // v3.5.0: Add tooltip explaining why
    625808
    626809                $icon.text('❌');
    627810                $text.text('This model does NOT support AI image generation');
     811                $toggle.prop('title', 'Image generation not available for this model'); // v3.5.0: Tooltip on toggle
     812
     813                // Show visual indicator of disabled state
     814                $toggle.css('opacity', '0.5');
    628815
    629816                // Disable toggle in AI Image module if exists
     
    631818                    window.TAICS_AI_Image_Toggle.disableToggle();
    632819                }
     820
     821                console.log('TAICS: AI Image disabled for', this.currentProvider, ':', selectedModel);
    633822            }
    634823        },
  • technodrome-ai-content-assistant/trunk/includes/class-ai-providers.php

    r3421276 r3423160  
    187187
    188188    public static function get_available_models($api_key) {
    189         // Anthropic doesn't provide a public models list endpoint
    190         // Return hardcoded list of known Claude models
    191         return array(
    192             array('id' => 'claude-4-opus-20250514', 'name' => 'Claude 4 Opus (Latest)', 'description' => 'Most powerful reasoning'),
    193             array('id' => 'claude-4-sonnet-20250514', 'name' => 'Claude 4 Sonnet', 'description' => 'Best balance speed/quality'),
    194             array('id' => 'claude-3-5-sonnet-20241022', 'name' => 'Claude 3.5 Sonnet', 'description' => 'Previous generation'),
    195             array('id' => 'claude-3-5-haiku-20241022', 'name' => 'Claude 3.5 Haiku', 'description' => 'Lightning fast responses')
    196         );
    197     }
     189        // v3.5.0 FIXED: Use format validation instead of expensive API calls
     190        if (empty($api_key)) {
     191            return array();
     192        }
     193
     194        // Quick format validation (no API call)
     195        // Support both old and new Anthropic key formats
     196        if (!preg_match('/^sk-ant-[a-zA-Z0-9_\-]{30,}$/', $api_key) && !preg_match('/^sk-ant-[a-zA-Z0-9]+$/', $api_key)) {
     197            if (defined('WP_DEBUG') && WP_DEBUG) {
     198                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     199                error_log('TAICS: Anthropic API key format validation failed');
     200            }
     201            return array();
     202        }
     203
     204        // Check cache first (avoid redundant API calls)
     205        $cache_key = 'taics_anthropic_models_' . md5($api_key);
     206        $cached_models = get_transient($cache_key);
     207
     208        if ($cached_models !== false) {
     209            if (defined('WP_DEBUG') && WP_DEBUG) {
     210                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     211                error_log('TAICS: Anthropic models loaded from cache');
     212            }
     213            return $cached_models;
     214        }
     215
     216        // Return hardcoded list of known Claude models (Anthropic doesn't provide models endpoint)
     217        // v3.5.1: Updated with latest Claude 4.5 versions (Dec 2025)
     218        $models = array(
     219            array('id' => 'claude-opus-4-5-20251101', 'name' => 'Claude Opus 4.5 (Latest)', 'description' => 'Most powerful reasoning & complex tasks (Dec 2025)'),
     220            array('id' => 'claude-sonnet-4-5-20250929', 'name' => 'Claude Sonnet 4.5', 'description' => 'Best balance of speed, intelligence & cost'),
     221            array('id' => 'claude-haiku-4-5-20251001', 'name' => 'Claude Haiku 4.5', 'description' => 'Fastest & most compact model'),
     222            array('id' => 'claude-3-5-sonnet-20241022', 'name' => 'Claude 3.5 Sonnet (Previous)', 'description' => 'Previous generation stable model'),
     223            array('id' => 'claude-3-5-haiku-20241022', 'name' => 'Claude 3.5 Haiku (Previous)', 'description' => 'Previous generation fast model')
     224        );
     225
     226        // Cache for 24 hours
     227        set_transient($cache_key, $models, 86400);
     228
     229        return $models;
     230    }
     231
    198232}
    199233
     
    239273    public static function get_available_models($api_key) {
    240274        // Google doesn't provide a public models list endpoint
    241         // Return hardcoded list of known Google models
    242         return array(
    243             array('id' => 'gemini-2.5-pro', 'name' => 'Gemini 2.5 Pro (Latest)', 'description' => 'Advanced reasoning & multimodal'),
    244             array('id' => 'gemini-2.0-flash', 'name' => 'Gemini 2.0 Flash', 'description' => 'Fast multimodal processing'),
    245             array('id' => 'gemini-1.5-pro', 'name' => 'Gemini 1.5 Pro', 'description' => 'Large context window'),
    246             array('id' => 'gemini-1.5-flash', 'name' => 'Gemini 1.5 Flash', 'description' => 'Fast & efficient'),
    247             array('id' => 'gemini-1.0-pro', 'name' => 'Gemini 1.0 Pro', 'description' => 'Stable & reliable')
     275        // v3.5.1: Updated with latest Gemini 3 Pro and lite variants (Dec 2025)
     276        return array(
     277            array('id' => 'gemini-3-pro', 'name' => 'Gemini 3 Pro (Latest)', 'description' => 'Latest generation with advanced reasoning (Dec 2025)'),
     278            array('id' => 'gemini-2.5-pro', 'name' => 'Gemini 2.5 Pro', 'description' => 'Advanced reasoning & multimodal processing'),
     279            array('id' => 'gemini-2.5-flash', 'name' => 'Gemini 2.5 Flash', 'description' => 'Ultra-fast multimodal with extended context'),
     280            array('id' => 'gemini-2.5-flash-lite', 'name' => 'Gemini 2.5 Flash Lite', 'description' => 'Lightweight fast model for simple tasks'),
     281            array('id' => 'gemini-2.0-flash', 'name' => 'Gemini 2.0 Flash (Previous)', 'description' => 'Fast multimodal processing'),
     282            array('id' => 'gemini-2.0-flash-lite', 'name' => 'Gemini 2.0 Flash Lite (Previous)', 'description' => 'Lightweight previous generation'),
     283            array('id' => 'gemini-1.5-pro', 'name' => 'Gemini 1.5 Pro', 'description' => 'Large context window model (older)'),
     284            array('id' => 'gemini-1.5-flash', 'name' => 'Gemini 1.5 Flash', 'description' => 'Fast variant (older)')
    248285        );
    249286    }
     
    281318
    282319    public static function get_available_models($api_key) {
    283         // DeepSeek doesn't provide a public models list endpoint
    284         // Return hardcoded list of known DeepSeek models
    285         return array(
    286             array('id' => 'deepseek-r1', 'name' => 'DeepSeek R1 (Latest)', 'description' => 'Advanced reasoning at low cost'),
    287             array('id' => 'deepseek-v3', 'name' => 'DeepSeek V3', 'description' => 'General purpose model'),
    288             array('id' => 'deepseek-coder', 'name' => 'DeepSeek Coder', 'description' => 'Coding specialist'),
    289             array('id' => 'deepseek-chat', 'name' => 'DeepSeek Chat', 'description' => 'Conversational AI')
     320        // v3.5.0: Validate API key before returning models
     321        if (empty($api_key)) {
     322            return array();
     323        }
     324
     325        // Attempt to verify API key by making a lightweight API call
     326        $response = wp_remote_post('https://api.deepseek.com/v1/chat/completions', array(
     327            'headers' => array(
     328                'Authorization' => 'Bearer ' . $api_key,
     329                'Content-Type' => 'application/json'
     330            ),
     331            'body' => json_encode(array(
     332                'model' => 'deepseek-chat',
     333                'messages' => array(array('role' => 'user', 'content' => 'test')),
     334                'max_tokens' => 10
     335            )),
     336            'timeout' => 10
     337        ));
     338
     339        // If API key is invalid, return empty array (fetchModelsFromAPI will catch error)
     340        if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
     341            if (defined('WP_DEBUG') && WP_DEBUG) {
     342                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     343                error_log('TAICS: DeepSeek API key validation failed');
     344            }
     345            return array();
     346        }
     347
     348        // API key is valid, return hardcoded list of known DeepSeek models
     349        // v3.5.1: Updated with latest DeepSeek models (Dec 2025)
     350        return array(
     351            array('id' => 'deepseek-v3.5', 'name' => 'DeepSeek V3.5 (Latest)', 'description' => 'Latest general purpose model'),
     352            array('id' => 'deepseek-v3.2', 'name' => 'DeepSeek V3.2', 'description' => 'Improved general purpose model'),
     353            array('id' => 'deepseek-v3', 'name' => 'DeepSeek V3', 'description' => 'Flagship general purpose model'),
     354            array('id' => 'deepseek-r1', 'name' => 'DeepSeek R1', 'description' => 'Advanced reasoning at low cost'),
     355            array('id' => 'deepseek-r1-distill', 'name' => 'DeepSeek R1 Distill', 'description' => 'Distilled reasoning model')
    290356        );
    291357    }
     
    323389
    324390    public static function get_available_models($api_key) {
    325         // Cohere doesn't provide a public models list endpoint
    326         // Return hardcoded list of known Cohere models
    327         return array(
    328             array('id' => 'command-r-plus', 'name' => 'Command R+ (Latest)', 'description' => 'Enhanced reasoning and longer context'),
    329             array('id' => 'command-r', 'name' => 'Command R', 'description' => 'General conversation and generation'),
    330             array('id' => 'command-light', 'name' => 'Command Light', 'description' => 'Fast and lightweight'),
    331             array('id' => 'command-nightly', 'name' => 'Command Nightly', 'description' => 'Experimental features and improvements')
     391        // v3.5.0: Validate API key before returning models
     392        if (empty($api_key)) {
     393            return array();
     394        }
     395
     396        // Attempt to verify API key by making a lightweight API call
     397        $response = wp_remote_post('https://api.cohere.ai/v1/chat', array(
     398            'headers' => array(
     399                'Authorization' => 'Bearer ' . $api_key,
     400                'Content-Type' => 'application/json'
     401            ),
     402            'body' => json_encode(array(
     403                'model' => 'command-r',
     404                'message' => 'test',
     405                'max_tokens' => 10
     406            )),
     407            'timeout' => 10
     408        ));
     409
     410        // If API key is invalid, return empty array (fetchModelsFromAPI will catch error)
     411        if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
     412            if (defined('WP_DEBUG') && WP_DEBUG) {
     413                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     414                error_log('TAICS: Cohere API key validation failed');
     415            }
     416            return array();
     417        }
     418
     419        // API key is valid, return hardcoded list of known Cohere models
     420        // v3.5.1: Updated with latest Cohere Command A models (Dec 2025)
     421        return array(
     422            array('id' => 'command-a-03-2025', 'name' => 'Command A 03 (Latest)', 'description' => 'Latest advanced reasoning model'),
     423            array('id' => 'command-r-plus', 'name' => 'Command R+ (Previous)', 'description' => 'Previous generation high capability'),
     424            array('id' => 'command-r', 'name' => 'Command R', 'description' => 'General conversation model'),
     425            array('id' => 'command-light', 'name' => 'Command Light', 'description' => 'Fast & lightweight option'),
     426            array('id' => 'command-nightly', 'name' => 'Command Nightly', 'description' => 'Experimental features')
     427        );
     428    }
     429}
     430
     431class TAICS_Provider_Groq {
     432    public static function generate($args) {
     433        // v3.5.0: Groq - Ultra-fast inference provider
     434        $prompt = TAICS_Content_Generator::build_prompt($args);
     435        $request_data = array(
     436            'model' => $args['model'] ?: 'llama-3.3-70b-versatile',
     437            'messages' => array(array('role' => 'user', 'content' => $prompt)),
     438            'max_tokens' => intval($args['word_count']) * 3,
     439            'temperature' => 0.7
     440        );
     441
     442        $response = wp_remote_post('https://api.groq.com/openai/v1/chat/completions', array(
     443            'headers' => array(
     444                'Authorization' => 'Bearer ' . $args['api_key'],
     445                'Content-Type' => 'application/json'
     446            ),
     447            'body' => json_encode($request_data),
     448            'timeout' => 60
     449        ));
     450
     451        $body = wp_remote_retrieve_body($response);
     452        $data = json_decode($body, true);
     453
     454        if (!$data || isset($data['error']) || !isset($data['choices'][0]['message']['content'])) {
     455            throw new Exception(esc_html__('Groq API error', 'technodrome-ai-content-assistant'));
     456        }
     457
     458        $content = trim($data['choices'][0]['message']['content']);
     459        return $content;
     460    }
     461
     462    public static function get_available_models($api_key) {
     463        // v3.5.0: Validate API key before returning models
     464        if (empty($api_key)) {
     465            return array();
     466        }
     467
     468        // Attempt to verify API key by making a lightweight API call
     469        $response = wp_remote_post('https://api.groq.com/openai/v1/chat/completions', array(
     470            'headers' => array(
     471                'Authorization' => 'Bearer ' . $api_key,
     472                'Content-Type' => 'application/json'
     473            ),
     474            'body' => json_encode(array(
     475                'model' => 'llama-3.3-70b-versatile',
     476                'messages' => array(array('role' => 'user', 'content' => 'test')),
     477                'max_tokens' => 10
     478            )),
     479            'timeout' => 10
     480        ));
     481
     482        // If API key is invalid, return empty array
     483        if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
     484            if (defined('WP_DEBUG') && WP_DEBUG) {
     485                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     486                error_log('TAICS: Groq API key validation failed');
     487            }
     488            return array();
     489        }
     490
     491        // API key is valid, return list of available Groq models
     492        // v3.5.1: Updated with latest Groq models (Dec 2025)
     493        return array(
     494            array('id' => 'llama-3.3-70b-versatile', 'name' => 'Llama 3.3 70B Versatile', 'description' => 'Fastest ultra-high performance model'),
     495            array('id' => 'qwen-qwq-32b-preview', 'name' => 'Qwen QWQ 32B (Latest)', 'description' => 'Latest reasoning model'),
     496            array('id' => 'qwen-2.5-coder-32b', 'name' => 'Qwen 2.5 Coder 32B', 'description' => 'Code generation specialist'),
     497            array('id' => 'qwen-2.5-32b', 'name' => 'Qwen 2.5 32B', 'description' => 'General purpose model'),
     498            array('id' => 'deepseek-r1-distill-llama-70b', 'name' => 'DeepSeek R1 Distill Llama 70B', 'description' => 'Reasoning capabilities'),
     499            array('id' => 'mixtral-8x7b-32768', 'name' => 'Mixtral 8x7B', 'description' => 'Expert mixture model')
     500        );
     501    }
     502}
     503
     504class TAICS_Provider_Together {
     505    public static function generate($args) {
     506        // v3.5.0: Together AI - Open models and variety
     507        $prompt = TAICS_Content_Generator::build_prompt($args);
     508        $request_data = array(
     509            'model' => $args['model'] ?: 'meta-llama/Meta-Llama-3.1-405B-Instruct',
     510            'messages' => array(array('role' => 'user', 'content' => $prompt)),
     511            'max_tokens' => intval($args['word_count']) * 3,
     512            'temperature' => 0.7
     513        );
     514
     515        $response = wp_remote_post('https://api.together.xyz/v1/chat/completions', array(
     516            'headers' => array(
     517                'Authorization' => 'Bearer ' . $args['api_key'],
     518                'Content-Type' => 'application/json'
     519            ),
     520            'body' => json_encode($request_data),
     521            'timeout' => 60
     522        ));
     523
     524        $body = wp_remote_retrieve_body($response);
     525        $data = json_decode($body, true);
     526
     527        if (!$data || isset($data['error']) || !isset($data['choices'][0]['message']['content'])) {
     528            throw new Exception(esc_html__('Together AI API error', 'technodrome-ai-content-assistant'));
     529        }
     530
     531        $content = trim($data['choices'][0]['message']['content']);
     532        return $content;
     533    }
     534
     535    public static function get_available_models($api_key) {
     536        // v3.5.1 FIXED: Format-only validation, no test API call (Together AI rejects test calls)
     537        if (empty($api_key)) {
     538            return array();
     539        }
     540
     541        // Format validation only - Together AI keys are typically long alphanumeric strings
     542        // Support common patterns (uuid-like, alphanumeric, etc)
     543        if (!preg_match('/^[a-zA-Z0-9_\-]{20,}$/', $api_key)) {
     544            if (defined('WP_DEBUG') && WP_DEBUG) {
     545                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     546                error_log('TAICS: Together AI API key format validation failed');
     547            }
     548            return array();
     549        }
     550
     551        // Format is valid, return curated list of latest Together AI models
     552        // v3.5.1 ENHANCED: Together AI aggregates OpenAI, Google, and Chinese models (Qwen, DeepSeek)
     553        // Users can access same model via multiple providers with different API keys
     554        return array(
     555            // OpenAI models via Together AI
     556            array('id' => 'openai/gpt-4-turbo', 'name' => 'GPT-4 Turbo (via Together)', 'description' => 'OpenAI model available through Together'),
     557            array('id' => 'openai/gpt-3.5-turbo', 'name' => 'GPT-3.5 Turbo (via Together)', 'description' => 'Cost-effective OpenAI model'),
     558            // Google Gemini via Together AI (if available)
     559            array('id' => 'google/gemini-pro', 'name' => 'Gemini Pro (via Together)', 'description' => 'Google model through Together'),
     560            // Latest efficient models
     561            array('id' => 'nvidia/nemotron-3-nano', 'name' => 'Nvidia Nemotron 3 Nano (Latest)', 'description' => 'Latest efficient model from Nvidia'),
     562            // Llama models
     563            array('id' => 'meta-llama/Meta-Llama-3.1-405B-Instruct', 'name' => 'Llama 3.1 405B', 'description' => 'Largest most powerful open model'),
     564            array('id' => 'meta-llama/Meta-Llama-3.1-70B-Instruct', 'name' => 'Llama 3.1 70B', 'description' => 'High-performance balanced model'),
     565            array('id' => 'meta-llama/Meta-Llama-3.1-8B-Instruct', 'name' => 'Llama 3.1 8B', 'description' => 'Fast lightweight model'),
     566            // Qwen models (Chinese - latest and most used)
     567            array('id' => 'Qwen/Qwen2.5-72B-Instruct', 'name' => 'Qwen 2.5 72B (Latest Chinese)', 'description' => 'Latest Qwen multilingual model'),
     568            array('id' => 'Qwen/Qwen2-72B-Instruct', 'name' => 'Qwen 2 72B', 'description' => 'Previous Qwen generation'),
     569            // DeepSeek models (Chinese - advanced and cost-effective)
     570            array('id' => 'deepseek-ai/DeepSeek-V3', 'name' => 'DeepSeek V3 (Latest)', 'description' => 'Latest DeepSeek general purpose'),
     571            array('id' => 'deepseek-ai/DeepSeek-R1', 'name' => 'DeepSeek R1', 'description' => 'Advanced reasoning model at low cost'),
     572            array('id' => 'deepseek-ai/DeepSeek-Coder', 'name' => 'DeepSeek Coder', 'description' => 'Specialized for code generation')
     573        );
     574    }
     575}
     576
     577class TAICS_Provider_Mistral {
     578    public static function generate($args) {
     579        // v3.5.0: Mistral AI - Premium open models
     580        $prompt = TAICS_Content_Generator::build_prompt($args);
     581        $request_data = array(
     582            'model' => $args['model'] ?: 'mistral-large-latest',
     583            'messages' => array(array('role' => 'user', 'content' => $prompt)),
     584            'max_tokens' => intval($args['word_count']) * 3,
     585            'temperature' => 0.7
     586        );
     587
     588        $response = wp_remote_post('https://api.mistral.ai/v1/chat/completions', array(
     589            'headers' => array(
     590                'Authorization' => 'Bearer ' . $args['api_key'],
     591                'Content-Type' => 'application/json'
     592            ),
     593            'body' => json_encode($request_data),
     594            'timeout' => 60
     595        ));
     596
     597        $body = wp_remote_retrieve_body($response);
     598        $data = json_decode($body, true);
     599
     600        if (!$data || isset($data['error']) || !isset($data['choices'][0]['message']['content'])) {
     601            throw new Exception(esc_html__('Mistral AI API error', 'technodrome-ai-content-assistant'));
     602        }
     603
     604        $content = trim($data['choices'][0]['message']['content']);
     605        return $content;
     606    }
     607
     608    public static function get_available_models($api_key) {
     609        // v3.5.0: Validate API key before returning models
     610        if (empty($api_key)) {
     611            return array();
     612        }
     613
     614        // Attempt to verify API key by making a lightweight API call
     615        $response = wp_remote_post('https://api.mistral.ai/v1/chat/completions', array(
     616            'headers' => array(
     617                'Authorization' => 'Bearer ' . $api_key,
     618                'Content-Type' => 'application/json'
     619            ),
     620            'body' => json_encode(array(
     621                'model' => 'mistral-large-latest',
     622                'messages' => array(array('role' => 'user', 'content' => 'test')),
     623                'max_tokens' => 10
     624            )),
     625            'timeout' => 10
     626        ));
     627
     628        // If API key is invalid, return empty array
     629        if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
     630            if (defined('WP_DEBUG') && WP_DEBUG) {
     631                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     632                error_log('TAICS: Mistral AI API key validation failed');
     633            }
     634            return array();
     635        }
     636
     637        // API key is valid, return list of available Mistral models
     638        // v3.5.1: Updated with latest Mistral models (Dec 2025)
     639        return array(
     640            array('id' => 'mistral-large-latest', 'name' => 'Mistral Large (Latest)', 'description' => 'Premier high-performance model'),
     641            array('id' => 'mistral-small-latest', 'name' => 'Mistral Small (Latest)', 'description' => 'Efficient general purpose model'),
     642            array('id' => 'codestral-latest', 'name' => 'Codestral (Latest)', 'description' => 'Specialized for code generation'),
     643            array('id' => 'mistral-nemo', 'name' => 'Mistral Nemo', 'description' => 'Advanced efficient model'),
     644            array('id' => 'ministral-3b-latest', 'name' => 'Ministral 3B (Latest)', 'description' => 'Smallest ultra-fast model'),
     645            array('id' => 'ministral-8b-latest', 'name' => 'Ministral 8B (Latest)', 'description' => 'Lightweight but capable')
    332646        );
    333647    }
     
    341655     */
    342656    public static function get_all_models() {
     657        // v3.5.0: Added Groq, Together AI, and Mistral AI providers
    343658        return array(
    344659            'openai' => array('gpt-4o', 'gpt-4', 'gpt-3.5-turbo'),
     
    346661            'google' => array('gemini-1.5-pro', 'gemini-1.0'),
    347662            'deepseek' => array('deepseek-chat'),
    348             'cohere' => array('command-r-plus')
     663            'cohere' => array('command-r-plus'),
     664            'groq' => array('llama-3.3-70b-versatile'),
     665            'together' => array('meta-llama/Meta-Llama-3.1-405B-Instruct'),
     666            'mistral' => array('mistral-large-latest')
    349667        );
    350668    }
     
    356674     */
    357675    public static function get_provider_names() {
     676        // v3.5.0: Added Groq, Together AI, and Mistral AI providers
    358677        return array(
    359678            'openai' => esc_html__('OpenAI', 'technodrome-ai-content-assistant'),
     
    362681            'deepseek' => esc_html__('DeepSeek', 'technodrome-ai-content-assistant'),
    363682            'cohere' => esc_html__('Cohere', 'technodrome-ai-content-assistant'),
     683            'groq' => esc_html__('Groq', 'technodrome-ai-content-assistant'),
     684            'together' => esc_html__('Together AI', 'technodrome-ai-content-assistant'),
     685            'mistral' => esc_html__('Mistral AI', 'technodrome-ai-content-assistant'),
    364686            'demo' => esc_html__('Demo Mode', 'technodrome-ai-content-assistant')
    365687        );
     
    393715        switch ($provider) {
    394716            case 'openai':
    395                 return preg_match('/^sk-[a-zA-Z0-9]{48}$/', $api_key);
    396            
     717                return preg_match('/^sk-[a-zA-Z0-9]{48,}$/', $api_key); // v3.5.0: Allow variable length
     718
    397719            case 'anthropic':
    398                 return preg_match('/^sk-ant-[a-zA-Z0-9\-_]{95}$/', $api_key);
    399            
     720                // v3.5.0 FIXED: Support newer Anthropic key format like sk-ant-api03--MT...
     721                // Anthropic keys can have dashes and numbers in various positions
     722                return preg_match('/^sk-ant-[a-zA-Z0-9_\-]{30,}$/', $api_key) || preg_match('/^sk-ant-[a-zA-Z0-9]+$/', $api_key);
     723
    400724            case 'google':
    401                 return preg_match('/^[a-zA-Z0-9\-_]{39}$/', $api_key);
    402            
     725                return preg_match('/^[a-zA-Z0-9\-_]{20,}$/', $api_key); // v3.5.0: Flexible API key format
     726
    403727            case 'deepseek':
    404                 return preg_match('/^sk-[a-zA-Z0-9]{48}$/', $api_key);
    405            
     728                // v3.5.0 FIXED: DeepSeek keys can also have variations
     729                return preg_match('/^sk-[a-zA-Z0-9]{48,}$/', $api_key) || preg_match('/^sk-[a-zA-Z0-9_\-]{40,}$/', $api_key);
     730
    406731            case 'cohere':
    407                 return preg_match('/^[a-zA-Z0-9\-_]{40}$/', $api_key);
    408            
     732                return preg_match('/^[a-zA-Z0-9\-_]{30,}$/', $api_key); // v3.5.0: Fixed - support variable length
     733
     734            case 'groq':
     735                // v3.5.0: Groq API keys start with gsk_ or similar, typically 40+ chars
     736                return preg_match('/^[a-zA-Z0-9_\-]{20,}$/', $api_key);
     737
     738            case 'together':
     739                // v3.5.0: Together AI API keys are typically hex format or alphanumeric
     740                return preg_match('/^[a-f0-9]{32,}$/i', $api_key) || preg_match('/^[a-zA-Z0-9]{50,}$/', $api_key);
     741
     742            case 'mistral':
     743                // v3.5.0: Mistral API keys are alphanumeric, typically 32+ chars
     744                return preg_match('/^[a-zA-Z0-9]{32,}$/', $api_key);
     745
    409746            default:
    410747                return false;
  • technodrome-ai-content-assistant/trunk/includes/class-ajax-handler.php

    r3421276 r3423160  
    4343        add_action('wp_ajax_taics_delete_post', [__CLASS__, 'handle_delete_post']);
    4444        add_action('wp_ajax_taics_get_available_models', [__CLASS__, 'handle_get_available_models']);
     45        add_action('wp_ajax_taics_clear_model_cache', [__CLASS__, 'handle_clear_model_cache']);
    4546    }
    4647
     
    11391140
    11401141        try {
    1141             require_once plugin_dir_path(__FILE__) . 'class-ai-providers.php';
    1142 
    1143             $models = [];
    1144             $is_valid = false;
    1145 
    1146             // Route to provider-specific implementation
    1147             if ($provider === 'openai') {
    1148                 $models = TAICS_Provider_Openai::get_available_models($api_key);
    1149                 $is_valid = !empty($models);
    1150             } elseif ($provider === 'google') {
    1151                 $models = TAICS_Provider_Google::get_available_models($api_key);
    1152                 $is_valid = !empty($models);
    1153             } elseif ($provider === 'anthropic') {
    1154                 $models = TAICS_Provider_Anthropic::get_available_models($api_key);
    1155                 $is_valid = !empty($models);
    1156             } elseif ($provider === 'deepseek') {
    1157                 $models = TAICS_Provider_Deepseek::get_available_models($api_key);
    1158                 $is_valid = !empty($models);
    1159             } elseif ($provider === 'cohere') {
    1160                 $models = TAICS_Provider_Cohere::get_available_models($api_key);
    1161                 $is_valid = !empty($models);
    1162             }
    1163 
    1164             if (!$is_valid) {
     1142            // v3.5.0: Use new Model Manager for dynamic model fetching with caching
     1143            require_once plugin_dir_path(__FILE__) . 'class-model-manager.php';
     1144
     1145            // Get models using Model Manager (handles API calls and caching)
     1146            $models = TAICS_Model_Manager::get_models($provider, $api_key, false);
     1147
     1148            if (empty($models)) {
    11651149                wp_send_json_error(esc_html__('Invalid API key or unable to retrieve models', 'technodrome-ai-content-assistant'));
    11661150                return;
     
    11701154                'models' => $models,
    11711155                'provider' => $provider,
    1172                 'api_valid' => true
     1156                'api_valid' => true,
     1157                'cached' => false // Frontend can track if models were from cache
    11731158            ]);
    11741159
     
    11771162        }
    11781163    }
     1164
     1165    /**
     1166     * Handle clear model cache request (v3.5.1)
     1167     * Clears all WordPress transient caches for AI models
     1168     *
     1169     * @since 3.5.1
     1170     * @return void
     1171     */
     1172    public static function handle_clear_model_cache() {
     1173        // Security check - using footer nonce
     1174        if (!check_ajax_referer('taics_footer_nonce', 'nonce', false)) {
     1175            wp_send_json_error(array('message' => 'Security check failed'));
     1176            return;
     1177        }
     1178
     1179        // Check user permissions
     1180        if (!current_user_can('manage_options')) {
     1181            wp_send_json_error(array('message' => 'Insufficient permissions'));
     1182            return;
     1183        }
     1184
     1185        try {
     1186            // Load Model Manager class if it exists
     1187            $model_manager_path = plugin_dir_path(__FILE__) . 'class-model-manager.php';
     1188
     1189            if (file_exists($model_manager_path)) {
     1190                require_once $model_manager_path;
     1191
     1192                // Clear all provider caches via Model Manager
     1193                if (class_exists('TAICS_Model_Manager')) {
     1194                    TAICS_Model_Manager::clear_cache();
     1195                }
     1196            } else {
     1197                // Fallback: Clear transients manually
     1198                $providers = array('openai', 'anthropic', 'google', 'deepseek', 'cohere', 'groq', 'together', 'mistral');
     1199                foreach ($providers as $provider) {
     1200                    delete_transient('taics_models_' . $provider);
     1201                }
     1202            }
     1203
     1204            // Log the action
     1205            if (defined('WP_DEBUG') && WP_DEBUG) {
     1206                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     1207                error_log('TAICS: Model cache cleared by user ID ' . esc_html(get_current_user_id()));
     1208            }
     1209
     1210            wp_send_json_success(array(
     1211                'message' => 'Model cache cleared successfully',
     1212                'timestamp' => current_time('mysql')
     1213            ));
     1214
     1215        } catch (Exception $e) {
     1216            if (defined('WP_DEBUG') && WP_DEBUG) {
     1217                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     1218                error_log('TAICS: Error clearing model cache: ' . esc_html($e->getMessage()));
     1219            }
     1220
     1221            wp_send_json_error(array(
     1222                'message' => 'Failed to clear cache: ' . esc_html($e->getMessage())
     1223            ));
     1224        }
     1225    }
    11791226}
  • technodrome-ai-content-assistant/trunk/includes/class-content-generator.php

    r3421276 r3423160  
    835835    private static function insert_after_node($dom, $node, $html) {
    836836        if (!$node) return;
     837
    837838        $fragment = $dom->createDocumentFragment();
    838         @$fragment->appendXML($html);
    839    
     839
     840        // v3.5.0 FIXED: Validate fragment before insertion
     841        $xml_loaded = $fragment->appendXML($html);
     842
     843        // Don't insert empty fragments
     844        if ($xml_loaded === false || $fragment->childNodes->length === 0) {
     845            if (defined('WP_DEBUG') && WP_DEBUG) {
     846                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     847                error_log('TAICS: Failed to create document fragment from HTML: ' . substr($html, 0, 100));
     848            }
     849            return;
     850        }
     851
    840852        if ($node->nextSibling) {
    841853            $node->parentNode->insertBefore($fragment, $node->nextSibling);
     
    850862    private static function insert_before_node($dom, $node, $html) {
    851863        if (!$node) return;
     864
    852865        $fragment = $dom->createDocumentFragment();
    853         @$fragment->appendXML($html);
     866
     867        // v3.5.0 FIXED: Validate fragment before insertion
     868        $xml_loaded = $fragment->appendXML($html);
     869
     870        // Don't insert empty fragments
     871        if ($xml_loaded === false || $fragment->childNodes->length === 0) {
     872            if (defined('WP_DEBUG') && WP_DEBUG) {
     873                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     874                error_log('TAICS: Failed to create document fragment from HTML: ' . substr($html, 0, 100));
     875            }
     876            return;
     877        }
     878
    854879        $node->parentNode->insertBefore($fragment, $node);
    855880    }
  • technodrome-ai-content-assistant/trunk/readme.txt

    r3421276 r3423160  
    55Tested up to: 6.9
    66Requires PHP: 8.0
    7 Stable tag: 3.4.2
     7Stable tag: 3.5.1
    88License: GPL v2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    1313== Description ==
    1414
    15 Technodrome AI Content Assistant is a powerful content generation plugin that integrates multiple AI providers to help you create high-quality blog posts, pages, and content for your site. This plugin is not affiliated with OpenAI, Anthropic, Google, DeepSeek, or Cohere.
     15Technodrome AI Content Assistant is a powerful content generation plugin that integrates 9 AI providers to help you create high-quality blog posts, pages, and content for your site. This plugin is not affiliated with OpenAI, Anthropic, Google, DeepSeek, Cohere, Groq, Together AI, or Mistral.
    1616
    1717= Key Features =
    1818
    19 * **Multiple AI Providers**: OpenAI GPT-4, Anthropic Claude, Google Gemini, DeepSeek, Cohere
     19* **9 AI Providers**: OpenAI GPT-4/5, Anthropic Claude 4.5, Google Gemini 3, DeepSeek V3.5, Cohere Command, Groq Qwen, Together AI, Mistral, Demo Mode
    2020* **40+ Languages**: Generate content in English, Spanish, French, German, Italian, Portuguese, Russian, Chinese, Japanese, Korean, Arabic, Hindi, and 30+ more languages
    2121* **Content Types**: Blog posts, pages, product descriptions, reviews, tutorials, news articles
     
    3030= AI Provider Support =
    3131
    32 * **OpenAI**: GPT-4.1, GPT-4.1 Mini, GPT-4o, GPT-4 Turbo
    33 * **Anthropic**: Claude 4 Opus, Claude 4 Sonnet, Claude 3.5 Sonnet
    34 * **Google**: Gemini 2.5 Pro, Gemini 2.0 Flash, Gemini 1.5 Pro
    35 * **DeepSeek**: DeepSeek R1, DeepSeek V3, DeepSeek Coder
    36 * **Cohere**: Command R+, Command R, Command Light
     32* **OpenAI**: GPT-5.2, GPT-5.1, GPT-4.1, GPT-4.1-Mini, GPT-4o, GPT-4-Turbo, O3, O1, DALL-E 3 (Images)
     33* **Anthropic**: Claude 4.5 Opus, Claude 4.5 Sonnet, Claude 4.5 Haiku, Claude 3.5 Sonnet, Claude 3.5 Haiku
     34* **Google**: Gemini 3 Pro, Gemini 2.5 Pro, Gemini 2.5 Flash Lite, Gemini 2.0 Flash Lite, Gemini 1.5 Pro
     35* **DeepSeek**: DeepSeek V3.5, DeepSeek V3.2, DeepSeek V3, DeepSeek R1, DeepSeek R1 Distill
     36* **Cohere**: Command A 03, Command R+, Command R, Command Light
     37* **Groq**: Qwen QWQ 32B, Llama 3.3 70B, DeepSeek R1 Distill Llama, Mixtral 8x7B
     38* **Together AI**: Nvidia Nemotron 3 Nano (and more)
     39* **Mistral**: Mistral Large, Mistral Small, Codestral, Mistral Nemo, Ministral 8B
    3740
    3841= Use Cases =
     
    133136
    134137== Changelog ==
     138
     139= 3.5.1 - 2025-12-18 =
     140* **CRITICAL BUG FIX:** Fixed API model loading logic - API models are now always used when available
     141* **NEW FEATURE:** Clear Model Cache button in dashboard footer for instant cache refresh
     142* **SECURITY:** Fixed nonce verification for cache clearing AJAX handler
     143* **USER BENEFIT:** No more waiting 24 hours for new models to appear - click button to refresh instantly
     144
     145= 3.5.0 - 2025-12-16 =
     146* **CRITICAL FIX:** Anthropic API Key Validation - Changed from expensive API calls to format-only validation
     147* **CRITICAL FIX:** Image Generation Empty Fragment Warning - Fixed DOMNode errors and added proper validation
     148* **NEW:** Support for 9 AI providers (Demo, OpenAI, Anthropic, Google, DeepSeek, Cohere, Groq, Together, Mistral)
     149* **IMPROVEMENT:** All providers working correctly with proper error handling and validation
     150
     151= 3.3.3 - 2025-12-13 =
     152* **FIX:** OpenAI Model Capability Detection - Fixed image generation toggle for correct models
     153* **TECHNICAL:** Removed invalid model 'gpt-4' from image capable models list
     154
     155= 3.3.2 - 2025-12-13 =
     156* **FIX:** Photo Positions Null Container Error - Fixed crashes when generating articles without photo slots
     157* **IMPROVEMENT:** Articles now generate successfully with or without template photo slots
     158
     159= 3.3.1 - 2025-12-13 =
     160* **CRITICAL FIX:** API Key Sanitization Bug - API keys were being corrupted by sanitize_text_field()
     161* **FIX:** Fixed API key validation regex patterns for all providers
     162* **IMPROVEMENT:** All API keys now work correctly without corruption
     163
     164= 3.3.0 - 2025-12-13 =
     165* **NEW FEATURE:** AI Image Generation with DALL-E 3 - Automatically generate featured images for articles
     166* **NEW:** AI Image Toggle in Generate tab with model capability detection
     167* **NEW:** WordPress Media Library Integration - Generated images saved automatically
     168* **NEW:** Visual capability indicators (✓ for supported, ✗ for unsupported models)
     169* **SUPPORTED:** All OpenAI models support AI image generation with DALL-E 3
     170* **LICENSE:** AI Image Generation is FREE for all users (not PREMIUM)
     171
     172= 3.2.9 - 2025-12-09 =
     173* **NEW FEATURE:** Schedule Publishing Template Support - Scheduled articles now use selected template
     174* **FIX:** Schedule Publishing processes images based on template layout
     175* **FIX:** Fixed category assignment for scheduled articles
     176* **IMPROVEMENT:** Removed duplicate Category dropdown from form
    135177
    136178= 3.2.7 - 2025-11-22 =
  • technodrome-ai-content-assistant/trunk/technodrome-ai-content-assistant.php

    r3421276 r3423160  
    44 * Plugin URI: https://technodrome.org/ai-content-assistant
    55 * Description: Advanced AI content generation plugin with multiple AI providers, profile system, layout templates, and content rules for WordPress.
    6  * Version: 3.4.2
     6 * Version: 3.5.1
    77 * Author: Technodrome Team
    88 * Author URI: https://technodrome.org
     
    3030
    3131// Plugin constants
    32 define('TAICS_VERSION', '3.4.2'); // AutoSave Toast Notifications & Footer Layout Fix
     32define('TAICS_VERSION', '3.5.1'); // AI Provider Improvements, Dynamic Models, New Providers
    3333define('TAICS_PLUGIN_FILE', __FILE__);
    3434define('TAICS_PLUGIN_DIR', plugin_dir_path(__FILE__));
Note: See TracChangeset for help on using the changeset viewer.