PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.6.89
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.6.89
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 / engines / openai.php
ai-engine / classes / engines Last commit date
core.php 3 years ago openai.php 3 years ago
openai.php
362 lines
1 <?php
2
3 class Meow_MWAI_Engines_OpenAI
4 {
5 private $core = null;
6 private $apiKey = null;
7
8 public function __construct($core)
9 {
10 $this->core = $core;
11 $this->apiKey = $this->core->get_option( 'openai_apikey' );
12 }
13
14 // Check if there are errors in the response from OpenAI, and throw an exception if so.
15 public function handleResponseErrors( $data ) {
16 if ( isset( $data['error'] ) ) {
17 $message = $data['error']['message'];
18 if ( preg_match( '/API key provided(: .*)\./', $message, $matches ) ) {
19 $message = str_replace( $matches[1], '', $message );
20 }
21 throw new Exception( $message );
22 }
23 }
24
25 public function listFiles()
26 {
27 return $this->run( 'GET', '/files' );
28 }
29
30 function getSuffixForModel($model)
31 {
32 preg_match("/:([a-zA-Z0-9\-]{1,40})-([0-9]{4})-([0-9]{2})-([0-9]{2})/", $model, $matches);
33 if ( count( $matches ) > 0 ) {
34 return $matches[1];
35 }
36 return 'N/A';
37 }
38
39 function getBaseModel($model)
40 {
41 preg_match("/:([a-zA-Z0-9\-]{1,40})-([0-9]{4})-([0-9]{2})-([0-9]{2})/", $model, $matches);
42 if (count($matches) > 0) {
43 return $matches[1];
44 }
45 return 'N/A';
46 }
47
48 public function listDeletedFineTunes()
49 {
50 $finetunes = $this->run('GET', '/fine-tunes');
51 $deleted = [];
52 $finetunes['data'] = array_filter( $finetunes['data'], function ( $finetune ) use ( &$deleted ) {
53 $name = $finetune['fine_tuned_model'];
54 $isSucceeded = $finetune['status'] === 'succeeded';
55 $exist = true;
56 if ($isSucceeded) {
57 try {
58 $finetune = $this->getModel( $name );
59 }
60 catch ( Exception $e ) {
61 $exist = false;
62 $deleted[] = $name;
63 }
64 }
65 return $exist;
66 });
67 //$this->core->update_option( 'openai_finetunes_deleted', $deleted );
68 return $deleted;
69 }
70
71 public function listFineTunes()
72 {
73 $finetunes = $this->run('GET', '/fine-tunes');
74 $finetunes['data'] = array_map( function ( $finetune ) {
75 $finetune['suffix'] = $this->getSuffixForModel( $finetune['fine_tuned_model'] );
76 return $finetune;
77 }, $finetunes['data']);
78
79 //$finetunes_option = $this->core->get_option('openai_finetunes');
80 $fresh_finetunes_options = array_map(function ($finetune) {
81 $entry = [];
82 $model = $finetune['fine_tuned_model'];
83 $entry['suffix'] = $finetune['suffix'];
84 $entry['model'] = $model;
85 //$entry['enabled'] = true;
86 // for ($i = 0; $i < count($finetunes_option); $i++) {
87 // if ($finetunes_option[$i]['model'] === $model) {
88 // $entry['enabled'] = $finetunes_option[$i]['enabled'];
89 // break;
90 // }
91 // }
92 return $entry;
93 }, $finetunes['data']);
94 $this->core->update_option('openai_finetunes', $fresh_finetunes_options);
95 return $finetunes;
96 }
97
98 public function moderate( $input ) {
99 $result = $this->run('POST', '/moderations', [
100 'input' => $input
101 ]);
102 return $result;
103 }
104
105 public function uploadFile( $filename, $data )
106 {
107 $result = $this->run('POST', '/files', null, [
108 'purpose' => 'fine-tune',
109 'data' => $data,
110 'file' => $filename
111 ] );
112 return $result;
113 }
114
115 public function deleteFile( $fileId )
116 {
117 return $this->run('DELETE', '/files/' . $fileId);
118 }
119
120 public function getModel( $modelId )
121 {
122 return $this->run('GET', '/models/' . $modelId);
123 }
124
125 public function cancelFineTune( $fineTuneId )
126 {
127 return $this->run('POST', '/fine-tunes/' . $fineTuneId . '/cancel');
128 }
129
130 public function deleteFineTune( $modelId )
131 {
132 return $this->run('DELETE', '/models/' . $modelId);
133 }
134
135 public function downloadFile( $fileId )
136 {
137 return $this->run('GET', '/files/' . $fileId . '/content', null, null, false);
138 }
139
140 public function fineTuneFile( $fileId, $model, $suffix, $hyperparams = [] )
141 {
142 $n_epochs = isset( $hyperparams['nEpochs'] ) ? (int)$hyperparams['nEpochs'] : 4;
143 $batch_size = isset( $hyperparams['batchSize'] ) ? (int)$hyperparams['batchSize'] : null;
144 $arguments = [
145 'training_file' => $fileId,
146 'model' => $model,
147 'suffix' => $suffix,
148 'n_epochs' => $n_epochs
149 ];
150 if ( $batch_size ) {
151 $arguments['batch_size'] = $batch_size;
152 }
153 $result = $this->run('POST', '/fine-tunes', $arguments);
154 return $result;
155 }
156
157 /**
158 * Build the body of a form request.
159 * If the field name is 'file', then the field value is the filename of the file to upload.
160 * The file contents are taken from the 'data' field.
161 *
162 * @param array $fields
163 * @param string $boundary
164 * @return string
165 */
166 public function buildFormBody( $fields, $boundary )
167 {
168 $body = '';
169 foreach ( $fields as $name => $value ) {
170 if ( $name == 'data' ) {
171 continue;
172 }
173 $body .= "--$boundary\r\n";
174 $body .= "Content-Disposition: form-data; name=\"$name\"";
175 if ( $name == 'file' ) {
176 $body .= "; filename=\"{$value}\"\r\n";
177 $body .= "Content-Type: application/json\r\n\r\n";
178 $body .= $fields['data'] . "\r\n";
179 }
180 else {
181 $body .= "\r\n\r\n$value\r\n";
182 }
183 }
184 $body .= "--$boundary--\r\n";
185 return $body;
186 }
187
188 /**
189 * Run a request to the OpenAI API.
190 * Fore more information about the $formFields, refer to the buildFormBody method.
191 *
192 * @param string $method POST, PUT, GET, DELETE...
193 * @param string $url The API endpoint
194 * @param array $query The query parameters (json)
195 * @param array $formFields The form fields (multipart/form-data)
196 * @param bool $json Whether to return the response as json or not
197 * @return array
198 */
199 public function run( $method, $url, $query = null, $formFields = null, $json = true )
200 {
201 $apiKey = $this->apiKey;
202 $headers = "Content-Type: application/json\r\n" . "Authorization: Bearer " . $apiKey . "\r\n";
203 $body = $query ? json_encode( $query ) : null;
204 if ( !empty( $formFields ) ) {
205 $boundary = wp_generate_password (24, false );
206 $headers = [
207 'Content-Type' => 'multipart/form-data; boundary=' . $boundary,
208 'Authorization' => 'Bearer ' . $this->apiKey,
209 ];
210 $body = $this->buildFormBody( $formFields, $boundary );
211 }
212
213 $url = 'https://api.openai.com/v1' . $url;
214 $options = [
215 "headers" => $headers,
216 "method" => $method,
217 "timeout" => MWAI_TIMEOUT,
218 "body" => $body,
219 "sslverify" => false
220 ];
221
222 try {
223 $response = wp_remote_request( $url, $options );
224 if ( is_wp_error( $response ) ) {
225 throw new Exception( $response->get_error_message() );
226 }
227 $response = wp_remote_retrieve_body( $response );
228 $data = $json ? json_decode( $response, true ) : $response;
229 $this->handleResponseErrors( $data );
230 return $data;
231 }
232 catch ( Exception $e ) {
233 error_log( $e->getMessage() );
234 throw new Exception( 'Error while calling OpenAI: ' . $e->getMessage() );
235 }
236 }
237
238 private function calculatePrice( $modelFamily, $units, $option = null, $finetune = false )
239 {
240 foreach ( MWAI_OPENAI_MODELS as $currentModel ) {
241 if ( $currentModel['family'] === $modelFamily ) {
242 if ( $currentModel['type'] === 'image' ) {
243 if ( !$option ) {
244 error_log( "AI Engine: Image models require an option." );
245 return null;
246 }
247 else {
248 foreach ( $currentModel['options'] as $imageType ) {
249 if ( $imageType['option'] == $option ) {
250 return $imageType['price'] * $units;
251 }
252 }
253 }
254 }
255 else {
256 if ( $finetune ) {
257 // The price is doubled for finetuned models.
258 return $currentModel['finetune']['price'] * $currentModel['unit'] * $units * 2;
259 }
260 return $currentModel['price'] * $currentModel['unit'] * $units;
261 }
262 }
263 }
264 error_log( "AI Engine: Invalid family ($modelFamily)." );
265 return null;
266 }
267
268 public function getPrice( Meow_MWAI_Query $query, Meow_MWAI_Reply $reply )
269 {
270 $model = $query->model;
271 $family = null;
272 $units = 0;
273 $option = null;
274 $currentModel = null;
275 $priceRules = null;
276
277 $finetune = false;
278 if ( is_a( $query, 'Meow_MWAI_QueryText' ) ) {
279 // Finetuned models
280 if ( preg_match('/^([a-zA-Z]{0,32}):/', $model, $matches ) ) {
281 $family = $matches[1];
282 $finetune = true;
283 }
284 // Standard models
285 else {
286 foreach ( MWAI_OPENAI_MODELS as $currentModel ) {
287 if ( $currentModel['model'] == $model ) {
288 $family = $currentModel['family'];
289 $priceRules = isset( $currentModel['priceRules'] ) ? $currentModel['priceRules'] : null;
290 break;
291 }
292 }
293 }
294 if ( empty( $family ) ) {
295 error_log("AI Engine: Cannot find the base model for $model.");
296 return null;
297 }
298 if ( !empty( $priceRules ) ) {
299 if ( $priceRules === "completion_x2" ) {
300 $units = $reply->getPromptTokens();
301 $units += $reply->getCompletionTokens() * 2;
302 return $this->calculatePrice( $family, $units, $option, $finetune );
303 }
304 else {
305 error_log("AI Engine: Unknown price rules ($priceRules) for $model.");
306 return null;
307 }
308 }
309 else {
310 $units = $reply->getTotalTokens();
311 return $this->calculatePrice( $family, $units, $option, $finetune );
312 }
313 }
314 else if ( is_a( $query, 'Meow_MWAI_QueryImage' ) ) {
315 $family = 'dall-e';
316 $units = $query->maxResults;
317 $option = "1024x1024";
318 return $this->calculatePrice( $family, $units, $option, $finetune );
319 }
320 else if ( is_a( $query, 'Meow_MWAI_QueryTranscribe' ) ) {
321 $family = 'whisper';
322 $units = $reply->getUnits();
323 return $this->calculatePrice( $family, $units, $option, $finetune );
324 }
325 else if ( is_a( $query, 'Meow_MWAI_QueryEmbed' ) ) {
326 foreach ( MWAI_OPENAI_MODELS as $currentModel ) {
327 if ( $currentModel['model'] == $model ) {
328 $family = $currentModel['family'];
329 break;
330 }
331 }
332 $units = $reply->getTotalTokens();
333 return $this->calculatePrice( $family, $units, $option, $finetune );
334 }
335 error_log("AI Engine: Cannot calculate price for $model.");
336 return null;
337 }
338
339 public function getIncidents() {
340 $url = 'https://status.openai.com/history.rss';
341 $response = wp_remote_get( $url );
342 if ( is_wp_error( $response ) ) {
343 throw new Exception( $response->get_error_message() );
344 }
345 $response = wp_remote_retrieve_body( $response );
346 $xml = simplexml_load_string( $response );
347 $incidents = array();
348 $oneWeekAgo = time() - 5 * 24 * 60 * 60;
349 foreach ( $xml->channel->item as $item ) {
350 $date = strtotime( $item->pubDate );
351 if ( $date > $oneWeekAgo ) {
352 $incidents[] = array(
353 'title' => (string) $item->title,
354 'description' => (string) $item->description,
355 'date' => $date
356 );
357 }
358 }
359 return $incidents;
360 }
361 }
362