123 lines
5.2 KiB
PHP
123 lines
5.2 KiB
PHP
<?php
|
|
/**
|
|
* WEVIA Life AI Email Classifier
|
|
* Uses Sovereign API to classify emails with LLM
|
|
* Called by wevialife-sync.php for new emails
|
|
* Also runnable standalone to reclassify recent unclassified emails
|
|
*/
|
|
|
|
$pdo = new PDO("pgsql:host=127.0.0.1;dbname=wevia_db;options='--search_path=admin,public'", "postgres", "");
|
|
$pdo->exec("SET search_path TO admin,public");
|
|
|
|
$SOVEREIGN = 'http://127.0.0.1:4000/v1/chat/completions';
|
|
$SYSTEM_PROMPT = 'Tu es un classificateur email pour le CEO de WEVAL Consulting, cabinet de consulting tech au Maroc (emarketing, partenariats Vistex/Scaleway/Arrow/Huawei, IA souveraine, campagnes HCP pharma avec Ethica).
|
|
Contacts VIP: Olga Vanurina (Vistex), Kaouther Najar (Ethica), Cedric Jarnias, Ray Wu/Chris Cen (Huawei), Omar Belchkar/Julien Bossu (Arrow/Scaleway), Cheikh Gassama (Scaleway), Joe Golemba/Udo Hannemann (Vistex), Ella Scarlett (CX3 Ads).
|
|
Reponds UNIQUEMENT en JSON valide sans backticks markdown:
|
|
{"category":"action_required|opportunity|risk|transactional|newsletter|fyi|spam","urgency_score":1-10,"importance_score":1-10,"eisenhower":"do_first|schedule|delegate|eliminate","requires_action":true|false,"summary":"resume en 1 phrase francais"}
|
|
Regles STRICTES:
|
|
- Contacts VIP (Olga,Kaouther,Cedric,Ray,Chris,Omar,Julien,Cheikh,Joe,Udo,Ella) = urgency >= 7, do_first
|
|
- Emails partnership/proposal/contract = opportunity, urgency 6-8
|
|
- Newsletters/promos/affiliate programs/marketing = newsletter, urgency 1-2, eliminate. JAMAIS do_first pour des promos
|
|
- Notifications auto (OVHcloud renewed, server created, 2FA code) = transactional, urgency 1-3, eliminate
|
|
- Payment WARNING/suspension/impaye = risk, urgency 8-10, do_first
|
|
- Payment CONFIRMED/received/renewed = transactional, urgency 2-3, schedule
|
|
- Spam/cold outreach/recrutement = spam, urgency 1, eliminate
|
|
- Action Required de services utilises (Optizmo, CX3, partenaires actifs) = action_required, urgency 6-7';
|
|
|
|
function classify_email($from, $subject, $sovereign_url, $system_prompt) {
|
|
$user_msg = "From: $from\nSubject: $subject";
|
|
$payload = json_encode([
|
|
'messages' => [
|
|
['role' => 'system', 'content' => $system_prompt],
|
|
['role' => 'user', 'content' => $user_msg]
|
|
],
|
|
'max_tokens' => 200,
|
|
'stream' => false
|
|
]);
|
|
|
|
$ch = curl_init($sovereign_url);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
|
CURLOPT_POSTFIELDS => $payload,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 12
|
|
]);
|
|
$resp = curl_exec($ch);
|
|
$err = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
if ($err || !$resp) return null;
|
|
|
|
$data = json_decode($resp, true);
|
|
$content = $data['choices'][0]['message']['content'] ?? '';
|
|
|
|
// Clean markdown backticks if present
|
|
$content = preg_replace('/```json\s*|```\s*/', '', trim($content));
|
|
|
|
$result = json_decode($content, true);
|
|
if (!$result || !isset($result['urgency_score'])) return null;
|
|
|
|
return $result;
|
|
}
|
|
|
|
// MODE: Reclassify recent emails that have urgency_score <= 1 or category='fyi'/'unknown'
|
|
$mode = $argv[1] ?? 'reclassify';
|
|
$days = intval($argv[2] ?? 7);
|
|
$limit = intval($argv[3] ?? 50);
|
|
|
|
if ($mode === 'reclassify') {
|
|
echo date('H:i:s') . " RECLASSIFY: last $days days, max $limit emails\n";
|
|
|
|
$stmt = $pdo->query("SELECT id, from_email, from_name, subject
|
|
FROM email_classifications
|
|
WHERE (urgency_score IS NULL OR urgency_score <= 1)
|
|
AND category IN ('fyi','unknown','newsletter')
|
|
AND received_at > NOW() - INTERVAL '$days days'
|
|
AND (resolved IS NULL OR resolved = false)
|
|
ORDER BY received_at DESC
|
|
LIMIT $limit");
|
|
|
|
$emails = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
echo date('H:i:s') . " Found " . count($emails) . " emails to reclassify\n";
|
|
|
|
$update = $pdo->prepare("UPDATE email_classifications
|
|
SET category = ?, urgency_score = ?, importance_score = ?,
|
|
eisenhower_quadrant = ?, requires_action = ?, summary = ?,
|
|
classified_at = NOW()
|
|
WHERE id = ?");
|
|
|
|
$classified = 0;
|
|
$upgraded = 0;
|
|
|
|
foreach ($emails as $e) {
|
|
$from = $e['from_name'] . ' <' . $e['from_email'] . '>';
|
|
$result = classify_email($from, $e['subject'], $SOVEREIGN, $SYSTEM_PROMPT);
|
|
|
|
if (!$result) {
|
|
echo " SKIP #{$e['id']}: LLM failed\n";
|
|
continue;
|
|
}
|
|
|
|
$update->execute([
|
|
$result['category'] ?? 'fyi',
|
|
$result['urgency_score'] ?? 1,
|
|
$result['importance_score'] ?? 1,
|
|
$result['eisenhower'] ?? 'schedule',
|
|
($result['requires_action'] ?? false) ? 't' : 'f',
|
|
$result['summary'] ?? $e['subject'],
|
|
$e['id']
|
|
]);
|
|
|
|
$classified++;
|
|
if (($result['urgency_score'] ?? 1) >= 6) {
|
|
$upgraded++;
|
|
echo " URGENT #{$e['id']}: {$e['from_name']} - {$e['subject']} => urgency={$result['urgency_score']}\n";
|
|
}
|
|
|
|
usleep(200000); // 200ms between calls to not overload
|
|
}
|
|
|
|
echo date('H:i:s') . " DONE: classified=$classified, upgraded_to_urgent=$upgraded\n";
|
|
}
|