src/Shared/Infrastructure/ChatGpt/ChatGptService.php line 9

Open in your IDE?
  1. <?php
  2. namespace App\Shared\Infrastructure\ChatGpt;
  3. class ChatGptService
  4. {
  5.     private $cache=[];
  6.     public function __construct(private array $apiKeys, private $cacheDir) {
  7.         $this->cache = @unserialize(@file_get_contents($this->cacheDir."/chatGpt.cache"))??[];
  8.     }
  9.     private function setCache($prompt$response){
  10.         $md5 md5($prompt);
  11.         $this->cache[$md5] = $response;
  12.         file_put_contents($this->cacheDir."/chatGpt.cache"serialize($this->cache));
  13.     }
  14.     private function getCache($prompt){
  15.         $md5 md5($prompt);
  16.         if (isset($this->cache[$md5])) return $this->cache[$md5];
  17.        return false;
  18.     }
  19.     public function __invoke($prompt$isJsonReponse false)
  20.     {
  21.         if ($this->getCache($prompt)) {
  22.             return $this->getCache($prompt);
  23.         }
  24.         $apiKeys $this->apiKeys;
  25.         $data trim($prompt) ;
  26.         $dataFile tempnam($this->cacheDir'data_') . '.txt';
  27.         file_put_contents($dataFile$data);
  28.         $pythonScript = <<<EOF
  29. import sys
  30. import json
  31. import openai
  32. import traceback
  33. import time
  34. import os
  35. import tempfile
  36. # Lista de claves API pasadas desde PHP
  37. api_keys = sys.argv[4].split(',')
  38. cache_dir = sys.argv[5]
  39. index_file = os.path.join(cache_dir, 'gpt_api_key_index.txt')
  40. backoff_file = os.path.join(cache_dir, 'api_key_backoff.txt')
  41. def read_backoff_times():
  42.     if os.path.exists(backoff_file):
  43.         with open(backoff_file, 'r') as file:
  44.             return json.load(file)
  45.     return {}
  46. def write_backoff_times(backoff_data):
  47.     with open(backoff_file, 'w') as file:
  48.         json.dump(backoff_data, file)
  49. def get_next_api_key():
  50.     backoff_data = read_backoff_times()
  51.     if os.path.exists(index_file):
  52.         with open(index_file, 'r') as f:
  53.             counter = int(f.read().strip())
  54.     else:
  55.         counter = -1
  56.     while True:
  57.         counter = (counter + 1) % len(api_keys)
  58.         current_key_index = str(counter)
  59.         if current_key_index in backoff_data and time.time() < backoff_data[current_key_index]:
  60.             continue
  61.         with open(index_file, 'w') as f:
  62.             f.write(str(counter))
  63.         return api_keys[counter]
  64. def api_call(data_file, output_file="response.txt", error_file="error.txt"):
  65.     with open(data_file, 'r') as file:
  66.         content = file.read()
  67.     backoff_data = read_backoff_times()
  68.     initial_backoff = 1
  69.     print(f"Data file: {data_file}\\n")
  70.     print(f"Output file {output_file}\\n")
  71.     print(f"Error file {error_file}\\n")
  72.     while True:
  73.         try:
  74.             api_key = get_next_api_key()
  75.             openai.api_key = api_key
  76.             print(f"API Key: {api_key}")
  77.             print(f"Content: {content}")
  78.             print("Trying to generate response...\\n")
  79.             sys.executable = '/usr/bin/python3'
  80.             print(f"sys.executable: {sys.executable}\\n")
  81.             response = openai.ChatCompletion.create(
  82.                 model="gpt-3.5-turbo",
  83.                 messages=[
  84.                     {"role": "system", "content": "You are a helpful assistant."},
  85.                     {"role": "user", "content": content}
  86.                 ],
  87.                 temperature=0.7
  88.             )
  89.             print("Response success\\n")
  90.             with open(output_file, "w") as file:
  91.                 file.write(response['choices'][0]['message']['content'].strip())
  92.             print("Response written to file successfully\\n")
  93.             break
  94.         except openai.error.RateLimitError as e:
  95.             current_key_index = str(api_keys.index(api_key))
  96.             print(f"Rate limit exceeded for API key {api_key}.")
  97.             next_backoff = min(initial_backoff * 2, 3600)
  98.             backoff_data[current_key_index] = time.time() + next_backoff
  99.             write_backoff_times(backoff_data)
  100.             initial_backoff = next_backoff
  101.         except Exception as e:
  102.             print(f"Unhandled exception: {e}")
  103.             error_details = f"{str(e)}\\n{traceback.format_exc()}"
  104.             with open(error_file, "w") as file:
  105.                 file.write(error_details)
  106.             break
  107.             
  108. if __name__ == "__main__":
  109.     data_file = sys.argv[1]
  110.     output_file = sys.argv[2] if len(sys.argv) > 2 else "response.txt"
  111.     error_file = sys.argv[3] if len(sys.argv) > 3 else "error.txt"
  112.     api_call(data_file, output_file=output_file, error_file=error_file)
  113. EOF;
  114.         $response '';
  115.         $attempt 0;
  116.         $maxAttempts 5;
  117.         $error "";
  118.         while ($attempt $maxAttempts && empty($response)) {
  119.             $outputFile tempnam($this->cacheDir'gpt_response_') . '.txt';
  120.             $errorFile tempnam($this->cacheDir'gpt_error_') . '.txt';
  121.             $tempName tempnam($this->cacheDir'py');
  122.             file_put_contents($tempName$pythonScript);
  123.             $apiKeysString implode(','$apiKeys);
  124.             $command escapeshellcmd("python3 " $tempName " " escapeshellarg($dataFile) . " " escapeshellarg($outputFile) . " " escapeshellarg($errorFile) . " " escapeshellarg($apiKeysString)." " escapeshellarg($this->cacheDir));
  125.             $output shell_exec($command);
  126.             if (file_exists($outputFile)) {
  127.                 $response trim(file_get_contents($outputFile));
  128.                 unlink($tempName);
  129.                 unlink($outputFile);
  130.                 unlink($errorFile);
  131.             } else {
  132.                 $error .= $output" ".file_get_contents($errorFile).PHP_EOL;
  133.             }
  134.             unlink($errorFile);
  135.             unlink($outputFile);
  136.             if ($isJsonReponse) {
  137.                 if (json_decode($responsetrue)) {
  138.                     $response json_decode($responsetrue);
  139.                 }
  140.                 else {
  141.                     $isMatch preg_match("/(?<json>[\[{].*?[}\]])/ms"$response$mt);
  142.                     if ($isMatch && isset($mt['json'])) {
  143.                         $response json_decode($mt['json'], true);
  144.                     }
  145.                     else $response null;
  146.                 }
  147.             }
  148.             $attempt++;
  149.         }
  150.         if (!$response) throw new \Exception("CHATGPT: ".$error);
  151.         unlink($dataFile);
  152.         $this->setCache($prompt$response);
  153.         return $response;
  154.     }
  155. }