'Unauthorized' ) ); } $test_key = sanitize_text_field( wp_unslash( $_POST['api_key'] ?? '' ) ); $test_model = sanitize_text_field( wp_unslash( $_POST['model'] ?? '' ) ); $is_connector_connected = self::is_wp_ai_connector_connected(); if ( $test_model === 'auto_wp_ai' ) { if ( ! $is_connector_connected ) { wp_send_json_error( array( 'message' => 'WordPress AI Connector has no active provider connected. Please install/connect a provider under Settings > Connectors, or select a direct AI model from the dropdown.', ) ); } $test_key = 'wp_ai_core'; } if ( empty( $test_key ) ) { $saved_settings = get_option( 'metaforge_ai_settings', array() ); if ( ! empty( $saved_settings['api_key'] ) ) { $test_key = $saved_settings['api_key']; } elseif ( $is_connector_connected ) { $test_key = 'wp_ai_core'; } else { wp_send_json_error( array( 'message' => 'Please enter an API key to test, or connect an AI provider under Settings > Connectors.' ) ); } } $clean_test_key = class_exists( 'MetaForge_APIClient' ) ? MetaForge_APIClient::decrypt_key( $test_key ) : $test_key; $trimmed_key = trim( $clean_test_key ); $model_lower = strtolower( $test_model ); // Only validate key prefixes if not testing through WordPress Core AI Client if ( $clean_test_key !== 'wp_ai_core' ) { if ( strpos( $trimmed_key, 'gsk_' ) === 0 && strpos( $model_lower, 'llama' ) === false && strpos( $model_lower, 'distill' ) === false && strpos( $model_lower, 'qwen' ) === false && strpos( $model_lower, 'gpt-oss' ) === false && strpos( $model_lower, 'groq' ) === false ) { wp_send_json_error( array( 'message' => 'This key starts with "gsk_", which is a Groq API key. Please select a Groq model from the dropdown.' ) ); } if ( ( strpos( $trimmed_key, 'AIzaSy' ) === 0 || strpos( $trimmed_key, 'AQ.' ) === 0 ) && strpos( $model_lower, 'gemini' ) === false ) { wp_send_json_error( array( 'message' => 'This key is a Google Gemini API key. Please select a Google Gemini model from the dropdown.' ) ); } if ( strpos( $trimmed_key, 'sk-ant-' ) === 0 && strpos( $model_lower, 'claude' ) === false ) { wp_send_json_error( array( 'message' => 'This key starts with "sk-ant-", which is an Anthropic Claude API key. Please select a Claude model from the dropdown.' ) ); } if ( strpos( $trimmed_key, 'sk-or-v1-' ) === 0 && strpos( $model_lower, 'openrouter' ) === false ) { wp_send_json_error( array( 'message' => 'This key starts with "sk-or-v1-", which is an OpenRouter key. Please select OpenRouter from the dropdown.' ) ); } if ( strpos( $trimmed_key, 'sk-proj-' ) === 0 && strpos( $model_lower, 'gpt' ) === false && strpos( $model_lower, 'o1' ) === false && strpos( $model_lower, 'o3' ) === false ) { wp_send_json_error( array( 'message' => 'This key starts with "sk-proj-", which is an OpenAI key. Please select an OpenAI model (e.g. GPT-4o Mini) from the dropdown.' ) ); } } // Temporarily override the saved options so the client uses the unsaved ones $filter_key = ( $clean_test_key === 'wp_ai_core' ) ? '' : $clean_test_key; add_filter( 'pre_option_metaforge_ai_settings', function () use ( $filter_key, $test_model ) { return array( 'api_key' => $filter_key, 'model' => $test_model, ); } ); // This is a dry run with minimal tokens $client = new MetaForge_APIClient(); $system = "You are a test script. Respond with exactly 'OK'."; $user = 'Test connection.'; $response = $client->generate( $system, $user, false ); if ( is_wp_error( $response ) ) { $error_data = $response->get_error_data(); $retry = isset( $error_data['retry_after'] ) ? intval( $error_data['retry_after'] ) : 0; wp_send_json_error( array( 'message' => $response->get_error_message(), 'code' => $response->get_error_code(), 'retry_after' => $retry, ) ); } wp_send_json_success( array( 'message' => 'Connection successful! Engine is ready.' ) ); } public function admin_menu() { add_submenu_page( 'metaforge', 'AI Settings', 'AI Settings', 'manage_metaforge', 'metaforge_ai_settings', array( $this, 'admin_page' ) ); } public function register_settings() { register_setting( 'metaforge_ai_settings_group', 'metaforge_ai_settings', array( $this, 'sanitize_settings' ) ); } public function sanitize_settings( $input ) { $sanitized = array(); $model = isset( $input['model'] ) ? sanitize_text_field( $input['model'] ) : 'gemini-2.0-flash'; $sanitized['model'] = $model; if ( isset( $input['api_key'] ) ) { $raw_key = sanitize_text_field( $input['api_key'] ); $sanitized['api_key'] = ! empty( $raw_key ) && class_exists( 'MetaForge_APIClient' ) ? MetaForge_APIClient::encrypt_key( $raw_key ) : $raw_key; } else { $sanitized['api_key'] = ''; } $sanitized['detect_layout'] = isset( $input['detect_layout'] ) ? '1' : '0'; $sanitized['generate_schema'] = isset( $input['generate_schema'] ) ? '1' : '0'; $sanitized['fallback_ai'] = isset( $input['fallback_ai'] ) ? '1' : '0'; return $sanitized; } public function admin_page() { if ( ! current_user_can( 'manage_metaforge' ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only notice query flag. if ( isset( $_GET['settings-updated'] ) && sanitize_text_field( wp_unslash( $_GET['settings-updated'] ) ) ) { add_settings_error( 'metaforge_ai_messages', 'metaforge_ai_message', 'Settings Saved Successfully', 'updated' ); } $options = get_option( 'metaforge_ai_settings', array( 'api_key' => '', 'model' => 'gemini-3.5-flash-lite', 'detect_layout' => '0', 'generate_schema' => '0', 'fallback_ai' => '0', ) ); ?>
' . esc_html( $error['message'] ) . '
🔒 Security Recommendation: AUTH_KEY is not explicitly set in wp-config.php. Define a unique AUTH_KEY or MetaForge_AI_API_KEY in wp-config.php for maximum API key encryption security.