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

@@ -15,13 +15,26 @@
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! function_exists( 'wpaw_get_provider_badge' ) ) {
function wpaw_get_provider_badge( $task, $task_providers ) {
$provider = $task_providers[ $task ] ?? ( 'image' === $task ? 'openrouter' : 'openrouter' );
if ( 'local_backend' === $provider ) {
return '<span class="badge border border-secondary text-secondary ms-2 align-middle" style="font-size: 0.65rem;"><i class="bi bi-hdd-network me-1"></i>Local Backend</span>';
} elseif ( 'codex' === $provider ) {
return '<span class="badge border border-secondary text-secondary ms-2 align-middle" style="font-size: 0.65rem;"><i class="bi bi-code-slash me-1"></i>Codex</span>';
} else {
return '<span class="badge border border-secondary text-secondary ms-2 align-middle" style="font-size: 0.65rem;"><i class="bi bi-cloud me-1"></i>OpenRouter</span>';
}
}
}
?>
<div class="row g-4">
<!-- Quick Presets -->
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-bottom-0 pt-3">
<div class="card border-0 ">
<div class="card-header border-bottom-0 pt-3">
<div class="d-flex align-items-center">
<div>
<h5 class="card-title mb-1 d-flex align-items-center gap-2"><i class="bi bi-lightning-fill text-warning"></i><?php esc_html_e( 'Quick Presets', 'wp-agentic-writer' ); ?></h5>
@@ -33,7 +46,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<div class="row g-3">
<!-- Budget Preset -->
<div class="col-md-4">
<div class="card border-2 preset-card" data-preset="budget" role="button" tabindex="0">
<div class="card border-2 border-secondary preset-card" data-preset="budget" role="button" tabindex="0" style="border:1px solid var(--wpaw-border) !important;">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-2">
<h6 class="card-title mb-0"><?php esc_html_e( 'Budget', 'wp-agentic-writer' ); ?></h6>
@@ -42,14 +55,14 @@ if ( ! defined( 'ABSPATH' ) ) {
<div class="small text-muted">
<div><strong>Chat/Clarity/Planning/Refinement:</strong> Gemini 2.5 Flash</div>
<div><strong>Writing:</strong> Mistral Small Creative</div>
<div><strong>Image:</strong> GPT-4o</div>
<div><strong>Image:</strong> FLUX.2 klein</div>
</div>
</div>
</div>
</div>
<!-- Balanced Preset -->
<div class="col-md-4">
<div class="card border-2 border-primary preset-card" data-preset="balanced" role="button" tabindex="0">
<div class="card border-2 border-primary preset-card" data-preset="balanced" role="button" tabindex="0" style="border:1px solid var(--wpaw-border) !important;">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-2">
<h6 class="card-title mb-0"><?php esc_html_e( 'Balanced', 'wp-agentic-writer' ); ?></h6>
@@ -58,14 +71,14 @@ if ( ! defined( 'ABSPATH' ) ) {
<div class="small text-muted">
<div><strong>Chat/Clarity/Planning:</strong> Gemini 2.5 Flash</div>
<div><strong>Writing/Refinement:</strong> Claude 3.5 Sonnet</div>
<div><strong>Image:</strong> GPT-4o</div>
<div><strong>Image:</strong> Riverflow V2 Max</div>
</div>
</div>
</div>
</div>
<!-- Premium Preset -->
<div class="col-md-4">
<div class="card border-2 preset-card" data-preset="premium" role="button" tabindex="0">
<div class="card border-2 border-secondary preset-card" data-preset="premium" role="button" tabindex="0" style="border:1px solid var(--wpaw-border) !important;">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-2">
<h6 class="card-title mb-0"><?php esc_html_e( 'Premium', 'wp-agentic-writer' ); ?></h6>
@@ -75,7 +88,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<div><strong>Chat/Planning:</strong> Gemini 3 Flash Preview</div>
<div><strong>Clarity:</strong> Claude Sonnet 4</div>
<div><strong>Writing/Refinement:</strong> GPT-4.1</div>
<div><strong>Image:</strong> GPT-4o</div>
<div><strong>Image:</strong> FLUX.2 max</div>
</div>
</div>
</div>
@@ -87,8 +100,8 @@ if ( ! defined( 'ABSPATH' ) ) {
<!-- Individual Model Selectors -->
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-bottom-0 pt-3">
<div class="card border-0 ">
<div class="card-header border-bottom-0 pt-3">
<div class="d-flex align-items-center justify-content-between">
<div class="d-flex align-items-center">
<div>
@@ -112,6 +125,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<label for="chat_model" class="form-label fw-semibold">
<?php esc_html_e( 'Chat Model', 'wp-agentic-writer' ); ?>
<span class="badge bg-info-subtle text-info ms-2">Discussion</span>
<?php echo wpaw_get_provider_badge( 'chat', $task_providers ?? array() ); ?>
</label>
<select id="chat_model" name="wp_agentic_writer_settings[chat_model]" class="form-select wpaw-select2-model" data-model-type="chat">
<option value="<?php echo esc_attr( $chat_model ); ?>"><?php echo esc_html( $chat_model ); ?></option>
@@ -124,6 +138,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<label for="clarity_model" class="form-label fw-semibold">
<?php esc_html_e( 'Clarity Model', 'wp-agentic-writer' ); ?>
<span class="badge bg-info-subtle text-info ms-2">Analysis</span>
<?php echo wpaw_get_provider_badge( 'clarity', $task_providers ?? array() ); ?>
</label>
<select id="clarity_model" name="wp_agentic_writer_settings[clarity_model]" class="form-select wpaw-select2-model" data-model-type="clarity">
<option value="<?php echo esc_attr( $clarity_model ); ?>"><?php echo esc_html( $clarity_model ); ?></option>
@@ -136,6 +151,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<label for="planning_model" class="form-label fw-semibold">
<?php esc_html_e( 'Planning Model', 'wp-agentic-writer' ); ?>
<span class="badge bg-info-subtle text-info ms-2">Outline</span>
<?php echo wpaw_get_provider_badge( 'planning', $task_providers ?? array() ); ?>
</label>
<select id="planning_model" name="wp_agentic_writer_settings[planning_model]" class="form-select wpaw-select2-model" data-model-type="planning">
<option value="<?php echo esc_attr( $planning_model ); ?>"><?php echo esc_html( $planning_model ); ?></option>
@@ -148,6 +164,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<label for="writing_model" class="form-label fw-semibold">
<?php esc_html_e( 'Writing Model', 'wp-agentic-writer' ); ?>
<span class="badge bg-primary ms-2">Main Writer</span>
<?php echo wpaw_get_provider_badge( 'writing', $task_providers ?? array() ); ?>
</label>
<select id="writing_model" name="wp_agentic_writer_settings[writing_model]" class="form-select wpaw-select2-model" data-model-type="execution">
<option value="<?php echo esc_attr( $writing_model ); ?>"><?php echo esc_html( $writing_model ); ?></option>
@@ -160,6 +177,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<label for="refinement_model" class="form-label fw-semibold">
<?php esc_html_e( 'Refinement Model', 'wp-agentic-writer' ); ?>
<span class="badge bg-info text-info ms-2">Editing</span>
<?php echo wpaw_get_provider_badge( 'refinement', $task_providers ?? array() ); ?>
</label>
<select id="refinement_model" name="wp_agentic_writer_settings[refinement_model]" class="form-select wpaw-select2-model" data-model-type="execution">
<option value="<?php echo esc_attr( $refinement_model ); ?>"><?php echo esc_html( $refinement_model ); ?></option>
@@ -172,6 +190,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<label for="image_model" class="form-label fw-semibold">
<?php esc_html_e( 'Image Model', 'wp-agentic-writer' ); ?>
<span class="badge text-bg-warning ms-2">Visual</span>
<?php echo wpaw_get_provider_badge( 'image', $task_providers ?? array() ); ?>
</label>
<select id="image_model" name="wp_agentic_writer_settings[image_model]" class="form-select wpaw-select2-model" data-model-type="image">
<option value="<?php echo esc_attr( $image_model ); ?>"><?php echo esc_html( $image_model ); ?></option>
@@ -252,7 +271,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<div class="mt-4 p-4 bg-dark rounded-3">
<div class="d-flex align-items-center justify-content-between">
<div>
<h6 class="mb-1"><?php esc_html_e( 'Estimated Cost Per Article', 'wp-agentic-writer' ); ?></h6>
<h6 class="mb-1"><?php esc_html_e( 'Estimated OpenRouter Cost Per Article', 'wp-agentic-writer' ); ?></h6>
<p class="text-muted small mb-0"><?php esc_html_e( 'Based on ~2K planning tokens, ~4K execution tokens, and 1 image.', 'wp-agentic-writer' ); ?></p>
</div>
<div class="text-end">