ai-auth.php
5 months ago
ai-form-builder.php
1 month ago
ai-helper.php
3 months ago
field-mapping.php
2 months ago
ai-form-builder.php
136 lines
| 1 | <?php |
| 2 | /** |
| 3 | * SureForms - AI Form Builder. |
| 4 | * |
| 5 | * @package sureforms |
| 6 | * @since 0.0.8 |
| 7 | */ |
| 8 | |
| 9 | namespace SRFM\Inc\AI_Form_Builder; |
| 10 | |
| 11 | use SRFM\Inc\Traits\Get_Instance; |
| 12 | |
| 13 | // Exit if accessed directly. |
| 14 | if ( ! defined( 'ABSPATH' ) ) { |
| 15 | exit; |
| 16 | } |
| 17 | |
| 18 | /** |
| 19 | * SureForms AI Form Builder Class. |
| 20 | */ |
| 21 | class AI_Form_Builder { |
| 22 | use Get_Instance; |
| 23 | |
| 24 | /** |
| 25 | * Fetches ai data from the middleware server |
| 26 | * |
| 27 | * @param \WP_REST_Request $request request object. |
| 28 | * @since 0.0.8 |
| 29 | * @return void |
| 30 | */ |
| 31 | public function generate_ai_form( $request ) { |
| 32 | |
| 33 | // Get the params. |
| 34 | $params = $request->get_params(); |
| 35 | |
| 36 | // If the message array doesn't exist, abandon ship. |
| 37 | if ( empty( $params['message_array'] ) || ! is_array( $params['message_array'] ) ) { |
| 38 | wp_send_json_error( [ 'message' => __( 'The message array was not supplied', 'sureforms' ) ] ); |
| 39 | } |
| 40 | |
| 41 | // Set the token count to 0, and create messages array. |
| 42 | $messages = []; |
| 43 | |
| 44 | // Start with the last message - going upwards until the token count hits 2000. |
| 45 | foreach ( array_reverse( $params['message_array'] ) as $current_message ) { |
| 46 | // If the message content doesn't exist, skip it. |
| 47 | if ( empty( $current_message['content'] ) ) { |
| 48 | continue; |
| 49 | } |
| 50 | |
| 51 | // Add the message to the start of the messages to send to the SCS Middleware. |
| 52 | array_unshift( $messages, $current_message ); |
| 53 | } |
| 54 | |
| 55 | // Bail if no usable prompt remained after filtering empty messages. |
| 56 | if ( empty( $messages ) || empty( $messages[0]['content'] ) ) { |
| 57 | wp_send_json_error( [ 'message' => __( 'No prompt was supplied.', 'sureforms' ) ] ); |
| 58 | } |
| 59 | |
| 60 | // Server-side prompt-length cap. The UI enforces a 2000-char limit via maxlength, but that |
| 61 | // is client-side only and can be bypassed by a crafted request, so mirror it here. This is |
| 62 | // cost/resource hardening — output is always escaped, so this is not an XSS concern. |
| 63 | if ( mb_strlen( (string) $messages[0]['content'] ) > 2000 ) { |
| 64 | wp_send_json_error( [ 'message' => __( 'The prompt is too long. Please shorten it and try again.', 'sureforms' ) ] ); |
| 65 | } |
| 66 | |
| 67 | // Get the response from the endpoint. |
| 68 | $response = AI_Helper::get_chat_completions_response( |
| 69 | apply_filters( |
| 70 | 'srfm_ai_form_generator_body', |
| 71 | [ |
| 72 | 'query' => $messages[0]['content'], |
| 73 | ], |
| 74 | $params |
| 75 | ) |
| 76 | ); |
| 77 | |
| 78 | // check if response is an array if not then send error. |
| 79 | if ( ! is_array( $response ) ) { |
| 80 | wp_send_json_error( [ 'message' => __( 'The SureForms AI Middleware encountered an error.', 'sureforms' ) ] ); |
| 81 | } |
| 82 | |
| 83 | if ( ! empty( $response['error'] ) ) { |
| 84 | // If the response has an error, handle it and report it back. |
| 85 | // We sanitize before returning so OpenAI / middleware infra details |
| 86 | // (URLs, request IDs, model names, account IDs) do not leak to the |
| 87 | // client; the raw message is preserved in the debug log via |
| 88 | // AI_Helper::sanitize_ai_error_message() when WP_DEBUG[_LOG] is on. |
| 89 | $raw = ''; |
| 90 | if ( is_array( $response['error'] ) && ! empty( $response['error']['message'] ) ) { |
| 91 | // If any error message received from OpenAI. |
| 92 | $raw = $response['error']['message']; |
| 93 | } elseif ( is_string( $response['error'] ) ) { |
| 94 | // If any error message received from the middleware server. |
| 95 | $raw = $response['error']; |
| 96 | } |
| 97 | $message = AI_Helper::sanitize_ai_error_message( $raw, 'generate/form' ); |
| 98 | if ( '' === $message ) { |
| 99 | $message = __( 'The SureForms AI Middleware encountered an error.', 'sureforms' ); |
| 100 | } |
| 101 | wp_send_json_error( [ 'message' => $message ] ); |
| 102 | } |
| 103 | |
| 104 | // Validate the expected form structure piece by piece so we can return specific errors. |
| 105 | if ( empty( $response['form'] ) || ! is_array( $response['form'] ) ) { |
| 106 | wp_send_json_error( |
| 107 | [ |
| 108 | 'message' => __( 'The AI did not return a form. Please refine your prompt and try again.', 'sureforms' ), |
| 109 | ] |
| 110 | ); |
| 111 | } |
| 112 | |
| 113 | if ( empty( $response['form']['formTitle'] ) ) { |
| 114 | wp_send_json_error( |
| 115 | [ |
| 116 | 'message' => __( 'The AI response is missing a form title. Please try again.', 'sureforms' ), |
| 117 | ] |
| 118 | ); |
| 119 | } |
| 120 | |
| 121 | if ( |
| 122 | empty( $response['form']['formFields'] ) || |
| 123 | ! is_array( $response['form']['formFields'] ) |
| 124 | ) { |
| 125 | wp_send_json_error( |
| 126 | [ |
| 127 | 'message' => __( 'The AI was unable to generate form fields. Please try again.', 'sureforms' ), |
| 128 | ] |
| 129 | ); |
| 130 | } |
| 131 | |
| 132 | wp_send_json_success( $response ); |
| 133 | } |
| 134 | |
| 135 | } |
| 136 |