Files
html/api/wevia-memory-bridge.php
2026-04-23 21:35:02 +02:00

114 lines
3.7 KiB
PHP

<?php
/*
WEVIA MEMORY BRIDGE · Doctrine 141
Opus · 23avr 21h35
Middleware léger pour doter tout chatbot interne de mémoire persistante
Redis DB 5 + Qdrant long-term, via un appel vers wevia-chat-memory.php
Usage:
require_once __DIR__ . '/wevia-memory-bridge.php';
$history = wevia_mem_load('claw-code', $user_id); // charge history
// ... logique métier du chatbot ...
wevia_mem_save('claw-code', $user_id, $message, $response); // save
- Redis DB 5 key format: chatmem:<chat_id>:<user_id>
- 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