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