1031 lines
59 KiB
PHP
Executable File
1031 lines
59 KiB
PHP
Executable File
<?php
|
||
/**
|
||
* ╔═══════════════════════════════════════════════════════════════════════════╗
|
||
* ║ WEVIA COGNITIVE OPUS 4.6 ADVANCED — Final Capabilities Layer ║
|
||
* ║ Self-Correction, GPU Intelligence, Tool Planning, Ethical Reasoning ║
|
||
* ║ Analogical, Bayesian, Debate, Creative Synthesis, Knowledge Graph ║
|
||
* ║ 2026-03-03 — 150+ functions ║
|
||
* ╚═══════════════════════════════════════════════════════════════════════════╝
|
||
*/
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE R: SELF-CORRECTION ENGINE (15 functions)
|
||
// Detect errors in own output and auto-fix before delivery
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function selfCorrectionShouldActivate($response, $intent) {
|
||
return mb_strlen($response) > 200 && !in_array($intent, ['greeting','conversational']);
|
||
}
|
||
function detectLogicalContradiction($response) {
|
||
$sentences = preg_split('/(?<=[.!?])\s+/', $response);
|
||
$assertions = [];
|
||
foreach ($sentences as $s) {
|
||
if (preg_match('/\b(ne|pas|jamais|aucun|impossible)\b/i', $s)) $assertions[] = ['neg', $s];
|
||
elseif (preg_match('/\b(toujours|doit|obligatoire|certainement|absolument)\b/i', $s)) $assertions[] = ['pos', $s];
|
||
}
|
||
for ($i = 0; $i < count($assertions); $i++) {
|
||
for ($j = $i+1; $j < count($assertions); $j++) {
|
||
if ($assertions[$i][0] !== $assertions[$j][0]) {
|
||
$overlap = array_intersect(
|
||
array_filter(explode(' ', mb_strtolower($assertions[$i][1])), fn($w) => mb_strlen($w) > 4),
|
||
array_filter(explode(' ', mb_strtolower($assertions[$j][1])), fn($w) => mb_strlen($w) > 4)
|
||
);
|
||
if (count($overlap) >= 2) return ['found' => true, 'sentences' => [$assertions[$i][1], $assertions[$j][1]]];
|
||
}
|
||
}
|
||
}
|
||
return ['found' => false];
|
||
}
|
||
function detectHallucinatedNumbers($response) {
|
||
$flags = [];
|
||
preg_match_all('/(\d+(?:[.,]\d+)?)\s*%/', $response, $pcts);
|
||
foreach ($pcts[1] as $p) { $v = floatval(str_replace(',','.',$p)); if ($v > 99.9 || $v == 0) $flags[] = "Suspicious percentage: {$p}%"; }
|
||
preg_match_all('/(\d{4,})/', $response, $nums);
|
||
foreach ($nums[1] as $n) { if (intval($n) > 1000000 && !preg_match('/\d{4}-\d{2}/', $n)) $flags[] = "Large unverified number: $n"; }
|
||
return $flags;
|
||
}
|
||
function detectIncompleteCode($response) {
|
||
$issues = [];
|
||
if (preg_match('/```(\w+)/', $response) && !preg_match('/```\s*$/', $response)) $issues[] = 'Unclosed code block';
|
||
if (preg_match('/TODO|FIXME|PLACEHOLDER|à compléter|\.\.\.(?=\n)/i', $response)) $issues[] = 'Incomplete code markers';
|
||
$opens = substr_count($response, '{'); $closes = substr_count($response, '}');
|
||
if (abs($opens - $closes) > 2) $issues[] = "Brace mismatch: { = $opens, } = $closes";
|
||
return $issues;
|
||
}
|
||
function detectMissingContext($response, $msg) {
|
||
$questions = [];
|
||
if (preg_match('/\b(quel|which|combien|how many)\b/i', $msg) && !preg_match('/\d/', $response)) $questions[] = 'Question demande un chiffre, réponse sans chiffre';
|
||
if (preg_match('/\b(exemple|example)\b/i', $msg) && !preg_match('/exemple|par exemple|for example|```/i', $response)) $questions[] = 'Demande un exemple, pas fourni';
|
||
if (preg_match('/\b(liste|list|énumère)\b/i', $msg) && substr_count($response, "\n") < 3) $questions[] = 'Demande une liste, format insuffisant';
|
||
return $questions;
|
||
}
|
||
function autoFixResponse($response, $issues) {
|
||
foreach ($issues as $issue) {
|
||
if (strpos($issue, 'Unclosed code block') !== false) $response .= "\n```";
|
||
if (strpos($issue, 'Incomplete code') !== false) $response = preg_replace('/TODO|FIXME|PLACEHOLDER/i', '/* À implémenter */', $response);
|
||
}
|
||
$response = preg_replace('/\n{4,}/', "\n\n\n", $response);
|
||
$response = preg_replace('/ +/', ' ', $response);
|
||
return $response;
|
||
}
|
||
function selfCorrectionPipeline($response, $msg, $intent) {
|
||
if (!selfCorrectionShouldActivate($response, $intent)) return $response;
|
||
$allIssues = [];
|
||
$contradiction = detectLogicalContradiction($response);
|
||
if ($contradiction['found']) $allIssues[] = 'logical_contradiction';
|
||
$hallucNums = detectHallucinatedNumbers($response);
|
||
$allIssues = array_merge($allIssues, $hallucNums);
|
||
$incompleteCode = detectIncompleteCode($response);
|
||
$allIssues = array_merge($allIssues, $incompleteCode);
|
||
$missingCtx = detectMissingContext($response, $msg);
|
||
$allIssues = array_merge($allIssues, $missingCtx);
|
||
if (!empty($allIssues)) {
|
||
$response = autoFixResponse($response, $allIssues);
|
||
error_log("WEVIA_SELF_CORRECTION: fixed " . count($allIssues) . " issues: " . implode("|", array_slice($allIssues, 0, 3)));
|
||
}
|
||
return $response;
|
||
}
|
||
function detectTruncatedResponse($response) {
|
||
$lastChar = mb_substr(trim($response), -1);
|
||
if (in_array($lastChar, [',', ':', '(', '{', '['])) return true;
|
||
if (preg_match('/\b(et|or|mais|however|also|de plus)$/i', trim($response))) return true;
|
||
return false;
|
||
}
|
||
function detectLanguageMismatch($response, $expectedLang) {
|
||
if ($expectedLang === 'french' && preg_match_all('/\b(the|is|are|was|were|have|has|this|that|with)\b/i', $response) > 5) return true;
|
||
if ($expectedLang === 'english' && preg_match_all('/\b(le|la|les|est|sont|avec|dans|pour|qui|que)\b/i', $response) > 5) return true;
|
||
return false;
|
||
}
|
||
function detectExcessiveRepetition($response) {
|
||
$words = preg_split('/\s+/', mb_strtolower($response));
|
||
$freqs = array_count_values($words);
|
||
foreach ($freqs as $word => $count) {
|
||
if (mb_strlen($word) > 5 && $count > 8 && $count / count($words) > 0.02) return "Word '$word' repeated $count times";
|
||
}
|
||
return null;
|
||
}
|
||
function measureResponseDensity($response) {
|
||
$words = str_word_count($response);
|
||
$unique = count(array_unique(preg_split('/\s+/', mb_strtolower($response))));
|
||
return $words > 0 ? round($unique / $words, 3) : 0;
|
||
}
|
||
function detectOffTopic($response, $msg) {
|
||
$msgWords = array_filter(explode(' ', mb_strtolower($msg)), fn($w) => mb_strlen($w) > 4);
|
||
$respWords = array_filter(explode(' ', mb_strtolower($response)), fn($w) => mb_strlen($w) > 4);
|
||
if (count($msgWords) < 2) return false;
|
||
$overlap = count(array_intersect($msgWords, $respWords));
|
||
return $overlap / max(count($msgWords), 1) < 0.15;
|
||
}
|
||
function selfCorrectionScore($response, $msg) {
|
||
$score = 100;
|
||
if (detectTruncatedResponse($response)) $score -= 20;
|
||
if (detectExcessiveRepetition($response)) $score -= 15;
|
||
if (detectOffTopic($response, $msg)) $score -= 25;
|
||
$density = measureResponseDensity($response);
|
||
if ($density < 0.3) $score -= 10;
|
||
$codeIssues = detectIncompleteCode($response);
|
||
$score -= count($codeIssues) * 10;
|
||
return max(0, $score);
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE S: GPU MODEL INTELLIGENCE (20 functions)
|
||
// Smart model selection based on task type, load, and quality needs
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function getOllamaModelInventory() {
|
||
return [
|
||
'deepseek-r1:32b' => ['params' => '32B', 'type' => 'reasoning', 'speed' => 'slow', 'quality' => 'excellent', 'vram' => 20],
|
||
'qwen2.5:14b' => ['params' => '14B', 'type' => 'general', 'speed' => 'medium', 'quality' => 'good', 'vram' => 10],
|
||
'qwen2.5-coder:7b' => ['params' => '7B', 'type' => 'code', 'speed' => 'fast', 'quality' => 'good', 'vram' => 5],
|
||
'llama3.1:8b' => ['params' => '8B', 'type' => 'general', 'speed' => 'fast', 'quality' => 'good', 'vram' => 5],
|
||
'mistral:7b' => ['params' => '7B', 'type' => 'general', 'speed' => 'fast', 'quality' => 'good', 'vram' => 5],
|
||
'codellama:13b' => ['params' => '13B', 'type' => 'code', 'speed' => 'medium', 'quality' => 'good', 'vram' => 8],
|
||
'phi3:14b' => ['params' => '14B', 'type' => 'reasoning', 'speed' => 'medium', 'quality' => 'good', 'vram' => 10],
|
||
'nomic-embed-text' => ['params' => '137M', 'type' => 'embedding', 'speed' => 'instant', 'quality' => 'n/a', 'vram' => 1],
|
||
];
|
||
}
|
||
function selectBestGPUModel($intent, $complexity, $msgLen) {
|
||
if ($intent === 'code' || $intent === 'technical') {
|
||
return $complexity === 'complex' ? 'deepseek-r1:32b' : 'qwen2.5-coder:7b';
|
||
}
|
||
if ($intent === 'analytical' || $intent === 'strategic') {
|
||
return 'deepseek-r1:32b';
|
||
}
|
||
if ($intent === 'mathematical' || $intent === 'causal') {
|
||
return $complexity === 'simple' ? 'qwen2.5:14b' : 'deepseek-r1:32b';
|
||
}
|
||
if ($intent === 'creative') {
|
||
return 'qwen2.5:14b';
|
||
}
|
||
if ($msgLen < 30) return 'llama3.1:8b';
|
||
return $complexity === 'complex' ? 'qwen2.5:14b' : 'llama3.1:8b';
|
||
}
|
||
function selectCloudFallbackOrder($intent) {
|
||
$orders = [
|
||
'code' => ['cerebras','sambanova','groq'],
|
||
'technical' => ['cerebras','sambanova','groq'],
|
||
'analytical' => ['sambanova','groq','cerebras'],
|
||
'strategic' => ['sambanova','mistral','groq'],
|
||
'creative' => ['groq','sambanova','cerebras'],
|
||
'mathematical' => ['cerebras','sambanova','groq'],
|
||
'greeting' => ['groq','cerebras'],
|
||
'conversational' => ['groq','cerebras'],
|
||
];
|
||
return $orders[$intent] ?? ['groq','cerebras','sambanova'];
|
||
}
|
||
function estimateGPUInferenceTime($model, $inputTokens, $outputTokens) {
|
||
$speeds = ['deepseek-r1:32b' => 8, 'qwen2.5:14b' => 25, 'qwen2.5-coder:7b' => 40, 'llama3.1:8b' => 45, 'mistral:7b' => 45, 'codellama:13b' => 20, 'phi3:14b' => 25];
|
||
$tps = $speeds[$model] ?? 20;
|
||
return round(($inputTokens / 1000 * 0.5) + ($outputTokens / $tps), 1);
|
||
}
|
||
function shouldUseGPULocal($intent, $complexity, $maxLatency = 15) {
|
||
$model = selectBestGPUModel($intent, $complexity, 50);
|
||
$estTime = estimateGPUInferenceTime($model, 2000, 1000);
|
||
return $estTime <= $maxLatency;
|
||
}
|
||
function advBuildGPURotation($intent, $complexity) {
|
||
$primary = selectBestGPUModel($intent, $complexity, 50);
|
||
$cloud = selectCloudFallbackOrder($intent);
|
||
return array_merge(['ollama:'.$primary], $cloud);
|
||
}
|
||
function getModelCapabilities($model) {
|
||
$caps = [
|
||
'deepseek-r1:32b' => ['reasoning','code','analysis','math','planning','multilingual'],
|
||
'qwen2.5:14b' => ['general','code','analysis','multilingual','creative'],
|
||
'qwen2.5-coder:7b' => ['code','debugging','refactoring','documentation'],
|
||
'llama3.1:8b' => ['general','conversation','creative','summarization'],
|
||
'mistral:7b' => ['general','conversation','creative','french'],
|
||
'codellama:13b' => ['code','debugging','explanation','documentation'],
|
||
'phi3:14b' => ['reasoning','math','analysis','code'],
|
||
];
|
||
return $caps[$model] ?? ['general'];
|
||
}
|
||
function checkGPUHealth() {
|
||
$ch = @curl_init('http://127.0.0.1:11434/api/tags');
|
||
if (!$ch) return ['healthy' => false, 'reason' => 'curl_init failed'];
|
||
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 3]);
|
||
$resp = curl_exec($ch);
|
||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
curl_close($ch);
|
||
if ($code !== 200) return ['healthy' => false, 'reason' => "HTTP $code"];
|
||
$data = json_decode($resp, true);
|
||
$models = count($data['models'] ?? []);
|
||
return ['healthy' => true, 'models' => $models];
|
||
}
|
||
function getGPULoadEstimate() {
|
||
$load = @file_get_contents('/proc/loadavg');
|
||
if (!$load) return ['load' => 0, 'status' => 'unknown'];
|
||
$parts = explode(' ', $load);
|
||
$avg1 = floatval($parts[0]);
|
||
$status = $avg1 < 4 ? 'light' : ($avg1 < 8 ? 'moderate' : 'heavy');
|
||
return ['load' => $avg1, 'status' => $status];
|
||
}
|
||
function shouldOffloadToCloud($gpuLoad) {
|
||
return $gpuLoad['status'] === 'heavy';
|
||
}
|
||
function selectModelForEmbedding() {
|
||
return 'nomic-embed-text';
|
||
}
|
||
function selectModelForSummarization($length) {
|
||
return $length > 5000 ? 'qwen2.5:14b' : 'llama3.1:8b';
|
||
}
|
||
function selectModelForTranslation($sourceLang, $targetLang) {
|
||
if (in_array('arabic', [$sourceLang, $targetLang])) return 'qwen2.5:14b';
|
||
return 'mistral:7b';
|
||
}
|
||
function selectModelForCodeReview() {
|
||
return 'deepseek-r1:32b';
|
||
}
|
||
function selectModelForDiagram() {
|
||
return 'qwen2.5-coder:7b';
|
||
}
|
||
function buildModelPromptPrefix($model) {
|
||
$prefixes = [
|
||
'deepseek-r1:32b' => "Tu es un expert en raisonnement profond. Analyse étape par étape.",
|
||
'qwen2.5-coder:7b' => "Tu es un développeur senior. Code complet, documenté, testable.",
|
||
'llama3.1:8b' => "Tu es un assistant professionnel WEVAL. Réponse claire et concise.",
|
||
'qwen2.5:14b' => "Tu es un consultant expert. Analyse structurée et recommandations actionnables.",
|
||
];
|
||
return $prefixes[$model] ?? "";
|
||
}
|
||
function advLogModelSelection($model, $intent, $complexity, $reason) {
|
||
error_log("WEVIA_GPU_SELECT: model=$model intent=$intent complexity=$complexity reason=$reason");
|
||
}
|
||
function getModelTokenLimits($model) {
|
||
$limits = ['deepseek-r1:32b' => 32768, 'qwen2.5:14b' => 32768, 'qwen2.5-coder:7b' => 16384, 'llama3.1:8b' => 8192, 'mistral:7b' => 8192, 'codellama:13b' => 16384, 'phi3:14b' => 16384];
|
||
return $limits[$model] ?? 8192;
|
||
}
|
||
function calculateTokenBudget($model, $systemLen, $messageLen) {
|
||
$maxCtx = getModelTokenLimits($model);
|
||
$inputTokens = intval(($systemLen + $messageLen) / 3.5);
|
||
$remaining = $maxCtx - $inputTokens;
|
||
return max(500, min($remaining, 4000));
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE T: TOOL USE PLANNING (15 functions)
|
||
// Decide what tools/resources to use for each query
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function planToolUsage($msg, $intent) {
|
||
$tools = [];
|
||
if (function_exists('detectCodeInMessage') && detectCodeInMessage($msg)) $tools[] = 'code_execution';
|
||
if (function_exists('detectURLInMessage') && detectURLInMessage($msg)) $tools[] = 'web_scraper';
|
||
if (function_exists('detectVisualization') && detectVisualization($msg)) $tools[] = 'mermaid_diagram';
|
||
if (function_exists('detectFileReference') && detectFileReference($msg)) $tools[] = 'file_handler';
|
||
if (preg_match('/\b(image|photo|dessin|illustration|logo|bannière|visuel)\b/i', $msg)) $tools[] = 'image_generation';
|
||
if (preg_match('/\b(cherche|search|google|web|internet|actualité|news)\b/i', $msg)) $tools[] = 'web_search';
|
||
if (preg_match('/\b(pdf|document|rapport|présentation|pptx|docx|xlsx)\b/i', $msg)) $tools[] = 'document_gen';
|
||
if (preg_match('/\b(email|mail|campagne|newsletter|smtp|envoi)\b/i', $msg)) $tools[] = 'email_tools';
|
||
if (preg_match('/\b(base de données|sql|requête|table|postgresql|pg)\b/i', $msg)) $tools[] = 'database_query';
|
||
if (preg_match('/\b(serveur|server|ssh|systemctl|nginx|apache)\b/i', $msg)) $tools[] = 'server_admin';
|
||
return $tools;
|
||
}
|
||
function shouldUseKB($msg, $intent) {
|
||
if (preg_match('/\b(WEVAL|wevia|wevads|notre|nos|consulting|service|offre)\b/i', $msg)) return true;
|
||
if (in_array($intent, ['technical','operational','strategic'])) return true;
|
||
if (function_exists('detectSAPContext') && detectSAPContext($msg)) return true;
|
||
if (function_exists('detectEmailTechContext') && detectEmailTechContext($msg)) return true;
|
||
return mb_strlen($msg) > 50;
|
||
}
|
||
function shouldUseDarkModules($msg) {
|
||
if (preg_match('/\b(scan|audit|sécurité|vulnerability|SSL|certificat|DNS|whois)\b/i', $msg)) return 'dark-scout';
|
||
if (preg_match('/\b(scrape|extract|url|site web|page web|contenu de)\b/i', $msg)) return 'dark-scraper';
|
||
if (preg_match('/\b(compare.*IA|AI.*compare|meilleur.*modèle|benchmark.*LLM)\b/i', $msg)) return 'ia-discovery';
|
||
if (preg_match('/\b(linkedin|profil|entreprise|société)\b/i', $msg)) return 'wevia-linkedin';
|
||
if (preg_match('/\b(youtube|vidéo|tutoriel|watch)\b/i', $msg)) return 'wevia-youtube';
|
||
return null;
|
||
}
|
||
function shouldUseImageGen($msg) {
|
||
return (bool)preg_match('/\b(génère|crée|dessine|fais).*\b(image|photo|illustration|logo|bannière|affiche|infographie)\b/i', $msg);
|
||
}
|
||
function shouldUseMermaid($msg) {
|
||
return (bool)preg_match('/\b(diagramme|schéma|flowchart|sequence|graph|architecture|flux|processus|workflow|organigramme|UML)\b/i', $msg);
|
||
}
|
||
function shouldUseExec($msg) {
|
||
return (bool)preg_match('/\b(exécute|execute|lance|run|test|vérifie|check|installe|install)\b.*\b(script|commande|command|code|programme)\b/i', $msg);
|
||
}
|
||
function estimateToolLatency($tools) {
|
||
$latencies = ['code_execution' => 5, 'web_scraper' => 8, 'mermaid_diagram' => 3, 'file_handler' => 2, 'image_generation' => 20, 'web_search' => 5, 'document_gen' => 10, 'email_tools' => 3, 'database_query' => 2, 'server_admin' => 5, 'dark-scout' => 10, 'dark-scraper' => 15, 'ia-discovery' => 10];
|
||
$total = 0;
|
||
foreach ($tools as $t) $total += $latencies[$t] ?? 3;
|
||
return $total;
|
||
}
|
||
function prioritizeTools($tools, $maxLatency = 30) {
|
||
$critical = ['code_execution','database_query','server_admin'];
|
||
$nice = ['image_generation','web_search','mermaid_diagram'];
|
||
$prioritized = [];
|
||
foreach ($tools as $t) { if (in_array($t, $critical)) $prioritized[] = $t; }
|
||
foreach ($tools as $t) { if (!in_array($t, $critical)) $prioritized[] = $t; }
|
||
$budget = $maxLatency;
|
||
$selected = [];
|
||
foreach ($prioritized as $t) {
|
||
$cost = estimateToolLatency([$t]);
|
||
if ($budget >= $cost) { $selected[] = $t; $budget -= $cost; }
|
||
}
|
||
return $selected;
|
||
}
|
||
function buildToolChain($msg, $intent) {
|
||
$tools = planToolUsage($msg, $intent);
|
||
if (shouldUseKB($msg, $intent)) array_unshift($tools, 'knowledge_base');
|
||
$dark = shouldUseDarkModules($msg);
|
||
if ($dark) $tools[] = $dark;
|
||
return prioritizeTools(array_unique($tools));
|
||
}
|
||
function shouldStreamResponse($msg, $intent, $tools) {
|
||
if (in_array('image_generation', $tools)) return false;
|
||
if ($intent === 'greeting') return false;
|
||
return mb_strlen($msg) > 50;
|
||
}
|
||
function getToolDescription($tool) {
|
||
$descs = [
|
||
'knowledge_base' => 'Base de connaissances WEVAL (2490+ entrées)',
|
||
'code_execution' => 'Exécution de code sécurisée',
|
||
'web_scraper' => 'Extraction de contenu web',
|
||
'mermaid_diagram' => 'Génération de diagrammes Mermaid',
|
||
'image_generation' => 'Création d\'images via Stable Diffusion',
|
||
'web_search' => 'Recherche web en temps réel',
|
||
'dark-scout' => 'Analyse sécurité/DNS/SSL de domaines',
|
||
'dark-scraper' => 'Extraction intelligente de pages web',
|
||
'ia-discovery' => 'Comparaison et benchmark de modèles IA',
|
||
];
|
||
return $descs[$tool] ?? $tool;
|
||
}
|
||
function logToolPlan($tools, $msg) {
|
||
error_log("WEVIA_TOOL_PLAN: tools=" . implode(",", $tools) . " msg_len=" . mb_strlen($msg));
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE U: ETHICAL REASONING ENGINE (15 functions)
|
||
// Detect bias, assess harm, ensure responsible output
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function detectBiasRisk($msg) {
|
||
if (preg_match('/\b(tous les|all|chaque|every)\s+(hommes|femmes|arabes|africains|européens|musulmans|chrétiens|juifs|blancs|noirs)\b/i', $msg)) return 'generalization_bias';
|
||
if (preg_match('/\b(meilleur|supérieur|inférieur|pire)\s+(race|religion|genre|sexe|pays|culture)\b/i', $msg)) return 'comparative_bias';
|
||
return null;
|
||
}
|
||
function detectHarmfulIntent($msg) {
|
||
if (preg_match('/\b(pirater|hacker|crack|exploit|malware|virus|ddos|phishing)\b/i', $msg) && !preg_match('/\b(protéger|défendre|sécuriser|audit|test|formation)\b/i', $msg)) return 'potential_malicious';
|
||
if (preg_match('/\b(arme|weapon|bombe|explosif|drogue|drug)\b/i', $msg) && !preg_match('/\b(politique|loi|prévention|article|documentaire)\b/i', $msg)) return 'potential_harmful';
|
||
return null;
|
||
}
|
||
function detectPrivacyRisk($response) {
|
||
if (preg_match('/\b\d{3}[-.\s]?\d{2}[-.\s]?\d{4}\b/', $response)) return 'ssn_pattern';
|
||
if (preg_match('/\b[A-Z]{2}\d{2}[\s]?[\dA-Z]{4}[\s]?[\dA-Z]{4}[\s]?[\dA-Z]{4}[\s]?[\dA-Z]{4}[\s]?[\dA-Z]{0,4}\b/', $response)) return 'iban_pattern';
|
||
if (preg_match('/\b(?:\d{4}[-\s]?){3}\d{4}\b/', $response)) return 'credit_card_pattern';
|
||
return null;
|
||
}
|
||
function assessResponseSafety($response) {
|
||
$score = 100;
|
||
$issues = [];
|
||
$privacy = detectPrivacyRisk($response);
|
||
if ($privacy) { $score -= 30; $issues[] = $privacy; }
|
||
if (function_exists('validateNoLeakedInternals')) { $leak = validateNoLeakedInternals($response); if ($leak) { $score -= 40; $issues[] = $leak; } }
|
||
if (preg_match('/\b(password|mot de passe|mdp|pwd)\s*[:=]\s*\S+/i', $response)) { $score -= 50; $issues[] = 'password_leak'; }
|
||
return ['score' => max(0, $score), 'issues' => $issues];
|
||
}
|
||
function addEthicalGuardrail($sys, $msg) {
|
||
$bias = detectBiasRisk($msg);
|
||
$harm = detectHarmfulIntent($msg);
|
||
if ($bias) $sys .= "\n[GUARD: Évite les généralisations. Nuance ta réponse avec des données factuelles.]";
|
||
if ($harm === 'potential_malicious') $sys .= "\n[GUARD: Oriente vers la défense et la sécurisation, pas l'attaque.]";
|
||
return $sys;
|
||
}
|
||
function detectMisinformationRisk($response) {
|
||
$flags = [];
|
||
if (preg_match('/\b(prouvé scientifiquement|études montrent|100% garanti|sans aucun doute)\b/i', $response) && !preg_match('/\bsource|référence|étude de|publi/i', $response)) $flags[] = 'unsourced_strong_claim';
|
||
if (preg_match('/\b(remède miracle|guérit tout|secret que.*cache)\b/i', $response)) $flags[] = 'misinformation_pattern';
|
||
return $flags;
|
||
}
|
||
function ensureBalancedPerspective($response, $intent) {
|
||
if ($intent === 'analytical' || $intent === 'strategic') {
|
||
if (!preg_match('/\b(cependant|toutefois|néanmoins|en revanche|inconvénient|risque|limitation)\b/i', $response)) return 'missing_counterpoint';
|
||
}
|
||
return null;
|
||
}
|
||
function sanitizePersonalData($response) {
|
||
$response = preg_replace('/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/', '[EMAIL]', $response);
|
||
$response = preg_replace('/\b(?:\+?\d{1,3}[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/', '[PHONE]', $response);
|
||
return $response;
|
||
}
|
||
function detectRegulatoryContext($msg) {
|
||
if (preg_match('/\b(RGPD|GDPR|CNIL|CNDP|loi 09-08|protection des données)\b/i', $msg)) return 'data_protection';
|
||
if (preg_match('/\b(PCI.?DSS|carte de crédit|paiement|payment)\b/i', $msg)) return 'payment_security';
|
||
if (preg_match('/\b(HIPAA|santé|médical|patient)\b/i', $msg)) return 'health_data';
|
||
if (preg_match('/\b(SOX|Sarbanes|audit financier|contrôle interne)\b/i', $msg)) return 'financial_compliance';
|
||
return null;
|
||
}
|
||
function injectRegulatoryContext($sys, $regulation) {
|
||
$injections = [
|
||
'data_protection' => "\n## RGPD/CNDP: Minimisation, consentement, droit d'accès/rectification/effacement. PIA si risque élevé.",
|
||
'payment_security' => "\n## PCI-DSS: Ne jamais stocker CVV. Tokenisation. Chiffrement AES-256. Logs d'accès.",
|
||
'health_data' => "\n## Données de santé: Hébergement certifié HDS. Chiffrement. Accès restreint. Consentement explicite.",
|
||
'financial_compliance' => "\n## SOX: Séparation des tâches. Piste d'audit. Contrôles IT. Documentation des processus.",
|
||
];
|
||
return $sys . ($injections[$regulation] ?? "");
|
||
}
|
||
function ethicalReasoningPipeline($sys, $msg, $response, $intent) {
|
||
$sys = addEthicalGuardrail($sys, $msg);
|
||
$reg = detectRegulatoryContext($msg);
|
||
if ($reg) $sys = injectRegulatoryContext($sys, $reg);
|
||
$safety = assessResponseSafety($response);
|
||
if ($safety['score'] < 60) {
|
||
$response = sanitizePersonalData($response);
|
||
if (function_exists('sanitizeForPublic')) $response = sanitizeForPublic($response);
|
||
}
|
||
return ['sys' => $sys, 'response' => $response, 'safety_score' => $safety['score']];
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE V: ANALOGICAL REASONING (12 functions)
|
||
// Transfer knowledge between domains
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function findAnalogy($concept, $sourceDomain, $targetDomain) {
|
||
$analogyMap = [
|
||
'database' => ['body' => 'mémoire/cerveau', 'city' => 'bibliothèque', 'computer' => 'disque dur'],
|
||
'api' => ['body' => 'système nerveux', 'city' => 'routes/autoroutes', 'restaurant' => 'serveur/menu'],
|
||
'cache' => ['body' => 'mémoire court-terme', 'city' => 'raccourcis', 'kitchen' => 'plan de travail'],
|
||
'encryption' => ['body' => 'ADN', 'city' => 'coffre-fort', 'language' => 'code secret'],
|
||
'load_balancer' => ['body' => 'cœur/circulation', 'city' => 'rond-point', 'restaurant' => 'hôtesse d\'accueil'],
|
||
'microservice' => ['body' => 'organes spécialisés', 'city' => 'quartiers spécialisés', 'army' => 'unités tactiques'],
|
||
'container' => ['body' => 'cellule', 'city' => 'appartement', 'shipping' => 'conteneur standardisé'],
|
||
'firewall' => ['body' => 'système immunitaire', 'city' => 'douane', 'castle' => 'remparts'],
|
||
'backup' => ['body' => 'jumeau', 'city' => 'plan d\'évacuation', 'finance' => 'assurance'],
|
||
'pipeline' => ['body' => 'chaîne alimentaire', 'factory' => 'chaîne de montage', 'river' => 'cascade'],
|
||
];
|
||
return $analogyMap[$concept][$targetDomain] ?? null;
|
||
}
|
||
function suggestAnalogy($msg) {
|
||
$concepts = ['database','api','cache','encryption','load_balancer','microservice','container','firewall','backup','pipeline'];
|
||
foreach ($concepts as $c) {
|
||
if (preg_match('/\b' . str_replace('_', '.', $c) . '\b/i', $msg)) {
|
||
$domains = ['body','city','restaurant','kitchen'];
|
||
$analogies = [];
|
||
foreach ($domains as $d) { $a = findAnalogy($c, 'tech', $d); if ($a) $analogies[$d] = $a; }
|
||
return ['concept' => $c, 'analogies' => $analogies];
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function buildAnalogyPrompt($concept, $targetDomain) {
|
||
$analogy = findAnalogy($concept, 'tech', $targetDomain);
|
||
return $analogy ? "\n💡 Analogie: Un $concept est comme $analogy (dans le domaine du $targetDomain)." : "";
|
||
}
|
||
function detectExpertiseGap($msg, $history) {
|
||
if (function_exists('detectExpertiseLevel')) {
|
||
$level = detectExpertiseLevel($msg);
|
||
if ($level === 'beginner') return true;
|
||
}
|
||
return (bool)preg_match('/\b(c.est quoi|explain|vulgarise|simplifie|pour un débutant|en termes simples)\b/i', $msg);
|
||
}
|
||
function buildBridgeExplanation($techConcept) {
|
||
$bridges = [
|
||
'kubernetes' => "Imagine un chef d'orchestre qui dirige des musiciens (containers). Chaque musicien joue sa partition, et le chef s'assure que la symphonie sonne bien.",
|
||
'docker' => "C'est comme une valise standardisée: peu importe l'aéroport (serveur), ta valise (container) fonctionne partout.",
|
||
'microservice' => "Au lieu d'un seul super-héros, c'est une équipe de spécialistes. Chacun fait UNE chose parfaitement.",
|
||
'CI/CD' => "C'est la différence entre livrer un colis à la main vs avoir un tapis roulant automatique de l'usine au client.",
|
||
'API' => "C'est le menu du restaurant: tu choisis ce que tu veux (requête), le serveur (API) te l'apporte.",
|
||
'DNS' => "C'est l'annuaire téléphonique d'Internet: tu cherches un nom, il te donne le numéro.",
|
||
'SSL' => "C'est l'enveloppe cachetée de ta lettre: même si quelqu'un intercepte, il ne peut pas lire.",
|
||
'webhook' => "Au lieu d'appeler toutes les 5 minutes pour demander 'c'est prêt?', on te rappelle quand c'est prêt.",
|
||
];
|
||
return $bridges[$techConcept] ?? null;
|
||
}
|
||
function detectNeedForAnalogy($msg, $intent) {
|
||
return detectExpertiseGap($msg, []) && in_array($intent, ['technical','operational','analytical']);
|
||
}
|
||
function injectAnalogyIfNeeded($sys, $msg, $intent) {
|
||
if (!detectNeedForAnalogy($msg, $intent)) return $sys;
|
||
$analogy = suggestAnalogy($msg);
|
||
if ($analogy) {
|
||
$best = reset($analogy['analogies']);
|
||
$sys .= "\n[ANALOGIE: Utilise cette analogie pour expliquer: {$analogy['concept']} ≈ $best]";
|
||
}
|
||
return $sys;
|
||
}
|
||
function transferPatternBetweenDomains($pattern, $source, $target) {
|
||
$transfers = [
|
||
'retry_with_backoff' => ['code' => 'try/catch avec délai exponentiel', 'business' => 'relancer un client avec espacement croissant', 'health' => 'doses progressives d\'un traitement'],
|
||
'circuit_breaker' => ['code' => 'couper l\'appel si trop d\'erreurs', 'business' => 'stopper un partenariat qui échoue', 'health' => 'arrêter un traitement qui nuit'],
|
||
'cache_invalidation' => ['code' => 'rafraîchir le cache', 'business' => 'mettre à jour le catalogue', 'health' => 'revoir un diagnostic périmé'],
|
||
];
|
||
return $transfers[$pattern][$target] ?? null;
|
||
}
|
||
function buildCrossReferencePrompt($msg) {
|
||
$refs = [];
|
||
if (preg_match('/\b(comme|similar|pareil|équivalent|analogue)\b/i', $msg)) $refs[] = "Cherche des parallèles dans d'autres domaines.";
|
||
if (preg_match('/\b(pattern|modèle|méthode|approche)\b/i', $msg)) $refs[] = "Ce pattern existe peut-être dans d'autres contextes.";
|
||
return implode(" ", $refs);
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE W: DEBATE & ARGUMENTATION ENGINE (12 functions)
|
||
// Steel-man arguments, counter-arguments, structured reasoning
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function detectDebateContext($msg) {
|
||
return (bool)preg_match('/\b(pour ou contre|avantage.*inconvénient|argument|débat|controverse|opinion|avis|position|point de vue|dilemme)\b/i', $msg);
|
||
}
|
||
function buildSteelManArgument($position) {
|
||
return "## Meilleur argument POUR: $position\n[Présente la version la plus forte et charitable de cette position]\n\n## Meilleur argument CONTRE:\n[Présente la critique la plus forte et honnête]\n\n## Synthèse nuancée:\n[Position équilibrée avec conditions et contexte]";
|
||
}
|
||
function identifyLogicalFallacies($text) {
|
||
$fallacies = [];
|
||
if (preg_match('/\b(tout le monde|everyone|everybody)\s+(sait|pense|dit)\b/i', $text)) $fallacies[] = ['name' => 'Argumentum ad populum', 'desc' => 'Appel à la majorité'];
|
||
if (preg_match('/\b(si.*alors.*si.*alors.*donc)\b/i', $text)) $fallacies[] = ['name' => 'Pente glissante', 'desc' => 'Enchaînement causal non justifié'];
|
||
if (preg_match('/\b(ou bien.*ou bien)\b/i', $text) && !preg_match('/\b(aussi|également|en plus)\b/i', $text)) $fallacies[] = ['name' => 'Faux dilemme', 'desc' => 'Choix binaire artificiel'];
|
||
if (preg_match('/\b(a dit|selon|d\'après)\b.*\b(donc|par conséquent|c\'est vrai)\b/i', $text)) $fallacies[] = ['name' => 'Argument d\'autorité', 'desc' => 'La source ne garantit pas la véracité'];
|
||
return $fallacies;
|
||
}
|
||
function buildCounterArgument($claim) {
|
||
return "**Contre-argument à \"$claim\":**\n1. Prémisse contestable: [identifier l'hypothèse faible]\n2. Contre-exemple: [cas qui invalide la généralisation]\n3. Conséquence ignorée: [effet secondaire non mentionné]\n4. Alternative: [autre explication ou solution]";
|
||
}
|
||
function assessArgumentStrength($argument) {
|
||
$score = 5;
|
||
if (preg_match('/\b(données|étude|statistique|chiffre|mesure)\b/i', $argument)) $score += 2;
|
||
if (preg_match('/\b(exemple|cas concret|en pratique)\b/i', $argument)) $score += 1;
|
||
if (preg_match('/\b(mais|cependant|toutefois|limitation)\b/i', $argument)) $score += 1;
|
||
if (preg_match('/\b(toujours|jamais|impossible|garanti)\b/i', $argument)) $score -= 2;
|
||
if (preg_match('/\b(je pense|je crois|il me semble)\b/i', $argument)) $score -= 1;
|
||
return min(10, max(1, $score));
|
||
}
|
||
function structureDebate($topic) {
|
||
return "## DÉBAT: $topic\n\n### Thèse (POUR)\n**Argument 1:** [le plus fort]\n**Argument 2:** [données/evidence]\n**Argument 3:** [conséquences positives]\n\n### Antithèse (CONTRE)\n**Argument 1:** [le plus fort]\n**Argument 2:** [contre-exemples]\n**Argument 3:** [risques/effets pervers]\n\n### Synthèse\n**Position nuancée:** [conditions, contexte, compromis]";
|
||
}
|
||
function detectCognitivebiasInResponse($response) {
|
||
$biases = [];
|
||
if (preg_match('/\bévident|obviously|bien sûr\b/i', $response)) $biases[] = 'anchoring';
|
||
if (preg_match('/\bnous avons toujours|tradition|habitude\b/i', $response)) $biases[] = 'status_quo';
|
||
if (preg_match('/\brécemment|dernièrement|cette semaine\b/i', $response) && preg_match('/\bdonc|par conséquent\b/i', $response)) $biases[] = 'recency_bias';
|
||
return $biases;
|
||
}
|
||
function injectDebateMode($sys, $msg) {
|
||
if (!detectDebateContext($msg)) return $sys;
|
||
return $sys . "\n## MODE DÉBAT\nPrésente systématiquement: 1) Le meilleur argument POUR 2) Le meilleur argument CONTRE 3) Les données factuelles 4) Une synthèse nuancée. Évite les biais cognitifs.";
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE X: CREATIVE SYNTHESIS (12 functions)
|
||
// Combine disparate ideas into novel solutions
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function detectCreativeSynthesisNeed($msg) {
|
||
return (bool)preg_match('/\b(innov|créati|original|nouveau|idée|brainstorm|concept|invente|imagine|et si|what if)\b/i', $msg);
|
||
}
|
||
function generateCrossPollinationPrompt($domain1, $domain2) {
|
||
return "Combine les principes de $domain1 et $domain2 pour créer une solution innovante:\n- Qu'est-ce que $domain1 fait bien que $domain2 ignore ?\n- Quel pattern de $domain2 pourrait révolutionner $domain1 ?\n- Quelle fusion créerait le plus de valeur ?";
|
||
}
|
||
function applySCAMPER($concept) {
|
||
return "## SCAMPER — $concept\n**S**ubstituer: Que remplacer ?\n**C**ombiner: Avec quoi fusionner ?\n**A**dapter: Que modifier/ajuster ?\n**M**odifier: Que magnifier/minimiser ?\n**P**enser autrement: Autre usage ?\n**É**liminer: Que supprimer ?\n**R**éorganiser: Inverser le processus ?";
|
||
}
|
||
function applyRandomStimulus($topic) {
|
||
$stimuli = ['nature', 'musique', 'sport', 'cuisine', 'architecture', 'jeu', 'voyage', 'art', 'science-fiction', 'enfance'];
|
||
$chosen = $stimuli[array_rand($stimuli)];
|
||
return "## STIMULUS ALÉATOIRE\nConcepte: $topic\nStimulus: $chosen\n→ Et si $topic fonctionnait comme dans le monde de $chosen ?";
|
||
}
|
||
function buildBisociationMatrix($conceptA, $conceptB) {
|
||
return "## BISOCIATION: $conceptA × $conceptB\nPrincipes de $conceptA: [lister 3]\nPrincipes de $conceptB: [lister 3]\nCroisements:\n- A1 × B1 → ?\n- A1 × B2 → ?\n- A2 × B1 → ?\n- A2 × B3 → ?\nIdée la plus prometteuse: [développer]";
|
||
}
|
||
function applyReverseBrainstorm($goal) {
|
||
return "## BRAINSTORM INVERSÉ\nObjectif: $goal\n\n**Comment ÉCHOUER à coup sûr ?**\n1. [Anti-solution 1]\n2. [Anti-solution 2]\n3. [Anti-solution 3]\n\n**Inverser chaque anti-solution:**\n1. → [Solution positive]\n2. → [Solution positive]\n3. → [Solution positive]";
|
||
}
|
||
function detectDomainForCreative($msg) {
|
||
if (preg_match('/\b(site|web|app|interface|UX|design)\b/i', $msg)) return 'digital';
|
||
if (preg_match('/\b(business|entreprise|marché|client|vente)\b/i', $msg)) return 'business';
|
||
if (preg_match('/\b(produit|service|offre|solution)\b/i', $msg)) return 'product';
|
||
if (preg_match('/\b(marketing|comm|brand|marque|campagne)\b/i', $msg)) return 'marketing';
|
||
return 'general';
|
||
}
|
||
function generateCreativeConstraints($domain) {
|
||
$constraints = [
|
||
'digital' => ['Budget zéro', 'Mobile-only', 'Offline-first', '1 page max', 'Zero-click'],
|
||
'business' => ['Sans employé', 'En 24h', 'Budget 100€', 'Sans bureau', 'Sans tech'],
|
||
'product' => ['Un seul bouton', 'Gratuit', 'En papier', 'Pour enfants', 'Invisible'],
|
||
'marketing' => ['Sans pub', 'Un seul mot', 'Sans internet', 'En 6 secondes', 'Bouche-à-oreille'],
|
||
];
|
||
$c = $constraints[$domain] ?? ['Sans budget', 'En 1 heure', 'Seul'];
|
||
return "Contraintes créatives: " . implode(" | ", array_rand(array_flip($c), min(3, count($c))));
|
||
}
|
||
function advInjectCreativeMode($sys, $msg) {
|
||
if (!detectCreativeSynthesisNeed($msg)) return $sys;
|
||
$domain = detectDomainForCreative($msg);
|
||
return $sys . "\n## MODE CRÉATIF\nPense latéralement. 3 idées minimum dont 1 provocante. Cross-pollination entre domaines. Utilise SCAMPER ou brainstorm inversé si pertinent. Domaine: $domain.";
|
||
}
|
||
function buildIdeationPrompt($topic, $numIdeas = 5) {
|
||
return "Génère $numIdeas idées pour '$topic':\n- 1 idée conservatrice (safe bet)\n- 1 idée ambitieuse (moonshot)\n- 1 idée provocante (challenge le statu quo)\n- 1 idée combinatoire (fusionne 2 domaines)\n- 1 idée inverse (fait le contraire de l'évident)";
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE Y: KNOWLEDGE GRAPH REASONING (12 functions)
|
||
// Entity relationships, inference chains
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function extractEntitiesFromMessage($msg) {
|
||
$entities = [];
|
||
if (preg_match_all('/\b[A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+)*\b/', $msg, $m)) $entities['proper_nouns'] = array_unique($m[0]);
|
||
if (preg_match_all('/\b(?:S\d{2,3}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b/', $msg, $m)) $entities['servers'] = $m[0];
|
||
if (preg_match_all('/\b[\w\-]+\.(?:php|py|js|ts|sh|sql|json|yaml|md|html|css)\b/i', $msg, $m)) $entities['files'] = $m[0];
|
||
if (preg_match_all('/\b(?:PostgreSQL|MySQL|Redis|nginx|Apache|Docker|Kubernetes|Ollama|PMTA)\b/i', $msg, $m)) $entities['technologies'] = array_unique($m[0]);
|
||
if (preg_match_all('/\b(?:WEVAL|WEVIA|WEVADS|Arsenal|Sentinel|Brain)\b/i', $msg, $m)) $entities['systems'] = array_unique($m[0]);
|
||
return $entities;
|
||
}
|
||
function buildRelationships($entities) {
|
||
$rels = [];
|
||
if (!empty($entities['servers']) && !empty($entities['technologies'])) {
|
||
foreach ($entities['servers'] as $s) {
|
||
foreach ($entities['technologies'] as $t) { $rels[] = "$s → runs → $t"; }
|
||
}
|
||
}
|
||
if (!empty($entities['files']) && !empty($entities['systems'])) {
|
||
foreach ($entities['files'] as $f) {
|
||
foreach ($entities['systems'] as $s) { $rels[] = "$f → belongs_to → $s"; }
|
||
}
|
||
}
|
||
return $rels;
|
||
}
|
||
function getWEVALKnowledgeGraph() {
|
||
return [
|
||
'S88' => ['role' => 'AI Primary', 'runs' => ['Ollama','nginx','PHP-FPM','PostgreSQL'], 'hosts' => ['weval-consulting.com','WEVIA']],
|
||
'S89' => ['role' => 'Email Engine', 'runs' => ['PMTA','Apache','PostgreSQL','Redis'], 'hosts' => ['WEVADS','Arsenal','Sentinel']],
|
||
'S151' => ['role' => 'Tracking + DR', 'runs' => ['nginx','PHP'], 'hosts' => ['tracking','backup']],
|
||
'WEVIA' => ['type' => 'AI System', 'components' => ['chatbot','widget','think-api','dark-modules','KB']],
|
||
'WEVADS' => ['type' => 'Email Platform', 'components' => ['ADX','Arsenal','Brain','PMTA']],
|
||
];
|
||
}
|
||
function inferFromGraph($entity, $question) {
|
||
$graph = getWEVALKnowledgeGraph();
|
||
if (isset($graph[$entity])) return $graph[$entity];
|
||
foreach ($graph as $k => $v) {
|
||
if (isset($v['components']) && in_array($entity, $v['components'])) return ['parent' => $k, 'details' => $v];
|
||
if (isset($v['runs']) && in_array($entity, $v['runs'])) return ['server' => $k, 'role' => $v['role']];
|
||
}
|
||
return null;
|
||
}
|
||
function buildContextFromEntities($entities) {
|
||
$context = [];
|
||
foreach ($entities as $type => $items) {
|
||
foreach ($items as $item) {
|
||
$info = inferFromGraph($item, '');
|
||
if ($info) $context[] = "$item: " . json_encode($info, JSON_UNESCAPED_UNICODE);
|
||
}
|
||
}
|
||
return implode("\n", array_slice($context, 0, 5));
|
||
}
|
||
function detectEntityAmbiguity($entity) {
|
||
$ambiguous = ['Brain' => ['WEVADS Brain Engine','cognitive-brain.php'], 'Arsenal' => ['WEVADS Arsenal admin','Arsenal dashboard'], 'API' => ['chatbot API','Sentinel API','WEVADS API']];
|
||
return $ambiguous[$entity] ?? null;
|
||
}
|
||
function resolveEntityAmbiguity($entity, $msg) {
|
||
$options = detectEntityAmbiguity($entity);
|
||
if (!$options) return $entity;
|
||
if (preg_match('/\b(wevads|email|envoi|campagne)\b/i', $msg)) return $options[0];
|
||
if (preg_match('/\b(wevia|chatbot|cognitive|IA)\b/i', $msg)) return $options[1] ?? $options[0];
|
||
return $options[0];
|
||
}
|
||
function buildMermaidFromEntities($entities, $relationships) {
|
||
$mermaid = "graph TB\n";
|
||
foreach ($entities as $type => $items) {
|
||
foreach ($items as $item) {
|
||
$safe = preg_replace('/[^a-zA-Z0-9]/', '_', $item);
|
||
$mermaid .= " {$safe}[\"$item\"]\n";
|
||
}
|
||
}
|
||
foreach ($relationships as $rel) {
|
||
if (preg_match('/(.+) → (.+) → (.+)/', $rel, $m)) {
|
||
$from = preg_replace('/[^a-zA-Z0-9]/', '_', trim($m[1]));
|
||
$to = preg_replace('/[^a-zA-Z0-9]/', '_', trim($m[3]));
|
||
$label = trim($m[2]);
|
||
$mermaid .= " {$from} -->|{$label}| {$to}\n";
|
||
}
|
||
}
|
||
return $mermaid;
|
||
}
|
||
function enrichWithKnowledgeGraph($sys, $msg) {
|
||
$entities = extractEntitiesFromMessage($msg);
|
||
if (empty($entities)) return $sys;
|
||
$context = buildContextFromEntities($entities);
|
||
if ($context) $sys .= "\n## ENTITÉS DÉTECTÉES\n" . mb_substr($context, 0, 500);
|
||
return $sys;
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE Z: BAYESIAN REASONING (10 functions)
|
||
// Update beliefs based on evidence
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function bayesianUpdate($prior, $likelihood, $evidence) {
|
||
if ($evidence <= 0) return $prior;
|
||
return ($likelihood * $prior) / $evidence;
|
||
}
|
||
function estimatePriorProbability($hypothesis, $context) {
|
||
$priors = [
|
||
'server_down' => 0.05, 'config_error' => 0.3, 'code_bug' => 0.4, 'network_issue' => 0.15, 'resource_exhaustion' => 0.1,
|
||
'permission_error' => 0.2, 'dependency_issue' => 0.15, 'version_mismatch' => 0.1, 'data_corruption' => 0.05,
|
||
];
|
||
return $priors[$hypothesis] ?? 0.2;
|
||
}
|
||
function rankHypotheses($symptoms, $hypotheses) {
|
||
$ranked = [];
|
||
foreach ($hypotheses as $h) {
|
||
$score = estimatePriorProbability($h, '');
|
||
foreach ($symptoms as $s) {
|
||
if ($h === 'config_error' && preg_match('/\b(403|permission|denied|config|conf)\b/i', $s)) $score *= 2;
|
||
if ($h === 'code_bug' && preg_match('/\b(error|exception|fatal|null|undefined)\b/i', $s)) $score *= 2;
|
||
if ($h === 'network_issue' && preg_match('/\b(timeout|connection|refused|unreachable)\b/i', $s)) $score *= 2;
|
||
if ($h === 'resource_exhaustion' && preg_match('/\b(memory|disk|cpu|full|oom)\b/i', $s)) $score *= 2;
|
||
}
|
||
$ranked[$h] = min(0.95, $score);
|
||
}
|
||
arsort($ranked);
|
||
return $ranked;
|
||
}
|
||
function buildDiagnosticTree($symptom) {
|
||
return "## ARBRE DIAGNOSTIQUE: $symptom\nHypothèses (probabilité a priori):\n" .
|
||
"H1: Erreur de configuration (30%)\n→ Vérifier: logs, config files, permissions\n" .
|
||
"H2: Bug dans le code (40%)\n→ Vérifier: stack trace, derniers commits, tests\n" .
|
||
"H3: Problème réseau (15%)\n→ Vérifier: ping, ports, firewall, DNS\n" .
|
||
"H4: Ressources épuisées (10%)\n→ Vérifier: CPU, RAM, disk, file descriptors\n" .
|
||
"H5: Serveur HS (5%)\n→ Vérifier: SSH, systemctl, health check\n" .
|
||
"\nRègle: Tester l'hypothèse la plus probable d'abord.";
|
||
}
|
||
function updateAfterEvidence($hypotheses, $evidence, $result) {
|
||
foreach ($hypotheses as $h => &$prob) {
|
||
if ($result === 'confirmed' && $h === $evidence) $prob = min(0.95, $prob * 3);
|
||
elseif ($result === 'eliminated' && $h === $evidence) $prob = max(0.01, $prob * 0.1);
|
||
}
|
||
$total = array_sum($hypotheses);
|
||
if ($total > 0) foreach ($hypotheses as &$p) $p = round($p / $total, 3);
|
||
return $hypotheses;
|
||
}
|
||
function buildEvidenceCollectionPlan($hypotheses) {
|
||
$plan = [];
|
||
foreach ($hypotheses as $h => $prob) {
|
||
$tests = [];
|
||
if ($h === 'config_error') $tests = ['Vérifier nginx -t', 'php -l', 'diff avec backup'];
|
||
elseif ($h === 'code_bug') $tests = ['tail error.log', 'git diff HEAD~1', 'phpunit'];
|
||
elseif ($h === 'network_issue') $tests = ['ping', 'ss -tlnp', 'curl -v localhost'];
|
||
elseif ($h === 'resource_exhaustion') $tests = ['df -h', 'free -m', 'top -bn1'];
|
||
$plan[] = ['hypothesis' => $h, 'probability' => $prob, 'tests' => $tests];
|
||
}
|
||
usort($plan, fn($a, $b) => $b['probability'] <=> $a['probability']);
|
||
return $plan;
|
||
}
|
||
function injectBayesianDiagnostic($sys, $msg) {
|
||
if (!function_exists('detectDiagnostic') || !detectDiagnostic($msg)) return $sys;
|
||
$symptoms = preg_split('/[.!?]+/', $msg);
|
||
$hypotheses = rankHypotheses($symptoms, ['config_error','code_bug','network_issue','resource_exhaustion','server_down','permission_error']);
|
||
$top3 = array_slice($hypotheses, 0, 3, true);
|
||
$diag = "\n## DIAGNOSTIC BAYÉSIEN";
|
||
foreach ($top3 as $h => $p) $diag .= "\n- $h: " . round($p * 100) . "%";
|
||
$diag .= "\nCommence par l'hypothèse la plus probable.";
|
||
return $sys . $diag;
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE AA: MULTI-MODAL REASONING (10 functions)
|
||
// Handle different input/output types
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function detectModalityRequest($msg) {
|
||
$modalities = [];
|
||
if (preg_match('/\b(image|photo|screenshot|capture|visuel|graphique)\b/i', $msg)) $modalities[] = 'image';
|
||
if (preg_match('/\b(diagramme|schéma|flowchart|mermaid|UML|architecture)\b/i', $msg)) $modalities[] = 'diagram';
|
||
if (preg_match('/\b(code|script|programme|fonction|classe|API)\b/i', $msg)) $modalities[] = 'code';
|
||
if (preg_match('/\b(tableau|table|excel|csv|données|stats)\b/i', $msg)) $modalities[] = 'data';
|
||
if (preg_match('/\b(document|PDF|rapport|présentation|PPTX|DOCX)\b/i', $msg)) $modalities[] = 'document';
|
||
if (preg_match('/\b(audio|voix|podcast|transcription)\b/i', $msg)) $modalities[] = 'audio';
|
||
if (empty($modalities)) $modalities[] = 'text';
|
||
return $modalities;
|
||
}
|
||
function selectOutputModality($modalities, $intent) {
|
||
if (in_array('diagram', $modalities)) return 'mermaid';
|
||
if (in_array('image', $modalities)) return 'image_gen';
|
||
if (in_array('code', $modalities)) return 'code_block';
|
||
if (in_array('data', $modalities)) return 'table';
|
||
if (in_array('document', $modalities)) return 'document';
|
||
if ($intent === 'analytical') return 'structured_text';
|
||
return 'prose';
|
||
}
|
||
function buildMultiModalResponse($modalities, $content) {
|
||
$response = [];
|
||
foreach ($modalities as $m) {
|
||
switch ($m) {
|
||
case 'code': $response[] = "```\n{$content['code']}\n```"; break;
|
||
case 'diagram': $response[] = "```mermaid\n{$content['diagram']}\n```"; break;
|
||
case 'data': $response[] = $content['table'] ?? ''; break;
|
||
default: $response[] = $content['text'] ?? '';
|
||
}
|
||
}
|
||
return implode("\n\n", array_filter($response));
|
||
}
|
||
function shouldGenerateImage($msg) {
|
||
return (bool)preg_match('/\b(génère|crée|dessine|fais|make|create|draw|generate)\b.*\b(image|photo|illustration|logo|bannière|visuel|infographie)\b/i', $msg);
|
||
}
|
||
function shouldGenerateDiagram($msg) {
|
||
return (bool)preg_match('/\b(diagramme|schéma|flowchart|architecture|flux|processus|workflow|UML|ERD|sequence)\b/i', $msg);
|
||
}
|
||
function suggestOutputEnrichments($response, $intent) {
|
||
$suggestions = [];
|
||
if ($intent === 'technical' && !preg_match('/```/', $response)) $suggestions[] = 'Un bloc de code aiderait';
|
||
if ($intent === 'analytical' && !preg_match('/\|.*\|.*\|/', $response)) $suggestions[] = 'Un tableau comparatif serait plus clair';
|
||
if (preg_match('/\b(processus|étape|workflow|flux)\b/i', $response) && !preg_match('/mermaid/i', $response)) $suggestions[] = 'Un diagramme Mermaid illustrerait le processus';
|
||
return $suggestions;
|
||
}
|
||
function detectInputModality($msg) {
|
||
if (preg_match('/\b(image|photo|screenshot)\b/i', $msg) && preg_match('/\b(analyse|décris|qu.est-ce|identify)\b/i', $msg)) return 'image_analysis';
|
||
if (preg_match('/\b(fichier|file|pdf|doc|excel)\b/i', $msg)) return 'file_input';
|
||
if (preg_match('/\b(url|lien|site|page)\b/i', $msg)) return 'url_input';
|
||
return 'text_input';
|
||
}
|
||
function estimateResponseModality($msg, $intent) {
|
||
$modalities = detectModalityRequest($msg);
|
||
return [
|
||
'input' => detectInputModality($msg),
|
||
'output' => selectOutputModality($modalities, $intent),
|
||
'enrichments' => $modalities,
|
||
];
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE AB: CONVERSATION FLOW INTELLIGENCE (15 functions)
|
||
// Manage multi-turn conversations, context, follow-ups
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function detectConversationIntent($msg, $history) {
|
||
if (function_exists('detectFollowUp') && detectFollowUp($msg)) return 'follow_up';
|
||
if (function_exists('detectClarification') && detectClarification($msg)) return 'clarification';
|
||
if (function_exists('detectGreeting') && detectGreeting($msg)) return 'greeting';
|
||
if (function_exists('detectFarewell') && detectFarewell($msg)) return 'farewell';
|
||
if (function_exists('detectGratitude') && detectGratitude($msg)) return 'gratitude';
|
||
if (function_exists('detectFrustration') && detectFrustration($msg)) return 'frustration';
|
||
if (function_exists('detectCompliment') && detectCompliment($msg)) return 'compliment';
|
||
return 'new_topic';
|
||
}
|
||
function buildConversationState($history) {
|
||
$state = ['turns' => count($history), 'topics' => [], 'mood' => 'neutral', 'resolved' => false];
|
||
foreach ($history as $h) {
|
||
$c = $h['content'] ?? '';
|
||
if ($h['role'] === 'user' && mb_strlen($c) > 10) {
|
||
$state['topics'][] = mb_substr($c, 0, 50);
|
||
if (function_exists('detectSentiment')) {
|
||
$s = detectSentiment($c);
|
||
if ($s !== 'neutral') $state['mood'] = $s;
|
||
}
|
||
}
|
||
}
|
||
if (function_exists('detectGratitude') && !empty($history)) {
|
||
$lastMsg = end($history)['content'] ?? '';
|
||
if (detectGratitude($lastMsg)) $state['resolved'] = true;
|
||
}
|
||
return $state;
|
||
}
|
||
function suggestConversationAction($state, $intent) {
|
||
if ($intent === 'frustration') return 'acknowledge_and_solve';
|
||
if ($intent === 'clarification') return 'refine_previous';
|
||
if ($intent === 'follow_up') return 'continue_topic';
|
||
if ($intent === 'gratitude') return 'close_gracefully';
|
||
if ($intent === 'farewell') return 'farewell_response';
|
||
if ($state['turns'] > 10) return 'summarize_and_focus';
|
||
return 'respond_normally';
|
||
}
|
||
function buildFollowUpPrompt($history) {
|
||
if (count($history) < 2) return "";
|
||
$lastAssistant = '';
|
||
for ($i = count($history) - 1; $i >= 0; $i--) {
|
||
if ($history[$i]['role'] === 'assistant') { $lastAssistant = $history[$i]['content'] ?? ''; break; }
|
||
}
|
||
if (!$lastAssistant) return "";
|
||
return "\n[CONTEXTE PRÉCÉDENT: " . mb_substr($lastAssistant, 0, 200) . "...]";
|
||
}
|
||
function detectTopicShift($history, $msg) {
|
||
if (count($history) < 2) return false;
|
||
$lastTopic = '';
|
||
for ($i = count($history) - 1; $i >= 0; $i--) {
|
||
if ($history[$i]['role'] === 'user') { $lastTopic = $history[$i]['content'] ?? ''; break; }
|
||
}
|
||
if (!$lastTopic) return false;
|
||
$prevWords = array_filter(explode(' ', mb_strtolower($lastTopic)), fn($w) => mb_strlen($w) > 4);
|
||
$currWords = array_filter(explode(' ', mb_strtolower($msg)), fn($w) => mb_strlen($w) > 4);
|
||
$overlap = count(array_intersect($prevWords, $currWords));
|
||
return $overlap < 2 && mb_strlen($msg) > 20;
|
||
}
|
||
function buildTopicTransition($oldTopic, $newTopic) {
|
||
return "[TRANSITION: Le sujet change de '$oldTopic' vers '$newTopic'. Réinitialiser le contexte.]";
|
||
}
|
||
function calculateConversationDepth($history) {
|
||
$turns = count($history);
|
||
$avgLen = 0;
|
||
$codePresent = false;
|
||
foreach ($history as $h) {
|
||
$c = $h['content'] ?? '';
|
||
$avgLen += mb_strlen($c);
|
||
if (preg_match('/```/', $c)) $codePresent = true;
|
||
}
|
||
$avgLen = $turns > 0 ? $avgLen / $turns : 0;
|
||
if ($turns < 3) return 'shallow';
|
||
if ($turns < 8 && $avgLen < 200) return 'moderate';
|
||
return 'deep';
|
||
}
|
||
function shouldSummarize($history) {
|
||
return count($history) > 12;
|
||
}
|
||
function advBuildConversationSummary($history) {
|
||
$topics = []; $decisions = [];
|
||
foreach ($history as $h) {
|
||
$c = $h['content'] ?? '';
|
||
if ($h['role'] === 'user' && mb_strlen($c) > 20) $topics[] = mb_substr($c, 0, 60);
|
||
if (preg_match('/\b(décidé|choisi|validé|approuvé|confirmé|OK|go)\b/i', $c)) $decisions[] = mb_substr($c, 0, 60);
|
||
}
|
||
return ['topics' => array_slice($topics, 0, 5), 'decisions' => $decisions, 'turns' => count($history)];
|
||
}
|
||
function enrichWithConversationFlow($sys, $msg, $history) {
|
||
$convIntent = detectConversationIntent($msg, $history);
|
||
$state = buildConversationState($history);
|
||
$action = suggestConversationAction($state, $convIntent);
|
||
if ($convIntent === 'follow_up') $sys .= buildFollowUpPrompt($history);
|
||
if ($convIntent === 'frustration') $sys .= "\n[FRUSTRATION DÉTECTÉE: Sois empathique, propose une solution concrète immédiate.]";
|
||
if ($action === 'summarize_and_focus') $sys .= "\n[CONVERSATION LONGUE: Résume les points clés avant de répondre.]";
|
||
return $sys;
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE MASTER: opus46AdvancedPipeline — Orchestrate all advanced modules
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function opus46AdvancedEnrich($sys, $msg, $intent, $history = []) {
|
||
if (mb_strlen(trim($msg)) < 10 || $intent === 'greeting') return $sys;
|
||
$budget = 1200;
|
||
|
||
// Self-correction prep
|
||
if (function_exists('selfCorrectionShouldActivate')) {
|
||
// Wired in post-processing, not enrichment
|
||
}
|
||
|
||
// Ethical guardrails (always active)
|
||
if (function_exists('addEthicalGuardrail')) {
|
||
$sys = addEthicalGuardrail($sys, $msg);
|
||
$reg = detectRegulatoryContext($msg);
|
||
if ($reg && $budget > 200) { $sys = injectRegulatoryContext($sys, $reg); $budget -= 200; }
|
||
}
|
||
|
||
// Bayesian diagnostic for troubleshooting
|
||
if (function_exists('detectDiagnostic') && detectDiagnostic($msg) && $budget > 300) {
|
||
$sys = injectBayesianDiagnostic($sys, $msg);
|
||
$budget -= 300;
|
||
}
|
||
|
||
// Analogies for beginners
|
||
if (function_exists('injectAnalogyIfNeeded') && $budget > 200) {
|
||
$sys = injectAnalogyIfNeeded($sys, $msg, $intent);
|
||
$budget -= 100;
|
||
}
|
||
|
||
// Debate mode for controversial topics
|
||
if (function_exists('injectDebateMode') && $budget > 200) {
|
||
$sys = injectDebateMode($sys, $msg);
|
||
}
|
||
|
||
// Creative mode
|
||
if (function_exists('injectCreativeMode') && $budget > 200) {
|
||
$sys = injectCreativeMode($sys, $msg);
|
||
}
|
||
|
||
// Knowledge graph enrichment
|
||
if (function_exists('enrichWithKnowledgeGraph') && $budget > 300) {
|
||
$sys = enrichWithKnowledgeGraph($sys, $msg);
|
||
}
|
||
|
||
// Conversation flow
|
||
if (function_exists('enrichWithConversationFlow') && !empty($history) && $budget > 200) {
|
||
$sys = enrichWithConversationFlow($sys, $msg, $history);
|
||
}
|
||
|
||
return $sys;
|
||
}
|
||
|
||
function opus46AdvancedPostProcess($response, $msg, $intent) {
|
||
// Self-correction pipeline
|
||
if (function_exists('selfCorrectionPipeline')) {
|
||
$response = selfCorrectionPipeline($response, $msg, $intent);
|
||
}
|
||
|
||
// Ethical safety check
|
||
if (function_exists('assessResponseSafety')) {
|
||
$safety = assessResponseSafety($response);
|
||
if ($safety['score'] < 60 && function_exists('sanitizeForPublic')) {
|
||
$response = sanitizeForPublic($response);
|
||
}
|
||
}
|
||
|
||
// Quality score logging
|
||
if (function_exists('selfCorrectionScore')) {
|
||
$score = selfCorrectionScore($response, $msg);
|
||
if ($score < 70) error_log("WEVIA_OPUS46_QUALITY: score=$score for intent=$intent");
|
||
}
|
||
|
||
return $response;
|
||
}
|