diff --git a/api/agent-leads-sync.json b/api/agent-leads-sync.json index 67a32decb..02bdb2467 100644 --- a/api/agent-leads-sync.json +++ b/api/agent-leads-sync.json @@ -1,6 +1,6 @@ { "agent": "V45_Leads_Sync", - "ts": "2026-04-22T03:10:03+02:00", + "ts": "2026-04-22T03:20:03+02:00", "paperclip_total": 48, "active_customer": 4, "warm_prospect": 5, diff --git a/api/ambre-pw-tests/output/results.json b/api/ambre-pw-tests/output/results.json index c2ca5dfb8..86a25b94f 100644 --- a/api/ambre-pw-tests/output/results.json +++ b/api/ambre-pw-tests/output/results.json @@ -59,18 +59,18 @@ }, "suites": [ { - "title": "v30-final-showcase.spec.js", - "file": "v30-final-showcase.spec.js", + "title": "v37-mermaid.spec.js", + "file": "v37-mermaid.spec.js", "column": 0, "line": 0, "specs": [ { - "title": "V30 · SHOWCASE CLIENT · 12 turns · Laura Carrefour Maroc", + "title": "V37 · mermaid inline render + artifact", "ok": true, "tags": [], "tests": [ { - "timeout": 1200000, + "timeout": 60000, "annotations": [], "expectedStatus": "passed", "projectId": "chromium", @@ -80,138 +80,33 @@ "workerIndex": 0, "parallelIndex": 0, "status": "passed", - "duration": 416707, + "duration": 20618, "errors": [], "stdout": [ { - "text": "📸 Landing\n" + "text": "Mermaid found · 1 divs · 1 SVG rendered · badges: [\"♻️ KB Reused (1 uses)\",\"flowchart\",\"2ms\"]\n" }, { - "text": "\n[01/12] 01-hi · Bonjour, présente toi\n" + "text": "Total: 1.5s · mermaid found: true\n" }, { - "text": " ✅ 1.5s · Enchanté ! Je suis WEVIA, consultant au sein de WEVAL Consulting, une entreprise spécialisée dans la croissance et le développement des entr\n" - }, - { - "text": "\n[02/12] 02-onboard · je m'appelle Laura, je dirige le marketing chez Carrefour Ma\n" - }, - { - "text": " ⚠️ 30.1s · \n" - }, - { - "text": "\n[03/12] 03-calc · calcule 2450 * 1.18 + 900\n" - }, - { - "text": " ⚠️ 21.1s · \n" - }, - { - "text": "\n[04/12] 04-qr · QR code pour https://carrefour.ma\n" - }, - { - "text": " ⚠️ 21.1s · \n" - }, - { - "text": "\n[05/12] 05-image · cree une image de: supermarché moderne éclairé\n" - }, - { - "text": " ⚠️ 36.4s · \n" - }, - { - "text": "\n[06/12] 06-pdf-prem · genere un PDF premium avec graphique sur: stratégie retail d\n" - }, - { - "text": " ⚠️ 60.2s · \n" - }, - { - "text": "\n[07/12] 07-hd · image HD 4K de: rétail store premium lighting\n" - }, - { - "text": " ⚠️ 36.1s · \n" - }, - { - "text": "\n[08/12] 08-search · actualités retail e-commerce Maroc 2026\n" - }, - { - "text": " ⚠️ 36.1s · \n" - }, - { - "text": "\n[09/12] 09-recall · tu te souviens de mon nom et entreprise?\n" - }, - { - "text": " ⚠️ 25.6s · \n" - }, - { - "text": "\n[10/12] 10-mermaid · schéma mermaid du parcours client retail omnicanal\n" - }, - { - "text": " ⚠️ 30.1s · \n" - }, - { - "text": "\n[11/12] 11-pptx · genere une presentation pptx 5 piliers IA retail\n" - }, - { - "text": " ⚠️ 45.2s · \n" - }, - { - "text": "\n[12/12] 12-bilan · récapitule ce qu'on a fait ensemble\n" - }, - { - "text": " ⚠️ 30.1s · \n" - }, - { - "text": "\n═══ V30 SHOWCASE BILAN · 1/12 PASS · 0 errors ═══\n" - }, - { - "text": " ✅ T1 · 01-hi · 1.5s\n" - }, - { - "text": " ⚠️ T2 · 02-onboard · 30.1s\n" - }, - { - "text": " ⚠️ T3 · 03-calc · 21.1s\n" - }, - { - "text": " ⚠️ T4 · 04-qr · 21.1s\n" - }, - { - "text": " ⚠️ T5 · 05-image · 36.4s\n" - }, - { - "text": " ⚠️ T6 · 06-pdf-prem · 60.2s\n" - }, - { - "text": " ⚠️ T7 · 07-hd · 36.1s\n" - }, - { - "text": " ⚠️ T8 · 08-search · 36.1s\n" - }, - { - "text": " ⚠️ T9 · 09-recall · 25.6s\n" - }, - { - "text": " ⚠️ T10 · 10-mermaid · 30.1s\n" - }, - { - "text": " ⚠️ T11 · 11-pptx · 45.2s\n" - }, - { - "text": " ⚠️ T12 · 12-bilan · 30.1s\n" + "text": "Final: {\"mmd_count\":2,\"svg_count\":2}\n" } ], "stderr": [], "retry": 0, - "startTime": "2026-04-22T00:48:02.981Z", + "startTime": "2026-04-22T01:24:19.355Z", "annotations": [], "attachments": [ { "name": "screenshot", "contentType": "image/png", - "path": "/var/www/html/api/ambre-pw-tests/output/v30-final-showcase-V30-·-S-60a02-rns-·-Laura-Carrefour-Maroc-chromium/test-finished-1.png" + "path": "/var/www/html/api/ambre-pw-tests/output/v37-mermaid-V37-·-mermaid-inline-render-artifact-chromium/test-finished-1.png" }, { "name": "video", "contentType": "video/webm", - "path": "/var/www/html/api/ambre-pw-tests/output/v30-final-showcase-V30-·-S-60a02-rns-·-Laura-Carrefour-Maroc-chromium/video.webm" + "path": "/var/www/html/api/ambre-pw-tests/output/v37-mermaid-V37-·-mermaid-inline-render-artifact-chromium/video.webm" } ] } @@ -219,9 +114,9 @@ "status": "expected" } ], - "id": "cc4310032093e4e60c9b-6c3cb198aa2caa48f606", - "file": "v30-final-showcase.spec.js", - "line": 4, + "id": "3cadd0be80db00e4146f-4379f990d10c802019f0", + "file": "v37-mermaid.spec.js", + "line": 3, "column": 1 } ] @@ -229,8 +124,8 @@ ], "errors": [], "stats": { - "startTime": "2026-04-22T00:48:02.397Z", - "duration": 417457.003, + "startTime": "2026-04-22T01:24:18.728Z", + "duration": 21481.236, "expected": 1, "skipped": 0, "unexpected": 0, diff --git a/api/ambre-pw-tests/output/v30-01-01-hi.png b/api/ambre-pw-tests/output/v30-01-01-hi.png deleted file mode 100644 index 4465f6a65..000000000 Binary files a/api/ambre-pw-tests/output/v30-01-01-hi.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-02-02-onboard.png b/api/ambre-pw-tests/output/v30-02-02-onboard.png deleted file mode 100644 index d0d2a7249..000000000 Binary files a/api/ambre-pw-tests/output/v30-02-02-onboard.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-03-03-calc.png b/api/ambre-pw-tests/output/v30-03-03-calc.png deleted file mode 100644 index cb40e8de7..000000000 Binary files a/api/ambre-pw-tests/output/v30-03-03-calc.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-04-04-qr.png b/api/ambre-pw-tests/output/v30-04-04-qr.png deleted file mode 100644 index 8f5e51d9e..000000000 Binary files a/api/ambre-pw-tests/output/v30-04-04-qr.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-05-05-image.png b/api/ambre-pw-tests/output/v30-05-05-image.png deleted file mode 100644 index 554c703da..000000000 Binary files a/api/ambre-pw-tests/output/v30-05-05-image.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-06-06-pdf-prem.png b/api/ambre-pw-tests/output/v30-06-06-pdf-prem.png deleted file mode 100644 index 27245ed30..000000000 Binary files a/api/ambre-pw-tests/output/v30-06-06-pdf-prem.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-07-07-hd.png b/api/ambre-pw-tests/output/v30-07-07-hd.png deleted file mode 100644 index 00ef538ad..000000000 Binary files a/api/ambre-pw-tests/output/v30-07-07-hd.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-08-08-search.png b/api/ambre-pw-tests/output/v30-08-08-search.png deleted file mode 100644 index d68d24e68..000000000 Binary files a/api/ambre-pw-tests/output/v30-08-08-search.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-09-09-recall.png b/api/ambre-pw-tests/output/v30-09-09-recall.png deleted file mode 100644 index 8ecb3d74d..000000000 Binary files a/api/ambre-pw-tests/output/v30-09-09-recall.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-10-10-mermaid.png b/api/ambre-pw-tests/output/v30-10-10-mermaid.png deleted file mode 100644 index 5aa394b94..000000000 Binary files a/api/ambre-pw-tests/output/v30-10-10-mermaid.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-11-11-pptx.png b/api/ambre-pw-tests/output/v30-11-11-pptx.png deleted file mode 100644 index dd70bf590..000000000 Binary files a/api/ambre-pw-tests/output/v30-11-11-pptx.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-12-12-bilan.png b/api/ambre-pw-tests/output/v30-12-12-bilan.png deleted file mode 100644 index ed8c25933..000000000 Binary files a/api/ambre-pw-tests/output/v30-12-12-bilan.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-99-final.png b/api/ambre-pw-tests/output/v30-99-final.png deleted file mode 100644 index ed8c25933..000000000 Binary files a/api/ambre-pw-tests/output/v30-99-final.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-bilan.json b/api/ambre-pw-tests/output/v30-bilan.json deleted file mode 100644 index 4fdea17da..000000000 --- a/api/ambre-pw-tests/output/v30-bilan.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "pass": 1, - "total": 12, - "results": [ - { - "t": 1, - "lb": "01-hi", - "pass": true, - "err": false, - "el": "1.5" - }, - { - "t": 2, - "lb": "02-onboard", - "pass": false, - "err": false, - "el": "30.1" - }, - { - "t": 3, - "lb": "03-calc", - "pass": false, - "err": false, - "el": "21.1" - }, - { - "t": 4, - "lb": "04-qr", - "pass": false, - "err": false, - "el": "21.1" - }, - { - "t": 5, - "lb": "05-image", - "pass": false, - "err": false, - "el": "36.4" - }, - { - "t": 6, - "lb": "06-pdf-prem", - "pass": false, - "err": false, - "el": "60.2" - }, - { - "t": 7, - "lb": "07-hd", - "pass": false, - "err": false, - "el": "36.1" - }, - { - "t": 8, - "lb": "08-search", - "pass": false, - "err": false, - "el": "36.1" - }, - { - "t": 9, - "lb": "09-recall", - "pass": false, - "err": false, - "el": "25.6" - }, - { - "t": 10, - "lb": "10-mermaid", - "pass": false, - "err": false, - "el": "30.1" - }, - { - "t": 11, - "lb": "11-pptx", - "pass": false, - "err": false, - "el": "45.2" - }, - { - "t": 12, - "lb": "12-bilan", - "pass": false, - "err": false, - "el": "30.1" - } - ] -} \ No newline at end of file diff --git a/api/ambre-pw-tests/output/v30-final-showcase-V30-·-S-60a02-rns-·-Laura-Carrefour-Maroc-chromium/test-finished-1.png b/api/ambre-pw-tests/output/v30-final-showcase-V30-·-S-60a02-rns-·-Laura-Carrefour-Maroc-chromium/test-finished-1.png deleted file mode 100644 index ed8c25933..000000000 Binary files a/api/ambre-pw-tests/output/v30-final-showcase-V30-·-S-60a02-rns-·-Laura-Carrefour-Maroc-chromium/test-finished-1.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-final-showcase-V30-·-S-60a02-rns-·-Laura-Carrefour-Maroc-chromium/video.webm b/api/ambre-pw-tests/output/v30-final-showcase-V30-·-S-60a02-rns-·-Laura-Carrefour-Maroc-chromium/video.webm deleted file mode 100644 index 41c6622d1..000000000 Binary files a/api/ambre-pw-tests/output/v30-final-showcase-V30-·-S-60a02-rns-·-Laura-Carrefour-Maroc-chromium/video.webm and /dev/null differ diff --git a/api/ambre-pw-tests/output/v30-00-landing.png b/api/ambre-pw-tests/output/v37-00-load.png similarity index 100% rename from api/ambre-pw-tests/output/v30-00-landing.png rename to api/ambre-pw-tests/output/v37-00-load.png diff --git a/api/ambre-pw-tests/output/v37-01-mermaid.png b/api/ambre-pw-tests/output/v37-01-mermaid.png new file mode 100644 index 000000000..382023834 Binary files /dev/null and b/api/ambre-pw-tests/output/v37-01-mermaid.png differ diff --git a/api/ambre-pw-tests/output/v37-02-reuse.png b/api/ambre-pw-tests/output/v37-02-reuse.png new file mode 100644 index 000000000..b317ff988 Binary files /dev/null and b/api/ambre-pw-tests/output/v37-02-reuse.png differ diff --git a/api/ambre-pw-tests/output/v37-mermaid-V37-·-mermaid-inline-render-artifact-chromium/test-finished-1.png b/api/ambre-pw-tests/output/v37-mermaid-V37-·-mermaid-inline-render-artifact-chromium/test-finished-1.png new file mode 100644 index 000000000..5b68bf6ab Binary files /dev/null and b/api/ambre-pw-tests/output/v37-mermaid-V37-·-mermaid-inline-render-artifact-chromium/test-finished-1.png differ diff --git a/api/ambre-pw-tests/output/v37-mermaid-V37-·-mermaid-inline-render-artifact-chromium/video.webm b/api/ambre-pw-tests/output/v37-mermaid-V37-·-mermaid-inline-render-artifact-chromium/video.webm new file mode 100644 index 000000000..35a3bc87e Binary files /dev/null and b/api/ambre-pw-tests/output/v37-mermaid-V37-·-mermaid-inline-render-artifact-chromium/video.webm differ diff --git a/api/ambre-pw-tests/tests/v30-final-showcase.spec.js b/api/ambre-pw-tests/tests/v30-final-showcase.spec.js deleted file mode 100644 index ce1c4e2b2..000000000 --- a/api/ambre-pw-tests/tests/v30-final-showcase.spec.js +++ /dev/null @@ -1,92 +0,0 @@ -const { test } = require("@playwright/test"); -const fs = require("fs"); - -test("V30 · SHOWCASE CLIENT · 12 turns · Laura Carrefour Maroc", async ({ page }) => { - test.setTimeout(1200000); - - await page.goto("/wevia.html"); - await page.evaluate(() => { try { sessionStorage.clear(); localStorage.clear(); } catch(e){} }); - await page.waitForLoadState("networkidle"); - await page.waitForTimeout(3000); - await page.screenshot({ path: "output/v30-00-landing.png" }); - console.log("📸 Landing"); - - const turns = [ - { lb: "01-hi", msg: "Bonjour, présente toi", needle: /WEVIA|bonjour|aider|consulting/i, mx: 25 }, - { lb: "02-onboard", msg: "je m'appelle Laura, je dirige le marketing chez Carrefour Maroc", needle: /Laura/i, mx: 30 }, - { lb: "03-calc", msg: "calcule 2450 * 1.18 + 900", needle: /3791|3792|🧮|calcul/i, mx: 20 }, - { lb: "04-qr", msg: "QR code pour https://carrefour.ma", needle: /wevia-qr-|📱|QR/i, mx: 20 }, - { lb: "05-image", msg: "cree une image de: supermarché moderne éclairé", needle: /wevia-img-|🎨|image/i, mx: 35 }, - { lb: "06-pdf-prem", msg: "genere un PDF premium avec graphique sur: stratégie retail digital Carrefour Maroc 2026", needle: /PDF Premium|\.pdf|📄|pages/i, mx: 60 }, - { lb: "07-hd", msg: "image HD 4K de: rétail store premium lighting", needle: /wevia-hd-|4K/i, mx: 35 }, - { lb: "08-search", msg: "actualités retail e-commerce Maroc 2026", needle: /🔍|source|actual|e-comm/i, mx: 35 }, - { lb: "09-recall", msg: "tu te souviens de mon nom et entreprise?", needle: /Laura.*Carrefour|Carrefour.*Laura/i, mx: 25 }, - { lb: "10-mermaid", msg: "schéma mermaid du parcours client retail omnicanal", needle: /graph|flowchart|mermaid/i, mx: 30 }, - { lb: "11-pptx", msg: "genere une presentation pptx 5 piliers IA retail", needle: /\.pptx|📊|PowerPoint|presentation/i, mx: 45 }, - { lb: "12-bilan", msg: "récapitule ce qu'on a fait ensemble", needle: /Laura|Carrefour|PDF|image|QR|pptx|parcours/i, mx: 30 }, - ]; - - const results = []; - - for (let i = 0; i < turns.length; i++) { - const t = turns[i]; - const num = String(i+1).padStart(2,"0"); - console.log(`\n[${num}/12] ${t.lb} · ${t.msg.substring(0,60)}`); - - try { - const input = page.locator("#msgInput"); - await input.click({force:true}); - await page.keyboard.press("Control+A"); - await page.keyboard.press("Delete"); - await input.fill(t.msg); - await page.waitForTimeout(400); - - const beforeCount = await page.evaluate(() => document.querySelectorAll(".msg.assistant").length); - await input.press("Enter"); - - const ws = Date.now(); - let found = false; let reply = ""; let hasErr = false; - - while (Date.now() - ws < t.mx * 1000) { - const s = await page.evaluate((bc) => { - const a = Array.from(document.querySelectorAll(".msg.assistant .bubble")); - const cnt = a.length; - const last = cnt > bc ? a[a.length-1].innerText : ""; - return { cnt, last }; - }, beforeCount); - - if (s.cnt > beforeCount && s.last.length > 25) { - reply = s.last; - if (t.needle.test(reply)) { found = true; break; } - if (/une erreur|indisponible/i.test(reply)) { hasErr = true; break; } - } - await page.waitForTimeout(1500); - } - - const el = ((Date.now()-ws)/1000).toFixed(1); - const st = found ? "✅" : (hasErr ? "❌" : "⚠️"); - console.log(` ${st} ${el}s · ${reply.substring(0,140).replace(/\n/g,' ')}`); - - await page.evaluate(() => { const m = document.getElementById("messages"); if(m) m.scrollTop = m.scrollHeight; }); - await page.waitForTimeout(1200); - await page.screenshot({ path: `output/v30-${num}-${t.lb}.png` }); - - results.push({ t: i+1, lb: t.lb, pass: found, err: hasErr, el }); - await page.waitForTimeout(800); - } catch (e) { - console.log(` ❌ exception: ${e.message.substring(0,90)}`); - results.push({ t: i+1, lb: t.lb, pass: false, err: true }); - } - } - - await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); - await page.waitForTimeout(2000); - await page.screenshot({ path: "output/v30-99-final.png", fullPage: true }); - - const p = results.filter(r=>r.pass).length; - const e = results.filter(r=>r.err).length; - console.log(`\n═══ V30 SHOWCASE BILAN · ${p}/${results.length} PASS · ${e} errors ═══`); - results.forEach(r => console.log(` ${r.pass?"✅":(r.err?"❌":"⚠️")} T${r.t} · ${r.lb} · ${r.el||"?"}s`)); - - fs.writeFileSync("output/v30-bilan.json", JSON.stringify({ pass: p, total: results.length, results }, null, 2)); -}); diff --git a/api/ambre-pw-tests/tests/v37-mermaid.spec.js b/api/ambre-pw-tests/tests/v37-mermaid.spec.js new file mode 100644 index 000000000..752f9c252 --- /dev/null +++ b/api/ambre-pw-tests/tests/v37-mermaid.spec.js @@ -0,0 +1,59 @@ +const { test } = require("@playwright/test"); + +test("V37 · mermaid inline render + artifact", async ({ page }) => { + test.setTimeout(60000); + + await page.goto("/wevia.html"); + await page.waitForLoadState("networkidle"); + await page.waitForTimeout(3500); + await page.screenshot({ path: "output/v37-00-load.png" }); + + const input = page.locator("#msgInput"); + await input.click({force:true}); + await input.fill("mermaid schéma architecture IA souveraine WEVIA"); + await page.waitForTimeout(400); + await input.press("Enter"); + + // Wait for mermaid div + const start = Date.now(); + let found = false; + let kbSrc = "unknown"; + while (Date.now() - start < 45000) { + const s = await page.evaluate(() => { + const mmd = document.querySelectorAll(".msg.assistant .mermaid"); + const badges = document.querySelectorAll(".msg.assistant .nx-badge"); + return { + mermaid_count: mmd.length, + svg_rendered: Array.from(mmd).filter(m => m.querySelector("svg")).length, + badge_texts: Array.from(badges).map(b => b.innerText), + }; + }); + if (s.mermaid_count > 0) { + found = true; + console.log(`Mermaid found · ${s.mermaid_count} divs · ${s.svg_rendered} SVG rendered · badges: ${JSON.stringify(s.badge_texts)}`); + if (s.svg_rendered > 0) break; + } + await page.waitForTimeout(1500); + } + + const el = ((Date.now()-start)/1000).toFixed(1); + console.log(`Total: ${el}s · mermaid found: ${found}`); + await page.waitForTimeout(2000); + await page.screenshot({ path: "output/v37-01-mermaid.png", fullPage: false }); + + // Test 2 · architecture déjà dans KB → reuse + await input.click({force:true}); + await page.keyboard.press("Control+A"); + await page.keyboard.press("Delete"); + await input.fill("diagramme parcours client retail omnicanal"); + await input.press("Enter"); + + await page.waitForTimeout(8000); + await page.screenshot({ path: "output/v37-02-reuse.png", fullPage: false }); + + const final = await page.evaluate(() => ({ + mmd_count: document.querySelectorAll(".msg.assistant .mermaid").length, + svg_count: document.querySelectorAll(".msg.assistant .mermaid svg").length, + })); + console.log("Final:", JSON.stringify(final)); +}); diff --git a/api/ambre-pw-v37-deploy.php b/api/ambre-pw-v37-deploy.php new file mode 100644 index 000000000..ddbd9c594 --- /dev/null +++ b/api/ambre-pw-v37-deploy.php @@ -0,0 +1,7 @@ + $written]); diff --git a/api/ambre-v10-fix.php b/api/ambre-v10-fix.php new file mode 100644 index 000000000..e4ba815cd --- /dev/null +++ b/api/ambre-v10-fix.php @@ -0,0 +1,27 @@ +"pattern not found in V10"]); + exit; +} + +$new_c = str_replace($old, $new, $c); +$backup = "/opt/wevads/vault/wevia.html.GOLD-" . date("Ymd-His") . "-v10-fix"; +@copy($path, $backup); +$wrote = @file_put_contents($path, $new_c); + +echo json_encode([ + "delta" => strlen($new_c) - $orig, + "wrote" => $wrote, +]); diff --git a/api/ambre-wire-v10.php b/api/ambre-wire-v10.php new file mode 100644 index 000000000..9fd6db82e --- /dev/null +++ b/api/ambre-wire-v10.php @@ -0,0 +1,122 @@ +true, "size"=>$orig]); + exit; +} + +// Insert BEFORE V9-PDF-PREMIUM (so mermaid match comes first if both patterns match) +$anchor = " // === AMBRE-V9-PDF-PREMIUM 2026-04-22"; +$idx = strpos($c, $anchor); +if ($idx === false) { + echo json_encode(["error"=>"V9 anchor not found"]); + exit; +} + +// Also make sure the _ambre_gen_pat doesn't catch "mermaid" which we want to route here first +// Currently _ambre_gen_pat matches mermaid → route to V2-GEN-ROUTER +// Solution: put V10 BEFORE V2 check (before all routers), by placing it in send() very early +// Actually just before V9 is OK since that's before V2 in flow + +$v10 = <<<'JS' + // === AMBRE-V10-MERMAID 2026-04-22 · Mermaid RAG + inline SVG render + artifact panel === + var _mermaid_intent_pat = /(?:mermaid|sch[eé]ma|diagramme|flowchart|sequence\s+diagram|gantt\s+chart)/i; + if (_mermaid_intent_pat.test(text)) { + if (typeof showThinking === 'function') showThinking(); + busy = true; + try{var sb=document.getElementById("sendBtn");if(sb)sb.disabled=true;}catch(e){} + + var _fetch = (typeof window.__ambreFetch === 'function') ? window.__ambreFetch : fetch; + _fetch('/api/ambre-tool-mermaid.php', { + method: 'POST', + headers: {'Content-Type':'application/json'}, + body: JSON.stringify({topic: text}) + }) + .then(function(r){ return r.text().then(function(t){ try{return JSON.parse(t);}catch(e){return null;} }); }) + .then(function(data){ + if (typeof hideThinking === 'function') hideThinking(); + busy = false; + try{var sb=document.getElementById("sendBtn");if(sb)sb.disabled=false;}catch(e){} + try{var mi=document.getElementById("msgInput");if(mi){mi.value="";mi.disabled=false;}}catch(e){} + + if (!data || !data.ok) { + addMsg('assistant', '❌ Erreur génération Mermaid. ' + ((data && data.error) || 'Réessayez.'), '0'); + return; + } + + var mcode = data.mermaid_code; + var topic = data.topic || text; + var src = data.source || 'unknown'; + var kind = data.kind || 'flowchart'; + + // Badges + var srcBadge = (src === 'kb_reused') ? + '♻️ KB Reused (' + (data.use_count || 0) + ' uses)' : + '🧠 LLM Generated'; + + var badges = '
' + + mcode.replace(//g,'>') + '
" + + mcode.replace(//g,">") + "
" + + mcode.replace(//g,">") + "