Files
html/api/wevia-deepseek-proxy.php
2026-04-20 12:50:02 +02:00

303 lines
16 KiB
PHP

<?php
// WEVIA DeepSeek Proxy v2 — 12+ models FREE via SambaNova + Gemini + Groq
header("Content-Type: application/json");
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Content-Type");
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit;
// Token management endpoint
$raw = json_decode(file_get_contents("php://input"), true) ?: [];
if (($raw['action'] ?? '') === 'set_ds_token' && !empty($raw['token'])) {
$token = trim($raw['token']);
file_put_contents("/etc/weval/deepseek-web-token.txt", $token);
// Also push to DeepSeek Web service
$ch = curl_init("http://localhost:8901/set_token");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_POST=>true, CURLOPT_POSTFIELDS=>json_encode(["token"=>$token]), CURLOPT_HTTPHEADER=>["Content-Type: application/json"], CURLOPT_TIMEOUT=>5]);
$r = curl_exec($ch);
echo json_encode(["status"=>"token_saved","length"=>strlen($token),"ds_web"=>$r?:"ok"]);
exit;
}
if (($raw['action'] ?? '') === 'ds_token_status') {
$has = file_exists("/etc/weval/deepseek-web-token.txt") && strlen(trim(@file_get_contents("/etc/weval/deepseek-web-token.txt"))) > 10;
echo json_encode(["has_token"=>$has]);
exit;
}
$input = json_decode(file_get_contents("php://input"), true);
$message = $input['message'] ?? '';
$mode = $input['mode'] ?? 'instant';
$model = $input['model'] ?? 'auto';
$history = $input['history'] ?? [];
// OPUS 20avr doctrine #2 ZERO simulation: check structured intents BEFORE LLM fallback
// Prevents hallucination on diagnostic/vault/self-reference questions
if (!empty($message) && file_exists(__DIR__ . "/wevia-self-diagnostic-intent.php")) {
require_once __DIR__ . "/wevia-self-diagnostic-intent.php";
if (function_exists("wevia_self_diagnostic")) {
$_sd_result = wevia_self_diagnostic($message);
if ($_sd_result) {
$_sd_content = isset($_sd_result["content"]) ? $_sd_result["content"] : json_encode($_sd_result, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
echo json_encode([
"content" => $_sd_content,
"provider" => "opus46",
"tool" => "self_diagnostic",
"model" => "php-intent-real-exec",
"note" => "Doctrine #2: real shell execution, not LLM simulation"
], JSON_UNESCAPED_UNICODE);
exit;
}
}
}
$temp = floatval($input['temperature'] ?? 0.7);
// === DEEPSEEK WEB DIRECT (port 8901) — FREE UNLIMITED ===
if ($model === 'deepseek-web' || $model === 'deepseek-web-think' || $model === 'deepseek-web-search') {
$ds_mode = 'instant';
if ($model === 'deepseek-web-think' || $mode === 'deepthink') $ds_mode = 'deepthink';
if ($model === 'deepseek-web-search' || $mode === 'search') $ds_mode = 'search';
if ($mode === 'deepthink-search') { $ds_mode = 'deepthink'; } // DeepSeek Web: think takes priority, search via SearXNG
$ds_payload = json_encode(["message" => $message, "mode" => $ds_mode], JSON_UNESCAPED_UNICODE);
$ch = curl_init("http://localhost:8901/chat");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $ds_payload,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_TIMEOUT => 5
]);
$r = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($code === 200) {
$d = @json_decode($r, true);
if (isset($d['content'])) {
$d['provider'] = 'DeepSeek Web UNLIMITED';
$d['model'] = 'deepseek-web-' . $ds_mode;
$d['cost'] = '0€ illimité';
echo json_encode($d);
exit;
}
}
// If DeepSeek Web fails (no token), fall through to cascade
$model = 'auto'; // FALLTHROUGH: use cascade when DS Web fails
}
// === GEMINI NATIVE DEEPTHINK (with thinking chain) ===
if (($mode === 'deepthink' || $mode === 'deepthink-search') && $model !== 'deepseek-web-think') {
$env2 = @file_get_contents("/etc/weval/secrets.env") ?: "";
$gkey = ""; if (preg_match("/GEMINI_KEY=(.+)/", $env2, $gm)) $gkey = trim($gm[1]);
if ($gkey) {
$prompt = $message;
// If search mode too, enrich with SearXNG
if ($mode === 'deepthink-search') {
$enc = urlencode($message);
$sch = curl_init("http://localhost:8888/search?q=$enc&format=json&categories=general");
curl_setopt_array($sch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 6]);
$sr = curl_exec($sch);
$sd = @json_decode($sr, true);
$web = [];
foreach (array_slice($sd["results"] ?? [], 0, 5) as $wr) {
$web[] = $wr["title"] . ": " . substr($wr["content"] ?? "", 0, 120);
}
if ($web) $prompt .= "\n\nResultats web:\n" . implode("\n", $web);
}
$payload = json_encode([
"contents" => [["parts" => [["text" => $prompt]]]],
"systemInstruction" => ["parts" => [["text" => "Tu es WEVIA DeepSeek en mode DeepThink. COMMENCE TOUJOURS par une section de reflexion entre balises <think> et </think> ou tu raisonnes etape par etape, analyses les hypotheses, explores les pistes. PUIS donne ta reponse finale APRES la balise </think>. Format: <think>\nRaisonnement...\n</think>\nReponse finale."]]],
"generationConfig" => [
"thinkingConfig" => ["thinkingBudget" => 2048],
"maxOutputTokens" => 4000
]
], JSON_UNESCAPED_UNICODE);
$t0 = microtime(true);
$ch = curl_init("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=$gkey");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $payload, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_TIMEOUT => 45, CURLOPT_SSL_VERIFYPEER => false]);
$r = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($code === 200) {
$d = @json_decode($r, true);
$parts = $d["candidates"][0]["content"]["parts"] ?? [];
$thinking = ""; $answer = "";
$full = "";
foreach ($parts as $p) {
if (isset($p["text"])) $full .= $p["text"];
}
// Parse <think>...</think> tags
if (preg_match('/<think>(.*?)<\/think>/s', $full, $tm)) {
$thinking = trim($tm[1]);
$answer = trim(preg_replace('/<think>.*?<\/think>/s', '', $full));
} else {
// Fallback: try to split on </think> even without matching regex
if (strpos($full, '</think>') !== false) {
$split = explode('</think>', $full, 2);
$thinking = trim(str_replace('<think>', '', $split[0]));
$answer = trim($split[1] ?? '');
} elseif (strpos($full, '<think>') !== false) {
// <think> exists but no closing tag - treat first 40% as thinking
$thinking = trim(str_replace('<think>', '', substr($full, 0, intval(strlen($full)*0.4))));
$answer = trim(substr($full, intval(strlen($full)*0.4)));
} else {
$answer = $full;
}
}
$elapsed = round((microtime(true) - $t0) * 1000);
$usage = $d["usageMetadata"] ?? [];
echo json_encode([
"content" => $answer ?: ($thinking ?: "No response"),
"thinking" => $thinking,
"provider" => "Gemini 2.5 Flash DeepThink",
"model" => "gemini-2.5-flash-thinking",
"mode" => $mode,
"tokens" => ($usage["totalTokenCount"] ?? 0),
"latency_ms" => $elapsed,
"cost" => "0EUR",
"has_thinking" => strlen($thinking) > 0
]);
exit;
}
}
}
$max_tokens = intval($input['max_tokens'] ?? 2000);
if (!$message) { echo json_encode(["error" => "no message"]); exit; }
$env = @file_get_contents("/etc/weval/secrets.env") ?: "";
// System prompts by mode
// OPUS 20avr doctrine #4 HONNETE: anti-hallucination guard preprended to all system prompts
$__anti_halluc_guard = "REGLES STRICTES DOCTRINE 4 HONNETETE: Tu es un LLM. Tu ne peux PAS executer de commandes shell ni lire de fichiers. Si user demande diagnostic/status/lecture fichier/exec commande: reponds 'Cette requete necessite un intent shell reel. Tape diagnostique toi ou demande a Opus de wire un intent dedie.' N INVENTE JAMAIS outputs commandes PIDs paths MD5 timestamps. Si tu ne connais pas une info factuelle dis-le explicitement. ";
$systems = [
'instant' => "Tu es WEVIA DeepSeek, IA souveraine. Reponds de facon concise et precise.",
'deepthink-search' => "Tu es WEVIA DeepSeek en mode DeepThink+Search. RAISONNE etape par etape ET utilise les resultats de recherche web pour enrichir ton analyse.",
'deepthink' => "Tu es WEVIA DeepSeek en mode DeepThink. RAISONNE etape par etape:\n1. Analyse le probleme\n2. Identifie les hypotheses\n3. Explore chaque piste\n4. Synthetise\n5. Conclus\nSois rigoureux et structure.",
'search' => "Tu es WEVIA DeepSeek avec acces web.",
'expert' => "Tu es WEVIA DeepSeek Expert. PhD-level. Reponds avec rigueur scientifique, citations, formules si necessaire. Profondeur maximale.",
'code' => "Tu es WEVCODE, expert code souverain. Code COMPLET, FONCTIONNEL, documente. Python/PHP/JS/SQL/Bash. Tests inclus.",
'creative' => "Tu es un ecrivain creatif de talent. Style riche, evocateur, immersif. Francais soutenu.",
];
$system = $__anti_halluc_guard . ($systems[$mode] ?? $systems['instant']);
// Search mode: enrich with SearXNG
if ($mode === 'search' || $mode === 'deepthink-search') {
$enc = urlencode($message);
$ch = curl_init("http://localhost:8888/search?q=$enc&format=json&categories=general");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 8]);
$sr = curl_exec($ch);
$sd = @json_decode($sr, true);
$results = [];
foreach (array_slice($sd['results'] ?? [], 0, 6) as $r) {
$results[] = "- " . ($r['title'] ?? '') . ": " . substr($r['content'] ?? '', 0, 150);
}
if ($results) {
$system .= "\n\nRESULTATS WEB:\n" . implode("\n", $results) . "\n\nSynthetise ces resultats dans ta reponse.";
}
}
// Model routing
$samba_key = ""; $gemini_key = ""; $groq_key = ""; $cerebras_key = "";
if (preg_match("/SAMBANOVA_KEY=(.+)/", $env, $m)) $samba_key = trim($m[1]);
if (preg_match("/GEMINI_KEY=(.+)/", $env, $m)) $gemini_key = trim($m[1]);
if (preg_match("/GROQ_KEY=(.+)/", $env, $m)) $groq_key = trim($m[1]);
if (preg_match("/CEREBRAS_API_KEY=(.+)/", $env, $m)) $cerebras_key = trim($m[1]);
// Auto model selection based on mode
$model_map = [
'auto' => [
'instant' => ['DeepSeek-V3.2', 'samba'],
'deepthink-search' => "Tu es WEVIA DeepSeek en mode DeepThink+Search. RAISONNE etape par etape ET utilise les resultats de recherche web pour enrichir ton analyse.",
'deepthink' => ['DeepSeek-R1-0528', 'samba'],
'search' => ['DeepSeek-V3.2', 'samba'],
'expert' => ['gemini-2.5-pro', 'gemini'],
'code' => ['DeepSeek-V3.2', 'samba'],
'creative' => ['Meta-Llama-3.3-70B-Instruct', 'samba'],
],
];
// Build provider list based on model selection
$providers = [];
if ($model === 'auto') {
$auto = $model_map['auto'][$mode] ?? ['DeepSeek-V3.2', 'samba'];
// Primary based on mode
if ($auto[1] === 'samba' && $samba_key) {
$providers[] = ["url" => "https://api.sambanova.ai/v1/chat/completions", "key" => $samba_key, "model" => $auto[0], "name" => "SambaNova " . $auto[0]];
}
if ($auto[1] === 'gemini' && $gemini_key) {
$providers[] = ["url" => "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions", "key" => $gemini_key, "model" => $auto[0], "name" => "Google " . $auto[0]];
}
// Fallbacks
if ($samba_key) $providers[] = ["url" => "https://api.sambanova.ai/v1/chat/completions", "key" => $samba_key, "model" => "DeepSeek-V3.2", "name" => "SambaNova DeepSeek-V3.2"];
if ($gemini_key) $providers[] = ["url" => "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions", "key" => $gemini_key, "model" => "gemini-2.5-flash", "name" => "Gemini 2.5 Flash"];
if ($groq_key) $providers[] = ["url" => "https://api.groq.com/openai/v1/chat/completions", "key" => $groq_key, "model" => "llama-3.3-70b-versatile", "name" => "Groq Llama-3.3-70B"];
if ($cerebras_key) $providers[] = ["url" => "https://api.cerebras.ai/v1/chat/completions", "key" => $cerebras_key, "model" => "llama3.1-8b", "name" => "Cerebras Llama-8B"];
} else {
// Specific model selected
$specific_models = [
'deepseek-r1' => ["url" => "https://api.sambanova.ai/v1/chat/completions", "key" => $samba_key, "model" => "DeepSeek-R1-0528"],
'deepseek-v3.2' => ["url" => "https://api.sambanova.ai/v1/chat/completions", "key" => $samba_key, "model" => "DeepSeek-V3.2"],
'deepseek-v3.1' => ["url" => "https://api.sambanova.ai/v1/chat/completions", "key" => $samba_key, "model" => "DeepSeek-V3.1"],
'llama-4' => ["url" => "https://api.sambanova.ai/v1/chat/completions", "key" => $samba_key, "model" => "Llama-4-Maverick-17B-128E-Instruct"],
'gpt-oss-120b' => ["url" => "https://api.sambanova.ai/v1/chat/completions", "key" => $samba_key, "model" => "gpt-oss-120b"],
'minimax' => ["url" => "https://api.sambanova.ai/v1/chat/completions", "key" => $samba_key, "model" => "MiniMax-M2.5"],
'gemma-3' => ["url" => "https://api.sambanova.ai/v1/chat/completions", "key" => $samba_key, "model" => "gemma-3-12b-it"],
'gemini-2.5-pro' => ["url" => "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions", "key" => $gemini_key, "model" => "gemini-2.5-pro"],
'gemini-2.5-flash' => ["url" => "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions", "key" => $gemini_key, "model" => "gemini-2.5-flash"],
'groq-llama' => ["url" => "https://api.groq.com/openai/v1/chat/completions", "key" => $groq_key, "model" => "llama-3.3-70b-versatile"],
];
if (isset($specific_models[$model])) {
$p = $specific_models[$model];
$p["name"] = ucfirst(str_replace('-', ' ', $model));
$providers[] = $p;
}
}
// Adjust max_tokens for DeepThink
if ($mode === 'deepthink') $max_tokens = max($max_tokens, 4000);
if ($mode === 'expert') $max_tokens = max($max_tokens, 3000);
$messages = [["role" => "system", "content" => $system]];
foreach (array_slice($history, -12) as $h) {
$messages[] = ["role" => $h['role'], "content" => $h['content']];
}
$messages[] = ["role" => "user", "content" => $message];
$t0 = microtime(true);
// === TIER 0: DeepSeek Web FREE UNLIMITED ===
require_once __DIR__ . chr(47) . "deepseek-web-bridge.php";
foreach ($providers as $p) {
if (!$p['key']) continue;
$data = json_encode(["model" => $p["model"], "messages" => $messages, "max_tokens" => $max_tokens, "temperature" => $temp], JSON_UNESCAPED_UNICODE);
$ch = curl_init($p["url"]);
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $data, CURLOPT_HTTPHEADER => ["Content-Type: application/json", "Authorization: Bearer " . $p["key"]], CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => false]);
$r = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($code === 200) {
$d = @json_decode($r, true);
$content = $d["choices"][0]["message"]["content"] ?? null;
$usage = $d["usage"] ?? [];
if ($content) {
$elapsed = round((microtime(true) - $t0) * 1000);
echo json_encode([
"content" => $content,
"provider" => $p["name"],
"model" => $p["model"],
"mode" => $mode,
"tokens" => ($usage["total_tokens"] ?? 0),
"prompt_tokens" => ($usage["prompt_tokens"] ?? 0),
"completion_tokens" => ($usage["completion_tokens"] ?? 0),
"latency_ms" => $elapsed,
"cost" => "0€"
]);
exit;
}
}
}
echo json_encode(["error" => "All providers failed", "content" => "Providers indisponibles. Reessayez."]);