advisor = $advisor; } /** * Registers routes. * * @return void */ public function register_routes() { register_rest_route( ReportController::NAMESPACE_V1, '/ai/models', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'models' ), 'permission_callback' => static function () { return current_user_can( Capabilities::MANAGE ); }, 'args' => array( 'provider' => array( 'type' => 'string', 'required' => true, 'pattern' => '^[a-z0-9_]{2,20}$', ), 'fresh' => array( 'type' => 'boolean', 'default' => false, ), ), ) ); register_rest_route( ReportController::NAMESPACE_V1, '/ai/ask', array( 'methods' => \WP_REST_Server::CREATABLE, 'callback' => array( $this, 'ask' ), 'permission_callback' => array( $this, 'can_ask' ), 'args' => array( 'scope' => array( 'type' => 'string', 'required' => true, 'validate_callback' => static function ( $v ) { return is_string( $v ) && ( 'all' === $v || preg_match( '/^[a-z0-9]{2,20}$/', $v ) ); }, ), 'question' => array( 'type' => 'string', 'required' => true, 'minLength' => 1, 'maxLength' => Advisor::MAX_QUESTION, ), 'mode' => array( 'type' => 'string', 'enum' => array( 'answer', 'slack' ), 'default' => 'answer', ), 'issue' => array( 'type' => 'string', 'default' => '', 'pattern' => '^([a-f0-9]{10})?$', ), 'history' => array( 'type' => 'array', 'default' => array(), 'maxItems' => Advisor::HISTORY, 'items' => array( 'type' => 'object', 'required' => array( 'role', 'text' ), 'additionalProperties' => false, 'properties' => array( 'role' => array( 'type' => 'string', 'enum' => array( 'user', 'assistant' ), ), 'text' => array( 'type' => 'string', 'maxLength' => Advisor::MAX_ANSWER_TURN, ), ), ), ), 'period' => array( 'type' => 'object', 'default' => array(), 'additionalProperties' => false, 'properties' => array_fill_keys( array( 's', 'e', 'ps', 'pe' ), array( 'type' => 'string', 'pattern' => '^\d{4}-\d{2}-\d{2}$', ) ), ), ), ) ); } /** * GET /ai/models: the models a provider's saved key can use (Settings → AI Advisor). * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response */ public function models( \WP_REST_Request $request ) { return rest_ensure_response( $this->advisor->live_models( (string) $request['provider'], (bool) $request['fresh'] ) ); } /** * Permission: manages TurtleReport and the Advisor is ready. * * @return bool|\WP_Error */ public function can_ask() { if ( ! current_user_can( Capabilities::MANAGE ) ) { return false; } if ( ! $this->advisor->ready() ) { return new \WP_Error( 'turtlereport_ai_off', __( 'The AI Advisor isn\'t switched on. An administrator can set it up in Settings → AI Advisor.', 'turtlereport' ), array( 'status' => 403 ) ); } return true; } /** * Answers a question. * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response|\WP_Error */ public function ask( \WP_REST_Request $request ) { $history = array(); foreach ( array_slice( (array) $request['history'], -Advisor::HISTORY ) as $turn ) { if ( is_array( $turn ) && isset( $turn['text'] ) && is_string( $turn['text'] ) ) { $history[] = array( 'role' => isset( $turn['role'] ) && 'assistant' === $turn['role'] ? 'assistant' : 'user', 'text' => (string) $turn['text'], ); } } $answer = $this->advisor->ask( get_current_user_id(), (string) $request['scope'], (string) $request['question'], $history, array( 'mode' => (string) $request['mode'], 'issue' => (string) $request['issue'], 'period' => (array) $request['period'], ) ); return is_wp_error( $answer ) ? $answer : rest_ensure_response( $answer ); } }