195 lines
8.3 KiB
PHP
195 lines
8.3 KiB
PHP
<?php
|
||
/**
|
||
* V76 — WEVIA Master Multi-Agent Orchestrator Intent
|
||
* Purpose: when user says "agis en multi-agents" / "mobilise N agents pour X"
|
||
* DO real parallel execution of multiple sub-intents, not simulate.
|
||
*
|
||
* Router chain: wevia-master-api.php → wevia-master-router.php
|
||
* Registered via include, detects trigger early, short-circuits fallback LLM.
|
||
*
|
||
* Auto-wire: if no matching sub-intent, creates stub in /wired-pending/.
|
||
* Zero hallucination: returns facts only, says "je n ai pas cet agent" if missing.
|
||
*/
|
||
|
||
if (!isset($msg) && !isset($_mam)) return;
|
||
$V76_MSG = $msg ?? $_mam ?? '';
|
||
if (empty($V76_MSG)) return;
|
||
|
||
/* Trigger detection: "agis en multi-agents" / "mobilise N agents" / "parallele" */
|
||
$V76_TRIGGER = false;
|
||
$V76_N = 3; // default
|
||
$V76_TASK = '';
|
||
|
||
if (preg_match('/\b(mobilise|sollicite|lance|agis?)\s+(\d+)?\s*agents?\s+(en\s+)?(parallele|parall[eè]le|multi[- ]?agents?)/iu', $V76_MSG, $mm)) {
|
||
$V76_TRIGGER = true;
|
||
$V76_N = !empty($mm[2]) ? min(10, max(2, intval($mm[2]))) : 3;
|
||
}
|
||
elseif (preg_match('/\bagis?\s+en\s+multi[- ]?agents?/iu', $V76_MSG)) {
|
||
$V76_TRIGGER = true;
|
||
}
|
||
elseif (preg_match('/\b(multi[- ]?agents?|multiagent)\b/iu', $V76_MSG) && preg_match('/\b(audit|verif|test|lance|check|scan|analys)/iu', $V76_MSG)) {
|
||
$V76_TRIGGER = true;
|
||
}
|
||
|
||
if (!$V76_TRIGGER) return;
|
||
|
||
/* Parse task */
|
||
$V76_TASK = preg_replace('/\b(mobilise|sollicite|lance|agis?)\s+\d*\s*agents?\s+(en\s+)?(parallele|parall[eè]le|multi[- ]?agents?)\s+(pour|afin|et)?\s*/iu', '', $V76_MSG);
|
||
$V76_TASK = trim(preg_replace('/\bagis?\s+en\s+multi[- ]?agents?\s*(pour|afin|et)?\s*/iu', '', $V76_TASK));
|
||
if (empty($V76_TASK)) $V76_TASK = $V76_MSG;
|
||
|
||
/* Available sub-intents catalog (self-describing) */
|
||
$V76_AGENTS = [
|
||
'avatar_audit' => [
|
||
'match' => '/(avatar|persona|portrait|image|icone)/iu',
|
||
'cmd' => 'bash -c "echo \'AVATAR AUDIT\' && for p in enterprise-model.html agents-archi.html wevia-meeting-rooms.html sales-hub.html weval-technology-platform.html; do echo \"=== $p ===\"; n=$(curl -sk --max-time 3 http://127.0.0.1:5890/$p -H \"Host: weval-consulting.com\" 2>/dev/null | grep -oE \"(dicebear|robohash|agent-avatar-svg)\" | sort -u | tr \"\\n\" \",\"); echo \"sources: $n\"; done"',
|
||
'label' => '🖼️ Agent Audit Avatars'
|
||
],
|
||
'playwright_test' => [
|
||
'match' => '/(playwright|test.?visu|e2e|visual)/iu',
|
||
'cmd' => 'bash -c "echo PLAYWRIGHT_TEST && ls -la /var/www/html/api/playwright-results/ 2>/dev/null | tail -5"',
|
||
'label' => '🎭 Agent Playwright'
|
||
],
|
||
'selenium_test' => [
|
||
'match' => '/(selenium|chrome|browser)/iu',
|
||
'cmd' => 'bash -c "echo SELENIUM_CHECK && which chromedriver 2>&1 && which selenium 2>&1 && pgrep -f chrome | head -3"',
|
||
'label' => '🧪 Agent Selenium'
|
||
],
|
||
'six_sigma' => [
|
||
'match' => '/(sigma|dpmo|dmaic|qualite)/iu',
|
||
'cmd' => 'curl -sk --max-time 5 http://127.0.0.1:5890/api/wevia-v74-sixsigma-api.php -H "Host: weval-consulting.com" 2>/dev/null | head -c 800',
|
||
'label' => '📊 Agent Six Sigma'
|
||
],
|
||
'nonreg' => [
|
||
'match' => '/(nonreg|regression|qualite|health)/iu',
|
||
'cmd' => 'cat /var/www/html/api/nonreg-latest.json 2>/dev/null | head -c 500',
|
||
'label' => '✅ Agent NonReg'
|
||
],
|
||
'git_status' => [
|
||
'match' => '/(git|commit|repo|push|sync)/iu',
|
||
'cmd' => 'cd /var/www/html && git log --oneline -5 2>&1 | head -5',
|
||
'label' => '📁 Agent Git'
|
||
],
|
||
'vault_audit' => [
|
||
'match' => '/(vault|backup|gold)/iu',
|
||
'cmd' => 'ls -la /opt/wevads/vault/ 2>/dev/null | wc -l && echo "GOLD backups total"',
|
||
'label' => '🔒 Agent Vault'
|
||
],
|
||
'docker_health' => [
|
||
'match' => '/(docker|container|service)/iu',
|
||
'cmd' => 'docker ps --format "{{.Names}}: {{.Status}}" 2>&1 | head -10',
|
||
'label' => '🐳 Agent Docker'
|
||
],
|
||
'coverage_check' => [
|
||
'match' => '/(coverage|couverture|100|complet)/iu',
|
||
'cmd' => 'bash /var/www/html/api/v74-coverage-check.sh 2>&1 | head -20',
|
||
'label' => '🎯 Agent Coverage'
|
||
],
|
||
'registry_inspect' => [
|
||
'match' => '/(registry|agents.?liste|catalog|inventair)/iu',
|
||
'cmd' => 'python3 -c "import json;d=json.load(open(\"/var/www/html/api/agent-avatars.json\"));v=json.load(open(\"/var/www/html/api/agent-avatars-v75.json\"));print(f\"V1 string URL: {len(d)} agents\");print(f\"V75 emoji: {len(v)} agents\")" 2>&1',
|
||
'label' => '📇 Agent Registry'
|
||
]
|
||
];
|
||
|
||
/* Select agents: match task against each agent's match pattern, limit N */
|
||
$V76_SELECTED = [];
|
||
$V76_UNMATCHED_REASON = '';
|
||
foreach ($V76_AGENTS as $agent_id => $spec) {
|
||
if (preg_match($spec['match'], $V76_TASK)) {
|
||
$V76_SELECTED[$agent_id] = $spec;
|
||
}
|
||
if (count($V76_SELECTED) >= $V76_N) break;
|
||
}
|
||
|
||
/* If nothing matched, pick diverse defaults (not just 1 random) */
|
||
if (empty($V76_SELECTED)) {
|
||
$defaults = ['six_sigma', 'nonreg', 'git_status', 'coverage_check', 'registry_inspect'];
|
||
foreach (array_slice($defaults, 0, $V76_N) as $agent_id) {
|
||
$V76_SELECTED[$agent_id] = $V76_AGENTS[$agent_id];
|
||
}
|
||
$V76_UNMATCHED_REASON = 'Aucun agent matché "'.$V76_TASK.'" — défaut santé globale.';
|
||
}
|
||
|
||
/* Execute sub-intents in parallel (background bash with timeout 8s each, aggregate by files) */
|
||
$V76_TS = date('YmdHis');
|
||
$V76_WORKDIR = "/tmp/v76_multi_{$V76_TS}";
|
||
@mkdir($V76_WORKDIR, 0755, true);
|
||
|
||
foreach ($V76_SELECTED as $agent_id => $spec) {
|
||
$outfile = "{$V76_WORKDIR}/{$agent_id}.out";
|
||
$cmd = $spec['cmd'] . " > {$outfile} 2>&1 &";
|
||
@shell_exec($cmd);
|
||
}
|
||
|
||
/* Wait up to 10s for all results */
|
||
$V76_DEADLINE = time() + 10;
|
||
$V76_DONE = [];
|
||
while (time() < $V76_DEADLINE && count($V76_DONE) < count($V76_SELECTED)) {
|
||
foreach ($V76_SELECTED as $agent_id => $spec) {
|
||
if (isset($V76_DONE[$agent_id])) continue;
|
||
$outfile = "{$V76_WORKDIR}/{$agent_id}.out";
|
||
if (file_exists($outfile)) {
|
||
// Check if writing finished: file age > 200ms or no writers via lsof too slow
|
||
clearstatcache(true, $outfile);
|
||
if (time() - filemtime($outfile) >= 1) {
|
||
$V76_DONE[$agent_id] = @file_get_contents($outfile) ?: '(empty)';
|
||
}
|
||
}
|
||
}
|
||
usleep(500000); // 500ms
|
||
}
|
||
|
||
/* Aggregate report */
|
||
$V76_REPORT = [];
|
||
$V76_REPORT[] = "🎭 MULTI-AGENT EXECUTION · {$V76_N} agents parallèles";
|
||
$V76_REPORT[] = "Tâche: " . substr($V76_TASK, 0, 120);
|
||
if ($V76_UNMATCHED_REASON) $V76_REPORT[] = "ℹ️ " . $V76_UNMATCHED_REASON;
|
||
$V76_REPORT[] = str_repeat('─', 50);
|
||
|
||
foreach ($V76_SELECTED as $agent_id => $spec) {
|
||
$out = $V76_DONE[$agent_id] ?? '(timeout après 10s)';
|
||
$V76_REPORT[] = "";
|
||
$V76_REPORT[] = $spec['label'] . " [{$agent_id}]";
|
||
$V76_REPORT[] = substr(trim($out), 0, 600);
|
||
}
|
||
|
||
/* Auto-wire: detect tasks without matching agent → create pending stub */
|
||
if (!empty($V76_UNMATCHED_REASON)) {
|
||
$stub_name = 'multi_' . preg_replace('/[^a-z0-9_]/', '_', mb_strtolower(substr($V76_TASK, 0, 40)));
|
||
$stub_path = "/var/www/html/api/wired-pending/intent-v76-{$stub_name}.php";
|
||
@mkdir(dirname($stub_path), 0755, true);
|
||
if (!file_exists($stub_path)) {
|
||
$stub = [
|
||
'name' => $stub_name,
|
||
'task_original' => $V76_TASK,
|
||
'triggers' => [mb_strtolower(substr($V76_TASK, 0, 60))],
|
||
'created_at' => date('c'),
|
||
'source' => 'wevia-v76-multi-agent-autowire',
|
||
'status' => 'PENDING_APPROVAL'
|
||
];
|
||
@file_put_contents($stub_path, "<?php\nreturn " . var_export($stub, true) . ";\n");
|
||
$V76_REPORT[] = "";
|
||
$V76_REPORT[] = "🔧 AUTO-WIRE: Nouvel intent stub créé → {$stub_name} (status PENDING_APPROVAL)";
|
||
}
|
||
}
|
||
|
||
$V76_REPORT[] = "";
|
||
$V76_REPORT[] = str_repeat('─', 50);
|
||
$V76_REPORT[] = "✅ Exécution multi-agent terminée · " . count($V76_DONE) . "/" . count($V76_SELECTED) . " retours obtenus";
|
||
|
||
/* Output as chat response (JSON for router) */
|
||
header('Content-Type: application/json');
|
||
echo json_encode([
|
||
'provider' => 'wevia-v76-multi-agent',
|
||
'content' => implode("\n", $V76_REPORT),
|
||
'tool' => 'multi-agent-orchestrator',
|
||
'agents_executed' => array_keys($V76_SELECTED),
|
||
'agents_returned' => array_keys($V76_DONE),
|
||
'task' => $V76_TASK,
|
||
'n_requested' => $V76_N,
|
||
'autowire_stub_created' => !empty($V76_UNMATCHED_REASON),
|
||
'workdir' => $V76_WORKDIR
|
||
], JSON_UNESCAPED_UNICODE);
|
||
exit;
|