168 lines
6.6 KiB
PHP
168 lines
6.6 KiB
PHP
<?php
|
|
// OPUS5 — Meta-Orchestrator v3.1 (doctrine 81) — FIX sous charge
|
|
// FIX v3.1 : cache check via Redis DIRECT (pas HTTP roundtrip) = toujours <5ms même sous charge FPM saturée
|
|
// + curl_multi pour appeler endpoints internes en parallèle + fastest-wins
|
|
// + Auto-store dans cache après winner (TTL 600s)
|
|
header('Content-Type: application/json');
|
|
$t0 = microtime(true);
|
|
$R = ['ts'=>date('c'), 'source'=>'opus5-autonomous-orchestrator-v3'];
|
|
|
|
$raw = file_get_contents('php://input');
|
|
$d = json_decode($raw, true) ?: [];
|
|
$msg = (string)($d['message'] ?? '');
|
|
$session = (string)($d['session'] ?? 'auto-' . bin2hex(random_bytes(4)));
|
|
if (!$msg) { http_response_code(400); echo json_encode(['err'=>'missing_message']); exit; }
|
|
|
|
// === STEP 1 : Cache check Redis DIRECT (pas HTTP) — FIX v3.1 ===
|
|
$t_cache = microtime(true);
|
|
$cached_hit = null;
|
|
$redis = null;
|
|
try {
|
|
$redis = new Redis();
|
|
if ($redis->connect('127.0.0.1', 6379, 0.5)) {
|
|
$cache_key = 'wevia:predcache:msg:' . md5(substr($msg, 0, 200));
|
|
$cache_val = $redis->get($cache_key);
|
|
// Update stats counters
|
|
$redis->hIncrBy('wevia:predcache:stats', 'gets', 1);
|
|
if ($cache_val !== false) {
|
|
$parsed = @json_decode((string)$cache_val, true);
|
|
if ($parsed && !empty($parsed['response'])) {
|
|
$cached_hit = $parsed;
|
|
$redis->hIncrBy('wevia:predcache:stats', 'hits', 1);
|
|
}
|
|
} else {
|
|
$redis->hIncrBy('wevia:predcache:stats', 'misses', 1);
|
|
}
|
|
}
|
|
} catch (Throwable $e) { /* fallback to full path */ }
|
|
|
|
if ($cached_hit !== null) {
|
|
$R['cache'] = 'HIT';
|
|
$R['cache_check_ms'] = round((microtime(true) - $t_cache) * 1000);
|
|
$R['final_response'] = $cached_hit['response'];
|
|
$R['final_provider'] = ($cached_hit['provider'] ?? 'cached') . '-hit';
|
|
$R['total_ms'] = round((microtime(true) - $t0) * 1000);
|
|
$R['doctrine'] = '81 v3.1 — cache HIT Redis direct (sub-5ms under load)';
|
|
if ($redis) $redis->close();
|
|
echo json_encode($R, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
$R['cache'] = 'MISS';
|
|
$R['cache_check_ms'] = round((microtime(true) - $t_cache) * 1000);
|
|
|
|
// === STEP 2 : Classification rapide ===
|
|
$words = str_word_count($msg);
|
|
$ml = strtolower($msg);
|
|
$has_multi = preg_match('/\b(puis|ensuite|apres|parallele)\b/u', $ml);
|
|
$has_analysis = preg_match('/\b(analys|explique|pourquoi|detail|approfond)\b/u', $ml);
|
|
$complexity = 'simple';
|
|
if ($has_multi) $complexity = 'multi-step';
|
|
elseif ($has_analysis || $words > 20) $complexity = 'deep';
|
|
$R['classification'] = ['words'=>$words, 'complexity'=>$complexity];
|
|
|
|
// === STEP 3 : Async background (fire-and-forget) — budget 0ms ===
|
|
$async_launched = [];
|
|
$logf = "/var/log/weval/orch-v3-$session.log";
|
|
if ($complexity === 'multi-step') {
|
|
@shell_exec("timeout 30 /opt/weval-ops/top-ia/dialectical.sh " . escapeshellarg(substr($msg,0,500)) . " >> $logf 2>&1 &");
|
|
$async_launched[] = 'dialectical';
|
|
}
|
|
if ($complexity === 'deep') {
|
|
@shell_exec("timeout 30 /opt/weval-ops/top-ia/cot_tree.sh " . escapeshellarg(substr($msg,0,500)) . " >> $logf 2>&1 &");
|
|
$async_launched[] = 'cot_tree';
|
|
}
|
|
@shell_exec("timeout 15 /opt/weval-ops/top-ia/memory_store.sh " . escapeshellarg($session) . " " . escapeshellarg(substr($msg,0,200)) . " >> $logf 2>&1 &");
|
|
$async_launched[] = 'memory_store';
|
|
$R['async_launched'] = $async_launched;
|
|
|
|
// === STEP 4 : curl_multi parallel providers (fastest wins) ===
|
|
$t_grid = microtime(true);
|
|
$PROVIDERS = [
|
|
['id'=>'safe_wrapper', 'url'=>'http://127.0.0.1/api/opus5-weval-ia-fast-safe.php'],
|
|
['id'=>'dispatch_proxy', 'url'=>'http://127.0.0.1/api/wevia-master-dispatch.php']
|
|
];
|
|
|
|
$mh = curl_multi_init();
|
|
$handles = [];
|
|
$payload = json_encode(['message'=>$msg, 'session'=>$session]);
|
|
foreach ($PROVIDERS as $p) {
|
|
$ch = curl_init($p['url']);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST=>true,
|
|
CURLOPT_POSTFIELDS=>$payload,
|
|
CURLOPT_HTTPHEADER=>['Content-Type: application/json'],
|
|
CURLOPT_RETURNTRANSFER=>true,
|
|
CURLOPT_TIMEOUT=>15,
|
|
CURLOPT_CONNECTTIMEOUT=>2
|
|
]);
|
|
curl_multi_add_handle($mh, $ch);
|
|
$handles[] = ['ch'=>$ch, 'provider'=>$p, 'started'=>microtime(true)];
|
|
}
|
|
|
|
$running = null;
|
|
do { curl_multi_exec($mh, $running); curl_multi_select($mh, 0.1); } while ($running > 0);
|
|
|
|
$grid_results = [];
|
|
$winner = null;
|
|
foreach ($handles as $h) {
|
|
$body = curl_multi_getcontent($h['ch']);
|
|
$http = curl_getinfo($h['ch'], CURLINFO_HTTP_CODE);
|
|
$dp = @json_decode((string)$body, true);
|
|
$resp_text = $dp['response'] ?? $dp['content'] ?? $dp['final_response'] ?? '';
|
|
$grid_results[] = [
|
|
'id' => $h['provider']['id'],
|
|
'http' => $http,
|
|
'ms' => round((microtime(true) - $h['started']) * 1000),
|
|
'len' => strlen((string)$resp_text),
|
|
'provider_engine' => $dp['provider'] ?? '?'
|
|
];
|
|
if (!$winner && $http === 200 && strlen((string)$resp_text) > 10) {
|
|
$winner = [
|
|
'provider' => $h['provider']['id'],
|
|
'engine' => $dp['provider'] ?? '?',
|
|
'response' => $resp_text,
|
|
'ms' => round((microtime(true) - $h['started']) * 1000)
|
|
];
|
|
}
|
|
curl_multi_remove_handle($mh, $h['ch']);
|
|
curl_close($h['ch']);
|
|
}
|
|
curl_multi_close($mh);
|
|
|
|
$R['grid_results'] = $grid_results;
|
|
$R['grid_ms'] = round((microtime(true) - $t_grid) * 1000);
|
|
|
|
// === STEP 5 : Store cache Redis DIRECT après winner — FIX v3.1 ===
|
|
if ($winner) {
|
|
$R['final_response'] = $winner['response'];
|
|
$R['final_provider'] = $winner['engine'];
|
|
$R['winner'] = $winner['provider'];
|
|
|
|
// Cache store Redis direct (no HTTP roundtrip)
|
|
if ($redis) {
|
|
try {
|
|
$cache_key = 'wevia:predcache:msg:' . md5(substr($msg, 0, 200));
|
|
$cache_data = json_encode([
|
|
'response' => $winner['response'],
|
|
'provider' => $winner['engine'],
|
|
'cached_at' => date('c')
|
|
], JSON_UNESCAPED_UNICODE);
|
|
$redis->setex($cache_key, 600, $cache_data); // 10 min TTL
|
|
$redis->hIncrBy('wevia:predcache:patterns', substr($msg, 0, 200), 1);
|
|
$redis->expire('wevia:predcache:patterns', 604800); // 7 days
|
|
$R['cached_for_future'] = true;
|
|
} catch (Throwable $e) { $R['cache_store_err'] = substr($e->getMessage(), 0, 100); }
|
|
}
|
|
} else {
|
|
$R['final_response'] = '[orchestrator v3.1] Tous les providers ont timeout.';
|
|
$R['final_provider'] = 'none';
|
|
}
|
|
|
|
if ($redis) $redis->close();
|
|
|
|
$R['total_ms'] = round((microtime(true) - $t0) * 1000);
|
|
$R['session'] = $session;
|
|
$R['doctrine'] = '81 v3.1 — cache Redis direct (no HTTP roundtrip) + curl_multi parallel';
|
|
|
|
echo json_encode($R, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE);
|