# backend/app/services/scaffold.py import re import uuid from sqlmodel import Session, select from ..models import Task, TaskStatus SCAFFOLDS = { "research": { "kind": "연구 · 논문 작성", "icon": "brain", "items": [ {"title": "선행 연구 조사", "est": "1일"}, {"title": "실험 데이터 취합", "est": "1일"}, {"title": "아키텍처 다이어그램 작성", "est": "반나절"}, {"title": "서론 초안", "est": "반나절"}, {"title": "결론 작성", "est": "반나절"}, ], }, "dev": { "kind": "개발 업무", "icon": "branch", "items": [ {"title": "API 엔드포인트 설계", "est": "2시간"}, {"title": "UI 폼 컴포넌트 개발", "est": "3시간"}, {"title": "토큰 저장소 세팅", "est": "1.5시간"}, {"title": "테스트 코드 작성", "est": "2시간"}, ], }, "doc": { "kind": "문서 · 리포트", "icon": "file", "items": [ {"title": "목차 & 범위 정의", "est": "30분"}, {"title": "핵심 데이터 수집", "est": "2시간"}, {"title": "섹션별 초안 작성", "est": "3시간"}, {"title": "리뷰 & 수정", "est": "1시간"}, {"title": "최종 정리 & 공유", "est": "30분"}, ], }, "generic": { "kind": "일반 업무", "icon": "list", "items": [ {"title": "범위 정의 & 자료 수집", "est": "1시간"}, {"title": "초안 작성", "est": "2시간"}, {"title": "리뷰 & 피드백 반영", "est": "1시간"}, {"title": "최종 정리 & 공유", "est": "30분"}, ], }, } # 원본 pickScaffold 정규식 (순서 중요: research > dev > doc > generic) RE_RESEARCH = re.compile(r"(논문|연구|리서치|실험|학회|발표|조사|인터뷰)") RE_DEV = re.compile(r"(구현|개발|API|로직|컴포넌트|버그|배포|인증|앱)") RE_DOC = re.compile(r"(리포트|문서|보고|자료|작성|초안|회고|정산)") def pick_scaffold(title: str, use_llm: bool = False) -> dict: t = title or "" if RE_RESEARCH.search(t): base = SCAFFOLDS["research"] elif RE_DEV.search(t): base = SCAFFOLDS["dev"] elif RE_DOC.search(t): base = SCAFFOLDS["doc"] else: base = SCAFFOLDS["generic"] if use_llm: try: from ..llm.provider import get_provider enriched = get_provider().generate_json( _scaffold_prompt(title, base), schema={"items": "list"} ) items = enriched.get("items") if isinstance(items, list) and items: norm = [ {"title": str(i.get("title", "")).strip(), "est": str(i.get("est", "")).strip()} for i in items if i.get("title") ] if norm: return {"kind": base["kind"], "icon": base["icon"], "items": norm} except Exception: pass # 폴백: 정규식 템플릿 그대로 return base def scaffold_create(s: Session, parent: Task, tpl: dict) -> list[str]: existing = s.exec(select(Task).where(Task.parent_id == parent.id)).all() order = max([x.sort_order for x in existing] + [-1]) + 1 ids = [] for i, it in enumerate(tpl["items"]): tid = "t-" + uuid.uuid4().hex[:8] s.add( Task( id=tid, title=it["title"], project_id=parent.project_id, parent_id=parent.id, status=TaskStatus.todo, assignee_id=parent.assignee_id, prio=parent.prio, est=it.get("est", ""), sort_order=order + i, ) ) ids.append(tid) s.commit() return ids def _scaffold_prompt(title: str, base: dict) -> str: return ( "다음 작업을 실행 가능한 하위작업으로 4~6개 쪼개세요. " "각 항목은 {title, est(예상 소요)} 형태의 JSON. 한국어.\n" f"작업: {title}\n" f"참고 템플릿({base['kind']}): {[i['title'] for i in base['items']]}\n" '반드시 {"items":[{"title":"...","est":"..."}]} 형식 JSON만 출력.' )