Files
html/api/wevia-stream-sovereign.php
2026-04-14 21:30:02 +02:00

97 lines
4.0 KiB
PHP

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Content-Type');
header('X-Accel-Buffering: no');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; }
set_time_limit(3600);
ignore_user_abort(false);
ob_implicit_flush(true);
while (ob_get_level()) ob_end_flush();
function sse($type, $data) {
echo "data: " . json_encode(['type' => $type, 'content' => $data], JSON_UNESCAPED_UNICODE) . "\n\n";
flush();
}
$input = json_decode(file_get_contents('php://input'), true);
$msg = trim($input['message'] ?? $input['prompt'] ?? '');
$system = $input['system'] ?? 'Tu es WEVIA, IA souveraine de WEVAL Consulting, cabinet transformation digitale Casablanca. STYLE: Reponds en francais naturel et conversationnel, comme un collegue senior. Phrases completes, structurees, jamais du JSON brut. Direct, pro, chaleureux. Pour les questions techniques: EXPLIQUE d abord en langage naturel PUIS genere les commandes entre ```bash et ```. Apres execution, commente le resultat. Si probleme, propose un fix. Ne dis JAMAIS Information confidentielle. Tu es la pour aider. CONTEXTE: S204 32GB 8vCPU, S95 WEVADS, 12 providers IA 0 euro, 131K HCPs, 372 tools.';
$maxTk = min((int)($input['max_tokens'] ?? 2000), 8000);
$exec = ($input['exec'] ?? true) !== false;
if (!$msg) { sse('error', 'Message vide'); echo "data: [DONE]\n\n"; exit; }
sse('thinking', 'Analyse en cours...');
$ch = curl_init('http://127.0.0.1:4000/v1/chat/completions');
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'model' => 'auto',
'messages' => [['role'=>'system','content'=>$system],['role'=>'user','content'=>$msg]],
'max_tokens' => $maxTk, 'temperature' => 0.3
])
]);
$resp = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$latency = round(curl_getinfo($ch, CURLINFO_TOTAL_TIME) * 1000);
curl_close($ch);
if ($httpCode !== 200 || !$resp) {
sse('error', 'Sovereign indisponible (HTTP ' . $httpCode . ')');
echo "data: [DONE]\n\n"; exit;
}
$data = json_decode($resp, true);
$fullText = $data['choices'][0]['message']['content'] ?? '';
$provider = $data['provider'] ?? 'sovereign';
if (!$fullText) { sse('error', 'Reponse vide'); echo "data: [DONE]\n\n"; exit; }
sse('provider', ['name' => $provider, 'latency_ms' => $latency]);
// Stream word by word + detect and execute bash blocks
$words = preg_split('/(\s+)/', $fullText, -1, PREG_SPLIT_DELIM_CAPTURE);
$buf = '';
$inCode = false;
$codeLang = '';
foreach ($words as $w) {
$buf .= $w;
if (preg_match('/```(\w*)/', $w, $m) && !$inCode) {
$inCode = true;
$codeLang = $m[1] ?: 'bash';
}
if ($inCode && substr_count($buf, '```') >= 2) {
$inCode = false;
if (preg_match('/```\w*\s*(.*?)```/s', $buf, $cm)) {
$code = trim($cm[1]);
sse('token', $buf);
$buf = '';
if ($exec && in_array($codeLang, ['bash','sh','']) && $code) {
$bad = ['rm -rf /','mkfs','dd if=',':(){','chmod -R 777 /','reboot','shutdown'];
$blocked = false;
foreach ($bad as $b) { if (stripos($code, $b) !== false) { $blocked = true; break; } }
if ($blocked) {
sse('exec_blocked', 'Commande dangereuse bloquee');
} else {
sse('exec_start', $code);
$out = trim(shell_exec($code . ' 2>&1'));
sse('exec_result', ['cmd'=>$code, 'output'=>substr($out,0,5000), 'ok'=>true]);
}
}
continue;
}
}
if (!$inCode && strlen($buf) > 15) {
sse('token', $buf);
$buf = '';
usleep(12000);
}
}
if ($buf) sse('token', $buf);
sse('done', ['provider'=>$provider, 'latency_ms'=>$latency, 'words'=>str_word_count($fullText)]);
echo "data: [DONE]\n\n";