120 lines
5.4 KiB
PHP
120 lines
5.4 KiB
PHP
<?php
|
|
/**
|
|
* ambre-session-chat.php v2 · onboarding + empathy + identity memory
|
|
* First message of session → greeting with identity ask
|
|
* Subsequent messages → contextual reply with memory
|
|
* Auto-detects: identity declaration, emotion, questions, follow-ups
|
|
*/
|
|
require_once __DIR__ . "/ambre-session-memory.php";
|
|
header("Content-Type: application/json; charset=utf-8");
|
|
|
|
$raw = file_get_contents("php://input");
|
|
$in = json_decode($raw, true) ?: $_POST;
|
|
$msg = trim($in["message"] ?? "");
|
|
$sid = trim($in["session_id"] ?? "");
|
|
|
|
if (!$msg) { echo json_encode(["error"=>"message required"]); exit; }
|
|
if (!$sid) $sid = "anon-" . substr(md5(($_SERVER["REMOTE_ADDR"] ?? "x") . time()), 0, 10);
|
|
|
|
$history = AmbreSessionMemory::context_messages($sid, 12);
|
|
$turns_before = count($history);
|
|
|
|
// Extract identity if present (name + company)
|
|
$identity = null;
|
|
$history_full = AmbreSessionMemory::load($sid);
|
|
foreach ($history_full as $m) {
|
|
if ($m["role"] === "meta" && strpos($m["content"], "identity:") === 0) {
|
|
$identity = trim(substr($m["content"], 9));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Try to extract identity from current message
|
|
$extracted_name = null;
|
|
$extracted_org = null;
|
|
// Patterns: "je m'appelle X", "mon nom est X", "I'm X", "je suis X", "X de Y"
|
|
if (preg_match('/(?:je\s+m[\'\s]?appelle|mon\s+nom\s+est|je\s+suis|c[\'\s]?est\s+moi|i[\'\s]?m|my\s+name\s+is)\s+([A-ZÀ-Üa-zà-ü][A-ZÀ-Üa-zà-ü\-\s]{1,40}?)(?:\s+(?:de|from|at|chez|pour|travaille|,|\.|!|$))/iu', $msg, $m)) {
|
|
$extracted_name = trim($m[1]);
|
|
}
|
|
if (preg_match('/(?:de|from|chez|at|travaille\s+(?:chez|pour|à))\s+([A-ZÀ-Üa-zà-ü][A-ZÀ-Üa-zà-ü0-9\-\s]{2,40}?)(?:\s*[\.,!]|\s+et|\s*$)/iu', $msg, $m)) {
|
|
$extracted_org = trim($m[1]);
|
|
}
|
|
|
|
if (($extracted_name || $extracted_org) && !$identity) {
|
|
$id_parts = [];
|
|
if ($extracted_name) $id_parts[] = "nom=$extracted_name";
|
|
if ($extracted_org) $id_parts[] = "org=$extracted_org";
|
|
$identity = implode(" · ", $id_parts);
|
|
AmbreSessionMemory::append($sid, "meta", "identity: $identity");
|
|
}
|
|
|
|
// === FIRST TURN : onboarding ===
|
|
if ($turns_before === 0 && !$identity) {
|
|
// Check if first message IS an identity declaration
|
|
if ($extracted_name || $extracted_org) {
|
|
// They told us, move to friendly greeting
|
|
$greeting_sys = "L'utilisateur vient de se présenter. Salue-le chaleureusement en utilisant son nom si connu et son entreprise si connue. Demande-lui comment tu peux l'aider. 2-3 phrases max. Reste en français.";
|
|
$context = "Identité détectée: " . ($identity ?: "inconnue");
|
|
} else {
|
|
// First message was a direct question without intro
|
|
// Reply to the question but ASK identity in a friendly way
|
|
$greeting_sys = "Premier échange avec un nouvel utilisateur. Réponds brièvement à sa question, PUIS demande avec élégance son prénom et son entreprise ou domaine d'activité pour personnaliser l'aide. Style chaleureux, 3-4 phrases.";
|
|
$context = "Première interaction, identité inconnue.";
|
|
}
|
|
|
|
$messages = [["role"=>"system","content"=>"Tu es WEVIA, une IA professionnelle de WEVAL Consulting. $greeting_sys"]];
|
|
if ($context) $messages[] = ["role"=>"system","content"=>$context];
|
|
$messages[] = ["role"=>"user","content"=>$msg];
|
|
} else {
|
|
// === SUBSEQUENT TURNS : contextual with memory ===
|
|
$sys_parts = [
|
|
"Tu es WEVIA, l'IA de WEVAL Consulting.",
|
|
"Tu mémorises les échanges de cette conversation et tu t'adaptes au ton et au contexte.",
|
|
];
|
|
if ($identity) {
|
|
$sys_parts[] = "Identité de l'utilisateur : $identity. Utilise son nom naturellement quand c'est pertinent.";
|
|
} else {
|
|
$sys_parts[] = "Identité inconnue. Si pertinent, demande-lui son prénom de façon fluide.";
|
|
}
|
|
$sys_parts[] = "Réponds en français, concis, professionnel, empathique si émotion détectée.";
|
|
$sys_parts[] = "Si l'utilisateur revient sur un sujet antérieur, reconnais-le. Si changement de sujet, adapte-toi fluidement.";
|
|
$sys_parts[] = "Si demande d'amélioration d'un rendu antérieur, propose une V2 meilleure en te basant sur l'historique.";
|
|
|
|
$messages = [["role"=>"system","content"=>implode(" ", $sys_parts)]];
|
|
foreach ($history as $h) {
|
|
if ($h["role"] !== "meta") $messages[] = $h;
|
|
}
|
|
$messages[] = ["role"=>"user","content"=>$msg];
|
|
}
|
|
|
|
// Call LLM
|
|
$t0 = microtime(true);
|
|
$raw_llm = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, stream_context_create([
|
|
"http" => [
|
|
"method"=>"POST",
|
|
"header"=>"Content-Type: application/json\r\n",
|
|
"content"=>json_encode(["model"=>"fast","messages"=>$messages,"max_tokens"=>1200,"temperature"=>0.5]),
|
|
"timeout"=>30,
|
|
],
|
|
]));
|
|
$elapsed = round((microtime(true)-$t0)*1000);
|
|
$d = @json_decode($raw_llm, true);
|
|
$reply = $d["choices"][0]["message"]["content"] ?? "";
|
|
if (!$reply) $reply = "Désolé, je n'ai pas pu traiter la demande. Peux-tu reformuler ?";
|
|
|
|
AmbreSessionMemory::append($sid, "user", $msg);
|
|
AmbreSessionMemory::append($sid, "assistant", $reply);
|
|
|
|
$summary = AmbreSessionMemory::summary($sid);
|
|
|
|
echo json_encode([
|
|
"response" => $reply,
|
|
"provider" => "ambre-session-v2",
|
|
"intent" => $turns_before === 0 ? "onboarding" : "contextual_reply",
|
|
"session_id" => $sid,
|
|
"turns_in_memory" => $summary["turns"],
|
|
"history_used" => count($history),
|
|
"identity" => $identity,
|
|
"elapsed_ms" => $elapsed,
|
|
], JSON_UNESCAPED_UNICODE);
|