diff --git a/studio/frontend/src/components/assistant-ui/__tests__/html-svg-renderer.test.tsx b/studio/frontend/src/components/assistant-ui/__tests__/html-svg-renderer.test.tsx
index 7a89654c11..c74a3d0495 100644
--- a/studio/frontend/src/components/assistant-ui/__tests__/html-svg-renderer.test.tsx
+++ b/studio/frontend/src/components/assistant-ui/__tests__/html-svg-renderer.test.tsx
@@ -203,6 +203,23 @@ describe("parseIncompleteCodeFence", () => {
it("returns null when the block is not a fence at all", () => {
expect(parseIncompleteCodeFence("just text")).toBeNull();
});
+
+ it("recovers a final-but-never-closed fence (small LLMs drop the closing ```)", () => {
+ // Regression: live probe against Qwen3-0.6B caught the model emitting a
+ // complete SVG body but no closing ```. parseCodeFence rejects (strict
+ // ``` ... ``` shape), so the StreamdownBlock fallback must still find the
+ // fence via parseIncompleteCodeFence even after streaming completes -- or
+ // HtmlSvgRenderer never mounts and the user sees a plain code block.
+ const finalNoClose =
+ "```svg\n";
+ expect(parseCodeFence(finalNoClose)).toBeNull();
+ const fence = parseIncompleteCodeFence(finalNoClose);
+ expect(fence).not.toBeNull();
+ expect(fence?.language).toBe("svg");
+ expect(fence?.source).toContain("");
+ });
});
describe("Fence helpers", () => {
diff --git a/studio/frontend/src/components/assistant-ui/markdown-text.tsx b/studio/frontend/src/components/assistant-ui/markdown-text.tsx
index 7201c9956b..b728f5302c 100644
--- a/studio/frontend/src/components/assistant-ui/markdown-text.tsx
+++ b/studio/frontend/src/components/assistant-ui/markdown-text.tsx
@@ -222,13 +222,14 @@ function renderHighlightedCode(props: BlockProps, codeFence: CodeFenceInfo) {
function StreamdownBlock(props: BlockProps) {
const hasMermaidFence = props.content.includes("```mermaid");
const mermaidSource = getMermaidSource(props.content);
- // parseCodeFence requires a closing ```; while the fence is still
- // streaming we fall through to parseIncompleteCodeFence so HtmlSvgRenderer
- // can mount with isIncomplete=true and lock the Code tab on partial
- // HTML/SVG fences (the advertised stream-in behaviour).
+ // parseCodeFence requires a closing ```; we fall back to
+ // parseIncompleteCodeFence both while the fence is still streaming AND
+ // when a finished reply forgot to emit the closing ``` (small local LLMs
+ // routinely drop it). Without the second fallback the HtmlSvgRenderer
+ // never mounts on an unclosed final message and the reply degrades to a
+ // plain code block.
const codeFence =
- parseCodeFence(props.content) ??
- (props.isIncomplete ? parseIncompleteCodeFence(props.content) : null);
+ parseCodeFence(props.content) ?? parseIncompleteCodeFence(props.content);
if (props.isIncomplete && hasMermaidFence) {
return (