'', 'usage' => array(), 'request_id' => '', 'cost' => 0.0, ); $body = array( 'model' => $model, 'messages' => array( array( 'role' => 'user', 'content' => $prompt, ), ), 'max_tokens' => 300, 'temperature' => 0.3, ); $retries = 0; $backoff = 2; // Initial backoff in seconds. while ( $retries <= self::MAX_RETRIES ) { $response = wp_remote_post( self::get_api_url(), array( 'timeout' => 15, 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Content-Type' => 'application/json', 'HTTP-Referer' => home_url(), 'X-Title' => 'Peptide News Aggregator', ), 'body' => wp_json_encode( $body ), ) ); if ( is_wp_error( $response ) ) { $result['content'] = $response; return $result; } $status = wp_remote_retrieve_response_code( $response ); // Handle rate limiting with exponential backoff. if ( 429 === $status && $retries < self::MAX_RETRIES ) { $retry_after = wp_remote_retrieve_header( $response, 'retry-after' ); $wait = $retry_after ? min( (int) $retry_after, 30 ) : $backoff; sleep( $wait ); $retries++; $backoff *= 2; continue; } break; } $data = json_decode( wp_remote_retrieve_body( $response ), true ); // Extract usage data from response regardless of success/failure. if ( is_array( $data ) && isset( $data['usage'] ) && is_array( $data['usage'] ) ) { $result['usage'] = array( 'prompt_tokens' => (int) ( $data['usage']['prompt_tokens'] ?? 0 ), 'completion_tokens' => (int) ( $data['usage']['completion_tokens'] ?? 0 ), 'total_tokens' => (int) ( $data['usage']['total_tokens'] ?? 0 ), ); } // Extract the request ID for debugging/reconciliation. if ( is_array( $data ) && isset( $data['id'] ) ) { $result['request_id'] = (string) $data['id']; } // OpenRouter may include generation cost in data.usage.total_cost. if ( is_array( $data ) && isset( $data['usage']['total_cost'] ) ) { $result['cost'] = (float) $data['usage']['total_cost']; } if ( $status < 200 || $status >= 300 ) { $error_msg = 'HTTP ' . $status; if ( is_array( $data ) && isset( $data['error'] ) && is_array( $data['error'] ) && isset( $data['error']['message'] ) ) { $error_msg = $data['error']['message']; } $result['content'] = new \WP_Error( 'openrouter_error', $error_msg ); return $result; } // Validate the response structure thoroughly. if ( ! is_array( $data ) ) { $result['content'] = new \WP_Error( 'openrouter_invalid', 'Invalid JSON response from OpenRouter' ); return $result; } if ( ! isset( $data['choices'] ) || ! is_array( $data['choices'] ) || empty( $data['choices'] ) ) { $result['content'] = new \WP_Error( 'openrouter_empty', 'No choices in OpenRouter response' ); return $result; } if ( ! isset( $data['choices'][0]['message'] ) || ! is_array( $data['choices'][0]['message'] ) ) { $result['content'] = new \WP_Error( 'openrouter_empty', 'Malformed choice in OpenRouter response' ); return $result; } if ( ! isset( $data['choices'][0]['message']['content'] ) || '' === trim( $data['choices'][0]['message']['content'] ) ) { $result['content'] = new \WP_Error( 'openrouter_empty', 'Empty content in OpenRouter response' ); return $result; } $result['content'] = trim( $data['choices'][0]['message']['content'] ); return $result; } }