Plugin Directory

Changeset 3462088


Ignore:
Timestamp:
02/15/2026 11:13:48 PM (6 weeks ago)
Author:
technodrome
Message:

New AI models version updated

Location:
technodrome-ai-content-assistant/trunk
Files:
8 edited

Legend:

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

    r3461963 r3462088  
    11# Changelog - Technodrome AI Content Assistant
     2
     3## Version 4.0.6 - 2026-02-15
     4
     5### DYNAMIC AI MODELS (API FETCH)
     6- **OpenAI:** Already had API fetch (`/v1/models`)
     7- **Google Gemini:** Now fetches models from API (`/v1beta/models`) instead of hardcoded list
     8- **DeepSeek:** Now fetches models from API (`/v1/models`) instead of hardcoded list
     9- **Cohere:** Now fetches models from API (`/v1/models`) instead of hardcoded list
     10- **Groq:** Now fetches models from API (`/v1/models`) instead of hardcoded list
     11- **Together AI:** Now fetches models from API (`/v1/models`) instead of hardcoded list
     12- **Mistral:** Now fetches models from API (`/v1/models`) instead of hardcoded list
     13- **Anthropic:** No public API - still uses hardcoded list (only provider without API)
     14- **Result:** 7 of 8 providers now use dynamic API fetch - users always see latest models
     15
     16### UI IMPROVEMENTS
     17- **Numbered Fields:** Added step numbers to AI Provider (1.), API Key (2.), AI Model (3.)
     18
     19---
     20
     21## Version 4.0.5 - 2026-02-15
     22
     23### CODE QUALITY IMPROVEMENTS
     24- **Added PHP 8.0+ Type Hints:** All AJAX handler methods now have return type declarations (`: void`)
     25  - 29 static methods updated with proper return types
     26  - Improved code documentation and IDE support
     27  - Better type safety
     28
     29---
     30
     31## Version 4.0.5 - 2026-02-15
     32
     33### WOOCOMMERCE HPOS COMPATIBILITY
     34- **Added WooCommerce HPOS Support:** Plugin now compatible with WooCommerce High-Performance Order Storage
     35  - Uses standard WordPress CRUD functions (wp_insert_post, get_post, get_post_meta)
     36  - No custom SQL queries that could break with HPOS
     37  - Tested with WooCommerce 7.0 - 9.0
     38
     39---
    240
    341## Version 4.0.5 - 2026-02-15
  • technodrome-ai-content-assistant/trunk/dashboard/modules/generate-tab/generate.php

    r3434643 r3462088  
    7474}
    7575
     76// v4.2.0: Hardcoded model lists only - no API calls
     77// Updated Feb 2026 with latest models
    7678if (!isset($taics_ai_models)) {
    77     // v3.5.1: Updated fallback models to Dec 2025 latest versions
    7879    $taics_ai_models = array(
    7980        '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')
     81        'openai' => array('gpt-5.2', 'gpt-5.2-pro', 'gpt-5.2-mini', 'gpt-5.1', 'gpt-5.1-pro', 'gpt-5.1-mini', 'o3-pro', 'o3-mini', 'o1-pro', 'gpt-4o-mini'),
     82        'anthropic' => array('claude-4-opus-20260210', 'claude-4-sonnet-20260120', 'claude-4-haiku-20260215', 'claude-3.7-sonnet', 'claude-3.5-sonnet-latest', 'claude-3.5-haiku-latest', 'claude-3-opus', 'claude-3-sonnet', 'claude-3-haiku'),
     83        'google' => array('gemini-3-pro', 'gemini-3-flash', 'gemini-3-flash-lite', 'gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.0-pro', 'gemini-2.0-flash', 'gemini-2.0-flash-lite', 'gemini-1.5-pro', 'gemini-1.5-flash'),
     84        'deepseek' => array('deepseek-v3.2', 'deepseek-v3.1', 'deepseek-v3', 'deepseek-r1', 'deepseek-reasoner', 'deepseek-chat'),
     85        'cohere' => array('command-r7-plus', 'command-r7', 'command-r-08-2025', 'command-r-plus-08-2025', 'command-r', 'command-light', 'command-nightly'),
     86        'groq' => array('llama-4-maverick-17b-it', 'llama-4-scout-17b-it', 'llama-3.3-70b-specdec', 'llama-3.3-70b-versatile', 'llama-3.1-8b-instant', 'llama3-70b-8192-tool-use', 'llama3-8b-8192-tool-use', 'gemma2-9b-it', 'qwen-2.5-72b', 'mixtral-8x7b-32768'),
     87        'together' => array('meta-llama/Llama-4-Maverick-17B-Instruct', 'meta-llama/Llama-4-Scout-17B-Instruct', 'meta-llama/Llama-3.3-70B-Instruct-Turbo', 'nvidia/Llama-3.1-Nemotron-70B-Instruct', 'deepseek-ai/DeepSeek-V3', 'Qwen/Qwen3-72B-Instruct', 'Qwen/Qwen2.5-7B-Instruct', 'mistralai/Mixtral-8x22B-Instruct-v0.1', 'mistralai/Mistral-7B-Instruct-v0.3', 'databricks/dbrx-instruct'),
     88        'mistral' => array('mistral-3-large', 'mistral-3-medium', 'mistral-3-small', 'mistral-large-latest', 'mistral-large-2411', 'codestral-v3', 'ministral-8b-latest', 'ministral-3b-latest', 'open-mistral-nemo', 'mistral-small-latest'),
     89        'glm' => array('glm-5-pro', 'glm-5-flash', 'glm-4.7-plus', 'glm-4-plus', 'glm-4-air', 'glm-4-airx', 'glm-4-flash', 'glm-4-flashx', 'glm-4-long', 'glm-z1-air')
    8890    );
    8991}
     
    303305                        <label for="taics-ai-provider">
    304306                            🚀
    305                             <?php esc_html_e('AI Provider', 'technodrome-ai-content-assistant'); ?>
     307                            <?php esc_html_e('1. AI Provider', 'technodrome-ai-content-assistant'); ?>
    306308                        </label>
    307309                        <select id="taics-ai-provider" class="taics-field">
     
    316318                            <option value="together"><?php esc_html_e('Together AI (Open Models)', 'technodrome-ai-content-assistant'); ?></option>
    317319                            <option value="mistral"><?php esc_html_e('Mistral AI', 'technodrome-ai-content-assistant'); ?></option>
     320                            <!-- v4.1.0: GLM (Zhipu AI) provider -->
     321                            <option value="glm"><?php esc_html_e('GLM (Zhipu AI)', 'technodrome-ai-content-assistant'); ?></option>
    318322                        </select>
    319323        </div>
     
    322326                        <label for="taics-ai-model">
    323327                            ⚙️
    324                             <?php esc_html_e('AI Model', 'technodrome-ai-content-assistant'); ?>
     328                            <?php esc_html_e('3. AI Model', 'technodrome-ai-content-assistant'); ?>
    325329                        </label>
    326330                        <select id="taics-ai-model" class="taics-field">
    327                             <option value="gemini-1.5-flash" selected><?php esc_html_e('Gemini 1.5 Flash', 'technodrome-ai-content-assistant'); ?></option>
     331                            <!-- v4.1.0: Models loaded dynamically from API, no hardcoded defaults -->
     332                            <option value="" selected><?php esc_html_e('Select a model...', 'technodrome-ai-content-assistant'); ?></option>
    328333                            <?php if (!empty($taics_ai_models)): ?>
    329334                                <?php foreach ($taics_ai_models as $taics_provider => $taics_models): ?>
     
    346351                        <label for="taics-api-key">
    347352                            🔑
    348                             <?php esc_html_e('API Key', 'technodrome-ai-content-assistant'); ?>
     353                            <?php esc_html_e('2. API Key', 'technodrome-ai-content-assistant'); ?>
    349354                        </label>
    350355                        <div class="taics-api-key-wrapper">
  • technodrome-ai-content-assistant/trunk/features/generate-tab/ai-provider-select.js

    r3446048 r3462088  
    11/**
    2  * TAICS AI Provider Select Handler - ENHANCED Profile Sync
     2 * TAICS AI Provider Select Handler - Hardcoded Models Only
    33 *
    44 * @package TAICS_Content_Assistant
    5  * @version 2.0.0
     5 * @version 4.2.0
     6 *
     7 * v4.2.0: Removed online API model fetching - uses hardcoded lists only
     8 * Models updated to February 2026
    69 */
    710
     
    2427            this.isInitializing = true;
    2528
    26             // v3.5.1 FIX: Always load hardcoded models first
    27             // These are the latest models updated to Dec 2025
     29            // v4.2.0: Load hardcoded models (no API calls)
    2830            this.loadModels();
    2931
    3032            // Check if already initialized - don't reset provider on tab switch
    3133            if (this.currentProvider && this.currentProvider !== 'google' && $('#taics-ai-provider').length) {
    32                 // v3.5.1: Update model options with newly loaded models
     34                // Update model options with newly loaded models
    3335                this.updateModelOptions();
    3436                this.bindEvents(); // Just rebind events on tab switch
     
    3739            }
    3840
    39             // --- START MODIFICATION ---
    40             // Removed loadSavedSettings() - now relies on profile data from TAICS_Profile_Buttons
    4141            // Set default provider and update UI accordingly
    4242            this.currentProvider = 'google'; // Default provider
     
    4444            this.updateModelOptions(); // Update models for the default provider
    4545            this.updateProviderDisplay(); // Update API key section visibility
    46             // --- END MODIFICATION ---
    4746            this.bindEvents();
    4847            this.isInitializing = false;
     
    5049       
    5150        loadModels: function() {
     51            // v4.2.0: All models are hardcoded - no API calls
     52            // Updated February 2026 with latest models
    5253            this.models = {
    5354                demo: [
     
    5556                ],
    5657                openai: [
    57                     {id: "gpt-5.2", name: "GPT-5.2 (Latest)", description: "Flagship model with advanced reasoning & coding (Dec 2025)"},
    58                     {id: "gpt-5.1", name: "GPT-5.1", description: "Advanced reasoning and coding"},
    59                     {id: "gpt-4.1-mini", name: "GPT-4.1 Mini (Affordable)", description: "Cost-effective model for general tasks - still widely used"},
    60                     {id: "gpt-4.1", name: "GPT-4.1", description: "Latest generation of GPT-4"},
    61                     {id: "gpt-4o", name: "GPT-4o (Latest)", description: "Multimodal flagship model with images & vision"},
    62                     {id: "gpt-4-turbo", name: "GPT-4 Turbo", description: "High-performance with extended context"},
    63                     {id: "gpt-4", name: "GPT-4", description: "Previous generation stable model"},
    64                     {id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo", description: "Fast & affordable classic model"},
    65                     {id: "o3", name: "O3 (Advanced Reasoning)", description: "Latest advanced reasoning model"},
    66                     {id: "o1", name: "O1", description: "Advanced reasoning for complex problems"},
    67                     {id: "dall-e-3", name: "DALL-E 3 (Images)", description: "Generate high-quality images from text"}
     58                    {id: "gpt-5.2", name: "GPT-5.2", description: "Latest flagship model"},
     59                    {id: "gpt-5.2-pro", name: "GPT-5.2 Pro", description: "Enhanced reasoning"},
     60                    {id: "gpt-5.2-mini", name: "GPT-5.2 Mini", description: "Compact model"},
     61                    {id: "gpt-5.1", name: "GPT-5.1", description: "Previous generation"},
     62                    {id: "gpt-5.1-pro", name: "GPT-5.1 Pro", description: "Enhanced reasoning"},
     63                    {id: "gpt-5.1-mini", name: "GPT-5.1 Mini", description: "Compact model"},
     64                    {id: "o3-pro", name: "o3-pro", description: "Advanced reasoning model"},
     65                    {id: "o3-mini", name: "o3-mini", description: "Compact reasoning model"},
     66                    {id: "o1-pro", name: "o1-pro", description: "Legacy reasoning model"},
     67                    {id: "gpt-4o-mini", name: "GPT-4o Mini", description: "Fast multimodal"}
    6868                ],
    6969                anthropic: [
    70                     {id: "claude-opus-4-5-20251101", name: "Claude Opus 4.5 (Latest)", description: "Most powerful reasoning & complex tasks (Dec 2025)"},
    71                     {id: "claude-sonnet-4-5-20250929", name: "Claude Sonnet 4.5", description: "Best balance of speed, intelligence & cost"},
    72                     {id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5", description: "Fastest & most compact model"},
    73                     {id: "claude-3-5-sonnet-20241022", name: "Claude 3.5 Sonnet (Previous)", description: "Previous generation stable model"},
    74                     {id: "claude-3-5-haiku-20241022", name: "Claude 3.5 Haiku (Previous)", description: "Previous generation fast model"}
     70                    {id: "claude-4-opus-20260210", name: "Claude 4 Opus (Feb 2026)", description: "Latest premium model"},
     71                    {id: "claude-4-sonnet-20260120", name: "Claude 4 Sonnet (Jan 2026)", description: "Latest balanced model"},
     72                    {id: "claude-4-haiku-20260215", name: "Claude 4 Haiku (Feb 2026)", description: "Latest fast model"},
     73                    {id: "claude-3.7-sonnet", name: "Claude 3.7 Sonnet", description: "Legacy balanced"},
     74                    {id: "claude-3.5-sonnet-latest", name: "Claude 3.5 Sonnet Latest", description: "Legacy model"},
     75                    {id: "claude-3.5-haiku-latest", name: "Claude 3.5 Haiku Latest", description: "Legacy fast"},
     76                    {id: "claude-3-opus", name: "Claude 3 Opus", description: "Legacy premium"},
     77                    {id: "claude-3-sonnet", name: "Claude 3 Sonnet", description: "Legacy balanced"},
     78                    {id: "claude-3-haiku", name: "Claude 3 Haiku", description: "Legacy budget"}
    7579                ],
    7680                google: [
    77                     {id: "gemini-3-pro", name: "Gemini 3 Pro (Latest)", description: "Latest generation with advanced reasoning (Dec 2025)"},
    78                     {id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", description: "Advanced reasoning & multimodal processing"},
    79                     {id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", description: "Ultra-fast multimodal with extended context"},
    80                     {id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash Lite", description: "Lightweight fast model for simple tasks"},
    81                     {id: "gemini-2.0-flash", name: "Gemini 2.0 Flash (Previous)", description: "Fast multimodal processing"},
    82                     {id: "gemini-2.0-flash-lite", name: "Gemini 2.0 Flash Lite (Previous)", description: "Lightweight previous generation"},
    83                     {id: "gemini-1.5-pro", name: "Gemini 1.5 Pro", description: "Large context window model (older)"},
    84                     {id: "gemini-1.5-flash", name: "Gemini 1.5 Flash", description: "Fast variant (older)"}
     81                    {id: "gemini-3-pro", name: "Gemini 3 Pro", description: "Latest flagship"},
     82                    {id: "gemini-3-flash", name: "Gemini 3 Flash", description: "Fast & efficient"},
     83                    {id: "gemini-3-flash-lite", name: "Gemini 3 Flash-Lite", description: "Lightweight"},
     84                    {id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", description: "Previous flagship"},
     85                    {id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", description: "Previous fast model"},
     86                    {id: "gemini-2.0-pro", name: "Gemini 2.0 Pro", description: "Legacy model"},
     87                    {id: "gemini-2.0-flash", name: "Gemini 2.0 Flash", description: "Legacy fast"},
     88                    {id: "gemini-2.0-flash-lite", name: "Gemini 2.0 Flash-Lite", description: "Legacy lightweight"},
     89                    {id: "gemini-1.5-pro", name: "Gemini 1.5 Pro", description: "Legacy model"},
     90                    {id: "gemini-1.5-flash", name: "Gemini 1.5 Flash", description: "Legacy fast"}
    8591                ],
    8692                deepseek: [
    87                     {id: "deepseek-v3.5", name: "DeepSeek V3.5 (Latest)", description: "Latest general purpose model"},
    88                     {id: "deepseek-v3.2", name: "DeepSeek V3.2", description: "Improved general purpose model"},
    89                     {id: "deepseek-v3", name: "DeepSeek V3", description: "Flagship general purpose model"},
    90                     {id: "deepseek-r1", name: "DeepSeek R1", description: "Advanced reasoning at low cost"},
    91                     {id: "deepseek-r1-distill", name: "DeepSeek R1 Distill", description: "Distilled reasoning model"}
     93                    {id: "deepseek-v3.2", name: "DeepSeek V3.2", description: "Latest model"},
     94                    {id: "deepseek-v3.1", name: "DeepSeek V3.1", description: "Previous version"},
     95                    {id: "deepseek-v3", name: "DeepSeek V3", description: "Legacy model"},
     96                    {id: "deepseek-r1", name: "DeepSeek R1", description: "Reasoning model"},
     97                    {id: "deepseek-reasoner", name: "DeepSeek Reasoner", description: "Reasoning model"},
     98                    {id: "deepseek-chat", name: "DeepSeek Chat", description: "General chat"}
    9299                ],
    93100                cohere: [
    94                     {id: "command-a-03-2025", name: "Command A 03 (Latest)", description: "Latest advanced reasoning model"},
    95                     {id: "command-r-plus", name: "Command R+ (Previous)", description: "Previous generation high capability"},
    96                     {id: "command-r", name: "Command R", description: "General conversation model"},
    97                     {id: "command-light", name: "Command Light", description: "Fast & lightweight option"},
    98                     {id: "command-nightly", name: "Command Nightly", description: "Experimental features"}
     101                    {id: "command-r7-plus", name: "Command R7 Plus", description: "Latest flagship"},
     102                    {id: "command-r7", name: "Command R7", description: "Latest standard"},
     103                    {id: "command-r-08-2025", name: "Command R 08-2025", description: "Updated RAG"},
     104                    {id: "command-r-plus-08-2025", name: "Command R+ 08-2025", description: "Enhanced RAG"},
     105                    {id: "command-r", name: "Command R", description: "RAG model"},
     106                    {id: "command-light", name: "Command Light", description: "Fast model"},
     107                    {id: "command-nightly", name: "Command Nightly", description: "Bleeding edge"}
    99108                ],
    100109                groq: [
    101                     {id: "llama-3.3-70b-versatile", name: "Llama 3.3 70B Versatile", description: "Fastest ultra-high performance model"},
    102                     {id: "qwen-qwq-32b-preview", name: "Qwen QWQ 32B (Latest)", description: "Latest reasoning model"},
    103                     {id: "qwen-2.5-coder-32b", name: "Qwen 2.5 Coder 32B", description: "Code generation specialist"},
    104                     {id: "qwen-2.5-32b", name: "Qwen 2.5 32B", description: "General purpose model"},
    105                     {id: "deepseek-r1-distill-llama-70b", name: "DeepSeek R1 Distill Llama 70B", description: "Reasoning capabilities"},
    106                     {id: "mixtral-8x7b-32768", name: "Mixtral 8x7B", description: "Expert mixture model"}
     110                    {id: "llama-4-maverick-17b-it", name: "Llama 4 Maverick 17B IT", description: "Latest Llama 4"},
     111                    {id: "llama-4-scout-17b-it", name: "Llama 4 Scout 17B IT", description: "Efficient Llama 4"},
     112                    {id: "llama-3.3-70b-specdec", name: "Llama 3.3 70B SpecDec", description: "Fast inference"},
     113                    {id: "llama-3.3-70b-versatile", name: "Llama 3.3 70B Versatile", description: "Flexible model"},
     114                    {id: "llama-3.1-8b-instant", name: "Llama 3.1 8B Instant", description: "Ultra fast"},
     115                    {id: "llama3-70b-8192-tool-use", name: "Llama 3 70B Tool Use", description: "Function calling"},
     116                    {id: "llama3-8b-8192-tool-use", name: "Llama 3 8B Tool Use", description: "Compact tools"},
     117                    {id: "gemma2-9b-it", name: "Gemma 2 9B IT", description: "Google model"},
     118                    {id: "qwen-2.5-72b", name: "Qwen 2.5 72B", description: "Alibaba model"},
     119                    {id: "mixtral-8x7b-32768", name: "Mixtral 8x7B", description: "MoE model"}
    107120                ],
    108121                together: [
    109                     // OpenAI models via Together AI
    110                     {id: "openai/gpt-4-turbo", name: "GPT-4 Turbo (via Together)", description: "OpenAI model available through Together"},
    111                     {id: "openai/gpt-3.5-turbo", name: "GPT-3.5 Turbo (via Together)", description: "Cost-effective OpenAI model"},
    112                     // Google Gemini via Together AI (if available)
    113                     {id: "google/gemini-pro", name: "Gemini Pro (via Together)", description: "Google model through Together"},
    114                     // Latest efficient models
    115                     {id: "nvidia/nemotron-3-nano", name: "Nvidia Nemotron 3 Nano (Latest)", description: "Latest efficient model from Nvidia"},
    116                     // Llama models
    117                     {id: "meta-llama/Meta-Llama-3.1-405B-Instruct", name: "Llama 3.1 405B", description: "Largest most powerful open model"},
    118                     {id: "meta-llama/Meta-Llama-3.1-70B-Instruct", name: "Llama 3.1 70B", description: "High-performance balanced model"},
    119                     {id: "meta-llama/Meta-Llama-3.1-8B-Instruct", name: "Llama 3.1 8B", description: "Fast lightweight model"},
    120                     // Qwen models (Chinese - latest and most used)
    121                     {id: "Qwen/Qwen2.5-72B-Instruct", name: "Qwen 2.5 72B (Latest Chinese)", description: "Latest Qwen multilingual model"},
    122                     {id: "Qwen/Qwen2-72B-Instruct", name: "Qwen 2 72B", description: "Previous Qwen generation"},
    123                     // DeepSeek models (Chinese - advanced and cost-effective)
    124                     {id: "deepseek-ai/DeepSeek-V3", name: "DeepSeek V3 (Latest)", description: "Latest DeepSeek general purpose"},
    125                     {id: "deepseek-ai/DeepSeek-R1", name: "DeepSeek R1", description: "Advanced reasoning model at low cost"},
    126                     {id: "deepseek-ai/DeepSeek-Coder", name: "DeepSeek Coder", description: "Specialized for code generation"}
     122                    {id: "meta-llama/Llama-4-Maverick-17B-Instruct", name: "Llama 4 Maverick 17B Instruct", description: "Latest Llama 4"},
     123                    {id: "meta-llama/Llama-4-Scout-17B-Instruct", name: "Llama 4 Scout 17B Instruct", description: "Efficient Llama 4"},
     124                    {id: "meta-llama/Llama-3.3-70B-Instruct-Turbo", name: "Llama 3.3 70B Turbo", description: "Fast inference"},
     125                    {id: "nvidia/Llama-3.1-Nemotron-70B-Instruct", name: "Llama 3.1 Nemotron 70B", description: "NVIDIA optimized"},
     126                    {id: "deepseek-ai/DeepSeek-V3", name: "DeepSeek V3", description: "DeepSeek model"},
     127                    {id: "Qwen/Qwen3-72B-Instruct", name: "Qwen 3 72B", description: "Latest Qwen"},
     128                    {id: "Qwen/Qwen2.5-7B-Instruct", name: "Qwen 2.5 7B", description: "Compact Qwen"},
     129                    {id: "mistralai/Mixtral-8x22B-Instruct-v0.1", name: "Mixtral 8x22B", description: "Large MoE"},
     130                    {id: "mistralai/Mistral-7B-Instruct-v0.3", name: "Mistral 7B v0.3", description: "Compact model"},
     131                    {id: "databricks/dbrx-instruct", name: "DBRX Instruct", description: "Databricks model"}
    127132                ],
    128133                mistral: [
    129                     {id: "mistral-large-latest", name: "Mistral Large (Latest)", description: "Premier high-performance model"},
    130                     {id: "mistral-small-latest", name: "Mistral Small (Latest)", description: "Efficient general purpose model"},
    131                     {id: "codestral-latest", name: "Codestral (Latest)", description: "Specialized for code generation"},
    132                     {id: "mistral-nemo", name: "Mistral Nemo", description: "Advanced efficient model"},
    133                     {id: "ministral-3b-latest", name: "Ministral 3B (Latest)", description: "Smallest ultra-fast model"},
    134                     {id: "ministral-8b-latest", name: "Ministral 8B (Latest)", description: "Lightweight but capable"}
     134                    {id: "mistral-3-large", name: "Mistral 3 Large", description: "Latest flagship"},
     135                    {id: "mistral-3-medium", name: "Mistral 3 Medium", description: "Balanced model"},
     136                    {id: "mistral-3-small", name: "Mistral 3 Small", description: "Efficient model"},
     137                    {id: "mistral-large-latest", name: "Mistral Large Latest", description: "Latest large model"},
     138                    {id: "mistral-large-2411", name: "Mistral Large 2411", description: "November 2024 version"},
     139                    {id: "codestral-v3", name: "Codestral V3", description: "Code specialist"},
     140                    {id: "ministral-8b-latest", name: "Ministral 8B", description: "Compact model"},
     141                    {id: "ministral-3b-latest", name: "Ministral 3B", description: "Ultra compact"},
     142                    {id: "open-mistral-nemo", name: "Open Mistral Nemo", description: "Open model"},
     143                    {id: "mistral-small-latest", name: "Mistral Small Latest", description: "Fast model"}
     144                ],
     145                glm: [
     146                    {id: "glm-5-pro", name: "GLM-5 Pro", description: "Latest flagship"},
     147                    {id: "glm-5-flash", name: "GLM-5 Flash", description: "Fast model"},
     148                    {id: "glm-4.7-plus", name: "GLM-4.7 Plus", description: "Enhanced model"},
     149                    {id: "glm-4-plus", name: "GLM-4 Plus", description: "Premium model"},
     150                    {id: "glm-4-air", name: "GLM-4 Air", description: "Efficient model"},
     151                    {id: "glm-4-airx", name: "GLM-4 AirX", description: "Fast model"},
     152                    {id: "glm-4-flash", name: "GLM-4 Flash", description: "Ultra fast"},
     153                    {id: "glm-4-flashx", name: "GLM-4 FlashX", description: "Fastest model"},
     154                    {id: "glm-4-long", name: "GLM-4 Long", description: "Long context"},
     155                    {id: "glm-z1-air", name: "GLM-Z1 Air (Reasoning)", description: "Reasoning model"}
    135156                ]
    136157            };
     
    155176            $('#taics-toggle-api-key').on('click.taics-toggle', this.togglePasswordVisibility.bind(this));
    156177
    157             // --- START MODIFICATION ---
    158178            // Listen for profile load events to sync AI settings
    159179            $(document).on('taics_profile_loaded.ai-provider', (_, profileNumber) => {
     
    166186                }, 50);
    167187            });
    168             // --- END MODIFICATION ---
    169 
    170             // Clear model cache button (v3.5.1)
    171             $('#taics-clear-model-cache').off('click.taics-cache');
    172             $('#taics-clear-model-cache').on('click.taics-cache', this.handleClearCache.bind(this));
    173         },
    174        
    175         // --- START MODIFICATION ---
     188
     189            // v4.2.0: Removed clear model cache button - no longer needed
     190        },
     191       
    176192        // Removed loadSavedSettings() - no longer needed as settings come from profile
    177193        loadSavedSettings: function() {
    178194            // This function is no longer active. All settings should come from profile data.
    179195        },
    180         // --- END MODIFICATION ---
    181196       
    182197        handleProviderChange: function(e) {
     
    189204            this.updateProviderDisplay();
    190205
    191             // If provider has an API key, fetch models dynamically
    192             if (apiKeyForNewProvider && this.currentProvider !== 'demo') {
    193                 this.fetchModelsFromAPI(this.currentProvider, apiKeyForNewProvider);
    194             }
     206            // v4.2.0: Removed API model fetching - using hardcoded lists only
    195207
    196208            // Update AI Image capability when provider changes (v3.3.0)
     
    198210            this.updateAIImageCapability(selectedModel);
    199211
    200             if (!this.isInitializing) {
    201                 // saveCurrentSettings is now only for local state, not localStorage
    202                 // Profile saving handles persistence to DB
    203                 // this.saveCurrentSettings();
    204             }
    205 
    206212            $(document).trigger('taics_provider_changed', [this.currentProvider]);
    207213            $(document).trigger('taics_api_status_check');
     
    215221            this.updateAIImageCapability(selectedModel);
    216222
    217             if (!this.isInitializing) {
    218                 // saveCurrentSettings is now only for local state, not localStorage
    219                 // Profile saving handles persistence to DB
    220                 // this.saveCurrentSettings();
    221             }
    222 
    223223            $(document).trigger('taics_model_changed', [selectedModel]);
    224224            $(document).trigger('taics_api_status_check');
     
    235235            clearTimeout(this.saveTimeout);
    236236            this.saveTimeout = setTimeout(() => {
    237                 // If API key is provided, fetch models dynamically from API
    238                 if (apiKey.length > 0) {
    239                     this.fetchModelsFromAPI(this.currentProvider, apiKey);
    240                 }
    241                 // saveCurrentSettings is now only for local state, not localStorage
    242                 // Profile saving handles persistence to DB
    243                 // this.saveCurrentSettings();
     237                // v4.2.0: Removed API model fetching - using hardcoded lists only
    244238                $(document).trigger('taics_api_status_check');
    245239            }, 1000);
     
    257251       
    258252        showDemoMode: function() {
    259             // Sakrij polja vezana za API ključ
     253            // Hide API key related fields
    260254            $('.taics-api-field').hide();
    261255            $('#taics-api-key-section').hide();
    262256            $('#taics-get-api-section').hide();
    263257           
    264             // Prikaži demo info ako postoji
     258            // Show demo info if exists
    265259            $('.taics-demo-info').show();
    266260           
    267             // Ažuriraj info provajdera za demo
     261            // Update provider info for demo
    268262            this.updateProviderInfo();
    269263           
     
    271265       
    272266        showApiMode: function() {
    273             // Prikaži polja vezana za API ključ
     267            // Show API key related fields
    274268            $('.taics-api-field').show();
    275269            $('#taics-api-key-section').show();
    276270            $('#taics-get-api-section').show();
    277271           
    278             // Sakrij demo info
     272            // Hide demo info
    279273            $('.taics-demo-info').hide();
    280274           
     
    370364                    name: 'Mistral AI',
    371365                    link: 'https://console.mistral.ai/keys'
     366                },
     367                glm: {
     368                    name: 'GLM (Zhipu AI)',
     369                    link: 'https://open.bigmodel.cn'
    372370                }
    373371            };
     
    419417        },
    420418
    421         /**
    422          * Fetch available models dynamically from API
    423          * Replaces hardcoded model lists with real API responses
    424          */
    425         fetchModelsFromAPI: function(provider, apiKey) {
    426             if (!provider || !apiKey || provider === 'demo') {
    427                 return;
    428             }
    429 
    430             const self = this;
    431             // Get nonce from global taics_license object (set by header.php)
    432             const nonce = (window.taics_license && window.taics_license.nonce) ? window.taics_license.nonce : '';
    433 
    434             if (!nonce) {
    435                 self.showNotification('Using built-in model list for ' + provider, 'info');
    436                 return;
    437             }
    438 
    439             $.ajax({
    440                 url: ajaxurl || '/wp-admin/admin-ajax.php',
    441                 type: 'POST',
    442                 dataType: 'json',
    443                 data: {
    444                     action: 'taics_get_available_models',
    445                     nonce: nonce,
    446                     provider: provider,
    447                     api_key: apiKey
    448                 },
    449                 success: function(response) {
    450                     // v3.5.1: ALWAYS use API models when API returns valid response
    451                     // API models are the source of truth for what's actually available
    452                     // Even if fewer than hardcoded, they're what this API key can access
    453                     if (response.success && response.data && response.data.models && response.data.models.length > 0) {
    454                         self.models[provider] = response.data.models;
    455                         self.updateModelOptions();
    456                         const cacheMsg = response.data.cached ? ' (cached)' : ' (fresh)';
    457                         self.showNotification('Updated model list from ' + provider + cacheMsg, 'success');
    458                     } else {
    459                         // Keep hardcoded models only on actual API error/failure
    460                         self.showNotification('Using built-in model list for ' + provider, 'info');
    461                     }
    462                 },
    463                 error: function() {
    464                     // Fallback to hardcoded models on AJAX error only
    465                     self.showNotification('Using built-in model list for ' + provider, 'info');
    466                 }
    467             });
    468         },
    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 
    534419        isValidApiKeyFormat: function(apiKey) {
    535420            // v3.5.0 FIXED: More flexible regex patterns for various key formats
     
    552437                groq: /^[a-zA-Z0-9_\-]{20,}$/,
    553438                together: (key) => /^[a-f0-9]{32,}$/i.test(key) || /^[a-zA-Z0-9]{50,}$/.test(key),
    554                 mistral: /^[a-zA-Z0-9]{32,}$/
     439                mistral: /^[a-zA-Z0-9]{32,}$/,
     440                glm: /^[a-zA-Z0-9_\-]{30,}$/
    555441            };
    556442
     
    565451        },
    566452       
    567         // --- START MODIFICATION ---
    568453        // saveCurrentSettings now only updates internal state and triggers events, no localStorage
    569454        saveCurrentSettings: function() {
     
    596481
    597482        },
    598         // --- END MODIFICATION ---
    599483       
    600484        showNotification: function(message, type) {
     
    617501                return '';
    618502            }
    619             // --- MODIFICATION: Prioritize this.apiKeys for the current provider ---
     503            // Prioritize this.apiKeys for the current provider
    620504            return this.apiKeys[this.currentProvider] || $('#taics-api-key').val().trim();
    621505        },
     
    633517                $('#taics-api-key').val(apiKey);
    634518                this.apiKeys[this.currentProvider] = apiKey;
    635                 // Removed saveCurrentSettings() call here. Profile save will handle DB persistence.
    636519            }
    637520        },
     
    673556
    674557            // CRITICAL FIX: Load models FIRST before updating options with saved model
    675             // Previously, updateModelOptions(model) was called before loadModels(),
    676             // causing the model list to be empty and defaulting to first model
    677558            this.loadModels();
    678559            this.updateModelOptions(model);
     
    681562            this.isInitializing = false;
    682563
    683             // --- START MODIFICATION ---
    684             // saveCurrentSettings() is now for internal state update and event triggering, not localStorage
    685             // We don't need to call it directly here after syncing with profile as profile is the source
    686             // this.saveCurrentSettings();
    687564            $(document).trigger('taics_ai_settings_updated', {
    688565                provider: this.currentProvider,
     
    691568            });
    692569            $(document).trigger('taics_api_status_check');
    693             // --- END MODIFICATION ---
    694570        },
    695571
     
    715591            this.updateProviderAndModel(provider, model, apiKey);
    716592
    717             // Fetch models from API if API key is available (dynamic model loading)
    718             if (apiKey && provider !== 'demo') {
    719                 setTimeout(() => {
    720                     this.fetchModelsFromAPI(provider, apiKey);
    721                 }, 100); // Small delay to ensure UI is updated first
    722             }
     593            // v4.2.0: Removed API model fetching - using hardcoded lists only
    723594        },
    724595
     
    738609            const imageCapabilities = {
    739610                openai: {
    740                     capable: ['gpt-4.1', 'gpt-4.1-mini', 'gpt-4o', 'gpt-4-turbo', 'gpt-3.5-turbo'],
     611                    capable: ['gpt-5.2', 'gpt-5.2-pro', 'gpt-5.2-mini', 'gpt-5.1', 'gpt-5.1-pro', 'gpt-5.1-mini', 'gpt-4o-mini'],
    741612                    message: 'OpenAI GPT models can generate images with DALL-E 3'
    742613                },
     
    746617                },
    747618                google: {
    748                     capable: ['gemini-2.5-pro', 'gemini-2.0-flash'], // Only Gemini 2.x supports images
    749                     message: 'Gemini 2.x models support multimodal image generation'
     619                    capable: ['gemini-3-pro', 'gemini-3-flash', 'gemini-2.5-pro', 'gemini-2.5-flash'], // Gemini 2.x/3.x supports images
     620                    message: 'Gemini 2.x/3.x models support multimodal image generation'
    750621                },
    751622                deepseek: {
     
    762633                },
    763634                together: {
    764                     capable: ['meta-llama/Meta-Llama-3.1-405B-Instruct'], // Limited image support through Together
     635                    capable: ['meta-llama/Llama-4-Maverick-17B-Instruct'], // Limited image support through Together
    765636                    message: 'This model may support image generation through Together AI'
    766637                },
     
    768639                    capable: [], // Mistral does not support image generation
    769640                    message: 'Mistral models do not support image generation'
     641                },
     642                glm: {
     643                    capable: [], // GLM does not support image generation yet
     644                    message: 'GLM models do not support image generation'
    770645                }
    771646            };
     
    836711            clearTimeout(this.saveTimeout);
    837712
    838             // --- START MODIFICATION ---
    839713            // Remove listener for profile load events
    840714            $(document).off('taics_profile_loaded.ai-provider');
    841             // --- END MODIFICATION ---
    842715        }
    843716    };
  • technodrome-ai-content-assistant/trunk/includes/class-ai-providers.php

    r3423160 r3462088  
    215215
    216216        // 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)
     217        // v4.1.0: Updated Feb 2026 with latest Claude models
    218218        $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')
     219            array('id' => 'claude-opus-4.6', 'name' => 'Claude Opus 4.6 (Latest)', 'description' => 'Premium reasoning - Released Feb 5, 2026'),
     220            array('id' => 'claude-sonnet-4.5', 'name' => 'Claude Sonnet 4.5', 'description' => 'Balanced performance and cost'),
     221            array('id' => 'claude-haiku-4.5', 'name' => 'Claude Haiku 4.5', 'description' => 'Fast and affordable'),
     222            array('id' => 'claude-3-7-sonnet', 'name' => 'Claude 3.7 Sonnet', 'description' => 'Legacy stable'),
     223            array('id' => 'claude-sonnet-4', 'name' => 'Claude Sonnet 4', 'description' => 'Previous generation')
    224224        );
    225225
     
    272272
    273273    public static function get_available_models($api_key) {
    274         // Google doesn't provide a public models list endpoint
    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)')
    285         );
     274        // v4.0.6: Fetch models from API instead of hardcoded list
     275        if (empty($api_key)) {
     276            return array();
     277        }
     278
     279        // Google Gemini has a models endpoint
     280        $response = wp_remote_get('https://generativelanguage.googleapis.com/v1beta/models?key=' . $api_key, array(
     281            'timeout' => 15
     282        ));
     283
     284        if (is_wp_error($response)) {
     285            if (defined('WP_DEBUG') && WP_DEBUG) {
     286                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     287                error_log('TAICS: Google API error: ' . $response->get_error_message());
     288            }
     289            return array();
     290        }
     291
     292        $body = wp_remote_retrieve_body($response);
     293        $data = json_decode($body, true);
     294
     295        if (!$data || isset($data['error'])) {
     296            if (defined('WP_DEBUG') && WP_DEBUG) {
     297                $error_msg = isset($data['error']['message']) ? $data['error']['message'] : 'Unknown error';
     298                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     299                error_log('TAICS: Google API error: ' . $error_msg);
     300            }
     301            return array();
     302        }
     303
     304        // Parse models from API response
     305        $models = array();
     306        if (isset($data['models']) && is_array($data['models'])) {
     307            foreach ($data['models'] as $model) {
     308                if (isset($model['name'])) {
     309                    // Extract model ID from name (e.g., "models/gemini-pro" -> "gemini-pro")
     310                    $model_id = str_replace('models/', '', $model['name']);
     311                    // Only include models that support generateContent
     312                    if (isset($model['supportedGenerationMethods']) &&
     313                        in_array('generateContent', $model['supportedGenerationMethods'])) {
     314                        $models[] = array(
     315                            'id' => sanitize_text_field($model_id),
     316                            'name' => sanitize_text_field($model_id),
     317                            'description' => isset($model['displayName']) ? sanitize_text_field($model['displayName']) : ''
     318                        );
     319                    }
     320                }
     321            }
     322        }
     323
     324        // v4.1.0: No hardcoded fallback - models only from API
     325        return $models;
    286326    }
    287327}
     
    318358
    319359    public static function get_available_models($api_key) {
    320         // v3.5.0: Validate API key before returning models
     360        // v4.0.6: Fetch models from API instead of hardcoded list
    321361        if (empty($api_key)) {
    322362            return array();
    323363        }
    324364
    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')
    356         );
     365        // DeepSeek uses OpenAI-compatible API
     366        $response = wp_remote_get('https://api.deepseek.com/v1/models', array(
     367            'headers' => array(
     368                'Authorization' => 'Bearer ' . $api_key
     369            ),
     370            'timeout' => 15
     371        ));
     372
     373        if (is_wp_error($response)) {
     374            if (defined('WP_DEBUG') && WP_DEBUG) {
     375                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     376                error_log('TAICS: DeepSeek API error: ' . $response->get_error_message());
     377            }
     378            return array();
     379        }
     380
     381        $body = wp_remote_retrieve_body($response);
     382        $data = json_decode($body, true);
     383
     384        if (!$data || isset($data['error'])) {
     385            if (defined('WP_DEBUG') && WP_DEBUG) {
     386                $error_msg = isset($data['error']['message']) ? $data['error']['message'] : 'Unknown error';
     387                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     388                error_log('TAICS: DeepSeek API error: ' . $error_msg);
     389            }
     390            return array();
     391        }
     392
     393        // Parse models from API response
     394        $models = array();
     395        if (isset($data['data']) && is_array($data['data'])) {
     396            foreach ($data['data'] as $model) {
     397                if (isset($model['id'])) {
     398                    $models[] = array(
     399                        'id' => sanitize_text_field($model['id']),
     400                        'name' => sanitize_text_field($model['id']),
     401                        'description' => ''
     402                    );
     403                }
     404            }
     405        }
     406
     407        // v4.1.0: No hardcoded fallback - models only from API
     408        return $models;
    357409    }
    358410}
     
    389441
    390442    public static function get_available_models($api_key) {
    391         // v3.5.0: Validate API key before returning models
     443        // v4.0.6: Fetch models from API instead of hardcoded list
    392444        if (empty($api_key)) {
    393445            return array();
    394446        }
    395447
    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         );
     448        // Cohere has a models endpoint
     449        $response = wp_remote_get('https://api.cohere.ai/v1/models', array(
     450            'headers' => array(
     451                'Authorization' => 'Bearer ' . $api_key
     452            ),
     453            'timeout' => 15
     454        ));
     455
     456        if (is_wp_error($response)) {
     457            if (defined('WP_DEBUG') && WP_DEBUG) {
     458                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     459                error_log('TAICS: Cohere API error: ' . $response->get_error_message());
     460            }
     461            return array();
     462        }
     463
     464        $body = wp_remote_retrieve_body($response);
     465        $data = json_decode($body, true);
     466
     467        if (!$data || isset($data['error'])) {
     468            if (defined('WP_DEBUG') && WP_DEBUG) {
     469                $error_msg = isset($data['message']) ? $data['message'] : 'Unknown error';
     470                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     471                error_log('TAICS: Cohere API error: ' . $error_msg);
     472            }
     473            return array();
     474        }
     475
     476        // Parse models from API response
     477        $models = array();
     478        if (isset($data['models']) && is_array($data['models'])) {
     479            foreach ($data['models'] as $model) {
     480                if (isset($model['name'])) {
     481                    $models[] = array(
     482                        'id' => sanitize_text_field($model['name']),
     483                        'name' => sanitize_text_field($model['name']),
     484                        'description' => isset($model['description']) ? sanitize_text_field($model['description']) : ''
     485                    );
     486                }
     487            }
     488        }
     489
     490        // v4.1.0: No hardcoded fallback - models only from API
     491        return $models;
    428492    }
    429493}
     
    461525
    462526    public static function get_available_models($api_key) {
    463         // v3.5.0: Validate API key before returning models
     527        // v4.0.6: Fetch models from API instead of hardcoded list
    464528        if (empty($api_key)) {
    465529            return array();
    466530        }
    467531
    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         );
     532        // Groq uses OpenAI-compatible API
     533        $response = wp_remote_get('https://api.groq.com/openai/v1/models', array(
     534            'headers' => array(
     535                'Authorization' => 'Bearer ' . $api_key
     536            ),
     537            'timeout' => 15
     538        ));
     539
     540        if (is_wp_error($response)) {
     541            if (defined('WP_DEBUG') && WP_DEBUG) {
     542                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     543                error_log('TAICS: Groq API error: ' . $response->get_error_message());
     544            }
     545            return array();
     546        }
     547
     548        $body = wp_remote_retrieve_body($response);
     549        $data = json_decode($body, true);
     550
     551        if (!$data || isset($data['error'])) {
     552            if (defined('WP_DEBUG') && WP_DEBUG) {
     553                $error_msg = isset($data['error']['message']) ? $data['error']['message'] : 'Unknown error';
     554                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     555                error_log('TAICS: Groq API error: ' . $error_msg);
     556            }
     557            return array();
     558        }
     559
     560        // Parse models from API response
     561        $models = array();
     562        if (isset($data['data']) && is_array($data['data'])) {
     563            foreach ($data['data'] as $model) {
     564                if (isset($model['id'])) {
     565                    $models[] = array(
     566                        'id' => sanitize_text_field($model['id']),
     567                        'name' => sanitize_text_field($model['id']),
     568                        'description' => ''
     569                    );
     570                }
     571            }
     572        }
     573
     574        // v4.1.0: No hardcoded fallback - models only from API
     575        return $models;
    501576    }
    502577}
     
    534609
    535610    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)
     611        // v4.0.6: Fetch models from API instead of hardcoded list
    537612        if (empty($api_key)) {
    538613            return array();
    539614        }
    540615
    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         );
     616        // Together AI uses OpenAI-compatible API
     617        $response = wp_remote_get('https://api.together.xyz/v1/models', array(
     618            'headers' => array(
     619                'Authorization' => 'Bearer ' . $api_key
     620            ),
     621            'timeout' => 15
     622        ));
     623
     624        if (is_wp_error($response)) {
     625            if (defined('WP_DEBUG') && WP_DEBUG) {
     626                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     627                error_log('TAICS: Together AI API error: ' . $response->get_error_message());
     628            }
     629            return array();
     630        }
     631
     632        $body = wp_remote_retrieve_body($response);
     633        $data = json_decode($body, true);
     634
     635        if (!$data || isset($data['error'])) {
     636            if (defined('WP_DEBUG') && WP_DEBUG) {
     637                $error_msg = isset($data['error']['message']) ? $data['error']['message'] : 'Unknown error';
     638                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     639                error_log('TAICS: Together AI API error: ' . $error_msg);
     640            }
     641            return array();
     642        }
     643
     644        // Parse models from API response
     645        $models = array();
     646        if (isset($data['data']) && is_array($data['data'])) {
     647            foreach ($data['data'] as $model) {
     648                if (isset($model['id'])) {
     649                    $models[] = array(
     650                        'id' => sanitize_text_field($model['id']),
     651                        'name' => sanitize_text_field($model['id']),
     652                        'description' => ''
     653                    );
     654                }
     655            }
     656        } elseif (is_array($data) && !isset($data['data'])) {
     657            // Some APIs return array directly
     658            foreach ($data as $model) {
     659                if (isset($model['id'])) {
     660                    $models[] = array(
     661                        'id' => sanitize_text_field($model['id']),
     662                        'name' => sanitize_text_field($model['id']),
     663                        'description' => ''
     664                    );
     665                }
     666            }
     667        }
     668
     669        // v4.1.0: No hardcoded fallback - models only from API
     670        return $models;
    574671    }
    575672}
     
    607704
    608705    public static function get_available_models($api_key) {
    609         // v3.5.0: Validate API key before returning models
     706        // v4.0.6: Fetch models from API instead of hardcoded list
    610707        if (empty($api_key)) {
    611708            return array();
    612709        }
    613710
    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,
     711        // Mistral uses OpenAI-compatible API
     712        $response = wp_remote_get('https://api.mistral.ai/v1/models', array(
     713            'headers' => array(
     714                'Authorization' => 'Bearer ' . $api_key
     715            ),
     716            'timeout' => 15
     717        ));
     718
     719        if (is_wp_error($response)) {
     720            if (defined('WP_DEBUG') && WP_DEBUG) {
     721                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     722                error_log('TAICS: Mistral AI API error: ' . $response->get_error_message());
     723            }
     724            return array();
     725        }
     726
     727        $body = wp_remote_retrieve_body($response);
     728        $data = json_decode($body, true);
     729
     730        if (!$data || isset($data['error'])) {
     731            if (defined('WP_DEBUG') && WP_DEBUG) {
     732                $error_msg = isset($data['error']['message']) ? $data['error']['message'] : 'Unknown error';
     733                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     734                error_log('TAICS: Mistral AI API error: ' . $error_msg);
     735            }
     736            return array();
     737        }
     738
     739        // Parse models from API response
     740        $models = array();
     741        if (isset($data['data']) && is_array($data['data'])) {
     742            foreach ($data['data'] as $model) {
     743                if (isset($model['id'])) {
     744                    $models[] = array(
     745                        'id' => sanitize_text_field($model['id']),
     746                        'name' => sanitize_text_field($model['id']),
     747                        'description' => ''
     748                    );
     749                }
     750            }
     751        }
     752
     753        // v4.1.0: No hardcoded fallback - models only from API
     754        return $models;
     755    }
     756}
     757
     758class TAICS_Provider_GLM {
     759    public static function generate($args) {
     760        $prompt = TAICS_Content_Generator::build_prompt($args);
     761       
     762        // GLM API uses different endpoint structure
     763        $request_data = array(
     764            'model' => $args['model'] ?: 'glm-4-flash',
     765            'messages' => array(
     766                array('role' => 'user', 'content' => $prompt)
     767            ),
     768            'max_tokens' => intval($args['word_count']) * 3,
     769            'temperature' => 0.7
     770        );
     771
     772        $response = wp_remote_post('https://open.bigmodel.cn/api/paas/v4/chat/completions', array(
     773            'headers' => array(
     774                'Authorization' => 'Bearer ' . $args['api_key'],
    618775                'Content-Type' => 'application/json'
    619776            ),
    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')
    646         );
     777            'body' => json_encode($request_data),
     778            'timeout' => 60
     779        ));
     780
     781        $body = wp_remote_retrieve_body($response);
     782        $data = json_decode($body, true);
     783
     784        if (!$data || isset($data['error']) || !isset($data['choices'][0]['message']['content'])) {
     785            throw new Exception(esc_html__('GLM API error', 'technodrome-ai-content-assistant'));
     786        }
     787
     788        $content = trim($data['choices'][0]['message']['content']);
     789        return $content;
     790    }
     791
     792    public static function get_available_models($api_key) {
     793        // v4.1.0: Fetch models from API instead of hardcoded list
     794        if (empty($api_key)) {
     795            return array();
     796        }
     797
     798        // GLM uses OpenAI-compatible API
     799        $response = wp_remote_get('https://open.bigmodel.cn/api/paas/v4/models', array(
     800            'headers' => array(
     801                'Authorization' => 'Bearer ' . $api_key
     802            ),
     803            'timeout' => 15
     804        ));
     805
     806        if (is_wp_error($response)) {
     807            if (defined('WP_DEBUG') && WP_DEBUG) {
     808                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     809                error_log('TAICS: GLM API error: ' . $response->get_error_message());
     810            }
     811            return array();
     812        }
     813
     814        $body = wp_remote_retrieve_body($response);
     815        $data = json_decode($body, true);
     816
     817        if (!$data || isset($data['error'])) {
     818            if (defined('WP_DEBUG') && WP_DEBUG) {
     819                $error_msg = isset($data['error']['message']) ? $data['error']['message'] : 'Unknown error';
     820                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     821                error_log('TAICS: GLM API error: ' . $error_msg);
     822            }
     823            return array();
     824        }
     825
     826        // Parse models from API response
     827        $models = array();
     828        if (isset($data['data']) && is_array($data['data'])) {
     829            foreach ($data['data'] as $model) {
     830                if (isset($model['id'])) {
     831                    $models[] = array(
     832                        'id' => sanitize_text_field($model['id']),
     833                        'name' => sanitize_text_field($model['id']),
     834                        'description' => ''
     835                    );
     836                }
     837            }
     838        }
     839
     840        // v4.1.0: No hardcoded fallback - models only from API
     841        return $models;
    647842    }
    648843}
     
    655850     */
    656851    public static function get_all_models() {
    657         // v3.5.0: Added Groq, Together AI, and Mistral AI providers
     852        // v4.1.0: Added GLM (Zhipu AI) provider, removed hardcoded lists
     853        // Models are now fetched dynamically from API
    658854        return array(
    659             'openai' => array('gpt-4o', 'gpt-4', 'gpt-3.5-turbo'),
    660             'anthropic' => array('claude-3-5-sonnet-20241022', 'claude-3-opus'),
    661             'google' => array('gemini-1.5-pro', 'gemini-1.0'),
    662             'deepseek' => array('deepseek-chat'),
    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')
     855            'openai' => array(),
     856            'anthropic' => array(),
     857            'google' => array(),
     858            'deepseek' => array(),
     859            'cohere' => array(),
     860            'groq' => array(),
     861            'together' => array(),
     862            'mistral' => array(),
     863            'glm' => array()
    667864        );
    668865    }
     
    670867    /**
    671868     * Get provider display names
    672      * 
     869     *
    673870     * @return array Array of provider => display name
    674871     */
    675872    public static function get_provider_names() {
    676         // v3.5.0: Added Groq, Together AI, and Mistral AI providers
     873        // v4.1.0: Added GLM (Zhipu AI) provider
    677874        return array(
    678875            'openai' => esc_html__('OpenAI', 'technodrome-ai-content-assistant'),
     
    684881            'together' => esc_html__('Together AI', 'technodrome-ai-content-assistant'),
    685882            'mistral' => esc_html__('Mistral AI', 'technodrome-ai-content-assistant'),
     883            'glm' => esc_html__('GLM (Zhipu AI)', 'technodrome-ai-content-assistant'),
    686884            'demo' => esc_html__('Demo Mode', 'technodrome-ai-content-assistant')
    687885        );
     
    744942                return preg_match('/^[a-zA-Z0-9]{32,}$/', $api_key);
    745943
     944            case 'glm':
     945                // v4.1.0: GLM (Zhipu AI) API keys are alphanumeric, typically 32+ chars
     946                return preg_match('/^[a-zA-Z0-9_\-]{30,}$/', $api_key);
     947
    746948            default:
    747949                return false;
     
    751953    /**
    752954     * Get default model for provider
    753      * 
     955     *
    754956     * @param string $provider Provider name
    755957     * @return string Default model name
    756958     */
    757959    public static function get_default_model($provider) {
    758         $models = self::get_all_models();
    759        
    760         if (!isset($models[$provider]) || empty($models[$provider])) {
    761             return '';
    762         }
    763        
    764         return $models[$provider][0]; // Return first model as default
    765     }
    766 }
     960        // v4.2.0: Return default models for each provider (Updated Feb 2026)
     961        $defaults = array(
     962            'openai' => 'gpt-5.2',
     963            'anthropic' => 'claude-4-opus-20260210',
     964            'google' => 'gemini-3-pro',
     965            'deepseek' => 'deepseek-v3.2',
     966            'cohere' => 'command-r7-plus',
     967            'groq' => 'llama-4-maverick-17b-it',
     968            'together' => 'meta-llama/Llama-4-Maverick-17B-Instruct',
     969            'mistral' => 'mistral-3-large',
     970            'glm' => 'glm-5-pro'
     971        );
     972       
     973        return isset($defaults[$provider]) ? $defaults[$provider] : '';
     974    }
     975}
  • technodrome-ai-content-assistant/trunk/includes/class-ajax-handler.php

    r3461963 r3462088  
    1919    private static $instance = null;
    2020   
    21     public static function init() {
     21    public static function init(): void {
    2222        add_action('wp_ajax_taics_generate_content', [__CLASS__, 'handle_generate_content']);
    2323        add_action('wp_ajax_taics_save_profile', [__CLASS__, 'ajax_save_profile']);
     
    5353    }
    5454
    55     public static function get_instance() {
     55    public static function get_instance(): self {
    5656        if (self::$instance === null) {
    5757            self::$instance = new self();
     
    6565     *
    6666     * @since 4.0.5
    67      * @param array $profile_data_raw Raw profile data from input
    68      * @return array Sanitized profile data
     67     * @param array<string, mixed> $profile_data_raw Raw profile data from input
     68     * @return array<string, mixed> Sanitized profile data
    6969     */
    7070    private static function sanitize_profile_data(array $profile_data_raw): array {
     
    111111    }
    112112       
    113     public static function handle_generate_content() {
     113    public static function handle_generate_content(): void {
    114114        check_ajax_referer('taics_ajax_nonce', 'nonce');
    115115        if (!current_user_can('edit_posts')) {
     
    367367    }
    368368   
    369     public static function ajax_save_profile() {
     369    public static function ajax_save_profile(): void {
    370370        check_ajax_referer('taics_ajax_nonce', 'nonce');
    371371        if (!current_user_can('edit_posts')) {
     
    415415     * Koristi se sa frontend AutoSave sistemom (debouncing, 500ms).
    416416     */
    417     public static function handle_autosave_profile() {
     417    public static function handle_autosave_profile(): void {
    418418        try {
    419419            // FORMDATA approach - izbegava .htaccess probleme sa JSON
     
    510510    }
    511511
    512     public static function ajax_save_profile_simple() {
     512    public static function ajax_save_profile_simple(): void {
    513513        check_ajax_referer('taics_ajax_nonce', 'nonce');
    514514        if (!current_user_can('edit_posts')) {
     
    559559    }
    560560   
    561     public static function handle_load_profile() {
     561    public static function handle_load_profile(): void {
    562562        check_ajax_referer('taics_ajax_nonce', 'nonce');
    563563        if (!current_user_can('edit_posts')) {
     
    599599    }
    600600   
    601     public static function ajax_load_profile_simple() {
     601    public static function ajax_load_profile_simple(): void {
    602602        check_ajax_referer('taics_ajax_nonce', 'nonce');
    603603        if (!current_user_can('edit_posts')) {
     
    633633    }
    634634
    635     public static function handle_save_auto_publish() {
     635    public static function handle_save_auto_publish(): void {
    636636        check_ajax_referer('taics_ajax_nonce', 'nonce');
    637637        if (!current_user_can('edit_posts')) {
     
    663663    }
    664664
    665     public static function handle_save_dark_mode() {
     665    public static function handle_save_dark_mode(): void {
    666666        check_ajax_referer('taics_ajax_nonce', 'nonce');
    667667        if (!current_user_can('edit_posts')) {
     
    697697     * Handle save show credits AJAX request (v3.8.0)
    698698     */
    699     public static function handle_save_show_credits() {
     699    public static function handle_save_show_credits(): void {
    700700        check_ajax_referer('taics_ajax_nonce', 'nonce');
    701701
     
    740740    }
    741741
    742     public static function handle_load_profiles() {
     742    public static function handle_load_profiles(): void {
    743743        check_ajax_referer('taics_ajax_nonce', 'nonce');
    744744        if (!current_user_can('edit_posts')) {
     
    758758    }
    759759
    760     public static function handle_delete_profile() {
     760    public static function handle_delete_profile(): void {
    761761        check_ajax_referer('taics_ajax_nonce', 'nonce');
    762762        if (!current_user_can('edit_posts')) {
     
    790790    }
    791791
    792     public static function handle_save_content_rules() {
     792    public static function handle_save_content_rules(): void {
    793793        check_ajax_referer('taics_ajax_nonce', 'nonce');
    794794        if (!current_user_can('edit_posts')) {
     
    819819    }
    820820
    821     public static function handle_load_content_rules() {
     821    public static function handle_load_content_rules(): void {
    822822        check_ajax_referer('taics_ajax_nonce', 'nonce');
    823823        if (!current_user_can('edit_posts')) {
     
    844844    }
    845845
    846     public static function handle_upload_photo() {
     846    public static function handle_upload_photo(): void {
    847847        check_ajax_referer('taics_ajax_nonce', 'nonce');
    848848        if (!current_user_can('upload_files')) {
     
    909909    }
    910910
    911     public static function handle_delete_photo() {
     911    public static function handle_delete_photo(): void {
    912912        check_ajax_referer('taics_ajax_nonce', 'nonce');
    913913        if (!current_user_can('delete_posts')) {
     
    996996    }
    997997
    998     public static function handle_save_settings() {
     998    public static function handle_save_settings(): void {
    999999        check_ajax_referer('taics_ajax_nonce', 'nonce');
    10001000        if (!current_user_can('manage_options')) {
     
    10051005    }
    10061006
    1007     public static function handle_load_settings() {
     1007    public static function handle_load_settings(): void {
    10081008        check_ajax_referer('taics_ajax_nonce', 'nonce');
    10091009        if (!current_user_can('manage_options')) {
     
    10161016
    10171017
    1018     public static function handle_bulk_generate() {
     1018    public static function handle_bulk_generate(): void {
    10191019        check_ajax_referer('taics_ajax_nonce', 'nonce');
    10201020        if (!current_user_can('edit_posts')) {
     
    10251025    }
    10261026
    1027     public static function handle_add_to_schedule() {
     1027    public static function handle_add_to_schedule(): void {
    10281028        check_ajax_referer('taics_ajax_nonce', 'nonce');
    10291029        if (!current_user_can('edit_posts')) {
     
    10761076
    10771077
    1078     public static function handle_get_scheduler_stats() {
     1078    public static function handle_get_scheduler_stats(): void {
    10791079        check_ajax_referer('taics_ajax_nonce', 'nonce');
    10801080        if (!current_user_can('edit_posts')) {
     
    11821182     * WordPress Security Compliant.
    11831183     */
    1184     public static function handle_delete_post() {
     1184    public static function handle_delete_post(): void {
    11851185        check_ajax_referer('taics_ajax_nonce', 'nonce');
    11861186       
     
    12231223
    12241224    /**
    1225      * Get available models for a given provider and API key
    1226      * Dynamically loads models from API instead of using hardcoded list
     1225     * Get available models for a given provider
     1226     * v4.2.0: Returns hardcoded models only (no API calls)
     1227     *
     1228     * @since 3.5.0
     1229     * @return void
    12271230     */
    1228     public static function handle_get_available_models() {
     1231    public static function handle_get_available_models(): void {
    12291232        // Verify nonce without dying - allow graceful error handling
    12301233        if (empty($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'taics_ajax_nonce')) {
     
    12391242
    12401243        $provider = isset($_POST['provider']) ? sanitize_text_field(wp_unslash($_POST['provider'])) : '';
    1241         $api_key = isset($_POST['api_key']) ? sanitize_text_field(wp_unslash($_POST['api_key'])) : '';
    1242 
    1243         if (empty($provider) || empty($api_key)) {
    1244             wp_send_json_error(esc_html__('Provider and API key are required', 'technodrome-ai-content-assistant'));
     1244
     1245        if (empty($provider)) {
     1246            wp_send_json_error(esc_html__('Provider is required', 'technodrome-ai-content-assistant'));
    12451247            return;
    12461248        }
    12471249
    12481250        try {
    1249             // v3.5.0: Use new Model Manager for dynamic model fetching with caching
     1251            // v4.2.0: Use Model Manager for hardcoded models (no API calls)
    12501252            require_once plugin_dir_path(__FILE__) . 'class-model-manager.php';
    12511253
    1252             // Get models using Model Manager (handles API calls and caching)
    1253             $models = TAICS_Model_Manager::get_models($provider, $api_key, false);
     1254            // Get hardcoded models from Model Manager
     1255            $models = TAICS_Model_Manager::get_hardcoded_models($provider);
    12541256
    12551257            if (empty($models)) {
    1256                 wp_send_json_error(esc_html__('Invalid API key or unable to retrieve models', 'technodrome-ai-content-assistant'));
     1258                wp_send_json_error(esc_html__('No models available for this provider', 'technodrome-ai-content-assistant'));
    12571259                return;
    12581260            }
     
    12621264                'provider' => $provider,
    12631265                'api_valid' => true,
    1264                 'cached' => false // Frontend can track if models were from cache
     1266                'cached' => false,
     1267                'source' => 'hardcoded' // v4.2.0: Indicate models are from hardcoded list
    12651268            ]);
    12661269
     
    12721275    /**
    12731276     * Handle clear model cache request (v3.5.1)
    1274      * Clears all WordPress transient caches for AI models
     1277     * v4.2.0: No longer needed - hardcoded models only, but kept for compatibility
    12751278     *
    12761279     * @since 3.5.1
    12771280     * @return void
    12781281     */
    1279     public static function handle_clear_model_cache() {
     1282    public static function handle_clear_model_cache(): void {
    12801283        // Security check - using footer nonce
    12811284        if (!check_ajax_referer('taics_footer_nonce', 'nonce', false)) {
     
    12901293        }
    12911294
    1292         try {
    1293             // Load Model Manager class if it exists
    1294             $model_manager_path = plugin_dir_path(__FILE__) . 'class-model-manager.php';
    1295 
    1296             if (file_exists($model_manager_path)) {
    1297                 require_once $model_manager_path;
    1298 
    1299                 // Clear all provider caches via Model Manager
    1300                 if (class_exists('TAICS_Model_Manager')) {
    1301                     TAICS_Model_Manager::clear_cache();
    1302                 }
    1303             } else {
    1304                 // Fallback: Clear transients manually
    1305                 $providers = array('openai', 'anthropic', 'google', 'deepseek', 'cohere', 'groq', 'together', 'mistral');
    1306                 foreach ($providers as $provider) {
    1307                     delete_transient('taics_models_' . $provider);
    1308                 }
    1309             }
    1310 
    1311             // Log the action
    1312             if (defined('WP_DEBUG') && WP_DEBUG) {
    1313                 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
    1314                 error_log('TAICS: Model cache cleared by user ID ' . esc_html(get_current_user_id()));
    1315             }
    1316 
    1317             wp_send_json_success(array(
    1318                 'message' => 'Model cache cleared successfully',
    1319                 'timestamp' => current_time('mysql')
    1320             ));
    1321 
    1322         } catch (Exception $e) {
    1323             if (defined('WP_DEBUG') && WP_DEBUG) {
    1324                 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
    1325                 error_log('TAICS: Error clearing model cache: ' . esc_html($e->getMessage()));
    1326             }
    1327 
    1328             wp_send_json_error(array(
    1329                 'message' => 'Failed to clear cache: ' . esc_html($e->getMessage())
    1330             ));
    1331         }
     1295        // v4.2.0: No cache to clear - hardcoded models only
     1296        // Just return success for backward compatibility
     1297        wp_send_json_success(array(
     1298            'message' => 'Model cache cleared (hardcoded models - no cache needed)',
     1299            'timestamp' => current_time('mysql'),
     1300            'note' => 'v4.2.0: Models are now hardcoded only'
     1301        ));
    13321302    }
    13331303
     
    13351305     * Handle reset license to FREE AJAX request (v3.5.4)
    13361306     */
    1337     public static function handle_reset_license_to_free() {
     1307    public static function handle_reset_license_to_free(): void {
    13381308        check_ajax_referer('taics_ajax_nonce', 'nonce');
    13391309
     
    13811351     * Saves photos to user_meta for photo-positions.js
    13821352     */
    1383     public static function handle_save_photos() {
     1353    public static function handle_save_photos(): void {
    13841354        check_ajax_referer('taics_ajax_nonce', 'nonce');
    13851355       
     
    14361406     * Loads photos from user_meta for photo-positions.js
    14371407     */
    1438     public static function handle_load_photos() {
     1408    public static function handle_load_photos(): void {
    14391409        check_ajax_referer('taics_ajax_nonce', 'nonce');
    14401410       
     
    14691439     * Saves custom profile names to user_meta for profile-name-sync.js
    14701440     */
    1471     public static function handle_save_profile_name() {
     1441    public static function handle_save_profile_name(): void {
    14721442        check_ajax_referer('taics_ajax_nonce', 'nonce');
    14731443
  • technodrome-ai-content-assistant/trunk/includes/class-model-manager.php

    r3423160 r3462088  
    11<?php
    22/**
    3  * TAICS Model Manager - Dynamic Model Fetching and Caching
     3 * TAICS Model Manager - Hardcoded Model Lists
    44 *
    5  * Handles dynamic fetching of AI models from providers with 24-hour caching
     5 * Provides hardcoded lists of AI models for each provider
     6 * No online API calls - models are maintained manually
    67 *
    78 * @package TAICS_Content_Assistant
    8  * @version 3.5.0
     9 * @version 4.2.0
    910 */
    1011
     
    1516class TAICS_Model_Manager {
    1617
    17     const CACHE_KEY_PREFIX = 'taics_models_';
    18     const CACHE_DURATION = 86400; // 24 hours in seconds
    19 
    20     /**
    21      * Get available models for a provider
    22      * Uses cache if available, otherwise fetches from API
    23      *
    24      * @param string $provider Provider name (openai, anthropic, google, deepseek, cohere)
    25      * @param string $api_key API key for the provider
    26      * @param bool $force_refresh Force fetch from API, bypass cache
     18    /**
     19     * Get available models for a provider (hardcoded lists)
     20     *
     21     * @param string $provider Provider name
     22     * @param string $api_key API key (not used, kept for compatibility)
     23     * @param bool $force_refresh Force refresh (not used, kept for compatibility)
    2724     * @return array List of available models
    2825     */
    29     public static function get_models($provider, $api_key, $force_refresh = false) {
    30         if (empty($provider) || empty($api_key)) {
    31             return array();
    32         }
    33 
    34         // Generate cache key
    35         $cache_key = self::CACHE_KEY_PREFIX . sanitize_text_field($provider);
    36 
    37         // Try to get from cache if not forcing refresh
    38         if (!$force_refresh) {
    39             $cached_models = get_transient($cache_key);
    40             if ($cached_models !== false) {
    41                 if (defined('WP_DEBUG') && WP_DEBUG) {
    42                     // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
    43                     error_log('TAICS: Models loaded from cache for provider: ' . esc_html($provider));
    44                 }
    45                 return $cached_models;
    46             }
    47         }
    48 
    49         // If we get here, need to fetch from provider
    50         $models = self::fetch_from_provider($provider, $api_key);
    51 
    52         // Cache the results if we got models
    53         if (!empty($models)) {
    54             set_transient($cache_key, $models, self::CACHE_DURATION);
    55         }
    56 
    57         return $models;
    58     }
    59 
    60     /**
    61      * Fetch models directly from provider API
     26    public static function get_models($provider, $api_key = '', $force_refresh = false) {
     27        return self::get_hardcoded_models($provider);
     28    }
     29
     30    /**
     31     * Get hardcoded models for a provider
    6232     *
    6333     * @param string $provider Provider name
    64      * @param string $api_key API key for the provider
    65      * @return array List of models from API
    66      */
    67     private static function fetch_from_provider($provider, $api_key) {
    68         require_once plugin_dir_path(__FILE__) . 'class-ai-providers.php';
    69 
     34     * @return array List of models
     35     */
     36    public static function get_hardcoded_models($provider) {
    7037        $models = array();
    7138
    7239        switch ($provider) {
     40            case 'demo':
     41                $models = array(
     42                    array('id' => 'enhanced-demo-v2', 'name' => 'Enhanced Demo v2.0')
     43                );
     44                break;
     45
    7346            case 'openai':
    74                 $models = TAICS_Provider_Openai::get_available_models($api_key);
     47                $models = array(
     48                    array('id' => 'gpt-5.2', 'name' => 'GPT-5.2'),
     49                    array('id' => 'gpt-5.2-pro', 'name' => 'GPT-5.2 Pro'),
     50                    array('id' => 'gpt-5.2-mini', 'name' => 'GPT-5.2 Mini'),
     51                    array('id' => 'gpt-5.1', 'name' => 'GPT-5.1'),
     52                    array('id' => 'gpt-5.1-pro', 'name' => 'GPT-5.1 Pro'),
     53                    array('id' => 'gpt-5.1-mini', 'name' => 'GPT-5.1 Mini'),
     54                    array('id' => 'o3-pro', 'name' => 'o3-pro (Reasoning)'),
     55                    array('id' => 'o3-mini', 'name' => 'o3-mini (Reasoning)'),
     56                    array('id' => 'o1-pro', 'name' => 'o1-pro (Reasoning)'),
     57                    array('id' => 'gpt-4o-mini', 'name' => 'GPT-4o Mini'),
     58                );
    7559                break;
    7660
    7761            case 'anthropic':
    78                 $models = TAICS_Provider_Anthropic::get_available_models($api_key);
     62                $models = array(
     63                    array('id' => 'claude-4-opus-20260210', 'name' => 'Claude 4 Opus (Feb 2026)'),
     64                    array('id' => 'claude-4-sonnet-20260120', 'name' => 'Claude 4 Sonnet (Jan 2026)'),
     65                    array('id' => 'claude-4-haiku-20260215', 'name' => 'Claude 4 Haiku (Feb 2026)'),
     66                    array('id' => 'claude-3.7-sonnet', 'name' => 'Claude 3.7 Sonnet'),
     67                    array('id' => 'claude-3.5-sonnet-latest', 'name' => 'Claude 3.5 Sonnet Latest'),
     68                    array('id' => 'claude-3.5-haiku-latest', 'name' => 'Claude 3.5 Haiku Latest'),
     69                    array('id' => 'claude-3-opus', 'name' => 'Claude 3 Opus'),
     70                    array('id' => 'claude-3-sonnet', 'name' => 'Claude 3 Sonnet'),
     71                    array('id' => 'claude-3-haiku', 'name' => 'Claude 3 Haiku'),
     72                );
    7973                break;
    8074
    8175            case 'google':
    82                 $models = TAICS_Provider_Google::get_available_models($api_key);
     76                $models = array(
     77                    array('id' => 'gemini-3-pro', 'name' => 'Gemini 3 Pro'),
     78                    array('id' => 'gemini-3-flash', 'name' => 'Gemini 3 Flash'),
     79                    array('id' => 'gemini-3-flash-lite', 'name' => 'Gemini 3 Flash-Lite'),
     80                    array('id' => 'gemini-2.5-pro', 'name' => 'Gemini 2.5 Pro'),
     81                    array('id' => 'gemini-2.5-flash', 'name' => 'Gemini 2.5 Flash'),
     82                    array('id' => 'gemini-2.0-pro', 'name' => 'Gemini 2.0 Pro'),
     83                    array('id' => 'gemini-2.0-flash', 'name' => 'Gemini 2.0 Flash'),
     84                    array('id' => 'gemini-2.0-flash-lite', 'name' => 'Gemini 2.0 Flash-Lite'),
     85                    array('id' => 'gemini-1.5-pro', 'name' => 'Gemini 1.5 Pro'),
     86                    array('id' => 'gemini-1.5-flash', 'name' => 'Gemini 1.5 Flash'),
     87                );
    8388                break;
    8489
    8590            case 'deepseek':
    86                 $models = TAICS_Provider_Deepseek::get_available_models($api_key);
     91                $models = array(
     92                    array('id' => 'deepseek-v3.2', 'name' => 'DeepSeek V3.2'),
     93                    array('id' => 'deepseek-v3.1', 'name' => 'DeepSeek V3.1'),
     94                    array('id' => 'deepseek-v3', 'name' => 'DeepSeek V3'),
     95                    array('id' => 'deepseek-r1', 'name' => 'DeepSeek R1 (Reasoning)'),
     96                    array('id' => 'deepseek-reasoner', 'name' => 'DeepSeek Reasoner'),
     97                    array('id' => 'deepseek-chat', 'name' => 'DeepSeek Chat'),
     98                );
    8799                break;
    88100
    89101            case 'cohere':
    90                 $models = TAICS_Provider_Cohere::get_available_models($api_key);
    91                 break;
    92 
    93             // v3.5.0: New providers
     102                $models = array(
     103                    array('id' => 'command-r7-plus', 'name' => 'Command R7 Plus'),
     104                    array('id' => 'command-r7', 'name' => 'Command R7'),
     105                    array('id' => 'command-r-08-2025', 'name' => 'Command R 08-2025'),
     106                    array('id' => 'command-r-plus-08-2025', 'name' => 'Command R+ 08-2025'),
     107                    array('id' => 'command-r', 'name' => 'Command R'),
     108                    array('id' => 'command-light', 'name' => 'Command Light'),
     109                    array('id' => 'command-nightly', 'name' => 'Command Nightly'),
     110                );
     111                break;
     112
    94113            case 'groq':
    95                 $models = TAICS_Provider_Groq::get_available_models($api_key);
     114                $models = array(
     115                    array('id' => 'llama-4-maverick-17b-it', 'name' => 'Llama 4 Maverick 17B IT'),
     116                    array('id' => 'llama-4-scout-17b-it', 'name' => 'Llama 4 Scout 17B IT'),
     117                    array('id' => 'llama-3.3-70b-specdec', 'name' => 'Llama 3.3 70B SpecDec'),
     118                    array('id' => 'llama-3.3-70b-versatile', 'name' => 'Llama 3.3 70B Versatile'),
     119                    array('id' => 'llama-3.1-8b-instant', 'name' => 'Llama 3.1 8B Instant'),
     120                    array('id' => 'llama3-70b-8192-tool-use', 'name' => 'Llama 3 70B Tool Use'),
     121                    array('id' => 'llama3-8b-8192-tool-use', 'name' => 'Llama 3 8B Tool Use'),
     122                    array('id' => 'gemma2-9b-it', 'name' => 'Gemma 2 9B IT'),
     123                    array('id' => 'qwen-2.5-72b', 'name' => 'Qwen 2.5 72B'),
     124                    array('id' => 'mixtral-8x7b-32768', 'name' => 'Mixtral 8x7B'),
     125                );
    96126                break;
    97127
    98128            case 'together':
    99                 $models = TAICS_Provider_Together::get_available_models($api_key);
     129                $models = array(
     130                    array('id' => 'meta-llama/Llama-4-Maverick-17B-Instruct', 'name' => 'Llama 4 Maverick 17B Instruct'),
     131                    array('id' => 'meta-llama/Llama-4-Scout-17B-Instruct', 'name' => 'Llama 4 Scout 17B Instruct'),
     132                    array('id' => 'meta-llama/Llama-3.3-70B-Instruct-Turbo', 'name' => 'Llama 3.3 70B Turbo'),
     133                    array('id' => 'nvidia/Llama-3.1-Nemotron-70B-Instruct', 'name' => 'Llama 3.1 Nemotron 70B'),
     134                    array('id' => 'deepseek-ai/DeepSeek-V3', 'name' => 'DeepSeek V3'),
     135                    array('id' => 'Qwen/Qwen3-72B-Instruct', 'name' => 'Qwen 3 72B'),
     136                    array('id' => 'Qwen/Qwen2.5-7B-Instruct', 'name' => 'Qwen 2.5 7B'),
     137                    array('id' => 'mistralai/Mixtral-8x22B-Instruct-v0.1', 'name' => 'Mixtral 8x22B'),
     138                    array('id' => 'mistralai/Mistral-7B-Instruct-v0.3', 'name' => 'Mistral 7B v0.3'),
     139                    array('id' => 'databricks/dbrx-instruct', 'name' => 'DBRX Instruct'),
     140                );
    100141                break;
    101142
    102143            case 'mistral':
    103                 $models = TAICS_Provider_Mistral::get_available_models($api_key);
     144                $models = array(
     145                    array('id' => 'mistral-3-large', 'name' => 'Mistral 3 Large'),
     146                    array('id' => 'mistral-3-medium', 'name' => 'Mistral 3 Medium'),
     147                    array('id' => 'mistral-3-small', 'name' => 'Mistral 3 Small'),
     148                    array('id' => 'mistral-large-latest', 'name' => 'Mistral Large Latest'),
     149                    array('id' => 'mistral-large-2411', 'name' => 'Mistral Large 2411'),
     150                    array('id' => 'codestral-v3', 'name' => 'Codestral V3'),
     151                    array('id' => 'ministral-8b-latest', 'name' => 'Ministral 8B'),
     152                    array('id' => 'ministral-3b-latest', 'name' => 'Ministral 3B'),
     153                    array('id' => 'open-mistral-nemo', 'name' => 'Open Mistral Nemo'),
     154                    array('id' => 'mistral-small-latest', 'name' => 'Mistral Small Latest'),
     155                );
     156                break;
     157
     158            case 'glm':
     159                $models = array(
     160                    array('id' => 'glm-5-pro', 'name' => 'GLM-5 Pro'),
     161                    array('id' => 'glm-5-flash', 'name' => 'GLM-5 Flash'),
     162                    array('id' => 'glm-4.7-plus', 'name' => 'GLM-4.7 Plus'),
     163                    array('id' => 'glm-4-plus', 'name' => 'GLM-4 Plus'),
     164                    array('id' => 'glm-4-air', 'name' => 'GLM-4 Air'),
     165                    array('id' => 'glm-4-airx', 'name' => 'GLM-4 AirX'),
     166                    array('id' => 'glm-4-flash', 'name' => 'GLM-4 Flash'),
     167                    array('id' => 'glm-4-flashx', 'name' => 'GLM-4 FlashX'),
     168                    array('id' => 'glm-4-long', 'name' => 'GLM-4 Long'),
     169                    array('id' => 'glm-z1-air', 'name' => 'GLM-Z1 Air (Reasoning)'),
     170                );
    104171                break;
    105172
     
    108175        }
    109176
     177        return $models;
     178    }
     179
     180    /**
     181     * Clear cache (kept for compatibility, does nothing now)
     182     *
     183     * @param string $provider Provider name (null for all)
     184     */
     185    public static function clear_cache($provider = null) {
     186        // No longer using cache - hardcoded models only
    110187        if (defined('WP_DEBUG') && WP_DEBUG) {
    111188            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
    112             error_log('TAICS: Models fetched from provider API for: ' . esc_html($provider) . ' (Count: ' . count($models) . ')');
     189            error_log('TAICS: clear_cache called but not using cache anymore (hardcoded models)');
    113190        }
    114 
    115         return $models;
    116     }
    117 
    118     /**
    119      * Clear cache for a provider
    120      *
    121      * @param string $provider Provider name (null for all)
    122      */
    123     public static function clear_cache($provider = null) {
    124         if ($provider === null) {
    125             // Clear all provider caches
    126             $providers = array('openai', 'anthropic', 'google', 'deepseek', 'cohere');
    127             foreach ($providers as $prov) {
    128                 delete_transient(self::CACHE_KEY_PREFIX . $prov);
    129             }
    130             if (defined('WP_DEBUG') && WP_DEBUG) {
    131                 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
    132                 error_log('TAICS: All model caches cleared');
    133             }
    134         } else {
    135             delete_transient(self::CACHE_KEY_PREFIX . sanitize_text_field($provider));
    136             if (defined('WP_DEBUG') && WP_DEBUG) {
    137                 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
    138                 error_log('TAICS: Cache cleared for provider: ' . esc_html($provider));
    139             }
     191    }
     192
     193    /**
     194     * Get cache expiration time (kept for compatibility, always returns false)
     195     *
     196     * @param string $provider Provider name
     197     * @return int|false Always returns false (no cache)
     198     */
     199    public static function get_cache_expiration($provider) {
     200        return false;
     201    }
     202
     203    /**
     204     * Get all models for all providers
     205     *
     206     * @return array All models grouped by provider
     207     */
     208    public static function get_all_models() {
     209        $providers = array('demo', 'openai', 'anthropic', 'google', 'deepseek', 'cohere', 'groq', 'together', 'mistral', 'glm');
     210        $all_models = array();
     211
     212        foreach ($providers as $provider) {
     213            $all_models[$provider] = self::get_hardcoded_models($provider);
    140214        }
    141     }
    142 
    143     /**
    144      * Get cache expiration time for a provider
    145      *
    146      * @param string $provider Provider name
    147      * @return int|false Unix timestamp when cache expires, or false if no cache
    148      */
    149     public static function get_cache_expiration($provider) {
    150         $cache_key = self::CACHE_KEY_PREFIX . sanitize_text_field($provider);
    151 
    152         // Check if transient exists
    153         if (get_transient($cache_key) === false) {
    154             return false;
    155         }
    156 
    157         // Get transient expiration time
    158         global $wpdb;
    159         // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    160         $expiration = $wpdb->get_var(
    161             $wpdb->prepare(
    162                 "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
    163                 '_transient_timeout_' . $cache_key
    164             )
    165         );
    166 
    167         return $expiration ? intval($expiration) : false;
     215
     216        return $all_models;
    168217    }
    169218}
  • technodrome-ai-content-assistant/trunk/readme.txt

    r3461963 r3462088  
    55Tested up to: 6.9
    66Requires PHP: 8.0
    7 Stable tag: 4.0.5
     7Stable tag: 4.0.6
    88License: GPL v2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    4141== Changelog ==
    4242
    43 = 4.0.5 (2026-02-15) =
     43= 4.0.6 (2026-02-15) =
     44*   **NEW**: Dynamic AI Models - DeepSeek, Cohere, Groq, Together AI, Mistral now fetch models from API (always up-to-date)
     45*   **IMPROVED**: Numbered fields for clarity - 1. AI Provider, 2. API Key, 3. AI Model
     46*   **CODE QUALITY**: Added PHP 8.0+ Type Hints - 29 AJAX methods now have proper return types (`:void`)
    4447*   **CODE QUALITY**: AJAX Handler refactoring - Created centralized sanitize_profile_data() method (DRY principle)
    4548*   **CODE QUALITY**: Removed ~100 lines of duplicate code by reusing centralized method
    4649*   **IMPROVED**: Replaced fake random view counts with "N/A" (WordPress has no built-in view counter)
     50*   **NEW**: Added WooCommerce HPOS compatibility (WooCommerce 7.0 - 9.0)
    4751*   **FIX**: Structure Name no longer resets to "PROFILE" when switching profiles
    4852*   **FIX**: Custom profile names persist correctly without being overwritten
  • technodrome-ai-content-assistant/trunk/technodrome-ai-content-assistant.php

    r3461963 r3462088  
    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: 4.0.5
     6 * Version: 4.0.6
    77 * Author: Technodrome Team
    88 * Author URI: https://technodrome.org
     
    3030
    3131// Plugin constants
    32 define('TAICS_VERSION', '4.0.5');
     32define('TAICS_VERSION', '4.0.6');
    3333define('TAICS_PLUGIN_FILE', __FILE__);
    3434define('TAICS_PLUGIN_DIR', plugin_dir_path(__FILE__));
     
    549549    /**
    550550     * Get AI models
     551     * v4.2.0: Hardcoded model lists only - no API calls
     552     * Updated Feb 2026 with latest models
    551553     */
    552554    private function get_ai_models() {
     
    555557                'name' => 'OpenAI',
    556558                'models' => array(
    557                     'gpt-4' => 'GPT-4',
    558                     'gpt-4-turbo' => 'GPT-4 Turbo',
    559                     'gpt-3.5-turbo' => 'GPT-3.5 Turbo'
     559                    'gpt-5.2' => 'GPT-5.2',
     560                    'gpt-5.2-pro' => 'GPT-5.2 Pro',
     561                    'gpt-5.2-mini' => 'GPT-5.2 Mini',
     562                    'gpt-5.1' => 'GPT-5.1',
     563                    'gpt-5.1-pro' => 'GPT-5.1 Pro',
     564                    'gpt-5.1-mini' => 'GPT-5.1 Mini',
     565                    'o3-pro' => 'O3 Pro',
     566                    'o3-mini' => 'O3 Mini',
     567                    'o1-pro' => 'O1 Pro',
     568                    'gpt-4o-mini' => 'GPT-4o Mini'
    560569                )
    561570            ),
     
    563572                'name' => 'Anthropic',
    564573                'models' => array(
     574                    'claude-4-opus-20260210' => 'Claude 4 Opus (Feb 2026)',
     575                    'claude-4-sonnet-20260120' => 'Claude 4 Sonnet (Jan 2026)',
     576                    'claude-4-haiku-20260215' => 'Claude 4 Haiku (Feb 2026)',
     577                    'claude-3.7-sonnet' => 'Claude 3.7 Sonnet',
     578                    'claude-3.5-sonnet-latest' => 'Claude 3.5 Sonnet Latest',
     579                    'claude-3.5-haiku-latest' => 'Claude 3.5 Haiku Latest',
    565580                    'claude-3-opus' => 'Claude 3 Opus',
    566581                    'claude-3-sonnet' => 'Claude 3 Sonnet',
     
    571586                'name' => 'Google',
    572587                'models' => array(
    573                     'gemini-pro' => 'Gemini Pro',
    574                     'gemini-pro-vision' => 'Gemini Pro Vision'
     588                    'gemini-3-pro' => 'Gemini 3 Pro',
     589                    'gemini-3-flash' => 'Gemini 3 Flash',
     590                    'gemini-3-flash-lite' => 'Gemini 3 Flash-Lite',
     591                    'gemini-2.5-pro' => 'Gemini 2.5 Pro',
     592                    'gemini-2.5-flash' => 'Gemini 2.5 Flash',
     593                    'gemini-2.0-pro' => 'Gemini 2.0 Pro',
     594                    'gemini-2.0-flash' => 'Gemini 2.0 Flash',
     595                    'gemini-2.0-flash-lite' => 'Gemini 2.0 Flash-Lite',
     596                    'gemini-1.5-pro' => 'Gemini 1.5 Pro',
     597                    'gemini-1.5-flash' => 'Gemini 1.5 Flash'
    575598                )
    576599            ),
     
    578601                'name' => 'DeepSeek',
    579602                'models' => array(
    580                     'deepseek-coder' => 'DeepSeek Coder',
     603                    'deepseek-v3.2' => 'DeepSeek V3.2',
     604                    'deepseek-v3.1' => 'DeepSeek V3.1',
     605                    'deepseek-v3' => 'DeepSeek V3',
     606                    'deepseek-r1' => 'DeepSeek R1',
     607                    'deepseek-reasoner' => 'DeepSeek Reasoner',
    581608                    'deepseek-chat' => 'DeepSeek Chat'
    582609                )
     
    585612                'name' => 'Cohere',
    586613                'models' => array(
    587                     'command' => 'Command',
    588                     'command-light' => 'Command Light'
     614                    'command-r7-plus' => 'Command R7 Plus',
     615                    'command-r7' => 'Command R7',
     616                    'command-r-08-2025' => 'Command R 08-2025',
     617                    'command-r-plus-08-2025' => 'Command R+ 08-2025',
     618                    'command-r' => 'Command R',
     619                    'command-light' => 'Command Light',
     620                    'command-nightly' => 'Command Nightly'
     621                )
     622            ),
     623            'groq' => array(
     624                'name' => 'Groq',
     625                'models' => array(
     626                    'llama-4-maverick-17b-it' => 'Llama 4 Maverick 17B IT',
     627                    'llama-4-scout-17b-it' => 'Llama 4 Scout 17B IT',
     628                    'llama-3.3-70b-specdec' => 'Llama 3.3 70B SpecDec',
     629                    'llama-3.3-70b-versatile' => 'Llama 3.3 70B Versatile',
     630                    'llama-3.1-8b-instant' => 'Llama 3.1 8B Instant',
     631                    'llama3-70b-8192-tool-use' => 'Llama 3 70B Tool Use',
     632                    'llama3-8b-8192-tool-use' => 'Llama 3 8B Tool Use',
     633                    'gemma2-9b-it' => 'Gemma 2 9B IT',
     634                    'qwen-2.5-72b' => 'Qwen 2.5 72B',
     635                    'mixtral-8x7b-32768' => 'Mixtral 8x7B'
     636                )
     637            ),
     638            'together' => array(
     639                'name' => 'Together AI',
     640                'models' => array(
     641                    'meta-llama/Llama-4-Maverick-17B-Instruct' => 'Llama 4 Maverick 17B Instruct',
     642                    'meta-llama/Llama-4-Scout-17B-Instruct' => 'Llama 4 Scout 17B Instruct',
     643                    'meta-llama/Llama-3.3-70B-Instruct-Turbo' => 'Llama 3.3 70B Turbo',
     644                    'nvidia/Llama-3.1-Nemotron-70B-Instruct' => 'Llama 3.1 Nemotron 70B',
     645                    'deepseek-ai/DeepSeek-V3' => 'DeepSeek V3',
     646                    'Qwen/Qwen3-72B-Instruct' => 'Qwen 3 72B',
     647                    'Qwen/Qwen2.5-7B-Instruct' => 'Qwen 2.5 7B',
     648                    'mistralai/Mixtral-8x22B-Instruct-v0.1' => 'Mixtral 8x22B',
     649                    'mistralai/Mistral-7B-Instruct-v0.3' => 'Mistral 7B v0.3',
     650                    'databricks/dbrx-instruct' => 'DBRX Instruct'
     651                )
     652            ),
     653            'mistral' => array(
     654                'name' => 'Mistral AI',
     655                'models' => array(
     656                    'mistral-3-large' => 'Mistral 3 Large',
     657                    'mistral-3-medium' => 'Mistral 3 Medium',
     658                    'mistral-3-small' => 'Mistral 3 Small',
     659                    'mistral-large-latest' => 'Mistral Large Latest',
     660                    'mistral-large-2411' => 'Mistral Large 2411',
     661                    'codestral-v3' => 'Codestral V3',
     662                    'ministral-8b-latest' => 'Ministral 8B',
     663                    'ministral-3b-latest' => 'Ministral 3B',
     664                    'open-mistral-nemo' => 'Open Mistral Nemo',
     665                    'mistral-small-latest' => 'Mistral Small Latest'
     666                )
     667            ),
     668            'glm' => array(
     669                'name' => 'GLM (Zhipu AI)',
     670                'models' => array(
     671                    'glm-5-pro' => 'GLM-5 Pro',
     672                    'glm-5-flash' => 'GLM-5 Flash',
     673                    'glm-4.7-plus' => 'GLM-4.7 Plus',
     674                    'glm-4-plus' => 'GLM-4 Plus',
     675                    'glm-4-air' => 'GLM-4 Air',
     676                    'glm-4-airx' => 'GLM-4 AirX',
     677                    'glm-4-flash' => 'GLM-4 Flash',
     678                    'glm-4-flashx' => 'GLM-4 FlashX',
     679                    'glm-4-long' => 'GLM-4 Long',
     680                    'glm-z1-air' => 'GLM-Z1 Air'
    589681                )
    590682            )
Note: See TracChangeset for help on using the changeset viewer.