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