PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.6.73
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.6.73
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 / core.php
ai-engine / classes / engines Last commit date
core.php 3 years ago openai.php 3 years ago
core.php
333 lines
1 <?php
2
3 class Meow_MWAI_Engines_Core {
4 private $core = null;
5 private $openai = null;
6 private $localApiKey = null;
7 private $localService = null;
8 private $localAzureEndpoint = null;
9 private $localAzureApiKey = null;
10 private $localAzureDeployments = null;
11 private $openaiEndpoint = 'https://api.openai.com/v1';
12 private $azureApiVersion = '?api-version=2023-03-15-preview';
13
14 public function __construct( $core ) {
15 $this->core = $core;
16 $this->openai = new Meow_MWAI_Engines_OpenAI( $this->core );
17 $this->localService = $this->core->get_option( 'openai_service' );
18 $this->localApiKey = $this->core->get_option( 'openai_apikey' );
19 $this->localAzureEndpoint = $this->core->get_option( 'openai_azure_endpoint' );
20 $this->localAzureApiKey = $this->core->get_option( 'openai_azure_apikey' );
21 $this->localAzureDeployments = $this->core->get_option( 'openai_azure_deployments' );
22 }
23
24 private function buildHeaders( $query ) {
25 $headers = array(
26 'Content-Type' => 'application/json',
27 'Authorization' => 'Bearer ' . $query->apiKey,
28 );
29 if ( $query->service === 'azure' ) {
30 $headers = array( 'Content-Type' => 'application/json', 'api-key' => $query->azureApiKey );
31 }
32 return $headers;
33 }
34
35 private function buildOptions( $headers, $json = null, $forms = null ) {
36
37 // Build body
38 $body = null;
39 if ( !empty( $forms ) ) {
40 $boundary = wp_generate_password ( 24, false );
41 $headers['Content-Type'] = 'multipart/form-data; boundary=' . $boundary;
42 $body = $this->openai->buildFormBody( $forms, $boundary );
43 }
44 else if ( !empty( $json ) ) {
45 $body = json_encode( $json );
46 }
47
48 // Build options
49 $options = array(
50 'headers' => $headers,
51 'method' => 'POST',
52 'timeout' => MWAI_TIMEOUT,
53 'body' => $body,
54 'sslverify' => false
55 );
56
57 return $options;
58 }
59
60 private function runQuery( $url, $options ) {
61 try {
62 $response = wp_remote_get( $url, $options );
63 if ( is_wp_error( $response ) ) {
64 throw new Exception( $response->get_error_message() );
65 }
66 $response = wp_remote_retrieve_body( $response );
67
68 // If Headers contains multipart/form-data then we don't need to decode the response
69 if ( strpos( $options['headers']['Content-Type'], 'multipart/form-data' ) !== false ) {
70 return $response;
71 }
72
73 $data = json_decode( $response, true );
74 $this->openai->handleResponseErrors( $data );
75 return $data;
76 }
77 catch ( Exception $e ) {
78 error_log( $e->getMessage() );
79 throw $e;
80 }
81 }
82
83 private function applyQueryParameters( $query ) {
84 if ( empty( $query->service ) ) {
85 $query->service = $this->localService;
86 }
87
88 // OpenAI will be used by default for everything
89 if ( empty( $query->apiKey ) ) {
90 $query->apiKey = $this->localApiKey;
91 }
92
93 // But if the service is set to Azure and the deployments/models are available,
94 // then we will use Azure instead.
95 if ( $query->service === 'azure' && !empty( $this->localAzureDeployments ) ) {
96 $found = false;
97 foreach ( $this->localAzureDeployments as $deployment ) {
98 if ( $deployment['model'] === $query->model ) {
99 $query->azureDeployment = $deployment['name'];
100 if ( empty( $query->azureEndpoint ) ) {
101 $query->azureEndpoint = $this->localAzureEndpoint;
102 }
103 if ( empty( $query->azureApiKey ) ) {
104 $query->azureApiKey = $this->localAzureApiKey;
105 }
106 $found = true;
107 break;
108 }
109 }
110 if ( !$found ) {
111 error_log( 'Azure deployment not found for model: ' . $query->model );
112 $query->service = 'openai';
113 }
114 }
115 }
116
117 private function getAudio( $url ) {
118 require_once( ABSPATH . 'wp-admin/includes/media.php' );
119 $tmpFile = tempnam( sys_get_temp_dir(), 'audio_' );
120 file_put_contents( $tmpFile, file_get_contents( $url ) );
121 $length = null;
122 $metadata = wp_read_audio_metadata( $tmpFile );
123 if ( isset( $metadata['length'] ) ) {
124 $length = $metadata['length'];
125 }
126 $data = file_get_contents( $tmpFile );
127 unlink( $tmpFile );
128 return [ 'data' => $data, 'length' => $length ];
129 }
130
131 private function runTranscribeQuery( $query ) {
132 $this->applyQueryParameters( $query );
133
134 // Prepare the request
135 $modeEndpoint = $query->mode === 'translation' ? 'translations' : 'transcriptions';
136 $url = 'https://api.openai.com/v1/audio/' . $modeEndpoint;
137 $audioData = $this->getAudio( $query->url );
138 $body = array(
139 'prompt' => $query->prompt,
140 'model' => $query->model,
141 'response_format' => 'text',
142 'file' => basename( $query->url ),
143 'data' => $audioData['data']
144 );
145 $headers = $this->buildHeaders( $query );
146 $options = $this->buildOptions( $headers, null, $body );
147
148 // Perform the request
149 try {
150 $data = $this->runQuery( $url, $options );
151 if ( empty( $data ) ) {
152 throw new Exception( 'Invalid data for transcription.' );
153 }
154 $usage = $this->core->recordAudioUsage( $query->model, $audioData['length'] );
155 $reply = new Meow_MWAI_Reply( $query );
156 $reply->setUsage( $usage );
157 $reply->setChoices( $data );
158 return $reply;
159 }
160 catch ( Exception $e ) {
161 error_log( $e->getMessage() );
162 throw new Exception( 'Error while calling OpenAI: ' . $e->getMessage() );
163 }
164 }
165
166 private function runEmbeddingQuery( $query ) {
167 $this->applyQueryParameters( $query );
168
169 // Prepare the request
170 $url = 'https://api.openai.com/v1/embeddings';
171 $body = array( 'input' => $query->prompt, 'model' => $query->model );
172 if ( $query->service === 'azure' ) {
173 $url = trailingslashit( $query->azureEndpoint ) . 'openai/deployments/' .
174 $query->azureDeployment . '/embeddings?api-version=2023-03-15-preview';
175 $body = array( "input" => $query->prompt );
176 }
177 $headers = $this->buildHeaders( $query );
178 $options = $this->buildOptions( $headers, $body );
179
180 // Perform the request
181 try {
182 $data = $this->runQuery( $url, $options );
183 if ( empty( $data ) || !isset( $data['data'] ) ) {
184 throw new Exception( 'Invalid data for embedding.' );
185 }
186 $usage = $data['usage'];
187 $this->core->recordTokensUsage( $query->model, $usage['prompt_tokens'] );
188 $reply = new Meow_MWAI_Reply( $query );
189 $reply->setUsage( $usage );
190 $reply->setChoices( $data['data'] );
191 return $reply;
192 }
193 catch ( Exception $e ) {
194 error_log( $e->getMessage() );
195 throw new Exception( 'Error while calling OpenAI: ' . $e->getMessage() );
196 }
197 }
198
199 private function runCompletionQuery( $query ) {
200 $this->applyQueryParameters( $query );
201 if ( $query->mode !== 'chat' && $query->mode !== 'completion' ) {
202 throw new Exception( 'Unknown mode for query: ' . $query->mode );
203 }
204
205 // Prepare the request
206 $body = array(
207 "model" => $query->model,
208 "stop" => $query->stop,
209 "n" => $query->maxResults,
210 "max_tokens" => $query->maxTokens,
211 "temperature" => $query->temperature,
212 );
213 if ( $query->mode === 'chat' ) {
214 $body['messages'] = $query->messages;
215 }
216 else if ( $query->mode === 'completion' ) {
217 $body['prompt'] = $query->getPrompt();
218 }
219 $url = $query->service === 'azure' ? trailingslashit( $query->azureEndpoint ) .
220 'openai/deployments/' . $query->azureDeployment : $this->openaiEndpoint;
221 if ( $query->mode === 'chat' ) {
222 $url .= $query->service === 'azure' ? '/chat/completions' . $this->azureApiVersion : '/chat/completions';
223 }
224 else if ($query->mode === 'completion') {
225 $url .= $query->service === 'azure' ? '/completions' . $this->azureApiVersion : '/completions';
226 }
227 $headers = $this->buildHeaders( $query );
228 $options = $this->buildOptions( $headers, $body );
229
230 try {
231 $data = $this->runQuery( $url, $options );
232 if ( !$data['model'] ) {
233 error_log( print_r( $data, 1 ) );
234 throw new Exception( "Got an unexpected response from OpenAI. Check your PHP Error Logs." );
235 }
236 $reply = new Meow_MWAI_Reply( $query );
237 try {
238 $usage = $this->core->recordTokensUsage(
239 $data['model'],
240 $data['usage']['prompt_tokens'],
241 $data['usage']['completion_tokens']
242 );
243 }
244 catch ( Exception $e ) {
245 error_log( $e->getMessage() );
246 }
247 $reply->setUsage( $usage );
248 $reply->setChoices( $data['choices'] );
249 return $reply;
250 }
251 catch ( Exception $e ) {
252 error_log( $e->getMessage() );
253 throw new Exception( 'Error while calling OpenAI: ' . $e->getMessage() );
254 }
255 }
256
257 // Request to DALL-E API
258 private function runImagesQuery( $query ) {
259 $this->applyQueryParameters( $query );
260
261 // Prepare the request
262 $url = 'https://api.openai.com/v1/images/generations';
263 $body = array(
264 "prompt" => $query->prompt,
265 "n" => $query->maxResults,
266 "size" => '1024x1024',
267 );
268 $headers = $this->buildHeaders( $query );
269 $options = $this->buildOptions( $headers, $body );
270
271 // Perform the request
272 try {
273 $data = $this->runQuery( $url, $options );
274 $reply = new Meow_MWAI_Reply( $query );
275 $usage = $this->core->recordImagesUsage( "dall-e", "1024x1024", $query->maxResults );
276 $reply->setUsage( $usage );
277 $reply->setChoices( $data['data'] );
278 $reply->setType( 'images' );
279 return $reply;
280 }
281 catch ( Exception $e ) {
282 error_log( $e->getMessage() );
283 throw new Exception( 'Error while calling OpenAI: ' . $e->getMessage() );
284 }
285 }
286
287 public function throwException( $message ) {
288 $message = apply_filters( 'mwai_ai_exception', $message );
289 throw new Exception( $message );
290 }
291
292 public function run( $query ) {
293 // Check if the query is allowed
294 $limits = $this->core->get_option( 'limits' );
295 $ok = apply_filters( 'mwai_ai_allowed', true, $query, $limits );
296 if ( $ok !== true ) {
297 $message = is_string( $ok ) ? $ok : 'Unauthorized query.';
298 $this->throwException( $message );
299 }
300
301 // Allow to modify the query
302 $query = apply_filters( 'mwai_ai_query', $query );
303 $query->finalChecks();
304
305 // Run the query
306 $reply = null;
307 try {
308 if ( $query instanceof Meow_MWAI_QueryText ) {
309 $reply = $this->runCompletionQuery( $query );
310 }
311 else if ( $query instanceof Meow_MWAI_QueryEmbed ) {
312 $reply = $this->runEmbeddingQuery( $query );
313 }
314 else if ( $query instanceof Meow_MWAI_QueryImage ) {
315 $reply = $this->runImagesQuery( $query );
316 }
317 else if ( $query instanceof Meow_MWAI_QueryTranscribe ) {
318 $reply = $this->runTranscribeQuery( $query );
319 }
320 else {
321 $this->throwException( 'Invalid query.' );
322 }
323 }
324 catch ( Exception $e ) {
325 $this->throwException( $e->getMessage() );
326 }
327
328 // Let's allow some modififications of the reply
329 $reply = apply_filters( 'mwai_ai_reply', $reply, $query );
330 return $reply;
331 }
332 }
333