316 lines
16 KiB
PHP
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);
|
|
}
|