# WP Agentic Writer: Brave Search Integration Implementation Plan **Date:** January 29, 2026 **Status:** Planning Phase **Integration Type:** Seamless addition to existing plugin architecture --- ## Executive Summary This document outlines the complete implementation plan for integrating **Brave Search API** into WP Agentic Writer as an alternative web search provider alongside the existing OpenRouter `:online` models. The integration follows the plugin's existing architecture patterns and provides users with flexible, cost-effective web research capabilities. **Key Design Principle:** Brave Search API is positioned as an **alternative provider** to OpenRouter's online models, giving users choice based on cost, performance, and feature requirements. --- ## Table of Contents 1. [Architecture Overview](#architecture-overview) 2. [Settings Integration](#settings-integration) 3. [Database Schema](#database-schema) 4. [Provider Architecture](#provider-architecture) 5. [REST API Endpoints](#rest-api-endpoints) 6. [Frontend Integration](#frontend-integration) 7. [Agent Integration](#agent-integration) 8. [Cost Tracking Integration](#cost-tracking-integration) 9. [Implementation Phases](#implementation-phases) 10. [File Structure](#file-structure) 11. [Testing Strategy](#testing-strategy) --- ## Architecture Overview ### Current Plugin Structure ``` wp-agentic-writer/ ├── includes/ │ ├── class-openrouter-provider.php ← Existing AI provider │ ├── class-gutenberg-sidebar.php ← Main REST API handler │ ├── class-settings.php ← Settings management │ ├── class-cost-tracker.php ← Cost tracking system │ └── class-markdown-parser.php ← Content parsing ├── assets/ │ └── js/ │ └── sidebar.js ← Frontend React app └── views/ └── settings/ └── tab-models.php ← Model settings UI ``` ### Integration Points ``` ┌─────────────────────────────────────────────────────────────────┐ │ SETTINGS PANEL │ │ │ │ API Configuration │ │ ├── OpenRouter API Key [sk-or-v1-...] │ │ └── Brave Search API Key [BSA...] ← NEW │ │ │ │ Web Search Provider │ │ ├── ○ OpenRouter :online models (perplexity, etc) │ │ └── ○ Brave Search API (independent index) ← NEW │ │ │ │ [Only show if Brave API key is set] │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## Settings Integration ### 1. Settings Schema Extension **File:** `includes/class-settings.php` Add Brave Search settings to existing settings array: ```php // In sanitize_settings() method - line ~320 $sanitized['openrouter_api_key'] = trim( $input['openrouter_api_key'] ?? '' ); // ADD NEW: $sanitized['brave_api_key'] = trim( $input['brave_api_key'] ?? '' ); $sanitized['brave_api_tier'] = sanitize_text_field( $input['brave_api_tier'] ?? 'base_ai' ); $sanitized['web_search_provider'] = sanitize_text_field( $input['web_search_provider'] ?? 'openrouter' ); $sanitized['brave_search_enabled'] = isset( $input['brave_search_enabled'] ) ? 1 : 0; $sanitized['brave_cache_enabled'] = isset( $input['brave_cache_enabled'] ) ? 1 : 0; $sanitized['brave_cache_duration_days'] = absint( $input['brave_cache_duration_days'] ?? 30 ); $sanitized['brave_monthly_budget'] = floatval( $input['brave_monthly_budget'] ?? 50.00 ); $sanitized['brave_include_citations'] = isset( $input['brave_include_citations'] ) ? 1 : 0; ``` ### 2. Settings UI - API Keys Section **File:** `views/settings/tab-models.php` Add Brave API key field after OpenRouter API key (around line 480): ```php

Brave Search API. Free tier: 2,000 queries/month.', 'wp-agentic-writer' ) ), 'https://brave.com/search/api/' ); ?>

``` ### 3. Settings UI - Web Search Provider Selection Add new section after model presets: ```php

Reuses search results for identical queries. Can save 40-60% on costs.

days

How long to keep cached search results (1-90 days).

$

Stop searches when monthly cost exceeds this amount.

``` --- ## Database Schema ### New Tables Create three new tables for Brave Search integration. Add to plugin activation hook. **File:** `wp-agentic-writer.php` (activation hook) ```php function wp_agentic_writer_activate() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); // Existing tables... // NEW: Brave Search tables $sql_searches = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpaw_searches ( id BIGINT AUTO_INCREMENT PRIMARY KEY, post_id BIGINT NOT NULL, search_query VARCHAR(500) NOT NULL, search_number INT, total_searches_for_post INT, results_count INT, results_json LONGTEXT, top_result_title VARCHAR(255), top_result_url VARCHAR(500), cost DECIMAL(10, 4), api_tier VARCHAR(50), cache_enabled TINYINT DEFAULT 1, cache_expires_at TIMESTAMP NULL, cache_hit TINYINT DEFAULT 0, search_category VARCHAR(100), status VARCHAR(30) DEFAULT 'completed', error_message TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_post (post_id), KEY idx_query (search_query(255)), KEY idx_cache_expires (cache_expires_at), KEY idx_status (status) ) $charset_collate;"; $sql_citations = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpaw_citations ( id BIGINT AUTO_INCREMENT PRIMARY KEY, post_id BIGINT NOT NULL, citation_number INT NOT NULL, citation_text VARCHAR(500), context_excerpt TEXT, search_id BIGINT, source_url VARCHAR(500) NOT NULL, source_title VARCHAR(255), source_domain VARCHAR(100), source_type VARCHAR(50), result_position INT, article_section VARCHAR(100), added_by VARCHAR(50), verified TINYINT DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, KEY idx_post (post_id), KEY idx_citation_number (post_id, citation_number), KEY idx_source_domain (source_domain) ) $charset_collate;"; $sql_cache = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpaw_search_cache ( id BIGINT AUTO_INCREMENT PRIMARY KEY, search_query_normalized VARCHAR(500) NOT NULL, search_category VARCHAR(100), cache_key VARCHAR(64), results_json LONGTEXT, result_count INT, cached_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMP, hit_count INT DEFAULT 0, cost_saved DECIMAL(10, 4) DEFAULT 0, quality_score DECIMAL(3,2), UNIQUE KEY unique_query_category (search_query_normalized(255), search_category(100)), KEY idx_expires (expires_at), KEY idx_hit_count (hit_count) ) $charset_collate;"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta( $sql_searches ); dbDelta( $sql_citations ); dbDelta( $sql_cache ); } ``` **Also add to:** `CREATE_TABLE.sql` for manual creation --- ## Provider Architecture ### New File: `includes/class-brave-search-provider.php` Create a new provider class following the same pattern as OpenRouter provider: ```php api_key = $settings['brave_api_key'] ?? ''; $this->tier = $settings['brave_api_tier'] ?? 'base_ai'; } /** * Perform web search * * @param string $query Search query * @param array $options Search options * @return array|WP_Error Search results or error */ public function search( $query, $options = array() ) { if ( empty( $this->api_key ) ) { return new WP_Error( 'no_api_key', 'Brave Search API key not configured' ); } // Check cache first if ( ! empty( $options['use_cache'] ) ) { $cached = $this->get_cached_result( $query, $options['category'] ?? null ); if ( $cached ) { return array( 'results' => $cached['results'], 'from_cache' => true, 'cache_age_hours' => $cached['age_hours'], 'cost' => 0, ); } } // Check budget $budget_check = $this->check_budget(); if ( is_wp_error( $budget_check ) ) { return $budget_check; } // Call API $response = $this->call_api( $query, $options ); if ( is_wp_error( $response ) ) { return $response; } // Calculate cost $cost = $this->calculate_cost(); // Store search record $search_id = $this->store_search( $query, $response, $cost, $options ); // Cache results if ( ! empty( $options['use_cache'] ) ) { $this->cache_results( $query, $response, $options ); } // Track cost $this->track_cost( $cost, $options['post_id'] ?? 0 ); return array( 'results' => $response, 'from_cache' => false, 'search_id' => $search_id, 'cost' => $cost, 'result_count' => count( $response['web']['results'] ?? array() ), ); } /** * Call Brave Search API */ private function call_api( $query, $options = array() ) { $endpoint = $this->api_base . '/web/search'; $params = array( 'q' => $query, 'count' => $options['count'] ?? 10, 'safesearch' => 'moderate', 'search_lang' => $options['language'] ?? 'en', 'country' => $options['country'] ?? 'US', ); $response = wp_remote_get( add_query_arg( $params, $endpoint ), array( 'headers' => array( 'X-Subscription-Token' => $this->api_key, 'Accept' => 'application/json', ), 'timeout' => 30, ) ); if ( is_wp_error( $response ) ) { return $response; } $status = wp_remote_retrieve_response_code( $response ); $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( 429 === $status ) { return new WP_Error( 'rate_limited', 'Brave API rate limit exceeded' ); } elseif ( 401 === $status ) { return new WP_Error( 'invalid_api_key', 'Invalid Brave API key' ); } elseif ( 200 !== $status ) { return new WP_Error( 'api_error', 'Brave API error: ' . $status ); } return $body; } /** * Check cache for existing results */ private function get_cached_result( $query, $category = null ) { global $wpdb; $cache_key = sha1( strtolower( trim( $query ) ) ); $cached = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wpaw_search_cache WHERE cache_key = %s AND expires_at > NOW() ORDER BY hit_count DESC LIMIT 1", $cache_key ) ); if ( $cached ) { // Update cache stats $wpdb->update( $wpdb->prefix . 'wpaw_search_cache', array( 'hit_count' => $cached->hit_count + 1, 'cost_saved' => $cached->cost_saved + $this->calculate_cost(), ), array( 'id' => $cached->id ) ); $age_seconds = time() - strtotime( $cached->cached_at ); return array( 'results' => json_decode( $cached->results_json, true ), 'age_hours' => ceil( $age_seconds / 3600 ), ); } return null; } /** * Store search in database */ private function store_search( $query, $response, $cost, $options = array() ) { global $wpdb; $top_result = $response['web']['results'][0] ?? null; $wpdb->insert( $wpdb->prefix . 'wpaw_searches', array( 'post_id' => $options['post_id'] ?? 0, 'search_query' => $query, 'search_number' => $options['search_number'] ?? 1, 'total_searches_for_post' => $options['total_searches'] ?? 1, 'results_count' => count( $response['web']['results'] ?? array() ), 'results_json' => wp_json_encode( $response ), 'top_result_title' => $top_result['title'] ?? null, 'top_result_url' => $top_result['url'] ?? null, 'cost' => $cost, 'api_tier' => $this->tier, 'search_category' => $options['category'] ?? 'general', 'status' => 'completed', ) ); return $wpdb->insert_id; } /** * Cache search results */ private function cache_results( $query, $response, $options = array() ) { global $wpdb; $settings = get_option( 'wp_agentic_writer_settings', array() ); $cache_days = absint( $settings['brave_cache_duration_days'] ?? 30 ); $cache_key = sha1( strtolower( trim( $query ) ) ); $wpdb->insert( $wpdb->prefix . 'wpaw_search_cache', array( 'search_query_normalized' => strtolower( trim( $query ) ), 'search_category' => $options['category'] ?? null, 'cache_key' => $cache_key, 'results_json' => wp_json_encode( $response ), 'result_count' => count( $response['web']['results'] ?? array() ), 'expires_at' => gmdate( 'Y-m-d H:i:s', strtotime( "+{$cache_days} days" ) ), 'quality_score' => 0.9, ) ); } /** * Calculate cost per search */ private function calculate_cost() { $tiers = array( 'free' => 0, 'base_ai' => 0.005, 'pro_ai' => 0.009, ); return $tiers[ $this->tier ] ?? 0; } /** * Check monthly budget */ private function check_budget() { global $wpdb; $settings = get_option( 'wp_agentic_writer_settings', array() ); $budget_limit = floatval( $settings['brave_monthly_budget'] ?? 50.00 ); $monthly_cost = $wpdb->get_var( "SELECT COALESCE(SUM(cost), 0) FROM {$wpdb->prefix}wpaw_searches WHERE MONTH(created_at) = MONTH(NOW()) AND YEAR(created_at) = YEAR(NOW())" ); if ( $monthly_cost >= $budget_limit ) { return new WP_Error( 'budget_exceeded', sprintf( 'Monthly Brave Search budget of $%.2f exceeded', $budget_limit ) ); } return true; } /** * Track cost in cost tracker */ private function track_cost( $cost, $post_id ) { do_action( 'wp_aw_after_api_request', $post_id, 'brave-search', 'web_search', 0, // No input tokens 0, // No output tokens $cost ); } } ``` --- ## REST API Endpoints ### Add to `includes/class-gutenberg-sidebar.php` Add new REST endpoints for Brave Search: ```php // In register_rest_routes() method, add: // Brave Search endpoint register_rest_route( 'wp-agentic-writer/v1', '/brave-search', array( 'methods' => 'POST', 'callback' => array( $this, 'handle_brave_search' ), 'permission_callback' => array( $this, 'check_permissions' ), 'args' => array( 'query' => array( 'required' => true, 'type' => 'string', ), 'postId' => array( 'required' => true, 'type' => 'integer', ), 'category' => array( 'type' => 'string', 'default' => 'general', ), 'useCache' => array( 'type' => 'boolean', 'default' => true, ), ), ) ); // Get searches for post register_rest_route( 'wp-agentic-writer/v1', '/searches/(?P\d+)', array( 'methods' => 'GET', 'callback' => array( $this, 'handle_get_searches' ), 'permission_callback' => array( $this, 'check_permissions' ), ) ); ``` ### Handler Methods ```php /** * Handle Brave Search request */ public function handle_brave_search( $request ) { $query = $request->get_param( 'query' ); $post_id = $request->get_param( 'postId' ); $category = $request->get_param( 'category' ); $use_cache = $request->get_param( 'useCache' ); $provider = WP_Agentic_Writer_Brave_Search_Provider::get_instance(); $result = $provider->search( $query, array( 'post_id' => $post_id, 'category' => $category, 'use_cache' => $use_cache, ) ); if ( is_wp_error( $result ) ) { return new WP_Error( $result->get_error_code(), $result->get_error_message(), array( 'status' => 400 ) ); } return rest_ensure_response( $result ); } /** * Get all searches for a post */ public function handle_get_searches( $request ) { global $wpdb; $post_id = $request->get_param( 'post_id' ); if ( ! current_user_can( 'edit_post', $post_id ) ) { return new WP_Error( 'unauthorized', 'Not allowed', array( 'status' => 403 ) ); } $searches = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wpaw_searches WHERE post_id = %d ORDER BY created_at DESC", $post_id ) ); $citations = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wpaw_citations WHERE post_id = %d ORDER BY citation_number ASC", $post_id ) ); return rest_ensure_response( array( 'searches' => $searches, 'citations' => $citations, 'total_cost' => array_sum( array_map( function( $s ) { return floatval( $s->cost ); }, $searches ) ), ) ); } ``` --- ## Frontend Integration ### Update `assets/js/sidebar.js` Add Brave Search support to the frontend: ```javascript // Add to state management (around line 50) const [webSearchProvider, setWebSearchProvider] = useState('openrouter'); const [braveSearchEnabled, setBraveSearchEnabled] = useState(false); // Load settings on mount useEffect(() => { const settings = wpAgenticWriter.settings || {}; setWebSearchProvider(settings.web_search_provider || 'openrouter'); setBraveSearchEnabled(settings.brave_search_enabled || false); }, []); // Add Brave Search function const performBraveSearch = async (query, category = 'general') => { try { const response = await fetch(wpAgenticWriter.apiUrl + '/brave-search', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpAgenticWriter.nonce, }, body: JSON.stringify({ query: query, postId: postId, category: category, useCache: true, }), }); if (!response.ok) { throw new Error('Brave Search failed'); } const data = await response.json(); // Track cost if (data.cost) { setCost(prev => ({ ...prev, session: prev.session + data.cost })); } return data; } catch (error) { console.error('Brave Search error:', error); return null; } }; // Modify article generation to use selected provider // In sendMessage() or executePlanFromCard(), check provider: if (webSearchProvider === 'brave' && braveSearchEnabled) { // Use Brave Search flow // Perform searches, then pass results to agent } else { // Use OpenRouter :online models // Existing flow } ``` --- ## Agent Integration ### Research Planning Create helper class for automatic search planning: **New File:** `includes/class-research-planner.php` ```php "$topic what is overview", 'category' => 'definition', 'priority' => 'critical', ); if ( 'medium' === $depth || 'deep' === $depth ) { // Current state $searches[] = array( 'query' => "$topic latest news 2024", 'category' => 'news', 'priority' => 'high', ); // Pricing/features $searches[] = array( 'query' => "$topic pricing features comparison", 'category' => 'pricing', 'priority' => 'high', ); } if ( 'deep' === $depth ) { // Use cases $searches[] = array( 'query' => "$topic use cases examples", 'category' => 'examples', 'priority' => 'medium', ); // Technical details $searches[] = array( 'query' => "$topic documentation api guide", 'category' => 'technical', 'priority' => 'medium', ); } return $searches; } } ``` ### Citation Management **New File:** `includes/class-citation-manager.php` ```php $citation_number, 'source' => $source, ); $citation_number++; } // Add References section $references = self::generate_references_section( $citations ); $content .= "\n\n" . $references; return array( 'content' => $content, 'citations' => $citations, ); } /** * Find source in search results */ private static function find_source_by_marker( $marker, $search_results ) { // Match marker to search result URL/title foreach ( $search_results as $search ) { if ( empty( $search['results']['web']['results'] ) ) { continue; } foreach ( $search['results']['web']['results'] as $result ) { $domain = parse_url( $result['url'], PHP_URL_HOST ); if ( stripos( $domain, $marker ) !== false || stripos( $result['title'], $marker ) !== false ) { return array( 'url' => $result['url'], 'title' => $result['title'], 'domain' => $domain, 'snippet' => $result['description'] ?? '', ); } } } return null; } /** * Store citation in database */ private static function store_citation( $post_id, $citation_number, $source ) { global $wpdb; $wpdb->insert( $wpdb->prefix . 'wpaw_citations', array( 'post_id' => $post_id, 'citation_number' => $citation_number, 'source_url' => $source['url'], 'source_title' => $source['title'], 'source_domain' => $source['domain'], 'added_by' => 'agent_automatic', ) ); } /** * Generate References section HTML */ private static function generate_references_section( $citations ) { if ( empty( $citations ) ) { return ''; } $html = "## References\n\n"; foreach ( $citations as $citation ) { $source = $citation['source']; $html .= sprintf( "%d. [%s](%s) - %s\n", $citation['number'], $source['title'], $source['url'], $source['domain'] ); } return $html; } } ``` --- ## Cost Tracking Integration Brave Search costs are automatically tracked via the existing cost tracking system using the `wp_aw_after_api_request` action hook (already implemented in the provider class). ### Display in Sidebar Update cost display to show Brave Search costs separately: ```javascript // In sidebar.js cost display section
AI Models: ${cost.session.toFixed(4)}
{braveSearchCost > 0 && (
Web Search: ${braveSearchCost.toFixed(4)}
)}
Total: ${(cost.session + braveSearchCost).toFixed(4)}
``` --- ## Implementation Phases ### Phase 1: Foundation (Week 1) **Goal:** Basic Brave Search integration - [ ] Create database tables (activation hook) - [ ] Create `class-brave-search-provider.php` - [ ] Add settings fields to `class-settings.php` - [ ] Add UI to `tab-models.php` - [ ] Test API connectivity **Deliverable:** Can perform Brave searches via settings panel test button ### Phase 2: REST API (Week 2) **Goal:** Frontend can call Brave Search - [ ] Add REST endpoints to `class-gutenberg-sidebar.php` - [ ] Add frontend functions to `sidebar.js` - [ ] Test search from frontend - [ ] Implement caching logic - [ ] Test cache hit/miss scenarios **Deliverable:** Frontend can perform searches and see cached results ### Phase 3: Agent Integration (Week 3) **Goal:** Agent uses Brave Search during article generation - [ ] Create `class-research-planner.php` - [ ] Create `class-citation-manager.php` - [ ] Integrate into article generation flow - [ ] Add provider selection logic - [ ] Test end-to-end: topic → searches → article with citations **Deliverable:** Can generate articles with Brave Search and citations ### Phase 4: Analytics & Polish (Week 4) **Goal:** Admin dashboard and optimization - [ ] Add search analytics tab to settings - [ ] Display cost breakdown - [ ] Show cache performance - [ ] Add budget alerts - [ ] Implement cache cleanup cron - [ ] Add error handling and logging **Deliverable:** Complete admin experience with analytics ### Phase 5: Testing & Documentation (Week 5) **Goal:** Production-ready - [ ] Test with free tier limits - [ ] Test with paid tiers - [ ] Test budget enforcement - [ ] Test cache expiry - [ ] Write user documentation - [ ] Create migration guide **Deliverable:** Production-ready feature with docs --- ## File Structure ### New Files to Create ``` includes/ ├── class-brave-search-provider.php ← Main provider class ├── class-research-planner.php ← Auto search planning └── class-citation-manager.php ← Citation extraction views/settings/ └── tab-brave-analytics.php ← Analytics dashboard (optional) ``` ### Files to Modify ``` includes/ ├── class-settings.php ← Add Brave settings ├── class-gutenberg-sidebar.php ← Add REST endpoints └── wp-agentic-writer.php ← Add table creation views/settings/ └── tab-models.php ← Add Brave UI assets/js/ └── sidebar.js ← Add Brave search functions CREATE_TABLE.sql ← Add table schemas ``` --- ## Testing Strategy ### Unit Tests 1. **Provider Tests** - API authentication - Search query formatting - Response parsing - Error handling 2. **Cache Tests** - Cache hit/miss - Expiry logic - Cost savings calculation 3. **Budget Tests** - Monthly limit enforcement - Alert triggering - Cost tracking accuracy ### Integration Tests 1. **Settings Flow** - Save Brave API key - Switch providers - Enable/disable features 2. **Search Flow** - Perform search - Cache result - Reuse cached result - Track cost 3. **Article Generation Flow** - Plan searches - Execute searches - Generate article with citations - Add References section ### User Acceptance Tests 1. **Free Tier User** - Set up with free API key - Generate article (2-3 searches) - Verify cost tracking - Test monthly limit 2. **Paid Tier User** - Set up with paid API key - Generate multiple articles - Verify cache reuse - Check cost savings 3. **OpenRouter User** - Keep using OpenRouter :online - Verify no breaking changes - Test switching between providers --- ## Migration & Compatibility ### Backward Compatibility - **No breaking changes:** Existing OpenRouter functionality remains unchanged - **Opt-in feature:** Brave Search only activates when API key is set - **Default behavior:** If no Brave API key, falls back to OpenRouter :online - **Settings migration:** No migration needed - new settings are additive ### Rollout Strategy 1. **Beta Phase:** Release to select users for testing 2. **Documentation:** Create setup guide and comparison chart 3. **Announcement:** Blog post explaining benefits 4. **Support:** Monitor for issues and provide quick fixes --- ## Cost Comparison Example ### Scenario: Generate 10 articles with web research **OpenRouter :online (Perplexity Sonar Pro)** - Model: `perplexity/sonar-pro` - Cost: ~$15 per 1M tokens - Average: 50K tokens per article with research - Total: 500K tokens = **$7.50** **Brave Search API + Standard Model** - Brave: 3 searches per article × 10 articles = 30 searches - Brave cost: 30 × $0.005 = **$0.15** - AI model: `google/gemini-2.0-flash-exp` (free or $0.075/1M) - AI tokens: 30K tokens per article (no search overhead) - AI cost: 300K tokens = **$0.02** - Total: **$0.17** (97% cheaper!) **With Caching (60% hit rate)** - Brave: 12 fresh + 18 cached = 12 × $0.005 = **$0.06** - AI cost: **$0.02** - Total: **$0.08** (99% cheaper!) --- ## Next Steps 1. **Review this plan** with stakeholders 2. **Set up Brave Search API account** (free tier for testing) 3. **Begin Phase 1 implementation** (database + provider class) 4. **Create test environment** with sample searches 5. **Iterate based on feedback** --- ## Questions & Decisions Needed 1. **Should we support both providers simultaneously?** - Current plan: User chooses one provider per article - Alternative: Use both (Brave for facts, OpenRouter for reasoning) 2. **Citation format preference?** - Current plan: Numbered [1], [2]... with References section - Alternative: Inline links, footnotes, or custom format 3. **Cache invalidation strategy?** - Current plan: 30-day automatic expiry - Alternative: Manual invalidation, topic-based expiry 4. **Budget alert method?** - Current plan: Email to admin - Alternative: Dashboard notification, Slack webhook --- **Document Status:** Ready for Implementation **Last Updated:** January 29, 2026 **Version:** 1.0