222 lines
9.2 KiB
PHP
222 lines
9.2 KiB
PHP
<?php
|
|
/**
|
|
* WEVIA MASTER API v1.0 — Standalone endpoint
|
|
* URL: /api/wevia-master-api.php
|
|
*
|
|
* ENDPOINTS:
|
|
* POST /api/wevia-master-api.php → Route a message
|
|
* GET /api/wevia-master-api.php?health → Health check
|
|
* GET /api/wevia-master-api.php?stats → Routing statistics
|
|
* GET /api/wevia-master-api.php?test → Quick test
|
|
* GET /api/wevia-master-api.php?dashboard → Visual dashboard
|
|
*/
|
|
|
|
header("Content-Type: application/json; charset=utf-8");
|
|
header("Access-Control-Allow-Origin: *");
|
|
header("Access-Control-Allow-Headers: Content-Type");
|
|
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
|
|
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") { http_response_code(200); exit; }
|
|
|
|
require_once "/opt/wevia-brain/wevia-master-router.php";
|
|
|
|
// ═══ ROUTING ═══
|
|
$action = null;
|
|
if (isset($_GET['health'])) $action = 'health';
|
|
elseif (isset($_GET['stats'])) $action = 'stats';
|
|
elseif (isset($_GET['test'])) $action = 'test';
|
|
elseif (isset($_GET['score'])) $action = 'score';
|
|
elseif (isset($_GET['dashboard'])) $action = 'dashboard';
|
|
elseif (isset($_GET['rag'])) $action = 'rag';
|
|
elseif (isset($_GET['capabilities'])) $action = 'capabilities';
|
|
|
|
switch ($action) {
|
|
case 'health':
|
|
echo json_encode(mr_healthCheck(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
|
|
case 'stats':
|
|
echo json_encode(mr_getStats(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
|
|
case 'test':
|
|
$msg = $_GET['q'] ?? 'Bonjour, comment vas-tu?';
|
|
$result = mr_route($msg, 'Tu es WEVIA, IA souveraine de WEVAL Consulting.');
|
|
echo json_encode([
|
|
'input' => $msg,
|
|
'response' => mb_substr($result['content'], 0, 500),
|
|
'model' => $result['model'],
|
|
'provider' => $result['provider'],
|
|
'tier' => $result['tier'],
|
|
'latency_ms' => $result['latency_ms'],
|
|
'source' => $result['source'],
|
|
'routing' => $result['routing'] ?? null,
|
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
|
|
case 'score':
|
|
$msg = $_GET['q'] ?? '';
|
|
if (empty($msg)) {
|
|
echo json_encode(['error' => 'Pass ?score&q=your+message']);
|
|
exit;
|
|
}
|
|
echo json_encode(mr_scoreComplexity($msg), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
|
|
case 'capabilities':
|
|
$caps = wevia_listCapabilities();
|
|
// Wire dormant capabilities from registry
|
|
$dormFile = '/opt/wevia-brain/wevia-master-capabilities.json';
|
|
if (file_exists($dormFile)) {
|
|
$dorm = json_decode(file_get_contents($dormFile), true);
|
|
$caps['dormant_capabilities'] = $dorm['summary'] ?? [];
|
|
$caps['total_capabilities'] = ($dorm['total'] ?? 0) + count($caps);
|
|
$caps['dormant_categories'] = array_keys($dorm['categories'] ?? []);
|
|
}
|
|
echo json_encode($caps, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
|
|
case 'rag':
|
|
$msg = $_GET['q'] ?? 'Comment fonctionne WEVADS?';
|
|
$rag = rag_search($msg);
|
|
echo json_encode($rag, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
|
|
case 'dashboard':
|
|
header("Content-Type: text/html; charset=utf-8");
|
|
$stats = mr_getStats();
|
|
$health = mr_healthCheck();
|
|
?><!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title>WEVIA Master Router — Dashboard</title>
|
|
<style>
|
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
body{background:#0a0a0f;color:#e8e6f0;font-family:-apple-system,sans-serif;padding:2rem}
|
|
h1{font-size:1.8rem;margin-bottom:1rem;color:#00ff88}
|
|
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:1rem;margin:1.5rem 0}
|
|
.card{background:#12121a;border:1px solid #2a2a3d;border-radius:12px;padding:1.2rem;text-align:center}
|
|
.card .num{font-size:2.2rem;font-weight:700;color:#00ff88;font-family:monospace}
|
|
.card .label{font-size:.75rem;color:#8888a8;margin-top:.3rem}
|
|
.status{display:inline-block;padding:.2em .6em;border-radius:100px;font-size:.7rem;font-family:monospace}
|
|
.up{background:#00ff8822;color:#00ff88;border:1px solid #00ff88}
|
|
.down{background:#ff446622;color:#ff4466;border:1px solid #ff4466}
|
|
table{width:100%;border-collapse:collapse;margin:1rem 0;font-size:.85rem}
|
|
th{background:#1a1a28;padding:.6rem;text-align:left;font-size:.7rem;text-transform:uppercase;letter-spacing:.1em;color:#8888a8}
|
|
td{padding:.5rem .6rem;border-bottom:1px solid #2a2a3d}
|
|
.t0{color:#00ff88}.t1{color:#4488ff}.t2{color:#aa66ff}.t3{color:#ff4466}
|
|
pre{background:#12121a;border:1px solid #2a2a3d;border-radius:8px;padding:1rem;overflow-x:auto;font-size:.75rem;margin:1rem 0}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>⚡ WEVIA Master Router v<?=MR_VERSION?></h1>
|
|
|
|
<div class="grid">
|
|
<div class="card">
|
|
<div class="num"><?=$health['ollama']==='UP'?'<span class="status up">UP</span>':'<span class="status down">DOWN</span>'?></div>
|
|
<div class="label">Ollama (port 11435)</div>
|
|
</div>
|
|
<div class="card"><div class="num"><?=$health['ollama_models']??0?></div><div class="label">Modèles locaux</div></div>
|
|
<div class="card"><div class="num"><?=$health['tier1_providers']?></div><div class="label">Tier 1 (free fast)</div></div>
|
|
<div class="card"><div class="num"><?=$health['tier2_providers']?></div><div class="label">Tier 2 (free quality)</div></div>
|
|
<div class="card"><div class="num"><?=$health['secrets_count']?></div><div class="label">Secrets chargés</div></div>
|
|
</div>
|
|
|
|
<h2 style="margin-top:2rem">📊 Stats par jour</h2>
|
|
<table>
|
|
<thead><tr><th>Date</th><th>Total</th><th class="t0">Tier 0 (local)</th><th class="t1">Tier 1 (fast)</th><th class="t2">Tier 2 (quality)</th><th>Latence moy.</th><th>Coût</th></tr></thead>
|
|
<tbody>
|
|
<?php
|
|
if (is_array($stats)) {
|
|
krsort($stats);
|
|
foreach ($stats as $date => $d) {
|
|
if (!is_array($d)) continue;
|
|
$t0 = $d['by_tier'][0] ?? 0;
|
|
$t1 = $d['by_tier'][1] ?? 0;
|
|
$t2 = $d['by_tier'][2] ?? 0;
|
|
$total = $d['total'] ?? 0;
|
|
$pct0 = $total > 0 ? round($t0/$total*100) : 0;
|
|
echo "<tr><td>$date</td><td>{$total}</td>";
|
|
echo "<td class='t0'>{$t0} ({$pct0}%)</td>";
|
|
echo "<td class='t1'>{$t1}</td><td class='t2'>{$t2}</td>";
|
|
echo "<td>{$d['avg_latency']}ms</td><td>{$d['cost']}€</td></tr>";
|
|
}
|
|
}
|
|
?>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h2>🔍 Test rapide</h2>
|
|
<p style="color:#8888a8;margin:.5rem 0">Exemples:</p>
|
|
<pre>
|
|
curl "<?=$_SERVER['HTTP_HOST']?>/api/wevia-master-api.php?test&q=Bonjour"
|
|
curl "<?=$_SERVER['HTTP_HOST']?>/api/wevia-master-api.php?score&q=Explique+comment+optimiser+nginx"
|
|
curl "<?=$_SERVER['HTTP_HOST']?>/api/wevia-master-api.php?health"
|
|
</pre>
|
|
|
|
<h2>🏗️ Architecture</h2>
|
|
<pre>
|
|
TIER 0 (Souverain, 0€) → Ollama:11435 [weval-brain-v2, qwen2.5:7b, qwen3:4b, mistral, medllama2]
|
|
TIER 1 (Free ultra-fast) → Cerebras, Groq, SambaNova
|
|
TIER 2 (Free quality) → Mistral Cloud, Cohere, Gemini
|
|
TIER 3 (Frontier, payant) → Claude API, GPT API (non implémenté — dernier recours)
|
|
</pre>
|
|
|
|
<h2>📡 Providers actifs</h2>
|
|
<pre><?php
|
|
$t1 = mr_getTier1Providers();
|
|
foreach ($t1 as $n => $c) echo "TIER1 $n: {$c['model']} ({$c['speed']})\n";
|
|
$t2 = mr_getTier2Providers();
|
|
foreach ($t2 as $n => $c) echo "TIER2 $n: {$c['model']} ({$c['speed']})\n";
|
|
?></pre>
|
|
|
|
<p style="margin-top:2rem;font-size:.7rem;color:#555">WEVIA Master Router — WEVAL Consulting — Avril 2026</p>
|
|
</body></html>
|
|
<?php
|
|
exit;
|
|
}
|
|
|
|
// ═══ POST: Route a message ═══
|
|
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
|
echo json_encode(['error' => 'POST required. Try ?health, ?stats, ?test, ?dashboard']);
|
|
exit;
|
|
}
|
|
|
|
// Debug: log raw input
|
|
$rawInput = file_get_contents("php://input");
|
|
@file_put_contents("/tmp/master-debug.log", date("H:i:s")." RAW=".substr($rawInput,0,200)."\n", FILE_APPEND);
|
|
$input = json_decode($rawInput, true);
|
|
$message = $input['message'] ?? $input['msg'] ?? $input['content'] ?? '';
|
|
$system = $input['system'] ?? 'Tu es WEVIA, l\'IA cognitive souveraine de WEVAL Consulting.';
|
|
$history = $input['history'] ?? [];
|
|
$options = $input['options'] ?? [];
|
|
|
|
if (empty(trim($message))) {
|
|
echo json_encode(['error' => 'message required']);
|
|
exit;
|
|
}
|
|
|
|
ob_start();
|
|
try {
|
|
$result = mr_route($message, $system, $history, $options);
|
|
$stray = ob_get_clean();
|
|
if (!$result || empty($result)) {
|
|
echo json_encode(['error'=>'empty_result','stray_output'=>substr($stray,0,500),'provider'=>'unknown','response'=>'']);
|
|
} else {
|
|
// Ensure response key exists for admin compatibility
|
|
if (!isset($result['response']) && isset($result['content'])) {
|
|
$result['response'] = $result['content'];
|
|
}
|
|
$json = json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
if ($json === false) {
|
|
// JSON encode failed - likely UTF8 issue
|
|
array_walk_recursive($result, function(&$v){if(is_string($v))$v=mb_convert_encoding($v,'UTF-8','UTF-8');});
|
|
$json = json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
|
|
}
|
|
echo $json;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
$stray = ob_get_clean();
|
|
echo json_encode(['error'=>$e->getMessage(),'file'=>basename($e->getFile()),'line'=>$e->getLine(),'stray'=>substr($stray,0,200)]);
|
|
}
|