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