Changeset 3462088
- Timestamp:
- 02/15/2026 11:13:48 PM (6 weeks ago)
- Location:
- technodrome-ai-content-assistant/trunk
- Files:
-
- 8 edited
-
changelog.txt (modified) (1 diff)
-
dashboard/modules/generate-tab/generate.php (modified) (5 diffs)
-
features/generate-tab/ai-provider-select.js (modified) (30 diffs)
-
includes/class-ai-providers.php (modified) (12 diffs)
-
includes/class-ajax-handler.php (modified) (33 diffs)
-
includes/class-model-manager.php (modified) (3 diffs)
-
readme.txt (modified) (2 diffs)
-
technodrome-ai-content-assistant.php (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
technodrome-ai-content-assistant/trunk/changelog.txt
r3461963 r3462088 1 1 # 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 --- 2 40 3 41 ## Version 4.0.5 - 2026-02-15 -
technodrome-ai-content-assistant/trunk/dashboard/modules/generate-tab/generate.php
r3434643 r3462088 74 74 } 75 75 76 // v4.2.0: Hardcoded model lists only - no API calls 77 // Updated Feb 2026 with latest models 76 78 if (!isset($taics_ai_models)) { 77 // v3.5.1: Updated fallback models to Dec 2025 latest versions78 79 $taics_ai_models = array( 79 80 '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') 88 90 ); 89 91 } … … 303 305 <label for="taics-ai-provider"> 304 306 🚀 305 <?php esc_html_e(' AI Provider', 'technodrome-ai-content-assistant'); ?>307 <?php esc_html_e('1. AI Provider', 'technodrome-ai-content-assistant'); ?> 306 308 </label> 307 309 <select id="taics-ai-provider" class="taics-field"> … … 316 318 <option value="together"><?php esc_html_e('Together AI (Open Models)', 'technodrome-ai-content-assistant'); ?></option> 317 319 <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> 318 322 </select> 319 323 </div> … … 322 326 <label for="taics-ai-model"> 323 327 ⚙️ 324 <?php esc_html_e(' AI Model', 'technodrome-ai-content-assistant'); ?>328 <?php esc_html_e('3. AI Model', 'technodrome-ai-content-assistant'); ?> 325 329 </label> 326 330 <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> 328 333 <?php if (!empty($taics_ai_models)): ?> 329 334 <?php foreach ($taics_ai_models as $taics_provider => $taics_models): ?> … … 346 351 <label for="taics-api-key"> 347 352 🔑 348 <?php esc_html_e(' API Key', 'technodrome-ai-content-assistant'); ?>353 <?php esc_html_e('2. API Key', 'technodrome-ai-content-assistant'); ?> 349 354 </label> 350 355 <div class="taics-api-key-wrapper"> -
technodrome-ai-content-assistant/trunk/features/generate-tab/ai-provider-select.js
r3446048 r3462088 1 1 /** 2 * TAICS AI Provider Select Handler - ENHANCED Profile Sync2 * TAICS AI Provider Select Handler - Hardcoded Models Only 3 3 * 4 4 * @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 6 9 */ 7 10 … … 24 27 this.isInitializing = true; 25 28 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) 28 30 this.loadModels(); 29 31 30 32 // Check if already initialized - don't reset provider on tab switch 31 33 if (this.currentProvider && this.currentProvider !== 'google' && $('#taics-ai-provider').length) { 32 // v3.5.1:Update model options with newly loaded models34 // Update model options with newly loaded models 33 35 this.updateModelOptions(); 34 36 this.bindEvents(); // Just rebind events on tab switch … … 37 39 } 38 40 39 // --- START MODIFICATION ---40 // Removed loadSavedSettings() - now relies on profile data from TAICS_Profile_Buttons41 41 // Set default provider and update UI accordingly 42 42 this.currentProvider = 'google'; // Default provider … … 44 44 this.updateModelOptions(); // Update models for the default provider 45 45 this.updateProviderDisplay(); // Update API key section visibility 46 // --- END MODIFICATION ---47 46 this.bindEvents(); 48 47 this.isInitializing = false; … … 50 49 51 50 loadModels: function() { 51 // v4.2.0: All models are hardcoded - no API calls 52 // Updated February 2026 with latest models 52 53 this.models = { 53 54 demo: [ … … 55 56 ], 56 57 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"} 68 68 ], 69 69 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"} 75 79 ], 76 80 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"} 85 91 ], 86 92 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"} 92 99 ], 93 100 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"} 99 108 ], 100 109 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"} 107 120 ], 108 121 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"} 127 132 ], 128 133 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"} 135 156 ] 136 157 }; … … 155 176 $('#taics-toggle-api-key').on('click.taics-toggle', this.togglePasswordVisibility.bind(this)); 156 177 157 // --- START MODIFICATION ---158 178 // Listen for profile load events to sync AI settings 159 179 $(document).on('taics_profile_loaded.ai-provider', (_, profileNumber) => { … … 166 186 }, 50); 167 187 }); 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 176 192 // Removed loadSavedSettings() - no longer needed as settings come from profile 177 193 loadSavedSettings: function() { 178 194 // This function is no longer active. All settings should come from profile data. 179 195 }, 180 // --- END MODIFICATION ---181 196 182 197 handleProviderChange: function(e) { … … 189 204 this.updateProviderDisplay(); 190 205 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 195 207 196 208 // Update AI Image capability when provider changes (v3.3.0) … … 198 210 this.updateAIImageCapability(selectedModel); 199 211 200 if (!this.isInitializing) {201 // saveCurrentSettings is now only for local state, not localStorage202 // Profile saving handles persistence to DB203 // this.saveCurrentSettings();204 }205 206 212 $(document).trigger('taics_provider_changed', [this.currentProvider]); 207 213 $(document).trigger('taics_api_status_check'); … … 215 221 this.updateAIImageCapability(selectedModel); 216 222 217 if (!this.isInitializing) {218 // saveCurrentSettings is now only for local state, not localStorage219 // Profile saving handles persistence to DB220 // this.saveCurrentSettings();221 }222 223 223 $(document).trigger('taics_model_changed', [selectedModel]); 224 224 $(document).trigger('taics_api_status_check'); … … 235 235 clearTimeout(this.saveTimeout); 236 236 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 244 238 $(document).trigger('taics_api_status_check'); 245 239 }, 1000); … … 257 251 258 252 showDemoMode: function() { 259 // Sakrij polja vezana za API ključ253 // Hide API key related fields 260 254 $('.taics-api-field').hide(); 261 255 $('#taics-api-key-section').hide(); 262 256 $('#taics-get-api-section').hide(); 263 257 264 // Prikaži demo info ako postoji258 // Show demo info if exists 265 259 $('.taics-demo-info').show(); 266 260 267 // Ažuriraj info provajdera zademo261 // Update provider info for demo 268 262 this.updateProviderInfo(); 269 263 … … 271 265 272 266 showApiMode: function() { 273 // Prikaži polja vezana za API ključ267 // Show API key related fields 274 268 $('.taics-api-field').show(); 275 269 $('#taics-api-key-section').show(); 276 270 $('#taics-get-api-section').show(); 277 271 278 // Sakrijdemo info272 // Hide demo info 279 273 $('.taics-demo-info').hide(); 280 274 … … 370 364 name: 'Mistral AI', 371 365 link: 'https://console.mistral.ai/keys' 366 }, 367 glm: { 368 name: 'GLM (Zhipu AI)', 369 link: 'https://open.bigmodel.cn' 372 370 } 373 371 }; … … 419 417 }, 420 418 421 /**422 * Fetch available models dynamically from API423 * Replaces hardcoded model lists with real API responses424 */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: apiKey448 },449 success: function(response) {450 // v3.5.1: ALWAYS use API models when API returns valid response451 // API models are the source of truth for what's actually available452 // Even if fewer than hardcoded, they're what this API key can access453 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/failure460 self.showNotification('Using built-in model list for ' + provider, 'info');461 }462 },463 error: function() {464 // Fallback to hardcoded models on AJAX error only465 self.showNotification('Using built-in model list for ' + provider, 'info');466 }467 });468 },469 470 /**471 * Handle Clear Model Cache button click472 * Clears all provider model caches via AJAX473 *474 * @since 3.5.1475 */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 state487 const $btn = $('#taics-clear-model-cache');488 if (!$btn.length) {489 return; // Button doesn't exist490 }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: nonce502 },503 success: function(response) {504 if (response.success) {505 self.showNotification('Model cache cleared! Reloading models...', 'success');506 507 // Reload models for current provider508 if (self.currentProvider && self.currentProvider !== 'demo') {509 const apiKey = $('#taics-api-key').val();510 if (apiKey) {511 // Force refresh from API512 self.fetchModelsFromAPI(self.currentProvider, apiKey);513 } else {514 // Just reload hardcoded models515 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 button525 $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 534 419 isValidApiKeyFormat: function(apiKey) { 535 420 // v3.5.0 FIXED: More flexible regex patterns for various key formats … … 552 437 groq: /^[a-zA-Z0-9_\-]{20,}$/, 553 438 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,}$/ 555 441 }; 556 442 … … 565 451 }, 566 452 567 // --- START MODIFICATION ---568 453 // saveCurrentSettings now only updates internal state and triggers events, no localStorage 569 454 saveCurrentSettings: function() { … … 596 481 597 482 }, 598 // --- END MODIFICATION ---599 483 600 484 showNotification: function(message, type) { … … 617 501 return ''; 618 502 } 619 // --- MODIFICATION: Prioritize this.apiKeys for the current provider ---503 // Prioritize this.apiKeys for the current provider 620 504 return this.apiKeys[this.currentProvider] || $('#taics-api-key').val().trim(); 621 505 }, … … 633 517 $('#taics-api-key').val(apiKey); 634 518 this.apiKeys[this.currentProvider] = apiKey; 635 // Removed saveCurrentSettings() call here. Profile save will handle DB persistence.636 519 } 637 520 }, … … 673 556 674 557 // 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 model677 558 this.loadModels(); 678 559 this.updateModelOptions(model); … … 681 562 this.isInitializing = false; 682 563 683 // --- START MODIFICATION ---684 // saveCurrentSettings() is now for internal state update and event triggering, not localStorage685 // We don't need to call it directly here after syncing with profile as profile is the source686 // this.saveCurrentSettings();687 564 $(document).trigger('taics_ai_settings_updated', { 688 565 provider: this.currentProvider, … … 691 568 }); 692 569 $(document).trigger('taics_api_status_check'); 693 // --- END MODIFICATION ---694 570 }, 695 571 … … 715 591 this.updateProviderAndModel(provider, model, apiKey); 716 592 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 723 594 }, 724 595 … … 738 609 const imageCapabilities = { 739 610 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'], 741 612 message: 'OpenAI GPT models can generate images with DALL-E 3' 742 613 }, … … 746 617 }, 747 618 google: { 748 capable: ['gemini- 2.5-pro', 'gemini-2.0-flash'], // Only Gemini 2.x supports images749 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' 750 621 }, 751 622 deepseek: { … … 762 633 }, 763 634 together: { 764 capable: ['meta-llama/ Meta-Llama-3.1-405B-Instruct'], // Limited image support through Together635 capable: ['meta-llama/Llama-4-Maverick-17B-Instruct'], // Limited image support through Together 765 636 message: 'This model may support image generation through Together AI' 766 637 }, … … 768 639 capable: [], // Mistral does not support image generation 769 640 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' 770 645 } 771 646 }; … … 836 711 clearTimeout(this.saveTimeout); 837 712 838 // --- START MODIFICATION ---839 713 // Remove listener for profile load events 840 714 $(document).off('taics_profile_loaded.ai-provider'); 841 // --- END MODIFICATION ---842 715 } 843 716 }; -
technodrome-ai-content-assistant/trunk/includes/class-ai-providers.php
r3423160 r3462088 215 215 216 216 // Return hardcoded list of known Claude models (Anthropic doesn't provide models endpoint) 217 // v 3.5.1: Updated with latest Claude 4.5 versions (Dec 2025)217 // v4.1.0: Updated Feb 2026 with latest Claude models 218 218 $models = array( 219 array('id' => 'claude-opus-4 -5-20251101', 'name' => 'Claude Opus 4.5 (Latest)', 'description' => 'Most powerful reasoning & complex tasks (Dec 2025)'),220 array('id' => 'claude-sonnet-4 -5-20250929', 'name' => 'Claude Sonnet 4.5', 'description' => 'Best balance of speed, intelligence &cost'),221 array('id' => 'claude-haiku-4 -5-20251001', 'name' => 'Claude Haiku 4.5', 'description' => 'Fastest & most compact model'),222 array('id' => 'claude-3- 5-sonnet-20241022', 'name' => 'Claude 3.5 Sonnet (Previous)', 'description' => 'Previous generation stable model'),223 array('id' => 'claude- 3-5-haiku-20241022', 'name' => 'Claude 3.5 Haiku (Previous)', 'description' => 'Previous generation fast model')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') 224 224 ); 225 225 … … 272 272 273 273 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; 286 326 } 287 327 } … … 318 358 319 359 public static function get_available_models($api_key) { 320 // v 3.5.0: Validate API key before returning models360 // v4.0.6: Fetch models from API instead of hardcoded list 321 361 if (empty($api_key)) { 322 362 return array(); 323 363 } 324 364 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; 357 409 } 358 410 } … … 389 441 390 442 public static function get_available_models($api_key) { 391 // v 3.5.0: Validate API key before returning models443 // v4.0.6: Fetch models from API instead of hardcoded list 392 444 if (empty($api_key)) { 393 445 return array(); 394 446 } 395 447 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; 428 492 } 429 493 } … … 461 525 462 526 public static function get_available_models($api_key) { 463 // v 3.5.0: Validate API key before returning models527 // v4.0.6: Fetch models from API instead of hardcoded list 464 528 if (empty($api_key)) { 465 529 return array(); 466 530 } 467 531 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; 501 576 } 502 577 } … … 534 609 535 610 public static function get_available_models($api_key) { 536 // v 3.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 537 612 if (empty($api_key)) { 538 613 return array(); 539 614 } 540 615 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; 574 671 } 575 672 } … … 607 704 608 705 public static function get_available_models($api_key) { 609 // v 3.5.0: Validate API key before returning models706 // v4.0.6: Fetch models from API instead of hardcoded list 610 707 if (empty($api_key)) { 611 708 return array(); 612 709 } 613 710 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 758 class 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'], 618 775 'Content-Type' => 'application/json' 619 776 ), 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; 647 842 } 648 843 } … … 655 850 */ 656 851 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 658 854 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() 667 864 ); 668 865 } … … 670 867 /** 671 868 * Get provider display names 672 * 869 * 673 870 * @return array Array of provider => display name 674 871 */ 675 872 public static function get_provider_names() { 676 // v 3.5.0: Added Groq, Together AI, and Mistral AI providers873 // v4.1.0: Added GLM (Zhipu AI) provider 677 874 return array( 678 875 'openai' => esc_html__('OpenAI', 'technodrome-ai-content-assistant'), … … 684 881 'together' => esc_html__('Together AI', 'technodrome-ai-content-assistant'), 685 882 'mistral' => esc_html__('Mistral AI', 'technodrome-ai-content-assistant'), 883 'glm' => esc_html__('GLM (Zhipu AI)', 'technodrome-ai-content-assistant'), 686 884 'demo' => esc_html__('Demo Mode', 'technodrome-ai-content-assistant') 687 885 ); … … 744 942 return preg_match('/^[a-zA-Z0-9]{32,}$/', $api_key); 745 943 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 746 948 default: 747 949 return false; … … 751 953 /** 752 954 * Get default model for provider 753 * 955 * 754 956 * @param string $provider Provider name 755 957 * @return string Default model name 756 958 */ 757 959 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 19 19 private static $instance = null; 20 20 21 public static function init() {21 public static function init(): void { 22 22 add_action('wp_ajax_taics_generate_content', [__CLASS__, 'handle_generate_content']); 23 23 add_action('wp_ajax_taics_save_profile', [__CLASS__, 'ajax_save_profile']); … … 53 53 } 54 54 55 public static function get_instance() {55 public static function get_instance(): self { 56 56 if (self::$instance === null) { 57 57 self::$instance = new self(); … … 65 65 * 66 66 * @since 4.0.5 67 * @param array $profile_data_raw Raw profile data from input68 * @return array Sanitized profile data67 * @param array<string, mixed> $profile_data_raw Raw profile data from input 68 * @return array<string, mixed> Sanitized profile data 69 69 */ 70 70 private static function sanitize_profile_data(array $profile_data_raw): array { … … 111 111 } 112 112 113 public static function handle_generate_content() {113 public static function handle_generate_content(): void { 114 114 check_ajax_referer('taics_ajax_nonce', 'nonce'); 115 115 if (!current_user_can('edit_posts')) { … … 367 367 } 368 368 369 public static function ajax_save_profile() {369 public static function ajax_save_profile(): void { 370 370 check_ajax_referer('taics_ajax_nonce', 'nonce'); 371 371 if (!current_user_can('edit_posts')) { … … 415 415 * Koristi se sa frontend AutoSave sistemom (debouncing, 500ms). 416 416 */ 417 public static function handle_autosave_profile() {417 public static function handle_autosave_profile(): void { 418 418 try { 419 419 // FORMDATA approach - izbegava .htaccess probleme sa JSON … … 510 510 } 511 511 512 public static function ajax_save_profile_simple() {512 public static function ajax_save_profile_simple(): void { 513 513 check_ajax_referer('taics_ajax_nonce', 'nonce'); 514 514 if (!current_user_can('edit_posts')) { … … 559 559 } 560 560 561 public static function handle_load_profile() {561 public static function handle_load_profile(): void { 562 562 check_ajax_referer('taics_ajax_nonce', 'nonce'); 563 563 if (!current_user_can('edit_posts')) { … … 599 599 } 600 600 601 public static function ajax_load_profile_simple() {601 public static function ajax_load_profile_simple(): void { 602 602 check_ajax_referer('taics_ajax_nonce', 'nonce'); 603 603 if (!current_user_can('edit_posts')) { … … 633 633 } 634 634 635 public static function handle_save_auto_publish() {635 public static function handle_save_auto_publish(): void { 636 636 check_ajax_referer('taics_ajax_nonce', 'nonce'); 637 637 if (!current_user_can('edit_posts')) { … … 663 663 } 664 664 665 public static function handle_save_dark_mode() {665 public static function handle_save_dark_mode(): void { 666 666 check_ajax_referer('taics_ajax_nonce', 'nonce'); 667 667 if (!current_user_can('edit_posts')) { … … 697 697 * Handle save show credits AJAX request (v3.8.0) 698 698 */ 699 public static function handle_save_show_credits() {699 public static function handle_save_show_credits(): void { 700 700 check_ajax_referer('taics_ajax_nonce', 'nonce'); 701 701 … … 740 740 } 741 741 742 public static function handle_load_profiles() {742 public static function handle_load_profiles(): void { 743 743 check_ajax_referer('taics_ajax_nonce', 'nonce'); 744 744 if (!current_user_can('edit_posts')) { … … 758 758 } 759 759 760 public static function handle_delete_profile() {760 public static function handle_delete_profile(): void { 761 761 check_ajax_referer('taics_ajax_nonce', 'nonce'); 762 762 if (!current_user_can('edit_posts')) { … … 790 790 } 791 791 792 public static function handle_save_content_rules() {792 public static function handle_save_content_rules(): void { 793 793 check_ajax_referer('taics_ajax_nonce', 'nonce'); 794 794 if (!current_user_can('edit_posts')) { … … 819 819 } 820 820 821 public static function handle_load_content_rules() {821 public static function handle_load_content_rules(): void { 822 822 check_ajax_referer('taics_ajax_nonce', 'nonce'); 823 823 if (!current_user_can('edit_posts')) { … … 844 844 } 845 845 846 public static function handle_upload_photo() {846 public static function handle_upload_photo(): void { 847 847 check_ajax_referer('taics_ajax_nonce', 'nonce'); 848 848 if (!current_user_can('upload_files')) { … … 909 909 } 910 910 911 public static function handle_delete_photo() {911 public static function handle_delete_photo(): void { 912 912 check_ajax_referer('taics_ajax_nonce', 'nonce'); 913 913 if (!current_user_can('delete_posts')) { … … 996 996 } 997 997 998 public static function handle_save_settings() {998 public static function handle_save_settings(): void { 999 999 check_ajax_referer('taics_ajax_nonce', 'nonce'); 1000 1000 if (!current_user_can('manage_options')) { … … 1005 1005 } 1006 1006 1007 public static function handle_load_settings() {1007 public static function handle_load_settings(): void { 1008 1008 check_ajax_referer('taics_ajax_nonce', 'nonce'); 1009 1009 if (!current_user_can('manage_options')) { … … 1016 1016 1017 1017 1018 public static function handle_bulk_generate() {1018 public static function handle_bulk_generate(): void { 1019 1019 check_ajax_referer('taics_ajax_nonce', 'nonce'); 1020 1020 if (!current_user_can('edit_posts')) { … … 1025 1025 } 1026 1026 1027 public static function handle_add_to_schedule() {1027 public static function handle_add_to_schedule(): void { 1028 1028 check_ajax_referer('taics_ajax_nonce', 'nonce'); 1029 1029 if (!current_user_can('edit_posts')) { … … 1076 1076 1077 1077 1078 public static function handle_get_scheduler_stats() {1078 public static function handle_get_scheduler_stats(): void { 1079 1079 check_ajax_referer('taics_ajax_nonce', 'nonce'); 1080 1080 if (!current_user_can('edit_posts')) { … … 1182 1182 * WordPress Security Compliant. 1183 1183 */ 1184 public static function handle_delete_post() {1184 public static function handle_delete_post(): void { 1185 1185 check_ajax_referer('taics_ajax_nonce', 'nonce'); 1186 1186 … … 1223 1223 1224 1224 /** 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 1227 1230 */ 1228 public static function handle_get_available_models() {1231 public static function handle_get_available_models(): void { 1229 1232 // Verify nonce without dying - allow graceful error handling 1230 1233 if (empty($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'taics_ajax_nonce')) { … … 1239 1242 1240 1243 $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')); 1245 1247 return; 1246 1248 } 1247 1249 1248 1250 try { 1249 // v 3.5.0: Use new Model Manager for dynamic model fetching with caching1251 // v4.2.0: Use Model Manager for hardcoded models (no API calls) 1250 1252 require_once plugin_dir_path(__FILE__) . 'class-model-manager.php'; 1251 1253 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); 1254 1256 1255 1257 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')); 1257 1259 return; 1258 1260 } … … 1262 1264 'provider' => $provider, 1263 1265 '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 1265 1268 ]); 1266 1269 … … 1272 1275 /** 1273 1276 * Handle clear model cache request (v3.5.1) 1274 * Clears all WordPress transient caches for AI models1277 * v4.2.0: No longer needed - hardcoded models only, but kept for compatibility 1275 1278 * 1276 1279 * @since 3.5.1 1277 1280 * @return void 1278 1281 */ 1279 public static function handle_clear_model_cache() {1282 public static function handle_clear_model_cache(): void { 1280 1283 // Security check - using footer nonce 1281 1284 if (!check_ajax_referer('taics_footer_nonce', 'nonce', false)) { … … 1290 1293 } 1291 1294 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 )); 1332 1302 } 1333 1303 … … 1335 1305 * Handle reset license to FREE AJAX request (v3.5.4) 1336 1306 */ 1337 public static function handle_reset_license_to_free() {1307 public static function handle_reset_license_to_free(): void { 1338 1308 check_ajax_referer('taics_ajax_nonce', 'nonce'); 1339 1309 … … 1381 1351 * Saves photos to user_meta for photo-positions.js 1382 1352 */ 1383 public static function handle_save_photos() {1353 public static function handle_save_photos(): void { 1384 1354 check_ajax_referer('taics_ajax_nonce', 'nonce'); 1385 1355 … … 1436 1406 * Loads photos from user_meta for photo-positions.js 1437 1407 */ 1438 public static function handle_load_photos() {1408 public static function handle_load_photos(): void { 1439 1409 check_ajax_referer('taics_ajax_nonce', 'nonce'); 1440 1410 … … 1469 1439 * Saves custom profile names to user_meta for profile-name-sync.js 1470 1440 */ 1471 public static function handle_save_profile_name() {1441 public static function handle_save_profile_name(): void { 1472 1442 check_ajax_referer('taics_ajax_nonce', 'nonce'); 1473 1443 -
technodrome-ai-content-assistant/trunk/includes/class-model-manager.php
r3423160 r3462088 1 1 <?php 2 2 /** 3 * TAICS Model Manager - Dynamic Model Fetching and Caching3 * TAICS Model Manager - Hardcoded Model Lists 4 4 * 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 6 7 * 7 8 * @package TAICS_Content_Assistant 8 * @version 3.5.09 * @version 4.2.0 9 10 */ 10 11 … … 15 16 class TAICS_Model_Manager { 16 17 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) 27 24 * @return array List of available models 28 25 */ 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 62 32 * 63 33 * @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) { 70 37 $models = array(); 71 38 72 39 switch ($provider) { 40 case 'demo': 41 $models = array( 42 array('id' => 'enhanced-demo-v2', 'name' => 'Enhanced Demo v2.0') 43 ); 44 break; 45 73 46 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 ); 75 59 break; 76 60 77 61 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 ); 79 73 break; 80 74 81 75 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 ); 83 88 break; 84 89 85 90 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 ); 87 99 break; 88 100 89 101 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 94 113 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 ); 96 126 break; 97 127 98 128 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 ); 100 141 break; 101 142 102 143 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 ); 104 171 break; 105 172 … … 108 175 } 109 176 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 110 187 if (defined('WP_DEBUG') && WP_DEBUG) { 111 188 // 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)'); 113 190 } 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); 140 214 } 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; 168 217 } 169 218 } -
technodrome-ai-content-assistant/trunk/readme.txt
r3461963 r3462088 5 5 Tested up to: 6.9 6 6 Requires PHP: 8.0 7 Stable tag: 4.0. 57 Stable tag: 4.0.6 8 8 License: GPL v2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 41 41 == Changelog == 42 42 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`) 44 47 * **CODE QUALITY**: AJAX Handler refactoring - Created centralized sanitize_profile_data() method (DRY principle) 45 48 * **CODE QUALITY**: Removed ~100 lines of duplicate code by reusing centralized method 46 49 * **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) 47 51 * **FIX**: Structure Name no longer resets to "PROFILE" when switching profiles 48 52 * **FIX**: Custom profile names persist correctly without being overwritten -
technodrome-ai-content-assistant/trunk/technodrome-ai-content-assistant.php
r3461963 r3462088 4 4 * Plugin URI: https://technodrome.org/ai-content-assistant 5 5 * Description: Advanced AI content generation plugin with multiple AI providers, profile system, layout templates, and content rules for WordPress. 6 * Version: 4.0. 56 * Version: 4.0.6 7 7 * Author: Technodrome Team 8 8 * Author URI: https://technodrome.org … … 30 30 31 31 // Plugin constants 32 define('TAICS_VERSION', '4.0. 5');32 define('TAICS_VERSION', '4.0.6'); 33 33 define('TAICS_PLUGIN_FILE', __FILE__); 34 34 define('TAICS_PLUGIN_DIR', plugin_dir_path(__FILE__)); … … 549 549 /** 550 550 * Get AI models 551 * v4.2.0: Hardcoded model lists only - no API calls 552 * Updated Feb 2026 with latest models 551 553 */ 552 554 private function get_ai_models() { … … 555 557 'name' => 'OpenAI', 556 558 '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' 560 569 ) 561 570 ), … … 563 572 'name' => 'Anthropic', 564 573 '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', 565 580 'claude-3-opus' => 'Claude 3 Opus', 566 581 'claude-3-sonnet' => 'Claude 3 Sonnet', … … 571 586 'name' => 'Google', 572 587 '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' 575 598 ) 576 599 ), … … 578 601 'name' => 'DeepSeek', 579 602 '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', 581 608 'deepseek-chat' => 'DeepSeek Chat' 582 609 ) … … 585 612 'name' => 'Cohere', 586 613 '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' 589 681 ) 590 682 )
Note: See TracChangeset
for help on using the changeset viewer.