PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 2.9.6
AI Engine – The Chatbot, AI Framework & MCP for WordPress v2.9.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 / rest.php
ai-engine / classes Last commit date
data 11 months ago engines 11 months ago exceptions 11 months ago modules 11 months ago query 11 months ago rest 11 months ago services 11 months ago admin.php 11 months ago api.php 11 months ago core.php 11 months ago discussion.php 11 months ago event.php 11 months ago init.php 11 months ago logging.php 11 months ago reply.php 11 months ago rest.php 11 months ago
rest.php
1407 lines
1 <?php
2
3 class Meow_MWAI_Rest {
4 private $core = null;
5 private $namespace = 'mwai/v1';
6
7 public function __construct( $core ) {
8 $this->core = $core;
9 add_action( 'rest_api_init', [ $this, 'rest_init' ] );
10 }
11
12 /**
13 * Retrieve the message from the parameters and optionally sanitize it.
14 *
15 * @param array &$params The parameters array, passed by reference.
16 * @param bool $sanitize Whether to sanitize the message using sanitize_text_field.
17 * @return string The retrieved (and optionally sanitized) message.
18 */
19 public function retrieve_message( &$params, $sanitize = false ): string {
20 if ( isset( $params['message'] ) ) {
21 $message = $params['message'];
22 }
23 elseif ( isset( $params['prompt'] ) ) {
24 $message = $params['prompt'];
25 unset( $params['prompt'] );
26 $params['message'] = $message;
27 Meow_MWAI_Logging::deprecated( '"prompt" is deprecated, please use "message" instead.' );
28 }
29 else {
30 $message = '';
31 }
32
33 if ( $sanitize ) {
34 $message = sanitize_text_field( $message );
35 }
36
37 return $message;
38 }
39
40 /**
41 * Helper method to create REST responses with automatic token refresh
42 *
43 * @param array $data The response data
44 * @param int $status HTTP status code
45 * @return WP_REST_Response
46 */
47 protected function create_rest_response( $data, $status = 200 ) {
48 // Always check if we need to provide a new nonce
49 $current_nonce = $this->core->get_nonce( true );
50 $request_nonce = isset( $_SERVER['HTTP_X_WP_NONCE'] ) ? $_SERVER['HTTP_X_WP_NONCE'] : null;
51
52 // Check if nonce is approaching expiration (WordPress nonces last 12-24 hours)
53 // We'll refresh if the nonce is older than 10 hours to be safe
54 $should_refresh = false;
55
56 if ( $request_nonce ) {
57 // Try to determine the age of the nonce
58 // WordPress uses a tick system where each tick is 12 hours
59 // If we're in the second half of the nonce's life, refresh it
60 $time = time();
61 $nonce_tick = wp_nonce_tick();
62
63 // Verify if the nonce is still valid but getting old
64 $verify = wp_verify_nonce( $request_nonce, 'wp_rest' );
65 if ( $verify === 2 ) {
66 // Nonce is valid but was generated 12-24 hours ago
67 $should_refresh = true;
68 // Log will be written when token is included in response
69 }
70 }
71
72 // If the nonce has changed or should be refreshed, include the new one
73 if ( $should_refresh || ( $request_nonce && $current_nonce !== $request_nonce ) ) {
74 $data['new_token'] = $current_nonce;
75
76 // Log if server debug mode is enabled
77 if ( $this->core->get_option( 'server_debug_mode' ) ) {
78 error_log( '[AI Engine] Token refresh: Nonce refreshed (12-24 hours old)' );
79 }
80 }
81
82 return new WP_REST_Response( $data, $status );
83 }
84
85 public function rest_init() {
86 try {
87 // Session Endpoint
88 register_rest_route( $this->namespace, '/start_session', [
89 'methods' => 'POST',
90 'permission_callback' => '__return_true', // Public endpoint for guest users
91 'callback' => [ $this, 'rest_start_session' ],
92 ] );
93
94 // Settings Endpoints
95 register_rest_route( $this->namespace, '/settings/update', [
96 'methods' => 'POST',
97 'permission_callback' => [ $this->core, 'can_access_settings' ],
98 'callback' => [ $this, 'rest_settings_update' ],
99 ] );
100 register_rest_route( $this->namespace, '/settings/options', [
101 'methods' => 'GET',
102 'permission_callback' => [ $this->core, 'can_access_settings' ],
103 'callback' => [ $this, 'rest_settings_list' ],
104 ] );
105 register_rest_route( $this->namespace, '/settings/reset', [
106 'methods' => 'POST',
107 'permission_callback' => [ $this->core, 'can_access_settings' ],
108 'callback' => [ $this, 'rest_settings_reset' ],
109 ] );
110 register_rest_route( $this->namespace, '/settings/chatbots', [
111 'methods' => ['GET', 'POST'],
112 'permission_callback' => [ $this->core, 'can_access_settings' ],
113 'callback' => [ $this, 'rest_settings_chatbots' ],
114 ] );
115 register_rest_route( $this->namespace, '/settings/themes', [
116 'methods' => ['GET', 'POST'],
117 'permission_callback' => [ $this->core, 'can_access_settings' ],
118 'callback' => [ $this, 'rest_settings_themes' ],
119 ] );
120
121 // System Endpoints
122 register_rest_route( $this->namespace, '/system/logs/list', [
123 'methods' => 'POST',
124 'permission_callback' => [ $this->core, 'can_access_settings' ],
125 'callback' => [ $this, 'rest_system_logs_list' ],
126 ] );
127 register_rest_route( $this->namespace, '/system/logs/delete', [
128 'methods' => 'POST',
129 'permission_callback' => [ $this->core, 'can_access_settings' ],
130 'callback' => [ $this, 'rest_system_logs_delete' ],
131 ] );
132 register_rest_route( $this->namespace, '/system/logs/meta', [
133 'methods' => 'POST',
134 'permission_callback' => [ $this->core, 'can_access_settings' ],
135 'callback' => [ $this, 'rest_system_logs_meta_get' ],
136 ] );
137 register_rest_route( $this->namespace, '/system/logs/activity', [
138 'methods' => 'POST',
139 'permission_callback' => [ $this->core, 'can_access_settings' ],
140 'callback' => [ $this, 'rest_system_logs_activity' ],
141 ] );
142 register_rest_route( $this->namespace, '/system/logs/activity_daily', [
143 'methods' => 'POST',
144 'permission_callback' => [ $this->core, 'can_access_settings' ],
145 'callback' => [ $this, 'rest_system_logs_activity_daily' ],
146 ] );
147 register_rest_route( $this->namespace, '/system/templates', [
148 'methods' => 'POST',
149 'permission_callback' => [ $this->core, 'can_access_features' ],
150 'callback' => [ $this, 'rest_system_templates_save' ],
151 ] );
152 register_rest_route( $this->namespace, '/system/templates', [
153 'methods' => 'GET',
154 'permission_callback' => [ $this->core, 'can_access_features' ],
155 'callback' => [ $this, 'rest_system_templates_get' ],
156 ] );
157
158 // AI Endpoints
159 register_rest_route( $this->namespace, '/ai/models', [
160 'methods' => 'POST',
161 'permission_callback' => [ $this->core, 'can_access_features' ],
162 'callback' => [ $this, 'rest_ai_models' ],
163 ] );
164 register_rest_route( $this->namespace, '/ai/test_connection', [
165 'methods' => 'POST',
166 'permission_callback' => [ $this->core, 'can_access_settings' ],
167 'callback' => [ $this, 'rest_ai_test_connection' ],
168 ] );
169 register_rest_route( $this->namespace, '/ai/completions', [
170 'methods' => 'POST',
171 'permission_callback' => [ $this->core, 'can_access_features' ],
172 'callback' => [ $this, 'rest_ai_completions' ],
173 ] );
174 register_rest_route( $this->namespace, '/ai/images', [
175 'methods' => 'POST',
176 'permission_callback' => [ $this->core, 'can_access_features' ],
177 'callback' => [ $this, 'rest_ai_images' ],
178 ] );
179 register_rest_route( $this->namespace, '/ai/image_edit', [
180 'methods' => 'POST',
181 'permission_callback' => [ $this->core, 'can_access_features' ],
182 'callback' => [ $this, 'rest_ai_image_edit' ],
183 ] );
184 register_rest_route( $this->namespace, '/ai/copilot', [
185 'methods' => 'POST',
186 'permission_callback' => [ $this->core, 'can_access_features' ],
187 'callback' => [ $this, 'rest_ai_copilot' ],
188 ] );
189
190 register_rest_route( $this->namespace, '/ai/magic_wand', [
191 'methods' => 'POST',
192 'callback' => [ $this, 'rest_ai_magic_wand' ],
193 'permission_callback' => [ $this->core, 'can_access_features' ],
194 ] );
195 register_rest_route( $this->namespace, '/ai/moderate', [
196 'methods' => 'POST',
197 'permission_callback' => [ $this->core, 'can_access_settings' ],
198 'callback' => [ $this, 'rest_ai_moderate' ],
199 ] );
200 register_rest_route( $this->namespace, '/ai/transcribe_audio', [
201 'methods' => 'POST',
202 'permission_callback' => [ $this->core, 'can_access_settings' ],
203 'callback' => [ $this, 'rest_ai_transcribe_audio' ],
204 ] );
205 register_rest_route( $this->namespace, '/ai/transcribe_image', [
206 'methods' => 'POST',
207 'permission_callback' => [ $this->core, 'can_access_settings' ],
208 'callback' => [ $this, 'rest_ai_transcribe_image' ],
209 ] );
210 register_rest_route( $this->namespace, '/ai/json', [
211 'methods' => 'POST',
212 'permission_callback' => [ $this->core, 'can_access_settings' ],
213 'callback' => [ $this, 'rest_ai_json' ],
214 ] );
215
216 // MCP Endpoints
217 register_rest_route( $this->namespace, '/mcp/functions', [
218 'methods' => 'GET',
219 'permission_callback' => [ $this->core, 'can_access_settings' ],
220 'callback' => [ $this, 'rest_mcp_functions' ],
221 ] );
222
223 // Helpers Endpoints
224 register_rest_route( $this->namespace, '/helpers/update_post_title', [
225 'methods' => 'POST',
226 'permission_callback' => [ $this->core, 'can_access_features' ],
227 'callback' => [ $this, 'rest_helpers_update_title' ],
228 ] );
229 register_rest_route( $this->namespace, '/helpers/update_post_excerpt', [
230 'methods' => 'POST',
231 'permission_callback' => [ $this->core, 'can_access_features' ],
232 'callback' => [ $this, 'rest_helpers_update_excerpt' ],
233 ] );
234 register_rest_route( $this->namespace, '/helpers/create_post', [
235 'methods' => 'POST',
236 'permission_callback' => [ $this->core, 'can_access_features' ],
237 'callback' => [ $this, 'rest_helpers_create_post' ],
238 ] );
239 register_rest_route( $this->namespace, '/helpers/create_image', [
240 'methods' => 'POST',
241 'permission_callback' => [ $this->core, 'can_access_features' ],
242 'callback' => [ $this, 'rest_helpers_create_images' ],
243 ] );
244 register_rest_route( $this->namespace, '/helpers/generate_image_meta', [
245 'methods' => 'POST',
246 'permission_callback' => [ $this->core, 'can_access_features' ],
247 'callback' => [ $this, 'rest_helpers_generate_image_meta' ],
248 ] );
249 register_rest_route( $this->namespace, '/helpers/count_posts', [
250 'methods' => 'GET',
251 'permission_callback' => [ $this->core, 'can_access_features' ],
252 'callback' => [ $this, 'rest_helpers_count_posts' ],
253 ] );
254 register_rest_route( $this->namespace, '/helpers/posts_ids', [
255 'methods' => 'GET',
256 'permission_callback' => [ $this->core, 'can_access_features' ],
257 'callback' => [ $this, 'rest_helpers_posts_ids' ],
258 ] );
259 register_rest_route( $this->namespace, '/helpers/post_types', [
260 'methods' => 'GET',
261 'permission_callback' => [ $this->core, 'can_access_features' ],
262 'callback' => [ $this, 'rest_helpers_post_types' ],
263 ] );
264 register_rest_route( $this->namespace, '/helpers/post_content', [
265 'methods' => 'GET',
266 'permission_callback' => [ $this->core, 'can_access_features' ],
267 'callback' => [ $this, 'rest_helpers_post_content' ],
268 ] );
269 register_rest_route( $this->namespace, '/helpers/run_tasks', [
270 'methods' => 'POST',
271 'permission_callback' => [ $this->core, 'can_access_features' ],
272 'callback' => [ $this, 'rest_helpers_run_tasks' ],
273 ] );
274 register_rest_route( $this->namespace, '/helpers/optimize_database', [
275 'methods' => 'POST',
276 'permission_callback' => [ $this->core, 'can_access_settings' ],
277 'callback' => [ $this, 'rest_helpers_optimize_database' ],
278 ] );
279
280 // OpenAI Endpoints
281 register_rest_route( $this->namespace, '/openai/files/list', [
282 'methods' => 'GET',
283 'permission_callback' => [ $this->core, 'can_access_settings' ],
284 'callback' => [ $this, 'rest_openai_files_get' ],
285 ] );
286 register_rest_route( $this->namespace, '/openai/files/upload', [
287 'methods' => 'POST',
288 'permission_callback' => [ $this->core, 'can_access_settings' ],
289 'callback' => [ $this, 'rest_openai_files_upload' ],
290 ] );
291 register_rest_route( $this->namespace, '/openai/files/delete', [
292 'methods' => 'POST',
293 'permission_callback' => [ $this->core, 'can_access_settings' ],
294 'callback' => [ $this, 'rest_openai_files_delete' ],
295 ] );
296 register_rest_route( $this->namespace, '/openai/files/download', [
297 'methods' => 'POST',
298 'permission_callback' => [ $this->core, 'can_access_settings' ],
299 'callback' => [ $this, 'rest_openai_files_download' ],
300 ] );
301 register_rest_route( $this->namespace, '/openai/files/finetune', [
302 'methods' => 'POST',
303 'permission_callback' => [ $this->core, 'can_access_settings' ],
304 'callback' => [ $this, 'rest_openai_files_finetune' ],
305 ] );
306 register_rest_route( $this->namespace, '/openai/finetunes/list_deleted', [
307 'methods' => 'GET',
308 'permission_callback' => [ $this->core, 'can_access_settings' ],
309 'callback' => [ $this, 'rest_openai_deleted_finetunes_get' ],
310 ] );
311
312 // register_rest_route( $this->namespace, '/openai/models', array(
313 // 'methods' => 'GET',
314 // 'permission_callback' => [ $this->core, 'can_access_settings' ],
315 // 'callback' => [ $this, 'rest_openai_models_get' ],
316 // ) );
317
318 register_rest_route( $this->namespace, '/openai/finetunes/list', [
319 'methods' => 'GET',
320 'permission_callback' => [ $this->core, 'can_access_settings' ],
321 'callback' => [ $this, 'rest_openai_finetunes_get' ],
322 ] );
323 register_rest_route( $this->namespace, '/openai/finetunes/delete', [
324 'methods' => 'POST',
325 'permission_callback' => [ $this->core, 'can_access_settings' ],
326 'callback' => [ $this, 'rest_openai_finetunes_delete' ],
327 ] );
328 register_rest_route( $this->namespace, '/openai/finetunes/cancel', [
329 'methods' => 'POST',
330 'permission_callback' => [ $this->core, 'can_access_settings' ],
331 'callback' => [ $this, 'rest_openai_finetunes_cancel' ],
332 ] );
333
334 // Logging Endpoints
335 register_rest_route( $this->namespace, '/get_logs', [
336 'methods' => 'GET',
337 'permission_callback' => [ $this->core, 'can_access_features' ],
338 'callback' => [ $this, 'rest_get_logs' ]
339 ] );
340 register_rest_route( $this->namespace, '/clear_logs', [
341 'methods' => 'GET',
342 'permission_callback' => [ $this->core, 'can_access_features' ],
343 'callback' => [ $this, 'rest_clear_logs' ]
344 ] );
345 }
346 catch ( Exception $e ) {
347 Meow_MWAI_Logging::error( 'REST API initialization failed: ' . $e->getMessage() );
348 }
349 }
350
351 public function rest_start_session() {
352 try {
353 $sessionId = $this->core->get_session_id();
354 $restNonce = $this->core->get_nonce( true );
355
356 $response = [
357 'success' => true,
358 'sessionId' => $sessionId,
359 'restNonce' => $restNonce
360 ];
361
362 // If in test mode and we have a new token, it will be added by create_rest_response
363 // But we also want to ensure the restNonce matches the test token if available
364 if ( get_option( 'mwai_token_test_mode' ) ) {
365 $token_data = get_option( 'mwai_test_token_data' );
366 if ( $token_data && isset( $token_data['token'] ) ) {
367 $response['restNonce'] = $token_data['token'];
368 }
369 }
370
371 return $this->create_rest_response( $response, 200 );
372 }
373 catch ( Exception $e ) {
374 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
375 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
376 }
377 }
378
379 public function rest_settings_list() {
380 return $this->create_rest_response( [
381 'success' => true,
382 'options' => $this->core->get_all_options()
383 ], 200 );
384 }
385
386 public function rest_settings_update( $request ) {
387 try {
388 $params = $request->get_json_params();
389 $value = $params['options'];
390 $options = $this->core->update_options( $value );
391 $success = !!$options;
392 $message = __( $success ? 'OK' : 'Could not update options.', 'ai-engine' );
393 return $this->create_rest_response( [ 'success' => $success, 'message' => $message, 'options' => $options ], 200 );
394 }
395 catch ( Exception $e ) {
396 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
397 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
398 }
399 }
400
401 public function rest_settings_reset() {
402 try {
403 $options = $this->core->reset_options();
404 $success = !!$options;
405 $message = __( $success ? 'OK' : 'Could not reset options.', 'ai-engine' );
406 return $this->create_rest_response( [ 'success' => $success, 'message' => $message, 'options' => $options ], 200 );
407 }
408 catch ( Exception $e ) {
409 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
410 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
411 }
412 }
413
414 public function rest_ai_models( $request ) {
415 try {
416 $params = $request->get_json_params();
417 $envId = $params['envId'];
418 $engine = Meow_MWAI_Engines_Factory::get( $this->core, $envId );
419 $models = $engine->retrieve_models();
420 return $this->create_rest_response( [ 'success' => true, 'models' => $models ], 200 );
421 }
422 catch ( Exception $e ) {
423 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
424 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
425 }
426 }
427
428 public function rest_ai_test_connection( $request ) {
429 try {
430 $params = $request->get_json_params();
431 $envId = $params['env_id'];
432
433 // Get the environment details
434 $env = null;
435 $envs = $this->core->get_option( 'ai_envs' );
436 foreach ( $envs as $e ) {
437 if ( $e['id'] === $envId ) {
438 $env = $e;
439 break;
440 }
441 }
442
443 if ( !$env ) {
444 throw new Exception( 'Environment not found.' );
445 }
446
447 // Get the engine and test connection
448 $engine = Meow_MWAI_Engines_Factory::get( $this->core, $envId );
449 $result = $engine->connection_check();
450
451 // Format the response based on provider
452 $response = [
453 'success' => true,
454 'provider' => $env['type'],
455 'name' => $env['name'],
456 'data' => $result
457 ];
458
459 return $this->create_rest_response( $response, 200 );
460 }
461 catch ( Exception $e ) {
462 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
463 return $this->create_rest_response( [
464 'success' => false,
465 'error' => $message,
466 'provider' => isset( $env ) ? $env['type'] : 'unknown'
467 ], 200 ); // Return 200 even on error for consistent modal display
468 }
469 }
470
471 public function rest_ai_completions( $request ) {
472 try {
473 $params = $request->get_json_params();
474 $message = $this->retrieve_message( $params );
475 $query = new Meow_MWAI_Query_Text( $message );
476 $query->inject_params( $params );
477
478 // Handle streaming
479 $stream = $params['stream'] ?? false;
480 $streamCallback = null;
481 if ( $stream ) {
482 $streamCallback = function ( $reply ) use ( $query ) {
483 //$raw = _wp_specialchars( $reply, ENT_NOQUOTES, 'UTF-8', true );
484 $raw = $reply;
485 $this->core->stream_push( [ 'type' => 'live', 'data' => $raw ], $query );
486 if ( ob_get_level() > 0 ) {
487 ob_flush();
488 }
489 flush();
490 };
491 if ( headers_sent( $filename, $linenum ) ) {
492 throw new Exception( "Headers already sent in $filename on line $linenum. Cannot start streaming." );
493 }
494 header( 'Cache-Control: no-cache' );
495 header( 'Content-Type: text/event-stream' );
496 header( 'X-Accel-Buffering: no' ); // This is useful to disable buffering in nginx through headers.
497 ob_implicit_flush( true );
498 if ( ob_get_level() > 0 ) {
499 ob_end_flush();
500 }
501 }
502
503 // Process Reply
504 $reply = $this->core->run_query( $query, $streamCallback );
505 $restRes = [
506 'success' => true,
507 'data' => $reply->result,
508 'usage' => $reply->usage
509 ];
510 if ( $stream ) {
511 $this->core->stream_push( [ 'type' => 'end', 'data' => json_encode( $restRes ) ], $query );
512 die();
513 }
514 return $this->create_rest_response( $restRes, 200 );
515 }
516 catch ( Exception $e ) {
517 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
518 if ( $stream ) {
519 $this->core->stream_push( [ 'type' => 'error', 'data' => $message ], $query );
520 }
521 else {
522 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
523 }
524 }
525 }
526
527 public function rest_ai_images( $request ) {
528 try {
529 $params = $request->get_json_params();
530 $message = $this->retrieve_message( $params );
531 $query = new Meow_MWAI_Query_Image( $message );
532 $query->inject_params( $params );
533 $reply = $this->core->run_query( $query );
534 return $this->create_rest_response( [ 'success' => true, 'data' => $reply->results, 'usage' => $reply->usage ], 200 );
535 }
536 catch ( Exception $e ) {
537 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
538 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
539 }
540 }
541
542 public function rest_ai_image_edit( $request ) {
543 try {
544 // Check if this is a multipart request with files
545 $files = $request->get_file_params();
546 $params = null;
547
548 // Debug logging
549 if ( $this->core->get_option( 'queries_debug_mode' ) ) {
550 error_log( '[AI Engine Queries] Image Edit Request - Method: ' . $request->get_method() );
551 $content_type = $request->get_content_type();
552 if ( is_array( $content_type ) ) {
553 error_log( '[AI Engine Queries] Image Edit Request - Content-Type: ' . $content_type['value'] );
554 }
555 else {
556 error_log( '[AI Engine Queries] Image Edit Request - Content-Type: ' . $content_type );
557 }
558 error_log( '[AI Engine Queries] Image Edit Request - Has files: ' . ( !empty( $files ) ? 'yes (' . count( $files ) . ')' : 'no' ) );
559 }
560
561 if ( !empty( $files ) ) {
562 // Handle multipart form data - get all params including POST data
563 $params = $request->get_params();
564 if ( $this->core->get_option( 'queries_debug_mode' ) ) {
565 error_log( '[AI Engine Queries] Image Edit Request - Using form data params' );
566 }
567 }
568 else {
569 // Try to get body params first (for form data without files)
570 $body_params = $request->get_body_params();
571 if ( !empty( $body_params ) ) {
572 $params = $body_params;
573 if ( $this->core->get_option( 'queries_debug_mode' ) ) {
574 error_log( '[AI Engine Queries] Image Edit Request - Using body params' );
575 }
576 }
577 else {
578 // Handle JSON request
579 $params = $request->get_json_params();
580 if ( $this->core->get_option( 'queries_debug_mode' ) ) {
581 error_log( '[AI Engine Queries] Image Edit Request - Using JSON params' );
582 }
583 }
584 }
585
586 // Ensure params is always an array
587 if ( empty( $params ) ) {
588 $params = [];
589 }
590
591 // Debug logging
592 if ( $this->core->get_option( 'queries_debug_mode' ) ) {
593 error_log( '[AI Engine Queries] Image Edit Request - Has files: ' . ( !empty( $files ) ? 'yes' : 'no' ) );
594 error_log( '[AI Engine Queries] Image Edit Request - Params: ' . json_encode( $params ) );
595 }
596
597 $message = $this->retrieve_message( $params );
598 $mediaId = isset( $params['mediaId'] ) ? intval( $params['mediaId'] ) : 0;
599 $query = new Meow_MWAI_Query_EditImage( $message );
600
601 // The inject_params method will handle setting the file from mediaId
602 $query->inject_params( $params );
603
604 // Handle mask file if provided
605 if ( !empty( $files['mask'] ) ) {
606 $mask_file = $files['mask'];
607 if ( $mask_file['error'] === UPLOAD_ERR_OK ) {
608 $mask_data = file_get_contents( $mask_file['tmp_name'] );
609 $query->set_mask( Meow_MWAI_Query_DroppedFile::from_data( $mask_data, 'vision', $mask_file['type'] ) );
610 }
611 }
612
613 $reply = $this->core->run_query( $query );
614 return $this->create_rest_response( [ 'success' => true, 'data' => $reply->results, 'usage' => $reply->usage ], 200 );
615 }
616 catch ( Exception $e ) {
617 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
618 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
619 }
620 }
621
622 public function rest_ai_magic_wand( $request ) {
623 try {
624 $params = $request->get_json_params();
625 $action = isset( $params['action'] ) ? $params['action'] : null;
626 $data = isset( $params['data'] ) ? $params['data'] : null;
627 if ( empty( $data ) || empty( $action ) ) {
628 return $this->create_rest_response( [ 'success' => false, 'message' => 'An action and some data are required.' ], 500 );
629 }
630 $data = apply_filters( 'mwai_magic_wand_' . $action, '', $data );
631 return $this->create_rest_response( [ 'success' => true, 'data' => $data ], 200 );
632 }
633 catch ( Exception $e ) {
634 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
635 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
636 }
637 }
638
639 public function rest_ai_copilot( $request ) {
640 try {
641 $params = $request->get_json_params();
642 $action = sanitize_text_field( $params['action'] );
643 $message = $this->retrieve_message( $params, true );
644 $context = sanitize_text_field( $params['context'] );
645 $postId = !empty( $params['postId'] ) ? intval( $params['postId'] ) : null;
646 if ( empty( $action ) || empty( $message ) ) {
647 return $this->create_rest_response( [ 'success' => false, 'message' => 'Copilot needs an action and a prompt.' ], 500 );
648 }
649
650 global $mwai;
651 $result = null;
652 $params = [ 'scope' => 'copilot' ];
653
654 if ( $action === 'text' ) {
655 $prompt = "Here is the current article: \n\n===\n\n" . $context . "\n\n===\n\nIn this article, instead of the [== CURRENT BLOCK ==] placeholder, the author needs additional content. This new content should use the same tone, style, context, it should naturally flow in the article. The author shared additional information for this request:\n\n===\n\n" . $message . "\n\n===\n\nPlease provide the additional content. Only output the additional content, not the entire article, no need for extra information, and no need for the placeholders. Only output the content that should be added.";
656 if ( !empty( $model ) ) {
657 $params['model'] = $model;
658 }
659 $result = $mwai->simpleTextQuery( $prompt, $params );
660 }
661 else if ( $action === 'image' ) {
662 $prompt = "Here is the current article: \n\n===\n\n" . $context . "\n\n===\n\nIn this article, instead of the [== CURRENT BLOCK ==] placeholder, the author needs an image. Please write a detailed description (prompt) for that image that would fit this context. The image should be relevant to the article. The author shared additional information for this request:\n\n===\n\n" . $message . "\n\n===\n\nPlease only output the description for the image, not the entire article, no need for extra information, and no need for the placeholders. Only output the description.";
663
664 // Create the image
665 $simplifiedPrompt = $mwai->simpleTextQuery( $prompt, $params );
666 $media = $mwai->imageQueryForMediaLibrary( $simplifiedPrompt, $params, $postId );
667 $result = [ 'media' => $media ];
668 }
669 return $this->create_rest_response( [ 'success' => true, 'data' => $result ], 200 );
670 }
671 catch ( Exception $e ) {
672 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
673 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
674 }
675 }
676
677 public function rest_helpers_update_title( $request ) {
678 try {
679 $params = $request->get_json_params();
680 $title = sanitize_text_field( $params['title'] );
681 $postId = intval( $params['postId'] );
682 $post = get_post( $postId );
683 if ( !$post ) {
684 throw new Exception( 'There is no post with this ID.' );
685 }
686 $post->post_title = $title;
687 //$post->post_name = sanitize_title( $title );
688 wp_update_post( $post );
689 return $this->create_rest_response( [ 'success' => true, 'message' => 'Title updated.' ], 200 );
690 }
691 catch ( Exception $e ) {
692 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
693 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
694 }
695 }
696
697 public function rest_helpers_update_excerpt( $request ) {
698 try {
699 $params = $request->get_json_params();
700 $excerpt = sanitize_text_field( $params['excerpt'] );
701 $postId = intval( $params['postId'] );
702 $post = get_post( $postId );
703 if ( !$post ) {
704 throw new Exception( 'There is no post with this ID.' );
705 }
706 $post->post_excerpt = $excerpt;
707 wp_update_post( $post );
708 return $this->create_rest_response( [ 'success' => true, 'message' => 'Excerpt updated.' ], 200 );
709 }
710 catch ( Exception $e ) {
711 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
712 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
713 }
714 }
715
716 public function rest_helpers_create_post( $request ) {
717 try {
718 $params = $request->get_json_params();
719 $title = sanitize_text_field( $params['title'] );
720 $content = sanitize_textarea_field( $params['content'] );
721 $excerpt = sanitize_text_field( $params['excerpt'] );
722 $postType = sanitize_text_field( $params['postType'] );
723 $post = new stdClass();
724 $post->post_title = $title;
725 $post->post_excerpt = $excerpt;
726 $post->post_content = $content;
727 $post->post_status = 'draft';
728 $post->post_type = isset( $postType ) ? $postType : 'post';
729 // TODO: Let's try to avoid using Markdown to create the Post
730 // Instead, we should create Gutenberg Blocks, or simple HTML.
731 // Then, we can get rid of the library for Markdown.
732 $post->post_content = $this->core->markdown_to_html( $post->post_content );
733 $postId = wp_insert_post( $post );
734 return $this->create_rest_response( [ 'success' => true, 'postId' => $postId ], 200 );
735 }
736 catch ( Exception $e ) {
737 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
738 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
739 }
740 }
741
742 public function rest_helpers_create_images( $request ) {
743 try {
744 $params = $request->get_json_params();
745 $title = sanitize_text_field( $params['title'] );
746 $caption = sanitize_text_field( $params['caption'] );
747 $alt = sanitize_text_field( $params['alt'] );
748 $description = sanitize_text_field( $params['description'] );
749 $url = $params['url'];
750 $filename = sanitize_text_field( $params['filename'] );
751 $attachmentId = $this->core->add_image_from_url( $url, $filename, $title, $description, $caption, $alt );
752 return $this->create_rest_response( [ 'success' => true, 'attachmentId' => $attachmentId ], 200 );
753 }
754 catch ( Exception $e ) {
755 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
756 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
757 }
758 }
759
760 public function rest_helpers_generate_image_meta( $request ) {
761 try {
762 global $mwai;
763 $params = $request->get_json_params();
764 $url = isset( $params['url'] ) ? esc_url_raw( $params['url'] ) : null;
765 if ( empty( $url ) ) {
766 throw new Exception( 'The url is required.' );
767 }
768 $prompt = 'Describe this image and suggest a short title, description and SEO-friendly (ASCII and lowercase) filename. '
769 . 'Return a JSON with the keys title, description, alt, caption, filename.';
770 $result = $mwai->simpleVisionQuery( $prompt, $url, null, [ 'image_remote_upload' => 'url', 'scope' => 'admin-tools' ] );
771 $result = preg_replace( '/^```json\s*/', '', $result );
772 $result = preg_replace( '/\s*```$/', '', $result );
773 if ( is_string( $result ) ) {
774 $data = json_decode( $result, true );
775 }
776 else {
777 $data = $result;
778 }
779 if ( !is_array( $data ) ) {
780 $data = [];
781 }
782 $data = array_merge( [ 'title' => '', 'description' => '', 'caption' => '', 'alt' => '', 'filename' => '' ], $data );
783 return $this->create_rest_response( [ 'success' => true, 'data' => $data ], 200 );
784 }
785 catch ( Exception $e ) {
786 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
787 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
788 }
789 }
790
791 public function rest_openai_files_get() {
792 try {
793 $envId = isset( $_GET['envId'] ) ? $_GET['envId'] : null;
794 $purposeFilter = isset( $_GET['purpose'] ) ? $_GET['purpose'] : null;
795 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
796 $files = $openai->list_files( $purposeFilter );
797 return $this->create_rest_response( [ 'success' => true, 'files' => $files ], 200 );
798 }
799 catch ( Exception $e ) {
800 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
801 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
802 }
803 }
804
805 public function rest_openai_deleted_finetunes_get() {
806 try {
807 $envId = isset( $_GET['envId'] ) ? $_GET['envId'] : null;
808 $legacy = isset( $_GET['legacy'] ) ? $_GET['legacy'] === 'true' : false;
809 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
810 $finetunes = $openai->list_deleted_finetunes( $legacy );
811 return $this->create_rest_response( [ 'success' => true, 'finetunes' => $finetunes ], 200 );
812 }
813 catch ( Exception $e ) {
814 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
815 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
816 }
817 }
818
819 public function rest_openai_finetunes_get() {
820 try {
821 $envId = isset( $_GET['envId'] ) ? $_GET['envId'] : null;
822 $legacy = isset( $_GET['legacy'] ) ? $_GET['legacy'] === 'true' : false;
823 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
824 $finetunes = $openai->list_finetunes( $legacy );
825 return $this->create_rest_response( [ 'success' => true, 'finetunes' => $finetunes ], 200 );
826 }
827 catch ( Exception $e ) {
828 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
829 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
830 }
831 }
832
833 public function rest_openai_files_upload( $request ) {
834 try {
835 $params = $request->get_json_params();
836 $envId = $params['envId'];
837 ;
838 $filename = sanitize_text_field( $params['filename'] );
839 $data = $params['data'];
840 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
841 $file = $openai->upload_file( $filename, $data );
842 return $this->create_rest_response( [ 'success' => true, 'file' => $file ], 200 );
843 }
844 catch ( Exception $e ) {
845 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
846 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
847 }
848 }
849
850 public function rest_openai_files_delete( $request ) {
851 try {
852 $params = $request->get_json_params();
853 $envId = $params['envId'];
854 ;
855 $fileId = $params['fileId'];
856 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
857 $openai->delete_file( $fileId );
858 return $this->create_rest_response( [ 'success' => true ], 200 );
859 }
860 catch ( Exception $e ) {
861 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
862 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
863 }
864 }
865
866 public function rest_openai_finetunes_cancel( $request ) {
867 try {
868 $params = $request->get_json_params();
869 $envId = $params['envId'];
870 ;
871 $finetuneId = $params['finetuneId'];
872 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
873 $openai->cancel_finetune( $finetuneId );
874 return $this->create_rest_response( [ 'success' => true ], 200 );
875 }
876 catch ( Exception $e ) {
877 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
878 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
879 }
880 }
881
882 public function rest_openai_finetunes_delete( $request ) {
883 try {
884 $params = $request->get_json_params();
885 $envId = $params['envId'];
886 ;
887 $modelId = $params['modelId'];
888 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
889 $openai->delete_finetune( $modelId );
890 return $this->create_rest_response( [ 'success' => true ], 200 );
891 }
892 catch ( Exception $e ) {
893 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
894 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
895 }
896 }
897
898 public function rest_openai_files_download( $request ) {
899 try {
900 $params = $request->get_json_params();
901 $envId = $params['envId'];
902 ;
903 $fileId = $params['fileId'];
904 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
905 $filename = $openai->download_file( $fileId );
906 $data = file_get_contents( $filename );
907 return $this->create_rest_response( [ 'success' => true, 'data' => $data ], 200 );
908 }
909 catch ( Exception $e ) {
910 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
911 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
912 }
913 }
914
915 public function rest_openai_files_finetune( $request ) {
916 try {
917 $params = $request->get_json_params();
918 $envId = $params['envId'];
919 ;
920 $fileId = $params['fileId'];
921 $model = $params['model'];
922 $suffix = $params['suffix'];
923 $hyperparams = [
924 'nEpochs' => isset( $params['nEpochs'] ) ? $params['nEpochs'] : null,
925 'batchSize' => isset( $params['batchSize'] ) ? $params['batchSize'] : null,
926 ];
927 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
928 $finetune = $openai->run_finetune( $fileId, $model, $suffix, $hyperparams );
929 return $this->create_rest_response( [ 'success' => true, 'finetune' => $finetune ], 200 );
930 }
931 catch ( Exception $e ) {
932 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
933 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
934 }
935 }
936
937 public function rest_helpers_count_posts( $request ) {
938 try {
939 $params = $request->get_query_params();
940 $postType = $params['postType'];
941 $postStatus = !empty( $params['postStatus'] ) ? explode( ',', $params['postStatus'] ) : [ 'publish' ];
942 $count = wp_count_posts( $postType );
943 $count = array_sum( array_intersect_key( (array) $count, array_flip( $postStatus ) ) );
944 return $this->create_rest_response( [ 'success' => true, 'count' => $count ], 200 );
945 }
946 catch ( Exception $e ) {
947 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
948 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
949 }
950 }
951
952 public function rest_helpers_posts_ids( $request ) {
953 try {
954 $params = $request->get_query_params();
955 $postType = $params['postType'];
956 $postStatus = !empty( $params['postStatus'] ) ? explode( ',', $params['postStatus'] ) : [ 'publish' ];
957 $posts = get_posts( [
958 'posts_per_page' => -1,
959 'post_type' => $postType,
960 'post_status' => $postStatus,
961 'fields' => 'ids'
962 ] );
963 return $this->create_rest_response( [ 'success' => true, 'postIds' => $posts ], 200 );
964 }
965 catch ( Exception $e ) {
966 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
967 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
968 }
969 }
970
971 public function rest_helpers_post_content( $request ) {
972 try {
973 $params = $request->get_query_params();
974 $offset = (int) $params['offset'];
975 $postType = $params['postType'];
976 $postStatus = isset( $params['postStatus'] ) ? explode( ',', $params['postStatus'] ) : [ 'publish' ];
977 $postId = (int) $params['postId'];
978
979 $post = null;
980 if ( !empty( $postId ) ) {
981 $post = get_post( $postId );
982 if ( $post->post_status !== 'publish' && $post->post_status !== 'future'
983 && $post->post_status !== 'draft' && $post->post_status !== 'private' ) {
984 $post = null;
985 }
986 }
987 else {
988 $posts = get_posts( [
989 'posts_per_page' => 1,
990 'post_type' => $postType,
991 'offset' => $offset,
992 'post_status' => $postStatus,
993 ] );
994 $post = count( $posts ) === 0 ? null : $posts[0];
995 }
996 if ( !$post ) {
997 return $this->create_rest_response( [ 'success' => false, 'message' => 'Post not found' ], 404 );
998 }
999 $cleanPost = $this->core->get_post( $post );
1000 return $this->create_rest_response( [ 'success' => true, 'content' => $cleanPost['content'],
1001 'checksum' => $cleanPost['checksum'], 'language' => $cleanPost['language'], 'excerpt' => $cleanPost['excerpt'],
1002 'postId' => $cleanPost['postId'], 'title' => $cleanPost['title'], 'url' => $cleanPost['url'] ], 200 );
1003 }
1004 catch ( Exception $e ) {
1005 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1006 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1007 }
1008 }
1009
1010 public function rest_helpers_run_tasks( $request ) {
1011 try {
1012 do_action( 'mwai_tasks_run' );
1013 return $this->create_rest_response( [ 'success' => true ], 200 );
1014 }
1015 catch ( Exception $e ) {
1016 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1017 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1018 }
1019 }
1020
1021 public function rest_helpers_optimize_database( $request ) {
1022 try {
1023 global $wpdb;
1024 $results = [];
1025
1026 // Add indexes to optimize query performance
1027 $indexes = [
1028 // mwai_logs indexes
1029 [ 'table' => 'mwai_logs', 'name' => 'idx_mwai_logs_time', 'columns' => 'time' ],
1030 [ 'table' => 'mwai_logs', 'name' => 'idx_mwai_logs_userId', 'columns' => 'userId' ],
1031 [ 'table' => 'mwai_logs', 'name' => 'idx_mwai_logs_envId', 'columns' => 'envId' ],
1032 [ 'table' => 'mwai_logs', 'name' => 'idx_mwai_logs_refId', 'columns' => 'refId' ],
1033 [ 'table' => 'mwai_logs', 'name' => 'idx_mwai_logs_time_model', 'columns' => 'time, model' ],
1034
1035 // mwai_logmeta indexes
1036 [ 'table' => 'mwai_logmeta', 'name' => 'idx_mwai_logmeta_log_id', 'columns' => 'log_id' ],
1037
1038 // mwai_vectors indexes
1039 [ 'table' => 'mwai_vectors', 'name' => 'idx_mwai_vectors_envId_status_dbId', 'columns' => 'envId, status, dbId' ],
1040 [ 'table' => 'mwai_vectors', 'name' => 'idx_mwai_vectors_refId', 'columns' => 'refId' ],
1041 [ 'table' => 'mwai_vectors', 'name' => 'idx_mwai_vectors_status', 'columns' => 'status' ],
1042 [ 'table' => 'mwai_vectors', 'name' => 'idx_mwai_vectors_updated', 'columns' => 'updated' ],
1043
1044 // mwai_files indexes
1045 [ 'table' => 'mwai_files', 'name' => 'idx_mwai_files_expires', 'columns' => 'expires' ],
1046 [ 'table' => 'mwai_files', 'name' => 'idx_mwai_files_userId', 'columns' => 'userId' ],
1047 [ 'table' => 'mwai_files', 'name' => 'idx_mwai_files_purpose', 'columns' => 'purpose' ],
1048
1049 // mwai_filemeta indexes
1050 [ 'table' => 'mwai_filemeta', 'name' => 'idx_mwai_filemeta_file_id', 'columns' => 'file_id' ],
1051
1052 // mwai_chats indexes
1053 [ 'table' => 'mwai_chats', 'name' => 'idx_mwai_chats_chatId_botId', 'columns' => 'chatId, botId' ],
1054 [ 'table' => 'mwai_chats', 'name' => 'idx_mwai_chats_chatId_userId', 'columns' => 'chatId, userId' ],
1055 [ 'table' => 'mwai_chats', 'name' => 'idx_mwai_chats_updated', 'columns' => 'updated' ],
1056 ];
1057
1058 // Add indexes
1059 foreach ( $indexes as $index ) {
1060 $table = $wpdb->prefix . $index['table'];
1061 $index_name = $index['name'];
1062 $columns = $index['columns'];
1063
1064 // Check if index already exists
1065 $existing = $wpdb->get_var( $wpdb->prepare(
1066 "SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS
1067 WHERE table_schema = %s AND table_name = %s AND index_name = %s",
1068 DB_NAME, $table, $index_name
1069 ) );
1070
1071 if ( !$existing ) {
1072 $wpdb->query( "ALTER TABLE `$table` ADD INDEX `$index_name` ($columns)" );
1073 $results[] = "Added index $index_name on $table";
1074 }
1075 }
1076
1077 // Clean up old logs (older than 3 months)
1078 $three_months_ago = date( 'Y-m-d H:i:s', strtotime( '-3 months' ) );
1079
1080 // Delete old logs
1081 $deleted_logs = $wpdb->query( $wpdb->prepare(
1082 "DELETE FROM {$wpdb->prefix}mwai_logs WHERE time < %s",
1083 $three_months_ago
1084 ) );
1085 $results[] = "Deleted $deleted_logs old log entries";
1086
1087 // Delete orphaned logmeta
1088 $deleted_logmeta = $wpdb->query(
1089 "DELETE lm FROM {$wpdb->prefix}mwai_logmeta lm
1090 LEFT JOIN {$wpdb->prefix}mwai_logs l ON lm.log_id = l.id
1091 WHERE l.id IS NULL"
1092 );
1093 $results[] = "Deleted $deleted_logmeta orphaned logmeta entries";
1094
1095 // Delete old chats (older than 3 months)
1096 $deleted_chats = $wpdb->query( $wpdb->prepare(
1097 "DELETE FROM {$wpdb->prefix}mwai_chats WHERE updated < %s",
1098 $three_months_ago
1099 ) );
1100 $results[] = "Deleted $deleted_chats old chat discussions";
1101
1102 // Optimize tables
1103 $tables = [ 'mwai_logs', 'mwai_logmeta', 'mwai_vectors', 'mwai_files', 'mwai_filemeta', 'mwai_chats' ];
1104 foreach ( $tables as $table ) {
1105 $wpdb->query( "OPTIMIZE TABLE {$wpdb->prefix}$table" );
1106 }
1107 $results[] = "Optimized all AI Engine tables";
1108
1109 $message = implode( "\n", $results );
1110 return $this->create_rest_response( [ 'success' => true, 'message' => $message ], 200 );
1111 }
1112 catch ( Exception $e ) {
1113 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1114 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1115 }
1116 }
1117
1118 public function rest_system_templates_get( $request ) {
1119 try {
1120 $params = $request->get_query_params();
1121 $category = $params['category'];
1122 $templates = [];
1123 $templates_option = get_option( 'mwai_templates', [] );
1124 if ( !is_array( $templates_option ) ) {
1125 update_option( 'mwai_templates', [] );
1126 }
1127 $categories = array_column( $templates_option, 'category' );
1128 $index = array_search( $category, $categories );
1129 $templates = [];
1130 if ( $index !== false ) {
1131 $templates = $templates_option[$index]['templates'];
1132 }
1133 return $this->create_rest_response( [ 'success' => true, 'templates' => $templates ], 200 );
1134 }
1135 catch ( Exception $e ) {
1136 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1137 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1138 }
1139 }
1140
1141 public function rest_system_templates_save( $request ) {
1142 try {
1143 $params = $request->get_json_params();
1144 $category = $params['category'];
1145 $templates = $params['templates'];
1146 $templates_option = get_option( 'mwai_templates', [] );
1147 $categories = array_column( $templates_option, 'category' );
1148 $index = array_search( $category, $categories );
1149 if ( $index !== false && $index >= 0 ) {
1150 $templates_option[$index]['templates'] = $templates;
1151 }
1152 else {
1153 $group = [ 'category' => $category, 'templates' => $templates ];
1154 $templates_option[] = $group;
1155 }
1156
1157 update_option( 'mwai_templates', $templates_option );
1158 return $this->create_rest_response( [ 'success' => true ], 200 );
1159 }
1160 catch ( Exception $e ) {
1161 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1162 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1163 }
1164 }
1165
1166 public function rest_system_logs_list( $request ) {
1167 try {
1168 $params = $request->get_json_params();
1169 $offset = $params['offset'];
1170 $limit = $params['limit'];
1171 $filters = $params['filters'];
1172 $sort = isset( $params['sort'] ) ? $params['sort'] : null;
1173 $logs = apply_filters( 'mwai_stats_logs_list', [], $offset, $limit, $filters, $sort );
1174 return $this->create_rest_response( [ 'success' => true, 'total' => $logs['total'], 'logs' => $logs['rows'] ], 200 );
1175 }
1176 catch ( Exception $e ) {
1177 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1178 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1179 }
1180 }
1181
1182 public function rest_system_logs_delete( $request ) {
1183 try {
1184 $params = $request->get_json_params();
1185 $logIds = $params['logIds'];
1186 $success = apply_filters( 'mwai_stats_logs_delete', true, $logIds );
1187 return $this->create_rest_response( [ 'success' => $success ], 200 );
1188 }
1189 catch ( Exception $e ) {
1190 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1191 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1192 }
1193 }
1194
1195 public function rest_system_logs_meta_get( $request ) {
1196 try {
1197 $params = $request->get_json_params();
1198 $logId = $params['logId'];
1199 $metaKeys = $params['metaKeys'];
1200 $data = apply_filters( 'mwai_stats_logs_meta', [], $logId, $metaKeys );
1201 return $this->create_rest_response( [ 'success' => true, 'data' => $data ], 200 );
1202 }
1203 catch ( Exception $e ) {
1204 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1205 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1206 }
1207 }
1208
1209 public function rest_system_logs_activity( $request ) {
1210 try {
1211 $params = $request->get_json_params();
1212 $hours = isset( $params['hours'] ) ? intval( $params['hours'] ) : 24;
1213 $data = apply_filters( 'mwai_stats_logs_activity', [], $hours );
1214 return $this->create_rest_response( [ 'success' => true, 'data' => $data ], 200 );
1215 }
1216 catch ( Exception $e ) {
1217 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1218 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1219 }
1220 }
1221
1222 public function rest_system_logs_activity_daily( $request ) {
1223 try {
1224 $params = $request->get_json_params();
1225 $days = isset( $params['days'] ) ? intval( $params['days'] ) : 31;
1226 $byModel = isset( $params['byModel'] ) ? (bool) $params['byModel'] : false;
1227
1228 if ( $byModel ) {
1229 $data = apply_filters( 'mwai_stats_logs_activity_daily_by_model', [], $days );
1230 } else {
1231 $data = apply_filters( 'mwai_stats_logs_activity_daily', [], $days );
1232 }
1233
1234 return $this->create_rest_response( [ 'success' => true, 'data' => $data ], 200 );
1235 }
1236 catch ( Exception $e ) {
1237 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1238 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1239 }
1240 }
1241
1242 public function rest_ai_moderate( $request ) {
1243 try {
1244 $params = $request->get_json_params();
1245 $envId = $params['envId'];
1246 $text = $params['text'];
1247 if ( !$text ) {
1248 return $this->create_rest_response( [ 'success' => false, 'message' => 'Text not found.' ], 404 );
1249 }
1250 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
1251 $results = $openai->moderate( $text );
1252 return $this->create_rest_response( [ 'success' => true, 'results' => $results ], 200 );
1253 }
1254 catch ( Exception $e ) {
1255 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1256 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1257 }
1258 }
1259
1260 public function rest_ai_transcribe_audio( $request ) {
1261 try {
1262 global $mwai;
1263 $params = $request->get_json_params();
1264 $url = !empty( $params['url'] ) ? $params['url'] : null;
1265 $mediaId = isset( $params['mediaId'] ) ? intval( $params['mediaId'] ) : 0;
1266 $path = !empty( $params['path'] ) ? $params['path'] : null;
1267
1268 // If mediaId is provided, get the file path
1269 if ( !$path && $mediaId > 0 ) {
1270 $path = get_attached_file( $mediaId );
1271 if ( empty( $path ) ) {
1272 throw new Exception( 'The media file cannot be found.' );
1273 }
1274 }
1275
1276 // Set the scope for admin tools
1277 if ( !isset( $params['scope'] ) ) {
1278 $params['scope'] = 'admin-tools';
1279 }
1280
1281 $result = $mwai->simpleTranscribeAudio( $url, $path, $params );
1282 return $this->create_rest_response( [ 'success' => true, 'data' => $result ], 200 );
1283 }
1284 catch ( Exception $e ) {
1285 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1286 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1287 }
1288 }
1289
1290 public function rest_ai_transcribe_image( $request ) {
1291 try {
1292 global $mwai;
1293 $params = $request->get_json_params();
1294 $message = $this->retrieve_message( $params );
1295 $url = !empty( $params['url'] ) ? $params['url'] : null;
1296 // This could lead to a security issue, so let's avoid using path directly.
1297 //$path = !empty( $params['path'] ) ? $params['path'] : null;
1298 $result = $mwai->simpleVisionQuery( $message, $url );
1299 return $this->create_rest_response( [ 'success' => true, 'data' => $result ], 200 );
1300 }
1301 catch ( Exception $e ) {
1302 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1303 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1304 }
1305 }
1306
1307 public function rest_ai_json( $request ) {
1308 try {
1309 global $mwai;
1310 $params = $request->get_json_params();
1311 $message = $this->retrieve_message( $params );
1312 $result = $mwai->simpleJsonQuery( $message );
1313 return $this->create_rest_response( [ 'success' => true, 'data' => $result ], 200 );
1314 }
1315 catch ( Exception $e ) {
1316 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1317 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1318 }
1319 }
1320
1321 public function rest_mcp_functions( $request ) {
1322 try {
1323 // Get all registered MCP tools
1324 $tools = apply_filters( 'mwai_mcp_tools', [] );
1325
1326 // Format the response
1327 $response = [
1328 'success' => true,
1329 'count' => count( $tools ),
1330 'functions' => $tools
1331 ];
1332
1333 return $this->create_rest_response( $response, 200 );
1334 }
1335 catch ( Exception $e ) {
1336 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1337 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1338 }
1339 }
1340
1341 public function rest_helpers_post_types() {
1342 try {
1343 $postTypes = $this->core->get_post_types();
1344 return $this->create_rest_response( [ 'success' => true, 'postTypes' => $postTypes ], 200 );
1345 }
1346 catch ( Exception $e ) {
1347 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1348 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1349 }
1350 }
1351
1352 public function rest_settings_themes( $request ) {
1353 try {
1354 $method = $request->get_method();
1355 if ( $method === 'GET' ) {
1356 $themes = $this->core->get_themes();
1357 return $this->create_rest_response( [ 'success' => true, 'themes' => $themes ], 200 );
1358 }
1359 else if ( $method === 'POST' ) {
1360 $params = $request->get_json_params();
1361 $themes = $params['themes'];
1362 $themes = $this->core->update_themes( $themes );
1363 return $this->create_rest_response( [ 'success' => true, 'themes' => $themes ], 200 );
1364 }
1365 }
1366 catch ( Exception $e ) {
1367 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1368 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1369 }
1370 }
1371
1372 public function rest_settings_chatbots( $request ) {
1373 try {
1374 $method = $request->get_method();
1375 if ( $method === 'GET' ) {
1376 $chatbots = $this->core->get_chatbots();
1377 return $this->create_rest_response( [ 'success' => true, 'chatbots' => $chatbots ], 200 );
1378 }
1379 else if ( $method === 'POST' ) {
1380 $params = $request->get_json_params();
1381 $chatbots = $params['chatbots'];
1382 $chatbots = $this->core->update_chatbots( $chatbots );
1383 return $this->create_rest_response( [ 'success' => true, 'chatbots' => $chatbots ], 200 );
1384 }
1385 return $this->create_rest_response( [ 'success' => false, 'message' => 'Method not allowed' ], 405 );
1386 }
1387 catch ( Exception $e ) {
1388 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1389 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1390 }
1391 }
1392
1393 #region Logs
1394
1395 public function rest_get_logs() {
1396 $logs = Meow_MWAI_Logging::get();
1397 return $this->create_rest_response( [ 'success' => true, 'data' => $logs ], 200 );
1398 }
1399
1400 public function rest_clear_logs() {
1401 Meow_MWAI_Logging::clear();
1402 return $this->create_rest_response( [ 'success' => true ], 200 );
1403 }
1404
1405 #endregion
1406 }
1407