import { useState, useEffect, useCallback } from "react"; import { Card, Button, Table, Modal, message, Tooltip, Form, Input, Select } from "antd"; import { Plus, ArrowUp, ArrowDown, Sparkles, } from "lucide-react"; import { FolderOpenOutlined, DeleteOutlined, EyeOutlined, ExperimentOutlined } from "@ant-design/icons"; import { Link, useNavigate } from "react-router"; import { SearchControls } from "@/components/SearchControls"; import { formatDateTime } from "@/utils/unit"; import { querySynthesisTasksUsingGet, deleteSynthesisTaskByIdUsingDelete, archiveSynthesisTaskToDatasetUsingPost, } from "@/pages/SynthesisTask/synthesis-api"; import { createDatasetUsingPost } from "@/pages/DataManagement/dataset.api"; import { createEvaluationTaskUsingPost } from "@/pages/DataEvaluation/evaluation.api"; import { queryModelListUsingGet } from "@/pages/SettingsPage/settings.apis"; import { ModelI } from "@/pages/SettingsPage/ModelAccess"; interface SynthesisTask { id: string; name: string; description?: string; status: string; synthesis_type: string; model_id: string; progress?: number; result_data_location?: string; text_split_config?: { chunk_size: number; chunk_overlap: number; }; synthesis_config?: { temperature?: number | null; prompt_template?: string; synthesis_count?: number | null; }; source_file_id?: string[]; total_files?: number; processed_files?: number; total_chunks?: number; processed_chunks?: number; total_synthesis_data?: number; created_at: string; updated_at?: string; created_by?: string; updated_by?: string; } interface SynthesisDataItem { id: string; [key: string]: any; } export default function SynthesisTaskTab() { const navigate = useNavigate(); const [searchQuery, setSearchQuery] = useState(""); const [tasks, setTasks] = useState([]); const [filterStatus, setFilterStatus] = useState("all"); const [sortBy, setSortBy] = useState<"createdAt" | "name">("createdAt"); const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc"); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [evalModalVisible, setEvalModalVisible] = useState(false); const [currentEvalTask, setCurrentEvalTask] = useState(null); const [evalLoading, setEvalLoading] = useState(false); const [models, setModels] = useState([]); const [modelLoading, setModelLoading] = useState(false); const [evalForm] = Form.useForm(); // 合成数据相关状态 const [activeChunkId, setActiveChunkId] = useState(null); const [synthesisData, setSynthesisData] = useState([]); const [selectedDataIds, setSelectedDataIds] = useState([]); // 获取任务列表 const loadTasks = async () => { setLoading(true); try { const params = { page: page, page_size: pageSize, } as { page?: number; page_size?: number; synthesis_type?: string; status?: string; name?: string; }; if (searchQuery) params.name = searchQuery; if (filterStatus !== "all") params.synthesis_type = filterStatus; const res = await querySynthesisTasksUsingGet(params); setTasks(res?.data?.content || []); setTotal(res?.data?.totalElements || 0); } catch { setTasks([]); setTotal(0); } finally { setLoading(false); } }; useEffect(() => { loadTasks(); // eslint-disable-next-line }, [searchQuery, filterStatus, page, pageSize]); // 类型映射 const typeMap: Record = { QA: "问答对生成", COT: "链式推理生成", }; // 表格列 const taskColumns = [ { title: ( ), dataIndex: "name", key: "name", fixed: "left" as const, render: (_: unknown, task: SynthesisTask) => (
{task.synthesis_type?.toUpperCase()?.slice(0, 1) || "T"}
{task.name}
), }, { title: "类型", dataIndex: "synthesis_type", key: "synthesis_type", render: (type: string) => typeMap[type] || type, }, { title: "文件数", dataIndex: "total_files", key: "total_files", render: (num: number, task: SynthesisTask) => {num ?? (task.source_file_id?.length ?? 0)}, }, { title: "创建时间", dataIndex: "created_at", key: "created_at", render: (val: string) => formatDateTime(val), }, { title: "操作", key: "actions", fixed: "right" as const, render: (_: unknown, task: SynthesisTask) => (
), }, ]; const handleArchiveTask = async (task: SynthesisTask) => { try { // 1. 创建目标数据集(使用简单的默认命名 + 随机后缀,可后续扩展为弹窗自定义) const randomSuffix = Math.random().toString(36).slice(2, 8); const datasetReq: { name: string; description: string; datasetType: string; category: string; format: string; status: string; } = { name: `${task.name}-合成数据留用${randomSuffix}`, description: `由合成任务 ${task.id} 留用生成`, datasetType: "TEXT", category: "SYNTHESIS", format: "JSONL", status: "DRAFT", }; const datasetRes = await createDatasetUsingPost(datasetReq); const datasetId = datasetRes?.data?.id; if (!datasetId) { message.error("创建数据集失败"); return; } // 2. 调用后端归档接口,将合成数据写入该数据集 await archiveSynthesisTaskToDatasetUsingPost(task.id, datasetId); message.success("归档成功"); // 3. 可选:跳转到数据集详情页 navigate(`/data/management/detail/${datasetId}`); } catch (e) { console.error(e); message.error("归档失败"); } }; const openEvalModal = (task: SynthesisTask) => { setCurrentEvalTask(task); setEvalModalVisible(true); evalForm.setFieldsValue({ name: `${task.name}-数据评估`, taskType: task.synthesis_type || "QA", evalMethod: "AUTO", }); // 懒加载模型列表 if (!models.length) { loadModels(); } }; const loadModels = async () => { try { setModelLoading(true); const { data } = await queryModelListUsingGet({ page: 0, size: 1000 }); setModels(data?.content || []); } catch (e) { console.error(e); message.error("获取模型列表失败"); } finally { setModelLoading(false); } }; const chatModelOptions = models .filter((m) => m.type === "CHAT") .map((m) => ({ label: `${m.modelName} (${m.provider})`, value: m.id, })); const handleCreateEvaluation = async () => { if (!currentEvalTask) return; try { const values = await evalForm.validateFields(); setEvalLoading(true); const taskType = currentEvalTask.synthesis_type || "QA"; const payload = { name: values.name, taskType, evalMethod: values.evalMethod, sourceType: "SYNTHESIS", sourceId: currentEvalTask.id, sourceName: currentEvalTask.name, evalConfig: { modelId: values.modelId, dimensions: [ { dimension: "问题是否独立", description: "仅分析问题,问题的主体和客体都比较明确,即使有省略,也符合语言习惯。在不需要补充其他信息的情况下不会引起疑惑。", }, { dimension: "语法是否错误", description: "问题为疑问句,答案为陈述句; 不存在词语搭配不当的情况;连接词和标点符号不存在错用情况;逻辑混乱的情况不存在;语法结构都正确且完整。", }, { dimension: "回答是否有针对性", description: "回答应对问题中的所有疑问点提供正面、直接的回答,不应引起疑惑。同时,答案不应有任何内容的遗漏,需构成一个完整的陈述。", }, ], }, }; await createEvaluationTaskUsingPost(payload); message.success("评估任务创建成功"); setEvalModalVisible(false); setCurrentEvalTask(null); evalForm.resetFields(); } catch (error) { const err = error as { errorFields?: unknown; response?: { data?: { message?: string } } }; if (err?.errorFields) return; // 表单校验错误 message.error(err?.response?.data?.message || "评估任务创建失败"); } finally { setEvalLoading(false); } }; return (
{/* 搜索和筛选 */} { setFilterStatus(filters.status?.[0] || "all"); }} showFilters showViewToggle={false} /> {/* 任务表格 */} { setPage(p); setPageSize(ps); }, showSizeChanger: true, }} scroll={{ x: "max-content" }} locale={{ emptyText: (

暂无合成任务

{searchQuery ? "没有找到匹配的任务" : "开始创建您的第一个合成任务"}

{!searchQuery && filterStatus === "all" && ( )}
), }} /> { setEvalModalVisible(false); setCurrentEvalTask(null); evalForm.resetFields(); }} onOk={handleCreateEvaluation} confirmLoading={evalLoading} okText="开始评估" cancelText="取消" >