PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.3.2
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.3.2
3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / classes / modules / chatbot.php
ai-engine / classes / modules Last commit date
assistants.php 3 years ago chatbot-chatgpt.css 3 years ago chatbot-chatgpt.scss 3 years ago chatbot.php 3 years ago chatbot_logs.php 3 years ago
chatbot.php
715 lines
1 <?php
2
3 class Meow_MWAI_Modules_Chatbot {
4 private $core = null;
5 private $namespace = 'ai-chatbot/v1';
6
7 public function __construct() {
8 global $mwai_core;
9 $this->core = $mwai_core;
10 if ( is_admin() ) { return; }
11 add_shortcode( 'mwai_chat', array( $this, 'chat' ) );
12 add_shortcode( 'mwai_chatbot', array( $this, 'chat' ) );
13 add_shortcode( 'mwai_imagesbot', array( $this, 'imageschat' ) );
14 add_action( 'rest_api_init', array( $this, 'rest_api_init' ) );
15 if ( $this->core->get_option( 'shortcode_chat_inject' ) ) {
16 add_action( 'wp_footer', array( $this, 'inject_chat' ) );
17 }
18
19 if ( $this->core->get_option( 'shortcode_chat_syntax_highlighting' ) ) {
20 wp_enqueue_script( 'mwai_chatbot', MWAI_URL . 'vendor/highlightjs/highlight.min.js', [], '11.7', false );
21 wp_enqueue_style( 'mwai_chatbot', MWAI_URL . '/vendor/highlightjs/stackoverflow-dark.min.css', [], '11.7' );
22 }
23 if ( $this->core->get_option( 'shortcode_chat_typewriter' ) ) {
24 wp_enqueue_script( 'mwai_chatbot_typewriter', MWAI_URL . 'vendor/typewriterjs/typewriter.min.js', [], '2.0', true );
25 }
26
27 $logs = $this->core->get_option( 'shortcode_chat_logs' );
28 if ( $logs && strpos( $logs, 'file' ) !== false ) {
29 new Meow_MWAI_Modules_Chatbot_Logs();
30 }
31
32 if ( $this->core->get_option( 'shortcode_chat_styles' ) ) {
33 add_filter( 'mwai_chatbot_style', [ $this, 'apply_chat_styles' ], 10, 2 );
34 }
35
36 }
37
38 public function rest_api_init() {
39 register_rest_route( $this->namespace, '/chat', array(
40 'methods' => 'POST',
41 'callback' => array( $this, 'rest_chat' ),
42 'permission_callback' => '__return_true'
43 ) );
44 register_rest_route( $this->namespace, '/imagesbot', array(
45 'methods' => 'POST',
46 'callback' => array( $this, 'rest_imagesbot' ),
47 'permission_callback' => '__return_true'
48 ) );
49 }
50
51 public function chatgpt_style( $id ) {
52 $css = file_get_contents( MWAI_PATH . '/classes/modules/chatbot-chatgpt.css' );
53 $css = str_replace( '#mwai-chat-id', "#mwai-chat-{$id}", $css );
54 return '<style>' . $css . '</style>';
55 }
56
57 public function rest_chat( $request ) {
58 try {
59 $params = $request->get_json_params();
60 $embeddingsIndex = $params['embeddingsIndex'];
61 $query = new Meow_MWAI_QueryText( $params['prompt'], 1024 );
62 $query->injectParams( $params );
63 $takeoverAnswer = apply_filters( 'mwai_chatbot_takeover', null, $query, $params );
64 if ( !empty( $takeoverAnswer ) ) {
65 return new WP_REST_Response( [ 'success' => true, 'answer' => $takeoverAnswer,
66 'html' => $takeoverAnswer, 'usage' => null ], 200 );
67 }
68
69 // Awareness & Embeddings
70 if ( $query->mode === 'chat' && !empty( $embeddingsIndex ) ) {
71 $context = apply_filters( 'mwai_context_search', $query, $embeddingsIndex );
72 if ( !empty( $context ) ) {
73 $query->injectContext( $context );
74 }
75 }
76
77 $answer = $this->core->ai->run( $query );
78 $rawText = $answer->result;
79 $html = apply_filters( 'mwai_chatbot_reply', $rawText, $query, $params );
80 if ( $this->core->get_option( 'shortcode_chat_formatting' ) ) {
81 $html = $this->core->markdown_to_html( $html );
82 }
83 return new WP_REST_Response( [ 'success' => true, 'answer' => $rawText,
84 'html' => $html, 'usage' => $answer->usage ], 200 );
85 }
86 catch ( Exception $e ) {
87 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
88 }
89 }
90
91 public function rest_imagesbot( $request ) {
92 try {
93 $params = $request->get_json_params();
94 $query = new Meow_MWAI_QueryImage( $params['prompt'] );
95 $query->injectParams( $params );
96 $answer = $this->core->ai->run( $query );
97 return new WP_REST_Response( [ 'success' => true, 'images' => $answer->results, 'usage' => $answer->usage ], 200 );
98 }
99 catch ( Exception $e ) {
100 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
101 }
102 }
103
104 public function apply_chat_styles( $css, $chatbotId ) {
105 $chatStyles = $this->core->get_option( 'shortcode_chat_styles' );
106 return preg_replace_callback( '/--mwai-(\w+):\s*([^;]+);/', function ( $matches ) use ( $chatStyles ) {
107 if ( isset( $chatStyles[$matches[1]] ) ) {
108 return '--mwai-' . $matches[1] . ': ' . $chatStyles[$matches[1]] . ';';
109 }
110 return $matches[0];
111 }, $css );
112 }
113
114 public function inject_chat() {
115 $params = $this->core->get_option( 'shortcode_chat_params' );
116 echo $this->chat( $params );
117 }
118
119 public function imageschat( $atts ) {
120 $atts['mode'] = 'images';
121 return $this->chat( $atts );
122 }
123
124 public function getCurrentUser() {
125 if ( is_user_logged_in() ) {
126 return wp_get_current_user();
127 }
128 return null;
129 }
130
131 public function handlePlaceholders( $data, $guestName = 'Guest: ' ) {
132 if ( strpos( $data, '{' ) === false ) {
133 return $data;
134 }
135 $placeholders_meta = [ '{FIRST_NAME}', '{LAST_NAME}' ];
136 $placeholders_data = [ '{USER_LOGIN}', '{DISPLAY_NAME}' ];
137 $user = $this->getCurrentUser();
138 if ( $user ) {
139 foreach ( $placeholders_meta as $placeholder ) {
140 if ( strpos( $data, $placeholder ) === false ) { continue; }
141 $lcPlaceholder = substr( strtolower( $placeholder ), 1, -1 );
142 $value = get_user_meta( $user->ID, $lcPlaceholder, true );
143 $data = str_replace( $placeholder, $value, $data );
144 }
145 foreach ( $placeholders_data as $placeholder ) {
146 if ( strpos( $data, $placeholder ) === false ) { continue; }
147 $lcPlaceholder = substr( strtolower( $placeholder ), 1, -1 );
148 $value = $user->data->$lcPlaceholder;
149 $data = str_replace( $placeholder, $value, $data );
150 }
151 if ( !empty( $data ) ) {
152 return $data;
153 }
154 }
155 return $guestName;
156 }
157
158 public function formatUserName( $userName, $guestName = 'Guest: ' ) {
159 // Default avatar
160 if ( empty( $userName ) ) {
161 $user = $this->getCurrentUser();
162 if ( $user ) {
163 // Gravatar
164 $userName = '<div class="mwai-avatar"><img src="' . get_avatar_url( $user->user_email ) . '" /></div>';
165 }
166 else {
167 // Default avatar
168 $userName = '<div class="mwai-avatar mwai-svg"><img src="' . MWAI_URL . '/images/avatar-user.svg" /></div>';
169 }
170 }
171 // Custom avatar
172 else if ( $this->core->isUrl( $userName ) ) {
173 $userName = '<div class="mwai-avatar"><img src="' . $userName . '" /></div>';
174 }
175 // Placeholders
176 else {
177 $userName = $this->handlePlaceholders( $userName, $guestName );
178 $userName = '<div class="mwai-name-text">' . $userName . '</div>';
179 }
180 return $userName;
181 }
182
183 public function formatAiName( $aiName ) {
184 // Default avatar
185 if ( empty( $aiName ) ) {
186 $aiName = '<div class="mwai-avatar mwai-svg"><img src="' . MWAI_URL . '/images/avatar-ai.svg" /></div>';
187 }
188 // Custom avatar
189 else if ( $this->core->isUrl( $aiName ) ) {
190 $aiName = '<div class="mwai-avatar"><img src="' . $aiName . '" /></div>';
191 }
192 else {
193 $aiName = '<div class="mwai-name-text">' . $aiName . '</div>';
194 }
195 return $aiName;
196 }
197
198 public function formatRawName( $aiName ) {
199 return 'AI: ';
200 }
201
202 public function formatRawUserName( $userName, $guestName ) {
203 return 'User: ';
204 }
205
206 public function chat( $atts ) {
207 // Use the core default parameters, or the user default parameters
208 $override = $this->core->get_option( 'shortcode_chat_params_override' );
209 $defaults_params = $override ? $this->core->get_option( 'shortcode_chat_params' ) :
210 $this->core->get_option( 'shortcode_chat_default_params' );
211
212 // Give a chance to modify the default parameters one last time
213 $defaults = apply_filters( 'mwai_chatbot_params_defaults', $defaults_params );
214
215 // Make sure all the mandatory params are set
216 foreach ( $this->core->defaultChatbotParams as $key => $value ) {
217 if ( !isset( $defaults[$key] ) ) {
218 $defaults[$key] = $value;
219 }
220 }
221
222 // Override with the shortcode, and before/after filters
223 $atts = apply_filters( 'mwai_chatbot_params_before', $atts );
224 $atts = shortcode_atts( $defaults, $atts );
225 $atts = apply_filters( 'mwai_chatbot_params', $atts );
226
227 // UI Parameters
228 $aiName = addslashes( trim( $atts['ai_name'] ) );
229 $userName = addslashes( trim( $atts['user_name'] ) );
230 $guestName = addslashes( trim( $atts['guest_name'] ) );
231 $sysName = addslashes( trim( $atts['sys_name'] ) );
232 $context = addslashes( $atts['context'] );
233 $context = preg_replace( '/\v+/', "\\n", $context );
234 $textSend = addslashes( trim( $atts['text_send'] ) );
235 $textClear = addslashes( trim( $atts['text_clear'] ) );
236 $textInputPlaceholder = addslashes( trim( $atts['text_input_placeholder'] ) );
237 $textCompliance = addslashes( trim( $atts['text_compliance'] ) );
238 $startSentence = addslashes( trim( $atts['start_sentence'] ) );
239 $window = filter_var( $atts['window'], FILTER_VALIDATE_BOOLEAN );
240 $fullscreen = filter_var( $atts['fullscreen'], FILTER_VALIDATE_BOOLEAN );
241 $icon = isset( $atts['icon'] ) ? addslashes( trim( $atts['icon'] ) ) : '';
242 $iconText = trim( $atts['icon_text'] );
243 $iconAlt = addslashes( trim( $atts['icon_alt'] ) );
244 $iconPosition = addslashes( trim( $atts['icon_position'] ) );
245 $style = $atts['style'];
246
247 // Validade & Enhance UI Parameters
248 $aiName = $this->formatAiName( $aiName );
249 $userName = $this->formatUserName( $userName, $guestName );
250 $rawAiName = $this->formatRawName( $aiName );
251 $rawUserName = $this->formatRawUserName( $userName, $guestName );
252
253 // Chatbot System Parameters
254 $id = empty( $atts['id'] ) ? uniqid() : $atts['id'];
255 $typewriter = $this->core->get_option( 'shortcode_chat_typewriter' );
256 $memorizeChat = !empty( $atts['id'] );
257 $id = preg_replace( '/[^a-zA-Z0-9]/', '', $id );
258 $env = $atts['env'];
259 $mode = $atts['mode'];
260 $maxResults = $mode === 'chat' ? 1 : $atts['max_results'];
261 $maxSentences = !empty( $atts['max_sentences'] ) ? intval( $atts['max_sentences'] ) : 1;
262 $sessionId = $this->core->get_session_id();
263 $rest_nonce = wp_create_nonce( 'wp_rest' );
264 $casuallyFineTuned = boolval( $atts['casually_fine_tuned'] );
265 $embeddingsIndex = $atts['embeddings_index'];
266 $promptEnding = addslashes( trim( $atts['prompt_ending'] ) );
267 $completionEnding = addslashes( trim( $atts['completion_ending'] ) );
268 if ( $casuallyFineTuned ) {
269 $promptEnding = "\\n\\n###\\n\\n";
270 $completionEnding = "\\n\\n";
271 }
272 $debugMode = $this->core->get_option( 'debug_mode' );
273
274 // OpenAI Parameters
275 $model = $atts['model'];
276 $temperature = $atts['temperature'];
277 $maxTokens = $atts['max_tokens'];
278 $apiKey = $atts['api_key'];
279
280 // Variables
281 $apiUrl = get_rest_url( null, $mode === 'images' ? 'ai-chatbot/v1/imagesbot' : 'ai-chatbot/v1/chat' );
282 $baseClasses = 'mwai-chat';
283 $baseClasses .= ( $window ? ' mwai-window' : '' );
284 $baseClasses .= ( !$window && $fullscreen ? ' mwai-fullscreen' : '' );
285 $baseClasses .= ( $style === 'chatgpt' ? ' mwai-chatgpt' : '' );
286 $baseClasses .= ( $window && !empty( $iconPosition ) ? ( ' mwai-' . $iconPosition ) : '' );
287
288 // Output CSS
289 ob_start();
290 $style_content = '';
291 if ( $style === 'chatgpt' ) {
292 $style_content = $this->chatgpt_style( $id, $style );
293 }
294 $style_content = apply_filters( 'mwai_chatbot_style', $style_content, $id );
295 echo wp_kses( $style_content, array( 'style' => array() ) );
296
297 // Output HTML & CSS
298 $chatStyles = $this->core->get_option( 'shortcode_chat_styles' );
299 $iconUrl = MWAI_URL . '/images/chat-green.svg';
300 if ( !empty( $icon ) ) {
301 $iconUrl = $icon;
302 }
303 else if ( !empty( $chatStyles ) && isset( $chatStyles['icon'] ) ) {
304 $url = $chatStyles['icon'];
305 $iconUrl = $this->core->isUrl( $url ) ? $url : ( MWAI_URL . 'images/' . $chatStyles['icon'] );
306 }
307 ?>
308 <div id="mwai-chat-<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $baseClasses ); ?>">
309 <?php if ( $window ) : ?>
310 <div class="mwai-open-button">
311 <?php if ( !empty( $iconText ) ) : ?>
312 <div class="mwai-icon-text"><?php echo esc_html( $iconText ); ?></div>
313 <?php endif; ?>
314 <img width="64" height="64" alt="<?php echo esc_attr( $iconAlt ); ?>" src="<?php echo esc_url( $iconUrl ); ?>" />
315 </div>
316 <div class="mwai-header">
317 <?php if ( $fullscreen ) : ?>
318 <div class="mwai-resize-button"></div>
319 <?php endif; ?>
320 <div class="mwai-close-button"></div>
321 </div>
322 <?php endif; ?>
323 <div class="mwai-content">
324 <div class="mwai-conversation">
325 </div>
326 <div class="mwai-input">
327 <textarea rows="1" placeholder="<?php echo esc_attr( $textInputPlaceholder ); ?>"></textarea>
328 <button><span><?php echo esc_html( $textSend ); ?></span></button>
329 </div>
330 <?php if ( !empty( $textCompliance ) ) : ?>
331 <div class="mwai-compliance">
332 ⚠️ <?php echo esc_html( $textCompliance ); ?>
333 </div>
334 <?php endif; ?>
335 </div>
336 </div>
337
338 <script>
339 (function () {
340 let isMobile = window.matchMedia( "only screen and (max-width: 760px)" ).matches;
341 let isWindow = <?php echo $window ? 'true' : 'false' ?>;
342 let isDebugMode = <?php echo $debugMode ? 'true' : 'false' ?>;
343 let isFullscreen = <?php echo $fullscreen ? 'true' : 'false' ?>;
344 let restNonce = '<?php echo esc_attr( $rest_nonce ) ?>';
345 let apiURL = '<?php echo esc_url( $apiUrl ) ?>';
346 let isCasuallyFineTuned = <?php echo $casuallyFineTuned ? 'true' : 'false' ?>;
347 let textCompliance = '<?php echo wp_kses_post( $textCompliance ) ?>';
348 let rawUserName = '<?php echo esc_attr( $rawUserName ) ?>';
349 let rawAiName = '<?php echo esc_attr( $rawAiName ) ?>';
350 let userName = '<?php echo wp_kses_post( $userName ) ?>';
351 let aiName = '<?php echo wp_kses_post( $aiName ) ?>';
352 let sysName = '<?php echo wp_kses_post( $sysName ) ?>';
353 let env = '<?php echo esc_attr( $env ) ?>';
354 let apiKey = '<?php echo esc_attr( $apiKey ) ?>';
355 let session = '<?php echo esc_attr( $sessionId ) ?>';
356 let mode = '<?php echo esc_attr( $mode ) ?>';
357 let model = '<?php echo esc_attr( $model ) ?>';
358 let context = isCasuallyFineTuned ? null : '<?php echo esc_attr( $context ) ?>';
359 let embeddingsIndex = '<?php echo esc_attr( $embeddingsIndex ) ?>';
360 let promptEnding = '<?php echo esc_attr( $promptEnding ) ?>';
361 let stop = '<?php echo esc_attr( $completionEnding ) ?>';
362 let startSentence = '<?php echo esc_attr( $startSentence ) ?>';
363 let maxSentences = <?php echo (int)$maxSentences ?>;
364 let memorizeChat = <?php echo $memorizeChat ? 'true' : 'false' ?>;
365 let maxTokens = <?php echo (int)$maxTokens ?>;
366 let maxResults = <?php echo (int)$maxResults ?>;
367 let temperature = <?php echo (int)$temperature ?>;
368 let memorizedChat = [];
369 let typewriter = <?php echo $typewriter ? 'true' : 'false' ?>;
370
371 if (isDebugMode) {
372 window.mwai_<?php echo esc_attr( $id ) ?> = {
373 memorizedChat: memorizedChat,
374 parameters: { mode: mode, model, temperature, maxTokens, context: context, startSentence,
375 textCompliance, isMobile, isWindow, isFullscreen, isCasuallyFineTuned, memorizeChat, maxSentences,
376 rawUserName, rawAiName, embeddingsIndex, typewriter, maxResults, userName, aiName, env, apiKey, session
377 }
378 };
379 }
380
381 // Set button text
382 function setButtonText() {
383 let input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
384 let button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
385 let buttonSpan = button.querySelector('span');
386 if (memorizedChat.length < 2) {
387 buttonSpan.innerHTML = '<?php echo esc_html( $textSend ); ?>';
388 }
389 else if (!input.value.length) {
390 button.classList.add('mwai-clear');
391 buttonSpan.innerHTML = '<?php echo esc_html( $textClear ); ?>';
392 }
393 else {
394 button.classList.remove('mwai-clear');
395 buttonSpan.innerHTML = '<?php echo esc_html( $textSend ); ?>';
396 }
397 }
398
399 // Inject timer
400 function injectTimer(element) {
401 let intervalId;
402 let startTime = new Date();
403 let timerElement = null;
404
405 function updateTimer() {
406 let now = new Date();
407 let timer = Math.floor((now - startTime) / 1000);
408 if (!timerElement) {
409 if (timer > 0.5) {
410 timerElement = document.createElement('div');
411 timerElement.classList.add('mwai-timer');
412 element.appendChild(timerElement);
413 }
414 }
415 if (timerElement) {
416 let minutes = Math.floor(timer / 60);
417 let seconds = timer - (minutes * 60);
418 seconds = seconds < 10 ? '0' + seconds : seconds;
419 let display = minutes + ':' + seconds;
420 timerElement.innerHTML = display;
421 }
422 }
423
424 intervalId = setInterval(updateTimer, 500);
425
426 return function stopTimer() {
427 clearInterval(intervalId);
428 if (timerElement) {
429 timerElement.remove();
430 }
431 };
432 }
433
434 // Push the reply in the conversation
435 function addReply(text, role = 'user', replay = false) {
436 var conversation = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-conversation');
437
438 if (memorizeChat) {
439 localStorage.setItem('mwai-chat-<?php echo esc_attr( $id ) ?>', JSON.stringify(memorizedChat));
440 }
441
442 // If text is array, then it's image URLs. Let's create a simple gallery in HTML in $text.
443 if (Array.isArray(text)) {
444 var newText = '<div class="mwai-gallery">';
445 for (var i = 0; i < text.length; i++) {
446 newText += '<a href="' + text[i] + '" target="_blank"><img src="' + text[i] + '" />';
447 }
448 text = newText + '</div>';
449 }
450
451 var mwaiClasses = ['mwai-reply'];
452 if (role === 'assistant') {
453 mwaiClasses.push('mwai-ai');
454 }
455 else if (role === 'system') {
456 mwaiClasses.push('mwai-system');
457 }
458 else {
459 mwaiClasses.push('mwai-user');
460 }
461 var div = document.createElement('div');
462 div.classList.add(...mwaiClasses);
463 var nameSpan = document.createElement('span');
464 nameSpan.classList.add('mwai-name');
465 if (role === 'assistant') {
466 nameSpan.innerHTML = aiName;
467 }
468 else if (role === 'system') {
469 nameSpan.innerHTML = sysName;
470 }
471 else {
472 nameSpan.innerHTML = userName;
473 }
474 var textSpan = document.createElement('span');
475 textSpan.classList.add('mwai-text');
476 textSpan.innerHTML = text;
477 div.appendChild(nameSpan);
478 div.appendChild(textSpan);
479 conversation.appendChild(div);
480
481 if (typewriter) {
482 if (role === 'assistant' && text !== startSentence && !replay) {
483 let typewriter = new Typewriter(textSpan, {
484 deleteSpeed: 50, delay: 25, loop: false, cursor: '', autoStart: true,
485 wrapperClassName: 'mwai-typewriter',
486 });
487 typewriter.typeString(text).start().callFunction((state) => {
488 state.elements.cursor.setAttribute('hidden', 'hidden');
489 typewriter.stop();
490 });
491 }
492 }
493
494 conversation.scrollTop = conversation.scrollHeight;
495 setButtonText();
496
497 // Syntax coloring
498 if (typeof hljs !== 'undefined') {
499 document.querySelectorAll('pre code').forEach((el) => {
500 hljs.highlightElement(el);
501 });
502 }
503 }
504
505 function buildPrompt(last = 15) {
506 let prompt = context ? (context + '\n\n') : '';
507 memorizedChat = memorizedChat.slice(-last);
508
509 // Casually fine tuned, let's use the last question
510 if (isCasuallyFineTuned) {
511 let lastLine = memorizedChat[memorizedChat.length - 1];
512 prompt = lastLine.content + promptEnding;
513 return prompt;
514 }
515
516 // Otherwise let's compile the latest conversation
517 let conversation = memorizedChat.map(x => x.who + x.content);
518 prompt += conversation.join('\n');
519 prompt += '\n' + rawAiName;
520 return prompt;
521 }
522
523 // Function to request the completion
524 function onSendClick() {
525 let input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
526 let inputText = input.value.trim();
527
528 // Reset the conversation if empty
529 if (inputText === '') {
530 document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-conversation').innerHTML = '';
531 localStorage.removeItem('mwai-chat-<?php echo esc_attr( $id ) ?>');
532 memorizedChat = [];
533 memorizedChat.push({ role: 'assistant', content: startSentence, who: rawAiName, html: startSentence });
534 addReply(startSentence, 'assistant');
535 return;
536 }
537
538 // Disable the button
539 var button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
540 button.disabled = true;
541
542 // Add the user reply
543 memorizedChat.push({ role: 'user', content: inputText, who: rawUserName, html: inputText });
544 addReply(inputText, 'user');
545 input.value = '';
546 input.setAttribute('rows', 1);
547 input.disabled = true;
548
549 let prompt = buildPrompt(maxSentences);
550
551 const data = mode === 'images' ? {
552 env, session: session,
553 prompt: inputText, rawInput: inputText,
554 model: model, maxResults, apiKey: apiKey,
555 } : {
556 env, session: session,
557 prompt: prompt, context: context,
558 messages: memorizedChat, rawInput: inputText,
559 userName: userName, aiName: aiName,
560 model: model, temperature: temperature, maxTokens: maxTokens, maxResults: 1, apiKey: apiKey,
561 embeddingsIndex: embeddingsIndex, stop: stop,
562 };
563
564 // Start the timer
565 const stopTimer = injectTimer(button);
566
567 // Send the request
568 if (isDebugMode) {
569 console.log('[BOT] Sent: ', data);
570 }
571 fetch(apiURL, { method: 'POST', headers: {
572 'Content-Type': 'application/json',
573 'X-WP-Nonce': restNonce,
574 },
575 body: JSON.stringify(data)
576 })
577 .then(response => response.json())
578 .then(data => {
579 if (isDebugMode) {
580 console.log('[BOT] Recv: ', data);
581 }
582 if (!data.success) {
583 addReply(data.message, 'system');
584 }
585 else {
586 let html = data.images ? data.images : data.html;
587 memorizedChat.push({ role: 'assistant', content: data.answer, who: rawAiName, html: html });
588 addReply(html, 'assistant');
589 }
590 button.disabled = false;
591 input.disabled = false;
592 stopTimer();
593
594 // Only focus only on desktop (to avoid the mobile keyboard to kick-in)
595 if (!isMobile) {
596 input.focus();
597 }
598 })
599 .catch(error => {
600 console.error(error);
601 button.disabled = false;
602 input.disabled = false;
603 stopTimer();
604 });
605 }
606
607 // Keep the textarea height in sync with the content
608 function resizeTextArea(ev) {
609 ev.target.style.height = 'auto';
610 ev.target.style.height = ev.target.scrollHeight + 'px';
611 }
612
613 // Keep the textarea height in sync with the content
614 function delayedResizeTextArea(ev) {
615 window.setTimeout(resizeTextArea, 0, event);
616 }
617
618 // Init the chatbot
619 function initMeowChatbot() {
620 var input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
621 var button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
622
623 input.addEventListener('keypress', (event) => {
624 let text = event.target.value;
625 if (event.keyCode === 13 && !text.length && !event.shiftKey) {
626 event.preventDefault();
627 return;
628 }
629 if (event.keyCode === 13 && text.length && !event.shiftKey) {
630 onSendClick();
631 }
632 });
633 input.addEventListener('keydown', (event) => {
634 var rows = input.getAttribute('rows');
635 if (event.keyCode === 13 && event.shiftKey) {
636 var lines = input.value.split('\n').length + 1;
637 //mwaiSetTextAreaHeight(input, lines);
638 }
639 });
640 input.addEventListener('keyup', (event) => {
641 var rows = input.getAttribute('rows');
642 var lines = input.value.split('\n').length ;
643 //mwaiSetTextAreaHeight(input, lines);
644 setButtonText();
645 });
646
647 input.addEventListener('change', resizeTextArea, false);
648 input.addEventListener('cut', delayedResizeTextArea, false);
649 input.addEventListener('paste', delayedResizeTextArea, false);
650 input.addEventListener('drop', delayedResizeTextArea, false);
651 input.addEventListener('keydown', delayedResizeTextArea, false);
652
653 button.addEventListener('click', (event) => {
654 onSendClick();
655 });
656
657 // If window, add event listener to mwai-open-button and mwai-close-button
658 if ( isWindow ) {
659 var openButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-open-button');
660 openButton.addEventListener('click', (event) => {
661 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
662 chat.classList.add('mwai-open');
663 // Only focus only on desktop (to avoid the mobile keyboard to kick-in)
664 if (!isMobile) {
665 input.focus();
666 }
667 });
668 var closeButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-close-button');
669 closeButton.addEventListener('click', (event) => {
670 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
671 chat.classList.remove('mwai-open');
672 });
673 if (isFullscreen) {
674 var resizeButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-resize-button');
675 resizeButton.addEventListener('click', (event) => {
676 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
677 chat.classList.toggle('mwai-fullscreen');
678 });
679 }
680 }
681
682 // Get back the previous chat if any for the same ID
683 var chatHistory = [];
684 if (memorizeChat) {
685 chatHistory = localStorage.getItem('mwai-chat-<?php echo esc_attr( $id ) ?>');
686 if (chatHistory) {
687 memorizedChat = JSON.parse(chatHistory);
688 memorizedChat = memorizedChat.filter(x => x && x.html && x.role);
689 memorizedChat.forEach(x => {
690 addReply(x.html, x.role, true);
691 });
692 }
693 else {
694 memorizedChat = [];
695 }
696 }
697 if (memorizedChat.length === 0) {
698 memorizedChat.push({ role: 'assistant', content: startSentence, who: rawAiName, html: startSentence });
699 addReply(startSentence, 'assistant');
700 }
701 }
702
703 // Let's go totally meoooow on this!
704 initMeowChatbot();
705 })();
706 </script>
707
708 <?php
709 $output = ob_get_contents();
710 ob_end_clean();
711 $output = apply_filters( 'mwai_chatbot', $output, $atts );
712 return $output;
713 }
714 }
715