PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.2.1
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.2.1
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
721 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 // Only for test now, but later we should probably import the JS/CSS
20 if ( $this->core->get_option( 'shortcode_chat_syntax_highlighting' ) ) {
21 wp_enqueue_script( 'mwai_chatbot',
22 '//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js', [], null, false );
23 wp_enqueue_style( 'mwai_chatbot',
24 '//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/stackoverflow-dark.min.css' );
25 }
26
27 if ( $this->core->get_option( 'shortcode_chat_typewriter' ) ) {
28 wp_enqueue_script( 'mwai_chatbot_typewriter',
29 '//cdnjs.cloudflare.com/ajax/libs/TypewriterJS/2.19.0/core.min.js', [], null, false );
30 }
31
32 $logs = $this->core->get_option( 'shortcode_chat_logs' );
33 if ( $logs && strpos( $logs, 'file' ) !== false ) {
34 new Meow_MWAI_Modules_Chatbot_Logs();
35 }
36
37 if ( $this->core->get_option( 'shortcode_chat_styles' ) ) {
38 add_filter( 'mwai_chatbot_style', [ $this, 'apply_chat_styles' ], 10, 2 );
39 }
40
41 }
42
43 function rest_api_init() {
44 try {
45 register_rest_route( $this->namespace, '/chat', array(
46 'methods' => 'POST',
47 'callback' => array( $this, 'rest_chat' ),
48 'permission_callback' => '__return_true'
49 ) );
50 register_rest_route( $this->namespace, '/imagesbot', array(
51 'methods' => 'POST',
52 'callback' => array( $this, 'rest_imagesbot' ),
53 'permission_callback' => '__return_true'
54 ) );
55 }
56 catch ( Exception $e ) {
57 var_dump( $e );
58 }
59 }
60
61 function chatgpt_style( $id ) {
62 $css = file_get_contents( MWAI_PATH . '/classes/modules/chatbot-chatgpt.css' );
63 $css = str_replace( '#mwai-chat-id', "#mwai-chat-{$id}", $css );
64 return "<style>" . $css . "</style>";
65 }
66
67 function rest_chat( $request ) {
68 try {
69 $params = $request->get_json_params();
70 $query = new Meow_MWAI_QueryText( $params['prompt'], 1024 );
71 $query->injectParams( $params );
72 $takeoverAnswer = apply_filters( 'mwai_chatbot_takeover', null, $query, $params );
73 if ( !empty( $takeoverAnswer ) ) {
74 return new WP_REST_Response([ 'success' => true, 'answer' => $takeoverAnswer,
75 'html' => $takeoverAnswer, 'usage' => null ], 200 );
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 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 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 function inject_chat() {
115 $params = $this->core->get_option( 'shortcode_chat_params' );
116 echo $this->chat( $params );
117 }
118
119 function imageschat( $atts ) {
120 $atts['mode'] = 'images';
121 return $this->chat( $atts );
122 }
123
124 function getCurrentUser() {
125 if ( is_user_logged_in() ) {
126 return wp_get_current_user();
127 }
128 return null;
129 }
130
131 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 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 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 function formatRawName( $aiName ) {
199 return "AI: ";
200 }
201
202 function formatRawUserName( $userName, $guestName ) {
203 return "User: ";
204 }
205
206 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 $iconPosition = addslashes( trim($atts['icon_position']) );
244 $style = $atts['style'];
245
246 // Validade & Enhance UI Parameters
247 $aiName = $this->formatAiName( $aiName );
248 $userName = $this->formatUserName( $userName, $guestName );
249 $rawAiName = $this->formatRawName( $aiName );
250 $rawUserName = $this->formatRawUserName( $userName, $guestName );
251
252 // Chatbot System Parameters
253 $id = empty( $atts['id'] ) ? uniqid() : $atts['id'];
254 $typewriter = $this->core->get_option( 'shortcode_chat_typewriter' );
255 $memorizeChat = !empty( $atts['id'] );
256 $id = preg_replace( '/[^a-zA-Z0-9]/', '', $id );
257 $env = $atts['env'];
258 $mode = $atts['mode'];
259 $maxResults = $mode === 'chat' ? 1 : $atts['max_results'];
260 $maxSentences = !empty( $atts['max_sentences'] ) ? intval( $atts['max_sentences'] ) : 1;
261 $sessionId = $this->core->get_session_id();
262 $rest_nonce = wp_create_nonce( 'wp_rest' );
263 $casuallyFineTuned = boolval( $atts['casually_fine_tuned'] );
264 $promptEnding = addslashes( trim( $atts['prompt_ending'] ) );
265 $completionEnding = addslashes( trim( $atts['completion_ending'] ) );
266 if ( $casuallyFineTuned ) {
267 $promptEnding = "\\n\\n###\\n\\n";
268 $completionEnding = "\\n\\n";
269 }
270 $debugMode = $chatStyles = $this->core->get_option( 'debug_mode' );
271
272 // OpenAI Parameters
273 $model = $atts['model'];
274 $temperature = $atts['temperature'];
275 $maxTokens = $atts['max_tokens'];
276 $apiKey = $atts['api_key'];
277
278 // Named functions
279 $initChatBotFn = "mwai_{$id}_initChatBot";
280
281 // Variables
282 $apiUrl = get_rest_url( null, $mode === 'images' ? 'ai-chatbot/v1/imagesbot' : 'ai-chatbot/v1/chat' );
283 $baseClasses = "mwai-chat";
284 $baseClasses .= ( $window ? " mwai-window" : "" );
285 $baseClasses .= ( !$window && $fullscreen ? " mwai-fullscreen" : "" );
286 $baseClasses .= ( $style === 'chatgpt' ? " mwai-chatgpt" : "" );
287 $baseClasses .= ( $window && !empty( $iconPosition ) ? (" mwai-" . $iconPosition) : "" );
288
289 // Output CSS
290 ob_start();
291 $style_content = "";
292 if ( $style === 'chatgpt' ) {
293 $style_content = $this->chatgpt_style( $id, $style );
294 }
295 echo apply_filters( 'mwai_chatbot_style', $style_content, $id );
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-<?= $id ?>" class="<?= $baseClasses ?>">
309 <?php if ( $window ) { ?>
310 <div class="mwai-open-button">
311 <?php if ( !empty( $iconText ) ) { ?>
312 <div class="mwai-icon-text"><?= $iconText ?></div>
313 <?php } ?>
314 <img width="64" height="64" src="<?= $iconUrl ?>" />
315 </div>
316 <div class="mwai-header">
317 <?php if ( $fullscreen ) { ?>
318 <div class="mwai-resize-button"></div>
319 <?php } ?>
320 <div class="mwai-close-button"></div>
321 </div>
322 <?php } ?>
323 <div class="mwai-content">
324 <div class="mwai-conversation">
325 </div>
326 <div class="mwai-input">
327 <textarea rows="1" placeholder="<?= $textInputPlaceholder ?>"></textarea>
328 <button><span><?= $textSend ?></span></button>
329 </div>
330 <?php if ( !empty( $textCompliance ) ) { ?>
331 <div class="mwai-compliance">
332 ⚠️ <?= $textCompliance ?>
333 </div>
334 <?php } ?>
335 </div>
336 </div>
337
338 <script>
339 (function () {
340 let isMobile = window.matchMedia("only screen and (max-width: 760px)").matches;
341 let isWindow = <?= $window ? 'true' : 'false' ?>;
342 let isDebugMode = <?= $debugMode ? 'true' : 'false' ?>;
343 let isFullscreen = <?= $fullscreen ? 'true' : 'false' ?>;
344 let isCasuallyFineTuned = <?= $casuallyFineTuned ? 'true' : 'false' ?>;
345 let textCompliance = '<?= $textCompliance ?>';
346 let rawUserName = '<?= $rawUserName ?>';
347 let rawAiName = '<?= $rawAiName ?>';
348 let mode = '<?= $mode ?>';
349 let model = '<?= $model ?>';
350 let context = isCasuallyFineTuned ? null : '<?= $context ?>';
351 let startSentence = '<?= $startSentence ?>';
352 let maxSentences = <?= $maxSentences ?>;
353 let memorizeChat = <?= $memorizeChat ? 'true' : 'false' ?>;
354 let maxTokens = <?= $maxTokens ?>;
355 let temperature = <?= $temperature ?>;
356 let memorizedChat = [];
357 let typewriter = <?= $typewriter ? 'true' : 'false' ?>;
358
359 if (isDebugMode) {
360 window.mwai_<?= $id ?> = {
361 memorizedChat: memorizedChat,
362 parameters: { mode: mode, model, temperature, maxTokens, context: context, startSentence,
363 textCompliance, isMobile, isWindow, isFullscreen, isCasuallyFineTuned, memorizeChat, maxSentences,
364
365 }
366 };
367 }
368
369 // Set button text
370 function setButtonText() {
371 let input = document.querySelector('#mwai-chat-<?= $id ?> .mwai-input textarea');
372 let button = document.querySelector('#mwai-chat-<?= $id ?> .mwai-input button');
373 let buttonSpan = button.querySelector('span');
374 if (memorizedChat.length < 2) {
375 buttonSpan.innerHTML = '<?= $textSend ?>';
376 }
377 else if (!input.value.length) {
378 button.classList.add('mwai-clear');
379 buttonSpan.innerHTML = '<?= $textClear ?>';
380 }
381 else {
382 button.classList.remove('mwai-clear');
383 buttonSpan.innerHTML = '<?= $textSend ?>';
384 }
385 }
386
387 // Inject timer
388 function injectTimer(element) {
389 let intervalId;
390 let startTime = new Date();
391 let timerElement = null;
392
393 function updateTimer() {
394 let now = new Date();
395 let timer = Math.floor((now - startTime) / 1000);
396 if (!timerElement) {
397 if (timer > 0.5) {
398 timerElement = document.createElement('div');
399 timerElement.classList.add('mwai-timer');
400 element.appendChild(timerElement);
401 }
402 }
403 if (timerElement) {
404 let minutes = Math.floor(timer / 60);
405 let seconds = timer - (minutes * 60);
406 seconds = seconds < 10 ? "0" + seconds : seconds;
407 let display = minutes + ":" + seconds;
408 timerElement.innerHTML = display;
409 }
410 }
411
412 intervalId = setInterval(updateTimer, 500);
413
414 return function stopTimer() {
415 clearInterval(intervalId);
416 if (timerElement) {
417 timerElement.remove();
418 }
419 };
420 }
421
422 // Push the reply in the conversation
423 function addReply(text, role = 'user', replay = false) {
424 var conversation = document.querySelector('#mwai-chat-<?= $id ?> .mwai-conversation');
425
426 if (memorizeChat) {
427 localStorage.setItem('mwai-chat-<?= $id ?>', JSON.stringify(memorizedChat));
428 }
429
430 // If text is array, then it's image URLs. Let's create a simple gallery in HTML in $text.
431 if (Array.isArray(text)) {
432 var newText = '<div class="mwai-gallery">';
433 for (var i = 0; i < text.length; i++) {
434 newText += '<a href="' + text[i] + '" target="_blank"><img src="' + text[i] + '" />';
435 }
436 text = newText + '</div>';
437 }
438
439 var mwaiClasses = ['mwai-reply'];
440 if (role === 'assistant') {
441 mwaiClasses.push('mwai-ai');
442 }
443 else if (role === 'system') {
444 mwaiClasses.push('mwai-system');
445 }
446 else {
447 mwaiClasses.push('mwai-user');
448 }
449 var div = document.createElement('div');
450 div.classList.add(...mwaiClasses);
451 var nameSpan = document.createElement('span');
452 nameSpan.classList.add('mwai-name');
453 if (role === 'assistant') {
454 nameSpan.innerHTML = '<?= $aiName ?>';
455 }
456 else if (role === 'system') {
457 nameSpan.innerHTML = '<?= $sysName ?>';
458 }
459 else {
460 nameSpan.innerHTML = '<?= $userName ?>';
461 }
462 var textSpan = document.createElement('span');
463 textSpan.classList.add('mwai-text');
464 textSpan.innerHTML = text;
465 div.appendChild(nameSpan);
466 div.appendChild(textSpan);
467 conversation.appendChild(div);
468
469 if (typewriter) {
470 if (role === 'assistant' && text !== startSentence && !replay) {
471 let typewriter = new Typewriter(textSpan, {
472 loop: true,
473 deleteSpeed: 50,
474 delay: 25,
475 loop: false,
476 cursor: '',
477 autoStart: true,
478 wrapperClassName: 'mwai-typewriter',
479 });
480 typewriter.typeString(text).start().callFunction((state) => {
481 state.elements.cursor.setAttribute('hidden', 'hidden');
482 typewriter.stop();
483 });
484 }
485 }
486
487 conversation.scrollTop = conversation.scrollHeight;
488 setButtonText();
489
490 // Syntax coloring
491 if (typeof hljs !== 'undefined') {
492 document.querySelectorAll('pre code').forEach((el) => {
493 hljs.highlightElement(el);
494 });
495 }
496 }
497
498 function buildPrompt(last = 15) {
499 let prompt = context ? (context + '\n\n') : '';
500 memorizedChat = memorizedChat.slice(-last);
501
502 // Casually fine tuned, let's use the last question
503 if (isCasuallyFineTuned) {
504 let lastLine = memorizedChat[memorizedChat.length - 1];
505 prompt = lastLine.content + '<?= $promptEnding ?>'
506 return prompt;
507 }
508
509 // Otherwise let's compile the latest conversation
510 let conversation = memorizedChat.map(x => x.who + x.content);
511 prompt += conversation.join('\n');
512 prompt += '\n' + rawAiName;
513 return prompt;
514 }
515
516 // Function to request the completion
517 function onSendClick() {
518 let input = document.querySelector('#mwai-chat-<?= $id ?> .mwai-input textarea');
519 let inputText = input.value.trim();
520
521 // Reset the conversation if empty
522 if (inputText === '') {
523 document.querySelector('#mwai-chat-<?= $id ?> .mwai-conversation').innerHTML = '';
524 localStorage.removeItem('mwai-chat-<?= $id ?>');
525 memorizedChat = [];
526 memorizedChat.push({ role: 'assistant', content: startSentence, who: rawAiName, html: startSentence });
527 addReply(startSentence, 'assistant');
528 return;
529 }
530
531 // Disable the button
532 var button = document.querySelector('#mwai-chat-<?= $id ?> .mwai-input button');
533 button.disabled = true;
534
535 // Add the user reply
536 memorizedChat.push({ role: 'user', content: inputText, who: rawUserName, html: inputText });
537 addReply(inputText, 'user');
538 input.value = '';
539 input.setAttribute('rows', 1);
540 input.disabled = true;
541
542 let prompt = buildPrompt(maxSentences);
543
544 // Prompt for the images
545 const data = mode === 'images' ? {
546 env: '<?= $env ?>',
547 session: '<?= $sessionId ?>',
548 prompt: inputText,
549 rawInput: inputText,
550 maxResults: <?= $maxResults ?>,
551 model: model,
552 apiKey: '<?= $atts['api_key'] ?>',
553 // Prompt for the chat
554 } : {
555 env: '<?= $env ?>',
556 session: '<?= $sessionId ?>',
557 prompt: prompt,
558 context: context,
559 messages: memorizedChat,
560 rawInput: inputText,
561 userName: '<?= $userName ?>',
562 aiName: '<?= $aiName ?>',
563 model: model,
564 temperature: temperature,
565 maxTokens: maxTokens,
566 stop: '<?= $completionEnding ?>',
567 maxResults: 1,
568 apiKey: '<?= $apiKey ?>',
569 };
570
571 // Start the timer
572 const stopTimer = injectTimer(button);
573
574 // Send the request
575 if (isDebugMode) {
576 console.log('[BOT] Sent: ', data);
577 }
578 fetch('<?= $apiUrl ?>', { method: 'POST', headers: {
579 'Content-Type': 'application/json',
580 'X-WP-Nonce': '<?= $rest_nonce ?>'
581 },
582 body: JSON.stringify(data)
583 })
584 .then(response => response.json())
585 .then(data => {
586 if (isDebugMode) {
587 console.log('[BOT] Recv: ', data);
588 }
589 if (!data.success) {
590 addReply(data.message, 'system');
591 }
592 else {
593 let html = data.images ? data.images : data.html;
594 memorizedChat.push({ role: 'assistant', content: data.answer, who: rawAiName, html: html });
595 addReply(html, 'assistant');
596 }
597 button.disabled = false;
598 input.disabled = false;
599 stopTimer();
600
601 // Only focus only on desktop (to avoid the mobile keyboard to kick-in)
602 if (!isMobile) {
603 input.focus();
604 }
605 })
606 .catch(error => {
607 console.error(error);
608 button.disabled = false;
609 input.disabled = false;
610 stopTimer();
611 });
612 }
613
614 // Keep the textarea height in sync with the content
615 function resizeTextArea(ev) {
616 ev.target.style.height = 'auto';
617 ev.target.style.height = ev.target.scrollHeight + 'px';
618 }
619
620 // Keep the textarea height in sync with the content
621 function delayedResizeTextArea(ev) {
622 window.setTimeout(resizeTextArea, 0, event);
623 }
624
625 // Init the chatbot
626 function <?= $initChatBotFn ?>() {
627 var input = document.querySelector('#mwai-chat-<?= $id ?> .mwai-input textarea');
628 var button = document.querySelector('#mwai-chat-<?= $id ?> .mwai-input button');
629
630 input.addEventListener('keypress', (event) => {
631 let text = event.target.value;
632 if (event.keyCode === 13 && !text.length && !event.shiftKey) {
633 event.preventDefault();
634 return;
635 }
636 if (event.keyCode === 13 && text.length && !event.shiftKey) {
637 onSendClick();
638 }
639 });
640 input.addEventListener('keydown', (event) => {
641 var rows = input.getAttribute('rows');
642 if (event.keyCode === 13 && event.shiftKey) {
643 var lines = input.value.split('\n').length + 1;
644 //mwaiSetTextAreaHeight(input, lines);
645 }
646 });
647 input.addEventListener('keyup', (event) => {
648 var rows = input.getAttribute('rows');
649 var lines = input.value.split('\n').length ;
650 //mwaiSetTextAreaHeight(input, lines);
651 setButtonText();
652 });
653
654 input.addEventListener('change', resizeTextArea, false);
655 input.addEventListener('cut', delayedResizeTextArea, false);
656 input.addEventListener('paste', delayedResizeTextArea, false);
657 input.addEventListener('drop', delayedResizeTextArea, false);
658 input.addEventListener('keydown', delayedResizeTextArea, false);
659
660 button.addEventListener('click', (event) => {
661 onSendClick();
662 });
663
664 // If window, add event listener to mwai-open-button and mwai-close-button
665 if ( isWindow ) {
666 var openButton = document.querySelector('#mwai-chat-<?= $id ?> .mwai-open-button');
667 openButton.addEventListener('click', (event) => {
668 var chat = document.querySelector('#mwai-chat-<?= $id ?>');
669 chat.classList.add('mwai-open');
670 // Only focus only on desktop (to avoid the mobile keyboard to kick-in)
671 if (!isMobile) {
672 input.focus();
673 }
674 });
675 var closeButton = document.querySelector('#mwai-chat-<?= $id ?> .mwai-close-button');
676 closeButton.addEventListener('click', (event) => {
677 var chat = document.querySelector('#mwai-chat-<?= $id ?>');
678 chat.classList.remove('mwai-open');
679 });
680 if (isFullscreen) {
681 var resizeButton = document.querySelector('#mwai-chat-<?= $id ?> .mwai-resize-button');
682 resizeButton.addEventListener('click', (event) => {
683 var chat = document.querySelector('#mwai-chat-<?= $id ?>');
684 chat.classList.toggle('mwai-fullscreen');
685 });
686 }
687 }
688
689 // Get back the previous chat if any for the same ID
690 var chatHistory = [];
691 if (memorizeChat) {
692 chatHistory = localStorage.getItem('mwai-chat-<?= $id ?>');
693 if (chatHistory) {
694 memorizedChat = JSON.parse(chatHistory);
695 memorizedChat = memorizedChat.filter(x => x && x.html && x.role);
696 memorizedChat.forEach(x => {
697 addReply(x.html, x.role, true);
698 });
699 }
700 else {
701 memorizedChat = [];
702 }
703 }
704 if (memorizedChat.length === 0) {
705 memorizedChat.push({ role: 'assistant', content: startSentence, who: rawAiName, html: startSentence });
706 addReply(startSentence, 'assistant');
707 }
708 }
709
710 // Let's go totally meoooow on this!
711 <?= $initChatBotFn ?>();
712 })();
713 </script>
714
715 <?php
716 $output = ob_get_contents();
717 ob_end_clean();
718 $output = apply_filters( 'mwai_chatbot', $output, $atts );
719 return $output;
720 }
721 }