HEX
Server: LiteSpeed
System: Linux s166.bitcommand.com 4.18.0-513.11.1.lve.el8.x86_64 #1 SMP Thu Jan 18 16:21:02 UTC 2024 x86_64
User: h340499 (1922)
PHP: 8.2.16
Disabled: exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Upload Files
File: /home/h340499/public_html/wp-content/plugins/learnpress/inc/Services/OpenAiService.php
<?php

namespace LearnPress\Services;

use Exception;
use LearnPress\Helpers\Singleton;
use LP_Helper;
use LP_Settings;

/**
 * Class OpenAiService
 *
 * Handles interactions with the OpenAI API for LearnPress.
 *
 * @package LearnPress\Services
 * @since 4.3.0
 * @version 1.0.0
 */
class OpenAiService {
	use Singleton;

	public string $baseUrl = 'https://api.openai.com/v1/';
	public string $urlChartCompletion;
	public string $urlResponses;
	public string $urlCompletionLegacy;
	public string $urlImage;

	public string $secret_key;
	public string $text_model_type;
	public string $image_model_type;
	public float $frequency_penalty;
	public float $presence_penalty;
	public float $creativity_level;
	public int $max_token;

	public function init() {
		$this->urlChartCompletion  = $this->baseUrl . 'chat/completions';
		$this->urlCompletionLegacy = $this->baseUrl . 'completions';
		$this->urlResponses        = $this->baseUrl . 'responses';
		$this->urlImage            = $this->baseUrl . 'images/generations';
		$this->get_settings();
	}

	/**
	 * Check OpenAI integration is enabled
	 */
	public function is_enable(): bool {
		return LP_Settings::get_option( 'enable_open_ai', 'no' ) === 'yes';
	}

	public function get_settings() {
		$this->secret_key        = LP_Settings::get_option( 'open_ai_secret_key', '' );
		$this->text_model_type   = LP_Settings::get_option( 'open_ai_text_model_type', 'chatgpt-4o-latest' );
		$this->image_model_type  = LP_Settings::get_option( 'open_ai_image_model_type', 'dall-e-3' );
		$this->frequency_penalty = LP_Settings::get_option( 'open_ai_frequency_penalty_level', 0.0 );
		$this->presence_penalty  = LP_Settings::get_option( 'open_ai_presence_penalty_level', 0.0 );
		$this->creativity_level  = LP_Settings::get_option( 'open_ai_creativity_level', 1.0 );
		$this->max_token         = LP_Settings::get_option( 'open_ai_max_token', 0 );
	}

	/**
	 * Call OpenAI API
	 *
	 * @throws Exception
	 */
	public function send_request( array $args ): array {
		// Handle args before send request
		$url = '';

		if ( in_array(
			$this->text_model_type,
			[ 'chatgpt-4o-latest', 'gpt-4o', 'gpt-4o-mini', 'gpt-3.5-turbo' ]
		) ) {
			$url  = $this->urlChartCompletion;
			$args = $this->handle_params_for_send_chat_completion( $args );
		} elseif ( $this->text_model_type == 'gpt-3.5-turbo-instruct' ) {
			$url  = $this->urlCompletionLegacy;
			$args = $this->handle_params_for_send_completion_legacy( $args );
		} else {
			$url  = $this->urlResponses;
			$args = $this->handle_params_for_send_responses( $args );
		}

		$response = wp_remote_post(
			$url,
			[
				'headers' => [
					'Authorization' => 'Bearer ' . $this->secret_key,
					'Content-Type'  => 'application/json',
				],
				'body'    => json_encode( $args ),
				'timeout' => 3600,
			]
		);

		if ( is_wp_error( $response ) ) {
			throw new Exception( $response->get_error_message(), 400 );
		}

		$body = wp_remote_retrieve_body( $response );
		$data = LP_Helper::json_decode( $body, true );
		if ( isset( $data['error'] ) ) {
			throw new Exception( $data['error']['message'] );
		}

		return $this->detected_data( $data );
	}

	/**
	 * @throws Exception
	 */
	public function send_request_create_image( $args ) {
		$args_default = [
			'model'  => $this->image_model_type,
			'prompt' => '',
		];

		$args = array_merge( $args_default, $args );

		$model_type = LP_Settings::get_option( 'open_ai_image_model_type', 'gpt-image-1' );
		if ( $model_type === 'gpt-image-1' ) {
			unset( $args['n'] );
		}

		if ( $model_type == 'dall-e-3' ) {
			// only n=1 is supported.
			$args['n'] = 1;
		}

		$response = wp_remote_post(
			$this->urlImage,
			[
				'headers' => [
					'Authorization' => 'Bearer ' . $this->secret_key,
					'Content-Type'  => 'application/json',
				],
				'body'    => json_encode( $args ),
				'timeout' => 3600,
			]
		);

		if ( is_wp_error( $response ) ) {
			throw new Exception( $response->get_error_message(), 400 );
		}

		$body = wp_remote_retrieve_body( $response );
		$data = LP_Helper::json_decode( $body, true );
		if ( isset( $data['error'] ) ) {
			throw new Exception( $data['error']['message'] );
		}

		return $data;
	}

	/**
	 * Handle params for send chat completion
	 *
	 * @docs https://platform.openai.com/docs/api-reference/chat/create
	 *
	 * @throws Exception
	 */
	public function handle_params_for_send_chat_completion( $params ): array {
		$params = [
			'model'                 => $this->text_model_type,
			'frequency_penalty'     => $this->frequency_penalty,
			'presence_penalty'      => $this->presence_penalty,
			'temperature'           => $this->creativity_level,
			'max_completion_tokens' => $this->max_token,
			'response_format'       => [ 'type' => 'json_object' ],
			'n'                     => $args['outputs'] ?? 1,
			'messages'              => [
				[
					'role'    => 'system',
					'content' => 'You are an AI assistant specialized in education and course design.',
				],
				[
					'role'    => 'user',
					'content' => $params['prompt'] ?? '',
				],
			],
		];

		if ( $this->max_token === 0 ) {
			unset( $params['max_completion_tokens'] );
		}

		return $params;
	}

	/**
	 * Handle params for send chat completion
	 *
	 * @docs https://platform.openai.com/docs/api-reference/completions/create
	 *
	 * @throws Exception
	 */
	public function handle_params_for_send_completion_legacy( $params ): array {
		$params = [
			'model'       => $this->text_model_type,
			'temperature' => $this->creativity_level,
			'max_tokens'  => $this->max_token,
			'n'           => $args['outputs'] ?? 1,
			'prompt'      => $params['prompt'] ?? '',
		];

		if ( $this->max_token === 0 ) {
			unset( $params['max_tokens'] );
		}

		return $params;
	}

	/**
	 * Handle params for send chat completion
	 *
	 * @docs https://platform.openai.com/docs/api-reference/responses/create
	 *
	 * @throws Exception
	 */
	public function handle_params_for_send_responses( $params ): array {
		$params = [
			'model'             => $this->text_model_type,
			//'temperature'       => $this->creativity_level,
			'max_output_tokens' => $this->max_token,
			'input'             => $params['prompt'] ?? '',
		];

		if ( $this->max_token === 0 ) {
			unset( $params['max_output_tokens'] );
		}

		return $params;
	}

	/**
	 * Detect data from response
	 *
	 * @throws Exception
	 */
	public function detected_data( array $data ): array {
		$data['lp_structure_data'] = [];

		if ( isset( $data['choices'] ) ) {
			foreach ( $data['choices'] as $choice ) {
				$text = $choice['message']['content'] ?? $choice['text'] ?? '';
				$text = str_replace( [ "\\n", "\\r", "\n", "\r", "\t" ], '', $text );

				$data['lp_structure_data'][] = LP_Helper::json_decode( $text, true );
			}
		} elseif ( isset( $data['output'] ) ) {
			foreach ( $data['output'] as $output ) {
				$content_data = $output['content'] ?? [];
				if ( empty( $content_data ) ) {
					continue;
				}

				foreach ( $output['content'] as $content ) {
					try {
						$text                        = $content['text'] ?? '';
						$text                        = str_replace( [ "\\n", "\\r", "\n", "\r", "\t" ], '', $text );
						$data['lp_structure_data'][] = LP_Helper::json_decode( $text, true );
					} catch ( Exception $e ) {
						$data['lp_structure_data'][] = $content['text'] ?? '';
					}
				}
			}
		}

		return $data;
	}
}