| 1 |
<?php |
| 2 |
/** |
| 3 |
* grok AI |
| 4 |
* |
| 5 |
* @package Botmaster |
| 6 |
* @since 1.0.0 |
| 7 |
*/ |
| 8 |
|
| 9 |
if ( ! defined( 'ABSPATH' ) ) { |
| 10 |
exit; // Exit if accessed directly. |
| 11 |
} |
| 12 |
|
| 13 |
if ( ! class_exists( 'qcld_wpgrok_addons' ) ) { |
| 14 |
|
| 15 |
|
| 16 |
/** |
| 17 |
* Main Class. |
| 18 |
*/ |
| 19 |
final class qcld_wpgrok_addons { |
| 20 |
|
| 21 |
private $id = 'Open AI'; |
| 22 |
|
| 23 |
/** |
| 24 |
* WPBot Pro version. |
| 25 |
* |
| 26 |
* @var string |
| 27 |
*/ |
| 28 |
public $version = '1.0.6'; |
| 29 |
|
| 30 |
/** |
| 31 |
* WPBot Pro helper. |
| 32 |
* |
| 33 |
* @var object |
| 34 |
*/ |
| 35 |
public $helper; |
| 36 |
|
| 37 |
/** |
| 38 |
* The single instance of the class. |
| 39 |
* |
| 40 |
* @var qcld_wb_Chatbot |
| 41 |
* @since 1.0.0 |
| 42 |
*/ |
| 43 |
protected static $_instance = null; |
| 44 |
|
| 45 |
/** |
| 46 |
* Main wpbot Instance. |
| 47 |
* |
| 48 |
* Ensures only one instance of wpbot is loaded or can be loaded. |
| 49 |
* |
| 50 |
* @return qcld_wb_Chatbot - Main instance. |
| 51 |
* @since 1.0.0 |
| 52 |
* @static |
| 53 |
*/ |
| 54 |
public static function instance() { |
| 55 |
|
| 56 |
if ( is_null( self::$_instance ) ) { |
| 57 |
self::$_instance = new self(); |
| 58 |
} |
| 59 |
|
| 60 |
return self::$_instance; |
| 61 |
} |
| 62 |
|
| 63 |
public $response_list; |
| 64 |
|
| 65 |
/** |
| 66 |
* Constructor |
| 67 |
*/ |
| 68 |
public function __construct() { |
| 69 |
$this->includes(); |
| 70 |
add_action( 'wp_ajax_qcld_grok_response', array( $this, 'grok_response_callback' ) ); |
| 71 |
add_action( 'wp_ajax_nopriv_qcld_grok_response', array( $this, 'grok_response_callback' ) ); |
| 72 |
add_action('wp_ajax_qcld_stream_grokai', array($this, 'qcld_stream_grok_callback')); |
| 73 |
add_action( 'wp_ajax_qcld_grok_add_collection', array( $this, 'qcld_grok_add_collection_callback' ) ); |
| 74 |
add_action('wp_ajax_qcld_grok_collectionlist', array($this, 'qcld_grok_collectionlist_callback')); |
| 75 |
add_action('wp_ajax_nopriv_qcld_stream_grokai', array($this, 'qcld_stream_grok_callback')); |
| 76 |
add_action('wp_ajax_qcld_grok_remove_collection', array($this, 'qcld_grok_remove_collection_callback')); |
| 77 |
add_action( 'wp_ajax_qcld_grok_settings_option', array( $this, 'qcld_grok_settings_option_callback' ) ); |
| 78 |
add_action( 'wp_ajax_qcld_grok_file_upload', array( $this, 'qcld_grok_file_upload_callback' ) ); |
| 79 |
if ( is_admin() && ! empty( $_GET['page'] ) && ( ( $_GET['page'] == 'openai-panel_dashboard' ) || ( $_GET['page'] == 'openai-panel_file' ) || ( $_GET['page'] == 'wpbot_openAi' ) || ( $_GET['page'] == 'openai-panel_help' ) ) ) {// phpcs:ignore WordPress.Security.NonceVerification.Missing |
| 80 |
add_action( 'admin_enqueue_scripts', array( $this, 'qcld_wb_chatbot_grok_admin_scripts' ) ); |
| 81 |
} |
| 82 |
// add_action('wp_enqueue_scripts', array($this, 'qcld_wb_chatbot_grok_scripts')); |
| 83 |
|
| 84 |
} |
| 85 |
|
| 86 |
public function qcld_wb_chatbot_grok_admin_scripts() { |
| 87 |
wp_register_script( |
| 88 |
'qcld-wp-chatbot-grok-admin', |
| 89 |
QCLD_wpCHATBOT_PLUGIN_URL . 'includes/integration/grok/js/qcld-wp-grok-admin.js', |
| 90 |
array( 'jquery' ), |
| 91 |
QCLD_wpCHATBOT_VERSION, |
| 92 |
true |
| 93 |
); |
| 94 |
|
| 95 |
// Localize the script with necessary data. |
| 96 |
wp_localize_script( |
| 97 |
'qcld-wp-chatbot-grok-admin', |
| 98 |
'ajax_object', |
| 99 |
array( |
| 100 |
'ajax_url' => admin_url( 'admin-ajax.php' ), |
| 101 |
'ajax_nonce' => wp_create_nonce( 'wp_chatbot' ), |
| 102 |
'grok_api_key' => get_option( 'qcld_grok_api_key' ), |
| 103 |
'grok_model' => get_option( 'qcld_grok_model' ), |
| 104 |
'grok_enabled' => get_option( 'qcld_grok_enabled' ), |
| 105 |
'qcld_grok_rag_enabled' => get_option( 'qcld_grok_rag_enabled' ), |
| 106 |
'qcld_grok_append_content' => get_option( 'qcld_grok_append_content' ), |
| 107 |
'qcld_grok_prepend_content' => get_option( 'qcld_grok_prepend_content' ), |
| 108 |
) |
| 109 |
); |
| 110 |
|
| 111 |
wp_enqueue_script( 'qcld-wp-chatbot-grok-admin' ); |
| 112 |
} |
| 113 |
|
| 114 |
/** |
| 115 |
* Define wpbot Constants. |
| 116 |
* |
| 117 |
* @return void |
| 118 |
* @since 1.0.0 |
| 119 |
*/ |
| 120 |
public function includes() { |
| 121 |
require_once QCLD_wpCHATBOT_PLUGIN_DIR_PATH . 'includes/Parsedown.php'; |
| 122 |
require_once QCLD_wpCHATBOT_PLUGIN_DIR_PATH . 'includes/class-common-function.php'; |
| 123 |
} |
| 124 |
|
| 125 |
public function qcld_grok_settings_option_callback() { |
| 126 |
$nonce = sanitize_text_field(wp_unslash($_POST['nonce'])); |
| 127 |
if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) { |
| 128 |
wp_send_json( |
| 129 |
array( |
| 130 |
'success' => false, |
| 131 |
'msg' => esc_html__( 'Failed in Security check', 'chatbot'), |
| 132 |
) |
| 133 |
); |
| 134 |
wp_die(); |
| 135 |
} else { |
| 136 |
$grok_api_key = sanitize_text_field( wp_unslash($_POST['grok_api_key']) ?? '' ); |
| 137 |
$grok_model = sanitize_text_field( wp_unslash($_POST['grok_model']) ?? '' ); |
| 138 |
$grok_enabled = sanitize_text_field(wp_unslash($_POST['grok_enabled'])); |
| 139 |
$qcld_grok_page_suggestion_enabled = sanitize_text_field(wp_unslash($_POST['qcld_grok_page_suggestion_enabled'])); |
| 140 |
$qcld_grok_append_content = sanitize_text_field(wp_unslash($_POST['qcld_grok_append_content'])) ?? ''; |
| 141 |
$qcld_grok_prepend_content = sanitize_text_field(wp_unslash($_POST['qcld_grok_prepend_content'])) ?? ''; |
| 142 |
$grok_rag_enabled = sanitize_text_field(wp_unslash($_POST['grok_rag_enabled'])) ?? ''; |
| 143 |
$qcld_grok_system_content = sanitize_text_field(wp_unslash($_POST['qcld_grok_system_content'])) ?? ''; |
| 144 |
$grok_stream_enabled = sanitize_text_field(wp_unslash($_POST['grok_stream_enabled'])) ?? ''; |
| 145 |
$grok_management_api_key = sanitize_text_field( wp_unslash($_POST['grok_management_api_key']) ?? '' ); |
| 146 |
$grok_collection_id = sanitize_text_field( wp_unslash($_POST['grok_collection_id']) ?? '' ); |
| 147 |
|
| 148 |
if ( $grok_management_api_key != '' ) { |
| 149 |
update_option( 'qcld_grok_management_api_key', $grok_management_api_key ); |
| 150 |
} |
| 151 |
if ( $grok_rag_enabled != '' ) { |
| 152 |
update_option( 'qcld_grok_rag_enabled', $grok_rag_enabled ); |
| 153 |
} |
| 154 |
if ( $grok_stream_enabled != '' ) { |
| 155 |
update_option( 'qcld_grok_stream_enabled', $grok_stream_enabled ); |
| 156 |
} |
| 157 |
if ( $grok_api_key != '' ) { |
| 158 |
update_option( 'qcld_grok_api_key', $grok_api_key ); |
| 159 |
} |
| 160 |
if ( $grok_model != '' ) { |
| 161 |
update_option( 'qcld_grok_model', $grok_model ); |
| 162 |
} |
| 163 |
if ( $grok_enabled != '' ) { |
| 164 |
update_option( 'qcld_grok_enabled', $grok_enabled ); |
| 165 |
} |
| 166 |
if ( $grok_collection_id != '' ) { |
| 167 |
update_option( 'qcld_grok_collection_id', $grok_collection_id ); |
| 168 |
} |
| 169 |
if ( $grok_enabled == '1' ) { |
| 170 |
update_option( 'ai_enabled', 0 ); |
| 171 |
update_option( 'qcld_ollama_enabled', 0 ); |
| 172 |
update_option('qcld_gemini_enabled', 0); |
| 173 |
update_option( 'qcld_openrouter_enabled', 0 ); |
| 174 |
update_option('disable_wp_chatbot_site_search',1); |
| 175 |
} |
| 176 |
if ( $qcld_grok_system_content != '' ) { |
| 177 |
update_option( 'qcld_grok_system_content', $qcld_grok_system_content ); |
| 178 |
} |
| 179 |
update_option( 'qcld_grok_page_suggestion_enabled', $qcld_grok_page_suggestion_enabled ); |
| 180 |
if ( isset( $_POST['openai_post_type'] ) ) { |
| 181 |
$openai_post_types = is_array( $_POST['openai_post_type'] ) |
| 182 |
? array_map( 'sanitize_text_field', wp_unslash( $_POST['openai_post_type'] ) ) |
| 183 |
: sanitize_text_field( wp_unslash( $_POST['openai_post_type'] ) ); |
| 184 |
update_option( 'qcld_openai_relevant_post', $openai_post_types ); |
| 185 |
} |
| 186 |
|
| 187 |
update_option( 'qcld_grok_append_content', $qcld_grok_append_content ); |
| 188 |
update_option( 'qcld_grok_prepend_content', $qcld_grok_prepend_content ); |
| 189 |
} |
| 190 |
wp_send_json( $grok_enabled ); |
| 191 |
} |
| 192 |
|
| 193 |
public function grok_response_callback() { |
| 194 |
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_chatbot' ) ) { |
| 195 |
wp_send_json_error([ |
| 196 |
'status' => 'error', |
| 197 |
'message' => esc_html__( 'Security check failed. Unauthorized request.', 'chatbot' ) |
| 198 |
]); |
| 199 |
wp_die(); |
| 200 |
} |
| 201 |
|
| 202 |
if (get_option('is_rate_limiting_enabled') == '1') { |
| 203 |
do_action('rate_limit_checker'); |
| 204 |
} |
| 205 |
$grok_api_key = get_option( 'qcld_grok_api_key' ); |
| 206 |
$keyword = isset($_POST['keyword']) ? sanitize_text_field( wp_unslash($_POST['keyword']) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing |
| 207 |
$grok_system_content = get_option( 'qcld_grok_system_content' ); |
| 208 |
|
| 209 |
// AI Interactive Form |
| 210 |
if (get_option('enable_ai_interactive_form') == '1') { |
| 211 |
$saved_ai_forms = get_option('wpbot_ai_forms', array()); |
| 212 |
if (!empty($saved_ai_forms) && is_array($saved_ai_forms)) { |
| 213 |
$grok_system_content .= "\n\nYou must handle the following interactive forms when the user asks for them:\n"; |
| 214 |
foreach ($saved_ai_forms as $form) { |
| 215 |
$grok_system_content .= "\nForm Title: " . $form['title'] . "\nInstructions: " . $form['prompt'] . "\n"; |
| 216 |
} |
| 217 |
$grok_system_content .= "\n\nCRITICAL INSTRUCTIONS FOR INTERACTIVE FORMS:\n"; |
| 218 |
$grok_system_content .= "When a user triggers an interactive form, you must act as a step-by-step data collection agent.\n"; |
| 219 |
$grok_system_content .= "1. DO NOT ask all questions at once. Ask exactly ONE question at a time.\n"; |
| 220 |
$grok_system_content .= "2. Wait for the user's response before asking the next question.\n"; |
| 221 |
$grok_system_content .= "3. Once all necessary information is collected for the form, you MUST output a final JSON block summarizing the collected data. The keys inside the \"data\" object MUST be dynamically named based on the specific questions you asked during the form collection (e.g., \"Full Name\", \"Company Size\", \"Email\", etc.). The final JSON block must be wrapped EXACTLY in these delimiters:\n"; |
| 222 |
$grok_system_content .= "__AI_FORM_DATA__{ \"form_title\": \"<Form Title>\", \"data\": { \"Question 1\": \"Answer 1\", \"Question 2\": \"Answer 2\" } }__AI_FORM_DATA_END__\n"; |
| 223 |
$grok_system_content .= "Do not include any other text after this JSON block once the form is complete.\n"; |
| 224 |
$grok_system_content .= "4. If the user provides an invalid, irrelevant, or nonsensical answer to your question, DO NOT apologize or state that you lack information. Instead, respond with 'Invalid answer found' and ask the exact same question again."; |
| 225 |
} |
| 226 |
} |
| 227 |
|
| 228 |
$action_prompt = !empty($_POST['action_prompt']) ? wp_unslash($_POST['action_prompt']) : ''; |
| 229 |
if (!empty($action_prompt)) { |
| 230 |
$grok_system_content .= "\n\nCRITICAL ACTIVE ACTION INSTRUCTION:\n" . $action_prompt; |
| 231 |
} |
| 232 |
|
| 233 |
$rag_context = ""; |
| 234 |
if (get_option('qcld_grok_rag_enabled') == '1') { |
| 235 |
$rag_context = Qcld_Bot_Rag()->run_rag_search($keyword); |
| 236 |
if (!empty($rag_context) && $rag_context != "No knowledge base found.") { |
| 237 |
$grok_system_content .= "\n\nRelevant Knowledge Base Information:\n" . $rag_context; |
| 238 |
} |
| 239 |
} |
| 240 |
$relevant_pagelink = Qcld_WPBot_Common_Functions::qcpd_relevant_pagelink( $keyword ); |
| 241 |
$relevant_pagelink = array_slice( $relevant_pagelink, 0, 5, true ); |
| 242 |
if ( ( get_option( 'page_suggestion_enabled' ) == '1' ) && count( $relevant_pagelink ) > 0 ) { |
| 243 |
$relevant_post_link = maybe_unserialize( get_option( 'qlcd_wp_chatbot_relevant_post_link_openai' ) ); |
| 244 |
if ( is_array( $relevant_post_link[ get_wpbot_locale() ] ) ) { |
| 245 |
$relevant_pagelinks = '<br><br><p><em>' . implode( '', $relevant_post_link[ get_wpbot_locale() ] ) . '</em><p>' . implode( '</br>', $relevant_pagelink ); |
| 246 |
} else { |
| 247 |
$relevant_pagelinks = '<br><br><p><em>' . $relevant_post_link[ get_wpbot_locale() ] . '</em><p>' . implode( '</br>', $relevant_pagelink ); |
| 248 |
} |
| 249 |
} else { |
| 250 |
$relevant_pagelinks = ''; |
| 251 |
} |
| 252 |
$collection_id = get_option( 'qcld_grok_collection_id' ) ?? ''; |
| 253 |
if( $collection_id != '' ) { |
| 254 |
$result = $this->direct_search_collection( $keyword ); |
| 255 |
$response['status'] = 'success'; |
| 256 |
$response['message'] = $result ; |
| 257 |
wp_send_json( $response ); |
| 258 |
} else { |
| 259 |
$messages = [ |
| 260 |
['role' => 'system', 'content' => $grok_system_content ?? 'You are a helpful and intelligent assistant for the website "' . site_url() . '". Use live website data and the provided context to respond accurately and briefly. Stay relevant and do not introduce additional topics.' ] |
| 261 |
]; |
| 262 |
|
| 263 |
if (!empty($_POST['ai_history'])) { |
| 264 |
$parsed_history = json_decode(wp_unslash($_POST['ai_history']), true); |
| 265 |
if (is_array($parsed_history)) { |
| 266 |
foreach ($parsed_history as $h) { |
| 267 |
if (isset($h['role']) && isset($h['content'])) { |
| 268 |
$role = ($h['role'] === 'assistant') ? 'assistant' : 'user'; |
| 269 |
$messages[] = [ |
| 270 |
'role' => $role, |
| 271 |
'content' => sanitize_text_field($h['content']) |
| 272 |
]; |
| 273 |
} |
| 274 |
} |
| 275 |
} |
| 276 |
} |
| 277 |
|
| 278 |
$last_msg = end($messages); |
| 279 |
if (!$last_msg || $last_msg['role'] !== 'user' || $last_msg['content'] !== $keyword) { |
| 280 |
$messages[] = ['role' => 'user', 'content' => $keyword]; |
| 281 |
} |
| 282 |
|
| 283 |
$response_mess = wp_remote_post('https://api.x.ai/v1/chat/completions', [ |
| 284 |
'headers' => [ |
| 285 |
'Authorization' => 'Bearer ' . $grok_api_key, |
| 286 |
'Content-Type' => 'application/json', |
| 287 |
], |
| 288 |
'body' => wp_json_encode([ |
| 289 |
'messages' => $messages, |
| 290 |
'model' => 'grok-3-latest', |
| 291 |
'temperature' => 0, |
| 292 |
'stream' => false |
| 293 |
]), |
| 294 |
'timeout' => 60, |
| 295 |
]); |
| 296 |
$response['message'] = ''; |
| 297 |
$status_code = wp_remote_retrieve_response_code($response_mess); |
| 298 |
$data = wp_remote_retrieve_body($response_mess); |
| 299 |
$data = json_decode($data, true); |
| 300 |
do_action('qcld_openai_user_rate_cal', 1); |
| 301 |
$response['status'] = 'success'; |
| 302 |
$reply_raw = $data['choices'][0]['message']['content'] ?? 'No content received'; |
| 303 |
$Parsedown = new Parsedown(); |
| 304 |
$reply_text = $Parsedown->text($reply_raw); |
| 305 |
if (strpos($reply_text, 'AI_FORM_DATA') !== false) { |
| 306 |
$reply_text = Qcld_WPBot_Common_Functions::format_and_save_ai_form_response($reply_text); |
| 307 |
$response['message'] = $reply_text; |
| 308 |
} else { |
| 309 |
$response['message'] = $reply_text . $relevant_pagelinks; |
| 310 |
} |
| 311 |
wp_send_json( $response ); |
| 312 |
} |
| 313 |
} |
| 314 |
public function qcld_stream_grok_callback() |
| 315 |
{ |
| 316 |
|
| 317 |
if (!defined('ABSPATH')) { |
| 318 |
// Optional: protect direct access or require wp-load.php |
| 319 |
// For security → better to use admin-ajax or custom rewrite rule in production |
| 320 |
http_response_code(403); |
| 321 |
exit; |
| 322 |
} |
| 323 |
|
| 324 |
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_chatbot' ) ) { |
| 325 |
echo "data: [ERROR] Security check failed.\n\n"; |
| 326 |
flush(); |
| 327 |
wp_die(); |
| 328 |
} |
| 329 |
|
| 330 |
if (get_option('is_rate_limiting_enabled') == '1') { |
| 331 |
do_action('rate_limit_checker'); |
| 332 |
} |
| 333 |
$api_key = get_option('qcld_grok_api_key'); // ← Use get_option() or env in real code! |
| 334 |
|
| 335 |
// Disable compression (critical for streaming) |
| 336 |
if (function_exists('apache_setenv')) { |
| 337 |
@apache_setenv('no-gzip', 1); |
| 338 |
} |
| 339 |
@ini_set('zlib.output_compression', 0); |
| 340 |
while (ob_get_level() > 0) { ob_end_clean(); } |
| 341 |
|
| 342 |
set_time_limit(180); // Allow long-running requests |
| 343 |
|
| 344 |
$headers = [ |
| 345 |
'Content-Type: application/json', |
| 346 |
'Authorization: Bearer ' . $api_key, |
| 347 |
]; |
| 348 |
$system_content = get_option( 'qcld_grok_system_content' ); |
| 349 |
if (empty($system_content)) { |
| 350 |
$system_content = 'You are a helpful and intelligent assistant for the website "' . site_url() . '". Use live website data and the provided context to respond accurately and briefly. Stay relevant and do not introduce additional topics.'; |
| 351 |
} |
| 352 |
// AI Interactive Form |
| 353 |
if (get_option('enable_ai_interactive_form') == '1') { |
| 354 |
$saved_ai_forms = get_option('wpbot_ai_forms', array()); |
| 355 |
if (!empty($saved_ai_forms) && is_array($saved_ai_forms)) { |
| 356 |
$system_content .= "\n\nYou must handle the following interactive forms when the user asks for them:\n"; |
| 357 |
foreach ($saved_ai_forms as $form) { |
| 358 |
$system_content .= "\nForm Title: " . $form['title'] . "\nInstructions: " . $form['prompt'] . "\n"; |
| 359 |
} |
| 360 |
$system_content .= "\n\nCRITICAL INSTRUCTIONS FOR INTERACTIVE FORMS:\n"; |
| 361 |
$system_content .= "When a user triggers an interactive form, you must act as a step-by-step data collection agent.\n"; |
| 362 |
$system_content .= "1. DO NOT ask all questions at once. Ask exactly ONE question at a time.\n"; |
| 363 |
$system_content .= "2. Wait for the user's response before asking the next question.\n"; |
| 364 |
$system_content .= "3. Once all necessary information is collected for the form, you MUST output a final JSON block summarizing the collected data. The keys inside the \"data\" object MUST be dynamically named based on the specific questions you asked during the form collection (e.g., \"Full Name\", \"Company Size\", \"Email\", etc.). The final JSON block must be wrapped EXACTLY in these delimiters:\n"; |
| 365 |
$system_content .= "__AI_FORM_DATA__{ \"form_title\": \"<Form Title>\", \"data\": { \"Question 1\": \"Answer 1\", \"Question 2\": \"Answer 2\" } }__AI_FORM_DATA_END__\n"; |
| 366 |
$system_content .= "Do not include any other text after this JSON block once the form is complete.\n"; |
| 367 |
$system_content .= "4. If the user provides an invalid, irrelevant, or nonsensical answer to your question, DO NOT apologize or state that you lack information. Instead, respond with 'Invalid answer found' and ask the exact same question again."; |
| 368 |
} |
| 369 |
} |
| 370 |
$messages = []; |
| 371 |
// Load previous conversation from cookie if continuity is enabled |
| 372 |
if ($conversation_continuity == 1 && !empty($_COOKIE['last_five_prompt'])) { |
| 373 |
$decoded_cookie = json_decode(stripslashes($_COOKIE['last_five_prompt']), true); |
| 374 |
if (is_array($decoded_cookie)) { |
| 375 |
$messages = $decoded_cookie; |
| 376 |
} |
| 377 |
} |
| 378 |
// Ensure system message is the first message. |
| 379 |
if (empty($messages) || (isset($messages[0]) && $messages[0]['role'] !== 'system')) { |
| 380 |
array_unshift($messages, ['role' => 'system', 'content' => $system_content]); |
| 381 |
} elseif (isset($messages[0]) && $messages[0]['role'] === 'system' && $messages[0]['content'] !== $system_content) { |
| 382 |
$messages[0]['content'] = $system_content; |
| 383 |
} |
| 384 |
|
| 385 |
array_push($messages, ['role' => 'user', 'content' => sanitize_text_field( wp_unslash( $_POST['message'] ) )]); |
| 386 |
$postData = json_encode([ |
| 387 |
'model' => 'grok-3-latest', |
| 388 |
'messages' => $messages, |
| 389 |
'stream' => true |
| 390 |
]); |
| 391 |
|
| 392 |
// phpcs:ignore WordPress.WP.AlternativeFunctions -- streaming requires native cURL |
| 393 |
$ch = curl_init('https://api.x.ai/v1/chat/completions'); |
| 394 |
curl_setopt($ch, CURLOPT_POST, true); // phpcs:ignore WordPress.WP.AlternativeFunctions |
| 395 |
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); // phpcs:ignore WordPress.WP.AlternativeFunctions |
| 396 |
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); // phpcs:ignore WordPress.WP.AlternativeFunctions |
| 397 |
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $chunk) use (&$full_assistant_response) { // phpcs:ignore WordPress.WP.AlternativeFunctions |
| 398 |
echo $chunk; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming raw output |
| 399 |
echo str_repeat(' ', 1024); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming padding |
| 400 |
flush(); |
| 401 |
ob_flush(); |
| 402 |
|
| 403 |
// Parse the chunk to extract assistant's content for continuity. |
| 404 |
$lines = explode("\n", $chunk); |
| 405 |
foreach ($lines as $line) { |
| 406 |
if (strpos($line, 'data: ') === 0) { |
| 407 |
$json_data = substr($line, 6); |
| 408 |
if ($json_data === '[DONE]') { |
| 409 |
continue; |
| 410 |
} |
| 411 |
$data = json_decode($json_data, true); |
| 412 |
if (isset($data['choices'][0]['delta']['content'])) { |
| 413 |
$full_assistant_response .= $data['choices'][0]['delta']['content']; |
| 414 |
} |
| 415 |
} |
| 416 |
} |
| 417 |
return strlen($chunk); |
| 418 |
}); |
| 419 |
curl_exec($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions |
| 420 |
curl_close($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions |
| 421 |
// phpcs:ignore WordPress.WP.AlternativeFunctions -- streaming requires native cURL |
| 422 |
$ch = curl_init('https://api.x.ai/v1/chat/completions'); |
| 423 |
|
| 424 |
// phpcs:ignore WordPress.WP.AlternativeFunctions |
| 425 |
curl_setopt_array($ch, [ |
| 426 |
CURLOPT_POST => true, |
| 427 |
CURLOPT_HTTPHEADER => $headers, |
| 428 |
CURLOPT_POSTFIELDS => $postData, |
| 429 |
CURLOPT_RETURNTRANSFER => false, // We echo ourselves |
| 430 |
CURLOPT_WRITEFUNCTION => function ($curl, $chunk) { |
| 431 |
echo $chunk; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming raw output |
| 432 |
flush(); // Push to browser immediately |
| 433 |
return strlen($chunk); |
| 434 |
}, |
| 435 |
CURLOPT_TIMEOUT => 150, |
| 436 |
CURLOPT_CONNECTTIMEOUT => 30, |
| 437 |
CURLOPT_SSL_VERIFYPEER => true, |
| 438 |
CURLOPT_FOLLOWLOCATION => true, |
| 439 |
CURLOPT_ENCODING => '', // Let server decide |
| 440 |
]); |
| 441 |
|
| 442 |
$success = curl_exec($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions |
| 443 |
do_action('qcld_openai_user_rate_cal', 1); |
| 444 |
if (curl_errno($ch)) { // phpcs:ignore WordPress.WP.AlternativeFunctions |
| 445 |
$err = curl_error($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions |
| 446 |
echo "data: " . wp_json_encode( array( 'error' => 'cURL error: ' . $err ) ) . "\n\n"; |
| 447 |
flush(); |
| 448 |
} |
| 449 |
|
| 450 |
curl_close($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions |
| 451 |
|
| 452 |
// Final done signal (optional but nice) |
| 453 |
echo "data: [DONE]\n\n"; |
| 454 |
flush(); |
| 455 |
} |
| 456 |
public function qcld_grok_collectionlist_callback() { |
| 457 |
$nonce = sanitize_text_field( wp_unslash($_POST['nonce']) ?? '' ); |
| 458 |
if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) { |
| 459 |
wp_send_json( |
| 460 |
array( |
| 461 |
'success' => false, |
| 462 |
'msg' => esc_html__( 'Failed in Security check', 'chatbot'), |
| 463 |
) |
| 464 |
); |
| 465 |
wp_die(); |
| 466 |
} |
| 467 |
$grok_api_key = get_option( 'qcld_grok_api_key' ); |
| 468 |
$response_raw = wp_remote_get( |
| 469 |
'https://api.x.ai/v1/collections', |
| 470 |
array( |
| 471 |
'headers' => array( |
| 472 |
'Authorization' => 'Bearer ' . $grok_api_key, |
| 473 |
'Content-Type' => 'application/json', |
| 474 |
), |
| 475 |
'timeout' => 60, |
| 476 |
) |
| 477 |
); |
| 478 |
$response = json_decode( wp_remote_retrieve_body( $response_raw ), true ); |
| 479 |
wp_send_json( $response ); |
| 480 |
} |
| 481 |
public function qcld_grok_file_upload_callback() |
| 482 |
{ |
| 483 |
|
| 484 |
$nonce = sanitize_text_field(wp_unslash($_POST['nonce'])); |
| 485 |
if (! wp_verify_nonce($nonce, 'wp_chatbot') || ! current_user_can( 'manage_options' ) ) { |
| 486 |
wp_send_json( |
| 487 |
array( |
| 488 |
'success' => false, |
| 489 |
'msg' => esc_html__('Failed in Security check', 'chatbot'), |
| 490 |
) |
| 491 |
); |
| 492 |
wp_die(); |
| 493 |
} else { |
| 494 |
// $uploadedfile = $_FILES['file']; |
| 495 |
// $collection_id = sanitize_text_field(wp_unslash($_POST['collection_id'])); |
| 496 |
// $api_key = get_option( 'qcld_grok_api_key' ); |
| 497 |
|
| 498 |
// $ch = curl_init(); |
| 499 |
|
| 500 |
// $file = new CURLFile( |
| 501 |
// $uploadedfile['tmp_name'], |
| 502 |
// $uploadedfile['type'] ?: 'application/octet-stream', |
| 503 |
// $uploadedfile['name'] |
| 504 |
// ); |
| 505 |
|
| 506 |
// $post_data = ['file' => $file]; |
| 507 |
|
| 508 |
// curl_setopt_array($ch, [ |
| 509 |
// CURLOPT_URL => 'https://api.x.ai/v1/files', |
| 510 |
// CURLOPT_POST => true, |
| 511 |
// CURLOPT_POSTFIELDS => $post_data, |
| 512 |
// CURLOPT_RETURNTRANSFER => true, |
| 513 |
// CURLOPT_HTTPHEADER => [ |
| 514 |
// 'Authorization: Bearer ' . $api_key, |
| 515 |
// // Do NOT add Content-Type here!! |
| 516 |
// ], |
| 517 |
// CURLOPT_TIMEOUT => 180, |
| 518 |
// CURLOPT_CONNECTTIMEOUT => 30, |
| 519 |
// ]); |
| 520 |
|
| 521 |
// $result = json_decode(curl_exec($ch)); |
| 522 |
// $error = curl_error($ch); |
| 523 |
// curl_close($ch); |
| 524 |
// $file_id = $result->id |
| 525 |
$collection_id = "collection_22aa88f1-80e8-4e1a-8e13-c119afb0481c"; |
| 526 |
$file_id = "file_06208083-0e27-44ff-af29-5faa211f4892"; |
| 527 |
$this->add_file_to_collection($collection_id, $file_id, []); |
| 528 |
if (! empty($res->error)) { |
| 529 |
$response['status'] = 'error'; |
| 530 |
$response['message'] = $res->error->message; |
| 531 |
} |
| 532 |
|
| 533 |
if (! empty($res->status)) { |
| 534 |
$response['status'] = 'success'; |
| 535 |
$response['message'] = 'Successfully Created file' . $res->id; |
| 536 |
} |
| 537 |
// echo wp_send_json(array($response)); |
| 538 |
// wp_die(); |
| 539 |
} |
| 540 |
} |
| 541 |
public function add_file_to_collection($collection_id, $file_id, $metadata = []) { |
| 542 |
$url = "https://management-api.x.ai/v1/collections/{$collection_id}/documents/{$file_id}"; |
| 543 |
$grok_management_api_key = get_option( 'qcld_grok_management_api_key' ); |
| 544 |
|
| 545 |
$args = [ |
| 546 |
'method' => 'POST', |
| 547 |
'timeout' => 30, |
| 548 |
'redirection' => 5, |
| 549 |
'httpversion' => '1.1', |
| 550 |
'blocking' => true, |
| 551 |
'headers' => [ |
| 552 |
'Authorization' => 'Bearer ' . $grok_management_api_key, // Management API key required |
| 553 |
'Content-Type' => 'application/json', |
| 554 |
], |
| 555 |
// 'body' => json_encode([ |
| 556 |
// 'fields' => $metadata // Optional: matches your field_definitions |
| 557 |
// ]), |
| 558 |
]; |
| 559 |
// return ['success' => false, 'error' => $response->get_error_message()]; |
| 560 |
$response = wp_remote_post( $url, $args ); |
| 561 |
$body = wp_remote_retrieve_body( $response ); |
| 562 |
$status_code = wp_remote_retrieve_response_code( $response ); |
| 563 |
if ($status_code >= 200 && $status_code < 300) { |
| 564 |
return ['success' => true, 'message' => 'File added to collection', 'body' => $body]; |
| 565 |
} else { |
| 566 |
return ['success' => false, 'status' => $status_code, 'error' => $body]; |
| 567 |
} |
| 568 |
} |
| 569 |
public function qcld_grok_remove_collection_callback() { |
| 570 |
$nonce = sanitize_text_field( wp_unslash($_POST['nonce']) ?? '' ); |
| 571 |
if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) { |
| 572 |
wp_send_json( |
| 573 |
array( |
| 574 |
'success' => false, |
| 575 |
'msg' => esc_html__( 'Failed in Security check', 'chatbot'), |
| 576 |
) |
| 577 |
); |
| 578 |
wp_die(); |
| 579 |
} |
| 580 |
$grok_api_key = get_option( 'qcld_grok_api_key' ); |
| 581 |
$collection_id = sanitize_text_field( wp_unslash($_POST['collection_id']) ?? '' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing |
| 582 |
$url = 'https://api.x.ai/v1/collections/' . $collection_id; // Check exact endpoint in docs.x.ai |
| 583 |
$args = [ |
| 584 |
'method' => 'DELETE', |
| 585 |
'headers' => [ |
| 586 |
'Authorization' => 'Bearer ' . $grok_api_key, |
| 587 |
'Content-Type' => 'application/json' |
| 588 |
], |
| 589 |
'timeout' => 60, |
| 590 |
]; |
| 591 |
$response = wp_remote_request( $url, $args ); |
| 592 |
$response_data = json_decode( wp_remote_retrieve_body( $response ), true ); |
| 593 |
wp_send_json( $response_data ); |
| 594 |
} |
| 595 |
public function qcld_grok_add_collection_callback() { |
| 596 |
$nonce = sanitize_text_field( wp_unslash($_POST['nonce']) ?? '' ); |
| 597 |
if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) { |
| 598 |
wp_send_json( |
| 599 |
array( |
| 600 |
'success' => false, |
| 601 |
'msg' => esc_html__( 'Failed in Security check', 'chatbot'), |
| 602 |
) |
| 603 |
); |
| 604 |
wp_die(); |
| 605 |
} |
| 606 |
$grok_api_key = get_option( 'qcld_grok_api_key' ); |
| 607 |
$collection_name = isset( $_POST['collection_name'] ) ? sanitize_text_field( wp_unslash( $_POST['collection_name'] ) ) : ( site_url() . wp_rand( 1, 999 ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing |
| 608 |
$response_raw = wp_remote_post( |
| 609 |
'https://api.x.ai/v1/collections', |
| 610 |
array( |
| 611 |
'headers' => array( |
| 612 |
'Authorization' => 'Bearer ' . $grok_api_key, |
| 613 |
'Content-Type' => 'application/json', |
| 614 |
), |
| 615 |
'body' => wp_json_encode( array( 'collection_name' => $collection_name, 'field_definitions' => array() ) ), |
| 616 |
'timeout' => 60, |
| 617 |
) |
| 618 |
); |
| 619 |
$response = json_decode( wp_remote_retrieve_body( $response_raw ), true ); |
| 620 |
wp_send_json( $response ); |
| 621 |
} |
| 622 |
public function direct_search_collection( $query ) { |
| 623 |
|
| 624 |
$collection_id = get_option( 'qcld_grok_collection_id' ); |
| 625 |
$grok_api_key = get_option( 'qcld_grok_api_key' ); |
| 626 |
$url = 'https://api.x.ai/v1/documents/search'; |
| 627 |
$payload = [ |
| 628 |
'query' => $query, |
| 629 |
'source' => ['collection_ids' => [$collection_id]], |
| 630 |
'retrieval_mode' => ['type' => 'hybrid'] // or 'semantic', 'keyword' |
| 631 |
]; |
| 632 |
$args = [ |
| 633 |
'method' => 'POST', |
| 634 |
'headers' => [ |
| 635 |
'Authorization' => 'Bearer ' . $grok_api_key, |
| 636 |
'Content-Type' => 'application/json', |
| 637 |
], |
| 638 |
'body' => wp_json_encode($payload), |
| 639 |
'timeout' => 30, |
| 640 |
]; |
| 641 |
$response = wp_remote_post($url, $args); |
| 642 |
$result = json_decode(wp_remote_retrieve_body($response), true); |
| 643 |
if (is_wp_error($response)) { |
| 644 |
return ['error' => $response->get_error_message()]; |
| 645 |
} |
| 646 |
foreach ($result['matches'] as $key => $match) { |
| 647 |
if ($key >= 1) break; |
| 648 |
$responses = $match ["chunk_content"]; |
| 649 |
} |
| 650 |
return $responses; |
| 651 |
} |
| 652 |
} |
| 653 |
|
| 654 |
/** |
| 655 |
* @return qcld_wpopenai_addon |
| 656 |
*/ |
| 657 |
if ( ! function_exists( 'qcld_wpgrok_addons' ) ) { |
| 658 |
|
| 659 |
function qcld_wpgrok_addons() { |
| 660 |
$qcld_wpgrok_addon = new qcld_wpgrok_addons(); |
| 661 |
return $qcld_wpgrok_addon->instance(); |
| 662 |
} |
| 663 |
} |
| 664 |
|
| 665 |
// fire off the plugin. |
| 666 |
qcld_wpgrok_addons(); |
| 667 |
|
| 668 |
} |