Skip to content

Neko #266

@xoxoxo-af

Description

@xoxoxo-af
import json
import time
import os
from datetime import datetime
from typing import Dict, Optional, List
import urllib.request
import urllib.error

# === ตรวจสอบและโหลดไลบรารีสำหรับระบบเสียง ===
try:
    import speech_recognition as sr
    from gtts import gTTS
    import playsound
    VOICE_AVAILABLE = True
except ImportError:
    VOICE_AVAILABLE = False
    print("⚠️  คำเตือน: ไม่พบไลบรารีเสียง - ระบบจะทำงานในโหมดข้อความ (Text-only Mode)")
    print("💡 ติดตั้งด้วยคำสั่ง: pip install SpeechRecognition gTTS playsound pyaudio")


class MandateSyntaxAPI:
    """
    ระบบประมวลผลคำสั่งระดับสูง (Mandate Architecture) ที่ได้รับการตรวจสอบและแก้ไขจุดบั๊กแล้ว
    เชื่อมต่อกับ Google Gemini API (gemini-2.5-flash-preview-09-2025) 
    เพื่อแปลภาษาไทยธรรมชาติเป็นโครงสร้างสั่งงานทีม AI ทำเพลง (Songwriter, Singer, Producer)
    """
    def __init__(self):
        self.system_active = True
        self.security_protocols = {
            "omega": {"clearance": 9, "risk_limit": "medium"},
            "alpha": {"clearance": 7, "risk_limit": "low"},
            "critical": {"encryption": "AES-512", "integrity": "SHA-512"}
        }
        self.log_archive: List[str] = []
        self.active_agents: List[str] = []
        
        # ดึง API Key จาก Environment variable
        self.api_key = os.environ.get("GEMINI_API_KEY", "")
        
        # เริ่มต้นระบบรับคำสั่งเสียง
        if VOICE_AVAILABLE:
            try:
                self.recognizer = sr.Recognizer()
                # ปรับจูนเพื่อไม่ให้โปรแกรมค้างหากรอบข้างมีเสียงรบกวน
                self.recognizer.dynamic_energy_threshold = True
                self.recognizer.energy_threshold = 3000 
                self.recognizer.pause_threshold = 1.0 # เว้นจังหวะหยุดพูด 1 วินาทีถึงจะประมวลผล
            except Exception as e:
                print(f"⚠️  ไม่สามารถเชื่อมต่อไมโครโฟนได้: {e}")

    def _speak(self, text: str) -> None:
        """แปลงข้อความภาษาไทยเป็นเสียงพูด พร้อมระบบป้องกันไฟล์ถูกล็อก (File Lock Prevention)"""
        print(f"🔊 AI: {text}")
        if not VOICE_AVAILABLE:
            return
            
        filename = f"response_{int(time.time())}.mp3" # ใช้ Timestamp เพื่อป้องกันชื่อไฟล์ซ้ำซ้อน
        try:
            tts = gTTS(text=text, lang='th', slow=False)
            tts.save(filename)
            
            # เล่นเสียงแบบบล็อกการทำงาน
            playsound.playsound(filename, block=True)
            
            # หน่วงเวลาสั้นๆ ป้องกันไม่ให้ระบบล็อกไฟล์ OS พังขณะทำการลบ
            time.sleep(0.3)
            if os.path.exists(filename):
                os.remove(filename)
        except Exception as e:
            self._log("AUDIO OUTPUT", f"ไม่สามารถเล่นหรือลบไฟล์เสียงได้: {e}", "WARNING")
            # ทำความสะอาดไฟล์ที่ค้างในระบบหากมี
            try:
                if os.path.exists(filename):
                    os.remove(filename)
            except:
                pass

    def _listen(self) -> str:
        """รับเสียงพูดและแปลงเป็นข้อความภาษาไทย พร้อมป้องกันโปรแกรมค้าง"""
        if not VOICE_AVAILABLE:
            return input("\n⌨️  ป้อนคำสั่งของคุณ: ").strip()
        
        try:
            with sr.Microphone() as source:
                print("\n🎤 กำลังฟัง... พูดคำสั่งทำเพลงภาษาไทยของคุณได้เลยค่ะ")
                # ลด duration ลงเหลือ 0.5 วินาที เพื่อไม่ให้ผู้ใช้งานรอนานตอนเริ่มระบบ
                self.recognizer.adjust_for_ambient_noise(source, duration=0.5)
                # กำหนด timeout และ limit ให้กระชับขึ้นเพื่อป้องกันการค้างยาว
                audio = self.recognizer.listen(source, timeout=5, phrase_time_limit=8)
                print("🔍 กำลังถอดรหัสเสียงพูด...")
                text = self.recognizer.recognize_google(audio, language='th-TH')
                print(f"👂 ได้ยินว่า: \"{text}\"")
                return text.strip()
        except sr.WaitTimeoutError:
            # ไม่มีเสียงพูดเข้ามาในเวลาที่กำหนด
            return ""
        except sr.UnknownValueError:
            self._speak("ขออภัยค่ะ ฉันฟังคำนี้ไม่ชัดเจนเลย")
            return ""
        except Exception as e:
            self._log("AUDIO INPUT", f"สลับโหมดเป็นคีย์บอร์ดเนื่องจากเกิดข้อผิดพลาด: {e}", "WARNING")
            return input("\n⌨️  เกิดข้อผิดพลาดด้านระบบเสียง กรุณาพิมพ์คำสั่งแทน: ").strip()

    def _log(self, stage: str, message: str, level: str = "INFO") -> None:
        """ระบบบันทึก Log การทำงานร่วมกันของ AI Team"""
        timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
        entry = f"[{timestamp}] [{level}] {stage}: {message}"
        self.log_archive.append(entry)
        print(entry)

    def _call_gemini_llm(self, user_command: str) -> Optional[Dict]:
        """
        เชื่อมต่อกับ Google Gemini API โดยใช้โครงสร้างและข้อกำหนดที่ถูกต้อง 100%
        มีระบบป้องกันความหน่วงแบบ Exponential Backoff สูงสุด 5 ครั้ง
        """
        if not self.api_key:
            self._log("LLM ENGINE", "ไม่พบ API Key ในระบบ", "ERROR")
            return None

        url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key={self.api_key}"
        
        # จัดชุดคำสั่ง (System Instruction) เพื่อบีบให้ AI ตอบกลับมาเป็น JSON ตามมาตรฐานที่คุณต้องการ
        system_instruction = (
            "คุณคือผู้จัดการกลางของทีม AI ทำเพลง (AI Music Orchestrator)\n"
            "มีหน้าที่รับคำสั่งภาษาไทยแล้วแปลงเป็น JSON Mandate เพื่อส่งต่อให้ทีมงาน AI ดังนี้:\n"
            "1. SONGWRITER-AI: แต่งเนื้อเพลง, คิด Hook ติดหู, วางโครงสร้างเพลง (Intro, Verse, Hook, Bridge)\n"
            "2. SINGER-AI: ตีความน้ำเสียง กำหนดอารมณ์ร้อง (Tone Voice, Emotion, Flow, Vocal Style)\n"
            "3. BEAT-DESIGNER: ดีไซน์บีท จังหวะ แนวเพลง (Sad Rap, Lo-Fi, Pop)\n"
            "4. VIRAL-HOOK: ปรับแต่งคำร้องจุดไคลแม็กซ์ให้เป็นไวรัลในโซเชียล\n"
            "5. MUSIC-ANALYST: วิเคราะห์โครงสร้างอารมณ์องค์รวมของเพลง\n\n"
            "คุณต้องตอบกลับเป็นข้อความ JSON เสมอ โดยห้ามมี Markdown หรือตัวอักษรอื่นนอกวงเล็บปีกกาเด็ดขาด และต้องประกอบไปด้วยคีย์เหล่านี้:\n"
            "{\n"
            "  \"intent\": \"[write_song | analyze_vocal | design_beat | system_status | shutdown | unknown]\",\n"
            "  \"mandate\": \"[ระบุวัตถุประสงค์งานภาษาอังกฤษสั้นๆ เช่น Compose Sad Rap Song]\",\n"
            "  \"priority\": \"[omega | alpha | critical]\",\n"
            "  \"target_agent\": \"[ระบุชื่อ Agent หลัก เช่น SONGWRITER-AI หรือ SINGER-AI]\",\n"
            "  \"constraints\": {\n"
            "     \"mood\": \"[ระบุอารมณ์เพลง เช่น เศร้า, เจ็บปวด, ดาร์ค, สดใส]\",\n"
            "     \"vocal_style\": \"[ระบุสไตล์เสียงร้อง เช่น เหนื่อยล้า, สดใส, แร็ปดุดัน]\",\n"
            "     \"genre\": \"[แนวเพลง เช่น Sad Rap, Lo-Fi, Hip Hop, R&B]\"\n"
            "  },\n"
            "  \"raw_text_extracted\": \"[ทวนข้อความต้นฉบับของผู้ใช้]\",\n"
            "  \"spoken_feedback\": \"[คำพูดภาษาไทยสั้นๆ 1 ประโยค เพื่อให้ระบบพูดตอบรับผู้ใช้]\"\n"
            "}"
        )

        # โครงสร้าง Payload ที่ถูกต้องตามหลักการพัฒนา Gemini API
        payload = {
            "contents": [{
                "parts": [{"text": f"คำสั่งผู้ใช้: {user_command}"}]
            }],
            "systemInstruction": {
                "parts": [{"text": system_instruction}]
            },
            "generationConfig": {
                "responseMimeType": "application/json"
            }
        }

        # การประมวลผลด้วย Exponential Backoff (1s, 2s, 4s, 8s, 16s)
        delays = [1, 2, 4, 8, 16]
        for attempt, delay in enumerate(delays):
            try:
                req = urllib.request.Request(
                    url,
                    data=json.dumps(payload).encode("utf-8"),
                    headers={"Content-Type": "application/json"},
                    method="POST"
                )
                with urllib.request.urlopen(req, timeout=12) as response:
                    res_body = response.read().decode("utf-8")
                    res_json = json.loads(res_body)
                    
                    # คัดแยกข้อมูลออกมาตรวจสอบความถูกต้อง
                    text_content = res_json['candidates'][0]['content']['parts'][0]['text']
                    return json.loads(text_content.strip())
                    
            except urllib.error.HTTPError as e:
                if e.code == 400 or e.code == 403:
                    self._log("LLM ENGINE", f"การยืนยันตัวตนผิดพลาด (กรุณาเช็ค API Key) Code: {e.code}", "ERROR")
                    break
                time.sleep(delay)
            except Exception as e:
                # เกิดปัญหาด้านการเชื่อมต่อหรือการพาร์ส JSON
                time.sleep(delay)
                
        return None

    def get_system_status(self) -> Dict:
        """ตรวจสอบสถานะระบบ"""
        return {
            "status": "ออนไลน์",
            "version": "Mandate AI Music Orchestrator v3.1 (Stable Verified)",
            "active_agents": len(self.active_agents),
            "logs_count": len(self.log_archive),
            "voice_system": "ทำงานปกติ" if VOICE_AVAILABLE else "ไม่พบบุปกรณ์เสียง (สลับใช้โหมดคีย์บอร์ด)"
        }

    def process_semantic_mandate(self, parsed_mandate: Dict) -> Dict:
        """
        ประมวลผลและส่งต่อหน้าที่ให้กลุ่ม AI Agents ทำงานร่วมกันตามระบบ Workflow ที่กำหนดไว้
        """
        intent = parsed_mandate.get("intent", "unknown")
        feedback = parsed_mandate.get("spoken_feedback", "กำลังประมวลผลคำสั่งตามแมนเดตค่ะ")
        self._speak(feedback)

        if intent == "shutdown":
            self.system_active = False
            return {"status": "ปิดระบบสมบูรณ์"}

        if intent == "system_status":
            status = self.get_system_status()
            self._speak(f"สถานะระบบขณะนี้เป็นปกติ มีบันทึกเหตุการณ์ทั้งหมด {status['logs_count']} รายการค่ะ")
            return status

        self._log("MANDATE INPUT", f"เริ่มกระบวนการตามเจตนา: {parsed_mandate.get('mandate')}", "SUCCESS")
        
        # คัดกรองและดึงค่า Constraints
        constraints = parsed_mandate.get("constraints", {})
        mood = constraints.get("mood", "ทั่วไป")
        vocal_style = constraints.get("vocal_style", "ปกติ")
        genre = constraints.get("genre", "Lo-Fi")
        
        # จัดแจงจัดสรร Agents (Orchestrator) ตามความคุ้นเคยและแนวคิด 6 Agents
        agents = ["MUSIC-ANALYST"]
        target_agent = parsed_mandate.get("target_agent", "SONGWRITER-AI")
        
        if target_agent == "SONGWRITER-AI":
            agents.extend(["SONGWRITER-AI", "VIRAL-HOOK"])
        elif target_agent == "SINGER-AI":
            agents.extend(["SINGER-AI", "SONGWRITER-AI"])
        elif target_agent == "BEAT-DESIGNER":
            agents.extend(["BEAT-DESIGNER", "SINGER-AI", "PRODUCER-AI"])
        else:
            agents.extend(["SONGWRITER-AI", "SINGER-AI", "PRODUCER-AI"])

        self.active_agents = list(set(agents))
        self._log("AGENT ORCHESTRATOR", f"ระดมพลทีมทำเพลง: {', '.join(self.active_agents)}", "SUCCESS")

        # จำลองการสร้างเนื้อเพลงและแนวการร้องตามกฎ AI Team ที่ตั้งไว้
        print("\n" + "🎵 " + "="*50 + " 🎵")
        print(f"🎬 [เริ่มสายการทำงานของ AI ทำเพลงอัตโนมัติ]")
        print(f"• อารมณ์ธีมเพลง: {mood}")
        print(f"• สไตล์การร้อง: {vocal_style}")
        print(f"• แนวบีทดนตรี: {genre}")
        print("="*54 + "\n")
        
        time.sleep(1.0) # จำลองระยะเวลาแต่งเนื้อเพลงและวิเคราะห์น้ำเสียง

        # การจำลองผลลัพธ์ของ Songwriter AI และ Singer AI แบบครบโครงสร้างจริง
        creative_output = {}
        if "SONGWRITER-AI" in self.active_agents:
            creative_output["Songwriter_AI_Output"] = {
                "structure": "Intro -> Verse 1 -> Hook -> Verse 2 -> Hook -> Bridge -> Final Hook",
                "lyric_sample": f"({mood.upper()} Verse 1) ในคืนที่ฟ้าหม่น แสงดาวมืดมนไร้ทาง... (Viral Hook) เจ็บปวดจนชินชา น้ำตามันไหลผ่านไหล่ขวา...",
                "flow_rap": "Slow-tempo 16-bars melodic flow"
            }
        
        if "SINGER-AI" in self.active_agents:
            creative_output["Singer_AI_Output"] = {
                "tone_voice": "Husky, Deep breathy chest tone",
                "emotion": f"{mood} (อารมณ์สะเทือนอารมณ์)",
                "vocal_style": f"{vocal_style} (เน้นการเว้นช่องไฟเพื่อสร้างความลึกของเพลง)"
            }

        result = {
            "status": "สร้างสรรค์ผลงานเสร็จสมบูรณ์",
            "theme_profile": constraints,
            "creative_details": creative_output,
            "system_stability": "100.0%",
            "completed_at": datetime.utcnow().isoformat()
        }

        self._log("EXECUTION LAYER", "ความเสถียรของงานเต็มพิกัด ส่งข้อมูลเข้าคลังข้อมูลสำเร็จ", "SUCCESS")
        self._speak("วิเคราะห์และเขียนเนื้อเพลงเสร็จสมบูรณ์เรียบร้อยแล้วค่ะ")
        return result

    def start_voice_assistant(self) -> None:
        """ฟังก์ชันเริ่มต้นคำสั่งเสียงทำเพลงร่วมกับ Gemini LLM"""
        # แนะนำการตั้งค่า API Key หากผู้ใช้ลืมระบุลงใน OS Environment
        if not self.api_key:
            print("\n⚠️  ระบบไม่พบ API Key ของ Google Gemini")
            print("กรุณาเปิด Terminal แล้วพิมพ์: export GEMINI_API_KEY=\"คีย์จริงของคุณ\"")
            self.api_key = input("🔑 หรือนำรหัส API Key มาวางตรงนี้เพื่อรันชั่วคราวได้เลยค่ะ: ").strip()
            if not self.api_key:
                print("❌ ไม่พบ API Key สิ้นสุดกระบวนการทำงาน")
                return

        print("\n" + "🚀 " + "="*56)
        print("🎤  [ระบบทีมสร้างสรรค์เพลงอัจฉริยะ AI Music Assistant - Verified]")
        print("="*60)
        
        self._speak("ระบบทีมเอไอพร้อมสร้างสรรค์เสียงดนตรีแล้วค่ะ")
        
        while self.system_active:
            try:
                user_input = self._listen()
                if not user_input:
                    continue

                print("🧠 กำลังประมวลผลคำสั่งเชิงลึกด้วยสมองกล Gemini...")
                parsed_mandate = self._call_gemini_llm(user_input)
                
                if parsed_mandate:
                    print(f"\n📋 โครงสร้างแมนเดตที่วิเคราะห์ได้จาก LLM:\n{json.dumps(parsed_mandate, indent=2, ensure_ascii=False)}")
                    result = self.process_semantic_mandate(parsed_mandate)
                    print(f"⚡ สรุปผลการปฏิบัติการ: {json.dumps(result, indent=2, ensure_ascii=False)}\n")
                else:
                    self._speak("เครือข่ายมีปัญหานิดหน่อยค่ะ ลองสั่งงานใหม่อีกครั้งนะคะ")
                    
            except KeyboardInterrupt:
                # รองรับการกด Ctrl+C เพื่อออกจากโปรแกรมอย่างปลอดภัย
                print("\n🛑 ตรวจพบคำสั่งหยุดการรันโปรแกรมจากคีย์บอร์ด")
                self._speak("ปิดระบบเรียบร้อยค่ะ ขอบคุณที่ใช้งานนะคะ")
                break


# -----------------------------------------------------------------------------
# 🚀 เริ่มทดสอบโปรแกรมทำเพลงอัจฉริยะ
# -----------------------------------------------------------------------------
if __name__ == "__main__":
    system = MandateSyntaxAPI()
    system.start_voice_assistant()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions