PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.3.38
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.3.38
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
341 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 // Why * 2? Need to check why I did this.
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
275 $finetune = false;
276 if ( is_a( $query, 'Meow_MWAI_QueryText' ) ) {
277 // Finetuned models
278 if ( preg_match('/^([a-zA-Z]{0,32}):/', $model, $matches ) ) {
279 $family = $matches[1];
280 $finetune = true;
281 }
282 // Standard models
283 else {
284 foreach ( MWAI_OPENAI_MODELS as $currentModel ) {
285 if ( $currentModel['model'] == $model ) {
286 $family = $currentModel['family'];
287 break;
288 }
289 }
290 }
291 if ( empty( $family ) ) {
292 error_log("AI Engine: Cannot find the base model for $model.");
293 return null;
294 }
295 $units = $answer->getTotalTokens();
296 }
297 else if ( is_a( $query, 'Meow_MWAI_QueryImage' ) ) {
298 $family = 'dall-e';
299 $units = $query->maxResults;
300 $option = "1024x1024";
301 }
302 else if ( is_a( $query, 'Meow_MWAI_QueryEmbed' ) ) {
303 foreach ( MWAI_OPENAI_MODELS as $currentModel ) {
304 if ( $currentModel['model'] == $model ) {
305 $family = $currentModel['family'];
306 break;
307 }
308 }
309 $units = $answer->getTotalTokens();
310 }
311 else {
312 error_log("AI Engine: Cannot find the base model for $model.");
313 return null;
314 }
315 return $this->calculatePrice( $family, $units, $option, $finetune );
316 }
317
318 public function getIncidents() {
319 $url = 'https://status.openai.com/history.rss';
320 $response = wp_remote_get( $url );
321 if ( is_wp_error( $response ) ) {
322 throw new Exception( $response->get_error_message() );
323 }
324 $response = wp_remote_retrieve_body( $response );
325 $xml = simplexml_load_string( $response );
326 $incidents = array();
327 $oneWeekAgo = time() - 7 * 24 * 60 * 60;
328 foreach ( $xml->channel->item as $item ) {
329 $date = strtotime( $item->pubDate );
330 if ( $date > $oneWeekAgo ) {
331 $incidents[] = array(
332 'title' => (string) $item->title,
333 'description' => (string) $item->description,
334 'date' => $date
335 );
336 }
337 }
338 return $incidents;
339 }
340 }
341