168 lines
6.2 KiB
PHP
168 lines
6.2 KiB
PHP
<?php
|
|
/**
|
|
* WEVIA CONSENSUS v4.0 "INTERNET ENABLED"
|
|
* 5 providers Groq + Recherche Web via Compound
|
|
* 100% GRATUIT - 100% ILLIMITÉ - ACCÈS INTERNET
|
|
*/
|
|
header("Content-Type: application/json");
|
|
|
|
class ConsensusV4 {
|
|
private $groq = "https://api.groq.com/openai/v1/chat/completions";
|
|
private $key = "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";
|
|
|
|
private $models = [
|
|
"compound" => "groq/compound",
|
|
"kimi" => "moonshotai/kimi-k2-instruct",
|
|
"qwen" => "qwen/qwen3-32b",
|
|
"llama4" => "meta-llama/llama-4-maverick-17b-128e-instruct",
|
|
"gpt-oss" => "openai/gpt-oss-120b"
|
|
];
|
|
|
|
public function ask($query, $sys = "", $webSearch = true) {
|
|
$start = microtime(true);
|
|
$responses = [];
|
|
$webResults = null;
|
|
|
|
// 1. Si recherche web demandée, utiliser Compound d'abord
|
|
if ($webSearch && $this->needsWebSearch($query)) {
|
|
$webResults = $this->searchWeb($query);
|
|
}
|
|
|
|
// 2. Enrichir le prompt avec les résultats web
|
|
$enrichedQuery = $query;
|
|
if ($webResults) {
|
|
$enrichedQuery = "Contexte web récent:\n" . $webResults . "\n\nQuestion: " . $query;
|
|
}
|
|
|
|
// 3. Query tous les modèles en parallèle
|
|
$mh = curl_multi_init();
|
|
$handles = [];
|
|
|
|
foreach ($this->models as $name => $model) {
|
|
if ($name === "compound" && $webResults) continue;
|
|
|
|
$ch = curl_init($this->groq);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 30,
|
|
CURLOPT_HTTPHEADER => [
|
|
"Content-Type: application/json",
|
|
"Authorization: Bearer " . $this->key
|
|
],
|
|
CURLOPT_POSTFIELDS => json_encode([
|
|
"model" => $model,
|
|
"messages" => [
|
|
["role" => "system", "content" => $sys ?: "Tu es WEVIA, assistant WEVAL Consulting Casablanca. Expert SAP, Cloud, Data, IA. Réponds en français, 2-3 phrases, direct et précis. Utilise les infos web fournies si disponibles."],
|
|
["role" => "user", "content" => $enrichedQuery]
|
|
],
|
|
"max_tokens" => 800,
|
|
"temperature" => 0.7
|
|
])
|
|
]);
|
|
curl_multi_add_handle($mh, $ch);
|
|
$handles[$name] = $ch;
|
|
}
|
|
|
|
do {
|
|
$status = curl_multi_exec($mh, $active);
|
|
curl_multi_select($mh);
|
|
} while ($active && $status == CURLM_OK);
|
|
|
|
foreach ($handles as $name => $ch) {
|
|
$result = curl_multi_getcontent($ch);
|
|
$data = json_decode($result, true);
|
|
if (isset($data["choices"][0]["message"]["content"])) {
|
|
$content = $data["choices"][0]["message"]["content"];
|
|
$content = preg_replace("/<think>.*<\/think>/s", "", $content);
|
|
$responses[$name] = trim($content);
|
|
}
|
|
curl_multi_remove_handle($mh, $ch);
|
|
curl_close($ch);
|
|
}
|
|
curl_multi_close($mh);
|
|
|
|
if (empty($responses)) {
|
|
return ["error" => "No responses", "models" => 0];
|
|
}
|
|
|
|
$best = $this->selectBest($responses);
|
|
$ms = round((microtime(true) - $start) * 1000);
|
|
|
|
return [
|
|
"response" => $best,
|
|
"models" => count($responses),
|
|
"providers" => array_keys($responses),
|
|
"web_search" => $webResults ? true : false,
|
|
"ms" => $ms,
|
|
"version" => "v4.0-internet",
|
|
"cost" => "0€"
|
|
];
|
|
}
|
|
|
|
private function needsWebSearch($query) {
|
|
$keywords = ["actualité", "news", "aujourd'hui", "récent", "2024", "2025", "2026",
|
|
"prix", "météo", "bourse", "cours", "dernier", "nouveau", "latest",
|
|
"current", "now", "today", "recherche", "find", "search", "internet"];
|
|
$q = strtolower($query);
|
|
foreach ($keywords as $kw) {
|
|
if (strpos($q, $kw) !== false) return true;
|
|
}
|
|
if (preg_match("/qui est|what is|where is|how much|combien|où est/i", $query)) return true;
|
|
return false;
|
|
}
|
|
|
|
private function searchWeb($query) {
|
|
$ch = curl_init($this->groq);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 20,
|
|
CURLOPT_HTTPHEADER => [
|
|
"Content-Type: application/json",
|
|
"Authorization: Bearer " . $this->key
|
|
],
|
|
CURLOPT_POSTFIELDS => json_encode([
|
|
"model" => "groq/compound",
|
|
"messages" => [
|
|
["role" => "system", "content" => "Tu es un assistant de recherche. Trouve les informations les plus récentes et pertinentes sur Internet. Réponds avec les faits clés uniquement."],
|
|
["role" => "user", "content" => $query]
|
|
],
|
|
"max_tokens" => 500
|
|
])
|
|
]);
|
|
$result = curl_exec($ch);
|
|
curl_close($ch);
|
|
|
|
$data = json_decode($result, true);
|
|
return $data["choices"][0]["message"]["content"] ?? null;
|
|
}
|
|
|
|
private function selectBest($responses) {
|
|
$best = "";
|
|
$bestScore = 0;
|
|
foreach ($responses as $name => $text) {
|
|
$score = strlen($text);
|
|
if (preg_match("/[.!?]$/", trim($text))) $score += 50;
|
|
if ($name === "compound") $score += 150;
|
|
if ($name === "kimi") $score += 100;
|
|
if (preg_match("/(\b\w+\b).*\1.*\1/i", $text)) $score -= 30;
|
|
if ($score > $bestScore) {
|
|
$bestScore = $score;
|
|
$best = $text;
|
|
}
|
|
}
|
|
return $best;
|
|
}
|
|
}
|
|
|
|
$input = json_decode(file_get_contents("php://input"), true);
|
|
$q = $input["message"] ?? $_GET["q"] ?? "";
|
|
$web = isset($input["web_search"]) ? $input["web_search"] : true;
|
|
|
|
if (!$q) {
|
|
echo json_encode(["error" => "Message requis", "version" => "v4.0-internet"]);
|
|
exit;
|
|
}
|
|
echo json_encode((new ConsensusV4())->ask($q, $input["system"] ?? "", $web));
|