fix: add multi-stage JSON parsing with fallback extraction

This commit is contained in:
danbusse
2026-06-22 22:37:54 -06:00
parent 22d9688bb1
commit f446cfaaa0
+37
View File
@@ -58,11 +58,48 @@ def _extract_text(message) -> str:
def _parse_json(text: str) -> dict: def _parse_json(text: str) -> dict:
# Strip markdown fences
text = re.sub(r"^```(?:json)?\s*", "", text, flags=re.MULTILINE) text = re.sub(r"^```(?:json)?\s*", "", text, flags=re.MULTILINE)
text = re.sub(r"\s*```$", "", text, flags=re.MULTILINE) text = re.sub(r"\s*```$", "", text, flags=re.MULTILINE)
text = text.strip() text = text.strip()
# First attempt: standard parse
try: try:
return json.loads(text) return json.loads(text)
except json.JSONDecodeError:
pass
# Second attempt: try with json5/demjson-style fixes
# Fix unescaped apostrophes inside JSON strings heuristically
# Replace 's that appear mid-word inside a string context
fixed = re.sub(r"(?<=[a-zA-Z])'(?=[a-zA-Z])", "\\'", text)
try:
return json.loads(fixed)
except json.JSONDecodeError:
pass
# Third attempt: extract just the cards array if top-level parse fails
# This handles truncated responses
cards_match = re.search(r'"cards"\s*:\s*(\[.*?\])\s*[,}]', text, re.DOTALL)
decklist_match = re.search(r'"decklist"\s*:\s*(\[.*?\])\s*[,}]', text, re.DOTALL)
deck_match = re.search(r'"deck"\s*:\s*(\[.*?\])\s*[,}]', text, re.DOTALL)
cards_text = None
for match in [cards_match, decklist_match, deck_match]:
if match:
cards_text = match.group(1)
break
if cards_text:
try:
cards = json.loads(cards_text)
return {"deck_name": "Generated Deck", "strategy_summary": "", "cards": cards}
except json.JSONDecodeError:
pass
# Last resort: raise with original error
try:
json.loads(text)
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
raise ValueError(f"Claude response was not valid JSON: {e}\n\nRaw: {text[:500]}") raise ValueError(f"Claude response was not valid JSON: {e}\n\nRaw: {text[:500]}")