|
|
""" |
|
|
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 |
|
|
""" |
|
|
|
|
|
cleaned = re.sub(r'```json\s*|\s*```', '', response, flags=re.IGNORECASE) |
|
|
cleaned = cleaned.strip() |
|
|
|
|
|
|
|
|
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 <think>...</think> tags and any reasoning content from text |
|
|
Qwen3 uses standard XML format: <think>...</think> |
|
|
|
|
|
Args: |
|
|
text: Text that may contain thinking tags |
|
|
|
|
|
Returns: |
|
|
Clean text without thinking tags |
|
|
""" |
|
|
|
|
|
cleaned = re.sub(r'<think>.*?</think>', '', text, flags=re.DOTALL | re.IGNORECASE) |
|
|
|
|
|
cleaned = re.sub(r'</?think>', '', cleaned, flags=re.IGNORECASE) |
|
|
return cleaned.strip() |
|
|
|