import json import os import sys from pathlib import Path from typing import List from dotenv import load_dotenv from google import genai from schemas.lyrics import LyricsSection, SongStructure sys.path.append(str(Path(__file__).parent.parent.parent)) load_dotenv() def generate_structured_lyrics(conversation: List[dict], genre: str, mood: str, theme: str) -> SongStructure: """ Generate structured lyrics using Gemini API based on conversation history and song parameters. This function takes a conversation history and song preferences, then uses Google's Gemini AI to generate structured song lyrics following a specific format. Args: conversation: List of conversation messages with 'role' and 'content' keys genre: Musical genre for the song (e.g., "pop", "rock") mood: Emotional mood for the song (e.g., "romantic", "sad") theme: Subject matter or theme of the song (e.g., "love", "friendship") Returns: SongStructure: A structured representation of the generated song Raises: ValueError: If no API key is found or if the model returns an empty response Exception: For any other errors during API communication or response parsing """ api_key = os.environ.get("GEMINI_API_KEY") if not api_key: raise ValueError("Please set the GEMINI_API_KEY environment variable") genai_client = genai.Client(api_key=api_key) model = "gemini-2.0-flash" # Convert conversation history into a single prompt string conversation_text = "\n".join([ f"{'User' if msg['role'] == 'user' else 'Assistant'}: {msg['content']}" for msg in conversation ]) prompt = f"""Based on the following conversation: {conversation_text} Create song lyrics with these parameters: - Genre: {genre} - Mood: {mood} - Theme: {theme} """ system_instruction = """Generate a complete song with the following structure: 1. A title 2. At least one verse 3. A chorus 4. Optional bridge 5. Optional outro The output must follow the exact JSON structure with these section types: VERSE, CHORUS, BRIDGE, OUTRO. """ try: config = { 'response_mime_type': 'application/json', 'response_schema': SongStructure, 'system_instruction': system_instruction } print(prompt) # Pass the prompt as a single string instead of conversation list response = genai_client.models.generate_content( contents=prompt, model=model, config=config ) if not response.text: raise ValueError("No response generated from the model") # Parse the JSON string into a dictionary lyrics_data = json.loads(response.text) sections = [] for section in lyrics_data["sections"]: sections.append(LyricsSection( section_type=section["section_type"], content=section["content"] )) song_structure = SongStructure( title=lyrics_data["title"], sections=sections, ) return song_structure except Exception as e: print(f"Error generating lyrics: {str(e)}") raise def format_lyrics(song_structure: SongStructure) -> str: """ Convert the structured lyrics into a formatted string for display. This function takes a SongStructure object and creates a human-readable text representation with appropriate section headings and spacing. Args: song_structure: A SongStructure object containing the song title and sections Returns: str: Formatted lyrics text ready for display """ formatted = f"TITLE: {song_structure.title}\n\n" for section in song_structure.sections: formatted += f"{section.section_type}:\n{section.content}\n\n" return formatted.strip() def format_lyrics_for_yue(song_structure: SongStructure, genre: str, mood: str, theme: str) -> str: """ Format lyrics in a specialized structure for the YUE music generation system. This function converts a SongStructure object into a specific format expected by the YUE music generation system, including genre and mood descriptors to guide the musical style generation. Args: song_structure: A SongStructure object containing the song title and sections genre: Musical genre that influences the instrumentation descriptors mood: Emotional mood that influences the style descriptors theme: Subject matter of the song (not directly used in formatting) Returns: str: Formatted text with appropriate section markers and descriptors for YUE """ genre_descriptors = { "pop": "pop vocal clear melodic synthesizer", "rock": "rock electric-guitar drums powerful energetic", "jazz": "jazz piano smooth saxophone melodic", "hip-hop": "rap hip-hop beats vocal rhythmic", "electronic": "electronic synthesizer beats modern" } mood_descriptors = { "upbeat": "energetic bright positive", "sad": "melancholic emotional soft", "energetic": "dynamic powerful strong", "chill": "relaxed smooth gentle", "romantic": "soft emotional intimate" } # Create combined genre description base_genre = genre_descriptors.get(genre.lower(), "") mood_desc = mood_descriptors.get(mood.lower(), "") formatted = "Generate music from the given lyrics segment by segment.\n" formatted += f"[Genre] {base_genre} {mood_desc} clear vocal\n\n" formatted += f"[Title] {song_structure.title}\n\n" for section in song_structure.sections: section_type = section.section_type.value.lower() formatted += f"[{section_type}]\n" lines = section.content.strip().split('\n') formatted += '\n'.join(line.strip() for line in lines if line.strip()) formatted += '\n\n' return formatted.strip() # Example usage: if __name__ == "__main__": """ Test module functionality by generating sample lyrics and displaying them. This test code creates a mock conversation about writing a love song, generates structured lyrics using the Gemini API, and displays both the raw structure and formatted lyrics. """ # Test conversation test_conversation = [ {"role": "user", "content": "I want to write a love song"}, {"role": "assistant", "content": "I'll help you create a love song. What style are you thinking of?"}, {"role": "user", "content": "Something romantic and modern"}, {"role": "assistant", "content": "Perfect! Here are the lyrics I've created:\n\nVERSE:\nSoft city lights paint the evening sky\nAs I think about you and I\nEvery moment we've shared feels right\nLike stars aligned in the night\n\nCHORUS:\nThis modern love, it's all we need\nBreaking rules and setting us free\nEvery text, every call, every memory\nMakes this love our reality"} ] try: song = generate_structured_lyrics( conversation=test_conversation, genre="pop", mood="romantic", theme="love" ) print("Generated Song Structure:") print(song.json(indent=2)) print("\nFormatted Lyrics:") print(format_lyrics(song)) except Exception as e: print(f"Error in test: {str(e)}")