Skip to content
Snippets Groups Projects
Verified Commit 2f044f84 authored by Sofiane Lasri's avatar Sofiane Lasri
Browse files

feat: add AI provider configuration and implement AiProviderService

- Introduced `ai-provider.php` configuration file to manage AI provider settings.
- Created `AiProviderService` class for handling AI interactions with support for image prompts.
- Enhanced `ImageTranscodingService` to allow specifying codec for image transcoding.
parent f9bcb1b5
No related branches found
No related tags found
1 merge request!59Ajouter service ia & détection bots user agents
Pipeline #1023 passed
<?php
namespace App\Services;
use App\Models\UploadedPicture;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use RuntimeException;
class AiProviderService
{
/**
* Prompt the AI provider with a text and pictures
*
* @param string $systemRole The system role to send to the AI provider. E.g. "You are a helpful assistant."
* @param string $prompt The prompt to send to the AI provider
* @return array The response from the AI provider.
*/
public function promptWithPictures(string $systemRole, string $prompt, UploadedPicture ...$pictures): array
{
$transcodingService = app(ImageTranscodingService::class);
$transcodedPictures = [];
foreach ($pictures as $picture) {
$picturePath = Storage::disk('public')->get($picture->path_original);
$transcodedPictures[] = $transcodingService->transcode($picturePath, UploadedPicture::MEDIUM_SIZE, 'jpeg');
}
$selectedProvider = config('ai-provider.selected-provider');
$picturesArray = array_map(fn (UploadedPicture $transcodedPicture) => [
'type' => 'image_url',
'image_url' => [
'url' => 'data:image/jpeg;base64,'.base64_encode($transcodedPicture),
],
], $transcodedPictures);
$requestBody = [
'headers' => [
'Authorization' => 'Bearer '.config('ai-provider.providers.'.$selectedProvider.'.api-key'),
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'model' => config('ai-provider.providers.'.$selectedProvider.'.model'),
'messages' => [
[
'role' => 'system',
'content' => [
[
'type' => 'text',
'text' => $systemRole,
],
],
],
[
'role' => 'user',
'content' => [
[
'type' => 'text',
'text' => $prompt,
],
...$picturesArray,
],
],
],
'temperature' => 1,
'max_tokens' => config('ai-provider.providers.'.$selectedProvider.'.max-tokens'),
'top_p' => 1,
'frequency_penalty' => 0,
'presence_penalty' => 0,
'response_format' => [
'type' => 'json_object',
],
],
];
return $this->callApi(config('ai-provider.providers.'.$selectedProvider.'.url'), $requestBody);
}
public function prompt(string $systemRole, string $prompt): array
{
$selectedProvider = config('ai-provider.selected-provider');
$requestBody = [
'headers' => [
'Authorization' => 'Bearer '.config('ai-provider.providers.'.$selectedProvider.'.api-key'),
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'model' => config('ai-provider.providers.'.$selectedProvider.'.model'),
'messages' => [
[
'role' => 'system',
'content' => [
[
'type' => 'text',
'text' => $systemRole,
],
],
],
[
'role' => 'user',
'content' => [
[
'type' => 'text',
'text' => $prompt,
],
],
],
],
'temperature' => 1,
'max_tokens' => config('ai-provider.providers.'.$selectedProvider.'.max-tokens'),
'top_p' => 1,
'frequency_penalty' => 0,
'presence_penalty' => 0,
'response_format' => [
'type' => 'json_object',
],
],
];
return $this->callApi(config('ai-provider.providers.'.$selectedProvider.'.url'), $requestBody);
}
/**
* Call the AI provider API
*
* @param string $url The URL of the AI provider API
* @param array $requestBody The request body to send to the AI provider API
* @return array The response from the AI provider
*/
private function callApi(string $url, array $requestBody): array
{
$client = new Client;
try {
$response = $client->post($url, $requestBody);
} catch (GuzzleException $e) {
Log::error('Failed to call AI provider API', [
'exception' => $e,
]);
throw new RuntimeException('Failed to call AI provider API');
}
$result = json_decode($response->getBody()->getContents(), true);
if (! isset($result['choices'][0]['message']['content'])) {
Log::error('Failed to get response from AI provider', [
'response' => $result,
]);
throw new RuntimeException('Failed to get response from AI provider');
}
return json_decode($result['choices'][0]['message']['content'], true);
}
}
......@@ -5,6 +5,9 @@
use Imagick;
use Intervention\Image\Drivers\Imagick\Driver as ImagickDriver;
use Intervention\Image\Encoders\AvifEncoder;
use Intervention\Image\Encoders\JpegEncoder;
use Intervention\Image\Encoders\PngEncoder;
use Intervention\Image\Encoders\WebpEncoder;
use Intervention\Image\Exceptions\RuntimeException;
use Intervention\Image\ImageManager;
use Log;
......@@ -23,9 +26,10 @@ public function __construct(ImagickDriver $driver)
*
* @param string $source The source image path or content. Eg: /path/to/image.jpg or file_get_contents('/path/to/image.jpg')
* @param int|null $resolution The new resolution to transcode the image to
* @param string $codec The codec to use for transcoding. Eg: jpeg, webp, png, avif
* @return string|null The transcoded image content
*/
public function transcode(string $source, ?int $resolution = null): ?string
public function transcode(string $source, ?int $resolution = null, string $codec = 'avif'): ?string
{
$image = $this->imageManager->read($source);
try {
......@@ -53,7 +57,12 @@ public function transcode(string $source, ?int $resolution = null): ?string
$image->scale($resolution);
}
return $image->encode(new AvifEncoder(quality: 85))->toString();
return match ($codec) {
'jpeg' => $image->encode(new JpegEncoder(quality: 85))->toString(),
'webp' => $image->encode(new WebpEncoder(quality: 85))->toString(),
'png' => $image->encode(new PngEncoder)->toString(),
default => $image->encode(new AvifEncoder(quality: 85))->toString(),
};
} catch (RuntimeException $exception) {
Log::error('Failed to transcode image', [
'exception' => $exception,
......
<?php
return [
'selected-provider' => env('AI_PROVIDER', 'openai'),
'providers' => [
'openai' => [
'url' => env('OPENAI_URL', 'https://api.openai.com/v1/chat/completions'),
'api-key' => env('OPENAI_API_KEY'),
'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),
'max-tokens' => env('OPENAI_MAX_TOKENS', 256),
],
],
];
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment