Files
html/api/ambre-claude-stream.php
2026-04-21 23:20:02 +02:00

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",
]);