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:
154
includes/class-brave-search-api.php
Normal file
154
includes/class-brave-search-api.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/**
|
||||
* Brave Search API Integration
|
||||
*
|
||||
* Handles fetching web search results for models that do not natively support web search
|
||||
*
|
||||
* @package WP_Agentic_Writer
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WP_Agentic_Writer_Brave_Search_API {
|
||||
|
||||
/**
|
||||
* Brave Search REST API Endpoint
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $api_endpoint = 'https://api.search.brave.com/res/v1/web/search';
|
||||
|
||||
/**
|
||||
* Get singleton instance.
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @return WP_Agentic_Writer_Brave_Search_API
|
||||
*/
|
||||
public static function get_instance() {
|
||||
static $instance = null;
|
||||
|
||||
if ( null === $instance ) {
|
||||
$instance = new self();
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a web search.
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @param string $query Required. The user's search query.
|
||||
* @param int $count Optional. Number of results to return. Default 3.
|
||||
* @return array|WP_Error Array of formatted search results, or WP_Error on failure.
|
||||
*/
|
||||
public function search( $query, $count = 3 ) {
|
||||
$settings = get_option( 'wp_agentic_writer_settings', array() );
|
||||
$api_key = $settings['brave_search_api_key'] ?? '';
|
||||
|
||||
if ( empty( $api_key ) ) {
|
||||
return new WP_Error(
|
||||
'brave_api_key_missing',
|
||||
__( 'Brave Search API Key is missing. Please configure it in WP Agentic Writer settings.', 'wp-agentic-writer' )
|
||||
);
|
||||
}
|
||||
|
||||
// Check cache first to prevent burning API limits on identical subsequent queries
|
||||
$cache_key = 'wpaw_brave_search_' . md5( $query . '_' . $count );
|
||||
$cached_results = get_transient( $cache_key );
|
||||
if ( false !== $cached_results ) {
|
||||
return $cached_results;
|
||||
}
|
||||
|
||||
$url = add_query_arg(
|
||||
array(
|
||||
'q' => urlencode( $query ),
|
||||
'count' => absint( $count ),
|
||||
'text_decorations' => 0, // Disable HTML tags in descriptions
|
||||
'spellcheck' => 1,
|
||||
),
|
||||
$this->api_endpoint
|
||||
);
|
||||
|
||||
$response = wp_remote_get(
|
||||
$url,
|
||||
array(
|
||||
'headers' => array(
|
||||
'Accept' => 'application/json',
|
||||
'Accept-Encoding' => 'gzip',
|
||||
'X-Subscription-Token' => $api_key,
|
||||
),
|
||||
'timeout' => 15,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$http_code = wp_remote_retrieve_response_code( $response );
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
|
||||
if ( 200 !== $http_code ) {
|
||||
return new WP_Error(
|
||||
'brave_api_error',
|
||||
sprintf(
|
||||
/* translators: %1$d is HTTP status code, %2$s is error message */
|
||||
__( 'Brave Search API Error %1$d: %2$s', 'wp-agentic-writer' ),
|
||||
$http_code,
|
||||
$body['message'] ?? __( 'Unknown Error', 'wp-agentic-writer' )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( empty( $body['web']['results'] ) ) {
|
||||
return array(); // No results found
|
||||
}
|
||||
|
||||
$formatted_results = array();
|
||||
foreach ( $body['web']['results'] as $result ) {
|
||||
$formatted_results[] = array(
|
||||
'title' => $result['title'] ?? '',
|
||||
'url' => $result['url'] ?? '',
|
||||
'description' => $result['description'] ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
// Cache results for 1 hour to prevent redundant API calls
|
||||
set_transient( $cache_key, $formatted_results, HOUR_IN_SECONDS );
|
||||
|
||||
return $formatted_results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats search results into a markdown context block for LLM System Prompt injection.
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @param array $results Search results array.
|
||||
* @param string $query Original query.
|
||||
* @return string Formatted markdown context string.
|
||||
*/
|
||||
public function format_results_for_llm( $results, $query ) {
|
||||
if ( empty( $results ) || is_wp_error( $results ) ) {
|
||||
return "No reliable web search results found for: {$query}";
|
||||
}
|
||||
|
||||
$markdown = "## LIVE WEB SEARCH CONTEXT\n";
|
||||
$markdown .= "> You successfully searched the internet for: \"{$query}\"\n";
|
||||
$markdown .= "> Please incorporate the following real-time data into your answer:\n\n";
|
||||
|
||||
$counter = 1;
|
||||
foreach ( $results as $item ) {
|
||||
$markdown .= "{$counter}. **{$item['title']}**\n";
|
||||
$markdown .= " URL: {$item['url']}\n";
|
||||
$markdown .= " Summary: {$item['description']}\n\n";
|
||||
$counter++;
|
||||
}
|
||||
|
||||
$markdown .= "---------------------------\n";
|
||||
|
||||
return $markdown;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user