Files
AI/ontology_platform/web/frontend/src/lib/api/research.ts

87 lines
2.8 KiB
TypeScript
Raw Normal View History

Phase 1.4: 자율 연구 — by-project 엔드포인트 + ResearchPage + 세션 이력 백엔드 (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>
2026-05-14 19:01:52 +09:00
import { z } from "zod";
import { apiClient } from "./client";
const idLike = z.union([z.string(), z.number()]).transform(String);
/**
* Research result and session payload are loose by design the backend
* returns `asdict(result)` of a Python dataclass we don't want to mirror
* field-for-field. Keep core fields strict and let extras pass through.
*/
export const researchRunResultSchema = z
.object({
job_id: idLike.optional(),
project_id: idLike.optional(),
project_name: z.string().optional(),
goal: z.string().optional(),
status: z.string().optional(),
started_at: z.string().nullable().optional(),
finished_at: z.string().nullable().optional(),
steps_taken: z.number().optional(),
pages_visited: z.number().optional(),
entities_found: z.number().optional(),
claims_added: z.number().optional(),
error: z.string().nullable().optional(),
})
.passthrough();
export type ResearchRunResult = z.infer<typeof researchRunResultSchema>;
export const researchSessionItemSchema = z
.object({
job_id: idLike,
status: z.string().optional(),
goal: z.string().nullable().optional(),
seed_url: z.string().nullable().optional(),
started_at: z.string().nullable().optional(),
finished_at: z.string().nullable().optional(),
steps_taken: z.number().nullable().optional(),
pages_visited: z.number().nullable().optional(),
entities_found: z.number().nullable().optional(),
})
.passthrough();
export const researchSessionListSchema = z.array(researchSessionItemSchema);
export type ResearchSessionItem = z.infer<typeof researchSessionItemSchema>;
export const researchSessionDetailSchema = researchSessionItemSchema.extend({
trace: z.array(z.record(z.string(), z.unknown())).optional(),
});
export type ResearchSessionDetail = z.infer<typeof researchSessionDetailSchema>;
export interface StartResearchRequest {
project_name: string;
source_name: string;
url?: string;
seed_entity_id?: number | null;
goal?: string;
max_depth?: number;
max_steps?: number;
max_branch?: number;
min_relevance?: number;
same_domain_only?: boolean;
analyze_page_types?: string[];
extractor_provider?: string;
extractor_model?: string | null;
extractor_base_url?: string | null;
check_robots_txt?: boolean;
respect_robots_txt?: boolean | null;
}
export const researchApi = {
startByProject: (body: StartResearchRequest) =>
apiClient.post("/research/run/by-project", researchRunResultSchema, body),
listSessions: (projectName: string, limit = 25) =>
apiClient.get(
`/projects/${encodeURIComponent(projectName)}/research/sessions`,
researchSessionListSchema,
{ query: { limit } },
),
getSession: (jobId: string) =>
apiClient.get(
`/research/sessions/${encodeURIComponent(jobId)}`,
researchSessionDetailSchema,
),
};