Update: 2026-05-06 01:38:39
This commit is contained in:
103
app/modules_app/voice/parse_intent.php
Normal file
103
app/modules_app/voice/parse_intent.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/**
|
||||
* Voice Parse Intent Proxy Endpoint
|
||||
* POST /v1/voice/parse-intent
|
||||
*
|
||||
* Proxies transcribed text to Gemini to extract intent and parameters.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Middleware\AuthMiddleware;
|
||||
use App\Middleware\RateLimitMiddleware;
|
||||
use App\Core\Security;
|
||||
use App\Core\Validator;
|
||||
|
||||
// Rate limit: 20 per minute
|
||||
RateLimitMiddleware::check(20, 60);
|
||||
|
||||
$decoded = AuthMiddleware::check();
|
||||
$data = Security::sanitize(input());
|
||||
|
||||
$errors = Validator::validate($data, ['text' => 'required']);
|
||||
if ($errors) {
|
||||
json_error('النص مطلوب', 422);
|
||||
}
|
||||
|
||||
$apiKey = env('GEMINI_API_KEY');
|
||||
if (!$apiKey) {
|
||||
json_error('Gemini API Key غير متوفر', 500);
|
||||
}
|
||||
|
||||
$text = $data['text'];
|
||||
|
||||
$systemPrompt = <<<PROMPT
|
||||
أنت محلل أوامر لنظام مُصادَق للفوترة الأردني.
|
||||
استخرج النية والمعاملات من النص وأرجع JSON فقط.
|
||||
|
||||
الأوامر المتاحة:
|
||||
- list_invoices: { company?: string, from?: date, to?: date, status?: string }
|
||||
- check_quota: {}
|
||||
- open_scanner: { company?: string }
|
||||
- search_invoice: { amount?: number, company?: string, number?: string }
|
||||
- get_report: { type: "tax"|"monthly", period?: string }
|
||||
- check_status: { invoice_id?: string, company?: string }
|
||||
- export_pdf: { invoice_id?: string, company?: string }
|
||||
- navigate: { screen: string }
|
||||
|
||||
أرجع JSON بهذا التنسيق:
|
||||
{
|
||||
"action": "...",
|
||||
"params": {...},
|
||||
"confirmation": "نص قصير تأكيد بالعامية الأردنية أو الفصحى المبسطة"
|
||||
}
|
||||
PROMPT;
|
||||
|
||||
$payload = [
|
||||
'contents' => [
|
||||
['parts' => [['text' => $text]]]
|
||||
],
|
||||
'systemInstruction' => [
|
||||
'parts' => [['text' => $systemPrompt]]
|
||||
],
|
||||
'generationConfig' => [
|
||||
'responseMimeType' => 'application/json',
|
||||
'temperature' => 0.2
|
||||
]
|
||||
];
|
||||
|
||||
// Determine appropriate endpoint based on env
|
||||
$model = env('GEMINI_MODEL', 'gemini-1.5-flash');
|
||||
$url = "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key={$apiKey}";
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => json_encode($payload),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json']
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode !== 200) {
|
||||
error_log("Gemini Error: $response | $error");
|
||||
json_error('فشل في تحليل الأمر', 500);
|
||||
}
|
||||
|
||||
$respData = json_decode($response, true);
|
||||
if (!isset($respData['candidates'][0]['content']['parts'][0]['text'])) {
|
||||
json_error('رد غير متوقع من AI', 500);
|
||||
}
|
||||
|
||||
$jsonText = $respData['candidates'][0]['content']['parts'][0]['text'];
|
||||
$parsed = json_decode($jsonText, true);
|
||||
|
||||
if (!$parsed) {
|
||||
json_error('فشل في تحليل الرد كـ JSON', 500);
|
||||
}
|
||||
|
||||
json_success($parsed, 'تم تحليل الأمر');
|
||||
Reference in New Issue
Block a user