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