src/Helper/AiHelper.php line 15

Open in your IDE?
  1. <?php
  2. namespace App\Helper;
  3. use App\Entity\AiCount;
  4. use Doctrine\ORM\EntityManagerInterface;
  5. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  6. use Symfony\Component\HttpKernel\Exception\HttpException;
  7. class AiHelper
  8. {
  9.     private $apiKey;
  10.     private $apiKeyMistral;
  11.     public $entityManager;
  12.     public function __construct(ParameterBagInterface $params, EntityManagerInterface $entityManager = null)
  13.     {
  14.         $this->apiKey = $params->get('gemini_api_key');
  15.         $this->apiKeyMistral = 'dFkwpcRnE237t2oKIFbHcTBNRnQg9NNc';
  16.         $this->entityManager = $entityManager;
  17.     }
  18.     public function generateContent(string $prompt, bool $jsonOutput = false)
  19.     {
  20.         // 1. Check Model Name (Ensure 2.0 is available, otherwise switch to 1.5)
  21.         $url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-lite-latest:generateContent?key=" . $this->apiKey;
  22.         $data = [
  23.             'contents' => [['parts' => [['text' => $prompt]]]]
  24.         ];
  25.         if ($jsonOutput) {
  26.             $data['generationConfig'] = [
  27.                 'response_mime_type' => 'application/json'
  28.             ];
  29.         }
  30.         $options = [
  31.             'http' => [
  32.                 'header'  => "Content-Type: application/json\r\n",
  33.                 'method'  => 'POST',
  34.                 'content' => json_encode($data),
  35.                 'ignore_errors' => true // 2. Allow reading the error body from Google
  36.             ]
  37.         ];
  38.         $context  = stream_context_create($options);
  39.         $result = file_get_contents($url, false, $context);
  40.         // 3. Robust Error Handling
  41.         if ($result === false) {
  42.             // Handle connection failure
  43.             return 'Error: Could not connect to AI service.';
  44.         }
  45.         $decodedResult = json_decode($result, true);
  46.         // Check if Google returned an error structure
  47.         if (isset($decodedResult['error'])) {
  48.             // Log this error or return it to understand why it fails
  49.             return 'API Error: ' . ($decodedResult['error']['message'] ?? 'Unknown error');
  50.         }
  51.         // 4. Fix Database Logic: Normalize date to Midnight (00:00:00)
  52.         // This ensures we only have ONE row per day
  53.         $countDate = new \DateTime('today midnight');
  54.         $existingAiCount = $this->entityManager->getRepository(AiCount::class)
  55.             ->findOneBy(['countDate' => $countDate]);
  56.         if ($existingAiCount) {
  57.             $existingAiCount->setCounter($existingAiCount->getCounter() + 1);
  58.         } else {
  59.             $aiCount = new AiCount();
  60.             $aiCount->setCountDate($countDate);
  61.             $aiCount->setCounter(1);
  62.             $this->entityManager->persist($aiCount);
  63.         }
  64.         // 5. Optimization: Move cleanup to a CRON job ideally.
  65.         // If keeping it here, do it randomly (1 in 100 calls) to save performance
  66.         if (rand(1, 100) === 1) {
  67.             $this->cleanupOldCounts();
  68.         }
  69.         $this->entityManager->flush();
  70.         return $decodedResult['candidates'][0]['content']['parts'][0]['text'] ?? 'No response generated';
  71.     }
  72.     public function generateContentMistral(string $prompt, $model = 'mistral-small-latest', bool $jsonOutput = false): string
  73.     {
  74.         $url = "https://api.mistral.ai/v1/chat/completions";
  75.         $data = [
  76. //            'model'    => $model, // mistral-large-latest / mistral-small-latest
  77.             'model'    => 'mistral-tiny-latest', // mistral-large-latest / mistral-small-latest
  78.             'messages' => [['role' => 'user', 'content' => $prompt]]
  79.         ];
  80.         if ($jsonOutput) {
  81.             $data['response_format'] = ['type' => 'json_object'];
  82.         }
  83.         $options = [
  84.             'http' => [
  85.                 'header'  => "Content-Type: application/json\r\n" .
  86.                     "Authorization: Bearer " . $this->apiKeyMistral . "\r\n",
  87.                 'method'  => 'POST',
  88.                 'content' => json_encode($data)
  89.             ]
  90.         ];
  91.         $context = stream_context_create($options);
  92.         $result  = file_get_contents($url, false, $context);
  93.         $decoded = json_decode($result, true);
  94.         $countDate = new \DateTime('today midnight');
  95.         $existingAiCount = $this->entityManager->getRepository(AiCount::class)
  96.             ->findOneBy(['countDate' => $countDate]);
  97.         if ($existingAiCount) {
  98.             $existingAiCount->setCounter($existingAiCount->getCounter() + 1);
  99.         } else {
  100.             $aiCount = new AiCount();
  101.             $aiCount->setCountDate($countDate);
  102.             $aiCount->setCounter(1);
  103.             $this->entityManager->persist($aiCount);
  104.         }
  105.         $this->entityManager->flush();
  106.         return $decoded['choices'][0]['message']['content'] ?? '';
  107.     }
  108.     private function cleanupOldCounts()
  109.     {
  110.         $threeMonthsAgo = (new \DateTime())->modify('-3 months');
  111.         // Use DELETE query instead of loading entities (Much faster)
  112.         $this->entityManager->createQueryBuilder()
  113.             ->delete(AiCount::class, 'a')
  114.             ->where('a.countDate < :threeMonthsAgo')
  115.             ->setParameter('threeMonthsAgo', $threeMonthsAgo)
  116.             ->getQuery()
  117.             ->execute();
  118.     }
  119.     public function justCorrect(string $text)
  120.     {
  121.         return $this->generateContent('Corriger ce texte sans en changer le sens ni la mise en forme ni balise html : "'.$text.'" Ne retourner que le texte corrigĂ© et rien d\'autre. Conserver la mise en forme HTML.');
  122.     }
  123. }