백엔드 (crawler_platform/app/api/routes.py):
- POST /research/run/by-project 추가 — config_path 의존 제거
- ResearchRunByProjectRequest: project_name 기반, DB project.config 재구성
- GraphResearchLoop 동기 실행 (장시간 가능), asdict(result) 반환
- 기존 /projects/{n}/research/sessions, /research/sessions/{job_id}는 변경 없음
프론트엔드 API (src/lib/api/):
- research.ts: researchApi.startByProject/listSessions/getSession + Zod 스키마
- 결과 페이로드는 passthrough() (백엔드 dataclass 그대로 노출)
TanStack Query 훅 (src/hooks/):
- useResearch.ts: useStartResearch, useResearchSessions, useResearchSession
- queryKeys에 research.sessions, research.session 키
ResearchPage 신규 (src/pages/ResearchPage.tsx):
- 2열 레이아웃: 좌측 연구 설정 폼 + 우측 결과/이력
- 폼: 소스 선택, goal 텍스트(필수, 3~500자), 시드 URL(선택),
max_steps/max_branch/max_depth/min_relevance, same_domain_only
react-hook-form + zod 검증
- 결과 카드: 단계/페이지/엔티티/클레임 4종 stats + raw JSON details
- 세션 이력 카드: GET /projects/{n}/research/sessions 결과 리스트
- 실행 중 안내 (장시간 가능), sonner 토스트
라우팅 & Sidebar:
- App.tsx: /research/:projectId 라우트 추가
- AppShell: 사이드바에 "자율 연구" 메뉴 (Brain 아이콘)
i18n: research.*, nav.research 키 (한/영)
다음 단계: Phase 1.5 — 엔티티/클레임 직접 입력
(백엔드 POST /projects/{n}/entities, /claims 신규 필요)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
38 lines
1.1 KiB
TypeScript
38 lines
1.1 KiB
TypeScript
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
import {
|
|
researchApi,
|
|
ResearchRunResult,
|
|
ResearchSessionDetail,
|
|
ResearchSessionItem,
|
|
StartResearchRequest,
|
|
} from "@/lib/api/research";
|
|
import { queryKeys } from "./queryKeys";
|
|
|
|
export function useStartResearch(projectName: string) {
|
|
const queryClient = useQueryClient();
|
|
return useMutation<ResearchRunResult, Error, StartResearchRequest>({
|
|
mutationFn: (body) => researchApi.startByProject(body),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({
|
|
queryKey: queryKeys.research.sessions(projectName),
|
|
});
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useResearchSessions(projectName: string, limit = 25) {
|
|
return useQuery<ResearchSessionItem[]>({
|
|
queryKey: queryKeys.research.sessions(projectName),
|
|
queryFn: () => researchApi.listSessions(projectName, limit),
|
|
enabled: Boolean(projectName),
|
|
});
|
|
}
|
|
|
|
export function useResearchSession(jobId: string | null | undefined) {
|
|
return useQuery<ResearchSessionDetail>({
|
|
queryKey: queryKeys.research.session(jobId ?? ""),
|
|
queryFn: () => researchApi.getSession(jobId!),
|
|
enabled: Boolean(jobId),
|
|
});
|
|
}
|