| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- <?php
- namespace App\Services;
- use App\Enums\UserTypeEnum;
- use GuzzleHttp\Client;
- use GuzzleHttp\Exception\GuzzleException;
- use GuzzleHttp\Exception\RequestException;
- use Illuminate\Support\Facades\Log;
- class GeminiService
- {
- private const API_BASE_URL = 'https://generativelanguage.googleapis.com/v1beta/models';
- private Client $client;
- public function __construct()
- {
- $this->client = new Client(['timeout' => 30]);
- }
- public function sendMessage(string $message, UserTypeEnum $userType, array $history = []): string
- {
- $apiKey = config('services.gemini.api_key');
- $model = config('services.gemini.model');
- $contents = $this->buildContents($history, $message);
- $payload = [
- 'systemInstruction' => [
- 'parts' => [['text' => $this->loadContext($userType)]],
- ],
- 'contents' => $contents,
- 'generationConfig' => [
- 'temperature' => 0.4,
- 'maxOutputTokens' => 512,
- ],
- ];
- try {
- $response = $this->client->post($this->buildApiUrl($model), [
- 'json' => $payload,
- 'query' => ['key' => $apiKey],
- ]);
- $data = json_decode($response->getBody()->getContents(), true);
- return $data['candidates'][0]['content']['parts'][0]['text']
- ?? __('chatbot.error_response');
- } catch (GuzzleException $e) {
- $this->logGeminiError($e, $model);
- return __('chatbot.error_response');
- }
- }
- private function buildApiUrl(string $model): string
- {
- return self::API_BASE_URL . '/' . rawurlencode($model) . ':generateContent';
- }
- private function logGeminiError(GuzzleException $e, string $model): void
- {
- $context = [
- 'model' => $model,
- 'message' => $this->sanitizeApiKey($e->getMessage()),
- ];
- if ($e instanceof RequestException && $e->hasResponse()) {
- $response = $e->getResponse();
- $context['status'] = $response->getStatusCode();
- $context['body'] = $this->sanitizeApiKey((string) $response->getBody());
- }
- Log::error('Gemini API error', $context);
- }
- private function sanitizeApiKey(string $message): string
- {
- return preg_replace('/([?&]key=)[^&\s"]+/i', '$1[REDACTED]', $message);
- }
- private function buildContents(array $history, string $newMessage): array
- {
- $contents = [];
- foreach ($history as $entry) {
- $role = $entry['role'] === 'model' ? 'model' : 'user';
- $contents[] = [
- 'role' => $role,
- 'parts' => [['text' => $entry['text']]],
- ];
- }
- $contents[] = [
- 'role' => 'user',
- 'parts' => [['text' => $newMessage]],
- ];
- return $contents;
- }
- private function loadContext(UserTypeEnum $userType): string
- {
- $restrictionsFile = resource_path('chatbot/restricoes.md');
- $restrictions = file_exists($restrictionsFile) ? file_get_contents($restrictionsFile) : '';
- $contextFile = match ($userType) {
- UserTypeEnum::CLIENT => resource_path('chatbot/context_cliente.txt'),
- UserTypeEnum::PROVIDER => resource_path('chatbot/context_prestador.txt'),
- default => resource_path('chatbot/context_cliente.txt'),
- };
- $context = file_exists($contextFile) ? file_get_contents($contextFile) : '';
- return $restrictions . "\n\n" . $context;
- }
- }
|