722 lines
35 KiB
PHP
722 lines
35 KiB
PHP
<?php
|
|
@require_once __DIR__ . '/wevia-sanitizer-guard.php'; // WAVE 206 sanitizer guard
|
|
|
|
$secrets=[];foreach(file("/etc/weval/secrets.env",2|4) as $l){if(strpos($l,"=")!==false){list($k,$v)=explode("=",$l,2);$secrets[trim($k)]=trim($v," \t\"'");}}
|
|
require_once __DIR__ . '/_secrets.php';
|
|
/**
|
|
* WEVAL Manager v3 - Unified AI Brain (100% Opus Target)
|
|
* Wires: dormant-loader (RAG+Memory+SelfHeal+Autoscale+Autofetch)
|
|
* + consensus-v3 (multi-provider parallel)
|
|
* + chain-executor (workflow chains)
|
|
* + code-execute (Python/PHP/JS sandbox)
|
|
* + 9 agents + 15 intents + 7 data sources
|
|
*/
|
|
header("Content-Type: application/json");
|
|
header("Access-Control-Allow-Origin: *");
|
|
header("Access-Control-Allow-Headers: Content-Type");
|
|
set_time_limit(60);
|
|
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") exit;
|
|
|
|
// === LOAD DORMANT CAPABILITIES ===
|
|
$_dormantModules = [];
|
|
$_brainDir = "/var/www/weval/wevia-ia/";
|
|
$_safeModules = ["wevia-rag-engine.php","wevia-memory-engine.php","wevia-self-healing.php","wevia-autofetch.php"];
|
|
foreach ($_safeModules as $_dm) {
|
|
if (file_exists($_brainDir . $_dm)) {
|
|
ob_start();
|
|
@include_once $_brainDir . $_dm;
|
|
ob_end_clean();
|
|
$_dormantModules[] = $_dm;
|
|
}
|
|
}
|
|
$_hasRAG = function_exists("ragSearch");
|
|
$_hasMemory = function_exists("extractEntities");
|
|
$_hasHealing = function_exists("weviaHealthCheck");
|
|
$_hasAutofetch = function_exists("weviaAutoFetchInfra");
|
|
|
|
$input = json_decode(file_get_contents("php://input"), true);
|
|
$msg = trim($input["message"] ?? "");
|
|
$history = $input["history"] ?? [];
|
|
$caps = $input["caps"] ?? [];
|
|
$use_kb = $caps["kb"] ?? true;
|
|
$use_git = $caps["git"] ?? false;
|
|
$use_infra = $caps["infra"] ?? true;
|
|
$use_sql = $caps["sql"] ?? false;
|
|
$use_crm = $caps["crm"] ?? false;
|
|
$use_deerflow = $caps["deerflow"] ?? false;
|
|
$use_nuclei = $caps["nuclei"] ?? false;
|
|
$use_playwright = $caps["playwright"] ?? false;
|
|
$use_harvester = $caps["harvester"] ?? false;
|
|
$use_coderabbit = $caps["coderabbit"] ?? false;
|
|
$use_mattermost = $caps["mattermost"] ?? false;
|
|
$use_searxng = $caps["searxng"] ?? false;
|
|
$use_toolfk = $caps["toolfk"] ?? false;
|
|
$use_pdf = $caps["pdf"] ?? false;
|
|
$use_consensus = $caps["consensus"] ?? true;
|
|
$use_chain = $caps["chain"] ?? true;
|
|
$use_code = $caps["code_exec"] ?? true;
|
|
$use_outbound = $caps["outbound"] ?? false;
|
|
$use_wevads = $caps["wevads"] ?? false;
|
|
$use_wevia_pub = $caps["wevia_pub"] ?? false;
|
|
$use_n8n = $caps["n8n"] ?? false;
|
|
$use_hermes_cap = $caps["hermes"] ?? false;
|
|
if (!$msg) { echo json_encode(["error" => "no message"]); exit; }
|
|
|
|
// === INTENT DETECTION ===
|
|
$msg_lower = strtolower($msg);
|
|
$intent = detect_intent($msg_lower);
|
|
$complexity = detect_complexity($msg_lower);
|
|
|
|
// === CONTEXT ENRICHMENT ===
|
|
$context = "";
|
|
$sources = [];
|
|
$agent = "";
|
|
|
|
// Business/WEVIA
|
|
if (in_array($intent, ["business","marketing","general","wevia"])) {
|
|
$kb = fetch_kb($msg_lower);
|
|
if ($kb) { $context .= $kb; $sources[] = "KB"; }
|
|
$agent = "WEVIA";
|
|
|
|
|
|
}
|
|
// DevOps/WEDROID
|
|
if (in_array($intent, ["devops","infra","docker","deploy"])) {
|
|
$git = fetch_git();
|
|
if ($git) { $context .= $git; $sources[] = "GIT"; }
|
|
$context .= fetch_infra();
|
|
$sources[] = "INFRA";
|
|
$exec = auto_execute($msg_lower);
|
|
if ($exec) { $context .= "\n[LIVE EXEC]\n$exec\n[/LIVE]\n"; $sources[] = "EXEC"; }
|
|
$agent = "WEDROID";
|
|
}
|
|
// Code/WEVCODE
|
|
if (in_array($intent, ["code","debug"])) {
|
|
$git = fetch_git();
|
|
if ($git) { $context .= $git; $sources[] = "GIT"; }
|
|
// Auto-read mentioned files
|
|
$file_ctx = auto_read_file($msg_lower);
|
|
if ($file_ctx) { $context .= $file_ctx; $sources[] = "FILE"; }
|
|
$agent = "WEVCODE";
|
|
}
|
|
// Productivity/WEVIA Life
|
|
if (in_array($intent, ["productivity","wellbeing"])) {
|
|
$kb = fetch_kb($msg_lower);
|
|
if ($kb) { $context .= $kb; $sources[] = "KB"; }
|
|
$agent = "WEVIA Life";
|
|
}
|
|
// Medical/Ethica
|
|
if (in_array($intent, ["ethica","medical"])) {
|
|
$ethica = fetch_ethica();
|
|
if ($ethica) { $context .= $ethica; $sources[] = "ETHICA_DB"; }
|
|
$sql = auto_sql($msg_lower);
|
|
if ($sql) { $context .= "\n[SQL]\n$sql\n[/SQL]\n"; $sources[] = "SQL"; }
|
|
$agent = $intent === "medical" ? "Meditron" : "Ethica";
|
|
}
|
|
// Security/BladeRazor
|
|
if ($intent === "security") {
|
|
$context .= fetch_infra();
|
|
$sources[] = "INFRA";
|
|
$agent = "BladeRazor";
|
|
}
|
|
// Data/SQL
|
|
if (in_array($intent, ["sql","data"])) {
|
|
$sql = auto_sql($msg_lower);
|
|
if ($sql) { $context .= "\n[SQL]\n$sql\n[/SQL]\n"; $sources[] = "SQL"; }
|
|
$ethica = fetch_ethica();
|
|
if ($ethica) { $context .= $ethica; $sources[] = "ETHICA_DB"; }
|
|
$agent = "Data Analyst";
|
|
}
|
|
// Tech/OSS
|
|
if (in_array($intent, ["tech","tools","opensource"])) {
|
|
$oss = fetch_opensource($msg_lower);
|
|
if ($oss) { $context .= $oss; $sources[] = "OSS_36"; }
|
|
$agent = "Tech Advisor";
|
|
}
|
|
// Execute code
|
|
if ($intent === "execute") {
|
|
$agent = "WEVCODE";
|
|
$sources[] = "CODE_EXEC";
|
|
}
|
|
// RAG DEEP SEARCH (Qdrant vectors)
|
|
if ($_hasRAG && strlen($msg) > 25) {
|
|
try {
|
|
$rag_ctx = ragSearch($msg, session_id());
|
|
if ($rag_ctx && is_string($rag_ctx) && strlen($rag_ctx) > 20) {
|
|
$context .= "\n[RAG DEEP]\n" . substr($rag_ctx, 0, 800) . "\n[/RAG]\n";
|
|
$sources[] = "RAG";
|
|
}
|
|
} catch (Exception $e) { /* silent */ }
|
|
}
|
|
|
|
// ENTITY EXTRACTION (memory engine)
|
|
if ($_hasMemory && strlen($msg) > 15) {
|
|
try {
|
|
$entities = extractEntities($msg);
|
|
if ($entities && is_array($entities) && count($entities) > 0) {
|
|
$context .= "\n[ENTITIES] " . implode(", ", array_slice($entities, 0, 8)) . "\n";
|
|
if (!in_array("MEMORY", $sources)) $sources[] = "MEMORY";
|
|
}
|
|
} catch (Exception $e) { /* silent */ }
|
|
}
|
|
|
|
// SELF-HEALING (for health/status queries)
|
|
if (false && $_hasHealing && preg_match("/health|sante|diagnostic|self.heal|status.*système|etat.*service/i", $msg)) {
|
|
try {
|
|
$health = weviaHealthCheck();
|
|
if ($health) {
|
|
$h_str = is_string($health) ? substr($health, 0, 400) : json_encode($health);
|
|
$context .= "\n[HEALTH CHECK]\n" . $h_str . "\n[/HEALTH]\n";
|
|
$sources[] = "HEALTH";
|
|
}
|
|
} catch (Exception $e) { /* silent */ }
|
|
}
|
|
|
|
// AUTOFETCH INFRA (enriches infra context)
|
|
if ($_hasAutofetch && in_array($intent, ["devops","infra","docker"])) {
|
|
try {
|
|
$infra_extra = "";
|
|
weviaAutoFetchInfra($infra_extra);
|
|
if ($infra_extra && strlen($infra_extra) > 20) {
|
|
$context .= "\n[AUTOFETCH]\n" . substr($infra_extra, 0, 500) . "\n[/AUTOFETCH]\n";
|
|
$sources[] = "AUTOFETCH";
|
|
}
|
|
} catch (Exception $e) { /* silent */ }
|
|
}
|
|
|
|
// DORMANT ENRICHMENT
|
|
if ($_hasRAG && strlen($msg) > 25) {
|
|
try { $rag = ragSearch($msg, "manager"); if ($rag && is_string($rag) && strlen($rag) > 20) { $context .= "\n[RAG]\n" . substr($rag, 0, 800) . "\n[/RAG]\n"; $sources[] = "RAG"; } } catch (Exception $e) {}
|
|
}
|
|
if ($_hasMemory && strlen($msg) > 15) {
|
|
try { $ent = extractEntities($msg); if ($ent && is_array($ent) && count($ent) > 0) { $context .= "\n[ENTITIES] " . implode(", ", array_slice($ent, 0, 8)) . "\n"; if (!in_array("MEMORY", $sources)) $sources[] = "MEMORY"; } } catch (Exception $e) {}
|
|
}
|
|
if (false && $_hasHealing && preg_match("/health|sante|diagnostic|status.*système|etat.*service/i", $msg)) {
|
|
try { ob_start(); $h = weviaHealthCheck(); ob_end_clean(); if ($h) { $context .= "\n[HEALTH]\n" . (is_string($h) ? substr($h, 0, 400) : json_encode($h)) . "\n[/HEALTH]\n"; $sources[] = "HEALTH"; } } catch (Exception $e) {}
|
|
}
|
|
if ($_hasAutofetch && in_array($intent, ["devops","infra","docker"])) {
|
|
try { $ax = ""; weviaAutoFetchInfra($ax); if ($ax && strlen($ax) > 20) { $context .= "\n[AUTOFETCH]\n" . substr($ax, 0, 500) . "\n[/AUTOFETCH]\n"; $sources[] = "AUTOFETCH"; } } catch (Exception $e) {}
|
|
}
|
|
|
|
// Fallback
|
|
if (!$context && !$agent) {
|
|
$kb = fetch_kb($msg_lower);
|
|
if ($kb) { $context .= $kb; $sources[] = "KB"; }
|
|
$agent = "WEVIA";
|
|
}
|
|
|
|
// === DORMANT ENRICHMENT ===
|
|
if ($_dormantAvailable && strlen($msg) > 20) {
|
|
$dormant_ctx = dormantEnrich($msg, $intent, "");
|
|
if ($dormant_ctx && strlen($dormant_ctx) > 10) {
|
|
$context .= $dormant_ctx;
|
|
$sources[] = "DORMANT";
|
|
}
|
|
}
|
|
|
|
|
|
// === CAPABILITY ENRICHMENT (from toggles) ===
|
|
$cap_context = "";
|
|
if ($use_crm) {
|
|
$crm_raw = shell_exec("curl -s -m 5 http://127.0.0.1/api/crm-api.php?action=stats 2>/dev/null");
|
|
if ($crm_raw) { $crm_d = json_decode($crm_raw, true); $cap_context .= "\n[CRM] Pipeline: " . ($crm_d["pipeline"] ?? "?") . " MAD, " . count($crm_d["deals"] ?? []) . " stages, " . ($crm_d["companies"] ?? 0) . " companies, " . ($crm_d["contacts"] ?? 0) . " contacts\n"; $sources[] = "CRM"; }
|
|
}
|
|
if ($use_deerflow) {
|
|
$df = @file_get_contents("http://localhost:2024/ok");
|
|
$cap_context .= "\n[DeerFlow SuperAgent] Status: " . ($df ?: "offline") . " | Gateway: localhost:8001 | Use for complex multi-step research tasks\n";
|
|
$sources[] = "DEERFLOW";
|
|
}
|
|
if ($use_nuclei) {
|
|
$nuc = shell_exec("timeout 10 sudo HOME=/root nuclei -u https://weval-consulting.com -severity critical,high -silent 2>&1 | head -5");
|
|
$cap_context .= "\n[Nuclei Security Scan]\n" . ($nuc ?: "No critical/high vulnerabilities found") . "\n";
|
|
$sources[] = "NUCLEI";
|
|
}
|
|
if ($use_playwright) {
|
|
$cap_context .= "\n[Playwright] Browser automation available. Version: " . trim(shell_exec("npx playwright --version 2>&1") ?: "?") . "\n";
|
|
$sources[] = "PLAYWRIGHT";
|
|
}
|
|
if ($use_harvester) {
|
|
$cap_context .= "\n[theHarvester+EmailFinder] OSINT enrichment available. Use: enrich domain → find emails + subdomains\n";
|
|
$sources[] = "OSINT";
|
|
}
|
|
if ($use_coderabbit) {
|
|
$cr = @file_get_contents("/var/www/html/.coderabbit.yaml");
|
|
$cap_context .= "\n[CodeRabbit] AI code review on GitHub PRs. Config: " . (strlen($cr) > 0 ? "active" : "missing") . "\n";
|
|
$sources[] = "CODERABBIT";
|
|
}
|
|
if ($use_mattermost) {
|
|
$mm_ping = @file_get_contents("http://localhost:8065/api/v4/system/ping");
|
|
$cap_context .= "\n[Mattermost] Team chat: " . ($mm_ping ? "online" : "offline") . " | Webhooks available for alerts\n";
|
|
$sources[] = "MATTERMOST";
|
|
}
|
|
if ($use_searxng) {
|
|
$cap_context .= "\n[SearXNG] Meta-search souverain sur localhost:8080. 70+ moteurs, zero tracking.\n";
|
|
$sources[] = "SEARXNG";
|
|
}
|
|
if ($use_toolfk) {
|
|
$cap_context .= "\n[ToolFK] 12 tools: JS obfuscate, PHP encrypt, AI image gen, OCR, QR, image compress, regex, DNS, PDF, barcode\n";
|
|
$sources[] = "TOOLFK";
|
|
}
|
|
if ($use_pdf) {
|
|
$cap_context .= "\n[PDF Gen] Generate proposals PDF via /api/weval-ia-pdf.php (Groq/Cerebras → fpdf2 DejaVu Unicode)\n";
|
|
$sources[] = "PDF";
|
|
}
|
|
if ($use_sql) {
|
|
$sql_ctx = shell_exec("PGPASSWORD=" . weval_secret('WEVAL_PG_ADMIN_PASS') . " psql -U admin -h 127.0.0.1 -d adx_system -t -c \"SELECT 'ethica:'||count(*) FROM ethica.medecins_real UNION ALL SELECT 'crm_deals:'||count(*) FROM crm.deals UNION ALL SELECT 'contacts:'||count(*) FROM crm.contacts\" 2>/dev/null");
|
|
$cap_context .= "\n[Live SQL] " . trim($sql_ctx ?: "N/A") . "\n";
|
|
$sources[] = "SQL";
|
|
}
|
|
if ($use_outbound) {
|
|
$seq_raw = shell_exec("curl -s -m 5 http://127.0.0.1/api/crm-api.php?action=sequences 2>/dev/null");
|
|
$seqs = json_decode($seq_raw, true);
|
|
$cap_context .= "\n[Outbound Sequences] " . (is_array($seqs) ? count($seqs) . " sequences configured" : "N/A") . "\n";
|
|
$sources[] = "SEQUENCES";
|
|
}
|
|
$context .= $cap_context;
|
|
// === CARL SKILLS (Piebald/Claude Code domain rules) ===
|
|
$carl_context = "";
|
|
$carl_dir = "/var/www/weval/.carl/";
|
|
$carl_domains = ["global","infra","ethica","wevads","wevia","site","commands","context"];
|
|
foreach ($carl_domains as $cd) {
|
|
$cf = $carl_dir . $cd;
|
|
if (file_exists($cf)) {
|
|
$content = file_get_contents($cf);
|
|
$carl_context .= "\n[CARL_SKILLS:$cd]\n" . substr($content, 0, 500) . "\n";
|
|
}
|
|
}
|
|
if ($carl_context) { $context .= $carl_context; $sources[] = "CARL"; }
|
|
|
|
// === WEVADS Arsenal context ===
|
|
if (isset($caps["wevads"]) && $caps["wevads"]) {
|
|
$wevads_status = shell_exec("curl -s -m 3 http://127.0.0.1/wevads-ia/ -o /dev/null -w '%{http_code}' 2>/dev/null");
|
|
$context .= "\n[WEVADS Arsenal] Status: " . trim($wevads_status) . " | 134KB SPA, 35 pages, 43 APIs, Graph API 6/9 tenants, PMTA v5.0r3, 7.3M contacts\n";
|
|
$sources[] = "WEVADS";
|
|
}
|
|
|
|
// === WEVIA Public context ===
|
|
if (isset($caps["wevia_pub"]) && $caps["wevia_pub"]) {
|
|
$wevia_api = shell_exec("curl -s -m 3 http://127.0.0.1/api/weval-ia -H 'Host: weval-consulting.com' 2>/dev/null");
|
|
$context .= "\n[WEVIA Public] API: " . trim(substr($wevia_api ?: "?", 0, 50)) . " | 178KB fullscreen, 71 modules, widget + Centre Commande\n";
|
|
$sources[] = "WEVIA";
|
|
}
|
|
|
|
// === n8n Workflows ===
|
|
if (isset($caps["n8n"]) && $caps["n8n"]) {
|
|
$n8n_status = shell_exec("curl -s -m 3 http://localhost:5678/healthz 2>/dev/null");
|
|
$context .= "\n[n8n] Status: " . trim($n8n_status ?: "offline") . " | 3 workflows: Health, AutoLearn, Error\n";
|
|
$sources[] = "N8N";
|
|
}
|
|
|
|
// === Hermes Skills ===
|
|
if (isset($caps["hermes"]) && $caps["hermes"]) {
|
|
$hermes = shell_exec("sudo HOME=/root hermes skills list 2>&1 | head -10");
|
|
$context .= "\n[Hermes 27 Skills]\n" . trim($hermes ?: "N/A") . "\n";
|
|
$sources[] = "HERMES";
|
|
}
|
|
|
|
|
|
|
|
// === PROVIDER SELECTION ===
|
|
$provider = select_provider($intent, $complexity);
|
|
$system = build_system_prompt($intent, $agent, $sources);
|
|
|
|
// === CALL AI (with consensus for complex queries) ===
|
|
$response = "";
|
|
$used_consensus = false;
|
|
|
|
if ($complexity === "high" && function_exists("consensus_ask")) {
|
|
// Use consensus for complex queries
|
|
try {
|
|
$response = consensus_ask($msg, $system . $context);
|
|
if ($response && strlen($response) > 50) {
|
|
$used_consensus = true;
|
|
$sources[] = "CONSENSUS";
|
|
}
|
|
} catch (Exception $e) {
|
|
// Fallback to single provider
|
|
}
|
|
}
|
|
|
|
if (!$response) {
|
|
if ($provider["type"] === "cloud") {
|
|
$response = cloud_call_chain($system . $context, $msg);
|
|
} else {
|
|
$response = ollama_call($provider["model"], $system . $context, $msg);
|
|
}
|
|
}
|
|
|
|
// === CHAIN EXECUTION for complex devops ===
|
|
if (in_array($intent, ["devops","infra"]) && in_array($complexity, ["high","medium"]) && function_exists("chain_execute")) {
|
|
$chain_result = chain_execute($msg, $system);
|
|
if ($chain_result && strlen($chain_result) > 20) {
|
|
$context .= "\n[CHAIN]\n" . $chain_result . "\n[/CHAIN]\n";
|
|
$sources[] = "CHAIN";
|
|
// Re-synthesize with chain results
|
|
if ($provider["type"] === "cloud") {
|
|
$response = cloud_call_chain(
|
|
$system . $context, "Synthetise les resultats d execution et reponds a: " . $msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
// === CODE EXECUTION (if detected) ===
|
|
$code_result = "";
|
|
if ($intent === "execute" && preg_match('/```(python|php|javascript|bash)\n(.*?)```/s', $msg, $cm)) {
|
|
$code_result = execute_code($cm[2], $cm[1]);
|
|
if ($code_result) $sources[] = "EXEC_OUTPUT";
|
|
}
|
|
|
|
echo json_encode([
|
|
"response" => $response ?: "No response",
|
|
"intent" => $intent,
|
|
"agent" => $agent,
|
|
"provider" => $used_consensus ? "Consensus (multi)" : $provider["name"],
|
|
"model" => $provider["model"],
|
|
"sources" => $sources,
|
|
"complexity" => $complexity,
|
|
"code_result" => $code_result ?: null,
|
|
"dormant" => count($_dormantModules),
|
|
"dormant_modules" => $_dormantModules
|
|
]);
|
|
|
|
// =================================================================
|
|
// INTENT DETECTION - 16 intents
|
|
// =================================================================
|
|
function detect_intent($msg) {
|
|
// Code execution
|
|
if (preg_match('/```(python|php|javascript|bash)/i', $msg)) return "execute";
|
|
if (preg_match('/execute.*code|run.*script|lance.*script/i', $msg)) return "execute";
|
|
|
|
// WEDROID
|
|
if (preg_match('/nginx|docker|pmta|server|disk|ram|cpu|systemctl|restart|deploy|backup|ssl|port|firewall|iptables|cron|uptime|container|image|volume|compose|commit|git|branch|merge|push|pull/i', $msg)) return "devops";
|
|
if (preg_match('/s204|s95|s151|sentinel|hetzner|ovh/i', $msg)) return "infra";
|
|
|
|
// WEVCODE
|
|
if (preg_match('/code|bug|fix|function|class|variable|error|exception|php|python|javascript|react|css|html|api|endpoint|regex|json|schema|column|refactor|unittest/i', $msg)) return "code";
|
|
if (preg_match('/debug|trace|log|stack|crash|broken|fails|undefined|null|404|500|timeout/i', $msg)) return "debug";
|
|
|
|
// WEVIA Life
|
|
if (preg_match('/planning|schedule|calendar|meeting|agenda|todo|task|priorite|productivite|habitude|routine|pause|focus|pomodoro|objectif|goal|review|standup|retrospective/i', $msg)) return "productivity";
|
|
if (preg_match('/stress|fatigue|burnout|wellbeing|bien-etre|motivation|equilibre|sommeil|sleep|exercise|sante|health|epuise|surcharge|concentr|organis|overwhelm/i', $msg)) return "wellbeing";
|
|
|
|
// Ethica/Medical
|
|
if (preg_match('/ethica|hcp|medecin|doctor|pharma|patient|prescription|specialite|consentement/i', $msg)) return "ethica";
|
|
if (preg_match('/meditron|pubmed|clinical|guideline|treatment|symptom|diagnosis|pathology|medication|dosage/i', $msg)) return "medical";
|
|
|
|
// Security
|
|
if (preg_match('/security|vulnerability|pentest|nuclei|scan|attack|exploit|cve|owasp|xss|injection|brute|credential|leak|audit/i', $msg)) return "security";
|
|
|
|
// Data/SQL
|
|
if (preg_match('/^select |^insert |^update |^delete |contacts|winners|configs|postgresql|database/i', $msg)) return "sql";
|
|
if (preg_match('/data|analytics|report|statistics|count|metric|dashboard|kpi/i', $msg)) return "data";
|
|
|
|
// Tech Advisor (BEFORE business)
|
|
if (preg_match('/open.?source|outil|framework|library|alternative|recommand|compar|benchmark|stack|langfuse|ragflow|chatwoot|twenty|plane/i', $msg)) return "tech";
|
|
|
|
// Business
|
|
if (preg_match('/weval|sap|vistex|consulting|client|partner|proposal|pricing|contract|lead|crm|offre|service|solution/i', $msg)) return "business";
|
|
if (preg_match('/marketing|email|campaign|newsletter|deliverability|warmup|audience|conversion|funnel/i', $msg)) return "marketing";
|
|
|
|
return "general";
|
|
}
|
|
|
|
function detect_complexity($msg) {
|
|
$len = strlen($msg);
|
|
if ($len > 200) return "high";
|
|
if (preg_match('/compar|analys|strategi|explain.*detail|exhausti|complet|architectur|refactor|migrat|audit|diagnostic|roadmap|plan.*action|verifie.*detail|nettoie.*gros/i', $msg)) return "high";
|
|
if ($len > 80 || preg_match('/comment|pourquoi|how|why|recommand|suggest|verifie|nettoie|optimise|liste|clean|purge|disk|docker/i', $msg)) return "medium";
|
|
return "low";
|
|
}
|
|
|
|
// =================================================================
|
|
// PROVIDER SELECTION
|
|
// =================================================================
|
|
function select_provider($intent, $complexity) {
|
|
global $secrets;
|
|
$alibaba = ["name"=>"Alibaba/Qwen","type"=>"cloud",
|
|
"url"=>"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions",
|
|
"key"=>"sk-34db1ad3152443cd86563d1bfc576c30","model"=>"qwen-plus"];
|
|
$cerebras = ["name"=>"Cerebras/Qwen3-235B","type"=>"cloud",
|
|
"url"=>"https://api.cerebras.ai/v1/chat/completions",
|
|
"key"=>"csk-4wrrhkpr568ry9xx49k9mcynwdx483nx53dd62yh5xedfckh","model"=>"qwen-3-235b-a22b-instruct-2507"];
|
|
$sambanova = ["name"=>"SambaNova/LLaMA-70B","type"=>"cloud",
|
|
"url"=>"https://api.sambanova.ai/v1/chat/completions",
|
|
"key"=>"9541b2a0-6ddc-4e7d-a957-c348d6119c3f","model"=>"Meta-Llama-3.3-70B-Instruct"];
|
|
$groq = ["name"=>"Groq/LLaMA-70B","type"=>"cloud",
|
|
"url"=>"https://api.groq.com/openai/v1/chat/completions",
|
|
"key"=>($secrets["GROQ_KEY"]??""),"model"=>"llama-3.3-70b-versatile"];
|
|
$meditron = ["name"=>"Ollama/Meditron","type"=>"local","model"=>"meditron:7b"];
|
|
|
|
if ($intent === "medical") return $meditron;
|
|
if ($complexity === "high") return $groq;
|
|
if (in_array($intent, ["code","debug","security","tech","execute"])) return $groq;
|
|
return $groq;
|
|
}
|
|
|
|
// =================================================================
|
|
// SYSTEM PROMPT
|
|
// =================================================================
|
|
function build_system_prompt($intent, $agent, $sources) {
|
|
$base = "Tu es WEVAL Manager, le cerveau unifie de WEVAL Consulting.\n" .
|
|
"Tu consolides 9 agents: WEVIA (commercial), WEDROID (DevOps), WEVCODE (dev), WEVIA Life (productivite), BladeRazor (sécurité), Ethica (pharma), Meditron (medical), Data Analyst, Tech Advisor.\n" .
|
|
"Tu as acces a: 436 KB files, 230 brain modules, 9 Ollama models, 15 Docker, PostgreSQL (50K+ HCPs, 6.5M contacts), 36 OSS tools, Git, Qdrant vectors, RAG engine, consensus multi-model.\n" .
|
|
"WEVAL = cabinet B2B: SAP Ecosystem Partner, Analytics, AI, Marketing, Ethica Pharma.\n" .
|
|
"Reponds dans la langue de la question. Sois precis et actionnable.\n" .
|
|
"JAMAIS mentionner Ollama/Groq/Cerebras/architecture interne. Tout = WEVIA Engine.\nCapabilities actives: RAG vectoriel (Qdrant), extraction entites, self-healing, consensus multi-modèle, chain execution, sandbox code (Python/PHP/JS/Bash).\n\n";
|
|
|
|
$personas = [
|
|
"WEVIA" => "Agent: WEVIA (commercial B2B). Services, clients, partenaires. RDV: booking.com/ymahboub/30min.\n",
|
|
"WEDROID" => "Agent: WEDROID (DevOps). S204 PRIMARY, S95 WEVADS, S151 OpenClaw. PHP 8.4, Node, Python, PG, nginx, Docker 15, PMTA. Reseau 10.1.0.2<->10.1.0.3.\n",
|
|
"WEVCODE" => "Agent: WEVCODE (dev). PHP/JS/Python/SQL/Bash. Enrichir existant JAMAIS _v2. GOLD avant modifier. Zero regression. React SPA + auth-session.php immutable.\n",
|
|
"WEVIA Life" => "Agent: WEVIA Life (productivite). Aide Yacine (Partner) et Ambre (tech lead). Planning, priorites, habitudes.\n",
|
|
"BladeRazor" => "Agent: BladeRazor (pentest). Cibles: S204, S95, S151. Nuclei + OWASP. Severity + remediation.\n",
|
|
"Ethica" => "Agent: Ethica Pharma. 50K+ HCPs (France, Maroc, Algerie, Tunisie, Belgique). DB: ethica.medecins_real.\n",
|
|
"Meditron" => "Agent: Meditron (medical). PubMed-based. Cite sources. JAMAIS remplacer jugement medical.\n",
|
|
"Data Analyst" => "Agent: Data Analyst. PostgreSQL live, Ethica stats, Brain configs. Insights actionnables.\n",
|
|
"Tech Advisor" => "Agent: Tech Advisor. 36 outils OSS evalues. Recommande selon contexte WEVAL.\n",
|
|
];
|
|
|
|
$sys = $base . ($personas[$agent] ?? $personas["WEVIA"]);
|
|
if ($sources) $sys .= "Sources: " . implode(", ", $sources) . "\n";
|
|
return $sys;
|
|
}
|
|
|
|
// =================================================================
|
|
// DATA FETCHERS
|
|
// =================================================================
|
|
function fetch_kb($msg) {
|
|
$stopwords = ["what","when","where","this","that","with","from","have","pour","dans","avec","quel","comment","quoi","tout","faire","bonjour","hello","quels","sont","services","list"];
|
|
$words = array_filter(explode(" ", preg_replace('/[^a-zA-Z0-9\s]/u', '', $msg)), function($w) use ($stopwords) {
|
|
return strlen($w) > 3 && !in_array($w, $stopwords);
|
|
});
|
|
$kw = implode("|", $words);
|
|
if (!$kw) return "";
|
|
$raw = shell_exec("grep -rilE '$kw' /var/www/weval/wevia-ia/kb-data/ 2>/dev/null | head -5 | while read f; do echo '--- KB ---'; head -12 \"\$f\" | tr -cd '[:print:][:space:]'; echo; done | head -c 1500");
|
|
return $raw ? "\n[KB]\n$raw\n[/KB]\n" : "";
|
|
}
|
|
|
|
function fetch_infra() {
|
|
$uptime = trim(shell_exec("uptime -p 2>/dev/null"));
|
|
$disk = trim(shell_exec("df -h / 2>/dev/null | awk 'NR==2{print \$5}'"));
|
|
$docker = trim(shell_exec("sudo docker ps -q 2>/dev/null | wc -l"));
|
|
$ollama = trim(shell_exec("curl -s http://localhost:11434/api/tags 2>/dev/null | python3 -c \"import sys,json;print(','.join([m['name'] for m in json.load(sys.stdin).get('models',[])])[:200])\" 2>/dev/null"));
|
|
$load = trim(shell_exec("cat /proc/loadavg 2>/dev/null | cut -d' ' -f1-3"));
|
|
$nginx = trim(shell_exec("systemctl is-active nginx 2>/dev/null"));
|
|
$pmta = trim(shell_exec("systemctl is-active pmta 2>/dev/null"));
|
|
return "\n[INFRA]\nS204: $uptime, disk=$disk, load=$load, docker=$docker, nginx=$nginx, pmta=$pmta\nOllama: $ollama\n[/INFRA]\n";
|
|
}
|
|
|
|
function fetch_git() {
|
|
$env = "GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0=/var/www/html";
|
|
$log = trim(shell_exec("$env git -C /var/www/html log --oneline -5 2>/dev/null"));
|
|
$status = trim(shell_exec("$env git -C /var/www/html status --short 2>/dev/null | head -8"));
|
|
$branch = trim(shell_exec("$env git -C /var/www/html branch --show-current 2>/dev/null"));
|
|
return $log ? "\n[GIT $branch]\n$log\n" . ($status ? "Modified: $status\n" : "") . "[/GIT]\n" : "";
|
|
}
|
|
|
|
function fetch_ethica() {
|
|
$stats = shell_exec("curl -s http://localhost/api/ethica-stats-api.php -H 'Host: weval-consulting.com' 2>/dev/null");
|
|
$sj = json_decode($stats, true);
|
|
$total = $sj["total"] ?? 0; $email = $sj["with_email"] ?? 0; $phone = $sj["with_telephone"] ?? 0;
|
|
return "\n[ETHICA]\nTotal: $total HCPs, Email: $email, Phone: $phone\n[/ETHICA]\n";
|
|
}
|
|
|
|
function fetch_opensource($msg) {
|
|
$raw = shell_exec("curl -s 'http://localhost/api/opensource-discovery.php?action=list' -H 'Host: weval-consulting.com' 2>/dev/null");
|
|
$d = json_decode($raw, true);
|
|
if (!$d || !isset($d["tools"])) return "";
|
|
$out = "[OSS - " . count($d["tools"]) . " tools]\n";
|
|
foreach ($d["tools"] as $t) {
|
|
$out .= "- {$t['name']} ({$t['cat']}, {$t['stars']}, {$t['status']}): {$t['desc']}\n";
|
|
}
|
|
return "\n" . $out . "[/OSS]\n";
|
|
}
|
|
|
|
function auto_read_file($msg) {
|
|
// Auto-read files mentioned in the message
|
|
if (preg_match('/(?:cat|read|voir|lis|montre|show|ouvre)\s+([\/\w\-\.]+\.(?:php|js|html|css|json|py|sh|conf|md))/i', $msg, $m)) {
|
|
$file = $m[1];
|
|
if (strpos($file, '/') !== 0) $file = "/var/www/html/" . $file;
|
|
$content = shell_exec("head -100 " . escapeshellarg($file) . " 2>&1 | head -c 3000");
|
|
return $content ? "\n[FILE $file]\n$content\n[/FILE]\n" : "";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function auto_execute($msg) {
|
|
if (preg_match('/s95|wevads|arsenal|sentinel/i', $msg)) {
|
|
$s95 = shell_exec("curl -s 'http://10.1.0.3:5890/api/sentinel-brain.php?action=exec&cmd=" . urlencode("uptime -p && df -h / | tail -1 && systemctl is-active apache2 && systemctl is-active postgresql") . "' 2>/dev/null");
|
|
$j = json_decode($s95, true);
|
|
return trim($j["output"] ?? "");
|
|
}
|
|
if (preg_match('/disk|storage|espace/i', $msg))
|
|
return trim(shell_exec("df -h / 2>/dev/null | tail -1"));
|
|
if (preg_match('/docker.*list|container.*list|docker.*status/i', $msg))
|
|
return trim(shell_exec("sudo docker ps --format 'table {{.Names}}\t{{.Status}}' 2>/dev/null | head -18"));
|
|
if (preg_match('/nginx.*status|nginx.*error/i', $msg))
|
|
return trim(shell_exec("systemctl status nginx 2>/dev/null | head -8"));
|
|
if (preg_match('/ollama.*model|model.*list/i', $msg))
|
|
return trim(shell_exec("sudo ollama list 2>/dev/null"));
|
|
if (preg_match('/cron|tache/i', $msg))
|
|
return trim(shell_exec("curl -s 'http://10.1.0.3:5890/api/sentinel-brain.php?action=exec&cmd=" . urlencode("crontab -l -u www-data 2>/dev/null | grep -v '^#' | head -10") . "' 2>/dev/null"));
|
|
if (preg_match('/git.*log|commit|dernier/i', $msg)) {
|
|
$env = "GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0=/var/www/html";
|
|
return trim(shell_exec("$env git -C /var/www/html log --oneline -10 2>/dev/null"));
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function auto_sql($msg) {
|
|
if (preg_match('/combien.*hcp|count.*hcp|nombre.*medecin|total.*ethica/i', $msg)) {
|
|
$r = shell_exec("curl -s 'http://10.1.0.3:5890/api/sentinel-brain.php?action=exec&cmd=" . urlencode("PGPASSWORD=" . weval_secret('WEVAL_PG_ADMIN_PASS') . " psql -h 127.0.0.1 -U admin -d adx_system -t -c \"SELECT count(*) FROM ethica.medecins_real\" 2>/dev/null") . "' 2>/dev/null");
|
|
return trim((json_decode($r, true))["output"] ?? "");
|
|
}
|
|
if (preg_match('/hcp.*par.*pays|hcp.*country|medecin.*pays/i', $msg)) {
|
|
$r = shell_exec("curl -s 'http://10.1.0.3:5890/api/sentinel-brain.php?action=exec&cmd=" . urlencode("PGPASSWORD=" . weval_secret('WEVAL_PG_ADMIN_PASS') . " psql -h 127.0.0.1 -U admin -d adx_system -c \"SELECT count(*),pays FROM ethica.medecins_real GROUP BY pays ORDER BY count DESC LIMIT 10\" 2>/dev/null") . "' 2>/dev/null");
|
|
return trim((json_decode($r, true))["output"] ?? "");
|
|
}
|
|
if (preg_match('/winner|sacred|gagnant/i', $msg)) {
|
|
$r = shell_exec("curl -s 'http://10.1.0.3:5890/api/sentinel-brain.php?action=exec&cmd=" . urlencode("sudo -u postgres psql -c \"SELECT id,name FROM adx_system.send_configs WHERE is_winner=true\" 2>/dev/null") . "' 2>/dev/null");
|
|
return trim((json_decode($r, true))["output"] ?? "");
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function execute_code($code, $lang) {
|
|
$payload = json_encode(["code" => $code, "language" => $lang, "timeout" => 15]);
|
|
$r = shell_exec("curl -s -m 20 -X POST http://localhost/wevia-ia/wevia-execute.php -H 'Host: weval-consulting.com' -H 'Content-Type: application/json' -d " . escapeshellarg($payload) . " 2>&1");
|
|
$j = json_decode($r, true);
|
|
if ($j && isset($j["output"])) {
|
|
return "Language: $lang\nSuccess: " . ($j["success"] ? "YES" : "NO") . "\nTime: " . ($j["execution_time"] ?? "?") . "ms\nOutput:\n" . $j["output"] . ($j["error"] ? "\nError: " . $j["error"] : "");
|
|
}
|
|
return $r ?: "";
|
|
}
|
|
|
|
// FILE OPERATIONS (safe read/write for WEVCODE)
|
|
function file_write($path, $content) {
|
|
// Safety: only allow writes in specific directories
|
|
$allowed = ["/var/www/html/", "/tmp/weval_"];
|
|
$safe = false;
|
|
foreach ($allowed as $dir) { if (strpos($path, $dir) === 0) { $safe = true; break; } }
|
|
if (!$safe) return "BLOCKED: path not in allowed directories";
|
|
// GOLD backup before write
|
|
if (file_exists($path)) {
|
|
$bak = $path . ".bak-" . date("Ymd-His");
|
|
copy($path, $bak);
|
|
}
|
|
file_put_contents($path, $content);
|
|
return "Written " . strlen($content) . " bytes to $path" . (isset($bak) ? " (backup: $bak)" : "");
|
|
}
|
|
|
|
// =================================================================
|
|
// AI CALLERS
|
|
// =================================================================
|
|
function ollama_call($model, $system, $msg) {
|
|
$user_msg = (strpos($model, 'qwen3') !== false) ? "/no_think $msg" : $msg;
|
|
$payload = json_encode(["model" => $model, "messages" => [
|
|
["role" => "system", "content" => $system],
|
|
["role" => "user", "content" => $user_msg]
|
|
], "stream" => false, "options" => ["num_predict" => 1024]]);
|
|
$raw = shell_exec("curl -s -m 120 http://localhost:11434/api/chat -d " . escapeshellarg($payload) . " 2>&1");
|
|
$j = json_decode($raw, true);
|
|
return trim(preg_replace('/<think>.*?<\/think>/s', '', $j["message"]["content"] ?? $raw));
|
|
}
|
|
|
|
function cloud_call_chain($system, $msg) {
|
|
global $secrets;
|
|
// Fallback chain: Groq 8s -> Cerebras 6s -> SambaNova 6s -> Alibaba 10s
|
|
$providers = [
|
|
["https://api.groq.com/openai/v1/chat/completions", ($secrets["GROQ_KEY"]??""), "llama-3.3-70b-versatile", 8],
|
|
["https://api.cerebras.ai/v1/chat/completions", "csk-4wrrhkpr568ry9xx49k9mcynwdx483nx53dd62yh5xedfckh", "qwen-3-235b-a22b-instruct-2507", 6],
|
|
["https://api.sambanova.ai/v1/chat/completions", "9541b2a0-6ddc-4e7d-a957-c348d6119c3f", "Meta-Llama-3.3-70B-Instruct", 6],
|
|
["https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions", "sk-34db1ad3152443cd86563d1bfc576c30", "qwen-plus", 10],
|
|
];
|
|
foreach ($providers as $p) {
|
|
$r = cloud_call($p[0], $p[1], $p[2], $system, $msg, $p[3]);
|
|
if ($r && strlen($r) > 30 && stripos($r, "rate limit") === false && stripos($r, "too_many_requests") === false && stripos($r, "queue_exceeded") === false && stripos($r, "error") === false) {
|
|
return $r;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function cloud_call($url, $key, $model, $system, $msg, $timeout=12) {
|
|
$payload = json_encode(["model" => $model, "messages" => [
|
|
["role" => "system", "content" => $system],
|
|
["role" => "user", "content" => $msg]
|
|
], "max_tokens" => 3000, "stream" => false]);
|
|
$raw = shell_exec("curl -s -m $timeout -X POST '$url' -H 'Authorization: Bearer $key' -H 'Content-Type: application/json' -d " . escapeshellarg($payload) . " 2>&1");
|
|
$j = json_decode($raw, true);
|
|
$content = $j["choices"][0]["message"]["content"] ?? "";
|
|
if (!$content && isset($j["error"])) return "";
|
|
return $content;
|
|
}
|
|
|
|
// =================================================================
|
|
// CONSENSUS (curl_multi parallel: Alibaba + Cerebras + Groq)
|
|
// =================================================================
|
|
function consensus_ask($msg, $system) {
|
|
global $secrets;
|
|
$providers = [
|
|
["n"=>"alibaba","u"=>"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions","k"=>"sk-34db1ad3152443cd86563d1bfc576c30","m"=>"qwen-plus"],
|
|
["n"=>"cerebras","u"=>"https://api.cerebras.ai/v1/chat/completions","k"=>"csk-4wrrhkpr568ry9xx49k9mcynwdx483nx53dd62yh5xedfckh","m"=>"qwen-3-235b-a22b-instruct-2507"],
|
|
["n"=>"groq","u"=>"https://api.groq.com/openai/v1/chat/completions","k"=>($secrets["GROQ_KEY"]??""),"m"=>"llama-3.3-70b-versatile"]
|
|
];
|
|
$base = ["messages"=>[["role"=>"system","content"=>$system],["role"=>"user","content"=>$msg]],"max_tokens"=>3000,"stream"=>false];
|
|
$mh = curl_multi_init();
|
|
$handles = [];
|
|
foreach ($providers as $p) {
|
|
$pl = $base; $pl["model"] = $p["m"];
|
|
$ch = curl_init($p["u"]);
|
|
curl_setopt_array($ch, [CURLOPT_POST=>true,CURLOPT_RETURNTRANSFER=>true,CURLOPT_TIMEOUT=>$timeout,
|
|
CURLOPT_HTTPHEADER=>["Authorization: Bearer ".$p["k"],"Content-Type: application/json"],
|
|
CURLOPT_POSTFIELDS=>json_encode($pl)]);
|
|
curl_multi_add_handle($mh, $ch);
|
|
$handles[$p["n"]] = $ch;
|
|
}
|
|
do { $st = curl_multi_exec($mh, $act); curl_multi_select($mh, 0.1); } while ($act && $st == CURLM_OK);
|
|
$responses = [];
|
|
foreach ($handles as $name => $ch) {
|
|
$raw = curl_multi_getcontent($ch);
|
|
$j = json_decode($raw, true);
|
|
$t = $j["choices"][0]["message"]["content"] ?? "";
|
|
if ($t && strlen($t) > 20) $responses[$name] = $t;
|
|
curl_multi_remove_handle($mh, $ch); curl_close($ch);
|
|
}
|
|
curl_multi_close($mh);
|
|
if (empty($responses)) return "";
|
|
$best = ""; $bs = 0;
|
|
foreach ($responses as $n => $t) {
|
|
$s = strlen($t) + (strpos($t,"**")!==false?200:0) + (strpos($t,"##")!==false?200:0) + (strpos($t,"|")!==false?150:0);
|
|
if ($s > $bs) { $bs = $s; $best = $t; }
|
|
}
|
|
return $best;
|
|
}
|
|
|
|
// =================================================================
|
|
// CHAIN EXECUTOR (multi-step autonomous)
|
|
// =================================================================
|
|
function chain_execute($task, $system) {
|
|
$analysis = cloud_call(
|
|
"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions",
|
|
"sk-34db1ad3152443cd86563d1bfc576c30", "qwen-plus",
|
|
$system, "Decompose cette tache en 3-5 commandes bash executables. Format: STEP: <cmd>. Tache: " . $task
|
|
);
|
|
$steps = [];
|
|
if (preg_match_all("/STEP:\\s*(.+)/m", $analysis, $m)) {
|
|
$steps = array_slice($m[1], 0, 5);
|
|
}
|
|
if (empty($steps)) return "Could not decompose: " . substr($analysis, 0, 300);
|
|
$blocked = ["rm -rf","mkfs","shutdown","reboot","passwd","dd if=","DROP TABLE","DROP DATABASE"];
|
|
$results = [];
|
|
foreach ($steps as $i => $cmd) {
|
|
$cmd = trim($cmd);
|
|
$skip = false;
|
|
foreach ($blocked as $b) { if (stripos($cmd, $b) !== false) { $skip = true; break; } }
|
|
if ($skip) { $results[] = "STEP ".($i+1).": BLOCKED"; continue; }
|
|
$output = shell_exec($cmd . " 2>&1 | head -c 500");
|
|
$results[] = "STEP ".($i+1).": " . $cmd . "\n" . trim($output);
|
|
}
|
|
return implode("\n---\n", $results);
|
|
}
|