Spaces:
Sleeping
Sleeping
""" | |
Formatting tools for displaying analysis results with rich formatting. | |
""" | |
import os | |
import tempfile | |
import traceback | |
from typing import Dict, Any, Tuple | |
from loguru import logger | |
from smolagents import Tool | |
from rich.console import Console | |
from rich.panel import Panel | |
from rich.text import Text | |
from rich.table import Table | |
from rich.theme import Theme | |
from rich.box import ROUNDED | |
from rich.console import Group | |
class FormatAnalysisResultsTool(Tool): | |
"""Tool for formatting analysis results""" | |
name = "format_analysis_results" | |
description = "Formats the analysis results for better readability" | |
inputs = { | |
"analysis_json": {"type": "any", "description": "JSON string or dictionary containing the analysis results"} | |
} | |
output_type = "string" | |
def forward(self, analysis_json: Dict) -> str: | |
""" | |
Formats the analysis results for better readability. | |
Args: | |
analysis_json: Dictionary containing the analysis results | |
Returns: | |
A formatted string representation of the analysis | |
""" | |
analysis = analysis_json | |
logger.debug(f"Analysis structure keys: {list(analysis.keys()) if isinstance(analysis, dict) else 'Not a dictionary'}") | |
try: | |
return self._format_rich(analysis) | |
except Exception as e: | |
error_traceback = traceback.format_exc() | |
logger.error(f"Error in rich formatting: {str(e)}\nTraceback:\n{error_traceback}") | |
self._log_analysis_debug_info(analysis) | |
# Return error message instead of falling back to simple formatting | |
return f"Error formatting analysis results: {str(e)}" | |
def _log_analysis_debug_info(self, analysis: Dict) -> None: | |
"""Log debug information about the analysis dictionary""" | |
if isinstance(analysis, dict): | |
for key, value in analysis.items(): | |
logger.debug(f"Key: {key}, Value type: {type(value)}, Value: {str(value)[:100]}{'...' if len(str(value)) > 100 else ''}") | |
def _create_rich_console(self) -> Tuple[Console, Any]: | |
"""Create and configure a Rich console with a temporary file""" | |
logger.debug("Starting rich formatting") | |
temp_file = tempfile.NamedTemporaryFile(mode='w+', encoding='utf-8', delete=False) | |
# Reduce console width for better display in Gradio UI | |
console = Console(file=temp_file, width=70) | |
# Create a custom theme for consistent styling | |
custom_theme = Theme({ | |
"heading": "bold cyan underline", | |
"highlight": "bold yellow", | |
"positive": "green", | |
"negative": "red", | |
"neutral": "magenta", | |
"quote": "italic yellow", | |
"metadata": "dim white", | |
"conclusion": "bold magenta" | |
}) | |
# Apply the theme to our console | |
console.theme = custom_theme | |
return console, temp_file | |
def _format_summary(self, console: Console, analysis: Dict) -> None: | |
"""Format and print the summary section""" | |
summary = analysis.get("summary", "No summary available") | |
logger.debug(f"Summary content: {summary if summary else 'Not available'}") | |
console.print(Panel( | |
Text(summary, justify="center"), | |
title="[heading]Song Analysis[/]", | |
subtitle="[metadata]Summary[/]", | |
expand=True # Расширять панель на всю доступную ширину | |
)) | |
def _format_info_table(self, console: Console, analysis: Dict) -> None: | |
"""Format and print the info table with themes and mood""" | |
info_table = Table(show_header=False, box=ROUNDED, expand=True) | |
info_table.add_column("Key", style="bold blue") | |
info_table.add_column("Value") | |
# Add the mood | |
mood = analysis.get("mood", "Not specified") | |
logger.debug(f"Mood content: {mood if mood else 'Not specified'}") | |
info_table.add_row("Mood", mood) | |
# Add the themes as a comma-separated list | |
themes = analysis.get("main_themes", []) | |
logger.debug(f"Themes: {themes if themes else 'Not available'}") | |
if themes: | |
themes_text = ", ".join([f"[highlight]{theme}[/]" for theme in themes]) | |
info_table.add_row("Main Themes", themes_text) | |
console.print(info_table) | |
def _format_section(self, console: Console, section: Dict) -> None: | |
"""Format and print a single section""" | |
section_type = section.get("section_type", "Unknown") | |
section_number = section.get("section_number", "") | |
logger.debug(f"Processing section: {section_type} {section_number}") | |
section_title = f"{section_type.title()} {section_number}" if section_number else section_type.title() | |
section_analysis = section.get("analysis", "No analysis available") | |
lines = section.get("lines", []) | |
logger.debug(f"Section lines count: {len(lines) if lines else 0}") | |
# Create a group with the lyrics and analysis | |
section_content = [] | |
if lines: | |
# Format lyrics in a more readable way | |
section_content.append(Text("Lyrics:", style="bold blue")) | |
# Format each lyrics line with the quote style | |
lyrics_lines = [] | |
for line in lines: | |
lyrics_lines.append(f"[quote]{line}[/]") | |
lyrics_panel = Panel( | |
"\n".join(lyrics_lines), | |
border_style="blue", | |
padding=(1, 2), | |
expand=True # Расширять панель на всю доступную ширину | |
) | |
section_content.append(lyrics_panel) | |
section_content.append(Text("Analysis:", style="bold blue")) | |
section_content.append(Text(section_analysis)) | |
# Add the section panel | |
console.print(Panel( | |
Group(*section_content), | |
title=f"[bold cyan]{section_title}[/]", | |
border_style="cyan", | |
expand=True # Расширять панель на всю доступную ширину | |
)) | |
def _format_sections(self, console: Console, analysis: Dict) -> None: | |
"""Format and print all sections""" | |
sections = analysis.get("sections_analysis", []) | |
logger.debug(f"Sections count: {len(sections) if sections else 0}") | |
if sections: | |
console.print("\n[heading]Section-by-Section Analysis[/]") | |
for section in sections: | |
self._format_section(console, section) | |
def _format_conclusion(self, console: Console, analysis: Dict) -> None: | |
"""Format and print the conclusion""" | |
conclusion = analysis.get("conclusion", "No conclusion available") | |
logger.debug(f"Conclusion content: {conclusion if conclusion else 'Not available'}") | |
console.print("\n[heading]Conclusion[/]") | |
console.print(Panel( | |
Text(conclusion, justify="left"), | |
border_style="magenta", | |
expand=True # Расширять панель на всю доступную ширину | |
)) | |
def _format_rich(self, analysis: Dict) -> str: | |
"""Format the analysis using rich formatting""" | |
console, temp_file = self._create_rich_console() | |
# Format each section | |
self._format_summary(console, analysis) | |
self._format_info_table(console, analysis) | |
self._format_sections(console, analysis) | |
self._format_conclusion(console, analysis) | |
# Save output to file and read back as string | |
temp_file.close() | |
with open(temp_file.name, 'r', encoding='utf-8') as f: | |
result = f.read() | |
# Градио не поддерживает HTML-теги pre, поэтому просто возвращаем текст как есть | |
# Текстовое форматирование Rich уже содержит необходимые отступы и пробелы | |
# Clean up the temp file | |
try: | |
os.unlink(temp_file.name) | |
except Exception as e: | |
logger.warning(f"Could not delete temporary file {temp_file.name}: {str(e)}") | |
return result | |