import os import re import uuid import torch import numpy as np import scipy.io.wavfile import gradio as gr import edge_tts import asyncio from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline from groq import Groq # API 키 및 환경 설정 GENAI_KEY = os.getenv("GEMINI_KEY") # 기존 키 유지 시 GROQ_API_KEY = os.getenv("GROQ_API_KEY") class ModelManager: _llm_pipeline = None _music_pipeline = None _groq_client = None @classmethod def get_qwen(cls): if cls._llm_pipeline is None: # CPU 최적화 버전 Qwen 0.5B model_id = "Qwen/Qwen2.5-0.5B-Instruct" tokenizer = AutoTokenizer.from_pretrained(model_id) model = AutoModelForCausalLM.from_pretrained(model_id, device_map="cpu", torch_dtype=torch.float32) cls._llm_pipeline = pipeline("text-generation", model=model, tokenizer=tokenizer) return cls._llm_pipeline @classmethod def get_groq(cls): if cls._groq_client is None and GROQ_API_KEY: cls._groq_client = Groq(api_key=GROQ_API_KEY) return cls._groq_client @classmethod def get_music(cls): if cls._music_pipeline is None: cls._music_pipeline = pipeline("text-to-audio", "facebook/musicgen-small", device="cpu") return cls._music_pipeline # 8인 멤버 보이스 매핑 MEMBERS_VOICE = { "서윤 (Korea)": "ko-KR-SunHiNeural", "Chloe (USA)": "en-US-AriaNeural", "Naomi (Japan)": "ja-JP-NanamiNeural", "Beatrice (Brazil)": "pt-BR-FranciscaNeural", "Elena (Spain)": "es-ES-ElviraNeural", "Amira (Egypt)": "ar-EG-SalmaNeural", "Liwei (China)": "zh-CN-XiaoxiaoNeural", "Sophie (France)": "fr-FR-DeniseNeural" } async def band_consulting(user_input, member_name, lang_code): req_id = str(uuid.uuid4())[:8] voice_path = f"/tmp/v_{req_id}.mp3" music_path = f"/tmp/m_{req_id}.wav" system_prompt = f"당신은 밴드 멤버 {member_name}입니다. {lang_code}로 답변하세요. 5줄 이내 요약, 상세 설명은 [TAB]에, 음악 프롬프트는 [MUSIC: 영어프롬프트]에 넣으세요." ai_text_raw = "" # 1. Groq 시도 groq_client = ModelManager.get_groq() if groq_client: try: res = groq_client.chat.completions.create( messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": user_input}], model="llama-3.3-70b-versatile" ) ai_text_raw = res.choices[0].message.content except: pass # 2. 로컬 Qwen Fallback if not ai_text_raw: qwen = ModelManager.get_qwen() out = qwen(f"<|im_start|>system\n{system_prompt}<|im_end|>\n<|im_start|>user\n{user_input}<|im_end|>\nassistant\n", max_new_tokens=512) ai_text_raw = out[0]['generated_text'].split("assistant\n")[-1] # 파싱 tab_match = re.search(r'\[TAB\](.*?)(\[|$)', ai_text_raw, re.DOTALL | re.IGNORECASE) music_match = re.search(r'\[MUSIC:(.*?)\]', ai_text_raw, re.IGNORECASE) tab_display = tab_match.group(1).strip() if tab_match else "상세 악보 준비 중..." clean_text = re.sub(r'\[TAB\].*?(\[|$)', '', ai_text_raw, flags=re.DOTALL | re.IGNORECASE) clean_text = re.sub(r'\[MUSIC:.*?\]', '', clean_text, flags=re.IGNORECASE).strip() # 3. TTS 음성 생성 (복구) tts_text = "\n".join(clean_text.split('\n')[:5]) voice_name = MEMBERS_VOICE.get(member_name, "ko-KR-SunHiNeural") communicate = edge_tts.Communicate(re.sub(r'[\*\#\-\_\~\|]', '', tts_text), voice_name) await communicate.save(voice_path) # 4. 음악 생성 (길이 연장: 512 토큰 = 약 12초) music_gen = ModelManager.get_music() music_p = music_match.group(1).strip() if music_match else "energetic rock guitar solo" music_output = music_gen(music_p, forward_params={"max_new_tokens": 512}) audio_data = np.squeeze(music_output["audio"]) audio_int16 = (audio_data * 32767).astype(np.int16) scipy.io.wavfile.write(music_path, music_output["sampling_rate"], audio_int16) return tts_text, voice_path, music_path, tab_display with gr.Blocks() as demo: i1 = gr.Textbox(); i2 = gr.Textbox(); i3 = gr.Textbox() o1 = gr.Textbox(); o2 = gr.Audio(); o3 = gr.Audio(); o4 = gr.Textbox() btn = gr.Button("GO"); btn.click(band_consulting, [i1, i2, i3], [o1, o2, o3, o4], api_name="predict") demo.queue().launch()