| 1 |
<?php |
| 2 |
defined( 'ABSPATH' ) || exit; |
| 3 |
|
| 4 |
add_action( 'init', 'ultp_chatgpt_init' ); |
| 5 |
function ultp_chatgpt_init() { |
| 6 |
if ( ultimate_post()->get_setting( 'ultp_chatgpt' ) == 'true' ) { |
| 7 |
add_filter( 'ultp_settings', 'ultp_get_chatgpt_settings', 10, 1 ); |
| 8 |
add_action( 'rest_api_init', 'ultp_chatgpt_register_route' ); |
| 9 |
} |
| 10 |
} |
| 11 |
|
| 12 |
/** |
| 13 |
* Register the route the editor uses to ask OpenAI. |
| 14 |
* |
| 15 |
* The OpenAI key stays on the server: the editor sends only the prompt, and |
| 16 |
* this route adds the key and the saved model settings. |
| 17 |
*/ |
| 18 |
function ultp_chatgpt_register_route() { |
| 19 |
register_rest_route( |
| 20 |
'ultp/v2', |
| 21 |
'/chatgpt/', |
| 22 |
array( |
| 23 |
'methods' => 'POST', |
| 24 |
'callback' => 'ultp_chatgpt_request', |
| 25 |
'permission_callback' => function () { |
| 26 |
return current_user_can( 'edit_posts' ); |
| 27 |
}, |
| 28 |
'args' => array( |
| 29 |
'prompt' => array( |
| 30 |
'type' => 'string', |
| 31 |
'required' => true, |
| 32 |
), |
| 33 |
), |
| 34 |
) |
| 35 |
); |
| 36 |
} |
| 37 |
|
| 38 |
/** |
| 39 |
* Send a prompt to OpenAI with the saved key and settings. |
| 40 |
* |
| 41 |
* Returns OpenAI's `choices` on success, or `error.code` (plus the message for |
| 42 |
* a missing model) on failure, which is the shape the editor already reads. |
| 43 |
* OpenAI's other error messages are not passed on, as some quote part of the key. |
| 44 |
* |
| 45 |
* @param WP_REST_Request $request Request. |
| 46 |
* @return WP_REST_Response|WP_Error |
| 47 |
*/ |
| 48 |
function ultp_chatgpt_request( $request ) { |
| 49 |
$api_key = ultimate_post()->get_setting( 'chatgpt_secret_key' ); |
| 50 |
if ( empty( $api_key ) ) { |
| 51 |
return rest_ensure_response( array( 'error' => array( 'code' => 'invalid_api_key' ) ) ); |
| 52 |
} |
| 53 |
|
| 54 |
$prompt = trim( wp_check_invalid_utf8( (string) $request->get_param( 'prompt' ) ) ); |
| 55 |
if ( '' === $prompt ) { |
| 56 |
return new WP_Error( 'ultp_chatgpt_empty_prompt', __( 'The prompt is empty.', 'ultimate-post' ), array( 'status' => 400 ) ); |
| 57 |
} |
| 58 |
$prompt = mb_substr( $prompt, 0, 20000 ); |
| 59 |
|
| 60 |
$model = ultimate_post()->get_setting( 'chatgpt_model' ); |
| 61 |
if ( ! in_array( $model, array( 'gpt-3.5-turbo', 'gpt-4', 'text-davinci-002', 'text-davinci-003' ), true ) ) { |
| 62 |
$model = 'gpt-3.5-turbo'; |
| 63 |
} |
| 64 |
$timeout = absint( ultimate_post()->get_setting( 'chatgpt_response_time' ) ); |
| 65 |
$timeout = $timeout ? min( max( $timeout, 5 ), 120 ) : 35; |
| 66 |
|
| 67 |
if ( 0 === strpos( $model, 'text-davinci' ) ) { |
| 68 |
$max_tokens = absint( ultimate_post()->get_setting( 'chatgpt_max_tokens' ) ); |
| 69 |
$url = 'https://api.openai.com/v1/completions'; |
| 70 |
$body = array( |
| 71 |
'model' => $model, |
| 72 |
'prompt' => $prompt, |
| 73 |
'max_tokens' => $max_tokens ? min( $max_tokens, 4096 ) : 200, |
| 74 |
); |
| 75 |
} else { |
| 76 |
$url = 'https://api.openai.com/v1/chat/completions'; |
| 77 |
$body = array( |
| 78 |
'model' => $model, |
| 79 |
'messages' => array( |
| 80 |
array( |
| 81 |
'role' => 'user', |
| 82 |
'content' => $prompt, |
| 83 |
), |
| 84 |
), |
| 85 |
); |
| 86 |
} |
| 87 |
|
| 88 |
$response = wp_safe_remote_post( |
| 89 |
$url, |
| 90 |
array( |
| 91 |
'timeout' => $timeout, |
| 92 |
'headers' => array( |
| 93 |
'Content-Type' => 'application/json', |
| 94 |
'Authorization' => 'Bearer ' . $api_key, |
| 95 |
), |
| 96 |
'body' => wp_json_encode( $body ), |
| 97 |
) |
| 98 |
); |
| 99 |
if ( is_wp_error( $response ) ) { |
| 100 |
return rest_ensure_response( array( 'error' => array( 'code' => 'request_failed' ) ) ); |
| 101 |
} |
| 102 |
|
| 103 |
$data = json_decode( wp_remote_retrieve_body( $response ), true ); |
| 104 |
if ( ! empty( $data['choices'] ) && is_array( $data['choices'] ) ) { |
| 105 |
return rest_ensure_response( array( 'choices' => $data['choices'] ) ); |
| 106 |
} |
| 107 |
|
| 108 |
$code = isset( $data['error']['code'] ) ? sanitize_key( $data['error']['code'] ) : 'request_failed'; |
| 109 |
$error = array( 'code' => $code ); |
| 110 |
if ( 'model_not_found' === $code && isset( $data['error']['message'] ) ) { |
| 111 |
$error['message'] = sanitize_text_field( $data['error']['message'] ); |
| 112 |
} |
| 113 |
return rest_ensure_response( array( 'error' => $error ) ); |
| 114 |
} |
| 115 |
|
| 116 |
function ultp_get_chatgpt_settings( $config ) { |
| 117 |
$arr = array( |
| 118 |
'ultp_chatgpt' => array( |
| 119 |
'label' => __( 'ChatGPT', 'ultimate-post' ), |
| 120 |
'attr' => array( |
| 121 |
'chatgpt_heading' => array( |
| 122 |
'type' => 'heading', |
| 123 |
'label' => __( 'ChatGPT Settings', 'ultimate-post' ), |
| 124 |
), |
| 125 |
'chatgpt_secret_key' => array( |
| 126 |
'type' => 'text', |
| 127 |
'label' => __( 'OpenAI API Secret Key', 'ultimate-post' ), |
| 128 |
'desc' => __( 'For using ChatGPT addon, you have to add your OpenAI secret key.', 'ultimate-post' ), |
| 129 |
'link' => 'https://platform.openai.com/account/api-keys', |
| 130 |
'linkText' => __( 'Get key.', 'ultimate-post' ), |
| 131 |
), |
| 132 |
'chatgpt_model' => array( |
| 133 |
'type' => 'select', |
| 134 |
'label' => __( 'OpenAI Model', 'ultimate-post' ), |
| 135 |
'desc' => __( 'Choose your preferable OpenAI model', 'ultimate-post' ), |
| 136 |
'options' => array( |
| 137 |
'gpt-3.5-turbo' => 'gpt-3.5-turbo', |
| 138 |
'text-davinci-002' => 'text-davinci-002', |
| 139 |
'text-davinci-003' => 'text-davinci-003', |
| 140 |
'gpt-4' => 'gpt-4', |
| 141 |
), |
| 142 |
'default' => 'gpt-3.5-turbo', |
| 143 |
), |
| 144 |
'chatgpt_response_time' => array( |
| 145 |
'type' => 'number', |
| 146 |
'label' => __( 'Response Time', 'ultimate-post' ), |
| 147 |
'desc' => __( 'Choose your preferable OpenAI response time', 'ultimate-post' ), |
| 148 |
'default' => 60, |
| 149 |
), |
| 150 |
'chatgpt_max_tokens' => array( |
| 151 |
'type' => 'number', |
| 152 |
'label' => __( 'Max Tokens', 'ultimate-post' ), |
| 153 |
'desc' => __( 'Choose your preferable OpenAI max Number of Tokens to be generated by ChatGPT', 'ultimate-post' ), |
| 154 |
'default' => 400, |
| 155 |
), |
| 156 |
), |
| 157 |
), |
| 158 |
); |
| 159 |
return array_merge( $config, $arr ); |
| 160 |
} |
| 161 |
|