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:
|
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]}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user