prefix . 'ai_app_onsite_models'; $model_row = $wpdb->get_row( $wpdb->prepare( "SELECT provider, shutdown_date FROM $table_name_models WHERE model = %s LIMIT 1", $model ), ARRAY_A ); if ( $model_row ) { // Provider/model mismatch check if ( ! empty( $active_provider ) && $model_row['provider'] !== $active_provider ) { $provider_label = ucfirst( $active_provider ); wp_send_json_error( 'Cannot save — the model "' . $model . '" belongs to ' . ucfirst( $model_row['provider'] ) . ', but your selected provider is ' . $provider_label . '. Please select a ' . $provider_label . ' model.' ); return; } // Shutdown check $shutdown_date = $model_row['shutdown_date']; if ( $shutdown_date !== null && $shutdown_date < current_time( 'Y-m-d' ) ) { wp_send_json_error( 'Cannot save — the selected model "' . $model . '" has been shut down. Please select an available model.' ); return; } } // Check if settings already exist in the database $table_name_settings = $wpdb->prefix . 'ai_app_onsite_model_settings'; // Enforce single row per app_id: if duplicates exist, keep only the latest $row_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM $table_name_settings WHERE app_id = 1" ); if ( $row_count > 1 ) { $keep_id = (int) $wpdb->get_var( "SELECT MAX(id) FROM $table_name_settings WHERE app_id = 1" ); $wpdb->query( $wpdb->prepare( "DELETE FROM $table_name_settings WHERE app_id = 1 AND id != %d", $keep_id ) ); } $existing_settings = $wpdb->get_row( "SELECT * FROM $table_name_settings WHERE app_id = 1 LIMIT 1" ); if ( $existing_settings ) { $old_data = (array) $existing_settings; $old_provider = $existing_settings->active_provider ?? 'openai'; $update_data = array( 'model' => $model, 'temperature' => $temperature, 'f_penalty' => $f_penalty, 'p_penalty' => $p_penalty, 'max_tokens' => $max_tokens, 'top_p' => $top_p, 'active_provider' => $active_provider, 'updated_at' => current_time( 'mysql' ), ); // Provider switch: disable old provider's key, enable new one if ( $old_provider !== $active_provider ) { if ( $old_provider === 'openai' ) { $update_data['key_status'] = 'false'; } elseif ( $old_provider === 'anthropic' ) { $update_data['anthropic_key_status'] = 'false'; } if ( $active_provider === 'openai' && ! empty( $existing_settings->openai_key ) ) { $update_data['key_status'] = 'true'; } elseif ( $active_provider === 'anthropic' && ! empty( $existing_settings->anthropic_key ) ) { $update_data['anthropic_key_status'] = 'true'; } } $wpdb->update( $table_name_settings, $update_data, array( 'id' => $existing_settings->id ) ); if ( $wpdb->last_error ) { wp_send_json_error( 'Error updating data: ' . $wpdb->last_error ); } $new_data = [ 'model' => $model, 'temperature' => $temperature, 'f_penalty' => $f_penalty, 'p_penalty' => $p_penalty, 'max_tokens' => $max_tokens, 'top_p' => $top_p, 'active_provider' => $active_provider ]; if ( function_exists( 'aiapp_maybe_invalidate_cache' ) ) { aiapp_maybe_invalidate_cache( $old_data, $new_data, 'Model settings updated' ); } $saved_settings = $wpdb->get_row( "SELECT * FROM $table_name_settings WHERE app_id = 1 LIMIT 1" ); wp_send_json_success( array( 'status' => '200', 'message' => 'Settings saved successfully', 'data' => $saved_settings, ) ); } else { // Insert new row (first time setup) $wpdb->insert( $table_name_settings, array( 'app_id' => 1, 'model' => $model, 'temperature' => $temperature, 'f_penalty' => $f_penalty, 'p_penalty' => $p_penalty, 'max_tokens' => $max_tokens, 'top_p' => $top_p, 'active_provider' => $active_provider, 'created_at' => current_time( 'mysql' ), 'updated_at' => current_time( 'mysql' ), ), array( '%d', '%s', '%f', '%f', '%f', '%d', '%f', '%s', '%s', '%s' ) ); if ( $wpdb->last_error ) { wp_send_json_error( 'Error saving data: ' . $wpdb->last_error ); } $saved_settings = $wpdb->get_row( "SELECT * FROM $table_name_settings WHERE app_id = 1 LIMIT 1" ); wp_send_json_success( array( 'status' => '200', 'message' => 'Settings saved successfully', 'data' => $saved_settings, ) ); } } function ai_app_onsite_get_model_list() { global $wpdb; $table_name_models = $wpdb->prefix . 'ai_app_onsite_models'; // Get active provider to filter models $table_name_settings = $wpdb->prefix . 'ai_app_onsite_model_settings'; $active_provider = $wpdb->get_var("SELECT active_provider FROM $table_name_settings WHERE app_id = 1"); $active_provider = $active_provider ?: 'openai'; $models_data = $wpdb->get_results( $wpdb->prepare( "SELECT category, model, supported_parameters, endpoint, description, shutdown_date FROM $table_name_models WHERE provider = %s ORDER BY category, model ASC", $active_provider ), ARRAY_A ); if (empty($models_data)) { return []; } return $this->build_grouped_model_list( $models_data ); } /** * Build a grouped model list with deprecation-aware grouping. * * Groups: * - Active models: grouped by their functional category * - Deprecated models (shutdown_date in future): grouped under "Deprecated" * - Shut Down models (shutdown_date in past): grouped under "Shut Down" * * @param array $models_data Raw model rows from the database. * @return array Grouped models with deprecation metadata. */ private function build_grouped_model_list( $models_data ) { $today = current_time( 'Y-m-d' ); $active_groups = []; $deprecated_models = []; $shutdown_models = []; foreach ( $models_data as $model ) { $shutdown_date = ! empty( $model['shutdown_date'] ) ? $model['shutdown_date'] : null; $status = 'active'; $is_selectable = true; $shutdown_display = null; if ( $shutdown_date ) { if ( $shutdown_date < $today ) { $status = 'shutdown'; $is_selectable = false; } else { $status = 'deprecated'; $shutdown_display = date_i18n( 'F j, Y', strtotime( $shutdown_date ) ); } } $model_entry = [ 'model' => $model['model'], 'supported_parameters' => json_decode( $model['supported_parameters'], true ), 'endpoint' => $model['endpoint'], 'description' => $model['description'], 'deprecation_status' => $status, 'shutdown_date' => $shutdown_date, 'shutdown_date_display' => $shutdown_display, 'is_selectable' => $is_selectable, 'original_category' => $model['category'], ]; if ( $status === 'active' ) { $cat = $model['category']; if ( ! isset( $active_groups[ $cat ] ) ) { $active_groups[ $cat ] = []; } $active_groups[ $cat ][] = $model_entry; } elseif ( $status === 'deprecated' ) { $deprecated_models[] = $model_entry; } else { $shutdown_models[] = $model_entry; } } // Sort deprecated: by shutdown_date DESC (farthest first), then model name ASC usort( $deprecated_models, function ( $a, $b ) { $date_cmp = strcmp( $b['shutdown_date'], $a['shutdown_date'] ); if ( $date_cmp !== 0 ) return $date_cmp; return strcmp( $a['model'], $b['model'] ); }); // Sort shutdown by model name for determinism usort( $shutdown_models, function ( $a, $b ) { return strcmp( $a['model'], $b['model'] ); }); // Build final grouped result: active categories (sorted), then Deprecated, then Shut Down $grouped = []; ksort( $active_groups ); foreach ( $active_groups as $cat => $models ) { $grouped[ $cat ] = $models; } if ( ! empty( $deprecated_models ) ) { $grouped['Deprecated'] = $deprecated_models; } if ( ! empty( $shutdown_models ) ) { $grouped['Shut Down'] = $shutdown_models; } return $grouped; } function ai_app_onsite_get_model_settings() { global $wpdb; $table_name_settings = $wpdb->prefix . 'ai_app_onsite_model_settings'; // Check if the table exists if ($wpdb->get_var("SHOW TABLES LIKE '$table_name_settings'") != $table_name_settings) { die('Table does not exist.'); } // Get the models list $models_list = $this->ai_app_onsite_get_model_list(); // Check if the table has rows $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings WHERE app_id = 1"); if ($rows_count == 0) { $results = array('status' => '404', 'message' => 'No rows found in the table.', 'models_list' => $models_list); // Return the data wp_send_json($results); die(); } // Fetch the data $results = $wpdb->get_results("SELECT * FROM $table_name_settings WHERE app_id = 1", ARRAY_A); // add models list to results $results[0]['models_list'] = $models_list; // Return the data wp_send_json($results); // Don't forget to exit exit; } function ai_app_onsite_get_openapi_data() { global $wpdb; $table_name_settings = $wpdb->prefix . 'ai_app_onsite_model_settings'; // Check if the table exists if ($wpdb->get_var("SHOW TABLES LIKE '$table_name_settings'") != $table_name_settings) { die('Table does not exist.'); } // Check if the table has rows $rows_count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name_settings"); if ($rows_count == 0) { $results = array('status' => '404', 'message' => 'No rows found in the table.'); // Return the data wp_send_json($results); die(); } // Determine which provider's key to return $provider = isset($_POST['provider']) ? sanitize_text_field(wp_unslash($_POST['provider'])) : ''; if ( ! empty( $provider ) && class_exists( 'AI_App_Onsite_Provider_Factory' ) ) { try { $provider_handler = AI_App_Onsite_Provider_Factory::get_provider( $provider ); $key_column = $provider_handler->get_key_column(); $status_column = $provider_handler->get_key_status_column(); } catch ( \InvalidArgumentException $e ) { $key_column = 'openai_key'; $status_column = 'key_status'; } } else { // Fallback: read active_provider from DB $active_provider = $wpdb->get_var("SELECT active_provider FROM $table_name_settings WHERE app_id = 1 LIMIT 1"); if ( $active_provider && $active_provider !== 'openai' && class_exists( 'AI_App_Onsite_Provider_Factory' ) ) { try { $provider_handler = AI_App_Onsite_Provider_Factory::get_provider( $active_provider ); $key_column = $provider_handler->get_key_column(); $status_column = $provider_handler->get_key_status_column(); $provider = $active_provider; } catch ( \InvalidArgumentException $e ) { $key_column = 'openai_key'; $status_column = 'key_status'; $provider = 'openai'; } } else { $key_column = 'openai_key'; $status_column = 'key_status'; $provider = 'openai'; } } // Fetch provider-specific key data (column names are from trusted provider factory, not user input) $row = $wpdb->get_row( "SELECT `{$key_column}` AS api_key, `{$status_column}` AS key_status FROM {$table_name_settings} WHERE app_id = 1 LIMIT 1", ARRAY_A ); // Return data in a consistent format $results = array( array( 'openai_key' => $row['api_key'] ?? '', 'key_status' => $row['key_status'] ?? 'true', 'provider' => $provider, ) ); wp_send_json($results); exit; } function ai_app_onsite_save_openai_key_callback() { global $wpdb; if ( ! isset($_POST['action']) || ! isset($_POST['api_Key_nonce']) || // Check if the nonce field is set ! wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['api_Key_nonce'])), 'ai_app_onsite_model_settings_nonce') ) { wp_send_json_error('Invalid nonce. Please refresh the page and try again.'); return; } // Extract individual settings values openai-iv $openai_key = isset($_POST['api_key']) ? sanitize_text_field(wp_unslash($_POST['api_key'])) : ''; $api_status = isset($_POST['api_status']) ? sanitize_text_field(wp_unslash($_POST['api_status'])) : ''; $engine_model = isset($_POST['engine_model']) ? sanitize_text_field(wp_unslash($_POST['engine_model'])) : ''; // Check if settings already exist in the database $table_name_settings = $wpdb->prefix . 'ai_app_onsite_model_settings'; $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings WHERE app_id = 1"); if ($existing_settings) { $wpdb->update( $table_name_settings, array( 'openai_key' => $openai_key, 'key_status' => $api_status, ), array('id' => $existing_settings->id), array('%s', '%s'), ); // Check if the database operation was successful if ($wpdb->last_error) { wp_send_json_error('Error updating data: ' . $wpdb->last_error); } else { // Return success message for update wp_send_json_success('Settings updated successfully.'); } } else { // Insert new row $wpdb->insert( $table_name_settings, array( 'app_id' => 1, 'openai_key' => $openai_key, 'key_status' => $api_status, 'model' => $engine_model, 'temperature' => '0.7', 'f_penalty' => '0', 'p_penalty' => '0', 'max_tokens' => '3000', 'top_p' => '1', ), array('%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ); // Check if the database operation was successful if ($wpdb->last_error) { wp_send_json_error('Error saving data: ' . $wpdb->last_error); } else { // Return success message for insert wp_send_json_success('Settings saved successfully.'); } } } /** * Save API key for any provider (provider-agnostic endpoint). */ function ai_app_onsite_save_provider_key_callback() { global $wpdb; if ( ! isset($_POST['action']) || ! isset($_POST['api_Key_nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['api_Key_nonce'])), 'ai_app_onsite_model_settings_nonce') ) { wp_send_json_error('Invalid nonce. Please refresh the page and try again.'); return; } $provider = isset($_POST['provider']) ? sanitize_text_field(wp_unslash($_POST['provider'])) : 'openai'; $api_key = isset($_POST['api_key']) ? sanitize_text_field(wp_unslash($_POST['api_key'])) : ''; $api_status = isset($_POST['api_status']) ? sanitize_text_field(wp_unslash($_POST['api_status'])) : ''; if ( ! class_exists( 'AI_App_Onsite_Provider_Factory' ) ) { wp_send_json_error('Provider system not available.'); return; } try { $provider_handler = AI_App_Onsite_Provider_Factory::get_provider( $provider ); } catch ( \InvalidArgumentException $e ) { wp_send_json_error('Unsupported provider: ' . $provider); return; } $key_column = $provider_handler->get_key_column(); $status_column = $provider_handler->get_key_status_column(); $table_name_settings = $wpdb->prefix . 'ai_app_onsite_model_settings'; $existing_settings = $wpdb->get_row("SELECT * FROM $table_name_settings WHERE app_id = 1"); if ($existing_settings) { $wpdb->update( $table_name_settings, array( $key_column => $api_key, $status_column => $api_status, ), array('id' => $existing_settings->id), array('%s', '%s'), ); if ($wpdb->last_error) { wp_send_json_error('Error updating data: ' . $wpdb->last_error); } else { wp_send_json_success('API key saved successfully.'); } } else { $insert_data = array( 'app_id' => 1, $key_column => $api_key, $status_column => $api_status, 'active_provider' => $provider, // Only set on fresh insert 'model' => '', 'temperature' => '0.7', 'f_penalty' => '0', 'p_penalty' => '0', 'max_tokens' => '3000', 'top_p' => '1', ); $wpdb->insert( $table_name_settings, $insert_data ); if ($wpdb->last_error) { wp_send_json_error('Error saving data: ' . $wpdb->last_error); } else { wp_send_json_success('API key saved successfully.'); } } } /** * Get model list filtered by provider. */ function ai_app_onsite_get_model_list_by_provider() { global $wpdb; $provider = isset($_POST['provider']) ? sanitize_text_field(wp_unslash($_POST['provider'])) : 'openai'; $table_name_models = $wpdb->prefix . 'ai_app_onsite_models'; $models_data = $wpdb->get_results( $wpdb->prepare( "SELECT category, model, supported_parameters, endpoint, description, shutdown_date FROM $table_name_models WHERE provider = %s ORDER BY category, model ASC", $provider ), ARRAY_A ); if (empty($models_data)) { wp_send_json([]); return; } wp_send_json( $this->build_grouped_model_list( $models_data ) ); } } new AI_APP_ONSITE_Model_Settings_Handler(); // Instantiate the class }