Files
html/api/opus5-autonomous-orchestrator-v3.php
2026-04-17 18:19:00 +02:00

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);