Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 59
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
GeminiAPI
0.00% covered (danger)
0.00%
0 / 59
0.00% covered (danger)
0.00%
0 / 6
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 post
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 requestToCurl
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 postCompletions
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 refreshAccessToken
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getAIAccessToken
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3namespace App\Services\FlyMsgAI;
4
5use App\Helpers\FlyMSGLogger;
6use Exception;
7use GuzzleHttp\Client;
8use Illuminate\Support\Facades\Artisan;
9use Psr\Http\Message\ResponseInterface;
10use GuzzleHttp\Exception\GuzzleException;
11use Illuminate\Support\Facades\Cache;
12use Illuminate\Support\Facades\Log;
13use Google\Client as GoogleClient;
14use GuzzleHttp\Psr7\Request;
15
16class GeminiAPI
17{
18    const FOURTY_MINUTES = 2400;
19
20    private $client;
21    private $headers;
22    private  $baseURL = "https://us-central1-aiplatform.googleapis.com/v1/projects/project-romeo/locations/us-central1/publishers/google/models/";
23    // private  $baseURL = "https://aiplatform.googleapis.com/v1/projects/project-romeo/locations/global/publishers/google/models/";
24
25    /**
26     * @throws Exception
27     */
28    public function __construct($accessToken)
29    {
30        if (!$accessToken) {
31            throw new Exception("Access token is required. gcloud auth print-access-token");
32        }
33        $this->client = new Client();
34        $this->headers = [
35            "Authorization" => "Bearer $accessToken",
36            "Content-Type" => "application/json",
37        ];
38    }
39
40    /**
41     * @throws GuzzleException
42     */
43    private function post($data = [], $model = "gemini-2.5-flash:streamGenerateContent"): ResponseInterface
44    {
45        $request = new Request(
46            'POST',
47            $this->baseURL . $model,
48            $this->headers,
49            json_encode($data)
50        );
51
52        $curlCommand = $this->requestToCurl($request);
53
54        Log::info('Executing cURL command: ', ['command' => $curlCommand]);
55
56        return $this->client->send($request);
57    }
58
59    function requestToCurl($request)
60    {
61        $method = $request->getMethod();
62        $url = (string) $request->getUri();
63        $headers = '';
64        foreach ($request->getHeaders() as $name => $values) {
65            foreach ($values as $value) {
66                // Each header separately
67                $headers .= sprintf(" -H '%s: %s'", $name, $value);
68            }
69        }
70        $body = (string) $request->getBody();
71        $data = $body !== '' ? " --data '$body'" : '';
72        return "curl -X $method$headers$data '$url'";
73    }
74
75    /**
76     * @param array $postParams
77     * @return ResponseInterface
78     * @throws GuzzleException
79     */
80    public function postCompletions(array $postParams, $model = "gemini-2.5-flash:streamGenerateContent"): ResponseInterface
81    {
82        return $this->post($postParams, $model);
83    }
84
85    public static function refreshAccessToken()
86    {
87        Cache::forget("ai_geminiapi_access_token");
88
89        return GeminiAPI::getAIAccessToken();
90    }
91
92    public static function getAIAccessToken()
93    {
94        $access_token = Cache::get("ai_geminiapi_access_token");
95        if ($access_token) {
96            return $access_token;
97        }
98
99        try {
100            $googleCredentials = SecretsAWS::getSecretValue();
101            $privateKey = str_replace("\\n", "\n", $googleCredentials['GOOGLE_PRIVATE_KEY']);
102
103            $client = new GoogleClient();
104
105            $credentials = [
106                'type' => 'service_account',
107                'project_id' => $googleCredentials['GOOGLE_CLOUD_PROJECT'],
108                'private_key_id' => $googleCredentials['GOOGLE_PRIVATE_KEY_ID'],
109                'private_key' => $privateKey,
110                'client_email' => $googleCredentials['GOOGLE_CLIENT_EMAIL'],
111                'client_id' => $googleCredentials['GOOGLE_CLIENT_ID'],
112                'auth_uri' => 'https://accounts.google.com/o/oauth2/auth',
113                'token_uri' => 'https://oauth2.googleapis.com/token',
114                'auth_provider_x509_cert_url' => 'https://www.googleapis.com/oauth2/v1/certs',
115                'client_x509_cert_url' => $googleCredentials['GOOGLE_CLIENT_X509_CERT_URL'],
116            ];
117
118            $client->setAuthConfig($credentials);
119            $client->setApplicationName("FlyMSG");
120            $client->setScopes(['https://www.googleapis.com/auth/cloud-platform']);
121
122            if ($client->isAccessTokenExpired()) {
123                $client->fetchAccessTokenWithAssertion();
124            }
125
126            $access_token = $client->getAccessToken();
127
128            if (!$access_token || !isset($access_token['access_token'])) {
129                Log::error("Access token is null.");
130            }
131
132            Cache::put("ai_geminiapi_access_token", $access_token['access_token'], GeminiAPI::FOURTY_MINUTES);
133
134            return $access_token['access_token'];
135        } catch (Exception $e) {
136            FlyMSGLogger::logError("FlyMSG-AI", $e);
137            return null;
138        }
139    }
140}