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