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