[go: up one dir, main page]

Skip to content

GFM rendering is broken in messages

Problem

While working on Fix GLQL visualization in Duo chat UI (gitlab#573052) we discovered that GitLab Flavored Markdown (GFM) rendering is broken in Duo UI messages.

The issue seems to be that renderGFM is called multiple times before a message has finished streaming. This causes some of the GFM components like GLQL to error out because they cannot render incomplete markdown.

I've logged the calls for a single user + agent message:

image

Root cause

The root cause appears to be that the safeguard preventing DuoChatMessage & MessageBase from calling renderGFM until the message has finished streaming is not working.

Debugging the code I see that this.isChunk always returns false because this.message.chunkId = undefined. Below I've logged out the message obj contents from a Duo agentic conversation.

Click to show logs

Stream started

{
    "status": null,
    "correlation_id": null,
    "message_type": "agent",
    "message_sub_type": null,
    "timestamp": "2025-10-14T12:22:07.971538+00:00",
    "content": "Here",
    "tool_info": null,
    "additional_context": null,
    "requestId": "154-1",
    "role": "assistant"
}

Still streaming…

{
    "status": null,
    "correlation_id": null,
    "message_type": "agent",
    "message_sub_type": null,
    "timestamp": "2025-10-14T12:22:07.971538+00:00",
    "content": "Here are the GLQL queries you requested:\n\n```glql\ndisplay: list\nfields: title\nlimit: 10\nquery: state = opened\n```\n\n```glql",
    "tool_info": null,
    "additional_context": null,
    "requestId": "154-1",
    "role": "assistant"
}

Finished streaming

{
    "message_type": "agent",
    "message_sub_type": null,
    "content": "Here are the GLQL queries you requested:\n\n```glql\ndisplay: list\nfields: title\nlimit: 10\nquery: state = opened\n```\n\n```glql\ndisplay: list\nfields: title\nlimit: 25\nquery: state = closed\n```",
    "timestamp": "2025-10-14T12:22:08.690550+00:00",
    "status": "success",
    "correlation_id": null,
    "tool_info": null,
    "additional_context": null,
    "requestId": "154-1",
    "role": "assistant"
}

Additionally there's a bug in the code where renderGFM is called immediately in $nextTick because it's not passed as a callback and is instead executed immediately.

this.$nextTick(this.renderGFM(this.$refs.content))

Proposed solution

Update the message components:

  • Only call renderGFM once the message has finished streaming.
    • The only reliable indicator of this seems to be "status":"success"
  • Ensure that renderGFM is only called once per message.
    • Updates to the messages array are causing each message to spam renderGFM.
  • Fix the $nextTick call of renderGFM.

Example:

      if (this.message.status === 'success' && !this.hasCalledGFM && this.$refs.content) {
        this.$nextTick(() => {
          this.renderGFM(this.$refs.content);
          console.log('hydrateContentWithGFM > $nextTick', this.$refs.content);
        });
        this.hasCalledGFM = true;
      }

Applying this fixes the render GFM issues in GitLab and it reduces the calls to 1 for each user + agent message.

Screenshot_2025-10-14_at_14.14.45

Debugging method

  • Have this project and GDK installed
  • Follow these instructions to link your local Duo UI with GDK
  • Modify Duo UI code

Note that I had a few bugs with the instructions and had to run this to fix it:

# in /gdk/gitlab/
cd ee/frontend_islands/apps/duo_next/
yalc add @gitlab/duo-ui
yarn install --check-files

To push changes:

# in duo-ui/
yarn build && yalc push

# Wait a second and then run this in gdk/gitlab/
gdk stop vite && rm -rf node_modules/.vite tmp/cache && gdk start vite
Edited by Jiaan Louw