tonko22 commited on
Commit
c3b461c
·
1 Parent(s): 7539685

Update a lot...

Browse files
Gradio_UI.py CHANGED
@@ -197,9 +197,15 @@ class GradioUI:
197
 
198
  def interact_with_agent(self, prompt, messages):
199
  import gradio as gr
200
-
201
  messages.append(gr.ChatMessage(role="user", content=prompt))
202
  yield messages
 
 
 
 
 
 
203
  for msg in stream_to_gradio(self.agent, task=prompt, reset_agent_memory=False):
204
  messages.append(msg)
205
  yield messages
@@ -275,7 +281,7 @@ class GradioUI:
275
  2. Agent will **search for the lyrics**.
276
  3. Agent will **summarize the song's meaning** based on the lyrics using pre-defined prompt (see files).
277
  """
278
-
279
  with gr.Blocks(fill_height=True) as demo:
280
  gr.Markdown(ui_tip_text)
281
  stored_messages = gr.State([])
 
197
 
198
  def interact_with_agent(self, prompt, messages):
199
  import gradio as gr
200
+
201
  messages.append(gr.ChatMessage(role="user", content=prompt))
202
  yield messages
203
+
204
+ prompt = f"""1. Find and extract the lyrics of the song: {prompt}.
205
+ 2. Perform deep lyrics analysis and return full lyrics and analysis
206
+ 3. Generate an image based on the song analysis using generate_image tool
207
+ 4. Return rich-formatted string.
208
+ """
209
  for msg in stream_to_gradio(self.agent, task=prompt, reset_agent_memory=False):
210
  messages.append(msg)
211
  yield messages
 
281
  2. Agent will **search for the lyrics**.
282
  3. Agent will **summarize the song's meaning** based on the lyrics using pre-defined prompt (see files).
283
  """
284
+
285
  with gr.Blocks(fill_height=True) as demo:
286
  gr.Markdown(ui_tip_text)
287
  stored_messages = gr.State([])
agents/single_agent.py CHANGED
@@ -9,6 +9,7 @@ from config import load_prompt_templates
9
  from tools.analysis_tools import AnalyzeLyricsTool
10
  from tools.formatting_tools import FormatAnalysisResultsTool
11
  from tools.search_tools import ThrottledDuckDuckGoSearchTool, BraveSearchTool
 
12
 
13
 
14
  def create_single_agent(model):
@@ -32,13 +33,14 @@ def create_single_agent(model):
32
  VisitWebpageTool(),
33
  AnalyzeLyricsTool(),
34
  FormatAnalysisResultsTool(),
 
35
  FinalAnswerTool(),
36
  ],
37
  model=model,
38
- additional_authorized_imports=['numpy', 'bs4', 'rich'],
39
  max_steps=25,
40
  verbosity_level=2,
41
- description="Specialized agent for finding and extracting song lyrics specified by the user using search and analysis tools. Performs lyrics search and after getting the lyrics full text from the web, it performs the analysis using given tools. Provides detailed commentary with beautiful and human-readable format using rich library formatting. Format analysis results in rich colorful output.",
42
  prompt_templates=prompt_templates
43
  )
44
 
 
9
  from tools.analysis_tools import AnalyzeLyricsTool
10
  from tools.formatting_tools import FormatAnalysisResultsTool
11
  from tools.search_tools import ThrottledDuckDuckGoSearchTool, BraveSearchTool
12
+ from tools.image_generation_tools import GenerateImageTool
13
 
14
 
15
  def create_single_agent(model):
 
33
  VisitWebpageTool(),
34
  AnalyzeLyricsTool(),
35
  FormatAnalysisResultsTool(),
36
+ GenerateImageTool(),
37
  FinalAnswerTool(),
38
  ],
39
  model=model,
40
+ additional_authorized_imports=['numpy', 'bs4', 'rich', 'json'],
41
  max_steps=25,
42
  verbosity_level=2,
43
+ description="Specialized agent for finding and extracting song lyrics specified by the user using search and analysis tools. Performs lyrics search and after getting the lyrics full text from the web, it performs the analysis using given tools. Generates visual representation of the song based on the analysis results. Provides detailed commentary with beautiful and human-readable format using rich library formatting. Format analysis results in rich colorful output.",
44
  prompt_templates=prompt_templates
45
  )
46
 
app.py CHANGED
@@ -5,8 +5,6 @@ Lyrics Analyzer Agent - Main Entry Point
5
  This module serves as the entry point for the Lyrics Analyzer application, which
6
  uses a system of specialized agents to search for and analyze song lyrics.
7
  """
8
- import os
9
-
10
  from loguru import logger
11
  from smolagents import LiteLLMModel
12
 
@@ -32,14 +30,16 @@ def main():
32
  setup_logger()
33
  load_api_keys()
34
 
35
- is_test = os.environ.get('SPACE_ID') is None
 
 
36
 
37
  # If using Ollama, we need to specify the API base URL
38
  # Initialize the LLM model based on configuration
39
- model_id = get_model_id(use_local=is_test)
40
 
41
  logger.info(f"Initializing with model: {model_id}")
42
- if is_test:
43
  api_base = get_ollama_api_base()
44
  logger.info(f"Using Ollama API base: {api_base}")
45
  model = LiteLLMModel(model_id=model_id, api_base=api_base)
@@ -55,21 +55,19 @@ def main():
55
  # Determine if we're in test mode (local) or production (HuggingFace)
56
  # HuggingFace environment has SPACE_ID environment variable
57
 
58
- gradio_config = get_gradio_config(is_test)
 
 
59
 
60
  # Launch with appropriate configuration
61
  launch_kwargs = {
62
  "debug": gradio_config["debug"],
63
- "share": gradio_config["share"]
 
 
64
  }
65
 
66
- # Add server parameters only for local testing
67
- if is_test:
68
- launch_kwargs.update({
69
- "server_name": gradio_config["server_name"],
70
- "server_port": gradio_config["server_port"]
71
- })
72
-
73
  GradioUI(single_agent).launch(**launch_kwargs)
74
  logger.success("Server started successfully")
75
 
 
5
  This module serves as the entry point for the Lyrics Analyzer application, which
6
  uses a system of specialized agents to search for and analyze song lyrics.
7
  """
 
 
8
  from loguru import logger
9
  from smolagents import LiteLLMModel
10
 
 
30
  setup_logger()
31
  load_api_keys()
32
 
33
+ # use_local = os.environ.get('SPACE_ID') is None
34
+ use_local_llm = False
35
+ use_localhost = True
36
 
37
  # If using Ollama, we need to specify the API base URL
38
  # Initialize the LLM model based on configuration
39
+ model_id = get_model_id(provider='openrouter')
40
 
41
  logger.info(f"Initializing with model: {model_id}")
42
+ if use_local_llm:
43
  api_base = get_ollama_api_base()
44
  logger.info(f"Using Ollama API base: {api_base}")
45
  model = LiteLLMModel(model_id=model_id, api_base=api_base)
 
55
  # Determine if we're in test mode (local) or production (HuggingFace)
56
  # HuggingFace environment has SPACE_ID environment variable
57
 
58
+ gradio_config = get_gradio_config(use_localhost=use_localhost)
59
+
60
+ # Инструкции агенту настраиваются непосредственно в GradioUI.py
61
 
62
  # Launch with appropriate configuration
63
  launch_kwargs = {
64
  "debug": gradio_config["debug"],
65
+ "share": gradio_config["share"],
66
+ "server_name": gradio_config["server_name"],
67
+ "server_port": gradio_config["server_port"]
68
  }
69
 
70
+ # Передаем инструкцию в конструктор GradioUI
 
 
 
 
 
 
71
  GradioUI(single_agent).launch(**launch_kwargs)
72
  logger.success("Server started successfully")
73
 
config.py CHANGED
@@ -70,7 +70,7 @@ SEARCH_TOOL_CONFIG = {
70
 
71
 
72
  # Gradio UI configuration
73
- def get_gradio_config(is_test=True):
74
  """Get the appropriate Gradio UI configuration based on environment.
75
 
76
  Args:
@@ -80,7 +80,7 @@ def get_gradio_config(is_test=True):
80
  Returns:
81
  Dictionary with Gradio configuration parameters.
82
  """
83
- if is_test:
84
  # Configuration for local development/testing
85
  return {
86
  "debug": True,
 
70
 
71
 
72
  # Gradio UI configuration
73
+ def get_gradio_config(use_localhost=True):
74
  """Get the appropriate Gradio UI configuration based on environment.
75
 
76
  Args:
 
80
  Returns:
81
  Dictionary with Gradio configuration parameters.
82
  """
83
+ if use_localhost:
84
  # Configuration for local development/testing
85
  return {
86
  "debug": True,
run_single_agent.py CHANGED
@@ -30,7 +30,7 @@ else:
30
  # model_id='https://pflgm2locj2t89co.us-east-1.aws.endpoints.huggingface.cloud'
31
 
32
  # Prompt the user for the song name
33
- song_data = "Felix Da Housecat - Everyone is someone in LA"
34
 
35
  agent = create_single_agent(model)
36
  prompt = f"""1. Find and extract the lyrics of the song: {song_data}.
 
30
  # model_id='https://pflgm2locj2t89co.us-east-1.aws.endpoints.huggingface.cloud'
31
 
32
  # Prompt the user for the song name
33
+ song_data = "Mechanical Me - Beachy Head"
34
 
35
  agent = create_single_agent(model)
36
  prompt = f"""1. Find and extract the lyrics of the song: {song_data}.
tools/__init__.py CHANGED
@@ -1,3 +1,5 @@
1
  """
2
- Tools for interacting with search engines and analyzing song lyrics.
3
  """
 
 
 
1
  """
2
+ Tools for interacting with search engines, analyzing song lyrics, and generating images.
3
  """
4
+
5
+ from .image_generation_tools import GenerateImageTool
tools/analysis_tools.py CHANGED
@@ -75,17 +75,11 @@ class AnalyzeLyricsTool(Tool):
75
 
76
  # Use the function with retry mechanism
77
  logger.info("Analyzing lyrics for song: '{}' by '{}'", song_title, artist)
 
78
  response_text = make_api_call_with_retry(model_to_use, prompt)
79
-
80
- try:
81
- # Parse the string response into a JSON object (dictionary)
82
- logger.debug(f"Parsing JSON response for {song_title}")
83
- response_json = json.loads(response_text)
84
- return response_json
85
- except json.JSONDecodeError as e:
86
- logger.error(f"Failed to parse lyrics analysis response as JSON: {str(e)}")
87
- # Return the raw text response if parsing fails
88
- # This will likely cause an error in the formatting step,
89
- # but at least we'll have the raw output for debugging
90
- return response_text
91
 
 
75
 
76
  # Use the function with retry mechanism
77
  logger.info("Analyzing lyrics for song: '{}' by '{}'", song_title, artist)
78
+
79
  response_text = make_api_call_with_retry(model_to_use, prompt)
80
+ # Parse the string response into a JSON object (dictionary)
81
+ logger.debug(f"Parsing JSON response for {song_title}")
82
+ response_json = json.loads(response_text)
83
+ return response_json
84
+
 
 
 
 
 
 
 
85
 
tools/formatting_tools.py CHANGED
@@ -2,9 +2,10 @@
2
  Formatting tools for displaying analysis results with rich formatting.
3
  """
4
 
5
- import json
 
6
  import traceback
7
- from typing import Dict, Any, List, Union
8
  from loguru import logger
9
  from smolagents import Tool
10
  from rich.console import Console
@@ -32,215 +33,172 @@ class FormatAnalysisResultsTool(Tool):
32
  Formats the analysis results for better readability.
33
 
34
  Args:
35
- analysis_json: Dics JSON containing the analysis results
36
- # Format:
37
- # {
38
- # "summary": "Overall analysis of the song vibes, meaning and mood",
39
- # "main_themes": ["theme1", "theme2", ...],
40
- # "mood": "The overall mood/emotion of the song",
41
- # "sections_analysis": [
42
- # {
43
- # "section_type": "verse/chorus/bridge/etc.",
44
- # "section_number": 1,
45
- # "lines": ["line1", "line2", ...],
46
- # "analysis": "Analysis of this section whith respect to the overall theme"
47
- # },
48
- # ...
49
- # ],
50
- # "conclusion": "The song vibes and concepts of the underlying meaning"
51
- # }
52
- pretty: Whether to use rich formatting (True) or simple text formatting (False)
53
 
54
  Returns:
55
  A formatted string representation of the analysis
56
  """
57
-
58
- analysis = analysis_json
59
- # Log the structure of the parsed analysis
60
- logger.debug(f"Analysis structure keys: {list(analysis.keys()) if isinstance(analysis, dict) else 'Not a dictionary'}")
61
-
62
- if pretty:
63
- # Rich formatting with the rich library
64
- try:
65
- logger.debug("Starting rich formatting")
66
- # Create a console that outputs to a temporary file
67
- import tempfile
68
- temp_file = tempfile.NamedTemporaryFile(mode='w+', encoding='utf-8', delete=False)
69
- console = Console(file=temp_file, width=100)
70
-
71
- # Create a custom theme for consistent styling
72
- # Using direct style definitions instead of named styles
73
- custom_theme = Theme({
74
- "heading": "bold cyan underline",
75
- "highlight": "bold yellow",
76
- "positive": "green",
77
- "negative": "red",
78
- "neutral": "magenta",
79
- "quote": "italic yellow",
80
- "metadata": "dim white",
81
- "conclusion": "bold magenta" # Add style for conclusion
82
- })
83
-
84
- # Apply the theme to our console
85
- console.theme = custom_theme
86
-
87
- # Format the summary section
88
- summary = analysis.get("summary", "No summary available")
89
- logger.debug(f"Summary content: {summary if summary else 'Not available'}")
90
- console.print(Panel(Text(summary, justify="center"), title="[heading]Song Analysis[/]", subtitle="[metadata]Summary[/]"))
91
-
92
- # Create a table for the main themes and mood
93
- info_table = Table(show_header=False, box=ROUNDED, expand=True)
94
- info_table.add_column("Key", style="bold blue")
95
- info_table.add_column("Value")
96
-
97
- # Add the mood
98
- mood = analysis.get("mood", "Not specified")
99
- logger.debug(f"Mood content: {mood if mood else 'Not specified'}")
100
- info_table.add_row("Mood", mood)
101
-
102
- # Add the themes as a comma-separated list
103
- themes = analysis.get("main_themes", [])
104
- logger.debug(f"Themes: {themes if themes else 'Not available'}")
105
- if themes:
106
- themes_text = ", ".join([f"[highlight]{theme}[/]" for theme in themes])
107
- info_table.add_row("Main Themes", themes_text)
108
-
109
- console.print(info_table)
110
-
111
- # Section-by-section analysis
112
- sections = analysis.get("sections_analysis", [])
113
- logger.debug(f"Sections count: {len(sections) if sections else 0}")
114
- if sections:
115
- console.print("\n[heading]Section-by-Section Analysis[/]")
116
-
117
- for section in sections:
118
- section_type = section.get("section_type", "Unknown")
119
- section_number = section.get("section_number", "")
120
- logger.debug(f"Processing section: {section_type} {section_number}")
121
- section_title = f"{section_type.title()} {section_number}" if section_number else section_type.title()
122
-
123
- section_analysis = section.get("analysis", "No analysis available")
124
- lines = section.get("lines", [])
125
- logger.debug(f"Section lines count: {len(lines) if lines else 0}")
126
-
127
- # Create a group with the lyrics and analysis
128
- section_content = []
129
-
130
- if lines:
131
- # Format lyrics in a more readable way
132
- section_content.append(Text("Lyrics:", style="bold blue"))
133
- # Форматируем каждую строку лирики с стилем quote
134
- lyrics_lines = []
135
- for line in lines:
136
- lyrics_lines.append(f"[quote]{line}[/]")
137
-
138
- lyrics_panel = Panel(
139
- "\n".join(lyrics_lines),
140
- border_style="blue",
141
- padding=(1, 2)
142
- )
143
- section_content.append(lyrics_panel)
144
-
145
- section_content.append(Text("Analysis:", style="bold blue"))
146
- section_content.append(Text(section_analysis))
147
-
148
- # Add the section panel
149
- console.print(Panel(
150
- Group(*section_content),
151
- title=f"[bold cyan]{section_title}[/]",
152
- border_style="cyan"
153
- ))
154
-
155
- # We no longer have significant_lines in the new format
156
-
157
- # Conclusion
158
- conclusion = analysis.get("conclusion", "No conclusion available")
159
- logger.debug(f"Conclusion content: {conclusion if conclusion else 'Not available'}")
160
- console.print("\n[heading]Conclusion[/]")
161
- console.print(Panel(conclusion, border_style="magenta"))
162
-
163
- # Save output to file and read back as string
164
- temp_file.close()
165
- with open(temp_file.name, 'r', encoding='utf-8') as f:
166
- result = f.read()
167
- # Clean up the temp file
168
- import os
169
- try:
170
- os.unlink(temp_file.name)
171
- except Exception as e:
172
- logger.warning(f"Could not delete temporary file {temp_file.name}: {str(e)}")
173
- return result
174
-
175
- except Exception as e:
176
- error_traceback = traceback.format_exc()
177
- logger.error(f"Error in rich formatting: {str(e)}\nTraceback:\n{error_traceback}")
178
- # Log the current state of the analysis object for debugging
179
- if isinstance(analysis, dict):
180
- for key, value in analysis.items():
181
- logger.debug(f"Key: {key}, Value type: {type(value)}, Value: {str(value)[:100]}{'...' if len(str(value)) > 100 else ''}")
182
- # Fall back to simple formatting if rich formatting fails
183
- logger.info("Falling back to simple text formatting")
184
- pretty = False
185
-
186
- if not pretty:
187
- # Simple text formatting
188
- formatted_text = []
189
-
190
- # Summary
191
- formatted_text.append("SONG ANALYSIS SUMMARY")
192
- formatted_text.append("====================")
193
- formatted_text.append(analysis.get("summary", "No summary available"))
194
- formatted_text.append("")
195
 
196
- # Main themes
197
- themes = analysis.get("main_themes", [])
198
- if themes:
199
- formatted_text.append("MAIN THEMES")
200
- formatted_text.append("===========")
201
- for theme in themes:
202
- formatted_text.append(f"• {theme}")
203
- formatted_text.append("")
204
-
205
- # Mood
206
- mood = analysis.get("mood", "Not specified")
207
- formatted_text.append("MOOD")
208
- formatted_text.append("====")
209
- formatted_text.append(mood)
210
- formatted_text.append("")
211
-
212
- # Sections analysis
213
- sections = analysis.get("sections_analysis", [])
214
- if sections:
215
- formatted_text.append("SECTION-BY-SECTION ANALYSIS")
216
- formatted_text.append("==========================")
217
-
218
- for i, section in enumerate(sections):
219
- section_type = section.get("section_type", "Unknown")
220
- section_number = section.get("section_number", i+1)
221
- section_analysis = section.get("analysis", "No analysis available")
222
-
223
- formatted_text.append(f"{section_type.upper()} {section_number}")
224
- formatted_text.append("-" * (len(section_type) + len(str(section_number)) + 1))
225
-
226
- # Format the section lines
227
- lines = section.get("lines", [])
228
- if lines:
229
- formatted_text.append("Lyrics:")
230
- for line in lines:
231
- formatted_text.append(f"> {line}")
232
- formatted_text.append("")
233
-
234
- formatted_text.append("Analysis:")
235
- formatted_text.append(section_analysis)
236
- formatted_text.append("")
237
-
238
- # We no longer have significant_lines in the new format
239
 
240
- # Conclusion
241
- conclusion = analysis.get("conclusion", "No conclusion available")
242
- formatted_text.append("CONCLUSION")
243
- formatted_text.append("==========")
244
- formatted_text.append(conclusion)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
 
246
- return "\n".join(formatted_text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  Formatting tools for displaying analysis results with rich formatting.
3
  """
4
 
5
+ import os
6
+ import tempfile
7
  import traceback
8
+ from typing import Dict, Any, Tuple
9
  from loguru import logger
10
  from smolagents import Tool
11
  from rich.console import Console
 
33
  Formats the analysis results for better readability.
34
 
35
  Args:
36
+ analysis_json: Dictionary containing the analysis results
37
+ pretty: Parameter kept for API compatibility but not used anymore
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  Returns:
40
  A formatted string representation of the analysis
41
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
+ analysis = analysis_json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
+ logger.debug(f"Analysis structure keys: {list(analysis.keys()) if isinstance(analysis, dict) else 'Not a dictionary'}")
46
+
47
+ try:
48
+ return self._format_rich(analysis)
49
+ except Exception as e:
50
+ error_traceback = traceback.format_exc()
51
+ logger.error(f"Error in rich formatting: {str(e)}\nTraceback:\n{error_traceback}")
52
+ self._log_analysis_debug_info(analysis)
53
+ # Return error message instead of falling back to simple formatting
54
+ return f"Error formatting analysis results: {str(e)}"
55
+
56
+
57
+ def _log_analysis_debug_info(self, analysis: Dict) -> None:
58
+ """Log debug information about the analysis dictionary"""
59
+ if isinstance(analysis, dict):
60
+ for key, value in analysis.items():
61
+ logger.debug(f"Key: {key}, Value type: {type(value)}, Value: {str(value)[:100]}{'...' if len(str(value)) > 100 else ''}")
62
+
63
+ def _create_rich_console(self) -> Tuple[Console, Any]:
64
+ """Create and configure a Rich console with a temporary file"""
65
+ logger.debug("Starting rich formatting")
66
+ temp_file = tempfile.NamedTemporaryFile(mode='w+', encoding='utf-8', delete=False)
67
+ # Reduce console width for better display in Gradio UI
68
+ console = Console(file=temp_file, width=70)
69
+
70
+ # Create a custom theme for consistent styling
71
+ custom_theme = Theme({
72
+ "heading": "bold cyan underline",
73
+ "highlight": "bold yellow",
74
+ "positive": "green",
75
+ "negative": "red",
76
+ "neutral": "magenta",
77
+ "quote": "italic yellow",
78
+ "metadata": "dim white",
79
+ "conclusion": "bold magenta"
80
+ })
81
+
82
+ # Apply the theme to our console
83
+ console.theme = custom_theme
84
+ return console, temp_file
85
+
86
+ def _format_summary(self, console: Console, analysis: Dict) -> None:
87
+ """Format and print the summary section"""
88
+ summary = analysis.get("summary", "No summary available")
89
+ logger.debug(f"Summary content: {summary if summary else 'Not available'}")
90
+ console.print(Panel(
91
+ Text(summary, justify="center"),
92
+ title="[heading]Song Analysis[/]",
93
+ subtitle="[metadata]Summary[/]",
94
+ expand=True # Расширять панель на всю доступную ширину
95
+ ))
96
+
97
+ def _format_info_table(self, console: Console, analysis: Dict) -> None:
98
+ """Format and print the info table with themes and mood"""
99
+ info_table = Table(show_header=False, box=ROUNDED, expand=True)
100
+ info_table.add_column("Key", style="bold blue")
101
+ info_table.add_column("Value")
102
+
103
+ # Add the mood
104
+ mood = analysis.get("mood", "Not specified")
105
+ logger.debug(f"Mood content: {mood if mood else 'Not specified'}")
106
+ info_table.add_row("Mood", mood)
107
+
108
+ # Add the themes as a comma-separated list
109
+ themes = analysis.get("main_themes", [])
110
+ logger.debug(f"Themes: {themes if themes else 'Not available'}")
111
+ if themes:
112
+ themes_text = ", ".join([f"[highlight]{theme}[/]" for theme in themes])
113
+ info_table.add_row("Main Themes", themes_text)
114
+
115
+ console.print(info_table)
116
+
117
+ def _format_section(self, console: Console, section: Dict) -> None:
118
+ """Format and print a single section"""
119
+ section_type = section.get("section_type", "Unknown")
120
+ section_number = section.get("section_number", "")
121
+ logger.debug(f"Processing section: {section_type} {section_number}")
122
+ section_title = f"{section_type.title()} {section_number}" if section_number else section_type.title()
123
+
124
+ section_analysis = section.get("analysis", "No analysis available")
125
+ lines = section.get("lines", [])
126
+ logger.debug(f"Section lines count: {len(lines) if lines else 0}")
127
+
128
+ # Create a group with the lyrics and analysis
129
+ section_content = []
130
+
131
+ if lines:
132
+ # Format lyrics in a more readable way
133
+ section_content.append(Text("Lyrics:", style="bold blue"))
134
+ # Format each lyrics line with the quote style
135
+ lyrics_lines = []
136
+ for line in lines:
137
+ lyrics_lines.append(f"[quote]{line}[/]")
138
 
139
+ lyrics_panel = Panel(
140
+ "\n".join(lyrics_lines),
141
+ border_style="blue",
142
+ padding=(1, 2),
143
+ expand=True # Расширять панель на всю доступную ширину
144
+ )
145
+ section_content.append(lyrics_panel)
146
+
147
+ section_content.append(Text("Analysis:", style="bold blue"))
148
+ section_content.append(Text(section_analysis))
149
+
150
+ # Add the section panel
151
+ console.print(Panel(
152
+ Group(*section_content),
153
+ title=f"[bold cyan]{section_title}[/]",
154
+ border_style="cyan",
155
+ expand=True # Расширять панель на всю доступную ширину
156
+ ))
157
+
158
+ def _format_sections(self, console: Console, analysis: Dict) -> None:
159
+ """Format and print all sections"""
160
+ sections = analysis.get("sections_analysis", [])
161
+ logger.debug(f"Sections count: {len(sections) if sections else 0}")
162
+ if sections:
163
+ console.print("\n[heading]Section-by-Section Analysis[/]")
164
+ for section in sections:
165
+ self._format_section(console, section)
166
+
167
+ def _format_conclusion(self, console: Console, analysis: Dict) -> None:
168
+ """Format and print the conclusion"""
169
+ conclusion = analysis.get("conclusion", "No conclusion available")
170
+ logger.debug(f"Conclusion content: {conclusion if conclusion else 'Not available'}")
171
+ console.print("\n[heading]Conclusion[/]")
172
+ console.print(Panel(
173
+ Text(conclusion, justify="left"),
174
+ border_style="magenta",
175
+ expand=True # Расширять панель на всю доступную ширину
176
+ ))
177
+
178
+ def _format_rich(self, analysis: Dict) -> str:
179
+ """Format the analysis using rich formatting"""
180
+ console, temp_file = self._create_rich_console()
181
+
182
+ # Format each section
183
+ self._format_summary(console, analysis)
184
+ self._format_info_table(console, analysis)
185
+ self._format_sections(console, analysis)
186
+ self._format_conclusion(console, analysis)
187
+
188
+ # Save output to file and read back as string
189
+ temp_file.close()
190
+ with open(temp_file.name, 'r', encoding='utf-8') as f:
191
+ result = f.read()
192
+
193
+ # Градио не поддерживает HTML-теги pre, поэтому просто возвращаем текст как есть
194
+ # Текстовое форматирование Rich уже содержит необходимые отступы и пробелы
195
+
196
+ # Clean up the temp file
197
+ try:
198
+ os.unlink(temp_file.name)
199
+ except Exception as e:
200
+ logger.warning(f"Could not delete temporary file {temp_file.name}: {str(e)}")
201
+
202
+ return result
203
+
204
+