Add AI writing assistant plugin with local backend, brave search, and image generation support

- Implement local backend AI provider with Ollama integration
- Add Brave Search API integration for real-time search suggestions
- Add image generation manager with multiple AI providers
- Create hybrid provider system with local/cloud fallback
- Add comprehensive settings UI with provider management
- Implement Gutenberg sidebar with writing assistance controls
- Add SEO schema generation for AI-generated content
- Multiple provider support: OpenRouter, local backend, Codex
This commit is contained in:
Dwindi Ramadhana
2026-05-17 10:48:05 +07:00
parent 97426d5ab1
commit d2c10756ab
61 changed files with 18725 additions and 806 deletions

View File

@@ -50,6 +50,7 @@ class WP_Agentic_Writer_Settings_V2 {
add_action( 'wp_ajax_wpaw_debug_models', array( $this, 'ajax_debug_models' ) );
add_action( 'wp_ajax_wpaw_save_custom_model', array( $this, 'ajax_save_custom_model' ) );
add_action( 'wp_ajax_wpaw_delete_custom_model', array( $this, 'ajax_delete_custom_model' ) );
add_action( 'wp_ajax_wpaw_test_local_backend', array( $this, 'ajax_test_local_backend' ) );
}
/**
@@ -79,9 +80,17 @@ class WP_Agentic_Writer_Settings_V2 {
wp_enqueue_style( 'wpaw-agentic-components', WP_AGENTIC_WRITER_URL . 'assets/css/agentic-components.css', array( 'wpaw-agentic-variables' ), WP_AGENTIC_WRITER_VERSION );
// Legacy plugin styles
wp_enqueue_style( 'wp-agentic-writer-admin-v2', WP_AGENTIC_WRITER_URL . 'assets/css/admin-v2.css', array( 'bootstrap', 'select2-bootstrap-5' ), WP_AGENTIC_WRITER_VERSION );
wp_enqueue_style( 'wp-agentic-writer-settings-v2', WP_AGENTIC_WRITER_URL . 'assets/css/settings-v2.css', array( 'wpaw-agentic-components' ), WP_AGENTIC_WRITER_VERSION );
wp_enqueue_style( 'wp-agentic-writer-cost-log-grouped', WP_AGENTIC_WRITER_URL . 'assets/css/cost-log-grouped.css', array( 'wp-agentic-writer-settings-v2' ), WP_AGENTIC_WRITER_VERSION );
$css_admin_path = WP_AGENTIC_WRITER_DIR . 'assets/css/admin-v2.css';
$css_settings_path = WP_AGENTIC_WRITER_DIR . 'assets/css/settings-v2.css';
$css_log_path = WP_AGENTIC_WRITER_DIR . 'assets/css/cost-log-grouped.css';
$ver_admin = file_exists($css_admin_path) ? filemtime($css_admin_path) : WP_AGENTIC_WRITER_VERSION;
$ver_settings = file_exists($css_settings_path) ? filemtime($css_settings_path) : WP_AGENTIC_WRITER_VERSION;
$ver_log = file_exists($css_log_path) ? filemtime($css_log_path) : WP_AGENTIC_WRITER_VERSION;
wp_enqueue_style( 'wp-agentic-writer-admin-v2', WP_AGENTIC_WRITER_URL . 'assets/css/admin-v2.css', array( 'bootstrap', 'select2-bootstrap-5' ), $ver_admin );
wp_enqueue_style( 'wp-agentic-writer-settings-v2', WP_AGENTIC_WRITER_URL . 'assets/css/settings-v2.css', array( 'wpaw-agentic-components' ), $ver_settings );
wp_enqueue_style( 'wp-agentic-writer-cost-log-grouped', WP_AGENTIC_WRITER_URL . 'assets/css/cost-log-grouped.css', array( 'wp-agentic-writer-settings-v2' ), $ver_log );
// Plugin scripts
wp_enqueue_script( 'wp-agentic-writer-settings-v2', WP_AGENTIC_WRITER_URL . 'assets/js/settings-v2.js', array( 'jquery', 'bootstrap', 'select2' ), WP_AGENTIC_WRITER_VERSION, true );
@@ -968,8 +977,13 @@ class WP_Agentic_Writer_Settings_V2 {
public function sanitize_settings( $input ) {
$sanitized = array();
// Sanitize API key
$sanitized['openrouter_api_key'] = trim( $input['openrouter_api_key'] ?? '' );
// Sanitize API keys (allow empty values to clear them)
if ( isset( $input['openrouter_api_key'] ) ) {
$sanitized['openrouter_api_key'] = trim( $input['openrouter_api_key'] );
}
if ( isset( $input['brave_search_api_key'] ) ) {
$sanitized['brave_search_api_key'] = trim( $input['brave_search_api_key'] );
}
// Sanitize model names (6 models)
$sanitized['chat_model'] = sanitize_text_field( $input['chat_model'] ?? 'google/gemini-2.5-flash' );
@@ -988,6 +1002,7 @@ class WP_Agentic_Writer_Settings_V2 {
$sanitized['web_search_enabled'] = isset( $input['web_search_enabled'] ) && '1' === $input['web_search_enabled'];
$sanitized['cost_tracking_enabled'] = isset( $input['cost_tracking_enabled'] ) && '1' === $input['cost_tracking_enabled'];
$sanitized['enable_clarification_quiz'] = isset( $input['enable_clarification_quiz'] ) && '1' === $input['enable_clarification_quiz'];
$sanitized['enable_faq_schema'] = isset( $input['enable_faq_schema'] ) ? '1' === $input['enable_faq_schema'] : false;
// Sanitize search options
$sanitized['search_engine'] = in_array( $input['search_engine'] ?? '', array( 'auto', 'native', 'exa' ), true )
@@ -1030,6 +1045,38 @@ class WP_Agentic_Writer_Settings_V2 {
$sanitized['custom_languages'] = array();
}
// Sanitize Local Backend settings (Fix for settings wiping out)
if ( isset( $input['local_backend_url'] ) ) {
$sanitized['local_backend_url'] = esc_url_raw( trim( $input['local_backend_url'] ) );
}
if ( isset( $input['local_backend_key'] ) ) {
$sanitized['local_backend_key'] = sanitize_text_field( trim( $input['local_backend_key'] ) );
}
if ( isset( $input['local_backend_model'] ) ) {
$sanitized['local_backend_model'] = sanitize_text_field( trim( $input['local_backend_model'] ) );
}
// Sanitize Task Providers Routing
if ( isset( $input['task_providers'] ) && is_array( $input['task_providers'] ) ) {
$sanitized_providers = array();
$allowed_tasks = array( 'chat', 'clarity', 'planning', 'writing', 'refinement', 'image' );
$allowed_providers_text = array( 'openrouter', 'local_backend', 'codex' );
foreach ( $input['task_providers'] as $task => $provider ) {
$task = sanitize_text_field( $task );
$provider = sanitize_text_field( $provider );
if ( in_array( $task, $allowed_tasks, true ) ) {
if ( 'image' === $task && 'openrouter' === $provider ) {
$sanitized_providers[ $task ] = $provider;
} elseif ( 'image' !== $task && in_array( $provider, $allowed_providers_text, true ) ) {
$sanitized_providers[ $task ] = $provider;
}
}
}
$sanitized['task_providers'] = $sanitized_providers;
}
return $sanitized;
}
@@ -1058,6 +1105,7 @@ class WP_Agentic_Writer_Settings_V2 {
private function prepare_view_data( $settings ) {
// Extract settings (6 models)
$api_key = $settings['openrouter_api_key'] ?? '';
$brave_search_api_key = $settings['brave_search_api_key'] ?? '';
$chat_model = $settings['chat_model'] ?? 'google/gemini-2.5-flash';
$clarity_model = $settings['clarity_model'] ?? 'google/gemini-2.5-flash';
$planning_model = $settings['planning_model'] ?? 'google/gemini-2.5-flash';
@@ -1071,6 +1119,7 @@ class WP_Agentic_Writer_Settings_V2 {
$monthly_budget = $settings['monthly_budget'] ?? 600;
$chat_history_limit = $settings['chat_history_limit'] ?? 20;
$enable_clarification_quiz = $settings['enable_clarification_quiz'] ?? true;
$enable_faq_schema = $settings['enable_faq_schema'] ?? false;
$clarity_confidence_threshold = $settings['clarity_confidence_threshold'] ?? '0.6';
$required_context_categories = $settings['required_context_categories'] ?? array(
'target_outcome',
@@ -1085,6 +1134,12 @@ class WP_Agentic_Writer_Settings_V2 {
$custom_languages = $settings['custom_languages'] ?? array();
$custom_models = get_option( 'wp_agentic_writer_custom_models', array() );
// Local Backend settings
$local_backend_url = $settings['local_backend_url'] ?? '';
$local_backend_key = $settings['local_backend_key'] ?? 'dummy';
$local_backend_model = $settings['local_backend_model'] ?? 'claude-local';
$task_providers = $settings['task_providers'] ?? array();
// Get cost tracking data
$cost_tracker = WP_Agentic_Writer_Cost_Tracker::get_instance();
$monthly_used = $cost_tracker->get_monthly_total();
@@ -1093,6 +1148,7 @@ class WP_Agentic_Writer_Settings_V2 {
return compact(
'api_key',
'brave_search_api_key',
'chat_model',
'clarity_model',
'planning_model',
@@ -1106,6 +1162,7 @@ class WP_Agentic_Writer_Settings_V2 {
'monthly_budget',
'chat_history_limit',
'enable_clarification_quiz',
'enable_faq_schema',
'clarity_confidence_threshold',
'required_context_categories',
'preferred_languages',
@@ -1113,7 +1170,12 @@ class WP_Agentic_Writer_Settings_V2 {
'custom_models',
'monthly_used',
'budget_percent',
'budget_status'
'budget_status',
'local_backend_url',
'local_backend_key',
'local_backend_model',
'task_providers',
'settings'
);
}
@@ -1151,4 +1213,37 @@ class WP_Agentic_Writer_Settings_V2 {
'Swedish' => 'Swedish (Svenska)',
);
}
/**
* AJAX handler: Test local backend connection
*
* @since 0.2.0
*/
public function ajax_test_local_backend() {
check_ajax_referer( 'wpaw_test_local_backend', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => 'Insufficient permissions' ) );
}
$url = sanitize_text_field( wp_unslash( $_POST['url'] ?? '' ) );
if ( empty( $url ) ) {
wp_send_json_error( array( 'message' => 'URL required' ) );
}
// Temporarily create provider with this URL
$temp_settings = get_option( 'wp_agentic_writer_settings', array() );
$temp_settings['local_backend_url'] = $url;
update_option( 'wp_agentic_writer_settings', $temp_settings );
$provider = new WP_Agentic_Writer_Local_Backend_Provider();
$result = $provider->test_connection();
if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
}
wp_send_json_success( $result );
}
}