PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.1.1
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.1.1
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 core.php 3 years ago init.php 3 years ago openai.php 3 years ago query.php 3 years ago queryimage.php 3 years ago querytext.php 3 years ago rest.php 3 years ago ui.php 3 years ago
openai.php
293 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, ['data' => $data, 'filename' => $filename]);
103 return $result;
104 }
105
106 public function deleteFile( $fileId )
107 {
108 return $this->run('DELETE', '/files/' . $fileId);
109 }
110
111 public function getModel( $modelId )
112 {
113 return $this->run('GET', '/models/' . $modelId);
114 }
115
116 public function deleteFineTune( $modelId )
117 {
118 return $this->run('DELETE', '/models/' . $modelId);
119 }
120
121 public function downloadFile( $fileId )
122 {
123 return $this->run('GET', '/files/' . $fileId . '/content', null, null, false);
124 }
125
126 public function fineTuneFile( $fileId, $model, $suffix, $hyperparams = [] )
127 {
128 $n_epochs = isset( $hyperparams['nEpochs'] ) ? (int)$hyperparams['nEpochs'] : 4;
129 $batch_size = isset( $hyperparams['batchSize'] ) ? (int)$hyperparams['batchSize'] : null;
130 $arguments = [
131 'training_file' => $fileId,
132 'model' => $model,
133 'suffix' => $suffix,
134 'n_epochs' => $n_epochs
135 ];
136 if ( $batch_size ) {
137 $arguments['batch_size'] = $batch_size;
138 }
139 $result = $this->run('POST', '/fine-tunes', $arguments);
140 return $result;
141 }
142
143 public function create_body_for_file($file, $boundary)
144 {
145 $fields = array(
146 'purpose' => 'fine-tune',
147 'file' => $file['filename']
148 );
149
150 $body = '';
151 foreach ($fields as $name => $value) {
152 $body .= "--$boundary\r\n";
153 $body .= "Content-Disposition: form-data; name=\"$name\"";
154 if ($name == 'file') {
155 $body .= "; filename=\"{$value}\"\r\n";
156 $body .= "Content-Type: application/json\r\n\r\n";
157 $body .= $file['data'] . "\r\n";
158 } else {
159 $body .= "\r\n\r\n$value\r\n";
160 }
161 }
162 $body .= "--$boundary--\r\n";
163 return $body;
164 }
165
166 public function run($method, $url, $query = null, $file = null, $json = true)
167 {
168 $apiKey = $this->apiKey;
169 $headers = "Content-Type: application/json\r\n" . "Authorization: Bearer " . $apiKey . "\r\n";
170 $body = $query ? json_encode($query) : null;
171 if (!empty($file)) {
172 $boundary = wp_generate_password(24, false);
173 $headers = [
174 'Content-Type' => 'multipart/form-data; boundary=' . $boundary,
175 'Authorization' => 'Bearer ' . $this->apiKey,
176 ];
177 $body = $this->create_body_for_file($file, $boundary);
178 }
179
180 $url = 'https://api.openai.com/v1' . $url;
181 $options = [
182 "headers" => $headers,
183 "method" => $method,
184 "timeout" => 120,
185 "body" => $body,
186 "sslverify" => false
187 ];
188
189 try {
190 $response = wp_remote_request($url, $options);
191 if (is_wp_error($response)) {
192 throw new Exception($response->get_error_message());
193 }
194 $response = wp_remote_retrieve_body($response);
195 $data = $json ? json_decode($response, true) : $response;
196
197 // Error handling
198 if (isset($data['error'])) {
199 $message = $data['error']['message'];
200 // If the message contains "Incorrect API key provided: THE_KEY.", replace the key by "----".
201 if (preg_match('/API key provided(: .*)\./', $message, $matches)) {
202 $message = str_replace($matches[1], '', $message);
203 }
204 throw new Exception($message);
205 }
206
207 return $data;
208 }
209 catch (Exception $e) {
210 error_log($e->getMessage());
211 throw new Exception('Error while calling OpenAI: ' . $e->getMessage());
212 }
213 }
214
215 private function calculatePrice( $model, $units, $option = null )
216 {
217 foreach ( MWAI_OPENAI_PRICING as $price ) {
218 if ( $price['model'] == $model ) {
219 if ( $price['type'] == 'image' ) {
220 if ( !$option ) {
221 error_log( "AI Engine: Image models require an option." );
222 return null;
223 }
224 else {
225 foreach ( $price['options'] as $imageType ) {
226 if ( $imageType['option'] == $option ) {
227 return $imageType['price'] * $units;
228 }
229 }
230 }
231 }
232 else {
233 return $price['price'] * $price['unit'] * $units;
234 }
235 }
236 }
237 error_log( "AI Engine: Invalid model ($model)." );
238 return null;
239 }
240
241 public function getPrice( Meow_MWAI_Query $query, Meow_MWAI_Answer $answer )
242 {
243 $model = $query->model;
244 $modelBase = null;
245 $units = 0;
246 $option = null;
247 if ( is_a( $query, 'Meow_MWAI_QueryText' ) ) {
248 // Finetuned models
249 if ( preg_match('/^([a-zA-Z]{0,32}):/', $model, $matches ) ) {
250 $modelBase = "fn-" . $matches[1];
251 }
252 // Standard models
253 else if ( preg_match('/^text-(\w+)-\d+/', $model, $matches ) ) {
254 $modelBase = $matches[1];
255 }
256 if ( empty( $modelBase ) ) {
257 error_log("AI Engine: Cannot find the base model for $model.");
258 return null;
259 }
260 $units = $answer->getTotalTokens();
261 }
262 else if ( is_a( $query, 'Meow_MWAI_QueryImage' ) ) {
263 $modelBase = 'dall-e';
264 $units = $query->maxResults;
265 $option = "1024x1024";
266 }
267 return $this->calculatePrice( $modelBase, $units, $option );
268 }
269
270 public function getIncidents() {
271 $url = 'https://status.openai.com/history.rss';
272 $response = wp_remote_get( $url );
273 if ( is_wp_error( $response ) ) {
274 throw new Exception( $response->get_error_message() );
275 }
276 $response = wp_remote_retrieve_body( $response );
277 $xml = simplexml_load_string( $response );
278 $incidents = array();
279 $oneWeekAgo = time() - 7 * 24 * 60 * 60;
280 foreach ( $xml->channel->item as $item ) {
281 $date = strtotime( $item->pubDate );
282 if ( $date > $oneWeekAgo ) {
283 $incidents[] = array(
284 'title' => (string) $item->title,
285 'description' => (string) $item->description,
286 'date' => $date
287 );
288 }
289 }
290 return $incidents;
291 }
292 }
293