""" Utility functions for the vibe-reader application """ import json import re from typing import Dict, Any, Optional def parse_json_response(response: str) -> Optional[Dict[str, Any]]: """ Parse JSON from LLM response, handling various formats Args: response: Raw LLM response that may contain JSON Returns: Parsed JSON dict, or None if parsing fails """ # Remove markdown code blocks if present cleaned = re.sub(r'```json\s*|\s*```', '', response, flags=re.IGNORECASE) cleaned = cleaned.strip() # Try to find JSON object in the response json_match = re.search(r'\{.*\}', cleaned, re.DOTALL) if json_match: try: return json.loads(json_match.group(0)) except json.JSONDecodeError as e: print(f"JSON parsing error: {e}") return None return None def extract_vibe_components(vibe_json: Dict[str, Any]) -> Dict[str, Any]: """ Extract and validate vibe components from parsed JSON Args: vibe_json: Parsed JSON from vibe extraction Returns: Dictionary with validated vibe components """ return { "aesthetic_genre_keywords": vibe_json.get("aesthetic_genre_keywords", []), "mood_atmosphere": vibe_json.get("mood_atmosphere", []), "core_themes": vibe_json.get("core_themes", []), "tropes": vibe_json.get("tropes", []), "feels_like": vibe_json.get("feels_like", "") } def strip_thinking_tags(text: str) -> str: """ Remove ... tags and any reasoning content from text Qwen3 uses standard XML format: ... Args: text: Text that may contain thinking tags Returns: Clean text without thinking tags """ # Remove ... blocks cleaned = re.sub(r'.*?', '', text, flags=re.DOTALL | re.IGNORECASE) # Remove any leftover tags cleaned = re.sub(r'', '', cleaned, flags=re.IGNORECASE) return cleaned.strip()