: - Scope internal = unlimited (no TTL) - Scope public = TTL 24h - Fail-safe: si Redis down, continue sans mémoire (pas de crash) */ if (!function_exists('wevia_mem_load')) { function wevia_mem_user_id(): string { // Priorité: JWT cookie > session cookie > IP+UA hash > anon $uid = $_COOKIE['weval_chat_session'] ?? ''; if (!$uid) { $ip = $_SERVER['REMOTE_ADDR'] ?? ''; $ua = $_SERVER['HTTP_USER_AGENT'] ?? ''; $uid = $ip ? 'anon-' . substr(md5($ip . $ua), 0, 12) : 'anon-default'; } return $uid; } function wevia_mem_redis(): ?Redis { static $r = null; if ($r === null) { try { $r = new Redis(); $r->connect('127.0.0.1', 6379, 1.5); $r->select(5); } catch (Throwable $e) { $r = false; } } return $r ?: null; } function wevia_mem_key(string $chat_id, string $user_id): string { return "chatmem:" . preg_replace('/[^a-z0-9_-]/i', '_', $chat_id) . ":" . $user_id; } function wevia_mem_load(string $chat_id, ?string $user_id = null, int $max = 20): array { $user_id = $user_id ?? wevia_mem_user_id(); $r = wevia_mem_redis(); if (!$r) return []; try { $raw = $r->get(wevia_mem_key($chat_id, $user_id)); if (!$raw) return []; $arr = json_decode($raw, true) ?: []; return array_slice($arr, -$max); } catch (Throwable $e) { return []; } } function wevia_mem_save(string $chat_id, ?string $user_id, string $message, string $response, string $scope = 'internal'): bool { $user_id = $user_id ?? wevia_mem_user_id(); $r = wevia_mem_redis(); if (!$r) return false; try { $k = wevia_mem_key($chat_id, $user_id); $raw = $r->get($k); $arr = $raw ? (json_decode($raw, true) ?: []) : []; $arr[] = ['role' => 'user', 'content' => substr($message, 0, 4000), 'ts' => date('c')]; $arr[] = ['role' => 'assistant', 'content' => substr($response, 0, 4000), 'ts' => date('c')]; // Cap à 500 messages $arr = array_slice($arr, -500); $r->set($k, json_encode($arr)); // TTL: internal = none (persistent), public = 24h if ($scope === 'public') { $r->expire($k, 86400); } return true; } catch (Throwable $e) { return false; } } function wevia_mem_context(string $chat_id, ?string $user_id = null, int $max = 10): string { // Retourne les N derniers messages comme contexte injecté dans prompt $history = wevia_mem_load($chat_id, $user_id, $max); if (!$history) return ''; $out = "=== Conversation history ===\n"; foreach ($history as $msg) { $role = $msg['role'] === 'user' ? 'User' : 'Assistant'; $content = substr($msg['content'] ?? '', 0, 500); $out .= "$role: $content\n"; } return $out; } function wevia_mem_stats(string $chat_id, ?string $user_id = null): array { $history = wevia_mem_load($chat_id, $user_id, 10000); return [ 'messages' => count($history), 'chat_id' => $chat_id, 'user_id' => $user_id ?? wevia_mem_user_id(), 'first_ts' => $history[0]['ts'] ?? null, 'last_ts' => end($history)['ts'] ?? null ]; } } // end if !function_exists