17avr-arena-sovereign-fix-6sigma

This commit is contained in:
opus
2026-04-17 17:52:49 +02:00
parent 87568f00df
commit a48c05a3f6
4 changed files with 316 additions and 102 deletions

View File

@@ -0,0 +1,140 @@
<?php
// OPUS5 — Meta-Orchestrator v3 (doctrine 78)
// v2 faisait async heavy + sync critical. v3 ajoute :
// - curl_multi pour appeler 3 providers en PARALLÈLE + fastest-wins
// - Cache prédictif checked FIRST (hit = instant return)
// - Fallback chain intelligent si tous providers timeout
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 prédictif FIRST (0-20ms) ===
$t_cache = microtime(true);
$ch = curl_init('http://127.0.0.1/api/opus5-predictive-cache.php?action=get&key=' . urlencode(substr($msg, 0, 200)));
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>2]);
$cache_resp = curl_exec($ch);
curl_close($ch);
$cached = @json_decode((string)$cache_resp, true);
if ($cached && ($cached['cache'] ?? '') === 'HIT' && !empty($cached['value']['response'])) {
$R['cache'] = 'HIT';
$R['final_response'] = $cached['value']['response'];
$R['final_provider'] = $cached['value']['provider'] ?? 'cached';
$R['total_ms'] = round((microtime(true) - $t0) * 1000);
$R['doctrine'] = '78 — meta-orch v3 cache HIT (instant)';
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) ===
$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 : Grid parallel via curl_multi (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 dans cache prédictif pour futur (async) ===
if ($winner) {
$R['final_response'] = $winner['response'];
$R['final_provider'] = $winner['engine'];
$R['winner'] = $winner['provider'];
// Store in predictive cache
$cache_payload = json_encode(['response'=>$winner['response'], 'provider'=>$winner['engine']]);
$ch = curl_init('http://127.0.0.1/api/opus5-predictive-cache.php?action=set&key=' . urlencode(substr($msg, 0, 200)) . '&ttl=600');
curl_setopt_array($ch, [CURLOPT_POST=>true, CURLOPT_POSTFIELDS=>$cache_payload, CURLOPT_HTTPHEADER=>['Content-Type: application/json'], CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>2]);
curl_exec($ch);
curl_close($ch);
$R['cached_for_future'] = true;
} else {
$R['final_response'] = '[orchestrator v3] Tous les providers ont timeout ou retourné vide.';
$R['final_provider'] = 'none';
}
$R['total_ms'] = round((microtime(true) - $t0) * 1000);
$R['session'] = $session;
$R['doctrine'] = '78 — meta-orch v3 (cache-first + curl_multi parallel + cache-after)';
echo json_encode($R, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE);

View File

@@ -1,162 +1,181 @@
<?php
// OPUS5 — Grid GPU distribué (doctrine 77)
// Shard un gros prompt sur plusieurs providers gratuits en parallèle via curl_multi
// Providers: Ollama local (toujours UP), puis Groq/Cerebras/SambaNova/HF Inference (si keys)
// Mode adaptatif : utilise seulement providers UP
// OPUS5 — Grid GPU v2 (doctrine 77) — MULTI-ENDPOINT PARALLEL via curl_multi
// Utilise les 3 endpoints LLM internes existants en parallèle pour ~3x throughput
// Endpoints: sovereign-proxy (SSE), weval-ia-fast-safe, opus5-autonomous-orchestrator
header('Content-Type: application/json');
$R = ['ts'=>date('c'), 'source'=>'opus5-gpu-grid'];
$R = ['ts'=>date('c'), 'source'=>'opus5-gpu-grid-v2'];
$raw = file_get_contents('php://input');
$d = json_decode($raw, true) ?: [];
$action = $_GET['action'] ?? ($d['action'] ?? 'health');
// PROVIDERS — configuration
$PROVIDERS = [
[
'id' => 'ollama_local',
'url' => 'http://127.0.0.1:11434/api/generate',
'model' => 'qwen3:4b',
'type' => 'ollama',
'header' => ['Content-Type: application/json'],
'weight' => 1
'id' => 'safe_wrapper',
'url' => 'http://127.0.0.1/api/opus5-weval-ia-fast-safe.php',
'type' => 'internal'
],
[
'id' => 'sovereign_cascade',
'url' => 'http://127.0.0.1/api/opus5-weval-ia-fast-safe.php',
'type' => 'internal',
'header' => ['Content-Type: application/json'],
'weight' => 1
'id' => 'meta_orchestrator',
'url' => 'http://127.0.0.1/api/opus5-autonomous-orchestrator.php',
'type' => 'internal_orch'
],
[
'id' => 'dispatch_proxy',
'url' => 'http://127.0.0.1/api/wevia-master-dispatch.php',
'type' => 'internal_disp'
]
];
function call_provider($p, $prompt, $max_tokens = 200) {
if ($p['type'] === 'ollama') {
$payload = json_encode(['model'=>$p['model'], 'prompt'=>$prompt, 'stream'=>false, 'options'=>['num_predict'=>$max_tokens]]);
} elseif ($p['type'] === 'internal') {
$payload = json_encode(['message'=>$prompt]);
} else {
$payload = json_encode(['prompt'=>$prompt]);
}
$ch = curl_init($p['url']);
function build_ch($url, $payload, $timeout = 20) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => $p['header'],
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 25,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => 0
]);
return $ch;
}
function parse_provider_response($p, $body) {
function parse_response($body) {
$d = @json_decode((string)$body, true);
if ($p['type'] === 'ollama') return $d['response'] ?? '';
if ($p['type'] === 'internal') return $d['response'] ?? $d['content'] ?? '';
return is_string($body) ? substr($body, 0, 500) : '';
if (!$d) return ['text' => substr((string)$body, 0, 500), 'provider' => '?'];
return [
'text' => $d['response'] ?? $d['content'] ?? $d['final_response'] ?? substr((string)$body, 0, 300),
'provider' => $d['provider'] ?? $d['final_provider'] ?? '?'
];
}
if ($action === 'health') {
// Quick health check each provider
$results = [];
$mh = curl_multi_init();
$handles = [];
$t0 = microtime(true);
foreach ($PROVIDERS as $p) {
$t0 = microtime(true);
if ($p['type'] === 'ollama') {
$ch = curl_init(str_replace('/api/generate', '/api/tags', $p['url']));
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>3]);
curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
} else {
$ch = curl_init($p['url']);
curl_setopt_array($ch, [CURLOPT_POST=>true, CURLOPT_POSTFIELDS=>'{"message":"ping"}', CURLOPT_HTTPHEADER=>$p['header'], CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>3, CURLOPT_SSL_VERIFYPEER=>false]);
curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
}
$results[] = [
'id' => $p['id'],
'url' => $p['url'],
'up' => $http === 200,
'ms' => round((microtime(true) - $t0) * 1000)
];
$ch = build_ch($p['url'], json_encode(['message'=>'ping']), 5);
curl_multi_add_handle($mh, $ch);
$handles[] = ['ch'=>$ch, 'provider'=>$p];
}
$running = null;
do { curl_multi_exec($mh, $running); curl_multi_select($mh, 0.1); } while ($running > 0);
$results = [];
foreach ($handles as $h) {
$http = curl_getinfo($h['ch'], CURLINFO_HTTP_CODE);
$results[] = ['id'=>$h['provider']['id'], 'up'=>$http===200, 'http'=>$http];
curl_multi_remove_handle($mh, $h['ch']);
curl_close($h['ch']);
}
curl_multi_close($mh);
$R['providers'] = $results;
$R['up_count'] = count(array_filter($results, function($r){ return $r['up']; }));
$R['total_count'] = count($results);
$R['up_count'] = count(array_filter($results, function($r){return $r['up'];}));
$R['parallel_ms'] = round((microtime(true)-$t0)*1000);
echo json_encode($R, JSON_PRETTY_PRINT);
exit;
}
if ($action === 'shard') {
// Shard un gros prompt en N morceaux + dispatch parallel via curl_multi
$text = (string)($d['text'] ?? '');
$instruction = (string)($d['instruction'] ?? 'Résume ce passage en 2 phrases');
if (!$text) { http_response_code(400); echo json_encode(['err'=>'no_text']); exit; }
if ($action === 'parallel_query') {
// Send same query to all providers in parallel, return first valid response + all for comparison
$query = (string)($d['query'] ?? '');
if (!$query) { http_response_code(400); echo json_encode(['err'=>'no_query']); exit; }
// Chunking simple : split par ~500 chars
$chunks = [];
$chunk_size = 500;
for ($i = 0; $i < strlen($text); $i += $chunk_size) {
$chunks[] = substr($text, $i, $chunk_size);
}
$R['chunks_count'] = count($chunks);
if (count($chunks) > 10) { $chunks = array_slice($chunks, 0, 10); } // cap 10 shards
// Dispatch round-robin across providers
$t0 = microtime(true);
$mh = curl_multi_init();
$handles = [];
foreach ($chunks as $idx => $chunk) {
$p = $PROVIDERS[$idx % count($PROVIDERS)];
$full_prompt = $instruction . "\n\nPassage:\n" . $chunk . "\n\nRéponse:";
$ch = call_provider($p, $full_prompt);
$t0 = microtime(true);
foreach ($PROVIDERS as $p) {
$ch = build_ch($p['url'], json_encode(['message'=>$query]), 25);
curl_multi_add_handle($mh, $ch);
$handles[] = ['ch'=>$ch, 'provider'=>$p, 'chunk_idx'=>$idx];
$handles[] = ['ch'=>$ch, 'provider'=>$p, 'started'=>microtime(true)];
}
// Run parallel
$running = null;
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh, 0.1);
} while ($running > 0);
do { curl_multi_exec($mh, $running); curl_multi_select($mh, 0.1); } while ($running > 0);
// Collect results
$shard_results = [];
$results = [];
foreach ($handles as $h) {
$body = curl_multi_getcontent($h['ch']);
$http = curl_getinfo($h['ch'], CURLINFO_HTTP_CODE);
$text_response = parse_provider_response($h['provider'], $body);
$shard_results[] = [
'chunk_idx' => $h['chunk_idx'],
'provider' => $h['provider']['id'],
$parsed = parse_response($body);
$results[] = [
'provider_id' => $h['provider']['id'],
'http' => $http,
'response_preview' => substr($text_response, 0, 300),
'response_len' => strlen($text_response)
'ms' => round((microtime(true) - $h['started']) * 1000),
'response_preview' => substr($parsed['text'] ?? '', 0, 300),
'response_len' => strlen($parsed['text'] ?? ''),
'engine' => $parsed['provider'] ?? '?'
];
curl_multi_remove_handle($mh, $h['ch']);
curl_close($h['ch']);
}
curl_multi_close($mh);
$R['shards'] = $shard_results;
$R['total_ms'] = round((microtime(true) - $t0) * 1000);
$R['providers_used'] = array_values(array_unique(array_column($shard_results, 'provider')));
// Pick winner: fastest with non-empty response
$valid = array_filter($results, function($r){ return $r['http'] === 200 && $r['response_len'] > 10; });
usort($valid, function($a,$b){ return $a['ms'] - $b['ms']; });
$R['winner'] = !empty($valid) ? $valid[0] : null;
$R['all_results'] = $results;
$R['total_wall_ms'] = round((microtime(true) - $t0) * 1000);
$R['speedup'] = count($results) > 0 ? round(array_sum(array_column($results, 'ms')) / $R['total_wall_ms'], 1) . 'x' : 'N/A';
$R['doctrine'] = '77 — grid GPU parallel (curl_multi multi-endpoint + fastest-wins)';
// Merge : concaténer responses dans ordre chunk_idx
usort($shard_results, function($a,$b){ return $a['chunk_idx'] - $b['chunk_idx']; });
$merged = [];
foreach ($shard_results as $s) {
if ($s['response_len'] > 10) $merged[] = $s['response_preview'];
echo json_encode($R, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE);
exit;
}
if ($action === 'shard_text') {
// Split large text into chunks, process each on different provider in parallel
$text = (string)($d['text'] ?? '');
$instruction = (string)($d['instruction'] ?? 'Résume');
if (!$text) { http_response_code(400); echo json_encode(['err'=>'no_text']); exit; }
$chunk_size = 600;
$chunks = [];
for ($i = 0; $i < strlen($text); $i += $chunk_size) {
$chunks[] = substr($text, $i, $chunk_size);
if (count($chunks) >= 6) break; // cap 6 shards
}
$R['merged_preview'] = substr(implode("\n---\n", $merged), 0, 1500);
$R['doctrine'] = '77 — grid GPU distribué (curl_multi shard round-robin multi-provider)';
$t0 = microtime(true);
$mh = curl_multi_init();
$handles = [];
foreach ($chunks as $idx => $chunk) {
$p = $PROVIDERS[$idx % count($PROVIDERS)];
$full = "$instruction\n\n$chunk";
$ch = build_ch($p['url'], json_encode(['message'=>$full]), 20);
curl_multi_add_handle($mh, $ch);
$handles[] = ['ch'=>$ch, 'provider'=>$p, 'idx'=>$idx];
}
$running = null;
do { curl_multi_exec($mh, $running); curl_multi_select($mh, 0.1); } while ($running > 0);
$shards = [];
foreach ($handles as $h) {
$body = curl_multi_getcontent($h['ch']);
$parsed = parse_response($body);
$shards[] = [
'idx' => $h['idx'],
'provider' => $h['provider']['id'],
'response_preview' => substr($parsed['text'] ?? '', 0, 200),
'response_len' => strlen($parsed['text'] ?? '')
];
curl_multi_remove_handle($mh, $h['ch']);
curl_close($h['ch']);
}
curl_multi_close($mh);
usort($shards, function($a,$b){ return $a['idx'] - $b['idx']; });
$R['chunks_count'] = count($chunks);
$R['shards'] = $shards;
$R['total_ms'] = round((microtime(true) - $t0) * 1000);
$R['merged'] = implode("\n---\n", array_column($shards, 'response_preview'));
$R['providers_used'] = array_values(array_unique(array_column($shards, 'provider')));
echo json_encode($R, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE);
exit;
}
http_response_code(400);
echo json_encode(['err'=>'unknown_action', 'available'=>['health','shard']]);
echo json_encode(['err'=>'unknown_action', 'available'=>['health','parallel_query','shard_text']]);

View File

@@ -295,6 +295,9 @@ if (!empty($_mam)) {
// === END OPUS_ROOT_CAUSE_GUARDS_EARLY_17AVR ===
// V26-SURGICAL normalizer prehook (Opus 17avr 17h50) — fix "combien j'ai de leads" → route correct
@require_once __DIR__ . "/wevia-nl-normalizer-prehook.php";
// === OPUS_BUSINESS_COUNT_GUARD_17AVR (natural language → SQL real) ===
if (!empty($_mam)) {
$__bm = $_mam;

View File

@@ -0,0 +1,52 @@
<?php
/**
* WEVIA NL NORMALIZER — V26-SURGICAL · Opus · 17 avril 2026 17h50
*
* Doctrine 72 (ex-71) respectée : fix chirurgical 1 ligne ajoutée dans master-api.php (pas modification)
* Doctrine 63 : non destructif, fichier séparé loadé avant OPUS_BUSINESS_COUNT_GUARD
*
* Fix : "combien j'ai de leads" avec apostrophe/espace → normalise vers "combien leads"
*
* Problème originel (ligne 322 master-api.php, auteur Opus5) :
* preg_match('/\b(?:combien|nombre)\s+(?:de\s+|d\'|of\s+)?([a-z_]+)/iu', ...)
* capture le premier token après "combien" → "j'ai" au lieu de "leads"
*
* Solution NL normalizer : normalise $_mam (message) en supprimant les intercalaires
* "j'ai/jai/j ai/nous avons/tu as/mon/mes/de mes..." → ""
*
* Tests : "combien j'ai de leads" → "combien leads" → route count_business_real OK
*/
if (!empty($_mam) && is_string($_mam)) {
$__orig = $_mam;
// Patterns à normaliser (ordre important: plus spécifique d'abord)
$__patterns = [
// Possessifs + auxiliaires entre "combien" et entité
'/\b(combien|nombre|count|total|quantit[eé])\s+(?:j[\' ]?ai|jai|nous\s+avons|tu\s+as|on\s+a|il\s+y\s+a|vous\s+avez)\s+(?:de\s+|des\s+|du\s+|d[\' ]\s*)?\b/iu' => '$1 ',
// "j'ai combien de X" → "combien X"
'/\bj[\' ]?ai\s+(combien|nombre|total)\s+(?:de\s+|des\s+|du\s+|d[\' ])?\b/iu' => '$1 ',
// "mes/mon/ma X" entre combien et entité
'/\b(combien|nombre|total|quantit[eé])\s+(?:de\s+|des\s+)?(?:mes|mon|ma|mon\s+pipeline|mes\s+actuels?)\s+\b/iu' => '$1 ',
// Articles résiduels
'/\b(combien|nombre|total)\s+(?:de\s+|des\s+|du\s+|d[\' ])\s+\b/iu' => '$1 ',
];
$__normalized = $__orig;
foreach ($__patterns as $__pat => $__rep) {
$__normalized = preg_replace($__pat, $__rep, $__normalized);
}
// Si normalisation a changé quelque chose, update $_mam pour le bloc suivant
if ($__normalized !== $__orig) {
// Log pour audit trail
@file_put_contents(
'/var/log/weval/wevia-nl-normalizer.log',
date('c') . " NORMALIZED: '$__orig' → '$__normalized'\n",
FILE_APPEND | LOCK_EX
);
$_mam = $__normalized;
// $_wm aussi (shared global)
if (isset($_wm)) $_wm = $__normalized;
}
}