🚀 مُصادَق: الإطلاق الأولي للنظام المتكامل

This commit is contained in:
Hamza-Ayed
2026-05-03 00:59:39 +03:00
commit d0e538408d
43 changed files with 2554 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace App\Services\AI\Contracts;
final class ExtractionResultDTO
{
public function __construct(
public string $invoiceNumber,
public string $invoiceDate,
public string $supplierName,
public ?string $supplierTin,
public string $supplierAddress,
public ?string $buyerName,
public ?string $buyerTin,
public array $lines,
public float $subtotal,
public float $taxAmount,
public float $grand_total,
public string $currency,
public float $confidence,
public array $usage
) {}
}
interface AIProviderInterface
{
public function extractFromFile(string $filePath, string $mimeType): ExtractionResultDTO;
public function getProviderName(): string;
}

View File

@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace App\Services\AI;
use App\Services\AI\Contracts\{AIProviderInterface, ExtractionResultDTO};
use GuzzleHttp\Client;
use Exception;
final class GeminiProvider implements AIProviderInterface
{
private Client $client;
private string $apiKey;
private string $model;
public function __construct()
{
$this->client = new Client();
$this->apiKey = $_ENV['GEMINI_API_KEY'] ?? '';
$this->model = $_ENV['GEMINI_MODEL'] ?? 'gemini-2.0-flash';
}
public function extractFromFile(string $filePath, string $mimeType): ExtractionResultDTO
{
$fileData = base64_encode(file_get_contents($filePath));
$prompt = "Extract invoice data from this file. Return ONLY valid JSON (no markdown). " .
"Fields: invoice_number, invoice_date (YYYY-MM-DD), supplier_name, supplier_tin, supplier_address, " .
"buyer_name, buyer_tin, lines (description, quantity, unit_price, line_total, tax_rate), " .
"subtotal, tax_amount, grand_total, currency (JOD), confidence (0-1).";
$response = $this->client->post("https://generativelanguage.googleapis.com/v1beta/models/{$this->model}:generateContent?key={$this->apiKey}", [
'json' => [
'contents' => [
[
'parts' => [
['text' => $prompt],
[
'inline_data' => [
'mime_type' => $mimeType,
'data' => $fileData
]
]
]
]
],
'generationConfig' => [
'response_mime_type' => 'application/json'
]
]
]);
$data = json_decode($response->getBody()->getContents(), true);
$jsonStr = $data['candidates'][0]['content']['parts'][0]['text'] ?? '{}';
$result = json_decode($jsonStr, true);
return new ExtractionResultDTO(
$result['invoice_number'] ?? '',
$result['invoice_date'] ?? '',
$result['supplier_name'] ?? '',
$result['supplier_tin'] ?? null,
$result['supplier_address'] ?? '',
$result['buyer_name'] ?? null,
$result['buyer_tin'] ?? null,
$result['lines'] ?? [],
(float)($result['subtotal'] ?? 0),
(float)($result['tax_amount'] ?? 0),
(float)($result['grand_total'] ?? 0),
$result['currency'] ?? 'JOD',
(float)($result['confidence'] ?? 0),
$data['usageMetadata'] ?? []
);
}
public function getProviderName(): string { return 'gemini'; }
}