PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.9.81
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.9.81
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 / queries / text.php
ai-engine / classes / queries Last commit date
base.php 2 years ago embed.php 2 years ago function.php 2 years ago image.php 2 years ago parameter.php 2 years ago text.php 2 years ago transcribe.php 2 years ago
text.php
418 lines
1 <?php
2
3 class Meow_MWAI_Query_Text extends Meow_MWAI_Query_Base implements JsonSerializable {
4 public int $maxTokens = 1024;
5 public float $temperature = 0.8;
6 public int $maxSentences = 15;
7 public ?string $stop = null;
8 public array $messages = [];
9 public ?string $context = null;
10 public ?string $newMessage = null;
11 public ?string $promptEnding = null;
12 public bool $casuallyFineTuned = false;
13 public ?int $promptTokens = null;
14
15 public function __construct( ?string $prompt = '', int $maxTokens = 1024, string $model = MWAI_DEFAULT_MODEL ) {
16 parent::__construct( $prompt );
17 $this->setModel( $model );
18 $this->setMaxTokens( $maxTokens );
19 }
20
21 public function jsonSerialize() {
22 return [
23 'class' => get_class( $this ),
24 'prompt' => $this->prompt,
25 'messages' => $this->messages,
26 'maxTokens' => $this->maxTokens,
27 'temperature' => $this->temperature,
28 'maxSentences' => $this->maxSentences,
29 'stop' => $this->stop,
30 'context' => $this->context,
31 'newMessage' => $this->newMessage,
32 'promptEnding' => $this->promptEnding,
33 'casuallyFineTuned' => $this->casuallyFineTuned,
34 'model' => $this->model,
35 'mode' => $this->mode,
36 'session' => $this->session,
37 'env' => $this->env,
38 'service' => $this->service,
39 ];
40 }
41
42 public function getPromptTokens( $refresh = false ): int {
43 if ( $this->promptTokens && !$refresh ) {
44 return $this->promptTokens;
45 }
46 $this->promptTokens = $this->estimateTokens( $this->messages );
47 return $this->promptTokens;
48 }
49
50 public function getLastPrompt(): string {
51 if ( empty( $this->messages ) ) {
52 return $this->prompt;
53 }
54 $lastMessage = end( $this->messages );
55 return $lastMessage['content'];
56 }
57
58 // Quick and dirty token estimation
59 // Let's keep this synchronized with Helpers in JS
60 function estimateTokens( $content ): int
61 {
62 $text = "";
63 // https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
64 if ( is_array( $content ) ) {
65 foreach ( $content as $message ) {
66 $role = $message['role'];
67 $content = $message['content'];
68 $text .= "=#=$role\n$content=#=\n";
69 }
70 }
71 else {
72 $text = $content;
73 }
74 $tokens = 0;
75 return apply_filters( 'mwai_estimate_tokens', (int)$tokens, $text, $this->model );
76 }
77
78 /**
79 * Make sure the maxTokens is not greater than the model's context length.
80 */
81 public function finalChecks() {
82 if ( empty( $this->model ) ) { return; }
83
84 // Make sure the number of messages is not too great
85 if ( !empty( $this->maxSentences ) ) {
86 $context = array_shift( $this->messages );
87 if ( !empty( $this->messages ) ) {
88 $this->messages = array_slice( $this->messages, -$this->maxSentences );
89 }
90 else {
91 $this->messages = [];
92 }
93 if ( !empty( $context ) ) {
94 array_unshift( $this->messages, $context );
95 }
96 }
97
98 // Make sure the max tokens are respected.
99 $realMax = 4096;
100 $finetuneFamily = preg_match('/^([a-zA-Z]{0,32}):/', $this->model, $matches );
101 $finetuneFamily = ( isset( $matches ) && count( $matches ) > 0 ) ? $matches[1] : 'N/A';
102 $foundModel = null;
103 $openai_models = Meow_MWAI_Engines_OpenAI::get_openai_models();
104 foreach ( $openai_models as $currentModel ) {
105 if ( $currentModel['model'] === $this->model || $currentModel['family'] === $finetuneFamily ) {
106 $foundModel = $currentModel['name'];
107 $realMax = $currentModel['maxTokens'];
108 break;
109 }
110 }
111 $estimatedTokens = $this->getPromptTokens();
112 if ( !empty( $realMax ) && $estimatedTokens > $realMax ) {
113 throw new Exception( "AI Engine: The prompt is too long! It contains about $estimatedTokens tokens (estimation). The $foundModel model only accepts a maximum of $realMax tokens. " );
114 }
115 $realMax = (int)($realMax - $estimatedTokens) - 16;
116 if ( $this->maxTokens > $realMax ) {
117 $this->maxTokens = $realMax;
118 }
119 }
120
121 /**
122 * ID of the model to use.
123 * @param string $model ID of the model to use.
124 */
125 public function setModel( string $model ) {
126 $this->model = $model;
127 $this->mode = 'completion';
128 $found = false;
129 $openai_models = Meow_MWAI_Engines_OpenAI::get_openai_models();
130 foreach ( $openai_models as $currentModel ) {
131 if ( $currentModel['model'] === $this->model ) {
132 if ( $currentModel['mode'] ) {
133 $this->mode = $currentModel['mode'];
134 }
135 $found = true;
136 break;
137 }
138 }
139 if ( !$found ) {
140 // If the model can't be found, it's because it's probably a fine-tuned model. In the past (before August 2023),
141 // fine-tuned models were always based on GPT-3 (and therefore, using completion mode). From now on, they can be
142 // based on GPT-3.5 or 4 (and therefore, using chat mode). We need to detect that.
143 $baseModel = Meow_MWAI_Engines_OpenAI::getBaseModelForFinetune( $model );
144 if ( preg_match( '/^gpt-3.5|^gpt-4/', $baseModel ) ) {
145 $this->mode = 'chat';
146 }
147 }
148 }
149
150 /**
151 * Given a prompt, the model will return one or more predicted completions.
152 * It can also return the probabilities of alternative tokens at each position.
153 * @param string $prompt The prompt to generate completions.
154 */
155 public function setPrompt( $prompt ) {
156 parent::setPrompt( $prompt );
157 $this->validateMessages();
158 }
159
160 /**
161 * The prompt is used by models who uses Text Completion (and not Chat Completion).
162 * This returns the prompt if it's not a chat, otherwise it will build a prompt with
163 * all the messages nicely formatted.
164 */
165 public function getPrompt(): ?string {
166 // In the case it's really just a prompt.
167 if ( count( $this->messages ) === 1 ) {
168 $first = reset( $this->messages );
169 return $first['content'];
170 }
171
172 // In the case it's a chat that we need to convert into a prompt.
173 $first = reset( $this->messages );
174 $prompt = "";
175 if ( $first && $first['role'] === 'system' ) {
176 $prompt = $first['content'] . "\n\n";
177 }
178
179 // Casually Fine-Tuned or Prompt-Ending
180 if ( !empty( $this->promptEnding ) ) {
181 $last = end( $this->messages );
182 if ( $last && $last['role'] === 'user' ) {
183 $prompt = $last['content'] . $this->promptEnding;
184 }
185 return $prompt;
186 }
187
188 // Standard Completion
189 while ( $message = next( $this->messages ) ) {
190 $role = $message['role'];
191 $content = $message['content'];
192 if ( $role === 'system' ) {
193 $prompt .= "$content\n\n";
194 }
195 if ( $role === 'user' ) {
196 $prompt .= "User: $content\n";
197 }
198 if ( $role === 'assistant' ) {
199 $prompt .= "AI: $content\n";
200 }
201 }
202 $prompt .= "AI: ";
203 return $prompt;
204 }
205
206 /**
207 * Similar to the prompt, but focus on the new/last message.
208 * Only used when the model has a chat mode (and only used in messages).
209 * @param string $prompt The messages to generate completions.
210 */
211 public function setNewMessage( string $newMessage ): void {
212 $this->newMessage = $newMessage;
213 $this->validateMessages();
214 }
215
216 public function replace( $search, $replace ) {
217 $this->prompt = str_replace( $search, $replace, $this->prompt );
218 $this->validateMessages();
219 }
220
221 /**
222 * Similar to the prompt, but use an array of messages instead.
223 * @param string $prompt The messages to generate completions.
224 */
225 public function setMessages( array $messages ) {
226 $messages = array_map( function( $message ) {
227 if ( is_array( $message ) ) {
228 return [ 'role' => $message['role'], 'content' => $message['content'] ];
229 }
230 else if ( is_object( $message ) ) {
231 return [ 'role' => $message->role, 'content' => $message->content ];
232 }
233 else {
234 throw new InvalidArgumentException( 'Unsupported message type.' );
235 }
236 }, $messages );
237 $this->messages = $messages;
238 $this->validateMessages();
239 }
240
241 public function getLastMessage(): ?string {
242 if ( !empty( $this->messages ) ) {
243 $lastMessageIndex = count( $this->messages ) - 1;
244 $lastMessage = $this->messages[$lastMessageIndex];
245 return $lastMessage['content'];
246 }
247 return null;
248 }
249
250 // Function that adds a message just before the last message
251 public function injectContext( string $content ): void {
252 if ( !empty( $this->messages ) ) {
253 $lastMessageIndex = count( $this->messages ) - 1;
254 $lastMessage = $this->messages[$lastMessageIndex];
255 $this->messages[$lastMessageIndex] = [ 'role' => 'system', 'content' => $content ];
256 array_push( $this->messages, $lastMessage );
257 }
258 $this->validateMessages();
259 }
260
261 /**
262 * The context that is used for the chat completion (mode === 'chat').
263 * @param string $context The context to use.
264 */
265 public function setContext( string $context ): void {
266 $this->context = apply_filters( 'mwai_ai_context', $context, $this );
267 $this->validateMessages();
268 }
269
270 private function validateMessages(): void {
271 // Messages should end with either the prompt or, if exists, the newMessage.
272 $message = empty( $this->newMessage ) ? $this->prompt : $this->newMessage;
273 if ( empty( $this->messages ) ) {
274 $this->messages = [ [ 'role' => 'user', 'content' => $message ] ];
275 }
276 else {
277 $last = &$this->messages[ count( $this->messages ) - 1 ];
278 if ( $last['role'] === 'user' ) {
279 $last['content'] = $message;
280 }
281 else {
282 array_push( $this->messages, [ 'role' => 'user', 'content' => $message ] );
283 }
284 }
285
286 // The main context must be first.
287 if ( !empty( $this->context ) ) {
288 if ( is_array( $this->messages ) && count( $this->messages ) > 0 ) {
289 if ( $this->messages[0]['role'] !== 'system' ) {
290 array_unshift( $this->messages, [ 'role' => 'system', 'content' => $this->context ] );
291 }
292 else {
293 $this->messages[0]['content'] = $this->context;
294 }
295 }
296 }
297 }
298
299 /**
300 * The maximum number of tokens to generate in the completion.
301 * The token count of your prompt plus max_tokens cannot exceed the model's context length.
302 * Most models have a context length of 2048 tokens (except for the newest models, which support 4096).
303 * @param float $prompt The maximum number of tokens.
304 */
305 public function setMaxTokens( int $maxTokens ): void {
306 $this->maxTokens = $maxTokens;
307 }
308
309 /**
310 * Set the sampling temperature to use. Higher values means the model will take more risks.
311 * Try 0.9 for more creative applications, and 0 for ones with a well-defined reply.
312 * @param float $temperature The temperature.
313 */
314 public function setTemperature( float $temperature ): void {
315 $temperature = floatval( $temperature );
316 if ( $temperature > 1 ) {
317 $temperature = 1;
318 }
319 if ( $temperature < 0 ) {
320 $temperature = 0;
321 }
322 $this->temperature = round( $temperature, 2 );
323 }
324
325 public function setMaxSentences( int $maxSentences ): void {
326 if ( !empty( $maxSentences ) ) {
327 $this->maxSentences = intval( $maxSentences );
328 $this->validateMessages();
329 }
330 }
331
332 public function setStop( string $stop ): void {
333 $this->stop = $stop;
334 }
335
336 private function convertKeys( $params )
337 {
338 $newParams = [];
339 foreach ( $params as $key => $value ) {
340 $newKey = '';
341 $capitalizeNextChar = false;
342 for ( $i = 0; $i < strlen( $key ); $i++ ) {
343 if ( $key[$i] == '_' ) {
344 $capitalizeNextChar = true;
345 }
346 else {
347 $newKey .= $capitalizeNextChar ? strtoupper($key[$i]) : $key[$i];
348 $capitalizeNextChar = false;
349 }
350 }
351 $newParams[$newKey] = $value;
352 }
353 return $newParams;
354 }
355
356 // Based on the params of the query, update the attributes
357 public function injectParams( array $params ): void
358 {
359 // Those are for the keys passed directly by the shortcode.
360 $params = $this->convertKeys( $params );
361
362 $acceptedValues = [ true, 1, '1' ];
363 if ( !empty( $params['model'] ) ) {
364 $this->setModel( $params['model'] );
365 }
366 if ( !empty( $params['casuallyFineTuned'] ) && in_array( $params['casuallyFineTuned'], $acceptedValues, true ) ) {
367 $this->promptEnding = "\n\n###\n\n";
368 $this->stop = "\n\n";
369 $this->casuallyFineTuned = true;
370 }
371 if ( !empty( $params['prompt'] ) ) {
372 $this->setPrompt( $params['prompt'] );
373 }
374 if ( !empty( $params['context'] ) ) {
375 $this->setContext( $params['context'] );
376 }
377 if ( !empty( $params['messages'] ) ) {
378 $this->setMessages( $params['messages'] );
379 }
380 if ( !empty( $params['newMessage'] ) ) {
381 $this->setNewMessage( $params['newMessage'] );
382 }
383 if ( !empty( $params['maxTokens'] ) && intval( $params['maxTokens'] ) > 0 ) {
384 $this->setMaxTokens( intval( $params['maxTokens'] ) );
385 }
386 if ( !empty( $params['maxMessages'] ) && intval( $params['maxMessages'] ) > 0 ) {
387 $this->setMaxSentences( intval( $params['maxMessages'] ) );
388 }
389 if ( !empty( $params['maxSentences'] ) && intval( $params['maxSentences'] ) > 0 ) {
390 $this->setMaxSentences( intval( $params['maxSentences'] ) );
391 }
392 if ( !empty( $params['temperature'] ) ) {
393 $this->setTemperature( $params['temperature'] );
394 }
395 if ( !empty( $params['stop'] ) ) {
396 $this->setStop( $params['stop'] );
397 }
398 if ( !empty( $params['maxResults'] ) ) {
399 $this->setMaxResults( $params['maxResults'] );
400 }
401 if ( !empty( $params['env'] ) ) {
402 $this->setEnv( $params['env'] );
403 }
404 if ( !empty( $params['session'] ) ) {
405 $this->setSession( $params['session'] );
406 }
407 // Should add the params related to Open AI and Azure
408 if ( !empty( $params['service'] ) ) {
409 $this->setService( $params['service'] );
410 }
411 if ( !empty( $params['apiKey'] ) ) {
412 $this->setApiKey( $params['apiKey'] );
413 }
414 if ( !empty( $params['botId'] ) ) {
415 $this->setBotId( $params['botId'] );
416 }
417 }
418 }