fix: 단계별 진행 표시 + em-dash 인코딩 수정
- _handle_task: pipeline.execute() 대신 단계별 호출로 변경 -> 계획/코딩/리뷰/재시도 각 단계를 Discord에 실시간 표시 -> 코딩 중.../리뷰 중... Embed가 완료 시 업데이트됨 - em-dash -> ASCII dash (cp949 인코딩 오류 방지)
This commit is contained in:
@@ -213,7 +213,7 @@ async def on_message(message: discord.Message):
|
||||
return
|
||||
|
||||
mode = result.get("mode", "chat")
|
||||
logger.info(f"통합 분류: {mode} — \"{user_text[:50]}\"")
|
||||
logger.info(f"통합 분류: {mode} - \"{user_text[:50]}\"")
|
||||
|
||||
if mode == "task":
|
||||
# Git/Vikunja 미설정 안내 (차단하지 않음)
|
||||
@@ -275,7 +275,7 @@ async def _send_setup_warning(message: discord.Message, ws):
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
async def _handle_task(message: discord.Message, text: str, ws):
|
||||
"""작업 요청 — 파이프라인 실행."""
|
||||
"""작업 요청 — 파이프라인 단계별 실행 + 진행 표시."""
|
||||
import uuid
|
||||
|
||||
task_id = uuid.uuid4().hex[:8]
|
||||
@@ -289,7 +289,7 @@ async def _handle_task(message: discord.Message, text: str, ws):
|
||||
status_msg = await message.channel.send(embed=embed)
|
||||
|
||||
try:
|
||||
from core.task_pipeline import TaskPipeline
|
||||
from core.task_pipeline import TaskPipeline, MAX_REVIEW_RETRIES
|
||||
|
||||
pipeline = TaskPipeline(
|
||||
project_path=ws.path,
|
||||
@@ -297,20 +297,14 @@ async def _handle_task(message: discord.Message, text: str, ws):
|
||||
)
|
||||
pipeline.setup()
|
||||
|
||||
# 진행 상태 표시
|
||||
# 1. Plan
|
||||
embed.title = "🔍 작업 분해 중..."
|
||||
embed.color = 0xF39C12
|
||||
embed.set_footer(text=f"🔍 작업 분해 중... (ID: {task_id})")
|
||||
await status_msg.edit(embed=embed)
|
||||
|
||||
# 전체 파이프라인 실행 (Plan -> Code(에이전트) -> Review(재시도) -> 총평)
|
||||
result = await pipeline.execute(text)
|
||||
|
||||
plan = result.get("plan", {})
|
||||
plan = await pipeline.plan(text)
|
||||
tasks = plan.get("tasks", [])
|
||||
review = result.get("review", {})
|
||||
summary = result.get("summary", {})
|
||||
|
||||
# 계획 표시
|
||||
if tasks:
|
||||
task_list = "\n".join(
|
||||
f"• {t.get('title', t.get('description', '?'))}"
|
||||
@@ -325,23 +319,78 @@ async def _handle_task(message: discord.Message, text: str, ws):
|
||||
name=f"태스크 ({len(tasks)}개)", value=task_list[:1000], inline=False,
|
||||
)
|
||||
await message.channel.send(embed=plan_embed)
|
||||
else:
|
||||
await message.channel.send(
|
||||
embed=discord.Embed(
|
||||
title="⚠️ 실행할 태스크 없음",
|
||||
description="요청을 더 구체적으로 해주세요.",
|
||||
color=0xF39C12,
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
# 2. Code + Review (재시도 루프)
|
||||
review = None
|
||||
code_outputs = []
|
||||
for attempt in range(1 + MAX_REVIEW_RETRIES):
|
||||
attempt_label = f" (재시도 {attempt})" if attempt > 0 else ""
|
||||
|
||||
# 코딩 진행 표시
|
||||
code_embed = discord.Embed(
|
||||
title=f"⚙️ 코딩 중...{attempt_label} ({len(tasks)}개 에이전트)",
|
||||
description="\n".join(
|
||||
f"• {t.get('title', '?')[:60]}" for t in tasks[:10]
|
||||
),
|
||||
color=0xE67E22,
|
||||
)
|
||||
code_msg = await message.channel.send(embed=code_embed)
|
||||
|
||||
code_outputs = await pipeline.code_parallel(tasks)
|
||||
|
||||
error_count = sum(1 for o in code_outputs if o.startswith("[ERROR]"))
|
||||
code_embed.title = f"✅ 코딩 완료{attempt_label} ({len(tasks) - error_count}/{len(tasks)} 성공)"
|
||||
code_embed.color = 0x2ECC71 if error_count == 0 else 0xF39C12
|
||||
await code_msg.edit(embed=code_embed)
|
||||
|
||||
# 리뷰
|
||||
review_embed = discord.Embed(
|
||||
title="🔍 리뷰 중...",
|
||||
color=0xF39C12,
|
||||
)
|
||||
review_msg = await message.channel.send(embed=review_embed)
|
||||
|
||||
review = await pipeline.batch_review(tasks, code_outputs)
|
||||
|
||||
# 리뷰 결과
|
||||
if review:
|
||||
passed = review.get("passed", True)
|
||||
if isinstance(passed, str):
|
||||
passed = passed.lower() in ("true", "yes", "pass")
|
||||
retry_count = result.get("retry_count", 0)
|
||||
retry_info = f" (재시도 {retry_count}회)" if retry_count > 0 else ""
|
||||
|
||||
review_embed.title = f"{'✅' if passed else '⚠️'} 리뷰 결과{attempt_label}"
|
||||
review_embed.description = review.get("summary", str(review))[:500]
|
||||
review_embed.color = 0x2ECC71 if passed else 0xE74C3C
|
||||
await review_msg.edit(embed=review_embed)
|
||||
|
||||
if passed:
|
||||
break
|
||||
elif attempt < MAX_REVIEW_RETRIES:
|
||||
# 재시도 안내
|
||||
await message.channel.send(
|
||||
embed=discord.Embed(
|
||||
title=f"{'✅' if passed else '⚠️'} 리뷰 결과{retry_info}",
|
||||
description=review.get("summary", str(review))[:500],
|
||||
color=0x2ECC71 if passed else 0xE74C3C,
|
||||
title=f"🔄 재시도 {attempt+1}/{MAX_REVIEW_RETRIES}",
|
||||
description="리뷰 피드백을 반영하여 다시 코딩합니다.",
|
||||
color=0xF39C12,
|
||||
)
|
||||
)
|
||||
feedback = review.get("summary", str(review))[:500]
|
||||
for task in tasks:
|
||||
task["review_feedback"] = (
|
||||
f"이전 시도에서 다음 리뷰 피드백을 받았습니다. "
|
||||
f"반드시 수정하세요:\n{feedback}"
|
||||
)
|
||||
|
||||
# 3. 총평
|
||||
summary = await pipeline.summarize(text, plan, code_outputs, review)
|
||||
|
||||
# 총평
|
||||
summary_embed = discord.Embed(
|
||||
title=f"📊 {summary.get('title', '작업 완료')}",
|
||||
description=summary.get("summary", "완료"),
|
||||
@@ -363,10 +412,13 @@ async def _handle_task(message: discord.Message, text: str, ws):
|
||||
val = "\n".join(f"• {s}" for s in items)
|
||||
summary_embed.add_field(name=field_name, value=val[:1000], inline=False)
|
||||
|
||||
|
||||
summary_embed.set_footer(text=f"ID: {task_id} | {ws.name}")
|
||||
await message.channel.send(embed=summary_embed)
|
||||
|
||||
# 기록
|
||||
pipeline.docs.record_session(text, summary, plan)
|
||||
pipeline.docs.append_changelog(summary.get("title", text[:50]))
|
||||
|
||||
except GeminiCallError as e:
|
||||
await message.channel.send(
|
||||
embed=discord.Embed(title="❌ AI 호출 오류",
|
||||
|
||||
Reference in New Issue
Block a user