Files
html/api/opus5-predictive-cache.php
2026-04-17 17:30:03 +02:00

139 lines
5.5 KiB
PHP

<?php
// OPUS5 — Cache Prédictif Redis v1 (doctrine 68)
// Pattern: GET /api/opus5-predictive-cache.php?action=(get|set|warm|stats|train)&key=X
// Utilise Redis local port 6379 (UP confirmed)
header('Content-Type: application/json');
$R = ['ts'=>date('c'), 'source'=>'opus5-predictive-cache'];
$redis = new Redis();
try {
$redis->connect('127.0.0.1', 6379, 1.0);
if (!$redis->ping()) { http_response_code(503); echo json_encode(['err'=>'redis_down']); exit; }
} catch (Throwable $e) { http_response_code(503); echo json_encode(['err'=>'redis_err','msg'=>$e->getMessage()]); exit; }
$PREFIX = 'wevia:predcache:';
$STATS_KEY = $PREFIX . 'stats';
$PATTERNS_KEY = $PREFIX . 'patterns';
$action = $_GET['action'] ?? 'stats';
switch ($action) {
case 'get':
$k = $_GET['key'] ?? '';
if (!$k) { http_response_code(400); echo json_encode(['err'=>'no_key']); exit; }
$hash = 'msg:' . md5($k);
$v = $redis->get($PREFIX . $hash);
$redis->hIncrBy($STATS_KEY, 'gets', 1);
if ($v !== false) {
$redis->hIncrBy($STATS_KEY, 'hits', 1);
$R['cache'] = 'HIT';
$R['value'] = json_decode($v, true);
} else {
$redis->hIncrBy($STATS_KEY, 'misses', 1);
$R['cache'] = 'MISS';
}
break;
case 'set':
$k = $_GET['key'] ?? '';
$raw = file_get_contents('php://input');
$body = json_decode($raw, true);
if (!$k || !$body) { http_response_code(400); echo json_encode(['err'=>'missing']); exit; }
$ttl = (int)($_GET['ttl'] ?? 300); // default 5 min
$hash = 'msg:' . md5($k);
$redis->setex($PREFIX . $hash, $ttl, json_encode($body, JSON_UNESCAPED_UNICODE));
// Track pattern (increment count for prediction)
$redis->hIncrBy($PATTERNS_KEY, $k, 1);
$redis->expire($PATTERNS_KEY, 86400 * 7); // 7 jours rolling
$R['cache'] = 'SET';
$R['hash'] = $hash;
$R['ttl'] = $ttl;
break;
case 'warm':
// Pré-calcule les N requêtes les plus fréquentes
$top = $redis->hGetAll($PATTERNS_KEY) ?: [];
arsort($top);
$top_n = array_slice($top, 0, 20, true);
$warmed = 0;
foreach ($top_n as $msg => $count) {
if ($count < 3) continue; // skip if rare
// Call autonomous-orchestrator pour pré-calculer
$ch = curl_init('https://127.0.0.1/api/opus5-autonomous-orchestrator.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['message'=>$msg, 'session'=>'cache-warm-' . date('YmdHi')]),
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Host: weval-consulting.com'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_TIMEOUT => 10
]);
$resp = curl_exec($ch);
curl_close($ch);
$parsed = @json_decode($resp, true);
if ($parsed && isset($parsed['final_response'])) {
$hash = 'msg:' . md5($msg);
$redis->setex($PREFIX . $hash, 600, json_encode([
'response' => $parsed['final_response'],
'provider' => 'predictive-warm',
'warmed_at' => date('c'),
'original_provider' => $parsed['final_provider'] ?? '?'
], JSON_UNESCAPED_UNICODE));
$warmed++;
}
}
$R['warmed'] = $warmed;
$R['top_patterns'] = array_slice($top_n, 0, 10, true);
break;
case 'stats':
$stats = $redis->hGetAll($STATS_KEY) ?: [];
$stats['gets'] = (int)($stats['gets'] ?? 0);
$stats['hits'] = (int)($stats['hits'] ?? 0);
$stats['misses'] = (int)($stats['misses'] ?? 0);
$stats['hit_rate_pct'] = $stats['gets'] > 0 ? round(($stats['hits'] / $stats['gets']) * 100, 1) : 0;
$patterns = $redis->hGetAll($PATTERNS_KEY) ?: [];
arsort($patterns);
$R['stats'] = $stats;
$R['patterns_count'] = count($patterns);
$R['top_5_patterns'] = array_slice($patterns, 0, 5, true);
// Keys count (scanned)
$keys = [];
$it = null;
while (($scan = $redis->scan($it, $PREFIX . 'msg:*', 100)) !== false) {
$keys = array_merge($keys, $scan);
if (count($keys) > 500) break;
}
$R['cached_entries'] = count($keys);
break;
case 'train':
// Analyse les logs pour extraire patterns temporels
// TODO: parse /var/log/wevia/requests.log si exists
$log = '/var/log/wevia/requests.log';
$detected = 0;
if (file_exists($log)) {
$lines = @file($log, FILE_IGNORE_NEW_LINES);
if ($lines) {
foreach (array_slice($lines, -1000) as $l) {
if (preg_match('/"message"\s*:\s*"([^"]+)"/', $l, $m)) {
$msg = substr($m[1], 0, 200);
$redis->hIncrBy($PATTERNS_KEY, $msg, 1);
$detected++;
}
}
}
}
$R['trained_from_log'] = $detected;
break;
default:
$R['err'] = 'unknown_action';
$R['available'] = ['get', 'set', 'warm', 'stats', 'train'];
}
$redis->close();
$R['doctrine'] = '68 — predictive cache Redis (hit rate tracking + top N warm + pattern learning)';
echo json_encode($R, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE);