RcmEmailAutomation / sa-orchestration /includes /class-sa-sara-rest.php
NujacsaintS's picture
reseed
f51c224
Raw
History Blame Contribute Delete
3.43 kB
<?php
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 );
}
}