112 lines
4.8 KiB
PHP
112 lines
4.8 KiB
PHP
<?php
|
|
// WEVIA Multi-AI API — Route vers le meilleur provider disponible
|
|
// Ollama local (qwen3:4b, gemma4:e4b) + cascade externe quand dispo
|
|
header("Content-Type: application/json; charset=utf-8");
|
|
header("Access-Control-Allow-Origin: *");
|
|
header("Access-Control-Allow-Headers: Content-Type");
|
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit;
|
|
|
|
$input = json_decode(file_get_contents("php://input"), true);
|
|
$message = $input['message'] ?? $_GET['q'] ?? '';
|
|
$mode = $input['mode'] ?? 'auto'; // auto, local, creative, deep, code
|
|
$model = $input['model'] ?? 'auto';
|
|
|
|
if (!$message) {
|
|
echo json_encode([
|
|
'service' => 'WEVIA Multi-AI',
|
|
'providers' => [
|
|
'ollama_qwen3' => ['status' => 'UP', 'model' => 'weval-brain-v3', 'speed' => 'fast', 'cost' => '0€'],
|
|
'ollama_gemma4' => ['status' => 'UP', 'model' => 'gemma4:e4b', 'speed' => 'fast', 'cost' => '0€'],
|
|
'deepseek_web' => ['status' => 'pending', 'model' => 'DeepSeek-V3', 'cost' => '0€', 'unlimited' => true],
|
|
'claude' => ['status' => 'key_needed', 'model' => 'claude-sonnet-4'],
|
|
'mistral' => ['status' => 'key_needed', 'model' => 'mistral-small'],
|
|
],
|
|
'modes' => ['auto','local','creative','deep','code'],
|
|
], JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
|
|
$secrets = [];
|
|
foreach (file('/etc/weval/secrets.env', FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES) as $l) {
|
|
if (empty(trim($l)) || $l[0] === '#') continue;
|
|
$p = strpos($l, '='); if ($p) $secrets[trim(substr($l, 0, $p))] = trim(substr($l, $p+1), " \t\"'");
|
|
}
|
|
|
|
// Model selection by mode
|
|
$models = [
|
|
'auto' => 'weval-brain-v3',
|
|
'creative' => 'gemma4:e4b',
|
|
'deep' => 'weval-brain-v3',
|
|
'code' => 'weval-brain-v3',
|
|
'local' => 'weval-brain-v3',
|
|
];
|
|
|
|
$system_prompts = [
|
|
'auto' => 'Tu es WEVIA Engine, IA souveraine de WEVAL Consulting. Réponds de façon précise et utile.',
|
|
'creative' => 'Tu es WEVIA Creative Engine. Écris avec style, créativité et profondeur. Français soutenu.',
|
|
'deep' => 'Tu es WEVIA DeepThink. Raisonne étape par étape. Analyse. Synthétise. Conclus.',
|
|
'code' => 'Tu es WEVCODE. Code COMPLET et FONCTIONNEL. Python/PHP/JS/SQL/Bash. Tests inclus.',
|
|
];
|
|
|
|
$selected_model = $model !== 'auto' ? $model : ($models[$mode] ?? 'weval-brain-v3');
|
|
$system = $system_prompts[$mode] ?? $system_prompts['auto'];
|
|
|
|
// Try providers in order
|
|
$providers = [
|
|
['name' => 'Ollama', 'url' => 'http://127.0.0.1:11434/v1/chat/completions', 'key' => 'ollama', 'model' => $selected_model],
|
|
];
|
|
|
|
// Add external providers if keys exist
|
|
if (!empty($secrets['ANTHROPIC_KEY'])) {
|
|
$providers[] = ['name' => 'Claude', 'url' => 'https://api.anthropic.com/v1/messages', 'key' => $secrets['ANTHROPIC_KEY'], 'model' => 'claude-sonnet-4-20250514', 'format' => 'anthropic'];
|
|
}
|
|
if (!empty($secrets['MISTRAL_KEY'])) {
|
|
$providers[] = ['name' => 'Mistral', 'url' => 'https://api.mistral.ai/v1/chat/completions', 'key' => $secrets['MISTRAL_KEY'], 'model' => 'mistral-small-latest'];
|
|
}
|
|
|
|
$t0 = microtime(true);
|
|
|
|
foreach ($providers as $prov) {
|
|
$ch = curl_init($prov['url']);
|
|
$headers = ['Content-Type: application/json'];
|
|
|
|
if (($prov['format'] ?? '') === 'anthropic') {
|
|
$headers[] = 'x-api-key: ' . $prov['key'];
|
|
$headers[] = 'anthropic-version: 2023-06-01';
|
|
$body = json_encode(['model' => $prov['model'], 'max_tokens' => 2000, 'system' => $system, 'messages' => [['role' => 'user', 'content' => $message]]]);
|
|
} else {
|
|
if ($prov['key'] !== 'ollama') $headers[] = 'Authorization: Bearer ' . $prov['key'];
|
|
$body = json_encode(['model' => $prov['model'], 'messages' => [['role' => 'system', 'content' => $system], ['role' => 'user', 'content' => $message]], 'max_tokens' => 2000, 'temperature' => 0.7]);
|
|
}
|
|
|
|
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, CURLOPT_HTTPHEADER => $headers, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => false]);
|
|
$resp = curl_exec($ch);
|
|
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($code === 200 && $resp) {
|
|
$data = json_decode($resp, true);
|
|
$content = '';
|
|
|
|
if (($prov['format'] ?? '') === 'anthropic') {
|
|
$content = $data['content'][0]['text'] ?? '';
|
|
} else {
|
|
$content = $data['choices'][0]['message']['content'] ?? '';
|
|
}
|
|
|
|
if (!empty($content)) {
|
|
echo json_encode([
|
|
'content' => $content,
|
|
'provider' => $prov['name'],
|
|
'model' => $prov['model'],
|
|
'mode' => $mode,
|
|
'latency_ms' => round((microtime(true) - $t0) * 1000),
|
|
'cost' => 0,
|
|
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
echo json_encode(['error' => 'All providers failed', 'tried' => count($providers)]);
|