From de8d16c64db3716abcd1e782868f63aa7c63303f Mon Sep 17 00:00:00 2001 From: aghorianducalis Date: Wed, 8 May 2024 14:12:45 +0300 Subject: [PATCH] Add basic src/ structure --- composer.json | 3 +- examples/core_example1/index.php | 61 +++++++---- src/Client.php | 81 +++++++++++++++ src/Configuration.php | 38 +++++++ src/Contracts/ServiceProviderInterface.php | 43 ++++++++ src/DTO/Response/OpenAIResponse.php | 9 ++ src/DTO/Response/Response.php | 20 ++++ src/Exceptions/AIException.php | 33 ++++++ src/Exceptions/OpenAIException.php | 12 +++ src/Providers/AbstractServiceProvider.php | 33 ++++++ src/Providers/OpenAIServiceProvider.php | 59 +++++++++++ src/Utils/OpenAI/CustomCurlClient.php | 112 +++++++++++++++++++++ 12 files changed, 482 insertions(+), 22 deletions(-) create mode 100644 src/Client.php create mode 100644 src/Configuration.php create mode 100644 src/Contracts/ServiceProviderInterface.php create mode 100644 src/DTO/Response/OpenAIResponse.php create mode 100644 src/DTO/Response/Response.php create mode 100644 src/Exceptions/AIException.php create mode 100644 src/Exceptions/OpenAIException.php create mode 100644 src/Providers/AbstractServiceProvider.php create mode 100644 src/Providers/OpenAIServiceProvider.php create mode 100644 src/Utils/OpenAI/CustomCurlClient.php diff --git a/composer.json b/composer.json index d11b3d9..14b706e 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,8 @@ "php": "^8.2", "league/commonmark": "^2.4", "orhanerday/open-ai": "^5.0", - "vlucas/phpdotenv": "^5.6" + "vlucas/phpdotenv": "^5.6", + "ext-curl": "*" }, "autoload": { "psr-4": { diff --git a/examples/core_example1/index.php b/examples/core_example1/index.php index ecd7680..a1ccbe4 100644 --- a/examples/core_example1/index.php +++ b/examples/core_example1/index.php @@ -1,40 +1,59 @@ load(); +// Set the API configuration for the AI service provider +$configuration = new Configuration($_ENV['OPEN_AI_KEY'], $_ENV['OPEN_AI_KEY_FOR_ORGANIZATION']); -/** See https://github.com/orhanerday/open-ai - * Note: This client is different than https://github.com/openai-php/client - */ +// init the API client (consider the dependency injection) +$apiClient = new OpenAi($configuration->getApiKey()); +$apiClient = new CustomCurlClient(configuration: $configuration); -$open_ai = new Orhanerday\OpenAi\OpenAi($_ENV['OPEN_AI_KEY']); // <-- INPUT -$open_ai->setORG($_ENV['OPEN_AI_KEY_FOR_ORGANIZATION']); // <-- INPUT +// Create an instance of the AI service provider +$provider = new OpenAIServiceProvider($configuration, $apiClient); + +// Create a client instance with the AI service provider +$client = new Client($provider); + +$prompt = 'What is life'; + +// Prepare the messages for the AI service $messages = [ - ...[], //<-- Following the pattern below, you would put previous messages here + ...[], // <-- Following the pattern below, you would put previous messages here [ - 'role' => 'user', // Ai_AbstractBase::SNUM_ROLE_user - 'content' => $prompt // <-- INPUT. This is the question we are asking open AI + 'role' => 'user', // Ai_AbstractBase::SNUM_ROLE_user + 'content' => $prompt, // <-- INPUT. This is the question we are asking open AI ]]; -$opts = [ - 'messages' => $messages, - 'temperature' => 0.9, - "max_tokens" => 150, - "frequency_penalty" => 0, - "presence_penalty" => 0.6, - "stream" => false, - 'model' => 'gpt-3.5-turbo',// This is the openAi model we want it to use +// Set options for the AI service request +$options = [ + 'messages' => $messages, + 'temperature' => 0.9, + 'max_tokens' => 150, + 'frequency_penalty' => 0, + 'presence_penalty' => 0.6, + 'stream' => false, + 'model' => 'gpt-3.5-turbo', // This is the openAi model we want it to use ]; -$response_asJson = $open_ai->chat($opts); -$response = json_decode($response_asJson); +// Generate a response from the AI service +$response = $client->generateResponse($prompt, $options); + +// Extract the answer from the response +$answer = $response->getRawResponseData()->choices[0]->message->content; -$answer = $response->choices[0]->message->content; +// Print the prompt and answer print << diff --git a/src/Client.php b/src/Client.php new file mode 100644 index 0000000..d2f58d2 --- /dev/null +++ b/src/Client.php @@ -0,0 +1,81 @@ +provider = $provider; + } + + /** + * Set the AI service provider instance. + * + * @param \Jjr\RobustChat\Contracts\ServiceProviderInterface $provider The AI service provider instance. + * @return void + */ + public function setProvider(ServiceProviderInterface $provider): void + { + $this->provider = $provider; + } + + /** + * Generate a response using the AI service provider. + * + * @param string $input The input text or data for generating the response. + * @param array $options Additional options for the request. + * @return \Jjr\RobustChat\DTO\Response\Response The generated response. + * @throws \Jjr\RobustChat\Exceptions\AIException If an error occurs during the request. + */ + public function generateResponse(string $input, array $options = []): Response + { + return $this->provider->generateResponse($input, $options); + } + + /** + * Classify text using the AI service provider. + * + * @param string $text The text to classify. + * @param array $options Additional options for the request. + * @return \Jjr\RobustChat\DTO\Response\Response The classification result. + * @throws \Jjr\RobustChat\Exceptions\AIException If an error occurs during the request. + */ + public function classifyText(string $text, array $options = []): Response + { + return $this->provider->classifyText($text, $options); + } + + /** + * Get a completion for the given prompt using the AI service provider. + * + * @param string $prompt The prompt for which to generate a completion. + * @param array $options Additional options for the request. + * @return \Jjr\RobustChat\DTO\Response\Response The generated completion. + * @throws \Jjr\RobustChat\Exceptions\AIException If an error occurs during the request. + */ + public function getCompletion(string $prompt, array $options = []): Response + { + return $this->provider->getCompletion($prompt, $options); + } +} diff --git a/src/Configuration.php b/src/Configuration.php new file mode 100644 index 0000000..a9da07f --- /dev/null +++ b/src/Configuration.php @@ -0,0 +1,38 @@ +apiKeyForOrganization; + } + + /** + * Get the API key for the AI service provider. + * + * @return string|null The API key. + */ + public function getApiKey(): ?string + { + return $this->apiKey; + } +} diff --git a/src/Contracts/ServiceProviderInterface.php b/src/Contracts/ServiceProviderInterface.php new file mode 100644 index 0000000..7bb5d0a --- /dev/null +++ b/src/Contracts/ServiceProviderInterface.php @@ -0,0 +1,43 @@ +rawResponseData; + } +} \ No newline at end of file diff --git a/src/Exceptions/AIException.php b/src/Exceptions/AIException.php new file mode 100644 index 0000000..217064d --- /dev/null +++ b/src/Exceptions/AIException.php @@ -0,0 +1,33 @@ +code]: $this->message\n"; + } +} diff --git a/src/Exceptions/OpenAIException.php b/src/Exceptions/OpenAIException.php new file mode 100644 index 0000000..ca80dc8 --- /dev/null +++ b/src/Exceptions/OpenAIException.php @@ -0,0 +1,12 @@ +apiClient)) { + case OpenAi::class: + $this->apiClient->setORG($this->configuration->getApiKeyForOrganization()); + $response_asJson = $this->apiClient->chat($options); + break; + case CustomCurlClient::class: + $response_asJson = $this->apiClient->chat(options: $options); + break; + default: + throw new OpenAIException('Cannot perform the API request: undefined API client'); + } + + $response = new OpenAIResponse(json_decode($response_asJson)); + + return $response; + } + + public function classifyText(string $text, array $options = []): OpenAIResponse + { + // https://api.openai.com/v1/classifications + return new OpenAIResponse(); + } + + public function getCompletion(string $prompt, array $options = []): OpenAIResponse + { + // https://api.openai.com/v1/completions + return new OpenAIResponse(); + } +} diff --git a/src/Utils/OpenAI/CustomCurlClient.php b/src/Utils/OpenAI/CustomCurlClient.php new file mode 100644 index 0000000..f94eb37 --- /dev/null +++ b/src/Utils/OpenAI/CustomCurlClient.php @@ -0,0 +1,112 @@ +contentTypes = [ + "application/json" => "Content-Type: application/json", + "multipart/form-data" => "Content-Type: multipart/form-data", + ]; + + $this->headers = [ + $this->contentTypes["application/json"], + "Authorization: Bearer {$configuration->getApiKey()}", + ]; + } + + /** + * @throws OpenAIException + */ + public function chat($options, $stream = null): bool|string + { + if ($stream != null && array_key_exists('stream', $options)) { + if (! $options['stream']) { + throw new OpenAIException('Please provide a stream function.'); + } + + $this->stream_method = $stream; + } + + $url = 'https://api.openai.com/v1/chat/completions'; + + return $this->sendRequest($url, 'POST', $options); + } + + /** + * @see \Orhanerday\OpenAi\OpenAi::sendRequest() + * + * @param string $url + * @param string $method + * @param array $options + * @return bool|string + * @throws OpenAIException + */ + private function sendRequest(string $url, string $method, array $options = []): bool|string + { + $post_fields = json_encode($options); + + if (array_key_exists('file', $options) || array_key_exists('image', $options)) { + $this->headers[0] = $this->contentTypes["multipart/form-data"]; + $post_fields = $options; + } else { + $this->headers[0] = $this->contentTypes["application/json"]; + } + + $curl_info = [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => $method, + CURLOPT_POSTFIELDS => $post_fields, + CURLOPT_HTTPHEADER => $this->headers, + ]; + + if ($options == []) { + unset($curl_info[CURLOPT_POSTFIELDS]); + } + + if (! empty($this->proxy)) { + $curl_info[CURLOPT_PROXY] = $this->proxy; + } + + if (array_key_exists('stream', $options) && $options['stream']) { + $curl_info[CURLOPT_WRITEFUNCTION] = $this->stream_method; + } + + $curl = curl_init(); + + curl_setopt_array($curl, $curl_info); + $response = curl_exec($curl); + + $info = curl_getinfo($curl); + + curl_close($curl); + + if (! $response) { + throw new OpenAIException(curl_error($curl)); + } + + return $response; + } +} \ No newline at end of file