PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.3.1
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.3.1
3.5.8 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
718 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 function rest_api_init() {
39 try {
40 register_rest_route( $this->namespace, '/chat', array(
41 'methods' => 'POST',
42 'callback' => array( $this, 'rest_chat' ),
43 'permission_callback' => '__return_true'
44 ) );
45 register_rest_route( $this->namespace, '/imagesbot', array(
46 'methods' => 'POST',
47 'callback' => array( $this, 'rest_imagesbot' ),
48 'permission_callback' => '__return_true'
49 ) );
50 }
51 catch ( Exception $e ) {
52 var_dump( $e );
53 }
54 }
55
56 function chatgpt_style( $id ) {
57 $css = file_get_contents( MWAI_PATH . '/classes/modules/chatbot-chatgpt.css' );
58 $css = str_replace( '#mwai-chat-id', "#mwai-chat-{$id}", $css );
59 return "<style>" . $css . "</style>";
60 }
61
62 function rest_chat( $request ) {
63 try {
64 $params = $request->get_json_params();
65 $embeddingsIndex = $params['embeddingsIndex'];
66 $query = new Meow_MWAI_QueryText( $params['prompt'], 1024 );
67 $query->injectParams( $params );
68 $takeoverAnswer = apply_filters( 'mwai_chatbot_takeover', null, $query, $params );
69 if ( !empty( $takeoverAnswer ) ) {
70 return new WP_REST_Response([ 'success' => true, 'answer' => $takeoverAnswer,
71 'html' => $takeoverAnswer, 'usage' => null ], 200 );
72 }
73
74 // Awareness & Embeddings
75 if ( $query->mode === 'chat' && !empty( $embeddingsIndex ) ) {
76 $context = apply_filters( 'mwai_context_search', $query, $embeddingsIndex );
77 if ( !empty( $context ) ) {
78 $query->injectContext( $context );
79 }
80 }
81
82 $answer = $this->core->ai->run( $query );
83 $rawText = $answer->result;
84 $html = apply_filters( 'mwai_chatbot_reply', $rawText, $query, $params );
85 if ( $this->core->get_option( 'shortcode_chat_formatting' ) ) {
86 $html = $this->core->markdown_to_html( $html );
87 }
88 return new WP_REST_Response([ 'success' => true, 'answer' => $rawText,
89 'html' => $html, 'usage' => $answer->usage ], 200 );
90 }
91 catch ( Exception $e ) {
92 return new WP_REST_Response([ 'success' => false, 'message' => $e->getMessage() ], 500 );
93 }
94 }
95
96 function rest_imagesbot( $request ) {
97 try {
98 $params = $request->get_json_params();
99 $query = new Meow_MWAI_QueryImage( $params['prompt'] );
100 $query->injectParams( $params );
101 $answer = $this->core->ai->run( $query );
102 return new WP_REST_Response([ 'success' => true, 'images' => $answer->results, 'usage' => $answer->usage ], 200 );
103 }
104 catch ( Exception $e ) {
105 return new WP_REST_Response([ 'success' => false, 'message' => $e->getMessage() ], 500 );
106 }
107 }
108
109 function apply_chat_styles( $css, $chatbotId ) {
110 $chatStyles = $this->core->get_option( 'shortcode_chat_styles' );
111 return preg_replace_callback( '/--mwai-(\w+):\s*([^;]+);/', function ( $matches ) use ($chatStyles ) {
112 if( isset( $chatStyles[$matches[1]] ) ) {
113 return "--mwai-" . $matches[1] . ": " . $chatStyles[$matches[1]] . ";";
114 }
115 return $matches[0];
116 }, $css );
117 }
118
119 function inject_chat() {
120 $params = $this->core->get_option( 'shortcode_chat_params' );
121 echo $this->chat( $params );
122 }
123
124 function imageschat( $atts ) {
125 $atts['mode'] = 'images';
126 return $this->chat( $atts );
127 }
128
129 function getCurrentUser() {
130 if ( is_user_logged_in() ) {
131 return wp_get_current_user();
132 }
133 return null;
134 }
135
136 function handlePlaceholders( $data, $guestName = "Guest: " ) {
137 if ( strpos( $data, '{' ) === false ) {
138 return $data;
139 }
140 $placeholders_meta = [ '{FIRST_NAME}', '{LAST_NAME}' ];
141 $placeholders_data = [ '{USER_LOGIN}', '{DISPLAY_NAME}' ];
142 $user = $this->getCurrentUser();
143 if ( $user ) {
144 foreach ( $placeholders_meta as $placeholder ) {
145 if ( strpos( $data, $placeholder ) === false ) { continue; }
146 $lcPlaceholder = substr( strtolower( $placeholder ), 1, -1 );
147 $value = get_user_meta( $user->ID, $lcPlaceholder, true );
148 $data = str_replace( $placeholder, $value, $data );
149 }
150 foreach ( $placeholders_data as $placeholder ) {
151 if ( strpos( $data, $placeholder ) === false ) { continue; }
152 $lcPlaceholder = substr( strtolower( $placeholder ), 1, -1 );
153 $value = $user->data->$lcPlaceholder;
154 $data = str_replace( $placeholder, $value, $data );
155 }
156 if ( !empty( $data ) ) {
157 return $data;
158 }
159 }
160 return $guestName;
161 }
162
163 function formatUserName( $userName, $guestName = "Guest: " ) {
164 // Default avatar
165 if ( empty( $userName ) ) {
166 $user = $this->getCurrentUser();
167 if ( $user ) {
168 // Gravatar
169 $userName = '<div class="mwai-avatar"><img src="' . get_avatar_url( $user->user_email ) . '" /></div>';
170 }
171 else {
172 // Default avatar
173 $userName = '<div class="mwai-avatar mwai-svg"><img src="' . MWAI_URL . '/images/avatar-user.svg" /></div>';
174 }
175 }
176 // Custom avatar
177 else if ( $this->core->isUrl( $userName ) ) {
178 $userName = '<div class="mwai-avatar"><img src="' . $userName . '" /></div>';
179 }
180 // Placeholders
181 else {
182 $userName = $this->handlePlaceholders( $userName, $guestName );
183 $userName = '<div class="mwai-name-text">' . $userName . '</div>';
184 }
185 return $userName;
186 }
187
188 function formatAiName( $aiName ) {
189 // Default avatar
190 if ( empty( $aiName ) ) {
191 $aiName = '<div class="mwai-avatar mwai-svg"><img src="' . MWAI_URL . '/images/avatar-ai.svg" /></div>';
192 }
193 // Custom avatar
194 else if ( $this->core->isUrl( $aiName ) ) {
195 $aiName = '<div class="mwai-avatar"><img src="' . $aiName . '" /></div>';
196 }
197 else {
198 $aiName = '<div class="mwai-name-text">' . $aiName . '</div>';
199 }
200 return $aiName;
201 }
202
203 function formatRawName( $aiName ) {
204 return "AI: ";
205 }
206
207 function formatRawUserName( $userName, $guestName ) {
208 return "User: ";
209 }
210
211 function chat( $atts ) {
212 // Use the core default parameters, or the user default parameters
213 $override = $this->core->get_option( 'shortcode_chat_params_override' );
214 $defaults_params = $override ? $this->core->get_option( 'shortcode_chat_params' ) :
215 $this->core->get_option( 'shortcode_chat_default_params' );
216
217 // Give a chance to modify the default parameters one last time
218 $defaults = apply_filters( 'mwai_chatbot_params_defaults', $defaults_params );
219
220 // Make sure all the mandatory params are set
221 foreach ( $this->core->defaultChatbotParams as $key => $value ) {
222 if ( !isset( $defaults[$key] ) ) {
223 $defaults[$key] = $value;
224 }
225 }
226
227 // Override with the shortcode, and before/after filters
228 $atts = apply_filters( 'mwai_chatbot_params_before', $atts );
229 $atts = shortcode_atts( $defaults, $atts );
230 $atts = apply_filters( 'mwai_chatbot_params', $atts );
231
232 // UI Parameters
233 $aiName = addslashes( trim($atts['ai_name']) );
234 $userName = addslashes( trim($atts['user_name']) );
235 $guestName = addslashes( trim($atts['guest_name']) );
236 $sysName = addslashes( trim($atts['sys_name']) );
237 $context = addslashes( $atts['context'] );
238 $context = preg_replace( '/\v+/', "\\n", $context );
239 $textSend = addslashes( trim( $atts['text_send'] ) );
240 $textClear = addslashes( trim( $atts['text_clear'] ) );
241 $textInputPlaceholder = addslashes( trim( $atts['text_input_placeholder'] ) );
242 $textCompliance = addslashes( trim( $atts['text_compliance'] ) );
243 $startSentence = addslashes( trim( $atts['start_sentence'] ) );
244 $window = filter_var( $atts['window'], FILTER_VALIDATE_BOOLEAN );
245 $fullscreen = filter_var( $atts['fullscreen'], FILTER_VALIDATE_BOOLEAN );
246 $icon = isset( $atts['icon'] ) ? addslashes( trim( $atts['icon'] ) ) : "";
247 $iconText = trim($atts['icon_text']);
248 $iconPosition = addslashes( trim($atts['icon_position']) );
249 $style = $atts['style'];
250
251 // Validade & Enhance UI Parameters
252 $aiName = $this->formatAiName( $aiName );
253 $userName = $this->formatUserName( $userName, $guestName );
254 $rawAiName = $this->formatRawName( $aiName );
255 $rawUserName = $this->formatRawUserName( $userName, $guestName );
256
257 // Chatbot System Parameters
258 $id = empty( $atts['id'] ) ? uniqid() : $atts['id'];
259 $typewriter = $this->core->get_option( 'shortcode_chat_typewriter' );
260 $memorizeChat = !empty( $atts['id'] );
261 $id = preg_replace( '/[^a-zA-Z0-9]/', '', $id );
262 $env = $atts['env'];
263 $mode = $atts['mode'];
264 $maxResults = $mode === 'chat' ? 1 : $atts['max_results'];
265 $maxSentences = !empty( $atts['max_sentences'] ) ? intval( $atts['max_sentences'] ) : 1;
266 $sessionId = $this->core->get_session_id();
267 $rest_nonce = wp_create_nonce( 'wp_rest' );
268 $casuallyFineTuned = boolval( $atts['casually_fine_tuned'] );
269 $embeddingsIndex = $atts['embeddings_index'];
270 $promptEnding = addslashes( trim( $atts['prompt_ending'] ) );
271 $completionEnding = addslashes( trim( $atts['completion_ending'] ) );
272 if ( $casuallyFineTuned ) {
273 $promptEnding = "\\n\\n###\\n\\n";
274 $completionEnding = "\\n\\n";
275 }
276 $debugMode = $chatStyles = $this->core->get_option( 'debug_mode' );
277
278 // OpenAI Parameters
279 $model = $atts['model'];
280 $temperature = $atts['temperature'];
281 $maxTokens = $atts['max_tokens'];
282 $apiKey = $atts['api_key'];
283
284 // Variables
285 $apiUrl = get_rest_url( null, $mode === 'images' ? 'ai-chatbot/v1/imagesbot' : 'ai-chatbot/v1/chat' );
286 $baseClasses = "mwai-chat";
287 $baseClasses .= ( $window ? " mwai-window" : "" );
288 $baseClasses .= ( !$window && $fullscreen ? " mwai-fullscreen" : "" );
289 $baseClasses .= ( $style === 'chatgpt' ? " mwai-chatgpt" : "" );
290 $baseClasses .= ( $window && !empty( $iconPosition ) ? (" mwai-" . $iconPosition) : "" );
291
292 // Output CSS
293 ob_start();
294 $style_content = "";
295 if ( $style === 'chatgpt' ) {
296 $style_content = $this->chatgpt_style( $id, $style );
297 }
298 echo apply_filters( 'mwai_chatbot_style', $style_content, $id );
299
300 // Output HTML & CSS
301 $chatStyles = $this->core->get_option( 'shortcode_chat_styles' );
302 $iconUrl = MWAI_URL . '/images/chat-green.svg';
303 if ( !empty( $icon ) ) {
304 $iconUrl = $icon;
305 }
306 else if ( !empty( $chatStyles ) && isset( $chatStyles['icon'] ) ) {
307 $url = $chatStyles['icon'];
308 $iconUrl = $this->core->isUrl( $url ) ? $url : (MWAI_URL . 'images/' . $chatStyles['icon']);
309 }
310 ?>
311 <div id="mwai-chat-<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $baseClasses ); ?>">
312 <?php if ( $window ) : ?>
313 <div class="mwai-open-button">
314 <?php if ( !empty( $iconText ) ) : ?>
315 <div class="mwai-icon-text"><?php echo esc_html( $iconText ); ?></div>
316 <?php endif; ?>
317 <img width="64" height="64" src="<?php echo esc_url( $iconUrl ); ?>" />
318 </div>
319 <div class="mwai-header">
320 <?php if ( $fullscreen ) : ?>
321 <div class="mwai-resize-button"></div>
322 <?php endif; ?>
323 <div class="mwai-close-button"></div>
324 </div>
325 <?php endif; ?>
326 <div class="mwai-content">
327 <div class="mwai-conversation">
328 </div>
329 <div class="mwai-input">
330 <textarea rows="1" placeholder="<?php echo esc_attr( $textInputPlaceholder ); ?>"></textarea>
331 <button><span><?php echo esc_html( $textSend ); ?></span></button>
332 </div>
333 <?php if ( !empty( $textCompliance ) ) : ?>
334 <div class="mwai-compliance">
335 ⚠️ <?php echo esc_html( $textCompliance ); ?>
336 </div>
337 <?php endif; ?>
338 </div>
339 </div>
340
341 <script>
342 (function () {
343 let isMobile = window.matchMedia( "only screen and (max-width: 760px)" ).matches;
344 let isWindow = <?php echo $window ? "true" : "false" ?>;
345 let isDebugMode = <?php echo $debugMode ? "true" : "false" ?>;
346 let isFullscreen = <?php echo $fullscreen ? "true" : "false" ?>;
347 let restNonce = '<?php echo esc_attr( $rest_nonce ) ?>';
348 let apiURL = '<?php echo esc_url( $apiUrl ) ?>';
349 let isCasuallyFineTuned = <?php echo $casuallyFineTuned ? "true" : "false" ?>;
350 let textCompliance = '<?php echo wp_kses_post( $textCompliance ) ?>';
351 let rawUserName = '<?php echo esc_attr( $rawUserName ) ?>';
352 let rawAiName = '<?php echo esc_attr( $rawAiName ) ?>';
353 let userName = '<?php echo wp_kses_post( $userName ) ?>';
354 let aiName = '<?php echo wp_kses_post( $aiName ) ?>';
355 let sysName = '<?php echo wp_kses_post( $sysName ) ?>';
356 let env = '<?php echo esc_attr( $env ) ?>';
357 let apiKey = '<?php echo esc_attr( $apiKey ) ?>';
358 let session = '<?php echo esc_attr( $sessionId ) ?>';
359 let mode = '<?php echo esc_attr( $mode ) ?>';
360 let model = '<?php echo esc_attr( $model ) ?>';
361 let context = isCasuallyFineTuned ? null : '<?php echo esc_attr( $context ) ?>';
362 let embeddingsIndex = '<?php echo esc_attr( $embeddingsIndex ) ?>';
363 let promptEnding = '<?php echo esc_attr( $promptEnding ) ?>';
364 let stop = '<?php echo esc_attr( $completionEnding ) ?>';
365 let startSentence = '<?php echo esc_attr( $startSentence ) ?>';
366 let maxSentences = <?php echo (int)$maxSentences ?>;
367 let memorizeChat = <?php echo $memorizeChat ? "true" : "false" ?>;
368 let maxTokens = <?php echo (int)$maxTokens ?>;
369 let maxResults = <?php echo (int)$maxResults ?>;
370 let temperature = <?php echo (int)$temperature ?>;
371 let memorizedChat = [];
372 let typewriter = <?php echo $typewriter ? "true" : "false" ?>;
373
374 if (isDebugMode) {
375 window.mwai_<?php echo esc_attr( $id ) ?> = {
376 memorizedChat: memorizedChat,
377 parameters: { mode: mode, model, temperature, maxTokens, context: context, startSentence,
378 textCompliance, isMobile, isWindow, isFullscreen, isCasuallyFineTuned, memorizeChat, maxSentences,
379 rawUserName, rawAiName, embeddingsIndex, typewriter, maxResults, userName, aiName, env, apiKey, session
380 }
381 };
382 }
383
384 // Set button text
385 function setButtonText() {
386 let input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
387 let button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
388 let buttonSpan = button.querySelector('span');
389 if (memorizedChat.length < 2) {
390 buttonSpan.innerHTML = '<?php echo esc_html( $textSend ); ?>';
391 }
392 else if (!input.value.length) {
393 button.classList.add('mwai-clear');
394 buttonSpan.innerHTML = '<?php echo esc_html( $textClear ); ?>';
395 }
396 else {
397 button.classList.remove('mwai-clear');
398 buttonSpan.innerHTML = '<?php echo esc_html( $textSend ); ?>';
399 }
400 }
401
402 // Inject timer
403 function injectTimer(element) {
404 let intervalId;
405 let startTime = new Date();
406 let timerElement = null;
407
408 function updateTimer() {
409 let now = new Date();
410 let timer = Math.floor((now - startTime) / 1000);
411 if (!timerElement) {
412 if (timer > 0.5) {
413 timerElement = document.createElement('div');
414 timerElement.classList.add('mwai-timer');
415 element.appendChild(timerElement);
416 }
417 }
418 if (timerElement) {
419 let minutes = Math.floor(timer / 60);
420 let seconds = timer - (minutes * 60);
421 seconds = seconds < 10 ? "0" + seconds : seconds;
422 let display = minutes + ":" + seconds;
423 timerElement.innerHTML = display;
424 }
425 }
426
427 intervalId = setInterval(updateTimer, 500);
428
429 return function stopTimer() {
430 clearInterval(intervalId);
431 if (timerElement) {
432 timerElement.remove();
433 }
434 };
435 }
436
437 // Push the reply in the conversation
438 function addReply(text, role = 'user', replay = false) {
439 var conversation = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-conversation');
440
441 if (memorizeChat) {
442 localStorage.setItem('mwai-chat-<?php echo esc_attr( $id ) ?>', JSON.stringify(memorizedChat));
443 }
444
445 // If text is array, then it's image URLs. Let's create a simple gallery in HTML in $text.
446 if (Array.isArray(text)) {
447 var newText = '<div class="mwai-gallery">';
448 for (var i = 0; i < text.length; i++) {
449 newText += '<a href="' + text[i] + '" target="_blank"><img src="' + text[i] + '" />';
450 }
451 text = newText + '</div>';
452 }
453
454 var mwaiClasses = ['mwai-reply'];
455 if (role === 'assistant') {
456 mwaiClasses.push('mwai-ai');
457 }
458 else if (role === 'system') {
459 mwaiClasses.push('mwai-system');
460 }
461 else {
462 mwaiClasses.push('mwai-user');
463 }
464 var div = document.createElement('div');
465 div.classList.add(...mwaiClasses);
466 var nameSpan = document.createElement('span');
467 nameSpan.classList.add('mwai-name');
468 if (role === 'assistant') {
469 nameSpan.innerHTML = aiName;
470 }
471 else if (role === 'system') {
472 nameSpan.innerHTML = sysName;
473 }
474 else {
475 nameSpan.innerHTML = userName;
476 }
477 var textSpan = document.createElement('span');
478 textSpan.classList.add('mwai-text');
479 textSpan.innerHTML = text;
480 div.appendChild(nameSpan);
481 div.appendChild(textSpan);
482 conversation.appendChild(div);
483
484 if (typewriter) {
485 if (role === 'assistant' && text !== startSentence && !replay) {
486 let typewriter = new Typewriter(textSpan, {
487 deleteSpeed: 50, delay: 25, loop: false, cursor: '', autoStart: true,
488 wrapperClassName: 'mwai-typewriter',
489 });
490 typewriter.typeString(text).start().callFunction((state) => {
491 state.elements.cursor.setAttribute('hidden', 'hidden');
492 typewriter.stop();
493 });
494 }
495 }
496
497 conversation.scrollTop = conversation.scrollHeight;
498 setButtonText();
499
500 // Syntax coloring
501 if (typeof hljs !== 'undefined') {
502 document.querySelectorAll('pre code').forEach((el) => {
503 hljs.highlightElement(el);
504 });
505 }
506 }
507
508 function buildPrompt(last = 15) {
509 let prompt = context ? (context + '\n\n') : '';
510 memorizedChat = memorizedChat.slice(-last);
511
512 // Casually fine tuned, let's use the last question
513 if (isCasuallyFineTuned) {
514 let lastLine = memorizedChat[memorizedChat.length - 1];
515 prompt = lastLine.content + promptEnding;
516 return prompt;
517 }
518
519 // Otherwise let's compile the latest conversation
520 let conversation = memorizedChat.map(x => x.who + x.content);
521 prompt += conversation.join('\n');
522 prompt += '\n' + rawAiName;
523 return prompt;
524 }
525
526 // Function to request the completion
527 function onSendClick() {
528 let input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
529 let inputText = input.value.trim();
530
531 // Reset the conversation if empty
532 if (inputText === '') {
533 document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-conversation').innerHTML = '';
534 localStorage.removeItem('mwai-chat-<?php echo esc_attr( $id ) ?>');
535 memorizedChat = [];
536 memorizedChat.push({ role: 'assistant', content: startSentence, who: rawAiName, html: startSentence });
537 addReply(startSentence, 'assistant');
538 return;
539 }
540
541 // Disable the button
542 var button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
543 button.disabled = true;
544
545 // Add the user reply
546 memorizedChat.push({ role: 'user', content: inputText, who: rawUserName, html: inputText });
547 addReply(inputText, 'user');
548 input.value = '';
549 input.setAttribute('rows', 1);
550 input.disabled = true;
551
552 let prompt = buildPrompt(maxSentences);
553
554 const data = mode === 'images' ? {
555 env, session: session,
556 prompt: inputText, rawInput: inputText,
557 model: model, maxResults, apiKey: apiKey,
558 } : {
559 env, session: session,
560 prompt: prompt, context: context,
561 messages: memorizedChat, rawInput: inputText,
562 userName: userName, aiName: aiName,
563 model: model, temperature: temperature, maxTokens: maxTokens, maxResults: 1, apiKey: apiKey,
564 embeddingsIndex: embeddingsIndex, stop: stop,
565 };
566
567 // Start the timer
568 const stopTimer = injectTimer(button);
569
570 // Send the request
571 if (isDebugMode) {
572 console.log('[BOT] Sent: ', data);
573 }
574 fetch(apiURL, { method: 'POST', headers: {
575 'Content-Type': 'application/json',
576 'X-WP-Nonce': restNonce,
577 },
578 body: JSON.stringify(data)
579 })
580 .then(response => response.json())
581 .then(data => {
582 if (isDebugMode) {
583 console.log('[BOT] Recv: ', data);
584 }
585 if (!data.success) {
586 addReply(data.message, 'system');
587 }
588 else {
589 let html = data.images ? data.images : data.html;
590 memorizedChat.push({ role: 'assistant', content: data.answer, who: rawAiName, html: html });
591 addReply(html, 'assistant');
592 }
593 button.disabled = false;
594 input.disabled = false;
595 stopTimer();
596
597 // Only focus only on desktop (to avoid the mobile keyboard to kick-in)
598 if (!isMobile) {
599 input.focus();
600 }
601 })
602 .catch(error => {
603 console.error(error);
604 button.disabled = false;
605 input.disabled = false;
606 stopTimer();
607 });
608 }
609
610 // Keep the textarea height in sync with the content
611 function resizeTextArea(ev) {
612 ev.target.style.height = 'auto';
613 ev.target.style.height = ev.target.scrollHeight + 'px';
614 }
615
616 // Keep the textarea height in sync with the content
617 function delayedResizeTextArea(ev) {
618 window.setTimeout(resizeTextArea, 0, event);
619 }
620
621 // Init the chatbot
622 function initMeowChatbot() {
623 var input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
624 var button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
625
626 input.addEventListener('keypress', (event) => {
627 let text = event.target.value;
628 if (event.keyCode === 13 && !text.length && !event.shiftKey) {
629 event.preventDefault();
630 return;
631 }
632 if (event.keyCode === 13 && text.length && !event.shiftKey) {
633 onSendClick();
634 }
635 });
636 input.addEventListener('keydown', (event) => {
637 var rows = input.getAttribute('rows');
638 if (event.keyCode === 13 && event.shiftKey) {
639 var lines = input.value.split('\n').length + 1;
640 //mwaiSetTextAreaHeight(input, lines);
641 }
642 });
643 input.addEventListener('keyup', (event) => {
644 var rows = input.getAttribute('rows');
645 var lines = input.value.split('\n').length ;
646 //mwaiSetTextAreaHeight(input, lines);
647 setButtonText();
648 });
649
650 input.addEventListener('change', resizeTextArea, false);
651 input.addEventListener('cut', delayedResizeTextArea, false);
652 input.addEventListener('paste', delayedResizeTextArea, false);
653 input.addEventListener('drop', delayedResizeTextArea, false);
654 input.addEventListener('keydown', delayedResizeTextArea, false);
655
656 button.addEventListener('click', (event) => {
657 onSendClick();
658 });
659
660 // If window, add event listener to mwai-open-button and mwai-close-button
661 if ( isWindow ) {
662 var openButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-open-button');
663 openButton.addEventListener('click', (event) => {
664 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
665 chat.classList.add('mwai-open');
666 // Only focus only on desktop (to avoid the mobile keyboard to kick-in)
667 if (!isMobile) {
668 input.focus();
669 }
670 });
671 var closeButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-close-button');
672 closeButton.addEventListener('click', (event) => {
673 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
674 chat.classList.remove('mwai-open');
675 });
676 if (isFullscreen) {
677 var resizeButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-resize-button');
678 resizeButton.addEventListener('click', (event) => {
679 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
680 chat.classList.toggle('mwai-fullscreen');
681 });
682 }
683 }
684
685 // Get back the previous chat if any for the same ID
686 var chatHistory = [];
687 if (memorizeChat) {
688 chatHistory = localStorage.getItem('mwai-chat-<?php echo esc_attr( $id ) ?>');
689 if (chatHistory) {
690 memorizedChat = JSON.parse(chatHistory);
691 memorizedChat = memorizedChat.filter(x => x && x.html && x.role);
692 memorizedChat.forEach(x => {
693 addReply(x.html, x.role, true);
694 });
695 }
696 else {
697 memorizedChat = [];
698 }
699 }
700 if (memorizedChat.length === 0) {
701 memorizedChat.push({ role: 'assistant', content: startSentence, who: rawAiName, html: startSentence });
702 addReply(startSentence, 'assistant');
703 }
704 }
705
706 // Let's go totally meoooow on this!
707 initMeowChatbot();
708 })();
709 </script>
710
711 <?php
712 $output = ob_get_contents();
713 ob_end_clean();
714 $output = apply_filters( 'mwai_chatbot', $output, $atts );
715 return $output;
716 }
717 }
718