fix: add multi-stage JSON parsing with fallback extraction
This commit is contained in:
@@ -58,11 +58,48 @@ def _extract_text(message) -> str:
|
||||
|
||||
|
||||
def _parse_json(text: str) -> dict:
|
||||
# Strip markdown fences
|
||||
text = re.sub(r"^```(?:json)?\s*", "", text, flags=re.MULTILINE)
|
||||
text = re.sub(r"\s*```$", "", text, flags=re.MULTILINE)
|
||||
text = text.strip()
|
||||
|
||||
# First attempt: standard parse
|
||||
try:
|
||||
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:
|
||||
raise ValueError(f"Claude response was not valid JSON: {e}\n\nRaw: {text[:500]}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user