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