PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.3.62
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.3.62
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 / openai.php
ai-engine / classes Last commit date
modules 3 years ago admin.php 3 years ago ai.php 3 years ago answer.php 3 years ago api.php 3 years ago core.php 3 years ago init.php 3 years ago openai.php 3 years ago query.php 3 years ago queryembed.php 3 years ago queryimage.php 3 years ago querytext.php 3 years ago querytranscribe.php 3 years ago rest.php 3 years ago ui.php 3 years ago
openai.php
357 lines
1 <?php
2
3 class Meow_MWAI_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 public function listFiles()
15 {
16 return $this->run( 'GET', '/files' );
17 }
18
19 function getSuffixForModel($model)
20 {
21 preg_match("/:([a-zA-Z\-]{1,40})-([0-9]{4})-([0-9]{2})-([0-9]{2})/", $model, $matches);
22 if ( count( $matches ) > 0 ) {
23 return $matches[1];
24 }
25 return 'N/A';
26 }
27
28 function getBaseModel($model)
29 {
30 preg_match("/:([a-zA-Z\-]{1,40})-([0-9]{4})-([0-9]{2})-([0-9]{2})/", $model, $matches);
31 if (count($matches) > 0) {
32 return $matches[1];
33 }
34 return 'N/A';
35 }
36
37 public function listFineTunes( $clean = false )
38 {
39 $finetunes = $this->run('GET', '/fine-tunes');
40
41 if ( $clean ) {
42 $deleted = [];
43 $finetunes['data'] = array_filter( $finetunes['data'], function ( $finetune ) use ( &$deleted ) {
44 $name = $finetune['fine_tuned_model'];
45 $isSucceeded = $finetune['status'] === 'succeeded';
46 $exist = true;
47 if ($isSucceeded) {
48 try {
49 $finetune = $this->getModel( $name );
50 }
51 catch ( Exception $e ) {
52 $exist = false;
53 $deleted[] = $name;
54 }
55 }
56 return $exist;
57 });
58 $this->core->update_option( 'openai_finetunes_deleted', $deleted );
59 }
60
61 $finetunes['data'] = array_map( function ( $finetune ) {
62 $finetune['suffix'] = $this->getSuffixForModel( $finetune['fine_tuned_model'] );
63 return $finetune;
64 }, $finetunes['data']);
65
66 // Get option openai_finetunes_deleted
67 $deleted_finetunes = $this->core->get_option('openai_finetunes_deleted');
68
69 // Remove all deleted finetunes from the list, make a new array, without using array_filter
70 $finetunes['data'] = array_values(array_filter($finetunes['data'], function ($finetune) use ($deleted_finetunes) {
71 return !in_array($finetune['fine_tuned_model'], $deleted_finetunes);
72 }));
73
74 $finetunes_option = $this->core->get_option('openai_finetunes');
75 $fresh_finetunes_options = array_map(function ($finetune) use ($finetunes_option) {
76 $entry = [];
77 $model = $finetune['fine_tuned_model'];
78 $entry['suffix'] = $finetune['suffix'];
79 $entry['model'] = $model;
80 $entry['enabled'] = true;
81 for ($i = 0; $i < count($finetunes_option); $i++) {
82 if ($finetunes_option[$i]['model'] === $model) {
83 $entry['enabled'] = $finetunes_option[$i]['enabled'];
84 break;
85 }
86 }
87 return $entry;
88 }, $finetunes['data']);
89 $this->core->update_option('openai_finetunes', $fresh_finetunes_options);
90 return $finetunes;
91 }
92
93 public function moderate( $input ) {
94 $result = $this->run('POST', '/moderations', [
95 'input' => $input
96 ]);
97 return $result;
98 }
99
100 public function uploadFile( $filename, $data )
101 {
102 $result = $this->run('POST', '/files', null, [
103 'purpose' => 'fine-tune',
104 'data' => $data,
105 'file' => $filename
106 ] );
107 return $result;
108 }
109
110 public function deleteFile( $fileId )
111 {
112 return $this->run('DELETE', '/files/' . $fileId);
113 }
114
115 public function getModel( $modelId )
116 {
117 return $this->run('GET', '/models/' . $modelId);
118 }
119
120 public function deleteFineTune( $modelId )
121 {
122 return $this->run('DELETE', '/models/' . $modelId);
123 }
124
125 public function downloadFile( $fileId )
126 {
127 return $this->run('GET', '/files/' . $fileId . '/content', null, null, false);
128 }
129
130 public function fineTuneFile( $fileId, $model, $suffix, $hyperparams = [] )
131 {
132 $n_epochs = isset( $hyperparams['nEpochs'] ) ? (int)$hyperparams['nEpochs'] : 4;
133 $batch_size = isset( $hyperparams['batchSize'] ) ? (int)$hyperparams['batchSize'] : null;
134 $arguments = [
135 'training_file' => $fileId,
136 'model' => $model,
137 'suffix' => $suffix,
138 'n_epochs' => $n_epochs
139 ];
140 if ( $batch_size ) {
141 $arguments['batch_size'] = $batch_size;
142 }
143 $result = $this->run('POST', '/fine-tunes', $arguments);
144 return $result;
145 }
146
147 /**
148 * Build the body of a form request.
149 * If the field name is 'file', then the field value is the filename of the file to upload.
150 * The file contents are taken from the 'data' field.
151 *
152 * @param array $fields
153 * @param string $boundary
154 * @return string
155 */
156 public function buildFormBody( $fields, $boundary )
157 {
158 $body = '';
159 foreach ( $fields as $name => $value ) {
160 if ( $name == 'data' ) {
161 continue;
162 }
163 $body .= "--$boundary\r\n";
164 $body .= "Content-Disposition: form-data; name=\"$name\"";
165 if ( $name == 'file' ) {
166 $body .= "; filename=\"{$value}\"\r\n";
167 $body .= "Content-Type: application/json\r\n\r\n";
168 $body .= $fields['data'] . "\r\n";
169 }
170 else {
171 $body .= "\r\n\r\n$value\r\n";
172 }
173 }
174 $body .= "--$boundary--\r\n";
175 return $body;
176 }
177
178 /**
179 * Run a request to the OpenAI API.
180 * Fore more information about the $formFields, refer to the buildFormBody method.
181 *
182 * @param string $method POST, PUT, GET, DELETE...
183 * @param string $url The API endpoint
184 * @param array $query The query parameters (json)
185 * @param array $formFields The form fields (multipart/form-data)
186 * @param bool $json Whether to return the response as json or not
187 * @return array
188 */
189 public function run( $method, $url, $query = null, $formFields = null, $json = true )
190 {
191 $apiKey = $this->apiKey;
192 $headers = "Content-Type: application/json\r\n" . "Authorization: Bearer " . $apiKey . "\r\n";
193 $body = $query ? json_encode( $query ) : null;
194 if ( !empty( $formFields ) ) {
195 $boundary = wp_generate_password (24, false );
196 $headers = [
197 'Content-Type' => 'multipart/form-data; boundary=' . $boundary,
198 'Authorization' => 'Bearer ' . $this->apiKey,
199 ];
200 $body = $this->buildFormBody( $formFields, $boundary );
201 }
202
203 $url = 'https://api.openai.com/v1' . $url;
204 $options = [
205 "headers" => $headers,
206 "method" => $method,
207 "timeout" => 120,
208 "body" => $body,
209 "sslverify" => false
210 ];
211
212 try {
213 $response = wp_remote_request( $url, $options );
214 if ( is_wp_error( $response ) ) {
215 throw new Exception( $response->get_error_message() );
216 }
217 $response = wp_remote_retrieve_body( $response );
218 $data = $json ? json_decode( $response, true ) : $response;
219
220 // Error handling
221 if ( isset( $data['error'] ) ) {
222 $message = $data['error']['message'];
223 // If the message contains "Incorrect API key provided: THE_KEY.", replace the key by "----".
224 if ( preg_match( '/API key provided(: .*)\./', $message, $matches ) ) {
225 $message = str_replace( $matches[1], '', $message );
226 }
227 throw new Exception( $message );
228 }
229
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_Answer $answer )
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 = $currentModel['priceRules'];
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 = $answer->getPromptTokens();
301 $units += $answer->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 = $answer->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_QueryEmbed' ) ) {
321 foreach ( MWAI_OPENAI_MODELS as $currentModel ) {
322 if ( $currentModel['model'] == $model ) {
323 $family = $currentModel['family'];
324 break;
325 }
326 }
327 $units = $answer->getTotalTokens();
328 return $this->calculatePrice( $family, $units, $option, $finetune );
329 }
330 error_log("AI Engine: Cannot calculate price for $model.");
331 return null;
332 }
333
334 public function getIncidents() {
335 $url = 'https://status.openai.com/history.rss';
336 $response = wp_remote_get( $url );
337 if ( is_wp_error( $response ) ) {
338 throw new Exception( $response->get_error_message() );
339 }
340 $response = wp_remote_retrieve_body( $response );
341 $xml = simplexml_load_string( $response );
342 $incidents = array();
343 $oneWeekAgo = time() - 5 * 24 * 60 * 60;
344 foreach ( $xml->channel->item as $item ) {
345 $date = strtotime( $item->pubDate );
346 if ( $date > $oneWeekAgo ) {
347 $incidents[] = array(
348 'title' => (string) $item->title,
349 'description' => (string) $item->description,
350 'date' => $date
351 );
352 }
353 }
354 return $incidents;
355 }
356 }
357