PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 2.8.6
AI Engine – The Chatbot, AI Framework & MCP for WordPress v2.8.6
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 / api.php
ai-engine / classes Last commit date
data 1 year ago engines 1 year ago exceptions 1 year ago modules 1 year ago query 1 year ago rest 1 year ago services 1 year ago admin.php 1 year ago api.php 1 year ago core.php 1 year ago discussion.php 1 year ago event.php 1 year ago init.php 11 months ago logging.php 1 year ago reply.php 1 year ago rest.php 1 year ago
api.php
798 lines
1 <?php
2
3 class Meow_MWAI_API {
4 public $core;
5 private $chatbot_module;
6 private $discussions_module;
7 private $bearer_token;
8 private $debug = false;
9
10 public function __construct( $chatbot_module, $discussions_module ) {
11 global $mwai_core;
12 $this->core = $mwai_core;
13 $this->chatbot_module = $chatbot_module;
14 $this->discussions_module = $discussions_module;
15 add_action( 'rest_api_init', [ $this, 'rest_api_init' ] );
16 $this->debug = $this->core->get_option( 'server_debug_mode' );
17 }
18
19 #region REST API
20 public function rest_api_init() {
21 $public_api = $this->core->get_option( 'public_api' );
22 if ( !$public_api ) {
23 return;
24 }
25 $this->bearer_token = $this->core->get_option( 'public_api_bearer_token' );
26 if ( !empty( $this->bearer_token ) ) {
27 add_filter( 'mwai_allow_public_api', [ $this, 'auth_via_bearer_token' ], 10, 3 );
28 }
29
30 register_rest_route( 'mwai/v1', '/simpleAuthCheck', [
31 'methods' => 'GET',
32 'callback' => [ $this, 'rest_simpleAuthCheck' ],
33 'permission_callback' => function ( $request ) {
34 return $this->core->can_access_public_api( 'simpleAuthCheck', $request );
35 },
36 ] );
37 register_rest_route( 'mwai/v1', '/simpleTextQuery', [
38 'methods' => 'POST',
39 'callback' => [ $this, 'rest_simpleTextQuery' ],
40 'permission_callback' => function ( $request ) {
41 return $this->core->can_access_public_api( 'simpleTextQuery', $request );
42 },
43 ] );
44 register_rest_route( 'mwai/v1', '/simpleImageQuery', [
45 'methods' => 'POST',
46 'callback' => [ $this, 'rest_simpleImageQuery' ],
47 'permission_callback' => function ( $request ) {
48 return $this->core->can_access_public_api( 'simpleImageQuery', $request );
49 },
50 ] );
51 register_rest_route( 'mwai/v1', '/simpleImageEditQuery', [
52 'methods' => 'POST',
53 'callback' => [ $this, 'rest_simpleImageEditQuery' ],
54 'permission_callback' => function ( $request ) {
55 return $this->core->can_access_public_api( 'simpleImageEditQuery', $request );
56 },
57 ] );
58 register_rest_route( 'mwai/v1', '/simpleVisionQuery', [
59 'methods' => 'POST',
60 'callback' => [ $this, 'rest_simpleVisionQuery' ],
61 'permission_callback' => function ( $request ) {
62 return $this->core->can_access_public_api( 'simpleVisionQuery', $request );
63 },
64 ] );
65 register_rest_route( 'mwai/v1', '/simpleJsonQuery', [
66 'methods' => 'POST',
67 'callback' => [ $this, 'rest_simpleJsonQuery' ],
68 'permission_callback' => function ( $request ) {
69 return $this->core->can_access_public_api( 'simpleJsonQuery', $request );
70 },
71 ] );
72 register_rest_route( 'mwai/v1', '/moderationCheck', [
73 'methods' => 'POST',
74 'callback' => [ $this, 'rest_moderationCheck' ],
75 'permission_callback' => function ( $request ) {
76 return $this->core->can_access_public_api( 'moderationCheck', $request );
77 },
78 ] );
79
80 if ( $this->chatbot_module ) {
81 register_rest_route( 'mwai/v1', '/simpleChatbotQuery', [
82 'methods' => 'POST',
83 'callback' => [ $this, 'rest_simpleChatbotQuery' ],
84 'permission_callback' => function ( $request ) {
85 return $this->core->can_access_public_api( 'simpleChatbotQuery', $request );
86 },
87 ] );
88
89 register_rest_route( 'mwai/v1', '/listChatbots', [
90 'methods' => 'GET',
91 'callback' => [ $this, 'rest_listChatbots' ],
92 'permission_callback' => function ( $request ) {
93 return $this->core->can_access_public_api( 'listChatbots', $request );
94 },
95 ] );
96 }
97 }
98
99 public function rest_simpleAuthCheck( $request ) {
100 try {
101 $params = $request->get_params();
102 $current_user = wp_get_current_user();
103 $current_email = $current_user->user_email;
104 return new WP_REST_Response( [ 'success' => true, 'data' => [
105 'type' => 'email',
106 'value' => $current_email
107 ] ], 200 );
108 }
109 catch ( Exception $e ) {
110 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
111 }
112 }
113
114 public function auth_via_bearer_token( $allow, $feature, $extra ) {
115 if ( !empty( $extra ) && !empty( $extra->get_header( 'Authorization' ) ) ) {
116 $token = $extra->get_header( 'Authorization' );
117 $token = str_replace( 'Bearer ', '', $token );
118 if ( $token === $this->bearer_token ) {
119 // We set the current user to the first admin.
120 $admin = $this->core->get_admin_user();
121 wp_set_current_user( $admin->ID, $admin->user_login );
122 return true;
123 }
124 }
125 return $allow;
126 }
127
128 public function rest_simpleChatbotQuery( $request ) {
129 try {
130 $params = $request->get_params();
131 $botId = isset( $params['botId'] ) ? $params['botId'] : '';
132 $message = isset( $params['message'] ) ? $params['message'] : '';
133 if ( empty( $message ) ) {
134 $message = isset( $params['prompt'] ) ? $params['prompt'] : '';
135 }
136 $chatId = isset( $params['chatId'] ) ? $params['chatId'] : null;
137 $params = null;
138 if ( !empty( $chatId ) ) {
139 $params = [ 'chatId' => $chatId ];
140 }
141 if ( empty( $botId ) || empty( $message ) ) {
142 throw new Exception( 'The botId and message are required.' );
143 }
144
145 if ( $this->debug ) {
146 $shortMessage = Meow_MWAI_Logging::shorten( $message, 64 );
147 $debug = sprintf( 'REST [SimpleChatbotQuery]: %s, %s', $shortMessage, json_encode( $params ) );
148 Meow_MWAI_Logging::log( $debug );
149 }
150
151 $reply = $this->simpleChatbotQuery( $botId, $message, $params, false );
152 return new WP_REST_Response( [
153 'success' => true,
154 'data' => $reply['reply'],
155 'extra' => [
156 'actions' => $reply['actions'],
157 'chatId' => $reply['chatId']
158 ]
159 ], 200 );
160 }
161 catch ( Exception $e ) {
162 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
163 }
164 }
165
166 public function rest_listChatbots( $request ) {
167 try {
168 // Get all chatbots
169 $chatbots = get_option( 'mwai_chatbots', [] );
170 $environments = $this->core->get_option( 'ai_envs' );
171 $mcp_envs = $this->core->get_option( 'mcp_envs', [] );
172
173 // Get all models from all environments
174 $all_models = [];
175 foreach ( $environments as $env ) {
176 try {
177 $engine = Meow_MWAI_Engines_Factory::get( $this->core, $env['id'] );
178 $env_models = $engine->retrieve_models();
179 foreach ( $env_models as $model ) {
180 $all_models[$model['model']] = $model;
181 }
182 }
183 catch ( Exception $e ) {
184 // Skip environments that fail
185 }
186 }
187
188 // Debug: Log model info for gpt-4.1-mini
189 if ( $this->debug && isset( $all_models['gpt-4.1-mini'] ) ) {
190 error_log( '[AI Engine API] Model info for gpt-4.1-mini: ' . json_encode( $all_models['gpt-4.1-mini'] ) );
191 }
192
193 // Get registered functions
194 $functions = apply_filters( 'mwai_functions_list', [] );
195 $function_names = [];
196 foreach ( $functions as $function ) {
197 $function_names[] = $function->name ?? 'unknown';
198 }
199
200 $result = [];
201
202 foreach ( $chatbots as $chatbotId => $chatbot ) {
203 // Debug log the chatbot structure
204 if ( $this->debug && ( $chatbot['name'] ?? '' ) === 'Jordy' ) {
205 error_log( '[AI Engine API] Jordy chatbot structure: ' . json_encode( $chatbot ) );
206 }
207
208 // Basic info
209 $info = [
210 'id' => $chatbot['botId'] ?? $chatbotId, // Use botId if available, fallback to array index
211 'name' => $chatbot['name'] ?? 'Unnamed',
212 'type' => 'chat', // Default type
213 'model' => null,
214 'model_name' => null,
215 'environment' => null,
216 'environment_name' => null,
217 'functions' => [],
218 'tools' => [], // Add tools array
219 'mcp_servers' => []
220 ];
221
222 // Determine chatbot type
223 if ( !empty( $chatbot['type'] ) ) {
224 $info['type'] = $chatbot['type'];
225 }
226 else {
227 // Try to infer type from model or other properties
228 if ( !empty( $chatbot['model'] ) ) {
229 if ( strpos( $chatbot['model'], 'realtime' ) !== false ) {
230 $info['type'] = 'realtime';
231 }
232 else if ( strpos( $chatbot['model'], 'image' ) !== false || strpos( $chatbot['model'], 'dall-e' ) !== false ) {
233 $info['type'] = 'images';
234 }
235 else if ( !empty( $chatbot['assistantId'] ) ) {
236 $info['type'] = 'assistant';
237 }
238 }
239 }
240
241 // Get model info
242 if ( !empty( $chatbot['model'] ) ) {
243 $info['model'] = $chatbot['model'];
244 // Find model name
245 if ( isset( $all_models[$chatbot['model']] ) ) {
246 $info['model_name'] = $all_models[$chatbot['model']]['name'] ?? $chatbot['model'];
247 }
248 else {
249 $info['model_name'] = $chatbot['model'];
250 }
251 }
252
253 // Get environment info
254 if ( !empty( $chatbot['envId'] ) ) {
255 $info['environment'] = $chatbot['envId'];
256 // Find environment name
257 foreach ( $environments as $env ) {
258 if ( $env['id'] === $chatbot['envId'] ) {
259 $info['environment_name'] = $env['name'] ?? $chatbot['envId'];
260 break;
261 }
262 }
263 }
264
265 // Check if it uses functions - get specific function names if available
266 if ( !empty( $chatbot['functions'] ) ) {
267 if ( is_array( $chatbot['functions'] ) ) {
268 // Functions are stored as array of objects with id and type
269 $chatbot_functions = [];
270 foreach ( $chatbot['functions'] as $func ) {
271 if ( isset( $func['id'] ) ) {
272 // Try to find function name by ID
273 $func_name = $this->get_function_name_by_id( $func['id'] );
274 if ( $func_name ) {
275 $chatbot_functions[] = $func_name;
276 }
277 else {
278 // Fallback: include the ID if name not found
279 $chatbot_functions[] = 'function_' . $func['id'];
280 }
281 }
282 }
283 $info['functions'] = $chatbot_functions;
284 }
285 else if ( $chatbot['functions'] === true ) {
286 // If functions is just true, it uses all registered functions
287 $info['functions'] = $function_names;
288 }
289 }
290
291 // Check for tools (Web Search, Image Generation)
292 if ( !empty( $chatbot['tools'] ) && is_array( $chatbot['tools'] ) ) {
293 // Filter tools based on model capabilities
294 $supported_tools = [];
295 if ( !empty( $chatbot['model'] ) ) {
296 // Try exact match first
297 $model_key = $chatbot['model'];
298 $model_info = $all_models[$model_key] ?? null;
299
300 // If not found and it's an OpenRouter model, try without prefix
301 if ( !$model_info && strpos( $model_key, '/' ) !== false ) {
302 $model_key = substr( $model_key, strpos( $model_key, '/' ) + 1 );
303 $model_info = $all_models[$model_key] ?? null;
304 }
305
306 if ( $model_info ) {
307 $model_tools = $model_info['tools'] ?? [];
308
309 // Only include tools that are both configured AND supported by the model
310 foreach ( $chatbot['tools'] as $tool ) {
311 if ( in_array( $tool, $model_tools ) ) {
312 $supported_tools[] = $tool;
313 }
314 }
315 }
316 else {
317 // If model not found in our list, keep all configured tools
318 // This allows custom models to use tools
319 $supported_tools = $chatbot['tools'];
320 }
321 }
322 $info['tools'] = $supported_tools;
323 }
324
325 // Check for MCP servers
326 if ( !empty( $chatbot['mcp_servers'] ) && is_array( $chatbot['mcp_servers'] ) ) {
327 foreach ( $chatbot['mcp_servers'] as $mcpServer ) {
328 if ( !empty( $mcpServer['id'] ) ) {
329 // Find MCP server name
330 foreach ( $mcp_envs as $mcp ) {
331 if ( $mcp['id'] === $mcpServer['id'] ) {
332 $info['mcp_servers'][] = [
333 'id' => $mcp['id'],
334 'name' => $mcp['name'] ?? 'Unnamed MCP Server'
335 ];
336 break;
337 }
338 }
339 }
340 }
341 }
342
343 $result[] = $info;
344 }
345
346 return new WP_REST_Response( [
347 'success' => true,
348 'data' => $result
349 ], 200 );
350 }
351 catch ( Exception $e ) {
352 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
353 }
354 }
355
356 public function rest_simpleTextQuery( $request ) {
357 try {
358 $params = $request->get_params();
359 $message = isset( $params['message'] ) ? $params['message'] : '';
360 if ( empty( $message ) ) {
361 $message = isset( $params['prompt'] ) ? $params['prompt'] : '';
362 }
363 $options = isset( $params['options'] ) ? $params['options'] : [];
364 $scope = isset( $params['scope'] ) ? $params['scope'] : 'public-api';
365 if ( !empty( $scope ) ) {
366 $options['scope'] = $scope;
367 }
368 if ( empty( $message ) ) {
369 throw new Exception( 'The message is required.' );
370 }
371
372 if ( $this->debug ) {
373 $shortMessage = Meow_MWAI_Logging::shorten( $message, 64 );
374 $debug = sprintf( 'REST [SimpleTextQuery]: %s, %s', $shortMessage, json_encode( $options ) );
375 Meow_MWAI_Logging::log( $debug );
376 }
377
378 $reply = $this->simpleTextQuery( $message, $options );
379 return new WP_REST_Response( [ 'success' => true, 'data' => $reply ], 200 );
380 }
381 catch ( Exception $e ) {
382 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
383 }
384 }
385
386 public function rest_simpleImageQuery( $request ) {
387 try {
388 $params = $request->get_params();
389 $message = isset( $params['message'] ) ? $params['message'] : '';
390 if ( empty( $message ) ) {
391 $message = isset( $params['prompt'] ) ? $params['prompt'] : '';
392 }
393 $options = isset( $params['options'] ) ? $params['options'] : [];
394 $resolution = isset( $params['resolution'] ) ? $params['resolution'] : '';
395 $scope = isset( $params['scope'] ) ? $params['scope'] : 'public-api';
396 if ( !empty( $scope ) ) {
397 $options['scope'] = $scope;
398 }
399 if ( empty( $message ) ) {
400 throw new Exception( 'The message is required.' );
401 }
402 if ( !empty( $resolution ) ) {
403 $options['resolution'] = $resolution;
404 }
405
406 if ( $this->debug ) {
407 $shortMessage = Meow_MWAI_Logging::shorten( $message, 64 );
408 $debug = sprintf( 'REST [SimpleImageQuery]: %s, %s', $shortMessage, json_encode( $options ) );
409 Meow_MWAI_Logging::log( $debug );
410 }
411
412 $reply = $this->simpleImageQuery( $message, $options );
413 return new WP_REST_Response( [ 'success' => true, 'data' => $reply ], 200 );
414 }
415 catch ( Exception $e ) {
416 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
417 }
418 }
419
420 public function rest_simpleImageEditQuery( $request ) {
421 try {
422 $params = $request->get_params();
423 $message = isset( $params['message'] ) ? $params['message'] : '';
424 if ( empty( $message ) ) {
425 $message = isset( $params['prompt'] ) ? $params['prompt'] : '';
426 }
427 $mediaId = isset( $params['mediaId'] ) ? intval( $params['mediaId'] ) : 0;
428 $options = isset( $params['options'] ) ? $params['options'] : [];
429 $resolution = isset( $params['resolution'] ) ? $params['resolution'] : '';
430 $scope = isset( $params['scope'] ) ? $params['scope'] : 'public-api';
431 if ( !empty( $scope ) ) {
432 $options['scope'] = $scope;
433 }
434 if ( empty( $message ) ) {
435 throw new Exception( 'The message is required.' );
436 }
437 if ( empty( $mediaId ) ) {
438 throw new Exception( 'The mediaId is required.' );
439 }
440 if ( !empty( $resolution ) ) {
441 $options['resolution'] = $resolution;
442 }
443
444 if ( $this->debug ) {
445 $shortMessage = Meow_MWAI_Logging::shorten( $message, 64 );
446 $debug = sprintf( 'REST [SimpleImageEditQuery]: %s, %s', $shortMessage, json_encode( $options ) );
447 Meow_MWAI_Logging::log( $debug );
448 }
449
450 $reply = $this->simpleImageEditQuery( $message, $mediaId, $options );
451 return new WP_REST_Response( [ 'success' => true, 'data' => $reply ], 200 );
452 }
453 catch ( Exception $e ) {
454 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
455 }
456 }
457
458 public function rest_simpleVisionQuery( $request ) {
459 try {
460 $params = $request->get_params();
461 $message = isset( $params['message'] ) ? $params['message'] : '';
462 if ( empty( $message ) ) {
463 $message = isset( $params['prompt'] ) ? $params['prompt'] : '';
464 }
465 $url = isset( $params['url'] ) ? $params['url'] : '';
466 $options = isset( $params['options'] ) ? $params['options'] : [];
467 $scope = isset( $params['scope'] ) ? $params['scope'] : 'public-api';
468 if ( !empty( $scope ) ) {
469 $options['scope'] = $scope;
470 }
471 if ( empty( $message ) ) {
472 throw new Exception( 'The message is required.' );
473 }
474 if ( empty( $url ) ) {
475 throw new Exception( 'The url is required.' );
476 }
477
478 if ( $this->debug ) {
479 $shortMessage = Meow_MWAI_Logging::shorten( $message, 64 );
480 $debug = sprintf( 'REST [SimpleVisionQuery]: %s, %s', $shortMessage, json_encode( $options ) );
481 Meow_MWAI_Logging::log( $debug );
482 }
483
484 $reply = $this->simpleVisionQuery( $message, $url, null, $options );
485 return new WP_REST_Response( [ 'success' => true, 'data' => $reply ], 200 );
486 }
487 catch ( Exception $e ) {
488 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
489 }
490 }
491
492 public function rest_simpleJsonQuery( $request ) {
493 try {
494 $params = $request->get_params();
495 $message = isset( $params['message'] ) ? $params['message'] : '';
496 if ( empty( $message ) ) {
497 $message = isset( $params['prompt'] ) ? $params['prompt'] : '';
498 }
499 $options = isset( $params['options'] ) ? $params['options'] : [];
500 $scope = isset( $params['scope'] ) ? $params['scope'] : 'public-api';
501 if ( !empty( $scope ) ) {
502 $options['scope'] = $scope;
503 }
504 if ( empty( $message ) ) {
505 throw new Exception( 'The message is required.' );
506 }
507
508 if ( $this->debug ) {
509 $shortMessage = Meow_MWAI_Logging::shorten( $message, 64 );
510 $debug = sprintf( 'REST [SimpleJsonQuery]: %s, %s', $shortMessage, json_encode( $options ) );
511 Meow_MWAI_Logging::log( $debug );
512 }
513
514 $reply = $this->simpleJsonQuery( $message, $options );
515 return new WP_REST_Response( [ 'success' => true, 'data' => $reply ], 200 );
516 }
517 catch ( Exception $e ) {
518 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
519 }
520 }
521
522 public function rest_moderationCheck( $request ) {
523 try {
524 $params = $request->get_params();
525 $text = $params['text'];
526 if ( empty( $text ) ) {
527 throw new Exception( 'The text is required.' );
528 }
529
530 if ( $this->debug ) {
531 $shortText = Meow_MWAI_Logging::shorten( $text, 64 );
532 $debug = sprintf( 'REST [ModerationCheck]: %s', $shortText );
533 Meow_MWAI_Logging::log( $debug );
534 }
535
536 $reply = $this->moderationCheck( $text );
537 return new WP_REST_Response( [ 'success' => true, 'data' => $reply ], 200 );
538 }
539 catch ( Exception $e ) {
540 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
541 }
542 }
543 #endregion
544
545 #region Simple API
546 /**
547 * Executes a vision query.`
548 *
549 * @param string $message The prompt for the AI.
550 * @param string $url The URL of the image to analyze.
551 * @param string|null $path The path to the image file. If provided, the image data will be read from this file.
552 * @param array $params Additional parameters for the AI query.
553 *
554 * @return string The result of the AI query.
555 */
556 public function simpleVisionQuery( $message, $url, $path = null, $params = [] ) {
557 global $mwai_core;
558 $ai_vision_default_env = $this->core->get_option( 'ai_vision_default_env' );
559 $ai_vision_default_model = $this->core->get_option( 'ai_vision_default_model' );
560 if ( empty( $ai_vision_default_model ) ) {
561 $ai_vision_default_model = MWAI_FALLBACK_MODEL_VISION;
562 }
563 $query = new Meow_MWAI_Query_Text( $message );
564 if ( !empty( $ai_vision_default_env ) ) {
565 $query->set_env_id( $ai_vision_default_env );
566 }
567 if ( !empty( $ai_vision_default_model ) ) {
568 $query->set_model( $ai_vision_default_model );
569 }
570 $query->inject_params( $params );
571 if ( isset( $params['image_remote_upload'] ) ) {
572 $query->image_remote_upload = $params['image_remote_upload'];
573 }
574 if ( !empty( $url ) ) {
575 $query->set_file( Meow_MWAI_Query_DroppedFile::from_url( $url, 'vision' ) );
576 }
577 else if ( !empty( $path ) ) {
578 $query->set_file( Meow_MWAI_Query_DroppedFile::from_path( $path, 'vision' ) );
579 }
580 $reply = $mwai_core->run_query( $query );
581 return $reply->result;
582 }
583
584 /**
585 * Executes a chatbot query.
586 * It will use the discussion if chatId is provided in the parameters.
587 *
588 * @param string $botId The ID of the chatbot.
589 * @param string $message The prompt for the AI.
590 * @param array $params Additional parameters for the AI query.
591 *
592 * @return string The result of the AI query.
593 */
594 public function simpleChatbotQuery( $botId, $message, $params = [], $onlyReply = true ) {
595 if ( !isset( $params['messages'] ) && isset( $params['chatId'] ) ) {
596 if ( $this->core->get_option( 'chatbot_discussions' ) ) {
597 $discussion = $this->discussions_module->get_discussion( $botId, $params['chatId'] );
598 if ( !empty( $discussion ) ) {
599 $params['messages'] = $discussion['messages'];
600 }
601 }
602 else {
603 $this->core->log( 'The chatId was provided; but the discussions are not enabled.' );
604 }
605 }
606 $data = $this->chatbot_module->chat_submit( $botId, $message, null, $params );
607 return $onlyReply ? $data['reply'] : $data;
608 }
609
610 /**
611 * Executes a text query.
612 *
613 * @param string $message The prompt for the AI.
614 * @param array $params Additional parameters for the AI query.
615 *
616 * @return string The result of the AI query.
617 */
618 public function simpleTextQuery( $message, $params = [] ) {
619 global $mwai_core;
620 $query = new Meow_MWAI_Query_Text( $message );
621 $query->inject_params( $params );
622 $reply = $mwai_core->run_query( $query );
623 return $reply->result;
624 }
625
626 public function simpleImageQuery( $message, $params = [] ) {
627 global $mwai_core;
628 $query = new Meow_MWAI_Query_Image( $message );
629 $query->inject_params( $params );
630 $reply = $mwai_core->run_query( $query );
631 return $reply->result;
632 }
633
634 public function simpleImageEditQuery( $message, $mediaId, $params = [] ) {
635 global $mwai_core;
636 $query = new Meow_MWAI_Query_EditImage( $message );
637 $query->inject_params( $params );
638 $path = get_attached_file( $mediaId );
639 if ( empty( $path ) ) {
640 throw new Exception( 'The media cannot be found.' );
641 }
642 // TODO: Maybe 'vision' should be 'edit'.
643 $query->set_file( Meow_MWAI_Query_DroppedFile::from_path( $path, 'vision' ) );
644 $reply = $mwai_core->run_query( $query );
645 return $reply->result;
646 }
647
648 /**
649 * Generates an image relevant to the text.
650 */
651 public function imageQueryForMediaLibrary( $message, $params = [], $postId = null ) {
652 $query = new Meow_MWAI_Query_Image( $message );
653 $query->inject_params( $params );
654 $query->set_local_download( null );
655 $reply = $this->core->run_query( $query );
656 preg_match( '/\!\[Image\]\((.*?)\)/', $reply->result, $matches );
657 $url = $matches[1] ?? $reply->result;
658
659 // Check if the URL is already a WordPress attachment URL to avoid duplicates
660 $attachmentId = null;
661 $upload_dir = wp_upload_dir();
662 if ( strpos( $url, $upload_dir['baseurl'] ) === 0 ) {
663 // This is already a local WordPress upload, try to find the attachment ID
664 // First try by GUID
665 global $wpdb;
666 $attachmentId = $wpdb->get_var( $wpdb->prepare(
667 "SELECT ID FROM {$wpdb->posts} WHERE guid = %s AND post_type = 'attachment'",
668 $url
669 ) );
670
671 // If not found by GUID, try by attachment URL (more reliable)
672 if ( empty( $attachmentId ) ) {
673 $attachmentId = attachment_url_to_postid( $url );
674 }
675 }
676
677 // If not found or not a local URL, add it to the media library
678 if ( empty( $attachmentId ) ) {
679 $attachmentId = $this->core->add_image_from_url( $url, null, null, null, null, null, $postId );
680 if ( empty( $attachmentId ) ) {
681 throw new Exception( 'Could not add the image to the Media Library.' );
682 }
683 }
684
685 // TODO: We should create a nice title, caption, and alt.
686 $media = [
687 'id' => $attachmentId,
688 'url' => wp_get_attachment_url( $attachmentId ),
689 'title' => get_the_title( $attachmentId ),
690 'caption' => wp_get_attachment_caption( $attachmentId ),
691 'alt' => get_post_meta( $attachmentId, '_wp_attachment_image_alt', true )
692 ];
693 return $media;
694 }
695
696 /**
697 * Executes a query that will have to return a JSON result.
698 *
699 * @param string $message The prompt for the AI.
700 * @param array $params Additional parameters for the AI query.
701 *
702 * @return array The result of the AI query.
703 */
704 public function simpleJsonQuery( $message, $url = null, $path = null, $params = [] ) {
705 if ( !empty( $url ) || !empty( $path ) ) {
706 throw new Exception( 'The url and path are not supported yet by the simpleJsonQuery.' );
707 }
708 global $mwai_core;
709 $query = new Meow_MWAI_Query_Text( $message . "\nYour reply must be a formatted JSON." );
710 $query->inject_params( $params );
711 $query->set_response_format( 'json' );
712 $ai_json_default_env = $mwai_core->get_option( 'ai_json_default_env' );
713 $ai_json_default_model = $mwai_core->get_option( 'ai_json_default_model' );
714 if ( !empty( $ai_json_default_env ) ) {
715 $query->set_env_id( $ai_json_default_env );
716 }
717 if ( !empty( $ai_json_default_model ) ) {
718 $query->set_model( $ai_json_default_model );
719 }
720 else {
721 $query->set_model( MWAI_FALLBACK_MODEL_JSON );
722 }
723 $reply = $mwai_core->run_query( $query );
724 try {
725 $json = json_decode( $reply->result, true );
726 return $json;
727 }
728 catch ( Exception $e ) {
729 throw new Exception( 'The result is not a valid JSON.' );
730 }
731 }
732 #endregion
733
734 #region Standard API
735 /**
736 * Checks if a text is safe or not.
737 *
738 * @param string $text The text to check.
739 *
740 * @return bool True if the text is safe, false otherwise.
741 */
742 public function moderationCheck( $text ) {
743 global $mwai_core;
744 $openai = Meow_MWAI_Engines_Factory::get_openai( $mwai_core );
745 $res = $openai->moderate( $text );
746 if ( !empty( $res ) && !empty( $res['results'] ) ) {
747 return (bool) $res['results'][0]['flagged'];
748 }
749 }
750 #endregion
751
752 #region Standard API (No REST API)
753
754 /**
755 * Checks the status of the AI environments.
756 *
757 * @return array The types of environments that are available.
758 */
759 public function checkStatus() {
760 $env_types = [];
761 $ai_envs = $this->core->get_option( 'ai_envs' );
762 if ( empty( $ai_envs ) ) {
763 throw new Exception( 'There are no AI environments yet.' );
764 }
765 foreach ( $ai_envs as $env ) {
766 if ( !empty( $env['apikey'] ) ) {
767 if ( !in_array( $env['type'], $env_types ) ) {
768 $env_types[] = $env['type'];
769 }
770 }
771 }
772 if ( empty( $env_types ) ) {
773 throw new Exception( 'There are no AI environments with an API key yet.' );
774 }
775 return $env_types;
776 }
777
778 /**
779 * Get function name by ID
780 */
781 private function get_function_name_by_id( $funcId ) {
782 // Get function from registry using the static method
783 $function = MeowPro_MWAI_FunctionAware::get_function( 'code-engine', $funcId );
784 if ( $function && isset( $function->name ) ) {
785 return $function->name;
786 }
787
788 // If not found, try snippet-vault type as well
789 $function = MeowPro_MWAI_FunctionAware::get_function( 'snippet-vault', $funcId );
790 if ( $function && isset( $function->name ) ) {
791 return $function->name;
792 }
793
794 return null;
795 }
796 #endregion
797 }
798