From 950f6750e0d52f7a8dc37f15908941ab3dfc9741 Mon Sep 17 00:00:00 2001 From: chihlasm Date: Sat, 21 Feb 2026 03:55:21 -0500 Subject: [PATCH] fix: strip markdown code fences from AI JSON responses Haiku sometimes wraps its JSON in ```json ... ``` despite the prompt instructing otherwise. Strip fences before parsing to avoid JSONDecodeError at char 0. Co-Authored-By: Claude Sonnet 4.6 --- backend/app/core/ai_tree_generator_service.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/backend/app/core/ai_tree_generator_service.py b/backend/app/core/ai_tree_generator_service.py index 805c84f7..34af11d2 100644 --- a/backend/app/core/ai_tree_generator_service.py +++ b/backend/app/core/ai_tree_generator_service.py @@ -9,6 +9,7 @@ System prompts are static constants to enable Anthropic prompt caching. """ import json import logging +import re import uuid from typing import Any @@ -94,6 +95,15 @@ Return a corrected full JSON object only. No markdown, no prose, no code fences. Fix ALL listed errors while maintaining the same troubleshooting/procedural logic.""" +def _strip_markdown_fences(text: str) -> str: + """Strip markdown code fences if the model wrapped its JSON response.""" + text = text.strip() + match = re.match(r"^```(?:json)?\s*([\s\S]*?)```$", text) + if match: + return match.group(1).strip() + return text + + def _get_client() -> anthropic.AsyncAnthropic: """Get configured async Anthropic client.""" if not settings.ANTHROPIC_API_KEY: @@ -141,7 +151,7 @@ async def scaffold_branches( messages=[{"role": "user", "content": user_message}], ) - raw_text = response.content[0].text + raw_text = _strip_markdown_fences(response.content[0].text) input_tokens = response.usage.input_tokens output_tokens = response.usage.output_tokens cost = _estimate_cost(input_tokens, output_tokens) @@ -197,7 +207,7 @@ async def generate_branch_detail( messages=messages, ) - raw_text = response.content[0].text + raw_text = _strip_markdown_fences(response.content[0].text) total_input += response.usage.input_tokens total_output += response.usage.output_tokens