mirror of
https://github.com/sakaljurgis/actions-api.git
synced 2026-07-08 22:07:40 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google;
|
||||
|
||||
use Google\Client;
|
||||
|
||||
class ApiAuthService
|
||||
{
|
||||
/**
|
||||
* @throws \Google\Exception
|
||||
*/
|
||||
public function getAccessToken(): string
|
||||
{
|
||||
$client = new Client();
|
||||
$serviceAccountKeyPath = env('GOOGLE_APPLICATION_CREDENTIALS');
|
||||
$client->setAuthConfig($serviceAccountKeyPath);
|
||||
$client->addScope('https://www.googleapis.com/auth/cloud-platform');
|
||||
$client->fetchAccessTokenWithAssertion();
|
||||
|
||||
// todo - cache, refresh, etc
|
||||
$accessToken = $client->getAccessToken();
|
||||
|
||||
return $accessToken['access_token'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google\DTO;
|
||||
|
||||
class ImageGenerationPrediction
|
||||
{
|
||||
public readonly string $bytesBase64Encoded;
|
||||
public readonly string $mimeType;
|
||||
|
||||
public function __construct(string $bytesBase64Encoded, string $mimeType)
|
||||
{
|
||||
$this->bytesBase64Encoded = $bytesBase64Encoded;
|
||||
$this->mimeType = $mimeType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google\DTO;
|
||||
|
||||
class ImageGenerationRequest
|
||||
{
|
||||
public function __construct(
|
||||
public readonly string $prompt,
|
||||
public readonly ?string $negativePrompt = null,
|
||||
public readonly ?int $sampleCount = null,
|
||||
public readonly ?string $aspectRatio = null,
|
||||
public readonly ?string $safetySetting = null,
|
||||
)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google\DTO;
|
||||
|
||||
use App\Services\Google\Error\APIError;
|
||||
|
||||
class ImageGenerationResponse
|
||||
{
|
||||
/**
|
||||
* @var \App\Services\Google\DTO\ImageGenerationPrediction[]
|
||||
*/
|
||||
public readonly array $predictions;
|
||||
public readonly ?APIError $error;
|
||||
|
||||
public function __construct(array $rawResponse)
|
||||
{
|
||||
if (isset($rawResponse['predictions']) && count($rawResponse['predictions']) > 0) {
|
||||
$predictions = [];
|
||||
foreach ($rawResponse['predictions'] as $prediction) {
|
||||
$predictions[] = new ImageGenerationPrediction($prediction['bytesBase64Encoded'], $prediction['mimeType']);
|
||||
}
|
||||
$this->predictions = $predictions;
|
||||
$this->error = null;
|
||||
}
|
||||
|
||||
if (isset($rawResponse['error'])) {
|
||||
$error = $rawResponse['error'];
|
||||
$this->error = new APIError($error['message'], $error['code']);
|
||||
}
|
||||
|
||||
if (!isset($this->predictions) && !isset($this->error)) {
|
||||
$this->error = new APIError('An unknown error occurred.', 0,);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google;
|
||||
|
||||
class EndpointBuilderService
|
||||
{
|
||||
public function buildPredictionEndpoint(string $model): string
|
||||
{
|
||||
return $this->buildEndpoint($model, 'predict');
|
||||
}
|
||||
|
||||
public function buildGenerationEndpoint(string $model): string
|
||||
{
|
||||
return $this->buildEndpoint($model, 'generateContent');
|
||||
}
|
||||
|
||||
private function buildEndpoint(string $model, string $action): string
|
||||
{
|
||||
$projectId = env('GOOGLE_CLOUD_PROJECT');
|
||||
$location = env('GOOGLE_LOCATION');
|
||||
$modelPath = 'projects/' . $projectId .
|
||||
'/locations/' . $location .
|
||||
'/publishers/google/models/' . $model .
|
||||
':' . $action;
|
||||
|
||||
return 'https://' . $location . '-aiplatform.googleapis.com/v1/' . $modelPath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google\Error;
|
||||
|
||||
use Exception;
|
||||
|
||||
class APIError extends Exception
|
||||
{
|
||||
public $code;
|
||||
public $message;
|
||||
|
||||
public function __construct(string $message, int $code)
|
||||
{
|
||||
parent::__construct($message);
|
||||
|
||||
$this->code = $code;
|
||||
$this->message = $message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google;
|
||||
|
||||
/**
|
||||
* "aspectRatio": "1:1, 9:16, 16:9, 3:4, or 4:3. default 1:1",
|
||||
*/
|
||||
enum ImageAspectRatios: string
|
||||
{
|
||||
case _1_1 = '1:1';
|
||||
case _9_16 = '9:16';
|
||||
case _16_9 = '16:9';
|
||||
case _3_4 = '3:4';
|
||||
case _4_3 = '4:3';
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google;
|
||||
|
||||
/**
|
||||
* "safetySetting": "block_none, block_only_high, block_medium_and_above, block_low_and_above. default block_medium_and_above"
|
||||
*/
|
||||
enum ImageGenerationSafetySettings: string
|
||||
{
|
||||
case BLOCK_NONE = 'block_none';
|
||||
case BLOCK_ONLY_HIGH = 'block_only_high';
|
||||
case BLOCK_MEDIUM_AND_ABOVE = 'block_medium_and_above';
|
||||
case BLOCK_LOW_AND_ABOVE = 'block_low_and_above';
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google;
|
||||
|
||||
use App\Services\Google\DTO\ImageGenerationRequest;
|
||||
use App\Services\Google\DTO\ImageGenerationResponse;
|
||||
|
||||
/**
|
||||
* ref: https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/imagen-api
|
||||
*/
|
||||
class ImageGenerationService
|
||||
{
|
||||
private readonly string $imagenModel;
|
||||
|
||||
public function __construct(
|
||||
private readonly ApiAuthService $apiAuthService,
|
||||
private readonly EndpointBuilderService $endpointBuilderService,
|
||||
)
|
||||
{
|
||||
$this->imagenModel = env('GOOGLE_IMAGEN_MODEL');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function generateImage(ImageGenerationRequest $request): ImageGenerationResponse
|
||||
{
|
||||
$requestBody = $this->buildRequestBody($request);
|
||||
|
||||
return $this->sendGenerationRequest($requestBody);
|
||||
}
|
||||
|
||||
private function buildRequestBody(ImageGenerationRequest $request): string {
|
||||
$instance = [
|
||||
'prompt' => $request->prompt,
|
||||
];
|
||||
if ($request->negativePrompt) {
|
||||
$instance['negativePrompt'] = $request->negativePrompt;
|
||||
}
|
||||
|
||||
$parameters = [
|
||||
'sampleCount' => 1,
|
||||
];
|
||||
|
||||
if ($request->sampleCount) {
|
||||
$parameters['sampleCount'] = $request->sampleCount;
|
||||
}
|
||||
if ($request->aspectRatio) {
|
||||
$parameters['aspectRatio'] = $request->aspectRatio;
|
||||
}
|
||||
if ($request->safetySetting) {
|
||||
$parameters['safetySetting'] = $request->safetySetting;
|
||||
}
|
||||
|
||||
$requestBody = [
|
||||
'instances' => [$instance],
|
||||
'parameters' => $parameters
|
||||
];
|
||||
|
||||
return json_encode($requestBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Google\Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function sendGenerationRequest(string $requestBody): ImageGenerationResponse
|
||||
{
|
||||
$endpoint = $this->endpointBuilderService->buildPredictionEndpoint($this->imagenModel);
|
||||
$accessToken = $this->apiAuthService->getAccessToken();
|
||||
|
||||
$ch = curl_init($endpoint);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: Bearer ' . $accessToken,
|
||||
'Content-Type: application/json',
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
if (curl_errno($ch) || $response === false) {
|
||||
throw new \Exception(curl_error($ch));
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
$responseArray = json_decode($response, true);
|
||||
|
||||
return new ImageGenerationResponse($responseArray);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Image;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ImageService
|
||||
{
|
||||
/**
|
||||
* @param string $base64ImageData
|
||||
* @param string $mimeType
|
||||
* @return array ['url', 'id']
|
||||
*/
|
||||
public function saveImage(string $base64ImageData, string $mimeType): array
|
||||
{
|
||||
$imageId = uniqid();
|
||||
$imageData = base64_decode($base64ImageData);
|
||||
$ext = explode('/', $mimeType)[1];
|
||||
|
||||
Storage::put('public/images/' . $imageId . '.' . $ext , $imageData);
|
||||
$relativePath = Storage::url('public/images/' . $imageId . '.png');
|
||||
|
||||
return [
|
||||
'url' => asset($relativePath),
|
||||
'id' => $imageId,
|
||||
// todo - size, width, height, thumb, etc
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user