[GH-ISSUE #4881] displaying a mermaid diagram slows down subsequent output #52438

Closed
opened 2026-05-05 13:31:42 -05:00 by GiteaMirror · 8 comments
Owner

Originally created by @thiswillbeyourgithub on GitHub (Aug 24, 2024).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/4881

Bug Report

Installation Method

docker

Environment

  • Open WebUI Version: 0.3.15
  • Browser (if applicable): brave 127

Confirmation:

  • [ X ] I have read and followed all the instructions provided in the README.md.
  • [ X ] I am on the latest version of both Open WebUI and Ollama.
  • [ X ] I have included the browser console logs.
  • [ X ] I have included the Docker container logs.
  • [ X ] I have provided the exact steps to reproduce the bug in the "Steps to Reproduce" section below.

Expected Behavior:

No slowing down of output

Actual Behavior:

Every token that arrives is several times slower than with no diagram

Description

Bug Summary:
As soon as a mermaid diagram is displayed in a chat window, every subsequent token of the same chat will be dramatically slowed down.

Reproduction Details

Steps to Reproduce:

  1. Ask anything to an LLM
  2. Replace its output by
```mermaid
flowchart TD
    A[Start] --> B{Is it raining?}
    B -- Yes --> C[Take an umbrella]
    B -- No --> D[Wear sunglasses]
    C --> E[Go outside]
    D --> E
    E --> F[Enjoy your day!]
  1. Make sure it's properly displayed
  2. Ask anything else to the LLM and notice how slower it is

Logs and Screenshots

Browser Console Logs:
No errors in chrome dev tools

Docker Container Logs:
No errors

Screenshots/Screen Recordings (if applicable):
image

Additional Information

Happens on android too

Note

Been happening from at least 0.3.13 I think

Originally created by @thiswillbeyourgithub on GitHub (Aug 24, 2024). Original GitHub issue: https://github.com/open-webui/open-webui/issues/4881 # Bug Report ## Installation Method docker ## Environment - **Open WebUI Version:** 0.3.15 - **Browser (if applicable):** brave 127 **Confirmation:** - [ X ] I have read and followed all the instructions provided in the README.md. - [ X ] I am on the latest version of both Open WebUI and Ollama. - [ X ] I have included the browser console logs. - [ X ] I have included the Docker container logs. - [ X ] I have provided the exact steps to reproduce the bug in the "Steps to Reproduce" section below. ## Expected Behavior: No slowing down of output ## Actual Behavior: Every token that arrives is several times slower than with no diagram ### Description **Bug Summary:** As soon as a mermaid diagram is displayed in a chat window, every subsequent token of the same chat will be dramatically slowed down. ## Reproduction Details **Steps to Reproduce:** 1. Ask anything to an LLM 2. Replace its output by ``` ```mermaid flowchart TD A[Start] --> B{Is it raining?} B -- Yes --> C[Take an umbrella] B -- No --> D[Wear sunglasses] C --> E[Go outside] D --> E E --> F[Enjoy your day!] ``` 3. Make sure it's properly displayed 4. Ask anything else to the LLM and notice how slower it is ## Logs and Screenshots **Browser Console Logs:** No errors in chrome dev tools **Docker Container Logs:** No errors **Screenshots/Screen Recordings (if applicable):** ![image](https://github.com/user-attachments/assets/62ecd8aa-b6c0-4812-8250-5b1035a42b36) ## Additional Information Happens on android too ## Note Been happening from at least 0.3.13 I think
Author
Owner

@thiswillbeyourgithub commented on GitHub (Aug 26, 2024):

Update: I confirm that simply breaking the rendering (turning mermaid into mermai d) of the code block completely resets the speed. Which is otherwise unbearably slow and making openwebui unusable for me. we're talking about speed like 1 token per second on claude sonnet!!

It's really a bummer because it's stopping me from inviting my closed ones on my instance as mermaid diagrams from claude sonnet are extremely helpful!

Edit: tested on firefox too!

edit: still present in 0.3.16

<!-- gh-comment-id:2310821755 --> @thiswillbeyourgithub commented on GitHub (Aug 26, 2024): Update: I confirm that simply breaking the rendering (turning mermaid into `mermai d`) of the code block completely resets the speed. Which is otherwise **unbearably slow** and making openwebui unusable for me. **we're talking about speed like 1 token per second on claude sonnet!!** It's really a bummer because it's stopping me from inviting my closed ones on my instance as mermaid diagrams from claude sonnet are extremely helpful! Edit: tested on firefox too! edit: still present in 0.3.16
Author
Owner

@thiswillbeyourgithub commented on GitHub (Sep 5, 2024):

Still present in 0.3.18 0.3.20 0.3.21

<!-- gh-comment-id:2332191632 --> @thiswillbeyourgithub commented on GitHub (Sep 5, 2024): Still present in ~~0.3.18~~ ~~0.3.20~~ 0.3.21
Author
Owner

@tjbck commented on GitHub (Sep 10, 2024):

Can't seem to reproduce wit the latest dev on my end, if anyone manages to reproduce and find a fix, please let us know!

<!-- gh-comment-id:2342096661 --> @tjbck commented on GitHub (Sep 10, 2024): Can't seem to reproduce wit the latest dev on my end, if anyone manages to reproduce and find a fix, please let us know!
Author
Owner

@mjbommar commented on GitHub (Sep 13, 2024):

@thiswillbeyourgithub are you using webui from a device with chrome hardware acceleration on an iffy platform?

i have this issue using webui from a device with an intel iGPU on latest chrome/ubuntu 24.04, but the issue goes away when disabling chrome hardware acceleration.

<!-- gh-comment-id:2350288362 --> @mjbommar commented on GitHub (Sep 13, 2024): @thiswillbeyourgithub are you using webui from a device with chrome hardware acceleration on an iffy platform? i have this issue using webui from a device with an intel iGPU on latest chrome/ubuntu 24.04, but the issue goes away when disabling chrome hardware acceleration.
Author
Owner

@thiswillbeyourgithub commented on GitHub (Sep 14, 2024):

Thanks for the idea.

Turning down graphics acceleration on brave does not solve the issue, be it on android brave or desktop brave.

On desktop firefox: also happening, not solved by toggling acceleration.
Same for desktop chromium, with acceleration on or off.

A few things to add:

  1. This happens every time a mermaid diagram is rendered and in the chat. I never saw it not do that.
  2. As soon as I break the rendering (eg replacing mermaid by mermaidd) it's instantly as smooth as butter.
  3. It is cumulative: if I render 2 mermaid diagrams, the output is about twice as slow.
  4. I have a a few pipes and filters that normally send a few emitters, I see them now I very slow succession. (And toggling then on or off does not change a thing). That includes filters that only have an inlet, meaning it happens before the generation even takes place.
  5. The CPU is through the roof when the completion happens, on the client side (that includes my phone, that gets hot).
  6. Starting a generation then switching tab does not solve it (the CPU keeps being high)
  7. Using firefox in safe mode does not solve it, so it's not caused by extensions.
  8. Looking at the brave logs, I think I see that my pipe (which prints a few debug prints) is running at a normal speed. Meaning the lag is only on the UI side. (Not so sure about that deduction though)

I did a brave performance debugging:
Here are screenshots, for both I started the recording, then started the generation, then stopped the recording then stopped the generation.

image

Here's the laggy one

image

Here's for a regular generation

Here's a deeper dive in the bottom-up time spending of the laggy version:
image
image
image
image

const render$1 = async function(id2, text, svgContainingElement) {
  var _a, _b, _c, _d, _e, _f;
  addDiagrams();
  const processed = processAndSetConfigs(text);
  text = processed.code;
  const config2 = getConfig$1();
  log$1.debug(config2);
  if (text.length > ((config2 == null ? void 0 : config2.maxTextSize) ?? MAX_TEXTLENGTH)) {
    text = MAX_TEXTLENGTH_EXCEEDED_MSG;
  }
  const idSelector = "#" + id2;
  const iFrameID = "i" + id2;
  const iFrameID_selector = "#" + iFrameID;
  const enclosingDivID = "d" + id2;
  const enclosingDivID_selector = "#" + enclosingDivID;
  let root = select("body");
  const isSandboxed = config2.securityLevel === SECURITY_LVL_SANDBOX;
  const isLooseSecurityLevel = config2.securityLevel === SECURITY_LVL_LOOSE;
  const fontFamily = config2.fontFamily;
  if (svgContainingElement !== void 0) {
    if (svgContainingElement) {
      svgContainingElement.innerHTML = "";
    }
    if (isSandboxed) {
      const iframe = sandboxedIframe(select(svgContainingElement), iFrameID);
      root = select(iframe.nodes()[0].contentDocument.body);
      root.node().style.margin = 0;
    } else {
      root = select(svgContainingElement);
    }
    appendDivSvgG(root, id2, enclosingDivID, `font-family: ${fontFamily}`, XMLNS_XLINK_STD);
  } else {
    removeExistingElements(document, id2, enclosingDivID, iFrameID);
    if (isSandboxed) {
      const iframe = sandboxedIframe(select("body"), iFrameID);
      root = select(iframe.nodes()[0].contentDocument.body);
      root.node().style.margin = 0;
    } else {
      root = select("body");
    }
    appendDivSvgG(root, id2, enclosingDivID);
  }
  let diag;
  let parseEncounteredException;
  try {
    diag = await getDiagramFromText(text, { title: processed.title });
  } catch (error) {
    diag = new Diagram("error");
    parseEncounteredException = error;
  }
  const element = root.select(enclosingDivID_selector).node();
  const diagramType = diag.type;
  const svg = element.firstChild;
  const firstChild = svg.firstChild;
  const diagramClassDefs = (_b = (_a = diag.renderer).getClasses) == null ? void 0 : _b.call(_a, text, diag);
  const rules = createUserStyles(config2, diagramType, diagramClassDefs, idSelector);
  const style1 = document.createElement("style");
  style1.innerHTML = rules;
  svg.insertBefore(style1, firstChild);
  try {
    await diag.renderer.draw(text, id2, version, diag);
  } catch (e) {
    errorRenderer.draw(text, id2, version);
    throw e;
  }
  const svgNode = root.select(`${enclosingDivID_selector} svg`);
  const a11yTitle = (_d = (_c = diag.db).getAccTitle) == null ? void 0 : _d.call(_c);
  const a11yDescr = (_f = (_e = diag.db).getAccDescription) == null ? void 0 : _f.call(_e);
  addA11yInfo(diagramType, svgNode, a11yTitle, a11yDescr);
  root.select(`[id="${id2}"]`).selectAll("foreignobject > *").attr("xmlns", XMLNS_XHTML_STD);
  let svgCode = root.select(enclosingDivID_selector).node().innerHTML;
  log$1.debug("config.arrowMarkerAbsolute", config2.arrowMarkerAbsolute);
  svgCode = cleanUpSvgCode(svgCode, isSandboxed, evaluate(config2.arrowMarkerAbsolute));
  if (isSandboxed) {
    const svgEl = root.select(enclosingDivID_selector + " svg").node();
    svgCode = putIntoIFrame(svgCode, svgEl);
  } else if (!isLooseSecurityLevel) {
    svgCode = DOMPurify.sanitize(svgCode, {
      ADD_TAGS: DOMPURIFY_TAGS,
      ADD_ATTR: DOMPURIFY_ATTR
    });
  }
  attachFunctions();
  if (parseEncounteredException) {
    throw parseEncounteredException;
  }
  const tmpElementSelector = isSandboxed ? iFrameID_selector : enclosingDivID_selector;
  const node = select(tmpElementSelector).node();
  if (node && "remove" in node) {
    node.remove();
  }
  return {
    svg: svgCode,
    bindFunctions: diag.db.bindFunctions
  };
};
Here's the content of the function at line 6036 in mermaid-js

I see here that mermaid is in version 10.9.1
The latest mermaid is in 11.2.0 . The version 11.0.0 started 3 weeks ago and has now apparently seen many debugs. @tjbck could it be tried to upgrade mermaid js? I don't know js and few things about browsers in general so I don't know the exact tradeoff to stay at version 10 instead of 11.

That's most probably as far as I can go on this. But it's really making mermaid unusable, which is a shame because it's a really nice and useful integration otherwise!

edit:

TL;DR

  • I think mermaid is getting rendered in a continuous loop during generations. Either i's a mermaid issue (but couldn't find anything on their github) : can we try upgrading mermaid to their latest v11 instead of staying in v10? OR it's open-webui that's triggering mermaid in a loop.

edit2: I'm thinking it's open-webui's fault. @tjbck is that run too often? If so maybe it's causing other kinds of slowdown for other renderings. I've seen that python seemed very slow to run in codeblocks but thought it must be due to a container of some sort or pyodide.

<!-- gh-comment-id:2350955730 --> @thiswillbeyourgithub commented on GitHub (Sep 14, 2024): Thanks for the idea. Turning down graphics acceleration on brave does not solve the issue, be it on android brave or desktop brave. On desktop firefox: also happening, not solved by toggling acceleration. Same for desktop chromium, with acceleration on or off. A few things to add: 1. This happens every time a mermaid diagram is rendered and in the chat. I never saw it not do that. 2. As soon as I break the rendering (eg replacing mermaid by `mermaidd`) it's instantly as smooth as butter. 3. It is cumulative: if I render 2 mermaid diagrams, the output is about twice as slow. 4. I have a a few pipes and filters that normally send a few emitters, I see them now I very slow succession. (And toggling then on or off does not change a thing). That includes filters that only have an inlet, meaning it happens before the generation even takes place. 5. The CPU is through the roof when the completion happens, on the client side (that includes my phone, that gets hot). 6. Starting a generation then switching tab does not solve it (the CPU keeps being high) 7. Using firefox in safe mode does not solve it, so it's not caused by extensions. 8. Looking at the brave logs, I think I see that my pipe (which prints a few debug prints) is running at a normal speed. Meaning the lag is only on the UI side. (Not so sure about that deduction though) I did a brave performance debugging: Here are screenshots, for both I started the recording, then started the generation, then stopped the recording then stopped the generation. <details> ![image](https://github.com/user-attachments/assets/5b8a251b-1ec6-4834-85f0-5bc1014c9f74) <summary> Here's the laggy one </summary> </details> <details> ![image](https://github.com/user-attachments/assets/9c0b3180-0dc6-4646-8af7-c2cee03a5175) <summary> Here's for a regular generation </summary> </details> Here's a deeper dive in the bottom-up time spending of the laggy version: ![image](https://github.com/user-attachments/assets/312c9d02-2fcc-4334-8f9f-2e12f0b94efd) ![image](https://github.com/user-attachments/assets/2216931a-60d6-4cdb-af4a-9a9fc0bb6655) ![image](https://github.com/user-attachments/assets/f642feb2-4db9-485c-bef6-a265ed369282) ![image](https://github.com/user-attachments/assets/0876b1a7-b79f-4b27-bf74-5455f6e13117) <details> ```js const render$1 = async function(id2, text, svgContainingElement) { var _a, _b, _c, _d, _e, _f; addDiagrams(); const processed = processAndSetConfigs(text); text = processed.code; const config2 = getConfig$1(); log$1.debug(config2); if (text.length > ((config2 == null ? void 0 : config2.maxTextSize) ?? MAX_TEXTLENGTH)) { text = MAX_TEXTLENGTH_EXCEEDED_MSG; } const idSelector = "#" + id2; const iFrameID = "i" + id2; const iFrameID_selector = "#" + iFrameID; const enclosingDivID = "d" + id2; const enclosingDivID_selector = "#" + enclosingDivID; let root = select("body"); const isSandboxed = config2.securityLevel === SECURITY_LVL_SANDBOX; const isLooseSecurityLevel = config2.securityLevel === SECURITY_LVL_LOOSE; const fontFamily = config2.fontFamily; if (svgContainingElement !== void 0) { if (svgContainingElement) { svgContainingElement.innerHTML = ""; } if (isSandboxed) { const iframe = sandboxedIframe(select(svgContainingElement), iFrameID); root = select(iframe.nodes()[0].contentDocument.body); root.node().style.margin = 0; } else { root = select(svgContainingElement); } appendDivSvgG(root, id2, enclosingDivID, `font-family: ${fontFamily}`, XMLNS_XLINK_STD); } else { removeExistingElements(document, id2, enclosingDivID, iFrameID); if (isSandboxed) { const iframe = sandboxedIframe(select("body"), iFrameID); root = select(iframe.nodes()[0].contentDocument.body); root.node().style.margin = 0; } else { root = select("body"); } appendDivSvgG(root, id2, enclosingDivID); } let diag; let parseEncounteredException; try { diag = await getDiagramFromText(text, { title: processed.title }); } catch (error) { diag = new Diagram("error"); parseEncounteredException = error; } const element = root.select(enclosingDivID_selector).node(); const diagramType = diag.type; const svg = element.firstChild; const firstChild = svg.firstChild; const diagramClassDefs = (_b = (_a = diag.renderer).getClasses) == null ? void 0 : _b.call(_a, text, diag); const rules = createUserStyles(config2, diagramType, diagramClassDefs, idSelector); const style1 = document.createElement("style"); style1.innerHTML = rules; svg.insertBefore(style1, firstChild); try { await diag.renderer.draw(text, id2, version, diag); } catch (e) { errorRenderer.draw(text, id2, version); throw e; } const svgNode = root.select(`${enclosingDivID_selector} svg`); const a11yTitle = (_d = (_c = diag.db).getAccTitle) == null ? void 0 : _d.call(_c); const a11yDescr = (_f = (_e = diag.db).getAccDescription) == null ? void 0 : _f.call(_e); addA11yInfo(diagramType, svgNode, a11yTitle, a11yDescr); root.select(`[id="${id2}"]`).selectAll("foreignobject > *").attr("xmlns", XMLNS_XHTML_STD); let svgCode = root.select(enclosingDivID_selector).node().innerHTML; log$1.debug("config.arrowMarkerAbsolute", config2.arrowMarkerAbsolute); svgCode = cleanUpSvgCode(svgCode, isSandboxed, evaluate(config2.arrowMarkerAbsolute)); if (isSandboxed) { const svgEl = root.select(enclosingDivID_selector + " svg").node(); svgCode = putIntoIFrame(svgCode, svgEl); } else if (!isLooseSecurityLevel) { svgCode = DOMPurify.sanitize(svgCode, { ADD_TAGS: DOMPURIFY_TAGS, ADD_ATTR: DOMPURIFY_ATTR }); } attachFunctions(); if (parseEncounteredException) { throw parseEncounteredException; } const tmpElementSelector = isSandboxed ? iFrameID_selector : enclosingDivID_selector; const node = select(tmpElementSelector).node(); if (node && "remove" in node) { node.remove(); } return { svg: svgCode, bindFunctions: diag.db.bindFunctions }; }; ``` <summary>Here's the content of the function at line 6036 in mermaid-js </summary> </details> I see [here](https://github.com/open-webui/open-webui/blob/b64c9d966a6498d4f2ffe12ab1498fab298afb37/package.json#L74) that mermaid is in version 10.9.1 The [latest mermaid](https://github.com/mermaid-js/mermaid/releases) is in 11.2.0 . The version 11.0.0 started 3 weeks ago and has now apparently seen many debugs. @tjbck could it be tried to upgrade mermaid js? I don't know js and few things about browsers in general so I don't know the exact tradeoff to stay at version 10 instead of 11. That's most probably as far as I can go on this. But it's really making mermaid unusable, which is a shame because it's a really nice and useful integration otherwise! edit: # TL;DR * I think mermaid is getting rendered in a continuous loop during generations. Either i's a mermaid issue (but couldn't find anything on their github) : can we try upgrading mermaid to their latest v11 instead of staying in v10? OR it's open-webui that's triggering mermaid in a loop. edit2: I'm thinking it's [open-webui's fault](https://github.com/open-webui/open-webui/blob/b64c9d966a6498d4f2ffe12ab1498fab298afb37/src/lib/components/chat/Messages/CodeBlock.svelte#L221). @tjbck is that run too often? If so maybe it's causing other kinds of slowdown for other renderings. I've seen that python seemed very slow to run in codeblocks but thought it must be due to a container of some sort or pyodide.
Author
Owner

@tjbck commented on GitHub (Sep 17, 2024):

investigating

<!-- gh-comment-id:2356708140 --> @tjbck commented on GitHub (Sep 17, 2024): investigating
Author
Owner

@tjbck commented on GitHub (Sep 21, 2024):

Related #3620

<!-- gh-comment-id:2365314398 --> @tjbck commented on GitHub (Sep 21, 2024): Related #3620
Author
Owner

@thiswillbeyourgithub commented on GitHub (Sep 24, 2024):

I confirm this is fixed in the latest release(s) of today. Thanks a lot!

<!-- gh-comment-id:2371494363 --> @thiswillbeyourgithub commented on GitHub (Sep 24, 2024): I confirm this is fixed in the latest release(s) of today. Thanks a lot!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#52438