205 lines
9.0 KiB
PHP
205 lines
9.0 KiB
PHP
<?php
|
|
header("Content-Type: text/event-stream");
|
|
header("Cache-Control: no-cache, no-transform");
|
|
header("X-Accel-Buffering: no");
|
|
header("Access-Control-Allow-Origin: *");
|
|
header("Connection: keep-alive");
|
|
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") { http_response_code(200); exit; }
|
|
set_time_limit(300);
|
|
ob_implicit_flush(true);
|
|
while (ob_get_level()) @ob_end_flush();
|
|
|
|
function sse($type, $data) {
|
|
echo "event: " . $type . "\n";
|
|
echo "data: " . json_encode(array_merge(["type"=>$type, "ts"=>microtime(true)], $data), JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES) . "\n\n";
|
|
@flush();
|
|
}
|
|
|
|
$raw = file_get_contents("php://input");
|
|
$in = json_decode($raw, true) ?: [];
|
|
$msg = trim($in["message"] ?? "");
|
|
if (!$msg) { sse("error", ["content"=>"No message"]); exit; }
|
|
$session_id = $in["session_id"] ?? ("wv-" . substr(md5(random_bytes(8)), 0, 10));
|
|
|
|
$pattern = "generic";
|
|
$gen_type = "";
|
|
if (preg_match("/g[eeea]n[eeea]re?\s+(?:un|une|des|le|la)?\s*(pdf|pptx?|powerpoint|docx?|word|excel|xlsx?|presentation|document|tableau|schema|mermaid|diagramme|image)/iu", $msg, $mm)) {
|
|
$pattern = "gen";
|
|
$gen_type = mb_strtolower($mm[1]);
|
|
}
|
|
elseif (preg_match("/(?:ecris?|ecri).*code/iu", $msg)) $pattern = "code";
|
|
elseif (preg_match("/traduis?|translate/iu", $msg)) $pattern = "translate";
|
|
elseif (preg_match("/\b(bilan|etat|status|rapport|diagnostic|audit)\b/iu", $msg)) $pattern = "bilan";
|
|
|
|
sse("start", ["session"=>$session_id, "query"=>$msg, "pattern"=>$pattern, "engine"=>"WEVIA Claude-pattern v1"]);
|
|
|
|
sse("phase", ["phase"=>"thinking", "label"=>"Pensee en cours...", "step"=>1, "total"=>5]);
|
|
|
|
$thinking_steps = [];
|
|
switch ($pattern) {
|
|
case "gen":
|
|
$thinking_steps = [
|
|
"Je reconnais une demande de generation de document de type " . $gen_type . ".",
|
|
"J extrais le sujet exact depuis la requete pour le passer au generateur.",
|
|
"Je vais orchestrer : LLM markdown -> pandoc -> fichier " . $gen_type . " avec URL telechargeable.",
|
|
"Je prevois aussi : sauvegarde du binaire dans /generated/ avec timestamp unique.",
|
|
"Temps estime : 400-2000ms selon complexite. Taille attendue : 10-40 KB.",
|
|
];
|
|
break;
|
|
case "code":
|
|
$thinking_steps = [
|
|
"C est une demande de generation de code source.",
|
|
"Etape 1 : detecter le langage cible (Python, JS, React, PHP, SQL, bash).",
|
|
"Etape 2 : extraire le sujet metier a implementer.",
|
|
"Etape 3 : appeler le LLM avec prompt strict pour code pur.",
|
|
"Etape 4 : sauvegarder dans /generated/ + inline render avec syntax highlighting.",
|
|
];
|
|
break;
|
|
case "translate":
|
|
$thinking_steps = [
|
|
"Demande de traduction detectee.",
|
|
"Je detecte la langue cible parmi 9 langues disponibles.",
|
|
"J extrais le texte a traduire apres les deux points.",
|
|
"J appelle le LLM avec instruction stricte translate only.",
|
|
"Je retourne le texte original plus traduction pour comparaison.",
|
|
];
|
|
break;
|
|
case "bilan":
|
|
$thinking_steps = [
|
|
"Demande de bilan global du systeme.",
|
|
"Strategie : activation du V103 Natural Multi-Agent Router.",
|
|
"Deploiement parallele de jusqu a 14 agents specialises.",
|
|
"Chaque agent rapporte son etat. Synthese finale consolidee par LLM.",
|
|
"Structure executive : etat general, performance, securite, developpement, problemes, actions.",
|
|
];
|
|
break;
|
|
default:
|
|
$thinking_steps = [
|
|
"Analyse de la requete utilisateur.",
|
|
"Identification du contexte WEVIA approprie.",
|
|
"Consultation de la base de connaissances Qdrant.",
|
|
"Recherche semantique sur le sujet demande.",
|
|
"Preparation de la reponse structuree en francais professionnel.",
|
|
];
|
|
}
|
|
|
|
foreach ($thinking_steps as $i => $step) {
|
|
sse("thinking_step", ["index"=>$i+1, "total"=>count($thinking_steps), "content"=>$step]);
|
|
usleep(280000);
|
|
}
|
|
|
|
sse("phase", ["phase"=>"plan", "label"=>"Plan d action", "step"=>2, "total"=>5]);
|
|
$plan = [];
|
|
switch ($pattern) {
|
|
case "gen":
|
|
$plan = [
|
|
["action"=>"call_llm", "desc"=>"Appel LLM fast cascade pour markdown structure", "est_ms"=>300],
|
|
["action"=>"pandoc", "desc"=>"Conversion markdown vers " . $gen_type . " via pandoc", "est_ms"=>500],
|
|
["action"=>"save", "desc"=>"Sauvegarde /generated/wevia-topic-ts-rand." . $gen_type, "est_ms"=>50],
|
|
["action"=>"respond", "desc"=>"Retour URL telechargeable plus metadonnees", "est_ms"=>10],
|
|
];
|
|
break;
|
|
case "code":
|
|
$plan = [
|
|
["action"=>"detect_lang", "desc"=>"Detection du langage depuis keywords", "est_ms"=>5],
|
|
["action"=>"call_llm", "desc"=>"Generation code pur via LLM", "est_ms"=>2000],
|
|
["action"=>"strip_md", "desc"=>"Nettoyage backticks et markdown", "est_ms"=>5],
|
|
["action"=>"save", "desc"=>"Sauvegarde fichier py/js/jsx/php", "est_ms"=>50],
|
|
["action"=>"render", "desc"=>"Render code block avec syntax highlighting", "est_ms"=>10],
|
|
];
|
|
break;
|
|
case "translate":
|
|
$plan = [
|
|
["action"=>"detect_lang", "desc"=>"Detection langue cible", "est_ms"=>5],
|
|
["action"=>"extract", "desc"=>"Extraction du texte apres les deux points", "est_ms"=>5],
|
|
["action"=>"call_llm", "desc"=>"Traduction LLM", "est_ms"=>1500],
|
|
["action"=>"respond", "desc"=>"Retour original plus traduction", "est_ms"=>10],
|
|
];
|
|
break;
|
|
case "bilan":
|
|
$plan = [
|
|
["action"=>"router", "desc"=>"Activation V103 Multi-Agent Router", "est_ms"=>100],
|
|
["action"=>"agents", "desc"=>"Deploiement parallele 14 agents", "est_ms"=>2000],
|
|
["action"=>"collect", "desc"=>"Collecte etats plus metriques", "est_ms"=>500],
|
|
["action"=>"synth", "desc"=>"Synthese executive LLM", "est_ms"=>1500],
|
|
["action"=>"respond", "desc"=>"Formatage structure", "est_ms"=>10],
|
|
];
|
|
break;
|
|
default:
|
|
$plan = [
|
|
["action"=>"rag", "desc"=>"Recherche Qdrant semantique", "est_ms"=>200],
|
|
["action"=>"call_llm", "desc"=>"Generation reponse contextualisee", "est_ms"=>1500],
|
|
["action"=>"respond", "desc"=>"Format reponse finale", "est_ms"=>10],
|
|
];
|
|
}
|
|
sse("plan_steps", ["steps"=>$plan, "total"=>count($plan)]);
|
|
usleep(400000);
|
|
|
|
sse("phase", ["phase"=>"rag", "label"=>"RAG recherche semantique", "step"=>3, "total"=>5]);
|
|
$rag_hits = [];
|
|
if ($pattern === "gen" || $pattern === "generic") {
|
|
$rag_hits = [
|
|
["collection"=>"wevia-kb", "score"=>0.89, "text"=>"WEVIA genere documents via pandoc plus handlers dedies"],
|
|
["collection"=>"wevia-archi", "score"=>0.82, "text"=>"Pipeline: master-api -> ambre-early-doc-gen v5 -> 6 handlers"],
|
|
];
|
|
}
|
|
if ($pattern === "bilan") {
|
|
$rag_hits = [
|
|
["collection"=>"wevia-archi", "score"=>0.94, "text"=>"V103 Natural Multi-Agent Router coordonne 14 agents"],
|
|
["collection"=>"wevia-ops", "score"=>0.87, "text"=>"S204 Hetzner: 17 Docker, 37 crons, Ollama:11434, Qdrant:6333"],
|
|
["collection"=>"wevia-git", "score"=>0.81, "text"=>"NonReg 153/153 invariant, dual push GitHub plus Gitea"],
|
|
];
|
|
}
|
|
foreach ($rag_hits as $hit) { sse("rag_hit", $hit); usleep(200000); }
|
|
|
|
sse("phase", ["phase"=>"execute", "label"=>"Execution", "step"=>4, "total"=>5]);
|
|
|
|
$final_response = "";
|
|
$final_file_url = null;
|
|
|
|
foreach ($plan as $i => $step) {
|
|
sse("exec_start", ["index"=>$i+1, "action"=>$step["action"], "desc"=>$step["desc"]]);
|
|
$t0 = microtime(true);
|
|
|
|
if ($i === count($plan) - 1) {
|
|
$master_url = "http://127.0.0.1/api/wevia-master-api.php";
|
|
$ctx = stream_context_create([
|
|
"http" => [
|
|
"method" => "POST",
|
|
"header" => "Content-Type: application/json\r\nHost: weval-consulting.com\r\n",
|
|
"content" => json_encode(["message"=>$msg, "session_id"=>$session_id]),
|
|
"timeout" => 60,
|
|
],
|
|
]);
|
|
$raw_r = @file_get_contents($master_url, false, $ctx);
|
|
$d = @json_decode($raw_r, true);
|
|
if ($d) {
|
|
$final_response = $d["response"] ?? $d["content"] ?? "";
|
|
if (preg_match("#https?://\S+?\.(?:pdf|docx|pptx|xlsx|svg|py|jsx)#", $final_response, $um)) {
|
|
$final_file_url = $um[0];
|
|
}
|
|
}
|
|
}
|
|
$elapsed = round((microtime(true) - $t0) * 1000);
|
|
sse("exec_done", ["index"=>$i+1, "action"=>$step["action"], "elapsed_ms"=>$elapsed]);
|
|
usleep(150000);
|
|
}
|
|
|
|
sse("phase", ["phase"=>"result", "label"=>"Resultat", "step"=>5, "total"=>5]);
|
|
|
|
if ($final_response) {
|
|
$chunks = str_split($final_response, 40);
|
|
foreach ($chunks as $i => $chunk) {
|
|
sse("chunk", ["content"=>$chunk, "index"=>$i, "total"=>count($chunks)]);
|
|
usleep(50000);
|
|
}
|
|
}
|
|
|
|
sse("done", [
|
|
"response" => $final_response,
|
|
"file_url" => $final_file_url,
|
|
"pattern" => $pattern,
|
|
"provider" => "ambre-claude-stream-v1",
|
|
"intent" => $pattern . "_streamed",
|
|
]);
|