- REST 404 응답을 HTTPException으로 변경 (tuple 반환 버그) - MusicAgent 폴링을 asyncio.create_task로 비동기화 (이벤트 루프 블로킹 해소) - WebSocket JSON 파싱 에러 핸들링 추가 - StockAgent add_alert 파라미터 검증 추가 - 미사용 의존성 제거 (requests, python-telegram-bot) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
125 lines
4.7 KiB
Python
125 lines
4.7 KiB
Python
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)
|
|
asyncio.create_task(self._poll_composition(task_id, task))
|
|
|
|
async def _poll_composition(self, task_id: str, task: dict) -> None:
|
|
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}",
|
|
)
|