From c3cf4d70e6c614177522eccc80a22b38b8e9bb82 Mon Sep 17 00:00:00 2001 From: gahusb Date: Sat, 11 Apr 2026 08:49:54 +0900 Subject: [PATCH] =?UTF-8?q?feat(agent-office):=20MusicAgent=20=E2=80=94=20?= =?UTF-8?q?compose=20with=20approval,=20polling,=20telegram=20notification?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- agent-office/app/agents/music.py | 122 +++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 agent-office/app/agents/music.py diff --git a/agent-office/app/agents/music.py b/agent-office/app/agents/music.py new file mode 100644 index 0000000..cd54366 --- /dev/null +++ b/agent-office/app/agents/music.py @@ -0,0 +1,122 @@ +import asyncio +from .base import BaseAgent +from ..db import create_task, update_task_status, approve_task, reject_task, add_log +from .. import service_proxy +from .. import telegram_bot + +class MusicAgent(BaseAgent): + agent_id = "music" + display_name = "음악 프로듀서" + + async def on_schedule(self) -> None: + pass + + async def on_command(self, command: str, params: dict) -> dict: + if command == "compose": + prompt = params.get("prompt", "") + style = params.get("style", "") + model = params.get("model", "V4") + instrumental = params.get("instrumental", False) + + if not prompt: + return {"ok": False, "message": "프롬프트를 입력해주세요"} + + task_id = create_task(self.agent_id, "compose", { + "prompt": prompt, "style": style, + "model": model, "instrumental": instrumental, + }, requires_approval=True) + + await self.transition("waiting", "프롬프트 승인 대기", task_id) + + detail = f"프롬프트: {prompt}" + if style: + detail += f"\n스타일: {style}" + detail += f"\n모델: {model}" + + await telegram_bot.send_approval_request( + self.agent_id, task_id, + "🎵 [음악 에이전트] 작곡 요청", detail, + ) + + return {"ok": True, "task_id": task_id, "message": "승인 대기 중"} + + if command == "credits": + credits = await service_proxy.get_music_credits() + return {"ok": True, "credits": credits} + + return {"ok": False, "message": f"Unknown command: {command}"} + + async def on_approval(self, task_id: str, approved: bool, feedback: str = "") -> None: + if not approved: + reject_task(task_id) + await self.transition("idle", "작곡 거절됨") + await telegram_bot.send_task_result( + self.agent_id, "🎵 [음악 에이전트] 작곡 취소", + "사용자가 거절했습니다.", + ) + return + + from ..db import get_task + task = get_task(task_id) + if not task: + return + + approve_task(task_id, via="telegram") + await self.transition("working", "작곡 중...", task_id) + + try: + input_data = task["input_data"] + payload = { + "provider": "suno", + "model": input_data.get("model", "V4"), + "prompt": input_data.get("prompt", ""), + "style": input_data.get("style", ""), + "instrumental": input_data.get("instrumental", False), + "custom_mode": True, + } + + result = await service_proxy.generate_music(payload) + music_task_id = result.get("task_id") + + if not music_task_id: + raise Exception("music-lab did not return task_id") + + for _ in range(60): + await asyncio.sleep(5) + status = await service_proxy.get_music_status(music_task_id) + state = status.get("status", "") + + if state == "succeeded": + tracks = status.get("tracks", []) + update_task_status(task_id, "succeeded", { + "music_task_id": music_task_id, + "tracks": tracks, + }) + await self.transition("reporting", "작곡 완료!") + + track_info = "" + for t in tracks: + title = t.get("title", "Untitled") + url = t.get("audio_url", "") + track_info += f"🎶 {title}\n{url}\n" + + await telegram_bot.send_task_result( + self.agent_id, "🎵 [음악 에이전트] 작곡 완료", + track_info or "트랙 생성 완료", + ) + await self.transition("idle", "작곡 완료") + return + + if state == "failed": + raise Exception(status.get("message", "Generation failed")) + + raise Exception("Timeout: 5분 초과") + + except Exception as e: + add_log(self.agent_id, f"Compose failed: {e}", "error", task_id) + update_task_status(task_id, "failed", {"error": str(e)}) + await self.transition("idle", f"오류: {e}") + await telegram_bot.send_task_result( + self.agent_id, "🎵 [음악 에이전트] 작곡 실패", + f"오류: {e}", + )