Spaces:
Sleeping
Sleeping
| defined( 'ABSPATH' ) || exit; | |
| /** | |
| * Sara REST endpoint. Provides the LLM round-trip from the JS bubble. | |
| * | |
| * Endpoint: POST /sa-orch/v1/sara/invoke | |
| * Body: { surface: string|null, ref: string|null, prompt: string } | |
| * Returns: { response: string, context: {...} } on success | |
| * { error: string, message: string } on failure (HTTP 502) | |
| * | |
| * Capability: manage_options. Same gate as the rest of the SA-Orch admin. | |
| * | |
| * No DB writes. No mutation. Pure round-trip to the configured LLM | |
| * provider via SA_Orch_Sara_Service. | |
| */ | |
| class SA_Orch_Sara_Rest { | |
| public static function register_routes() { | |
| register_rest_route( 'sa-orch/v1', '/sara/invoke', [ | |
| 'methods' => 'POST', | |
| 'callback' => [ __CLASS__, 'invoke' ], | |
| 'permission_callback' => function () { | |
| return current_user_can( 'manage_options' ); | |
| }, | |
| 'args' => [ | |
| 'surface' => [ 'type' => 'string', 'required' => false ], | |
| 'ref' => [ 'type' => 'string', 'required' => false ], | |
| 'page' => [ 'type' => 'string', 'required' => false ], | |
| 'prompt' => [ 'type' => 'string', 'required' => true ], | |
| 'history' => [ 'type' => 'array', 'required' => false ], | |
| 'asterion_file' => [ 'type' => 'string', 'required' => false ], // Board #28 | |
| ], | |
| ] ); | |
| } | |
| public static function invoke( WP_REST_Request $request ) { | |
| $surface = sanitize_text_field( (string) $request->get_param( 'surface' ) ); | |
| $ref = sanitize_text_field( (string) $request->get_param( 'ref' ) ); | |
| $page = sanitize_text_field( (string) $request->get_param( 'page' ) ); | |
| $prompt = (string) $request->get_param( 'prompt' ); | |
| $asterion_file = sanitize_text_field( (string) $request->get_param( 'asterion_file' ) ); | |
| $history = $request->get_param( 'history' ); | |
| if ( ! is_array( $history ) ) { | |
| $history = []; | |
| } | |
| if ( trim( $prompt ) === '' ) { | |
| return new WP_REST_Response( [ | |
| 'error' => 'empty_prompt', | |
| 'message' => 'prompt is required', | |
| ], 400 ); | |
| } | |
| $context = [ | |
| 'surface' => $surface !== '' ? $surface : null, | |
| 'ref' => $ref !== '' ? $ref : null, | |
| ]; | |
| $result = SA_Orch_Sara_Service::invoke( $context, $prompt, $history, $page, $asterion_file ); | |
| if ( is_wp_error( $result ) ) { | |
| return new WP_REST_Response( [ | |
| 'error' => $result->get_error_code(), | |
| 'message' => $result->get_error_message(), | |
| ], 502 ); | |
| } | |
| // Service now returns a wrapping shape: { response, usage, log_id }. | |
| // Pass the structured response through as-is for backward-compat with | |
| // the existing JS contract; surface usage + log_id alongside for Board #16. | |
| return new WP_REST_Response( [ | |
| 'response' => $result['response'], | |
| 'usage' => $result['usage'] ?? null, | |
| 'log_id' => $result['log_id'] ?? null, | |
| 'context' => $context, | |
| 'page' => $page !== '' ? $page : null, | |
| 'turn' => count( $history ) / 2 + 1, // 1-indexed turn number for UI feedback | |
| ], 200 ); | |
| } | |
| } | |