includes();
add_action( 'wp_ajax_claude_response', array( $this, 'claude_response_callback' ) );
add_action( 'wp_ajax_nopriv_claude_response', array( $this, 'claude_response_callback' ) );
add_action( 'wp_ajax_qcld_stream_claude', array( $this, 'qcld_stream_claude_callback' ) );
add_action( 'wp_ajax_nopriv_qcld_stream_claude', array( $this, 'qcld_stream_claude_callback' ) );
add_action( 'wp_ajax_qcld_claude_settings_option', array( $this, 'qcld_claude_settings_option_callback' ) );
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' ) ) ) {
add_action( 'admin_enqueue_scripts', array( $this, 'qcld_wb_chatbot_claude_admin_scripts' ) );
}
}
public function qcld_wb_chatbot_claude_admin_scripts() {
wp_register_script(
'qcld-wp-chatbot-claude-admin-js',
QCLD_wpCHATBOT_PLUGIN_URL . 'includes/integration/claude/assets/js/qcld-wp-claude-admin.js',
array( 'jquery' ),
QCLD_wpCHATBOT_VERSION,
true
);
wp_localize_script(
'qcld-wp-chatbot-claude-admin-js',
'ajax_object',
array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'ajax_nonce' => wp_create_nonce( 'wp_chatbot' ),
)
);
wp_enqueue_script( 'qcld-wp-chatbot-claude-admin-js' );
}
public function includes() {
require_once QCLD_wpCHATBOT_PLUGIN_DIR_PATH . 'includes/Parsedown.php';
require_once QCLD_wpCHATBOT_PLUGIN_DIR_PATH . 'includes/class-common-function.php';
}
public function qcld_claude_settings_option_callback() {
$nonce = sanitize_text_field( $_POST['nonce'] );
if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) {
wp_send_json( array( 'success' => false, 'msg' => esc_html__( 'Failed in Security check', 'chatbot' ) ) );
wp_die();
} else {
$claude_api_key = sanitize_text_field( $_POST['claude_api_key'] ?? '' );
$voyage_api_key = sanitize_text_field( $_POST['voyage_api_key'] ?? '' );
$claude_model = sanitize_text_field( $_POST['claude_model'] ?? '' );
$claude_enabled = sanitize_text_field( $_POST['claude_enabled'] );
$claude_system_content = sanitize_text_field( $_POST['claude_system_content'] ) ?? '';
$qcld_claude_page_suggestion_enabled = sanitize_text_field( $_POST['qcld_claude_page_suggestion_enabled'] );
$qcld_claude_append_content = sanitize_text_field( $_POST['qcld_claude_append_content'] ) ?? '';
$qcld_claude_prepend_content = sanitize_text_field( $_POST['qcld_claude_prepend_content'] ) ?? '';
$claude_rag_enabled = sanitize_text_field( $_POST['claude_rag_enabled'] ) ?? '';
$claude_stream_enabled = sanitize_text_field( $_POST['claude_stream_enabled'] ?? '0' );
update_option( 'qcld_claude_stream_enabled', $claude_stream_enabled );
if ( $claude_rag_enabled != '' ) { update_option( 'qcld_claude_rag_enabled', $claude_rag_enabled ); }
update_option( 'qcld_claude_api_key', $claude_api_key );
update_option( 'qcld_voyage_api_key', $voyage_api_key );
if ( $claude_model != '' ) { update_option( 'qcld_claude_model', $claude_model ); }
if ( $claude_system_content != '' ) { update_option( 'qcld_claude_system_content', $claude_system_content ); }
if ( $claude_enabled != '' ) { update_option( 'qcld_claude_enabled', $claude_enabled ); }
if ( $claude_enabled == '1' ) {
update_option( 'ai_enabled', 0 );
update_option( 'qcld_ollama_enabled', 0 );
update_option( 'qcld_openrouter_enabled', 0 );
update_option( 'qcld_gemini_enabled', 0 );
update_option( 'qcld_mistral_enabled', 0 );
update_option( 'qcld_grok_enabled', 0 );
}
update_option( 'qcld_claude_page_suggestion_enabled', $qcld_claude_page_suggestion_enabled );
if (isset($_POST['openai_post_type'])) update_option( 'qcld_openai_relevant_post', $_POST['openai_post_type'] );
update_option( 'qcld_claude_append_content', $qcld_claude_append_content );
update_option( 'qcld_claude_prepend_content', $qcld_claude_prepend_content );
}
// Test query
$claude_api_key = get_option( 'qcld_claude_api_key' );
$data = array(
'model' => get_option('qcld_claude_model') ? get_option('qcld_claude_model') : 'claude-3-5-sonnet-latest',
'max_tokens' => 100,
'messages' => array(
array('role' => 'user', 'content' => 'give a confirmation that you are Claude AI (a small response)')
)
);
$args = array(
'body' => json_encode($data),
'headers' => array(
'Content-Type' => 'application/json',
'x-api-key' => $claude_api_key,
'anthropic-version' => '2023-06-01'
),
'timeout' => 60,
'sslverify' => true,
);
$api_url = 'https://api.anthropic.com/v1/messages';
$result = wp_remote_post($api_url, $args);
$result_body = json_decode(wp_remote_retrieve_body($result), true);
if( isset($result_body['error']) ) {
wp_send_json( array( 'status' => 'error', 'msg' => esc_html( $result_body['error']['message'] ) ) );
} elseif ( isset($result_body['content']) && is_array($result_body['content']) ) {
$text_content = '';
foreach ($result_body['content'] as $block) {
if (isset($block['type']) && $block['type'] === 'text' && !empty($block['text'])) {
$text_content .= $block['text'];
}
}
if (!empty($text_content)) {
wp_send_json( array( 'status' => 'success', 'msg' => esc_html( $text_content ) ) );
}
}
wp_die();
}
public function qcld_stream_claude_callback() {
if ( get_option( 'is_rate_limiting_enabled' ) == '1' ) { do_action( 'rate_limit_checker' ); }
if ( function_exists( 'apache_setenv' ) ) { @apache_setenv( 'no-gzip', 1 ); }
@ini_set( 'zlib.output_compression', 0 );
@ini_set( 'implicit_flush', 1 );
while ( ob_get_level() ) { ob_end_clean(); }
ob_implicit_flush( 1 );
header( 'Content-Type: text/event-stream' );
header( 'Cache-Control: no-cache' );
header( 'Connection: keep-alive' );
header( 'X-Accel-Buffering: no' );
$claude_api_key = get_option( 'qcld_claude_api_key' );
$message = isset( $_POST['message'] ) ? sanitize_text_field( wp_unslash( $_POST['message'] ) ) : '';
if ( empty( $claude_api_key ) || empty( $message ) ) {
echo "data: [ERROR] Missing API key or message\n\n";
flush(); wp_die();
}
$claude_model = get_option( 'qcld_claude_model' );
if ( empty( $claude_model ) || strpos( $claude_model, 'latest' ) !== false ) { $claude_model = 'claude-sonnet-4-6'; }
$history_html = isset( $_POST['wpwHistory'] ) ? wp_unslash( $_POST['wpwHistory'] ) : '';
$history = $this->qcld_parse_wpwhistory_to_json( $history_html );
$history = $this->qcld_limit_conversation_history( $history, 10 );
$system_content = get_option( 'qcld_claude_system_content' ) ? get_option( 'qcld_claude_system_content' ) : 'You are a helpful assistant.';
$page_title = isset( $_POST['page_title'] ) ? sanitize_text_field( $_POST['page_title'] ) : '';
$system_content = str_replace( '[page_title]', $page_title, $system_content );
$rag_context = '';
if ( get_option( 'qcld_claude_rag_enabled' ) == '1' ) {
$rag_context = Qcld_Bot_Rag()->run_rag_search( $message );
}
if ( ! empty( $rag_context ) && $rag_context != 'No knowledge base found.' ) {
$system_content .= "\n\nContext from Knowledge Base:\n" . $rag_context;
}
$contents = $history;
$contents[] = [ 'role' => 'user', 'content' => $message ];
$data = [
'model' => $claude_model,
'max_tokens' => 1024,
'system' => $system_content,
'messages' => $contents,
'stream' => true
];
$api_url = 'https://api.anthropic.com/v1/messages';
// phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_init, WordPress.WP.AlternativeFunctions.curl_curl_setopt, WordPress.WP.AlternativeFunctions.curl_curl_exec, WordPress.WP.AlternativeFunctions.curl_curl_errno, WordPress.WP.AlternativeFunctions.curl_curl_error, WordPress.WP.AlternativeFunctions.curl_curl_close -- SSE streaming requires cURL write callback.
$ch = curl_init( $api_url );
curl_setopt( $ch, CURLOPT_POST, true );
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'x-api-key: ' . $claude_api_key,
'anthropic-version: 2023-06-01'
] );
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $data ) );
curl_setopt( $ch, CURLOPT_TIMEOUT, 120 );
curl_setopt( $ch, CURLOPT_WRITEFUNCTION, function( $ch, $chunk ) {
$lines = explode( "\n", $chunk );
foreach ( $lines as $line ) {
$line = trim( $line );
if ( strpos( $line, 'data: ' ) === 0 ) {
$json_str = substr( $line, 6 );
$decoded = json_decode( $json_str, true );
if ( isset($decoded['type']) && $decoded['type'] === 'message_stop' ) {
echo "data: [DONE]\n\n"; flush(); continue;
}
if ( isset($decoded['type']) && $decoded['type'] === 'content_block_delta' ) {
$text_delta = $decoded['delta']['text'];
$payload = json_encode( [ 'choices' => [ [ 'delta' => [ 'content' => $text_delta ] ] ] ] );
echo 'data: ' . $payload . "\n\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming raw JSON output
echo str_repeat( ' ', 1024 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming padding
flush();
}
}
}
return strlen( $chunk );
} );
curl_exec( $ch );
if ( curl_errno( $ch ) ) {
echo 'data: [ERROR] ' . esc_html( curl_error( $ch ) ) . "\n\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- curl_error is already escaped above
flush();
}
curl_close( $ch );
// phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_init, WordPress.WP.AlternativeFunctions.curl_curl_setopt, WordPress.WP.AlternativeFunctions.curl_curl_exec, WordPress.WP.AlternativeFunctions.curl_curl_errno, WordPress.WP.AlternativeFunctions.curl_curl_error, WordPress.WP.AlternativeFunctions.curl_curl_close
do_action( 'qcld_openai_user_rate_cal', 1 );
exit;
}
public function claude_response_callback() {
if (get_option('is_rate_limiting_enabled') == '1') { do_action('rate_limit_checker'); }
$claude_api_key = get_option( 'qcld_claude_api_key' );
$keyword = isset($_POST['keyword']) ? $_POST['keyword'] : '';
$rag_context = "";
if (get_option('qcld_claude_rag_enabled') == '1') {
$rag_context = Qcld_Bot_Rag()->run_rag_search($keyword);
}
$relevant_pagelink = Qcld_WPBot_Common_Functions::qcpd_relevant_pagelink( $keyword );
$relevant_pagelink = array_slice( $relevant_pagelink, 0, 5, true );
$relevant_pagelinks = '';
if ( ( get_option( 'qcld_claude_page_suggestion_enabled' ) == '1' ) && count( $relevant_pagelink ) > 0 ) {
$relevant_post_link = maybe_unserialize( get_option( 'qlcd_wp_chatbot_relevant_post_link_openai' ) );
if ( is_array( $relevant_post_link[ get_wpbot_locale() ] ) ) {
$relevant_pagelinks = '
' . implode( '', $relevant_post_link[ get_wpbot_locale() ] ) . '
' . implode( '', $relevant_pagelink );
} else {
$relevant_pagelinks = '
' . $relevant_post_link[ get_wpbot_locale() ] . '
' . implode( '', $relevant_pagelink ); } } $Parsedown = new Qcld_Parsedown(); $claude_model = get_option( 'qcld_claude_model' ); if ( empty( $claude_model ) || strpos( $claude_model, 'latest' ) !== false ) { $claude_model = 'claude-sonnet-4-6'; } $api_url = 'https://api.anthropic.com/v1/messages'; $page_title = isset($_POST['page_title']) ? sanitize_text_field($_POST['page_title']) : ''; $history = []; if (!empty($_POST['ai_history'])) { $parsed_history = json_decode(wp_unslash($_POST['ai_history']), true); if (is_array($parsed_history)) { foreach ($parsed_history as $h) { if (isset($h['role']) && isset($h['content'])) { $role = ($h['role'] === 'assistant') ? 'assistant' : 'user'; $history[] = [ 'role' => $role, 'content' => sanitize_text_field($h['content']) ]; } } } } elseif (!empty($_POST['wpwHistory'])) { $history_html = wp_unslash($_POST['wpwHistory']); $history = $this->qcld_parse_wpwhistory_to_json($history_html); } $history = $this->qcld_limit_conversation_history($history, 10); $system_content = get_option( 'qcld_claude_system_content' ) ? get_option( 'qcld_claude_system_content' ) : 'You are a helpful assistant.'; $system_content = str_replace('[page_title]', $page_title, $system_content); if (!empty($page_title) && strpos($system_content, $page_title) === false) { $system_content .= "\n\nThe user is currently browsing the page: \"" . $page_title . "\". Use this information to help them navigate or provide context-aware answers."; } if (!empty($rag_context) && $rag_context != "No knowledge base found.") { $system_content .= "\n\nContext from Knowledge Base:\n" . $rag_context; } $action_prompt = !empty($_POST['action_prompt']) ? wp_unslash($_POST['action_prompt']) : ''; if (!empty($action_prompt)) { $system_content .= "\n\nCRITICAL ACTIVE ACTION INSTRUCTION:\n" . $action_prompt; } $saved_ai_forms = get_option('wpbot_ai_forms', array()); if (!empty($saved_ai_forms) && is_array($saved_ai_forms)) { $system_content .= "\n\nYou must handle the following interactive forms when the user asks for them:\n"; foreach ($saved_ai_forms as $form) { $system_content .= "\nForm Title: " . $form['title'] . "\nInstructions: " . $form['prompt'] . "\n"; } $system_content .= "\n\nCRITICAL INSTRUCTIONS FOR INTERACTIVE FORMS:\n"; $system_content .= "When a user triggers an interactive form, you must act as a step-by-step data collection agent.\n"; $system_content .= "1. DO NOT ask all questions at once. Ask exactly ONE question at a time.\n"; $system_content .= "2. Wait for the user's response before asking the next question.\n"; $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"; $system_content .= "__AI_FORM_DATA__{ \"form_title\": \"