| 1 |
<?php |
| 2 |
/** |
| 3 |
* Claude 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_wpclaude_addons' ) ) { |
| 14 |
|
| 15 |
class qcld_wpclaude_addons { |
| 16 |
|
| 17 |
private $id = 'Claude AI'; |
| 18 |
public $version = '1.0.0'; |
| 19 |
public $helper; |
| 20 |
protected static $_instance = null; |
| 21 |
|
| 22 |
public static function instance() { |
| 23 |
if ( is_null( self::$_instance ) ) { |
| 24 |
self::$_instance = new self(); |
| 25 |
} |
| 26 |
return self::$_instance; |
| 27 |
} |
| 28 |
|
| 29 |
public function __construct() { |
| 30 |
$this->includes(); |
| 31 |
add_action( 'wp_ajax_claude_response', array( $this, 'claude_response_callback' ) ); |
| 32 |
add_action( 'wp_ajax_nopriv_claude_response', array( $this, 'claude_response_callback' ) ); |
| 33 |
add_action( 'wp_ajax_qcld_stream_claude', array( $this, 'qcld_stream_claude_callback' ) ); |
| 34 |
add_action( 'wp_ajax_nopriv_qcld_stream_claude', array( $this, 'qcld_stream_claude_callback' ) ); |
| 35 |
add_action( 'wp_ajax_qcld_claude_settings_option', array( $this, 'qcld_claude_settings_option_callback' ) ); |
| 36 |
|
| 37 |
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' ) ) ) { |
| 38 |
add_action( 'admin_enqueue_scripts', array( $this, 'qcld_wb_chatbot_claude_admin_scripts' ) ); |
| 39 |
} |
| 40 |
} |
| 41 |
|
| 42 |
public function qcld_wb_chatbot_claude_admin_scripts() { |
| 43 |
wp_register_script( |
| 44 |
'qcld-wp-chatbot-claude-admin-js', |
| 45 |
QCLD_wpCHATBOT_PLUGIN_URL . 'includes/integration/claude/assets/js/qcld-wp-claude-admin.js', |
| 46 |
array( 'jquery' ), |
| 47 |
QCLD_wpCHATBOT_VERSION, |
| 48 |
true |
| 49 |
); |
| 50 |
|
| 51 |
wp_localize_script( |
| 52 |
'qcld-wp-chatbot-claude-admin-js', |
| 53 |
'ajax_object', |
| 54 |
array( |
| 55 |
'ajax_url' => admin_url( 'admin-ajax.php' ), |
| 56 |
'ajax_nonce' => wp_create_nonce( 'wp_chatbot' ), |
| 57 |
) |
| 58 |
); |
| 59 |
wp_enqueue_script( 'qcld-wp-chatbot-claude-admin-js' ); |
| 60 |
} |
| 61 |
|
| 62 |
public function includes() { |
| 63 |
require_once QCLD_wpCHATBOT_PLUGIN_DIR_PATH . 'includes/Parsedown.php'; |
| 64 |
require_once QCLD_wpCHATBOT_PLUGIN_DIR_PATH . 'includes/class-common-function.php'; |
| 65 |
} |
| 66 |
|
| 67 |
public function qcld_claude_settings_option_callback() { |
| 68 |
$nonce = sanitize_text_field( $_POST['nonce'] ); |
| 69 |
if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) { |
| 70 |
wp_send_json( array( 'success' => false, 'msg' => esc_html__( 'Failed in Security check', 'chatbot' ) ) ); |
| 71 |
wp_die(); |
| 72 |
} else { |
| 73 |
$claude_api_key = sanitize_text_field( $_POST['claude_api_key'] ?? '' ); |
| 74 |
$voyage_api_key = sanitize_text_field( $_POST['voyage_api_key'] ?? '' ); |
| 75 |
$claude_model = sanitize_text_field( $_POST['claude_model'] ?? '' ); |
| 76 |
$claude_enabled = sanitize_text_field( $_POST['claude_enabled'] ); |
| 77 |
$claude_system_content = sanitize_text_field( $_POST['claude_system_content'] ) ?? ''; |
| 78 |
$qcld_claude_page_suggestion_enabled = sanitize_text_field( $_POST['qcld_claude_page_suggestion_enabled'] ); |
| 79 |
$qcld_claude_append_content = sanitize_text_field( $_POST['qcld_claude_append_content'] ) ?? ''; |
| 80 |
$qcld_claude_prepend_content = sanitize_text_field( $_POST['qcld_claude_prepend_content'] ) ?? ''; |
| 81 |
$claude_rag_enabled = sanitize_text_field( $_POST['claude_rag_enabled'] ) ?? ''; |
| 82 |
$claude_stream_enabled = sanitize_text_field( $_POST['claude_stream_enabled'] ?? '0' ); |
| 83 |
|
| 84 |
update_option( 'qcld_claude_stream_enabled', $claude_stream_enabled ); |
| 85 |
if ( $claude_rag_enabled != '' ) { update_option( 'qcld_claude_rag_enabled', $claude_rag_enabled ); } |
| 86 |
update_option( 'qcld_claude_api_key', $claude_api_key ); |
| 87 |
update_option( 'qcld_voyage_api_key', $voyage_api_key ); |
| 88 |
if ( $claude_model != '' ) { update_option( 'qcld_claude_model', $claude_model ); } |
| 89 |
if ( $claude_system_content != '' ) { update_option( 'qcld_claude_system_content', $claude_system_content ); } |
| 90 |
if ( $claude_enabled != '' ) { update_option( 'qcld_claude_enabled', $claude_enabled ); } |
| 91 |
if ( $claude_enabled == '1' ) { |
| 92 |
update_option( 'ai_enabled', 0 ); |
| 93 |
update_option( 'qcld_ollama_enabled', 0 ); |
| 94 |
update_option( 'qcld_openrouter_enabled', 0 ); |
| 95 |
update_option( 'qcld_gemini_enabled', 0 ); |
| 96 |
update_option( 'qcld_mistral_enabled', 0 ); |
| 97 |
update_option( 'qcld_grok_enabled', 0 ); |
| 98 |
} |
| 99 |
update_option( 'qcld_claude_page_suggestion_enabled', $qcld_claude_page_suggestion_enabled ); |
| 100 |
if (isset($_POST['openai_post_type'])) update_option( 'qcld_openai_relevant_post', $_POST['openai_post_type'] ); |
| 101 |
update_option( 'qcld_claude_append_content', $qcld_claude_append_content ); |
| 102 |
update_option( 'qcld_claude_prepend_content', $qcld_claude_prepend_content ); |
| 103 |
} |
| 104 |
|
| 105 |
// Test query |
| 106 |
$claude_api_key = get_option( 'qcld_claude_api_key' ); |
| 107 |
$data = array( |
| 108 |
'model' => get_option('qcld_claude_model') ? get_option('qcld_claude_model') : 'claude-3-5-sonnet-latest', |
| 109 |
'max_tokens' => 100, |
| 110 |
'messages' => array( |
| 111 |
array('role' => 'user', 'content' => 'give a confirmation that you are Claude AI (a small response)') |
| 112 |
) |
| 113 |
); |
| 114 |
|
| 115 |
$args = array( |
| 116 |
'body' => json_encode($data), |
| 117 |
'headers' => array( |
| 118 |
'Content-Type' => 'application/json', |
| 119 |
'x-api-key' => $claude_api_key, |
| 120 |
'anthropic-version' => '2023-06-01' |
| 121 |
), |
| 122 |
'timeout' => 60, |
| 123 |
'sslverify' => true, |
| 124 |
); |
| 125 |
$api_url = 'https://api.anthropic.com/v1/messages'; |
| 126 |
$result = wp_remote_post($api_url, $args); |
| 127 |
$result_body = json_decode(wp_remote_retrieve_body($result), true); |
| 128 |
|
| 129 |
if( isset($result_body['error']) ) { |
| 130 |
wp_send_json( array( 'status' => 'error', 'msg' => esc_html( $result_body['error']['message'] ) ) ); |
| 131 |
} elseif ( isset($result_body['content']) && is_array($result_body['content']) ) { |
| 132 |
$text_content = ''; |
| 133 |
foreach ($result_body['content'] as $block) { |
| 134 |
if (isset($block['type']) && $block['type'] === 'text' && !empty($block['text'])) { |
| 135 |
$text_content .= $block['text']; |
| 136 |
} |
| 137 |
} |
| 138 |
if (!empty($text_content)) { |
| 139 |
wp_send_json( array( 'status' => 'success', 'msg' => esc_html( $text_content ) ) ); |
| 140 |
} |
| 141 |
} |
| 142 |
wp_die(); |
| 143 |
} |
| 144 |
|
| 145 |
public function qcld_stream_claude_callback() { |
| 146 |
if ( get_option( 'is_rate_limiting_enabled' ) == '1' ) { do_action( 'rate_limit_checker' ); } |
| 147 |
|
| 148 |
if ( function_exists( 'apache_setenv' ) ) { @apache_setenv( 'no-gzip', 1 ); } |
| 149 |
@ini_set( 'zlib.output_compression', 0 ); |
| 150 |
@ini_set( 'implicit_flush', 1 ); |
| 151 |
while ( ob_get_level() ) { ob_end_clean(); } |
| 152 |
ob_implicit_flush( 1 ); |
| 153 |
|
| 154 |
header( 'Content-Type: text/event-stream' ); |
| 155 |
header( 'Cache-Control: no-cache' ); |
| 156 |
header( 'Connection: keep-alive' ); |
| 157 |
header( 'X-Accel-Buffering: no' ); |
| 158 |
|
| 159 |
$claude_api_key = get_option( 'qcld_claude_api_key' ); |
| 160 |
$message = isset( $_POST['message'] ) ? sanitize_text_field( wp_unslash( $_POST['message'] ) ) : ''; |
| 161 |
|
| 162 |
if ( empty( $claude_api_key ) || empty( $message ) ) { |
| 163 |
echo "data: [ERROR] Missing API key or message\n\n"; |
| 164 |
flush(); wp_die(); |
| 165 |
} |
| 166 |
|
| 167 |
$claude_model = get_option( 'qcld_claude_model' ); |
| 168 |
if ( empty( $claude_model ) || strpos( $claude_model, 'latest' ) !== false ) { $claude_model = 'claude-sonnet-4-6'; } |
| 169 |
|
| 170 |
$history_html = isset( $_POST['wpwHistory'] ) ? wp_unslash( $_POST['wpwHistory'] ) : ''; |
| 171 |
$history = $this->qcld_parse_wpwhistory_to_json( $history_html ); |
| 172 |
$history = $this->qcld_limit_conversation_history( $history, 10 ); |
| 173 |
|
| 174 |
$system_content = get_option( 'qcld_claude_system_content' ) ? get_option( 'qcld_claude_system_content' ) : 'You are a helpful assistant.'; |
| 175 |
$page_title = isset( $_POST['page_title'] ) ? sanitize_text_field( $_POST['page_title'] ) : ''; |
| 176 |
$system_content = str_replace( '[page_title]', $page_title, $system_content ); |
| 177 |
|
| 178 |
$rag_context = ''; |
| 179 |
if ( get_option( 'qcld_claude_rag_enabled' ) == '1' ) { |
| 180 |
$rag_context = Qcld_Bot_Rag()->run_rag_search( $message ); |
| 181 |
} |
| 182 |
if ( ! empty( $rag_context ) && $rag_context != 'No knowledge base found.' ) { |
| 183 |
$system_content .= "\n\nContext from Knowledge Base:\n" . $rag_context; |
| 184 |
} |
| 185 |
|
| 186 |
$contents = $history; |
| 187 |
$contents[] = [ 'role' => 'user', 'content' => $message ]; |
| 188 |
|
| 189 |
$data = [ |
| 190 |
'model' => $claude_model, |
| 191 |
'max_tokens' => 1024, |
| 192 |
'system' => $system_content, |
| 193 |
'messages' => $contents, |
| 194 |
'stream' => true |
| 195 |
]; |
| 196 |
|
| 197 |
$api_url = 'https://api.anthropic.com/v1/messages'; |
| 198 |
|
| 199 |
// 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. |
| 200 |
$ch = curl_init( $api_url ); |
| 201 |
curl_setopt( $ch, CURLOPT_POST, true ); |
| 202 |
curl_setopt( $ch, CURLOPT_HTTPHEADER, [ |
| 203 |
'Content-Type: application/json', |
| 204 |
'x-api-key: ' . $claude_api_key, |
| 205 |
'anthropic-version: 2023-06-01' |
| 206 |
] ); |
| 207 |
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $data ) ); |
| 208 |
curl_setopt( $ch, CURLOPT_TIMEOUT, 120 ); |
| 209 |
curl_setopt( $ch, CURLOPT_WRITEFUNCTION, function( $ch, $chunk ) { |
| 210 |
$lines = explode( "\n", $chunk ); |
| 211 |
foreach ( $lines as $line ) { |
| 212 |
$line = trim( $line ); |
| 213 |
if ( strpos( $line, 'data: ' ) === 0 ) { |
| 214 |
$json_str = substr( $line, 6 ); |
| 215 |
$decoded = json_decode( $json_str, true ); |
| 216 |
if ( isset($decoded['type']) && $decoded['type'] === 'message_stop' ) { |
| 217 |
echo "data: [DONE]\n\n"; flush(); continue; |
| 218 |
} |
| 219 |
if ( isset($decoded['type']) && $decoded['type'] === 'content_block_delta' ) { |
| 220 |
$text_delta = $decoded['delta']['text']; |
| 221 |
$payload = json_encode( [ 'choices' => [ [ 'delta' => [ 'content' => $text_delta ] ] ] ] ); |
| 222 |
echo 'data: ' . $payload . "\n\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming raw JSON output |
| 223 |
echo str_repeat( ' ', 1024 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming padding |
| 224 |
flush(); |
| 225 |
} |
| 226 |
} |
| 227 |
} |
| 228 |
return strlen( $chunk ); |
| 229 |
} ); |
| 230 |
|
| 231 |
curl_exec( $ch ); |
| 232 |
if ( curl_errno( $ch ) ) { |
| 233 |
echo 'data: [ERROR] ' . esc_html( curl_error( $ch ) ) . "\n\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- curl_error is already escaped above |
| 234 |
flush(); |
| 235 |
} |
| 236 |
curl_close( $ch ); |
| 237 |
// 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 |
| 238 |
do_action( 'qcld_openai_user_rate_cal', 1 ); |
| 239 |
exit; |
| 240 |
} |
| 241 |
|
| 242 |
public function claude_response_callback() { |
| 243 |
if (get_option('is_rate_limiting_enabled') == '1') { do_action('rate_limit_checker'); } |
| 244 |
$claude_api_key = get_option( 'qcld_claude_api_key' ); |
| 245 |
$keyword = isset($_POST['keyword']) ? $_POST['keyword'] : ''; |
| 246 |
|
| 247 |
$rag_context = ""; |
| 248 |
if (get_option('qcld_claude_rag_enabled') == '1') { |
| 249 |
$rag_context = Qcld_Bot_Rag()->run_rag_search($keyword); |
| 250 |
} |
| 251 |
|
| 252 |
$relevant_pagelink = Qcld_WPBot_Common_Functions::qcpd_relevant_pagelink( $keyword ); |
| 253 |
$relevant_pagelink = array_slice( $relevant_pagelink, 0, 5, true ); |
| 254 |
$relevant_pagelinks = ''; |
| 255 |
if ( ( get_option( 'qcld_claude_page_suggestion_enabled' ) == '1' ) && count( $relevant_pagelink ) > 0 ) { |
| 256 |
$relevant_post_link = maybe_unserialize( get_option( 'qlcd_wp_chatbot_relevant_post_link_openai' ) ); |
| 257 |
if ( is_array( $relevant_post_link[ get_wpbot_locale() ] ) ) { |
| 258 |
$relevant_pagelinks = '<br><br><p><em>' . implode( '', $relevant_post_link[ get_wpbot_locale() ] ) . '</em><p>' . implode( '</br>', $relevant_pagelink ); |
| 259 |
} else { |
| 260 |
$relevant_pagelinks = '<br><br><p><em>' . $relevant_post_link[ get_wpbot_locale() ] . '</em><p>' . implode( '</br>', $relevant_pagelink ); |
| 261 |
} |
| 262 |
} |
| 263 |
|
| 264 |
$Parsedown = new Qcld_Parsedown(); |
| 265 |
|
| 266 |
$claude_model = get_option( 'qcld_claude_model' ); |
| 267 |
if ( empty( $claude_model ) || strpos( $claude_model, 'latest' ) !== false ) { $claude_model = 'claude-sonnet-4-6'; } |
| 268 |
$api_url = 'https://api.anthropic.com/v1/messages'; |
| 269 |
|
| 270 |
$page_title = isset($_POST['page_title']) ? sanitize_text_field($_POST['page_title']) : ''; |
| 271 |
$history = []; |
| 272 |
if (!empty($_POST['ai_history'])) { |
| 273 |
$parsed_history = json_decode(wp_unslash($_POST['ai_history']), true); |
| 274 |
if (is_array($parsed_history)) { |
| 275 |
foreach ($parsed_history as $h) { |
| 276 |
if (isset($h['role']) && isset($h['content'])) { |
| 277 |
$role = ($h['role'] === 'assistant') ? 'assistant' : 'user'; |
| 278 |
$history[] = [ |
| 279 |
'role' => $role, |
| 280 |
'content' => sanitize_text_field($h['content']) |
| 281 |
]; |
| 282 |
} |
| 283 |
} |
| 284 |
} |
| 285 |
} elseif (!empty($_POST['wpwHistory'])) { |
| 286 |
$history_html = wp_unslash($_POST['wpwHistory']); |
| 287 |
$history = $this->qcld_parse_wpwhistory_to_json($history_html); |
| 288 |
} |
| 289 |
$history = $this->qcld_limit_conversation_history($history, 10); |
| 290 |
|
| 291 |
$system_content = get_option( 'qcld_claude_system_content' ) ? get_option( 'qcld_claude_system_content' ) : 'You are a helpful assistant.'; |
| 292 |
$system_content = str_replace('[page_title]', $page_title, $system_content); |
| 293 |
|
| 294 |
if (!empty($page_title) && strpos($system_content, $page_title) === false) { |
| 295 |
$system_content .= "\n\nThe user is currently browsing the page: \"" . $page_title . "\". Use this information to help them navigate or provide context-aware answers."; |
| 296 |
} |
| 297 |
|
| 298 |
if (!empty($rag_context) && $rag_context != "No knowledge base found.") { |
| 299 |
$system_content .= "\n\nContext from Knowledge Base:\n" . $rag_context; |
| 300 |
} |
| 301 |
|
| 302 |
$action_prompt = !empty($_POST['action_prompt']) ? wp_unslash($_POST['action_prompt']) : ''; |
| 303 |
if (!empty($action_prompt)) { |
| 304 |
$system_content .= "\n\nCRITICAL ACTIVE ACTION INSTRUCTION:\n" . $action_prompt; |
| 305 |
} |
| 306 |
|
| 307 |
$saved_ai_forms = get_option('wpbot_ai_forms', array()); |
| 308 |
if (!empty($saved_ai_forms) && is_array($saved_ai_forms)) { |
| 309 |
$system_content .= "\n\nYou must handle the following interactive forms when the user asks for them:\n"; |
| 310 |
foreach ($saved_ai_forms as $form) { |
| 311 |
$system_content .= "\nForm Title: " . $form['title'] . "\nInstructions: " . $form['prompt'] . "\n"; |
| 312 |
} |
| 313 |
$system_content .= "\n\nCRITICAL INSTRUCTIONS FOR INTERACTIVE FORMS:\n"; |
| 314 |
$system_content .= "When a user triggers an interactive form, you must act as a step-by-step data collection agent.\n"; |
| 315 |
$system_content .= "1. DO NOT ask all questions at once. Ask exactly ONE question at a time.\n"; |
| 316 |
$system_content .= "2. Wait for the user's response before asking the next question.\n"; |
| 317 |
$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"; |
| 318 |
$system_content .= "__AI_FORM_DATA__{ \"form_title\": \"<Form Title>\", \"data\": { \"Question 1\": \"Answer 1\", \"Question 2\": \"Answer 2\" } }__AI_FORM_DATA_END__\n"; |
| 319 |
$system_content .= "Do not include any other text after this JSON block once the form is complete.\n"; |
| 320 |
$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."; |
| 321 |
} |
| 322 |
|
| 323 |
$contents = $history; |
| 324 |
$last_msg = end($contents); |
| 325 |
if (!$last_msg || $last_msg['role'] !== 'user' || $last_msg['content'] !== $keyword) { |
| 326 |
$contents[] = [ 'role' => 'user', 'content' => $keyword ]; |
| 327 |
} |
| 328 |
|
| 329 |
$data = array( |
| 330 |
'model' => $claude_model, |
| 331 |
'max_tokens' => 1024, |
| 332 |
'system' => $system_content, |
| 333 |
'messages' => $contents |
| 334 |
); |
| 335 |
|
| 336 |
$max_attempts = 3; |
| 337 |
$attempt = 0; |
| 338 |
$response = ['status' => 'error', 'message' => 'Unknown error']; |
| 339 |
|
| 340 |
while ($attempt < $max_attempts) { |
| 341 |
$attempt++; |
| 342 |
$args = array( |
| 343 |
'body' => json_encode($data), |
| 344 |
'headers' => array( |
| 345 |
'Content-Type' => 'application/json', |
| 346 |
'x-api-key' => $claude_api_key, |
| 347 |
'anthropic-version' => '2023-06-01' |
| 348 |
), |
| 349 |
'timeout' => 60, |
| 350 |
'sslverify' => true, |
| 351 |
); |
| 352 |
|
| 353 |
$result = wp_remote_post($api_url, $args); |
| 354 |
|
| 355 |
if ( is_wp_error( $result ) ) { |
| 356 |
$response['status'] = 'error'; |
| 357 |
$response['message'] = 'WP_Error: ' . $result->get_error_message(); |
| 358 |
break; |
| 359 |
} |
| 360 |
|
| 361 |
$http_code = wp_remote_retrieve_response_code($result); |
| 362 |
$body = wp_remote_retrieve_body($result); |
| 363 |
$msg = json_decode($body, true); |
| 364 |
|
| 365 |
if (isset($msg['content']) && is_array($msg['content'])) { |
| 366 |
$text_content = ''; |
| 367 |
foreach ($msg['content'] as $block) { |
| 368 |
if (isset($block['type']) && $block['type'] === 'text' && !empty($block['text'])) { |
| 369 |
$text_content .= $block['text']; |
| 370 |
} |
| 371 |
} |
| 372 |
if (!empty($text_content)) { |
| 373 |
$response['status'] = 'success'; |
| 374 |
$reply_text = $Parsedown->text( $text_content ); |
| 375 |
if (strpos($reply_text, 'AI_FORM_DATA') !== false) { |
| 376 |
$reply_text = Qcld_WPBot_Common_Functions::format_and_save_ai_form_response($reply_text); |
| 377 |
$response['message'] = $reply_text; |
| 378 |
} else { |
| 379 |
$response['message'] = $reply_text . $relevant_pagelinks; |
| 380 |
} |
| 381 |
break; |
| 382 |
} |
| 383 |
} |
| 384 |
|
| 385 |
if (($http_code == 503 || $http_code == 429) && $attempt < $max_attempts) { |
| 386 |
$wait_seconds = $attempt * 2; |
| 387 |
sleep($wait_seconds); |
| 388 |
continue; |
| 389 |
} |
| 390 |
|
| 391 |
$response['status'] = 'error'; |
| 392 |
if ( isset( $msg['error']['message'] ) ) { |
| 393 |
$response['message'] = 'Claude API Error: ' . $msg['error']['message']; |
| 394 |
} else { |
| 395 |
$response['message'] = 'Invalid response format from Claude API'; |
| 396 |
} |
| 397 |
$response['raw_response'] = $body; |
| 398 |
break; |
| 399 |
} |
| 400 |
do_action('qcld_openai_user_rate_cal', 1); |
| 401 |
echo json_encode( $response ); |
| 402 |
wp_die(); |
| 403 |
} |
| 404 |
|
| 405 |
public function qcld_parse_wpwhistory_to_json($html) { |
| 406 |
if (empty($html)) return []; |
| 407 |
$messages = []; |
| 408 |
preg_match_all('/<li[^>]*>(.*?)<\/li>/is', $html, $matches); |
| 409 |
if (empty($matches[0])) return []; |
| 410 |
|
| 411 |
foreach ($matches[0] as $li_html) { |
| 412 |
$is_assistant = (strpos($li_html, 'wp-chatbot-msg') !== false && strpos($li_html, 'wp-chatbot-agent') !== false); |
| 413 |
$content = ''; |
| 414 |
if (preg_match('/<div[^>]*class=["\'][^"\']*wp-chatbot-paragraph[^"\']*["\'][^>]*>(.*?)<\/div>/is', $li_html, $p_matches)) { |
| 415 |
$content = $p_matches[1]; |
| 416 |
} else if (preg_match('/<div[^>]*class=["\'][^"\']*wpw-chatbot-user-msg[^"\']*["\'][^>]*>(.*?)<\/div>/is', $li_html, $u_matches)) { |
| 417 |
$content = $u_matches[1]; |
| 418 |
} else { |
| 419 |
$clean_li = preg_replace('/<div[^>]*class=["\'][^"\']*wp-chatbot-avatar[^"\']*["\'][^>]*>.*?<\/div>/is', '', $li_html); |
| 420 |
$clean_li = preg_replace('/<div[^>]*class=["\'][^"\']*wp-chatbot-agent[^"\']*["\'][^>]*>.*?<\/div>/is', '', $clean_li); |
| 421 |
$content = wp_strip_all_tags( $clean_li ); |
| 422 |
} |
| 423 |
|
| 424 |
$content = preg_replace('/<br\s*\/?>/i', "\n", $content); |
| 425 |
$content = preg_replace('/<div[^>]*class=["\'][^"\']*relevant-links[^"\']*["\'][^>]*>.*?<\/div>/is', '', $content); |
| 426 |
$content = preg_replace('/<span[^>]*class=["\'][^"\']*qcld-chatbot-wildcard[^"\']*["\'][^>]*>.*?<\/span>/is', '', $content); |
| 427 |
$content = trim( wp_strip_all_tags( $content ) ); |
| 428 |
$content = html_entity_decode($content, ENT_QUOTES, 'UTF-8'); |
| 429 |
|
| 430 |
if (!empty($content)) { |
| 431 |
$messages[] = [ |
| 432 |
'role' => $is_assistant ? 'assistant' : 'user', // Claude uses 'assistant' |
| 433 |
'content' => $content |
| 434 |
]; |
| 435 |
} |
| 436 |
} |
| 437 |
return $messages; |
| 438 |
} |
| 439 |
|
| 440 |
public function qcld_limit_conversation_history($messages, $max_messages = 10) { |
| 441 |
if (count($messages) > $max_messages) { |
| 442 |
return array_slice($messages, -$max_messages); |
| 443 |
} |
| 444 |
return $messages; |
| 445 |
} |
| 446 |
} |
| 447 |
|
| 448 |
if ( ! function_exists( 'qcld_wpclaude_addons' ) ) { |
| 449 |
function qcld_wpclaude_addons() { |
| 450 |
$qcld_wpclaude_addon = new qcld_wpclaude_addons(); |
| 451 |
return $qcld_wpclaude_addon->instance(); |
| 452 |
} |
| 453 |
} |
| 454 |
|
| 455 |
qcld_wpclaude_addons(); |
| 456 |
} |
| 457 |
|