PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.6.89
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.6.89
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.php 3 years ago chatbot_legacy.php 3 years ago discussions.php 3 years ago security.php 3 years ago
chatbot.php
410 lines
1 <?php
2
3 // Params for the chatbot (front and server)
4
5 define( 'MWAI_CHATBOT_FRONT_PARAMS', [ 'aiName', 'userName', 'guestName', 'textSend', 'textClear',
6 'textInputPlaceholder', 'textInputMaxLength', 'textCompliance', 'startSentence', 'localMemory',
7 'themeId', 'window', 'icon', 'iconText', 'iconAlt', 'iconPosition', 'fullscreen', 'copyButton'
8 ] );
9 define( 'MWAI_CHATBOT_SERVER_PARAMS', [ 'id', 'env', 'mode', 'contentAware', 'embeddingsIndex', 'context',
10 'casuallyFineTuned', 'promptEnding', 'completionEnding', 'model', 'temperature', 'maxTokens',
11 'maxResults', 'apiKey', 'service'
12 ] );
13
14 // Params for the discussions (front and server)
15
16 define( 'MWAI_DISCUSSIONS_FRONT_PARAMS', [ 'themeId' ] );
17 define( 'MWAI_DISCUSSIONS_SERVER_PARAMS', [] );
18
19 class Meow_MWAI_Modules_Chatbot {
20 private $core = null;
21 private $namespace = 'mwai-ui/v1';
22 private $siteWideChatId = null;
23
24 public function __construct() {
25 global $mwai_core;
26 $this->core = $mwai_core;
27 add_shortcode( 'mwai_chatbot_v2', array( $this, 'chat_shortcode' ) );
28 add_action( 'rest_api_init', array( $this, 'rest_api_init' ) );
29 $this->siteWideChatId = $this->core->get_option( 'botId' );
30 add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts' ) );
31
32 if ( $this->core->get_option( 'shortcode_chat_discussions' ) ) {
33 add_shortcode( 'mwai_discussions', [ $this, 'shortcode_chat_discussions' ] );
34 }
35 }
36
37 public function register_scripts() {
38 wp_register_script( 'mwai_highlight', MWAI_URL . 'vendor/highlightjs/highlight.min.js', [], '11.7', false );
39 $physical_file = trailingslashit( MWAI_PATH ) . 'app/chatbot.js';
40 $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : MWAI_VERSION;
41 wp_register_script( 'mwai_chatbot', trailingslashit( MWAI_URL ) . 'app/chatbot.js',
42 [ 'wp-element' ], $cache_buster, false );
43 if ( !empty( $this->siteWideChatId ) && $this->siteWideChatId !== 'none' ) {
44 $this->enqueue_scripts();
45 add_action( 'wp_footer', array( $this, 'inject_chat' ) );
46 }
47 }
48
49 public function enqueue_scripts() {
50 wp_enqueue_script( "mwai_chatbot" );
51 if ( $this->core->get_option( 'shortcode_chat_syntax_highlighting' ) ) {
52 wp_enqueue_script( "mwai_highlight" );
53 }
54 }
55
56 public function rest_api_init() {
57 register_rest_route( $this->namespace, '/chats/submit', array(
58 'methods' => 'POST',
59 'callback' => array( $this, 'rest_chat' ),
60 'permission_callback' => '__return_true'
61 ) );
62 }
63
64 public function basics_security_check( $id, $botId, $newMessage ) {
65 if ( empty( $newMessage ) ) {
66 error_log("AI Engine: The query was rejected - message was empty.");
67 return false;
68 }
69 if ( !$botId && !$id ) {
70 error_log("AI Engine: The query was rejected - no botId nor id was specified.");
71 return false;
72 }
73 $length = strlen( $newMessage );
74 if ( $length < 1 || $length > ( 4096 - 512 ) ) {
75 error_log("AI Engine: The query was rejected - message was too short or too long.");
76 return false;
77 }
78 return true;
79 }
80
81 public function rest_chat( $request ) {
82 try {
83 $params = $request->get_json_params();
84 $id = $params['id'] ?? null;
85 $botId = $params['botId'] ?? null;
86 $newMessage = trim( $params['newMessage'] ?? '' );
87
88 if ( !$this->basics_security_check( $id, $botId, $newMessage )) {
89 return new WP_REST_Response( [
90 'success' => false,
91 'message' => 'Sorry, your query has been rejected.' ], 403
92 );
93 }
94
95 // Custom Chatbot
96 if ( $id ) {
97 $chatbot = get_transient( 'mwai_custom_chatbot_' . $id );
98 }
99 // Registered Chatbot
100 if ( !$chatbot && $botId ) {
101 $chatbot = $this->core->getChatbot( $botId );
102 }
103
104 if ( !$chatbot ) {
105 error_log("AI Engine: No chatbot was found for this query.");
106 return new WP_REST_Response( [
107 'success' => false,
108 'message' => 'Sorry, your query has been rejected.' ], 403
109 );
110 }
111
112 // Create QueryText
113 $context = null;
114 $mode = $chatbot['mode'] ?? 'chat';
115
116 if ( $mode === 'images' ) {
117 $query = new Meow_MWAI_QueryImage( $newMessage );
118
119 // Handle Params
120 $newParams = [];
121 foreach ( $chatbot as $key => $value ) {
122 $newParams[$key] = $value;
123 }
124 foreach ( $params as $key => $value ) {
125 $newParams[$key] = $value;
126 }
127 $params = apply_filters( 'mwai_chatbot_params', $newParams );
128 $params['env'] = empty( $params['env'] ) ? 'chatbot' : $params['env'];
129 $query->injectParams( $params );
130 }
131 else {
132 $query = new Meow_MWAI_QueryText( $newMessage, 1024 );
133 $query->setIsChat( true );
134
135 // Handle Params
136 $newParams = [];
137 foreach ( $chatbot as $key => $value ) {
138 $newParams[$key] = $value;
139 }
140 foreach ( $params as $key => $value ) {
141 $newParams[$key] = $value;
142 }
143 $params = apply_filters( 'mwai_chatbot_params', $newParams );
144 $params['env'] = empty( $params['env'] ) ? 'chatbot' : $params['env'];
145 $query->injectParams( $params );
146
147 // Takeover
148 $takeoverAnswer = apply_filters( 'mwai_chatbot_takeover', null, $query, $params );
149 if ( !empty( $takeoverAnswer ) ) {
150 return new WP_REST_Response( [ 'success' => true, 'reply' => $takeoverAnswer,
151 'html' => $takeoverAnswer, 'usage' => null ], 200 );
152 }
153
154 // Moderation
155 if ( $this->core->get_option( 'shortcode_chat_moderation' ) ) {
156 global $mwai;
157 $isFlagged = $mwai->moderationCheck( $query->prompt );
158 if ( $isFlagged ) {
159 return new WP_REST_Response( [
160 'success' => false,
161 'message' => 'Sorry, your message has been rejected by moderation.' ], 403
162 );
163 }
164 }
165
166 // Awareness & Embeddings
167 // TODO: This is same in Chatbot Legacy and Forms, maybe we should move it to the core?
168 $embeddingsIndex = $params['embeddingsIndex'] ?? null;
169 if ( $query->mode === 'chat' ) {
170 $context = apply_filters( 'mwai_context_search', $context, $query, [ 'embeddingsIndex' => $embeddingsIndex ] );
171 if ( !empty( $context ) ) {
172 if ( isset( $context['content'] ) ) {
173 $content = $this->core->cleanSentences( $context['content'] );
174 $query->injectContext( $content );
175 }
176 else {
177 error_log("AI Engine: A context without content was returned.");
178 }
179 }
180 }
181 }
182
183 // Query the AI
184 $reply = $this->core->ai->run( $query );
185 $rawText = $reply->result;
186 $extra = [];
187 if ( $context ) {
188 $extra = [ 'embeddings' => $context['embeddings'] ];
189 }
190 $html = apply_filters( 'mwai_chatbot_reply', $rawText, $query, $params, $extra );
191 if ( $this->core->get_option( 'shortcode_chat_formatting' ) ) {
192 $html = $this->core->markdown_to_html( $html );
193 }
194
195 return new WP_REST_Response( [
196 'success' => true,
197 'reply' => $rawText,
198 'images' => $reply->getType() === 'images' ? $reply->results : null,
199 'html' => $html,
200 'usage' => $reply->usage
201 ], 200 );
202 }
203 catch ( Exception $e ) {
204 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
205 return new WP_REST_Response([ 'success' => false, 'message' => $message ], 500 );
206 }
207 }
208
209 public function inject_chat() {
210 $params = $this->core->getChatbot( $this->siteWideChatId );
211 $cleanParams = [];
212 if ( !empty( $params ) ) {
213 $cleanParams['window'] = true;
214 $cleanParams['id'] = $this->siteWideChatId;
215 echo $this->chat_shortcode( $cleanParams );
216 }
217 return null;
218 }
219
220 public function build_front_params( $id, $botId ) {
221 $frontSystem = [
222 'id' => $id,
223 'botId' => $botId,
224 'userData' => $this->core->getUserData(),
225 'sessionId' => $this->core->get_session_id(),
226 'restNonce' => $this->core->get_nonce(),
227 'contextId' => get_the_ID(),
228 'pluginUrl' => MWAI_URL,
229 'restUrl' => untrailingslashit( rest_url() ),
230 'debugMode' => $this->core->get_option( 'debug_mode' ),
231 'typewriter' => $this->core->get_option( 'shortcode_chat_typewriter' ),
232 'speech_recognition' => $this->core->get_option( 'speech_recognition' ),
233 'speech_synthesis' => $this->core->get_option( 'speech_synthesis' ),
234 ];
235 return $frontSystem;
236 }
237
238 public function chat_shortcode( $atts ) {
239 $atts = empty($atts) ? [] : $atts;
240
241 // Properly handle the id, botId, and chatbot
242 // We have the same in discussions.php
243 $chatbot = null;
244 $botId = $atts['chat_id'] ?? null;
245 $id = $atts['id'] ?? null;
246 unset( $atts['chat_id'], $atts['id'] );
247 if ( $botId ) {
248 $chatbot = $this->core->getChatbot( $botId );
249 if ( !$chatbot ) {
250 return "AI Engine: Chatbot not found.";
251 }
252 }
253 if ( $id && !$chatbot ) {
254 $chatbot = $this->core->getChatbot( $id );
255 $botId = $chatbot ? $id : 'default';
256 }
257 $chatbot = $chatbot ?: $this->core->getChatbot( 'default' );
258 $botId = $botId ?: 'default';
259 $isCustom = $botId == 'default' && isset( $atts['id'] );
260
261 // Rename the keys of the atts into camelCase to match the internal params system.
262 $atts = array_map( function( $key, $value ) {
263 $key = str_replace( '_', ' ', $key );
264 $key = ucwords( $key );
265 $key = str_replace( ' ', '', $key );
266 $key = lcfirst( $key );
267 return [ $key => $value ];
268 }, array_keys( $atts ), $atts );
269 $atts = array_merge( ...$atts );
270
271 $frontParams = [];
272 foreach ( MWAI_CHATBOT_FRONT_PARAMS as $param ) {
273 if ( isset( $atts[$param] ) ) {
274 if ( $param === 'localMemory' ) {
275 $frontParams[$param] = $atts[$param] === 'true';
276 }
277 else {
278 $frontParams[$param] = $atts[$param];
279 }
280 }
281 else if ( isset( $chatbot[$param] ) ) {
282 $frontParams[$param] = $chatbot[$param];
283 }
284 }
285
286 // Server Params
287 $serverParams = [];
288 foreach ( MWAI_CHATBOT_SERVER_PARAMS as $param ) {
289 if ( isset( $atts[$param] ) ) {
290 $serverParams[$param] = $atts[$param];
291 }
292 }
293
294 // Front Params
295 $frontSystem = $this->build_front_params( $id, $botId );
296
297 // Clean Params
298 $frontParams = $this->cleanParams( $frontParams );
299 $frontSystem = $this->cleanParams( $frontSystem );
300 $serverParams = $this->cleanParams( $serverParams );
301
302 // Server-side: Keep the System Params
303 if ( count( $serverParams ) > 0 ) {
304 if ( !$isCustom ) {
305 $id = md5( json_encode( $serverParams ) );
306 $botId = null;
307 $frontSystem['id'] = $id;
308 $frontSystem['botId'] = $botId;
309 }
310 set_transient( 'mwai_custom_chatbot_' . $id, $serverParams, 60 * 60 * 24 );
311 }
312
313 // Client-side: Prepare JSON for Front Params and System Params
314 $theme = isset( $frontParams['themeId'] ) ? $this->core->getTheme( $frontParams['themeId'] ) : null;
315 $jsonFrontParams = htmlspecialchars( json_encode( $frontParams ), ENT_QUOTES, 'UTF-8' );
316 $jsonFrontSystem = htmlspecialchars( json_encode( $frontSystem ), ENT_QUOTES, 'UTF-8' );
317 $jsonFrontTheme = htmlspecialchars( json_encode( $theme ), ENT_QUOTES, 'UTF-8' );
318 //$jsonAttributes = htmlspecialchars(json_encode($atts), ENT_QUOTES, 'UTF-8');
319
320 $this->enqueue_scripts();
321 return "<div class='mwai-chatbot-container' data-params='{$jsonFrontParams}' data-system='{$jsonFrontSystem}' data-theme='{$jsonFrontTheme}'></div>";
322 }
323
324 function shortcode_chat_discussions( $atts ) {
325 $atts = empty($atts) ? [] : $atts;
326
327 // Properly handle the id, botId, and chatbot
328 // We have the same in chatbot.php
329 $chatbot = null;
330 $botId = $atts['chat_id'] ?? null;
331 $id = $atts['id'] ?? null;
332 unset( $atts['chat_id'], $atts['id'] );
333 if ( $botId ) {
334 $chatbot = $this->core->getChatbot( $botId );
335 if ( !$chatbot ) {
336 return "AI Engine: Chatbot not found.";
337 }
338 }
339 if ( $id && !$chatbot ) {
340 $chatbot = $this->core->getChatbot( $id );
341 $botId = $chatbot ? $id : 'default';
342 }
343 $chatbot = $chatbot ?: $this->core->getChatbot( 'default' );
344 $botId = $botId ?: 'default';
345 $isCustom = $botId == 'default' && isset( $atts['id'] );
346
347 // Rename the keys of the atts into camelCase to match the internal params system.
348 $atts = array_map( function( $key, $value ) {
349 $key = str_replace( '_', ' ', $key );
350 $key = ucwords( $key );
351 $key = str_replace( ' ', '', $key );
352 $key = lcfirst( $key );
353 return [ $key => $value ];
354 }, array_keys( $atts ), $atts );
355 $atts = array_merge( ...$atts );
356
357 // Front Params
358 $frontParams = [];
359 foreach ( MWAI_DISCUSSIONS_FRONT_PARAMS as $param ) {
360 if ( isset( $atts[$param] ) ) {
361 $frontParams[$param] = $atts[$param];
362 }
363 else if ( isset( $chatbot[$param] ) ) {
364 $frontParams[$param] = $chatbot[$param];
365 }
366 }
367
368 // Server Params
369 $serverParams = [];
370 foreach ( MWAI_DISCUSSIONS_SERVER_PARAMS as $param ) {
371 if ( isset( $atts[$param] ) ) {
372 $serverParams[$param] = $atts[$param];
373 }
374 }
375
376
377 // Front System
378 $frontSystem = $this->build_front_params( $id, $botId );
379
380 // Clean Params
381 $frontParams = $this->cleanParams( $frontParams );
382 $frontSystem = $this->cleanParams( $frontSystem );
383 $serverParams = $this->cleanParams( $serverParams );
384
385 $theme = isset( $frontParams['themeId'] ) ? $this->core->getTheme( $frontParams['themeId'] ) : null;
386 $jsonFrontParams = htmlspecialchars( json_encode( $frontParams ), ENT_QUOTES, 'UTF-8' );
387 $jsonFrontSystem = htmlspecialchars( json_encode( $frontSystem ), ENT_QUOTES, 'UTF-8' );
388 $jsonFrontTheme = htmlspecialchars( json_encode( $theme ), ENT_QUOTES, 'UTF-8' );
389
390 return "<div class='mwai-discussions-container' data-params='{$jsonFrontParams}' data-system='{$jsonFrontSystem}' data-theme='{$jsonFrontTheme}'></div>";
391 }
392
393 function cleanParams( &$params ) {
394 foreach ( $params as $param => $value ) {
395 if ( empty( $value ) || is_array( $value ) ) {
396 continue;
397 }
398 $lowerCaseValue = strtolower( $value );
399 if ( $lowerCaseValue === 'true' || $lowerCaseValue === 'false' || is_bool( $value ) ) {
400 $params[$param] = filter_var( $value, FILTER_VALIDATE_BOOLEAN );
401 }
402 else if ( is_numeric( $value ) ) {
403 $params[$param] = filter_var( $value, FILTER_VALIDATE_FLOAT );
404 }
405 }
406 return $params;
407 }
408
409 }
410