'gpt-5', 'gpt-5-mini' => 'gpt-5-mini', 'gpt-4o' => 'gpt-4o', ); /** * Constructor. * * @param string $api_key OpenAI API key. */ public function __construct( string $api_key ) { $this->api_key = trim( $api_key ); } /** * Execute ChatGPT API request. * * @param string $text Text to process. * @param string $prompt System prompt. * @param array $options Request options. * @return array Raw API response. */ public function execute_request( string $text, string $prompt, array $options = array() ): array { $request_data = $this->prepare_request_data( $text, $prompt, $options ); return $this->send_http_request( $request_data ); } /** * Prepare request data for ChatGPT API. * * @param string $text Input text. * @param string $prompt System prompt. * @param array $options Request options. * @return array Formatted request data. */ private function prepare_request_data( string $text, string $prompt, array $options = array() ): array { $selected_model = $options['model'] ?? get_option( 'accessibility_audio_tts_chatgpt_model', 'gpt-5' ); $api_model = self::MODEL_MAPPING[ $selected_model ] ?? $selected_model; $is_gpt5_model = 0 === strpos( $selected_model, 'gpt-5' ) || 0 === strpos( $api_model, 'gpt-5' ); if ( function_exists( 'accessibility_audio_tts_debug_log' ) ) { accessibility_audio_tts_debug_log( '[ChatGPT_Request_Handler] Using model: ' . $api_model . ' (selected: ' . $selected_model . ')', true ); } // Use Responses API for GPT-5 family. if ( $is_gpt5_model ) { // GPT-5 reasoning models need more tokens: minimum 2000, or 4x text length. $calculated_tokens = max( 2000, min( 8000, strlen( $text ) * 4 ) ); return array( '_endpoint' => 'responses', 'model' => $api_model, 'input' => array( array( 'role' => 'system', 'content' => $prompt, ), array( 'role' => 'user', 'content' => $text, ), ), 'max_output_tokens' => $options['max_output_tokens'] ?? $calculated_tokens, ); } // Default: Chat Completions API for older models. $request = array( '_endpoint' => 'chat', 'model' => $api_model, 'messages' => array( array( 'role' => 'system', 'content' => $prompt, ), array( 'role' => 'user', 'content' => $text, ), ), 'max_tokens' => $options['max_tokens'] ?? min( 4000, strlen( $text ) * 2 ), ); $request['temperature'] = $options['temperature'] ?? 0.05; $request['top_p'] = $options['top_p'] ?? 0.8; return $request; } /** * Send HTTP request to OpenAI API using WordPress HTTP API. * * @param array $request_data Formatted request data. * @return array Raw HTTP response data. */ private function send_http_request( array $request_data ): array { if ( function_exists( 'accessibility_audio_tts_debug_log' ) ) { accessibility_audio_tts_debug_log( '[ChatGPT_Request_Handler] Executing API request', true ); } $endpoint = $request_data['_endpoint'] ?? 'chat'; unset( $request_data['_endpoint'] ); // Do not retain application state for text correction or explicit key tests. // Provider abuse-monitoring retention is controlled separately by the account. $request_data['store'] = false; $url = 'responses' === $endpoint ? 'https://api.openai.com/v1/responses' : 'https://api.openai.com/v1/chat/completions'; $args = array( 'headers' => array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $this->api_key, 'User-Agent' => 'WordPress/Accessibility-Audio-TTS', ), 'timeout' => 60, 'body' => wp_json_encode( $request_data ), 'method' => 'POST', ); $start_time = microtime( true ); $response = wp_remote_request( $url, $args ); $elapsed = microtime( true ) - $start_time; if ( is_wp_error( $response ) ) { return array( 'response' => '', 'http_code' => 0, 'curl_error' => $response->get_error_message(), 'request_time' => $elapsed, ); } $http_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); if ( function_exists( 'accessibility_audio_tts_debug_log' ) ) { accessibility_audio_tts_debug_log( '[ChatGPT_Request_Handler] Request completed in ' . round( $elapsed, 2 ) . 's with HTTP code: ' . $http_code, true ); } return array( 'response' => $body, 'http_code' => $http_code, 'curl_error' => '', 'request_time' => $elapsed, ); } /** * Test API connection. * * @return array Connection test results. */ public function test_connection(): array { $test_data = array( 'model' => 'gpt-4o-mini', 'messages' => array( array( 'role' => 'user', 'content' => 'Hello, this is a connection test. Please respond with "Connection successful".', ), ), 'max_tokens' => 50, 'temperature' => 0.1, ); $response = $this->send_http_request( $test_data ); if ( $response['curl_error'] ) { return array( 'success' => false, 'error' => 'cURL Error: ' . $response['curl_error'], ); } if ( 200 !== $response['http_code'] ) { $error_message = 'HTTP Error: ' . $response['http_code']; if ( 401 === $response['http_code'] ) { $error_message = 'Invalid API key'; } elseif ( 429 === $response['http_code'] ) { $error_message = 'API quota exceeded'; } return array( 'success' => false, 'error' => $error_message, ); } return array( 'success' => true, 'message' => 'Connection successful', 'response_time' => round( $response['request_time'], 2 ) . 's', ); } }