PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 2.9.1
AI Engine – The Chatbot, AI Framework & MCP for WordPress v2.9.1
3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / classes / 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
1297 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 if ( $this->core->get_option( 'debug_mode' ) ) {
69 error_log( '[MWAI] Token refresh: Nonce is 12-24 hours old, providing fresh token' );
70 }
71 }
72 }
73
74 // If the nonce has changed or should be refreshed, include the new one
75 if ( $should_refresh || ( $request_nonce && $current_nonce !== $request_nonce ) ) {
76 $data['new_token'] = $current_nonce;
77
78 // Log if debug mode is enabled
79 if ( $this->core->get_option( 'debug_mode' ) ) {
80 error_log( '[MWAI] Token refresh: Sending new token in response' );
81 }
82 }
83
84 return new WP_REST_Response( $data, $status );
85 }
86
87 public function rest_init() {
88 try {
89 // Session Endpoint
90 register_rest_route( $this->namespace, '/start_session', [
91 'methods' => 'POST',
92 'permission_callback' => '__return_true', // Public endpoint for guest users
93 'callback' => [ $this, 'rest_start_session' ],
94 ] );
95
96 // Settings Endpoints
97 register_rest_route( $this->namespace, '/settings/update', [
98 'methods' => 'POST',
99 'permission_callback' => [ $this->core, 'can_access_settings' ],
100 'callback' => [ $this, 'rest_settings_update' ],
101 ] );
102 register_rest_route( $this->namespace, '/settings/options', [
103 'methods' => 'GET',
104 'permission_callback' => [ $this->core, 'can_access_settings' ],
105 'callback' => [ $this, 'rest_settings_list' ],
106 ] );
107 register_rest_route( $this->namespace, '/settings/reset', [
108 'methods' => 'POST',
109 'permission_callback' => [ $this->core, 'can_access_settings' ],
110 'callback' => [ $this, 'rest_settings_reset' ],
111 ] );
112 register_rest_route( $this->namespace, '/settings/chatbots', [
113 'methods' => ['GET', 'POST'],
114 'permission_callback' => [ $this->core, 'can_access_settings' ],
115 'callback' => [ $this, 'rest_settings_chatbots' ],
116 ] );
117 register_rest_route( $this->namespace, '/settings/themes', [
118 'methods' => ['GET', 'POST'],
119 'permission_callback' => [ $this->core, 'can_access_settings' ],
120 'callback' => [ $this, 'rest_settings_themes' ],
121 ] );
122
123 // System Endpoints
124 register_rest_route( $this->namespace, '/system/logs/list', [
125 'methods' => 'POST',
126 'permission_callback' => [ $this->core, 'can_access_settings' ],
127 'callback' => [ $this, 'rest_system_logs_list' ],
128 ] );
129 register_rest_route( $this->namespace, '/system/logs/delete', [
130 'methods' => 'POST',
131 'permission_callback' => [ $this->core, 'can_access_settings' ],
132 'callback' => [ $this, 'rest_system_logs_delete' ],
133 ] );
134 register_rest_route( $this->namespace, '/system/logs/meta', [
135 'methods' => 'POST',
136 'permission_callback' => [ $this->core, 'can_access_settings' ],
137 'callback' => [ $this, 'rest_system_logs_meta_get' ],
138 ] );
139 register_rest_route( $this->namespace, '/system/logs/activity', [
140 'methods' => 'POST',
141 'permission_callback' => [ $this->core, 'can_access_settings' ],
142 'callback' => [ $this, 'rest_system_logs_activity' ],
143 ] );
144 register_rest_route( $this->namespace, '/system/logs/activity_daily', [
145 'methods' => 'POST',
146 'permission_callback' => [ $this->core, 'can_access_settings' ],
147 'callback' => [ $this, 'rest_system_logs_activity_daily' ],
148 ] );
149 register_rest_route( $this->namespace, '/system/templates', [
150 'methods' => 'POST',
151 'permission_callback' => [ $this->core, 'can_access_features' ],
152 'callback' => [ $this, 'rest_system_templates_save' ],
153 ] );
154 register_rest_route( $this->namespace, '/system/templates', [
155 'methods' => 'GET',
156 'permission_callback' => [ $this->core, 'can_access_features' ],
157 'callback' => [ $this, 'rest_system_templates_get' ],
158 ] );
159
160 // AI Endpoints
161 register_rest_route( $this->namespace, '/ai/models', [
162 'methods' => 'POST',
163 'permission_callback' => [ $this->core, 'can_access_features' ],
164 'callback' => [ $this, 'rest_ai_models' ],
165 ] );
166 register_rest_route( $this->namespace, '/ai/test_connection', [
167 'methods' => 'POST',
168 'permission_callback' => [ $this->core, 'can_access_settings' ],
169 'callback' => [ $this, 'rest_ai_test_connection' ],
170 ] );
171 register_rest_route( $this->namespace, '/ai/completions', [
172 'methods' => 'POST',
173 'permission_callback' => [ $this->core, 'can_access_features' ],
174 'callback' => [ $this, 'rest_ai_completions' ],
175 ] );
176 register_rest_route( $this->namespace, '/ai/images', [
177 'methods' => 'POST',
178 'permission_callback' => [ $this->core, 'can_access_features' ],
179 'callback' => [ $this, 'rest_ai_images' ],
180 ] );
181 register_rest_route( $this->namespace, '/ai/image_edit', [
182 'methods' => 'POST',
183 'permission_callback' => [ $this->core, 'can_access_features' ],
184 'callback' => [ $this, 'rest_ai_image_edit' ],
185 ] );
186 register_rest_route( $this->namespace, '/ai/copilot', [
187 'methods' => 'POST',
188 'permission_callback' => [ $this->core, 'can_access_features' ],
189 'callback' => [ $this, 'rest_ai_copilot' ],
190 ] );
191
192 register_rest_route( $this->namespace, '/ai/magic_wand', [
193 'methods' => 'POST',
194 'callback' => [ $this, 'rest_ai_magic_wand' ],
195 'permission_callback' => [ $this->core, 'can_access_features' ],
196 ] );
197 register_rest_route( $this->namespace, '/ai/moderate', [
198 'methods' => 'POST',
199 'permission_callback' => [ $this->core, 'can_access_settings' ],
200 'callback' => [ $this, 'rest_ai_moderate' ],
201 ] );
202 register_rest_route( $this->namespace, '/ai/transcribe_audio', [
203 'methods' => 'POST',
204 'permission_callback' => [ $this->core, 'can_access_settings' ],
205 'callback' => [ $this, 'rest_ai_transcribe_audio' ],
206 ] );
207 register_rest_route( $this->namespace, '/ai/transcribe_image', [
208 'methods' => 'POST',
209 'permission_callback' => [ $this->core, 'can_access_settings' ],
210 'callback' => [ $this, 'rest_ai_transcribe_image' ],
211 ] );
212 register_rest_route( $this->namespace, '/ai/json', [
213 'methods' => 'POST',
214 'permission_callback' => [ $this->core, 'can_access_settings' ],
215 'callback' => [ $this, 'rest_ai_json' ],
216 ] );
217
218 // MCP Endpoints
219 register_rest_route( $this->namespace, '/mcp/functions', [
220 'methods' => 'GET',
221 'permission_callback' => [ $this->core, 'can_access_settings' ],
222 'callback' => [ $this, 'rest_mcp_functions' ],
223 ] );
224
225 // Helpers Endpoints
226 register_rest_route( $this->namespace, '/helpers/update_post_title', [
227 'methods' => 'POST',
228 'permission_callback' => [ $this->core, 'can_access_features' ],
229 'callback' => [ $this, 'rest_helpers_update_title' ],
230 ] );
231 register_rest_route( $this->namespace, '/helpers/update_post_excerpt', [
232 'methods' => 'POST',
233 'permission_callback' => [ $this->core, 'can_access_features' ],
234 'callback' => [ $this, 'rest_helpers_update_excerpt' ],
235 ] );
236 register_rest_route( $this->namespace, '/helpers/create_post', [
237 'methods' => 'POST',
238 'permission_callback' => [ $this->core, 'can_access_features' ],
239 'callback' => [ $this, 'rest_helpers_create_post' ],
240 ] );
241 register_rest_route( $this->namespace, '/helpers/create_image', [
242 'methods' => 'POST',
243 'permission_callback' => [ $this->core, 'can_access_features' ],
244 'callback' => [ $this, 'rest_helpers_create_images' ],
245 ] );
246 register_rest_route( $this->namespace, '/helpers/generate_image_meta', [
247 'methods' => 'POST',
248 'permission_callback' => [ $this->core, 'can_access_features' ],
249 'callback' => [ $this, 'rest_helpers_generate_image_meta' ],
250 ] );
251 register_rest_route( $this->namespace, '/helpers/count_posts', [
252 'methods' => 'GET',
253 'permission_callback' => [ $this->core, 'can_access_features' ],
254 'callback' => [ $this, 'rest_helpers_count_posts' ],
255 ] );
256 register_rest_route( $this->namespace, '/helpers/posts_ids', [
257 'methods' => 'GET',
258 'permission_callback' => [ $this->core, 'can_access_features' ],
259 'callback' => [ $this, 'rest_helpers_posts_ids' ],
260 ] );
261 register_rest_route( $this->namespace, '/helpers/post_types', [
262 'methods' => 'GET',
263 'permission_callback' => [ $this->core, 'can_access_features' ],
264 'callback' => [ $this, 'rest_helpers_post_types' ],
265 ] );
266 register_rest_route( $this->namespace, '/helpers/post_content', [
267 'methods' => 'GET',
268 'permission_callback' => [ $this->core, 'can_access_features' ],
269 'callback' => [ $this, 'rest_helpers_post_content' ],
270 ] );
271 register_rest_route( $this->namespace, '/helpers/run_tasks', [
272 'methods' => 'POST',
273 'permission_callback' => [ $this->core, 'can_access_features' ],
274 'callback' => [ $this, 'rest_helpers_run_tasks' ],
275 ] );
276
277 // OpenAI Endpoints
278 register_rest_route( $this->namespace, '/openai/files/list', [
279 'methods' => 'GET',
280 'permission_callback' => [ $this->core, 'can_access_settings' ],
281 'callback' => [ $this, 'rest_openai_files_get' ],
282 ] );
283 register_rest_route( $this->namespace, '/openai/files/upload', [
284 'methods' => 'POST',
285 'permission_callback' => [ $this->core, 'can_access_settings' ],
286 'callback' => [ $this, 'rest_openai_files_upload' ],
287 ] );
288 register_rest_route( $this->namespace, '/openai/files/delete', [
289 'methods' => 'POST',
290 'permission_callback' => [ $this->core, 'can_access_settings' ],
291 'callback' => [ $this, 'rest_openai_files_delete' ],
292 ] );
293 register_rest_route( $this->namespace, '/openai/files/download', [
294 'methods' => 'POST',
295 'permission_callback' => [ $this->core, 'can_access_settings' ],
296 'callback' => [ $this, 'rest_openai_files_download' ],
297 ] );
298 register_rest_route( $this->namespace, '/openai/files/finetune', [
299 'methods' => 'POST',
300 'permission_callback' => [ $this->core, 'can_access_settings' ],
301 'callback' => [ $this, 'rest_openai_files_finetune' ],
302 ] );
303 register_rest_route( $this->namespace, '/openai/finetunes/list_deleted', [
304 'methods' => 'GET',
305 'permission_callback' => [ $this->core, 'can_access_settings' ],
306 'callback' => [ $this, 'rest_openai_deleted_finetunes_get' ],
307 ] );
308
309 // register_rest_route( $this->namespace, '/openai/models', array(
310 // 'methods' => 'GET',
311 // 'permission_callback' => [ $this->core, 'can_access_settings' ],
312 // 'callback' => [ $this, 'rest_openai_models_get' ],
313 // ) );
314
315 register_rest_route( $this->namespace, '/openai/finetunes/list', [
316 'methods' => 'GET',
317 'permission_callback' => [ $this->core, 'can_access_settings' ],
318 'callback' => [ $this, 'rest_openai_finetunes_get' ],
319 ] );
320 register_rest_route( $this->namespace, '/openai/finetunes/delete', [
321 'methods' => 'POST',
322 'permission_callback' => [ $this->core, 'can_access_settings' ],
323 'callback' => [ $this, 'rest_openai_finetunes_delete' ],
324 ] );
325 register_rest_route( $this->namespace, '/openai/finetunes/cancel', [
326 'methods' => 'POST',
327 'permission_callback' => [ $this->core, 'can_access_settings' ],
328 'callback' => [ $this, 'rest_openai_finetunes_cancel' ],
329 ] );
330
331 // Logging Endpoints
332 register_rest_route( $this->namespace, '/get_logs', [
333 'methods' => 'GET',
334 'permission_callback' => [ $this->core, 'can_access_features' ],
335 'callback' => [ $this, 'rest_get_logs' ]
336 ] );
337 register_rest_route( $this->namespace, '/clear_logs', [
338 'methods' => 'GET',
339 'permission_callback' => [ $this->core, 'can_access_features' ],
340 'callback' => [ $this, 'rest_clear_logs' ]
341 ] );
342 }
343 catch ( Exception $e ) {
344 Meow_MWAI_Logging::error( 'REST API initialization failed: ' . $e->getMessage() );
345 }
346 }
347
348 public function rest_start_session() {
349 try {
350 $sessionId = $this->core->get_session_id();
351 $restNonce = $this->core->get_nonce( true );
352
353 $response = [
354 'success' => true,
355 'sessionId' => $sessionId,
356 'restNonce' => $restNonce
357 ];
358
359 // If in test mode and we have a new token, it will be added by create_rest_response
360 // But we also want to ensure the restNonce matches the test token if available
361 if ( get_option( 'mwai_token_test_mode' ) ) {
362 $token_data = get_option( 'mwai_test_token_data' );
363 if ( $token_data && isset( $token_data['token'] ) ) {
364 $response['restNonce'] = $token_data['token'];
365 }
366 }
367
368 return $this->create_rest_response( $response, 200 );
369 }
370 catch ( Exception $e ) {
371 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
372 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
373 }
374 }
375
376 public function rest_settings_list() {
377 return $this->create_rest_response( [
378 'success' => true,
379 'options' => $this->core->get_all_options()
380 ], 200 );
381 }
382
383 public function rest_settings_update( $request ) {
384 try {
385 $params = $request->get_json_params();
386 $value = $params['options'];
387 $options = $this->core->update_options( $value );
388 $success = !!$options;
389 $message = __( $success ? 'OK' : 'Could not update options.', 'ai-engine' );
390 return $this->create_rest_response( [ 'success' => $success, 'message' => $message, 'options' => $options ], 200 );
391 }
392 catch ( Exception $e ) {
393 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
394 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
395 }
396 }
397
398 public function rest_settings_reset() {
399 try {
400 $options = $this->core->reset_options();
401 $success = !!$options;
402 $message = __( $success ? 'OK' : 'Could not reset options.', 'ai-engine' );
403 return $this->create_rest_response( [ 'success' => $success, 'message' => $message, 'options' => $options ], 200 );
404 }
405 catch ( Exception $e ) {
406 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
407 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
408 }
409 }
410
411 public function rest_ai_models( $request ) {
412 try {
413 $params = $request->get_json_params();
414 $envId = $params['envId'];
415 $engine = Meow_MWAI_Engines_Factory::get( $this->core, $envId );
416 $models = $engine->retrieve_models();
417 return $this->create_rest_response( [ 'success' => true, 'models' => $models ], 200 );
418 }
419 catch ( Exception $e ) {
420 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
421 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
422 }
423 }
424
425 public function rest_ai_test_connection( $request ) {
426 try {
427 $params = $request->get_json_params();
428 $envId = $params['env_id'];
429
430 // Get the environment details
431 $env = null;
432 $envs = $this->core->get_option( 'ai_envs' );
433 foreach ( $envs as $e ) {
434 if ( $e['id'] === $envId ) {
435 $env = $e;
436 break;
437 }
438 }
439
440 if ( !$env ) {
441 throw new Exception( 'Environment not found.' );
442 }
443
444 // Get the engine and test connection
445 $engine = Meow_MWAI_Engines_Factory::get( $this->core, $envId );
446 $result = $engine->connection_check();
447
448 // Format the response based on provider
449 $response = [
450 'success' => true,
451 'provider' => $env['type'],
452 'name' => $env['name'],
453 'data' => $result
454 ];
455
456 return $this->create_rest_response( $response, 200 );
457 }
458 catch ( Exception $e ) {
459 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
460 return $this->create_rest_response( [
461 'success' => false,
462 'error' => $message,
463 'provider' => isset( $env ) ? $env['type'] : 'unknown'
464 ], 200 ); // Return 200 even on error for consistent modal display
465 }
466 }
467
468 public function rest_ai_completions( $request ) {
469 try {
470 $params = $request->get_json_params();
471 $message = $this->retrieve_message( $params );
472 $query = new Meow_MWAI_Query_Text( $message );
473 $query->inject_params( $params );
474
475 // Handle streaming
476 $stream = $params['stream'] ?? false;
477 $streamCallback = null;
478 if ( $stream ) {
479 $streamCallback = function ( $reply ) use ( $query ) {
480 //$raw = _wp_specialchars( $reply, ENT_NOQUOTES, 'UTF-8', true );
481 $raw = $reply;
482 $this->core->stream_push( [ 'type' => 'live', 'data' => $raw ], $query );
483 if ( ob_get_level() > 0 ) {
484 ob_flush();
485 }
486 flush();
487 };
488 if ( headers_sent( $filename, $linenum ) ) {
489 throw new Exception( "Headers already sent in $filename on line $linenum. Cannot start streaming." );
490 }
491 header( 'Cache-Control: no-cache' );
492 header( 'Content-Type: text/event-stream' );
493 header( 'X-Accel-Buffering: no' ); // This is useful to disable buffering in nginx through headers.
494 ob_implicit_flush( true );
495 if ( ob_get_level() > 0 ) {
496 ob_end_flush();
497 }
498 }
499
500 // Process Reply
501 $reply = $this->core->run_query( $query, $streamCallback );
502 $restRes = [
503 'success' => true,
504 'data' => $reply->result,
505 'usage' => $reply->usage
506 ];
507 if ( $stream ) {
508 $this->core->stream_push( [ 'type' => 'end', 'data' => json_encode( $restRes ) ], $query );
509 die();
510 }
511 return $this->create_rest_response( $restRes, 200 );
512 }
513 catch ( Exception $e ) {
514 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
515 if ( $stream ) {
516 $this->core->stream_push( [ 'type' => 'error', 'data' => $message ], $query );
517 }
518 else {
519 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
520 }
521 }
522 }
523
524 public function rest_ai_images( $request ) {
525 try {
526 $params = $request->get_json_params();
527 $message = $this->retrieve_message( $params );
528 $query = new Meow_MWAI_Query_Image( $message );
529 $query->inject_params( $params );
530 $reply = $this->core->run_query( $query );
531 return $this->create_rest_response( [ 'success' => true, 'data' => $reply->results, 'usage' => $reply->usage ], 200 );
532 }
533 catch ( Exception $e ) {
534 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
535 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
536 }
537 }
538
539 public function rest_ai_image_edit( $request ) {
540 try {
541 // Check if this is a multipart request with files
542 $files = $request->get_file_params();
543 $params = null;
544
545 // Log all request data
546 error_log( 'Image Edit Request - Method: ' . $request->get_method() );
547 $content_type = $request->get_content_type();
548 if ( is_array( $content_type ) ) {
549 error_log( 'Image Edit Request - Content-Type: ' . $content_type['value'] );
550 }
551 else {
552 error_log( 'Image Edit Request - Content-Type: ' . $content_type );
553 }
554 error_log( 'Image Edit Request - Has files: ' . ( !empty( $files ) ? 'yes (' . count( $files ) . ')' : 'no' ) );
555
556 if ( !empty( $files ) ) {
557 // Handle multipart form data - get all params including POST data
558 $params = $request->get_params();
559 error_log( 'Image Edit Request - Using form data params' );
560 }
561 else {
562 // Try to get body params first (for form data without files)
563 $body_params = $request->get_body_params();
564 if ( !empty( $body_params ) ) {
565 $params = $body_params;
566 error_log( 'Image Edit Request - Using body params' );
567 }
568 else {
569 // Handle JSON request
570 $params = $request->get_json_params();
571 error_log( 'Image Edit Request - Using JSON params' );
572 }
573 }
574
575 // Ensure params is always an array
576 if ( empty( $params ) ) {
577 $params = [];
578 }
579
580 // Debug logging
581 error_log( 'Image Edit Request - Has files: ' . ( !empty( $files ) ? 'yes' : 'no' ) );
582 error_log( 'Image Edit Request - Params: ' . json_encode( $params ) );
583
584 $message = $this->retrieve_message( $params );
585 $mediaId = isset( $params['mediaId'] ) ? intval( $params['mediaId'] ) : 0;
586 $query = new Meow_MWAI_Query_EditImage( $message );
587
588 // The inject_params method will handle setting the file from mediaId
589 $query->inject_params( $params );
590
591 // Handle mask file if provided
592 if ( !empty( $files['mask'] ) ) {
593 $mask_file = $files['mask'];
594 if ( $mask_file['error'] === UPLOAD_ERR_OK ) {
595 $mask_data = file_get_contents( $mask_file['tmp_name'] );
596 $query->set_mask( Meow_MWAI_Query_DroppedFile::from_data( $mask_data, 'vision', $mask_file['type'] ) );
597 }
598 }
599
600 $reply = $this->core->run_query( $query );
601 return $this->create_rest_response( [ 'success' => true, 'data' => $reply->results, 'usage' => $reply->usage ], 200 );
602 }
603 catch ( Exception $e ) {
604 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
605 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
606 }
607 }
608
609 public function rest_ai_magic_wand( $request ) {
610 try {
611 $params = $request->get_json_params();
612 $action = isset( $params['action'] ) ? $params['action'] : null;
613 $data = isset( $params['data'] ) ? $params['data'] : null;
614 if ( empty( $data ) || empty( $action ) ) {
615 return $this->create_rest_response( [ 'success' => false, 'message' => 'An action and some data are required.' ], 500 );
616 }
617 $data = apply_filters( 'mwai_magic_wand_' . $action, '', $data );
618 return $this->create_rest_response( [ 'success' => true, 'data' => $data ], 200 );
619 }
620 catch ( Exception $e ) {
621 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
622 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
623 }
624 }
625
626 public function rest_ai_copilot( $request ) {
627 try {
628 $params = $request->get_json_params();
629 $action = sanitize_text_field( $params['action'] );
630 $message = $this->retrieve_message( $params, true );
631 $context = sanitize_text_field( $params['context'] );
632 $postId = !empty( $params['postId'] ) ? intval( $params['postId'] ) : null;
633 if ( empty( $action ) || empty( $message ) ) {
634 return $this->create_rest_response( [ 'success' => false, 'message' => 'Copilot needs an action and a prompt.' ], 500 );
635 }
636
637 global $mwai;
638 $result = null;
639 $params = [ 'scope' => 'copilot' ];
640
641 if ( $action === 'text' ) {
642 $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.";
643 if ( !empty( $model ) ) {
644 $params['model'] = $model;
645 }
646 $result = $mwai->simpleTextQuery( $prompt, $params );
647 }
648 else if ( $action === 'image' ) {
649 $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.";
650
651 // Create the image
652 $simplifiedPrompt = $mwai->simpleTextQuery( $prompt, $params );
653 $media = $mwai->imageQueryForMediaLibrary( $simplifiedPrompt, $params, $postId );
654 $result = [ 'media' => $media ];
655 }
656 return $this->create_rest_response( [ 'success' => true, 'data' => $result ], 200 );
657 }
658 catch ( Exception $e ) {
659 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
660 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
661 }
662 }
663
664 public function rest_helpers_update_title( $request ) {
665 try {
666 $params = $request->get_json_params();
667 $title = sanitize_text_field( $params['title'] );
668 $postId = intval( $params['postId'] );
669 $post = get_post( $postId );
670 if ( !$post ) {
671 throw new Exception( 'There is no post with this ID.' );
672 }
673 $post->post_title = $title;
674 //$post->post_name = sanitize_title( $title );
675 wp_update_post( $post );
676 return $this->create_rest_response( [ 'success' => true, 'message' => 'Title updated.' ], 200 );
677 }
678 catch ( Exception $e ) {
679 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
680 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
681 }
682 }
683
684 public function rest_helpers_update_excerpt( $request ) {
685 try {
686 $params = $request->get_json_params();
687 $excerpt = sanitize_text_field( $params['excerpt'] );
688 $postId = intval( $params['postId'] );
689 $post = get_post( $postId );
690 if ( !$post ) {
691 throw new Exception( 'There is no post with this ID.' );
692 }
693 $post->post_excerpt = $excerpt;
694 wp_update_post( $post );
695 return $this->create_rest_response( [ 'success' => true, 'message' => 'Excerpt updated.' ], 200 );
696 }
697 catch ( Exception $e ) {
698 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
699 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
700 }
701 }
702
703 public function rest_helpers_create_post( $request ) {
704 try {
705 $params = $request->get_json_params();
706 $title = sanitize_text_field( $params['title'] );
707 $content = sanitize_textarea_field( $params['content'] );
708 $excerpt = sanitize_text_field( $params['excerpt'] );
709 $postType = sanitize_text_field( $params['postType'] );
710 $post = new stdClass();
711 $post->post_title = $title;
712 $post->post_excerpt = $excerpt;
713 $post->post_content = $content;
714 $post->post_status = 'draft';
715 $post->post_type = isset( $postType ) ? $postType : 'post';
716 // TODO: Let's try to avoid using Markdown to create the Post
717 // Instead, we should create Gutenberg Blocks, or simple HTML.
718 // Then, we can get rid of the library for Markdown.
719 $post->post_content = $this->core->markdown_to_html( $post->post_content );
720 $postId = wp_insert_post( $post );
721 return $this->create_rest_response( [ 'success' => true, 'postId' => $postId ], 200 );
722 }
723 catch ( Exception $e ) {
724 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
725 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
726 }
727 }
728
729 public function rest_helpers_create_images( $request ) {
730 try {
731 $params = $request->get_json_params();
732 $title = sanitize_text_field( $params['title'] );
733 $caption = sanitize_text_field( $params['caption'] );
734 $alt = sanitize_text_field( $params['alt'] );
735 $description = sanitize_text_field( $params['description'] );
736 $url = $params['url'];
737 $filename = sanitize_text_field( $params['filename'] );
738 $attachmentId = $this->core->add_image_from_url( $url, $filename, $title, $description, $caption, $alt );
739 return $this->create_rest_response( [ 'success' => true, 'attachmentId' => $attachmentId ], 200 );
740 }
741 catch ( Exception $e ) {
742 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
743 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
744 }
745 }
746
747 public function rest_helpers_generate_image_meta( $request ) {
748 try {
749 global $mwai;
750 $params = $request->get_json_params();
751 $url = isset( $params['url'] ) ? esc_url_raw( $params['url'] ) : null;
752 if ( empty( $url ) ) {
753 throw new Exception( 'The url is required.' );
754 }
755 $prompt = 'Describe this image and suggest a short title, description and SEO-friendly (ASCII and lowercase) filename. '
756 . 'Return a JSON with the keys title, description, alt, caption, filename.';
757 $result = $mwai->simpleVisionQuery( $prompt, $url, null, [ 'image_remote_upload' => 'url', 'scope' => 'admin-tools' ] );
758 $result = preg_replace( '/^```json\s*/', '', $result );
759 $result = preg_replace( '/\s*```$/', '', $result );
760 if ( is_string( $result ) ) {
761 $data = json_decode( $result, true );
762 }
763 else {
764 $data = $result;
765 }
766 if ( !is_array( $data ) ) {
767 $data = [];
768 }
769 $data = array_merge( [ 'title' => '', 'description' => '', 'caption' => '', 'alt' => '', 'filename' => '' ], $data );
770 return $this->create_rest_response( [ 'success' => true, 'data' => $data ], 200 );
771 }
772 catch ( Exception $e ) {
773 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
774 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
775 }
776 }
777
778 public function rest_openai_files_get() {
779 try {
780 $envId = isset( $_GET['envId'] ) ? $_GET['envId'] : null;
781 $purposeFilter = isset( $_GET['purpose'] ) ? $_GET['purpose'] : null;
782 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
783 $files = $openai->list_files( $purposeFilter );
784 return $this->create_rest_response( [ 'success' => true, 'files' => $files ], 200 );
785 }
786 catch ( Exception $e ) {
787 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
788 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
789 }
790 }
791
792 public function rest_openai_deleted_finetunes_get() {
793 try {
794 $envId = isset( $_GET['envId'] ) ? $_GET['envId'] : null;
795 $legacy = isset( $_GET['legacy'] ) ? $_GET['legacy'] === 'true' : false;
796 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
797 $finetunes = $openai->list_deleted_finetunes( $legacy );
798 return $this->create_rest_response( [ 'success' => true, 'finetunes' => $finetunes ], 200 );
799 }
800 catch ( Exception $e ) {
801 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
802 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
803 }
804 }
805
806 public function rest_openai_finetunes_get() {
807 try {
808 $envId = isset( $_GET['envId'] ) ? $_GET['envId'] : null;
809 $legacy = isset( $_GET['legacy'] ) ? $_GET['legacy'] === 'true' : false;
810 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
811 $finetunes = $openai->list_finetunes( $legacy );
812 return $this->create_rest_response( [ 'success' => true, 'finetunes' => $finetunes ], 200 );
813 }
814 catch ( Exception $e ) {
815 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
816 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
817 }
818 }
819
820 public function rest_openai_files_upload( $request ) {
821 try {
822 $params = $request->get_json_params();
823 $envId = $params['envId'];
824 ;
825 $filename = sanitize_text_field( $params['filename'] );
826 $data = $params['data'];
827 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
828 $file = $openai->upload_file( $filename, $data );
829 return $this->create_rest_response( [ 'success' => true, 'file' => $file ], 200 );
830 }
831 catch ( Exception $e ) {
832 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
833 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
834 }
835 }
836
837 public function rest_openai_files_delete( $request ) {
838 try {
839 $params = $request->get_json_params();
840 $envId = $params['envId'];
841 ;
842 $fileId = $params['fileId'];
843 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
844 $openai->delete_file( $fileId );
845 return $this->create_rest_response( [ 'success' => true ], 200 );
846 }
847 catch ( Exception $e ) {
848 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
849 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
850 }
851 }
852
853 public function rest_openai_finetunes_cancel( $request ) {
854 try {
855 $params = $request->get_json_params();
856 $envId = $params['envId'];
857 ;
858 $finetuneId = $params['finetuneId'];
859 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
860 $openai->cancel_finetune( $finetuneId );
861 return $this->create_rest_response( [ 'success' => true ], 200 );
862 }
863 catch ( Exception $e ) {
864 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
865 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
866 }
867 }
868
869 public function rest_openai_finetunes_delete( $request ) {
870 try {
871 $params = $request->get_json_params();
872 $envId = $params['envId'];
873 ;
874 $modelId = $params['modelId'];
875 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
876 $openai->delete_finetune( $modelId );
877 return $this->create_rest_response( [ 'success' => true ], 200 );
878 }
879 catch ( Exception $e ) {
880 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
881 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
882 }
883 }
884
885 public function rest_openai_files_download( $request ) {
886 try {
887 $params = $request->get_json_params();
888 $envId = $params['envId'];
889 ;
890 $fileId = $params['fileId'];
891 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
892 $filename = $openai->download_file( $fileId );
893 $data = file_get_contents( $filename );
894 return $this->create_rest_response( [ 'success' => true, 'data' => $data ], 200 );
895 }
896 catch ( Exception $e ) {
897 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
898 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
899 }
900 }
901
902 public function rest_openai_files_finetune( $request ) {
903 try {
904 $params = $request->get_json_params();
905 $envId = $params['envId'];
906 ;
907 $fileId = $params['fileId'];
908 $model = $params['model'];
909 $suffix = $params['suffix'];
910 $hyperparams = [
911 'nEpochs' => isset( $params['nEpochs'] ) ? $params['nEpochs'] : null,
912 'batchSize' => isset( $params['batchSize'] ) ? $params['batchSize'] : null,
913 ];
914 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
915 $finetune = $openai->run_finetune( $fileId, $model, $suffix, $hyperparams );
916 return $this->create_rest_response( [ 'success' => true, 'finetune' => $finetune ], 200 );
917 }
918 catch ( Exception $e ) {
919 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
920 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
921 }
922 }
923
924 public function rest_helpers_count_posts( $request ) {
925 try {
926 $params = $request->get_query_params();
927 $postType = $params['postType'];
928 $postStatus = !empty( $params['postStatus'] ) ? explode( ',', $params['postStatus'] ) : [ 'publish' ];
929 $count = wp_count_posts( $postType );
930 $count = array_sum( array_intersect_key( (array) $count, array_flip( $postStatus ) ) );
931 return $this->create_rest_response( [ 'success' => true, 'count' => $count ], 200 );
932 }
933 catch ( Exception $e ) {
934 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
935 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
936 }
937 }
938
939 public function rest_helpers_posts_ids( $request ) {
940 try {
941 $params = $request->get_query_params();
942 $postType = $params['postType'];
943 $postStatus = !empty( $params['postStatus'] ) ? explode( ',', $params['postStatus'] ) : [ 'publish' ];
944 $posts = get_posts( [
945 'posts_per_page' => -1,
946 'post_type' => $postType,
947 'post_status' => $postStatus,
948 'fields' => 'ids'
949 ] );
950 return $this->create_rest_response( [ 'success' => true, 'postIds' => $posts ], 200 );
951 }
952 catch ( Exception $e ) {
953 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
954 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
955 }
956 }
957
958 public function rest_helpers_post_content( $request ) {
959 try {
960 $params = $request->get_query_params();
961 $offset = (int) $params['offset'];
962 $postType = $params['postType'];
963 $postStatus = isset( $params['postStatus'] ) ? explode( ',', $params['postStatus'] ) : [ 'publish' ];
964 $postId = (int) $params['postId'];
965
966 $post = null;
967 if ( !empty( $postId ) ) {
968 $post = get_post( $postId );
969 if ( $post->post_status !== 'publish' && $post->post_status !== 'future'
970 && $post->post_status !== 'draft' && $post->post_status !== 'private' ) {
971 $post = null;
972 }
973 }
974 else {
975 $posts = get_posts( [
976 'posts_per_page' => 1,
977 'post_type' => $postType,
978 'offset' => $offset,
979 'post_status' => $postStatus,
980 ] );
981 $post = count( $posts ) === 0 ? null : $posts[0];
982 }
983 if ( !$post ) {
984 return $this->create_rest_response( [ 'success' => false, 'message' => 'Post not found' ], 404 );
985 }
986 $cleanPost = $this->core->get_post( $post );
987 return $this->create_rest_response( [ 'success' => true, 'content' => $cleanPost['content'],
988 'checksum' => $cleanPost['checksum'], 'language' => $cleanPost['language'], 'excerpt' => $cleanPost['excerpt'],
989 'postId' => $cleanPost['postId'], 'title' => $cleanPost['title'], 'url' => $cleanPost['url'] ], 200 );
990 }
991 catch ( Exception $e ) {
992 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
993 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
994 }
995 }
996
997 public function rest_helpers_run_tasks( $request ) {
998 try {
999 do_action( 'mwai_tasks_run' );
1000 return $this->create_rest_response( [ 'success' => true ], 200 );
1001 }
1002 catch ( Exception $e ) {
1003 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1004 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1005 }
1006 }
1007
1008 public function rest_system_templates_get( $request ) {
1009 try {
1010 $params = $request->get_query_params();
1011 $category = $params['category'];
1012 $templates = [];
1013 $templates_option = get_option( 'mwai_templates', [] );
1014 if ( !is_array( $templates_option ) ) {
1015 update_option( 'mwai_templates', [] );
1016 }
1017 $categories = array_column( $templates_option, 'category' );
1018 $index = array_search( $category, $categories );
1019 $templates = [];
1020 if ( $index !== false ) {
1021 $templates = $templates_option[$index]['templates'];
1022 }
1023 return $this->create_rest_response( [ 'success' => true, 'templates' => $templates ], 200 );
1024 }
1025 catch ( Exception $e ) {
1026 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1027 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1028 }
1029 }
1030
1031 public function rest_system_templates_save( $request ) {
1032 try {
1033 $params = $request->get_json_params();
1034 $category = $params['category'];
1035 $templates = $params['templates'];
1036 $templates_option = get_option( 'mwai_templates', [] );
1037 $categories = array_column( $templates_option, 'category' );
1038 $index = array_search( $category, $categories );
1039 if ( $index !== false && $index >= 0 ) {
1040 $templates_option[$index]['templates'] = $templates;
1041 }
1042 else {
1043 $group = [ 'category' => $category, 'templates' => $templates ];
1044 $templates_option[] = $group;
1045 }
1046
1047 update_option( 'mwai_templates', $templates_option );
1048 return $this->create_rest_response( [ 'success' => true ], 200 );
1049 }
1050 catch ( Exception $e ) {
1051 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1052 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1053 }
1054 }
1055
1056 public function rest_system_logs_list( $request ) {
1057 try {
1058 $params = $request->get_json_params();
1059 $offset = $params['offset'];
1060 $limit = $params['limit'];
1061 $filters = $params['filters'];
1062 $sort = $params['sort'];
1063 $logs = apply_filters( 'mwai_stats_logs_list', [], $offset, $limit, $filters, $sort );
1064 return $this->create_rest_response( [ 'success' => true, 'total' => $logs['total'], 'logs' => $logs['rows'] ], 200 );
1065 }
1066 catch ( Exception $e ) {
1067 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1068 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1069 }
1070 }
1071
1072 public function rest_system_logs_delete( $request ) {
1073 try {
1074 $params = $request->get_json_params();
1075 $logIds = $params['logIds'];
1076 $success = apply_filters( 'mwai_stats_logs_delete', true, $logIds );
1077 return $this->create_rest_response( [ 'success' => $success ], 200 );
1078 }
1079 catch ( Exception $e ) {
1080 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1081 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1082 }
1083 }
1084
1085 public function rest_system_logs_meta_get( $request ) {
1086 try {
1087 $params = $request->get_json_params();
1088 $logId = $params['logId'];
1089 $metaKeys = $params['metaKeys'];
1090 $data = apply_filters( 'mwai_stats_logs_meta', [], $logId, $metaKeys );
1091 return $this->create_rest_response( [ 'success' => true, 'data' => $data ], 200 );
1092 }
1093 catch ( Exception $e ) {
1094 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1095 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1096 }
1097 }
1098
1099 public function rest_system_logs_activity( $request ) {
1100 try {
1101 $params = $request->get_json_params();
1102 $hours = isset( $params['hours'] ) ? intval( $params['hours'] ) : 24;
1103 $data = apply_filters( 'mwai_stats_logs_activity', [], $hours );
1104 return $this->create_rest_response( [ 'success' => true, 'data' => $data ], 200 );
1105 }
1106 catch ( Exception $e ) {
1107 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1108 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1109 }
1110 }
1111
1112 public function rest_system_logs_activity_daily( $request ) {
1113 try {
1114 $params = $request->get_json_params();
1115 $days = isset( $params['days'] ) ? intval( $params['days'] ) : 31;
1116 $byModel = isset( $params['byModel'] ) ? (bool) $params['byModel'] : false;
1117
1118 if ( $byModel ) {
1119 $data = apply_filters( 'mwai_stats_logs_activity_daily_by_model', [], $days );
1120 } else {
1121 $data = apply_filters( 'mwai_stats_logs_activity_daily', [], $days );
1122 }
1123
1124 return $this->create_rest_response( [ 'success' => true, 'data' => $data ], 200 );
1125 }
1126 catch ( Exception $e ) {
1127 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1128 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1129 }
1130 }
1131
1132 public function rest_ai_moderate( $request ) {
1133 try {
1134 $params = $request->get_json_params();
1135 $envId = $params['envId'];
1136 $text = $params['text'];
1137 if ( !$text ) {
1138 return $this->create_rest_response( [ 'success' => false, 'message' => 'Text not found.' ], 404 );
1139 }
1140 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $envId );
1141 $results = $openai->moderate( $text );
1142 return $this->create_rest_response( [ 'success' => true, 'results' => $results ], 200 );
1143 }
1144 catch ( Exception $e ) {
1145 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1146 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1147 }
1148 }
1149
1150 public function rest_ai_transcribe_audio( $request ) {
1151 try {
1152 global $mwai;
1153 $params = $request->get_json_params();
1154 $url = !empty( $params['url'] ) ? $params['url'] : null;
1155 $mediaId = isset( $params['mediaId'] ) ? intval( $params['mediaId'] ) : 0;
1156
1157 // If mediaId is provided, get the file path
1158 $path = null;
1159 if ( $mediaId > 0 ) {
1160 $path = get_attached_file( $mediaId );
1161 if ( empty( $path ) ) {
1162 throw new Exception( 'The media file cannot be found.' );
1163 }
1164 }
1165
1166 // Set the scope for admin tools
1167 if ( !isset( $params['scope'] ) ) {
1168 $params['scope'] = 'admin-tools';
1169 }
1170
1171 $result = $mwai->simpleTranscribeAudio( $url, $path, $params );
1172 return $this->create_rest_response( [ 'success' => true, 'data' => $result ], 200 );
1173 }
1174 catch ( Exception $e ) {
1175 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1176 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1177 }
1178 }
1179
1180 public function rest_ai_transcribe_image( $request ) {
1181 try {
1182 global $mwai;
1183 $params = $request->get_json_params();
1184 $message = $this->retrieve_message( $params );
1185 $url = !empty( $params['url'] ) ? $params['url'] : null;
1186 // This could lead to a security issue, so let's avoid using path directly.
1187 //$path = !empty( $params['path'] ) ? $params['path'] : null;
1188 $result = $mwai->simpleVisionQuery( $message, $url );
1189 return $this->create_rest_response( [ 'success' => true, 'data' => $result ], 200 );
1190 }
1191 catch ( Exception $e ) {
1192 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1193 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1194 }
1195 }
1196
1197 public function rest_ai_json( $request ) {
1198 try {
1199 global $mwai;
1200 $params = $request->get_json_params();
1201 $message = $this->retrieve_message( $params );
1202 $result = $mwai->simpleJsonQuery( $message );
1203 return $this->create_rest_response( [ 'success' => true, 'data' => $result ], 200 );
1204 }
1205 catch ( Exception $e ) {
1206 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1207 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1208 }
1209 }
1210
1211 public function rest_mcp_functions( $request ) {
1212 try {
1213 // Get all registered MCP tools
1214 $tools = apply_filters( 'mwai_mcp_tools', [] );
1215
1216 // Format the response
1217 $response = [
1218 'success' => true,
1219 'count' => count( $tools ),
1220 'functions' => $tools
1221 ];
1222
1223 return $this->create_rest_response( $response, 200 );
1224 }
1225 catch ( Exception $e ) {
1226 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1227 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1228 }
1229 }
1230
1231 public function rest_helpers_post_types() {
1232 try {
1233 $postTypes = $this->core->get_post_types();
1234 return $this->create_rest_response( [ 'success' => true, 'postTypes' => $postTypes ], 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_settings_themes( $request ) {
1243 try {
1244 $method = $request->get_method();
1245 if ( $method === 'GET' ) {
1246 $themes = $this->core->get_themes();
1247 return $this->create_rest_response( [ 'success' => true, 'themes' => $themes ], 200 );
1248 }
1249 else if ( $method === 'POST' ) {
1250 $params = $request->get_json_params();
1251 $themes = $params['themes'];
1252 $themes = $this->core->update_themes( $themes );
1253 return $this->create_rest_response( [ 'success' => true, 'themes' => $themes ], 200 );
1254 }
1255 }
1256 catch ( Exception $e ) {
1257 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1258 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1259 }
1260 }
1261
1262 public function rest_settings_chatbots( $request ) {
1263 try {
1264 $method = $request->get_method();
1265 if ( $method === 'GET' ) {
1266 $chatbots = $this->core->get_chatbots();
1267 return $this->create_rest_response( [ 'success' => true, 'chatbots' => $chatbots ], 200 );
1268 }
1269 else if ( $method === 'POST' ) {
1270 $params = $request->get_json_params();
1271 $chatbots = $params['chatbots'];
1272 $chatbots = $this->core->update_chatbots( $chatbots );
1273 return $this->create_rest_response( [ 'success' => true, 'chatbots' => $chatbots ], 200 );
1274 }
1275 return $this->create_rest_response( [ 'success' => false, 'message' => 'Method not allowed' ], 405 );
1276 }
1277 catch ( Exception $e ) {
1278 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
1279 return $this->create_rest_response( [ 'success' => false, 'message' => $message ], 500 );
1280 }
1281 }
1282
1283 #region Logs
1284
1285 public function rest_get_logs() {
1286 $logs = Meow_MWAI_Logging::get();
1287 return $this->create_rest_response( [ 'success' => true, 'data' => $logs ], 200 );
1288 }
1289
1290 public function rest_clear_logs() {
1291 Meow_MWAI_Logging::clear();
1292 return $this->create_rest_response( [ 'success' => true ], 200 );
1293 }
1294
1295 #endregion
1296 }
1297