callGemini($prompt, SMS_GEMINI_MODEL); } catch (Throwable $e) { error_log('[sms/gemini] استثناء: ' . $e->getMessage()); return null; } if (!is_array($out) || !isset($out['intent'])) { return null; } // ‏لا نثق بشكل الرد: النموذج قد يرد بأنواع مختلفة عن المطلوب. $str = static fn($v) => (is_string($v) && trim($v) !== '' && strtolower(trim($v)) !== 'null') ? mb_substr(trim($v), 0, 150) : null; return [ 'intent' => $out['intent'] === 'ride' ? 'ride' : 'other', 'start' => $str($out['start'] ?? null), 'end' => $str($out['end'] ?? null), 'note' => $str($out['note'] ?? null), 'confidence' => is_numeric($out['confidence'] ?? null) ? max(0.0, min(1.0, (float) $out['confidence'])) : 0.0, ]; } /** * ‏عتبة الثقة. تحت هذا الحد نسأل الراكب بدل أن نرسل له سائقاً إلى مكان * ‏خمّنّاه — رسالة استيضاح تكلّف ثوانٍ، وسائق في المكان الخطأ يكلّف * ‏رحلة ضائعة وثقة مفقودة. */ const SMS_GEMINI_MIN_CONFIDENCE = 0.55; /** * ‏المسار الكامل بطبقتيه: القواعد ثم جيميني. * * ‏نفس عقد `smsParseRideRequest` مع حقل `via` ليُعرف مصدر الفهم — بلا * ‏هذا الحقل لن نعرف لاحقاً أي الطبقتين تحمل العبء، ولا هل يستحق * ‏جيميني كلفته. */ function smsParseRideRequestSmart(PDO $con, string $body): array { // ── الطبقة الأولى: القواعد ────────────────────────────── $rule = smsParseRideRequest($con, $body); if ($rule['ok']) { $rule['via'] = 'rules'; return $rule; } // ── الطبقة الثانية: جيميني ────────────────────────────── $ai = smsGeminiExtract($body); if (!$ai) { return $rule; // ‏لا جيميني ⇐ نرد بنتيجة القواعد كما هي. } if ($ai['intent'] !== 'ride') { return ['ok' => false, 'reason' => 'not_ride', 'via' => 'gemini']; } if ($ai['confidence'] < SMS_GEMINI_MIN_CONFIDENCE) { return ['ok' => false, 'reason' => 'low_confidence', 'via' => 'gemini']; } if (!$ai['start'] || !$ai['end']) { return [ 'ok' => false, 'reason' => 'incomplete', 'via' => 'gemini', 'missing' => !$ai['start'] ? 'نقطة الانطلاق' : 'الوجهة', ]; } // ‏الأسماء التي فهمها النموذج تمرّ على مطابِق المعالم نفسه. $start = smsResolvePoint($con, $ai['start']); $end = smsResolvePoint($con, $ai['end']); if (!$start || !$end) { return [ 'ok' => false, 'reason' => 'place', 'via' => 'gemini', 'raw_start' => $ai['start'], 'raw_end' => $ai['end'], 'missing' => !$start ? $ai['start'] : $ai['end'], ]; } return [ 'ok' => true, 'via' => 'gemini', 'raw_start' => $ai['start'], 'raw_end' => $ai['end'], 'note' => $ai['note'], 'start' => $start, 'end' => $end, ]; }