PluginProbe
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services / 8.7.8
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services v8.7.8
8.7.8 8.7.7 8.7.6 8.7.5 8.7.4 8.7.3 8.7.2 8.7.1 8.7.0 8.6.9 8.6.8 8.6.7 8.6.6 8.6.5 8.6.4 8.6.2 8.6.1 8.6.0 8.5.9 8.5.8 8.5.7 8.5.6 8.5.5 8.5.4 8.5.3 All 534 releases
chatbot / includes / integration / claude / qcld-bot-claude.php

qcld-bot-claude.php in WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services 8.7.8, at includes/integration/claude/qcld-bot-claude.php

457 lines 19.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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