PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.6.78
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.6.78
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
323 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 run( $query ) {
288 // Check if the query is allowed
289 $limits = $this->core->get_option( 'limits' );
290 $ok = apply_filters( 'mwai_ai_allowed', true, $query, $limits );
291 if ( $ok !== true ) {
292 $message = is_string( $ok ) ? $ok : 'Unauthorized query.';
293 throw new Exception( $message );
294 }
295
296 // Allow to modify the query
297 $query = apply_filters( 'mwai_ai_query', $query );
298 $query->finalChecks();
299
300 // Run the query
301 $reply = null;
302 if ( $query instanceof Meow_MWAI_QueryText ) {
303 $reply = $this->runCompletionQuery( $query );
304 }
305 else if ( $query instanceof Meow_MWAI_QueryEmbed ) {
306 $reply = $this->runEmbeddingQuery( $query );
307 }
308 else if ( $query instanceof Meow_MWAI_QueryImage ) {
309 $reply = $this->runImagesQuery( $query );
310 }
311 else if ( $query instanceof Meow_MWAI_QueryTranscribe ) {
312 $reply = $this->runTranscribeQuery( $query );
313 }
314 else {
315 throw new Exception( 'Unknown query type.' );
316 }
317
318 // Let's allow some modififications of the reply
319 $reply = apply_filters( 'mwai_ai_reply', $reply, $query );
320 return $reply;
321 }
322 }
323