Files
html/api/wevia-brain.php
2026-04-12 22:57:03 +02:00

316 lines
16 KiB
PHP

<?php
require_once __DIR__ . '/_secrets.php';
/**
* WEVIA BRAIN ORCHESTRATOR v2.0
* Unified: Collective Consciousness + Persistent Memory + Auto-Learning + Opus Skills
*
* ENDPOINTS:
* POST /api/wevia-brain.php — main orchestrator
* action=ask — collective consensus query
* action=remember — store persistent memory
* action=recall — retrieve memories
* action=learn — store learning from interaction
* action=skills — list Opus skills
* action=status — full brain status
*/
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit;
require_once '/opt/wevads/vault/credentials.php';
$input = json_decode(file_get_contents('php://input'), true) ?: $_GET;
$action = $input['action'] ?? $_GET['action'] ?? 'status';
// ═══════════════════════════════════════════════════════
// DATABASE CONNECTION
// ═══════════════════════════════════════════════════════
function getDB() {
static $pdo;
if (!$pdo) {
try {
$pdo = new PDO('pgsql:host=127.0.0.1;dbname=adx_system', 'admin', weval_secret('WEVAL_PG_ADMIN_PASS'));
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Auto-create tables if missing
$pdo->exec("CREATE SCHEMA IF NOT EXISTS brain");
$pdo->exec("CREATE TABLE IF NOT EXISTS brain.memory (
id SERIAL PRIMARY KEY,
session_id VARCHAR(100),
user_id VARCHAR(100) DEFAULT 'default',
memory_type VARCHAR(50) DEFAULT 'fact',
key TEXT NOT NULL,
value TEXT NOT NULL,
confidence REAL DEFAULT 1.0,
source VARCHAR(100) DEFAULT 'conversation',
access_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
)");
$pdo->exec("CREATE TABLE IF NOT EXISTS brain.learnings (
id SERIAL PRIMARY KEY,
category VARCHAR(100),
scenario VARCHAR(200),
input TEXT,
output TEXT,
score REAL,
quality VARCHAR(20),
provider VARCHAR(100),
improvement TEXT,
applied BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW()
)");
$pdo->exec("CREATE TABLE IF NOT EXISTS brain.consensus_log (
id SERIAL PRIMARY KEY,
query TEXT,
provider_count INT,
winner VARCHAR(100),
response TEXT,
elapsed_ms INT,
created_at TIMESTAMP DEFAULT NOW()
)");
$pdo->exec("CREATE INDEX IF NOT EXISTS idx_memory_key ON brain.memory(key)");
$pdo->exec("CREATE INDEX IF NOT EXISTS idx_memory_type ON brain.memory(memory_type)");
$pdo->exec("CREATE INDEX IF NOT EXISTS idx_learnings_cat ON brain.learnings(category)");
} catch (Exception $e) {
return null;
}
}
return $pdo;
}
// ═══════════════════════════════════════════════════════
// CLAUDE OPUS 4.6 SKILL SYSTEM
// ═══════════════════════════════════════════════════════
function getOpusSkills() {
$skills = [
// Core Reasoning Skills
'chain-of-thought' => ['desc' => 'Deep reasoning with step-by-step thinking', 'trigger' => '/analyse|compare|pourquoi|explain|debug/i'],
'tree-of-thoughts' => ['desc' => 'Multi-path exploration for complex problems', 'trigger' => '/strateg|optimis|architect|plan|design/i'],
'self-critique' => ['desc' => 'Verify own response accuracy before output', 'trigger' => '/verifi|precise|exact|certain|sure/i'],
// Domain Skills
'erp-sap-expert' => ['desc' => 'SAP S/4HANA, modules FI/CO/MM/SD, Vistex', 'trigger' => '/sap|erp|vistex|s4hana|fiori|abap/i'],
'cloud-architect' => ['desc' => 'AWS, Azure, GCP, hybrid, sovereign cloud', 'trigger' => '/cloud|aws|azure|gcp|kubernetes|docker/i'],
'cybersecurity' => ['desc' => 'OWASP, pentest, SOC, SIEM, zero-trust', 'trigger' => '/securit|owasp|pentest|soc|siem|firewall|vulnerability/i'],
'data-engineer' => ['desc' => 'ETL, data lake, BI, ML pipelines', 'trigger' => '/data|etl|pipeline|warehouse|bi|machine.learning/i'],
'email-deliverability' => ['desc' => 'SPF, DKIM, DMARC, PMTA, KumoMTA, warmup', 'trigger' => '/email|delivrabilit|dkim|spf|dmarc|pmta|kumo|warmup/i'],
'pharma-hcp' => ['desc' => 'Life Sciences, HCP outreach, Ethica, Maghreb regulations', 'trigger' => '/pharma|hcp|medecin|ethica|loi.*09.*08|consentement|maghreb.*sante/i'],
// Output Skills
'mermaid-architect' => ['desc' => 'Generate Mermaid diagrams (flowcharts, sequences, ERDs)', 'trigger' => '/schema|diagramme|flowchart|mermaid|architecture|processus/i'],
'svg-designer' => ['desc' => 'Generate SVG logos and illustrations', 'trigger' => '/logo|svg|icone|illustration|branding/i'],
'pdf-generator' => ['desc' => 'Generate structured PDF documents', 'trigger' => '/pdf|document|rapport|genere.*document/i'],
'code-generator' => ['desc' => 'Python, PHP, JS, SQL, Bash code generation', 'trigger' => '/code|script|python|php|javascript|sql|bash|function/i'],
// Analysis Skills
'swot-analyst' => ['desc' => 'SWOT, PESTEL, Porter, BCG analysis', 'trigger' => '/swot|pestel|porter|bcg|analyse.*strateg/i'],
'market-researcher' => ['desc' => 'Market research, competitive analysis, trends', 'trigger' => '/marche|concurrent|benchmark|trend|etude/i'],
'financial-advisor' => ['desc' => 'Financial modeling, ROI, TCO calculations', 'trigger' => '/roi|tco|financ|budget|cout|pricing|tarif/i'],
// Communication Skills
'proposal-writer' => ['desc' => 'Write professional proposals and offers', 'trigger' => '/proposition|offre|devis|proposal|commercial/i'],
'email-composer' => ['desc' => 'Draft professional emails', 'trigger' => '/email.*redige|mail.*ecri|courrier|draft/i'],
'multilingual' => ['desc' => '14 languages: FR, EN, AR, Darija, ES, DE, IT, PT, ZH, JA, KO, RU, TR, HI', 'trigger' => '/traduire|translate|langue|language/i'],
];
return $skills;
}
function detectSkills($query) {
$skills = getOpusSkills();
$matched = [];
foreach ($skills as $name => $skill) {
if (preg_match($skill['trigger'], $query)) {
$matched[] = $name;
}
}
return $matched ?: ['chain-of-thought']; // default
}
// ═══════════════════════════════════════════════════════
// COLLECTIVE CONSCIOUSNESS (Multi-model consensus)
// ═══════════════════════════════════════════════════════
function collectiveAsk($query, $sys = '', $lang = 'fr') {
$providers = [
['name'=>'Groq', 'url'=>'https://api.groq.com/openai/v1/chat/completions', 'key'=>GROQ_KEY, 'model'=>'llama-3.3-70b-versatile', 'timeout'=>8],
['name'=>'Cerebras', 'url'=>'https://api.cerebras.ai/v1/chat/completions', 'key'=>CEREBRAS_KEY, 'model'=>'qwen-3-235b-a22b-instruct-2507', 'timeout'=>8],
];
// Inject matched skills into system prompt
$skills = detectSkills($query);
$skillContext = "Tes compétences activees pour cette requete: " . implode(', ', $skills) . ".";
// Memory context
$memories = recallMemories($query, 3);
$memContext = '';
if ($memories) {
$memContext = " Contexte memoire: " . implode('; ', array_map(fn($m) => $m['key'].': '.$m['value'], $memories));
}
$fullSys = ($sys ?: "Tu es WEVIA, IA souveraine WEVAL Consulting.") . " $skillContext" . $memContext;
// Parallel query
$mh = curl_multi_init();
$handles = [];
foreach ($providers as $p) {
$ch = curl_init($p['url']);
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $p['timeout'], CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer '.$p['key']],
CURLOPT_POSTFIELDS => json_encode(['model'=>$p['model'], 'messages'=>[
['role'=>'system','content'=>$fullSys],
['role'=>'user','content'=>mb_substr($query,0,2000)]
], 'max_tokens'=>800, 'temperature'=>0.7])
]);
curl_multi_add_handle($mh, $ch);
$handles[] = ['ch'=>$ch, 'name'=>$p['name']];
}
// Execute all in parallel
$running = null;
do { curl_multi_exec($mh, $running); curl_multi_select($mh, 0.1); } while ($running > 0);
$responses = [];
foreach ($handles as $h) {
$r = curl_multi_getcontent($h['ch']);
$code = curl_getinfo($h['ch'], CURLINFO_HTTP_CODE);
curl_multi_remove_handle($mh, $h['ch']);
curl_close($h['ch']);
if ($code == 200 && $r) {
$d = json_decode($r, true);
$text = trim($d['choices'][0]['message']['content'] ?? '');
if (strlen($text) > 20) {
$responses[] = ['provider'=>$h['name'], 'text'=>$text, 'len'=>strlen($text)];
}
}
}
curl_multi_close($mh);
if (empty($responses)) return ['response'=>'Service indisponible', 'provider'=>'none', 'skills'=>$skills];
// Pick best (longest + most complete)
usort($responses, fn($a,$b) => $b['len'] - $a['len']);
$winner = $responses[0];
// Log consensus
$db = getDB();
if ($db) {
try {
$db->prepare("INSERT INTO brain.consensus_log (query, provider_count, winner, response, elapsed_ms) VALUES (?,?,?,?,?)")
->execute([mb_substr($query,0,200), count($responses), $winner['provider'], mb_substr($winner['text'],0,500), 0]);
} catch(Exception $e) {}
}
return ['response'=>$winner['text'], 'provider'=>$winner['provider'], 'providers_responded'=>count($responses), 'skills'=>$skills];
}
// ═══════════════════════════════════════════════════════
// PERSISTENT MEMORY
// ═══════════════════════════════════════════════════════
function storeMemory($key, $value, $type = 'fact', $source = 'conversation', $session = '', $confidence = 1.0) {
$db = getDB();
if (!$db) return false;
try {
$db->prepare("INSERT INTO brain.memory (session_id, memory_type, key, value, confidence, source) VALUES (?,?,?,?,?,?)")
->execute([$session, $type, $key, $value, $confidence, $source]);
return true;
} catch(Exception $e) { return false; }
}
function recallMemories($query, $limit = 5) {
$db = getDB();
if (!$db) return [];
try {
// Simple keyword matching (could be upgraded to vector search via Qdrant)
$words = array_filter(explode(' ', preg_replace('/[^a-zA-Z0-9\s]/u', '', strtolower($query))), fn($w) => strlen($w) > 3);
if (empty($words)) return [];
$conditions = array_map(fn($w) => "LOWER(key || ' ' || value) LIKE '%$w%'", array_slice($words, 0, 5));
$sql = "SELECT key, value, memory_type, confidence FROM brain.memory WHERE " . implode(' OR ', $conditions) . " ORDER BY confidence DESC, updated_at DESC LIMIT $limit";
return $db->query($sql)->fetchAll(PDO::FETCH_ASSOC);
} catch(Exception $e) { return []; }
}
// ═══════════════════════════════════════════════════════
// AUTO-LEARNING
// ═══════════════════════════════════════════════════════
function storeLearning($category, $scenario, $input, $output, $score, $quality, $provider, $improvement = '') {
$db = getDB();
if (!$db) return false;
try {
$db->prepare("INSERT INTO brain.learnings (category, scenario, input, output, score, quality, provider, improvement) VALUES (?,?,?,?,?,?,?,?)")
->execute([$category, $scenario, mb_substr($input,0,500), mb_substr($output,0,1000), $score, $quality, $provider, $improvement]);
// Auto-store high-quality responses as memories
if ($score >= 8 && $quality === 'excellent') {
storeMemory("learned:$scenario", mb_substr($output, 0, 500), 'learned', 'auto-learning', '', $score/10);
}
return true;
} catch(Exception $e) { return false; }
}
function getLearningStats() {
$db = getDB();
if (!$db) return ['error' => 'no db'];
try {
$total = $db->query("SELECT COUNT(*) FROM brain.learnings")->fetchColumn();
$excellent = $db->query("SELECT COUNT(*) FROM brain.learnings WHERE quality='excellent'")->fetchColumn();
$recent = $db->query("SELECT category, quality, score FROM brain.learnings ORDER BY created_at DESC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC);
return ['total'=>$total, 'excellent'=>$excellent, 'recent'=>$recent];
} catch(Exception $e) { return ['error' => $e->getMessage()]; }
}
// ═══════════════════════════════════════════════════════
// ACTION ROUTER
// ═══════════════════════════════════════════════════════
switch ($action) {
case 'ask':
$query = $input['query'] ?? $input['message'] ?? '';
if (!$query) { echo json_encode(['error'=>'query required']); break; }
$result = collectiveAsk($query, $input['system'] ?? '', $input['lang'] ?? 'fr');
echo json_encode($result);
break;
case 'remember':
$ok = storeMemory($input['key'] ?? '', $input['value'] ?? '', $input['type'] ?? 'fact', $input['source'] ?? 'api', $input['session'] ?? '');
echo json_encode(['success'=>$ok]);
break;
case 'recall':
$memories = recallMemories($input['query'] ?? '', $input['limit'] ?? 5);
echo json_encode(['memories'=>$memories, 'count'=>count($memories)]);
break;
case 'learn':
$ok = storeLearning($input['category']??'general', $input['scenario']??'', $input['input']??'', $input['output']??'', floatval($input['score']??0), $input['quality']??'unknown', $input['provider']??'');
echo json_encode(['success'=>$ok]);
break;
case 'skills':
$skills = getOpusSkills();
$query = $input['query'] ?? '';
$matched = $query ? detectSkills($query) : array_keys($skills);
echo json_encode(['total'=>count($skills), 'matched'=>$matched, 'skills'=>$skills]);
break;
case 'status':
default:
$db = getDB();
$stats = ['db'=>'disconnected', 'memory'=>0, 'learnings'=>['total'=>0], 'skills'=>count(getOpusSkills()), 'models'=>['cloud'=>3,'local'=>10], 'consensus'=>'ready'];
if ($db) {
try {
$stats['db'] = 'connected';
$stats['memory'] = $db->query("SELECT COUNT(*) FROM brain.memory")->fetchColumn();
$stats['learnings'] = getLearningStats();
$stats['consensus_log'] = $db->query("SELECT COUNT(*) FROM brain.consensus_log")->fetchColumn();
} catch(Exception $e) { $stats['db_error'] = $e->getMessage(); }
}
echo json_encode([
'service' => 'WEVIA Brain Orchestrator',
'version' => '2.0',
'capabilities' => ['collective_consciousness', 'persistent_memory', 'auto_learning', 'opus_skills', 'multi_language'],
'stats' => $stats,
'status' => 'live'
], JSON_PRETTY_PRINT);
}