From a6c371226a16a10f7fd352d9059fa210af4ecc06 Mon Sep 17 00:00:00 2001 From: Dallas98 <990259227@qq.com> Date: Thu, 11 Dec 2025 16:41:07 +0800 Subject: [PATCH 1/8] fix(chart): update Helm chart helpers and values for improved configuration --- deployment/helm/label-studio/templates/_helpers.tpl | 7 +++---- deployment/helm/label-studio/values.yaml | 10 ++++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/deployment/helm/label-studio/templates/_helpers.tpl b/deployment/helm/label-studio/templates/_helpers.tpl index 0e6b76957..04d95dd99 100644 --- a/deployment/helm/label-studio/templates/_helpers.tpl +++ b/deployment/helm/label-studio/templates/_helpers.tpl @@ -2,7 +2,7 @@ Expand the name of the chart. */}} {{- define "label-studio.name" -}} -{{- default .Chart.name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- default .Values.nameOverride .Chart.Name | trunc 63 | trimSuffix "-" -}} {{- end -}} {{/* @@ -12,7 +12,7 @@ Create a default fully qualified app name. {{- if .Values.fullnameOverride -}} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} {{- else -}} -{{- $name := default .Chart.name .Values.nameOverride -}} +{{- $name := default .Values.nameOverride .Chart.Name -}} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} {{- end -}} {{- end -}} @@ -21,6 +21,5 @@ Create a default fully qualified app name. Create chart name and version as used by the chart label. */}} {{- define "label-studio.chart" -}} -{{- printf "%s-%s" .Chart.name .Chart.version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} {{- end -}} - diff --git a/deployment/helm/label-studio/values.yaml b/deployment/helm/label-studio/values.yaml index 347be476e..696e5a40d 100644 --- a/deployment/helm/label-studio/values.yaml +++ b/deployment/helm/label-studio/values.yaml @@ -1,5 +1,6 @@ # Default values for label-studio Helm chart. # This mirrors the configuration from deployment/docker/label-studio/docker-compose.yml +fullnameOverride: label-studio replicaCount: 1 @@ -24,9 +25,9 @@ postgres: size: 10Gi service: - type: ClusterIP + type: NodePort port: 8000 - nodePort: null + nodePort: 30001 # Corresponds to docker-compose port mapping 30001:8000 ingress: @@ -54,7 +55,7 @@ env: POSTGRE_USER: "postgres" POSTGRE_PASSWORD: "" POSTGRE_PORT: 5432 - POSTGRE_HOST: "db" + POSTGRE_HOST: "label-studio-postgres" LABEL_STUDIO_HOST: "" # can be overridden LOCAL_FILES_SERVING_ENABLED: "true" LOCAL_FILES_DOCUMENT_ROOT: "/label-studio/local" @@ -75,5 +76,6 @@ persistence: # If not set and persistence.enabled=true, a PVC will be created automatically. datasetVolume: enabled: true - claimName: "" # if empty, uses same PVC as persistence or creates a dedicated one + claimName: datamate-dataset-pvc # if empty, uses same PVC as persistence or creates a dedicated one + From 175cdb9f5351a0965e60862d94941d4be79ffd1b Mon Sep 17 00:00:00 2001 From: Dallas98 <990259227@qq.com> Date: Thu, 11 Dec 2025 16:56:19 +0800 Subject: [PATCH 2/8] feat(SynthesisTaskTab): enhance task table with tooltip support and improved column widths --- .../components/SynthesisTaskTab.tsx | 63 ++++++++++++++----- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/frontend/src/pages/SynthesisTask/components/SynthesisTaskTab.tsx b/frontend/src/pages/SynthesisTask/components/SynthesisTaskTab.tsx index 832fa3231..01a0b538a 100644 --- a/frontend/src/pages/SynthesisTask/components/SynthesisTaskTab.tsx +++ b/frontend/src/pages/SynthesisTask/components/SynthesisTaskTab.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect } from "react"; import { Card, Button, Table, Modal, message, Tooltip, Form, Input, Select } from "antd"; import { Plus, @@ -50,11 +50,6 @@ interface SynthesisTask { updated_by?: string; } -interface SynthesisDataItem { - id: string; - [key: string]: any; -} - export default function SynthesisTaskTab() { const navigate = useNavigate(); const [searchQuery, setSearchQuery] = useState(""); @@ -74,11 +69,6 @@ export default function SynthesisTaskTab() { const [evalForm] = Form.useForm(); - // 合成数据相关状态 - const [activeChunkId, setActiveChunkId] = useState(null); - const [synthesisData, setSynthesisData] = useState([]); - const [selectedDataIds, setSelectedDataIds] = useState([]); - // 获取任务列表 const loadTasks = async () => { setLoading(true); @@ -118,7 +108,16 @@ export default function SynthesisTaskTab() { }; // 表格列 + const ellipsisStyle = { + maxWidth: 100, + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap", + display: "inline-block", + verticalAlign: "middle", + }; const taskColumns = [ + { title: ( @@ -113,13 +154,13 @@ export default function SynthFileTask() { render: (status?: string) => { let badgeStatus: BadgeProps["status"] = "default"; let text = status || "未知"; - if (status === "pending" || status === "processing") { + if (status === "pending" || status === "PROCESSING" || status === "processing") { badgeStatus = "processing"; text = "处理中"; - } else if (status === "completed") { + } else if (status === "COMPLETED" || status === "completed") { badgeStatus = "success"; text = "已完成"; - } else if (status === "failed") { + } else if (status === "FAILED" || status === "failed") { badgeStatus = "error"; text = "失败"; } @@ -155,40 +196,122 @@ export default function SynthFileTask() { }, ]; + const handleRefresh = async () => { + // 刷新按钮:明确触发一次顶部 loading,让用户看到“闪一下”的效果 + window.dispatchEvent(new Event("loading:show")); + try { + await fetchTaskDetail(); + await fetchData(pagination.current || 1, pagination.pageSize || 10, false); + } finally { + window.dispatchEvent(new Event("loading:hide")); + } + }; + + const handleDelete = async () => { + if (!taskId) return; + try { + await deleteSynthesisTaskByIdUsingDelete(taskId); + message.success("合成任务已删除"); + navigate("/data/synthesis/task"); + } catch { + message.error("删除合成任务失败"); + } + }; + + // 头部统计与操作 + const headerData: Record = taskInfo + ? { + name: taskInfo.name, + id: taskInfo.id, + icon: , + description: taskInfo.description, + createdAt: taskInfo.created_at ? formatDateTime(taskInfo.created_at) : "--", + } + : {}; + + const statistics = [ + { + key: "type", + icon: , + label: "类型", + value: + taskInfo?.synthesis_type === "QA" + ? "问答对生成" + : taskInfo?.synthesis_type === "COT" + ? "链式推理生成" + : taskInfo?.synthesis_type || "--", + }, + { + key: "fileCount", + icon: , + label: "文件数", + value: taskInfo?.total_files ?? "--", + }, + ]; + + const operations = [ + { + key: "refresh", + label: "刷新", + icon: , + onClick: handleRefresh, + }, + { + key: "delete", + label: "删除任务", + icon: , + danger: true, + confirm: { + title: "确认删除该合成任务?", + description: "删除后将无法恢复,请谨慎操作。", + okText: "确认删除", + cancelText: "取消", + onConfirm: handleDelete, + placement: "top", + overlayStyle: { + marginTop: 40, + }, + }, + }, + ]; + + const tabList = [ + { + key: "files", + label: "处理文件", + }, + ]; + + const breadItems = [ + { + title: 合成任务, + }, + { + title: taskInfo?.name || "任务详情", + }, + ]; + return ( -
- {/* 顶部任务信息和返回按钮 */} -
-
- {taskInfo && ( - <> -
- {taskInfo.name} - - {taskInfo.synthesis_type === "QA" ? "问答对生成" : taskInfo.synthesis_type === "COT" ? "链式推理生成" : taskInfo.synthesis_type} - - - 状态:{taskInfo.status === "pending" ? "等待中" : taskInfo.status === "completed" ? "已完成" : taskInfo.status === "failed" ? "失败" : taskInfo.status} - -
-
- 创建时间:{formatDateTime(taskInfo.created_at)} - 模型ID:{taskInfo.model_id} -
- + <> + +
+ +
+
+ +
+ {activeTab === "files" && ( + + rowKey="id" + loading={loading} + dataSource={data} + columns={columns} + pagination={pagination} + onChange={handleTableChange} + /> )}
-
- {/* 文件任务表格 */} - - rowKey="id" - loading={loading} - dataSource={data} - columns={columns} - pagination={pagination} - onChange={handleTableChange} - /> -
+ ); } diff --git a/runtime/datamate-python/app/module/generation/schema/generation.py b/runtime/datamate-python/app/module/generation/schema/generation.py index 9fe16efa9..f279aa03b 100644 --- a/runtime/datamate-python/app/module/generation/schema/generation.py +++ b/runtime/datamate-python/app/module/generation/schema/generation.py @@ -2,7 +2,7 @@ from enum import Enum from typing import List, Optional, Dict, Any -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, field_validator class TextSplitConfig(BaseModel): @@ -27,13 +27,21 @@ class SynthesisType(Enum): class CreateSynthesisTaskRequest(BaseModel): """创建数据合成任务请求""" name: str = Field(..., description="合成任务名称") - description: str = Field(None, description="合成任务描述") + description: Optional[str] = Field(None, description="合成任务描述") model_id: str = Field(..., description="模型ID") source_file_id: list[str] = Field(..., description="原始文件ID列表") text_split_config: TextSplitConfig = Field(None, description="文本切片配置") synthesis_config: SynthesisConfig = Field(..., description="合成配置") synthesis_type: SynthesisType = Field(..., description="合成类型") + @field_validator("description") + @classmethod + def empty_string_to_none(cls, v: Optional[str]) -> Optional[str]: + """前端如果传入空字符串,将其统一转换为 None,避免存库时看起来像有描述但实际上为空。""" + if isinstance(v, str) and v.strip() == "": + return None + return v + class DataSynthesisTaskItem(BaseModel): """数据合成任务列表/详情项""" From f92dd5981e27d5cefdf234bfbce07b00639fc4e2 Mon Sep 17 00:00:00 2001 From: Dallas98 <990259227@qq.com> Date: Thu, 11 Dec 2025 18:55:34 +0800 Subject: [PATCH 4/8] feat(SynthFileTask): enhance file display with progress tracking and delete action --- .../src/pages/SynthesisTask/SynthFileTask.tsx | 79 ++++++++++++------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/frontend/src/pages/SynthesisTask/SynthFileTask.tsx b/frontend/src/pages/SynthesisTask/SynthFileTask.tsx index 14c8a3fa5..7fc9c12d3 100644 --- a/frontend/src/pages/SynthesisTask/SynthFileTask.tsx +++ b/frontend/src/pages/SynthesisTask/SynthFileTask.tsx @@ -2,7 +2,7 @@ import { App } from "antd"; import { useEffect, useState } from "react"; import { Link, useNavigate, useParams } from "react-router"; import { DeleteOutlined, ReloadOutlined } from "@ant-design/icons"; -import { Badge, Breadcrumb, Button, Table, Tabs } from "antd"; +import { Badge, Breadcrumb, Button, Table, Tabs, Progress, Tooltip } from "antd"; import type { BadgeProps } from "antd"; import type { ColumnsType, TablePaginationConfig } from "antd/es/table"; @@ -13,7 +13,7 @@ import { deleteSynthesisTaskByIdUsingDelete, } from "@/pages/SynthesisTask/synthesis-api"; import { formatDateTime } from "@/utils/unit"; -import { Folder, Sparkles } from "lucide-react"; +import { Folder, Sparkles, Trash2 } from "lucide-react"; interface SynthesisFileTaskItem { id: string; @@ -131,20 +131,22 @@ export default function SynthFileTask() { const columns: ColumnsType = [ { - title: "文件名", - dataIndex: "file_name", - key: "file_name", - render: (text: string, record) => ( - + title: "文件", + key: "file", + render: (_text, record) => ( +
+ + +
), }, { @@ -168,19 +170,28 @@ export default function SynthFileTask() { }, }, { - title: "切片进度", - key: "chunks", - render: (_text, record) => ( - - {record.processed_chunks}/{record.total_chunks} - - ), + title: "切片总数", + dataIndex: "total_chunks", + key: "total_chunks", }, { - title: "目标文件路径", - dataIndex: "target_file_location", - key: "target_file_location", - ellipsis: true, + title: "处理进度", + key: "progress", + render: (_text, record) => { + const total = record.total_chunks || 0; + const processed = record.processed_chunks || 0; + const percent = total > 0 ? Math.min(100, Math.round((processed / total) * 100)) : 0; + return ( +
+ `${processed}/${total}`} + /> +
+ ); + }, }, { title: "创建时间", @@ -194,6 +205,20 @@ export default function SynthFileTask() { key: "updated_at", render: (val?: string) => (val ? formatDateTime(val) : "-"), }, + { + title: "操作", + key: "actions", + render: () => ( + + -
+ const breadItems = [ + { + title: 合成任务, + }, + { + title: state.taskId ? ( + {taskInfo?.name || "任务详情"} + ) : ( + taskInfo?.name || "任务详情" + ), + }, + { + title: state.fileName || "文件详情", + }, + ]; - {/* 主体左右布局 */} -
- {/* 左侧 Chunk 列表:占比 2/5 */} -
-
- Chunk 列表 -
-
- {chunkLoading ? ( -
- + return ( + <> + +
+
+ {/* 左侧 Chunk 列表 */} +
+
+
+ Chunk 列表 + {chunkPagination.total ? ( + 共 {chunkPagination.total} 条 + ) : null}
- ) : chunks.length === 0 ? ( - - ) : ( - { - const active = item.id === selectedChunkId; - return ( - -
-
setSelectedChunkId(item.id)} - > - Chunk #{item.chunk_index} - -
-
+ {chunkLoading ? ( +
+ +
+ ) : chunks.length === 0 ? ( + + ) : ( + { + const active = item.id === selectedChunkId; + return ( + setSelectedChunkId(item.id)} > - {item.chunk_content} -
-
- { - setSelectedChunkId(item.id); - handleDeleteCurrentChunk(); - }} - okText="删除" - cancelText="取消" - > - - - 删除该 Chunk 及合成数据 - - - ), - }, - ], - }} - trigger={["click"]} - > -
-
-
- ); - }} - /> - )} -
-
- `共 ${total} 条`} - /> +
+
+
+ + Chunk #{item.chunk_index} + +
+ {/* 右侧显示 Chunk ID,完整展示 */} + + ID: {item.id} + +
+
+ {item.chunk_content} +
+
+ + ); + }} + /> + )} +
+ {chunkPagination.total ? ( +
+ `共 ${total} 条`} + /> +
+ ) : null} +
-
- {/* 右侧合成数据展示:占比 3/5 */} -
-
- 合成数据 - {currentChunk && ( - - 当前 Chunk #{currentChunk.chunk_index} - - )} -
-
- {dataLoading ? ( -
- + {/* 右侧合成数据展示 */} +
+
+
+ 合成数据 + {currentChunk && ( + + 当前 Chunk #{currentChunk.chunk_index} + + )}
- ) : !selectedChunkId ? ( - - ) : synthDataList.length === 0 ? ( - - ) : ( -
- {synthDataList.map((item, index) => { - const isEditing = editingId === item.id; - return ( -
-
- 记录 {index + 1} - ID:{item.id} -
+
+ {dataLoading ? ( +
+ +
+ ) : !selectedChunkId ? ( + + ) : synthDataList.length === 0 ? ( + + ) : ( +
+ {synthDataList.map((item, index) => { + const isEditing = editingId === item.id; + return ( +
+
+ 记录 {index + 1} + ID:{item.id} +
- {/* 右下角更多操作按钮:编辑 & 删除 */} -
- - - 编辑 - - ), - onClick: (info) => { - info.domEvent.stopPropagation(); - startEdit(item); - }, - }, - { - key: "delete-data", - danger: true, - label: ( - { - e?.stopPropagation(); - handleDeleteSingleSynthesisData(item.id); - }} - okText="删除" - cancelText="取消" + {/* 顶部操作按钮:编辑 & 删除,更显眼 */} +
+ {!isEditing && ( + <> + + handleDeleteSingleSynthesisData(item.id)} + > +
+ 删除 + +
+ + )} +
- {/* 表格形式的 key-value 展示 + 可编辑 value */} -
- {getDataEntries(item.data).map(([key, value], rowIdx) => { - const displayValue = - typeof value === "string" || - typeof value === "number" || - typeof value === "boolean" - ? String(value) - : JSON.stringify(value, null, 2); + {/* key-value 展示区域:不再截断,完整展示 */} +
+ {getDataEntries(item.data).map(([key, value], rowIdx) => { + const displayValue = + typeof value === "string" || + typeof value === "number" || + typeof value === "boolean" + ? String(value) + : JSON.stringify(value, null, 2); - return ( -
-
- {key} -
-
- {isEditing ? ( - { - const v = e.target.value; - setEditingMap((prev) => ({ ...prev, [key]: v })); - }} - autoSize={{ minRows: 1, maxRows: 4 }} - /> - ) : ( - displayValue - )} -
-
- ); - })} -
+ return ( +
+
+ {key} +
+
+ {isEditing ? ( + { + const v = e.target.value; + setEditingMap((prev) => ({ ...prev, [key]: v })); + }} + autoSize={{ minRows: 1, maxRows: 6 }} + /> + ) : ( + displayValue + )} +
+
+ ); + })} +
- {isEditing && ( -
- - + {isEditing && ( +
+ + +
+ )}
- )} -
- ); - })} + ); + })} +
+ )}
- )} +
-
+ ); } From 951b065af36a9b90adf70ae7371153f187aa23d4 Mon Sep 17 00:00:00 2001 From: Dallas98 <990259227@qq.com> Date: Thu, 11 Dec 2025 20:50:38 +0800 Subject: [PATCH 6/8] feat(SynthDataDetail): add delete action for chunks with confirmation prompt --- .../pages/SynthesisTask/SynthDataDetail.tsx | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/frontend/src/pages/SynthesisTask/SynthDataDetail.tsx b/frontend/src/pages/SynthesisTask/SynthDataDetail.tsx index 6ce20ada7..aa9be354b 100644 --- a/frontend/src/pages/SynthesisTask/SynthDataDetail.tsx +++ b/frontend/src/pages/SynthesisTask/SynthDataDetail.tsx @@ -299,7 +299,7 @@ export default function SynthDataDetail() { return (
- {/* 右侧显示 Chunk ID,完整展示 */} - - ID: {item.id} - +
+ {/* 右侧显示 Chunk ID,完整展示 */} + + ID: {item.id} + + {/* 删除该 Chunk 按钮 */} + { + e?.stopPropagation(); + setSelectedChunkId(item.id); + handleDeleteCurrentChunk(); + }} + onCancel={(e) => e?.stopPropagation()} + > +
{item.chunk_content} From a7463ca0d1b1ea6fb02975525c8db5591bc2b8b6 Mon Sep 17 00:00:00 2001 From: Dallas98 <990259227@qq.com> Date: Thu, 11 Dec 2025 20:52:35 +0800 Subject: [PATCH 7/8] feat(SynthDataDetail): update edit and delete buttons to icon-only format --- .../pages/SynthesisTask/SynthDataDetail.tsx | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/frontend/src/pages/SynthesisTask/SynthDataDetail.tsx b/frontend/src/pages/SynthesisTask/SynthDataDetail.tsx index aa9be354b..33c07bdbb 100644 --- a/frontend/src/pages/SynthesisTask/SynthDataDetail.tsx +++ b/frontend/src/pages/SynthesisTask/SynthDataDetail.tsx @@ -400,18 +400,17 @@ export default function SynthDataDetail() { ID:{item.id}
- {/* 顶部操作按钮:编辑 & 删除,更显眼 */} -
+ {/* 顶部操作按钮:编辑 & 删除,改为仅图标按钮 */} +
{!isEditing && ( <> + /> handleDeleteSingleSynthesisData(item.id)} > + icon={} + onClick={(e) => e.stopPropagation()} + /> )} From 61230fafd6083ae6a2f42e3c8f6bccfc27fb5637 Mon Sep 17 00:00:00 2001 From: Dallas98 <990259227@qq.com> Date: Thu, 11 Dec 2025 21:00:04 +0800 Subject: [PATCH 8/8] feat(SynthDataDetail): add confirmation modals for chunk and synthesis data deletion --- .../pages/SynthesisTask/SynthDataDetail.tsx | 137 ++++++++++++------ 1 file changed, 93 insertions(+), 44 deletions(-) diff --git a/frontend/src/pages/SynthesisTask/SynthDataDetail.tsx b/frontend/src/pages/SynthesisTask/SynthDataDetail.tsx index 33c07bdbb..bfc364b1e 100644 --- a/frontend/src/pages/SynthesisTask/SynthDataDetail.tsx +++ b/frontend/src/pages/SynthesisTask/SynthDataDetail.tsx @@ -83,7 +83,8 @@ export default function SynthDataDetail() { const [chunkLoading, setChunkLoading] = useState(false); const [dataLoading, setDataLoading] = useState(false); const [synthDataList, setSynthDataList] = useState([]); - // 展开/收起逻辑已去除,尽量直接展示完整数据 + const [chunkConfirmVisibleId, setChunkConfirmVisibleId] = useState(null); + const [dataConfirmVisibleId, setDataConfirmVisibleId] = useState(null); // 加载任务信息(用于顶部展示) useEffect(() => { @@ -268,9 +269,72 @@ export default function SynthDataDetail() { }, ]; + const showChunkConfirm = (id: string) => setChunkConfirmVisibleId(id); + const hideChunkConfirm = () => setChunkConfirmVisibleId(null); + + const showDataConfirm = (id: string) => setDataConfirmVisibleId(id); + const hideDataConfirm = () => setDataConfirmVisibleId(null); + return ( <> + {/* 全局删除确认遮罩:Chunk */} + {chunkConfirmVisibleId && ( +
+
+
确认删除该 Chunk 及其合成数据?
+
+ ID: {chunkConfirmVisibleId} +
+
+ + +
+
+
+ )} + + {/* 全局删除确认遮罩:合成数据 */} + {dataConfirmVisibleId && ( +
+
+
确认删除该条合成数据?
+
+ ID: {dataConfirmVisibleId} +
+
+ + +
+
+
+ )} +
{/* 左侧 Chunk 列表 */} @@ -318,26 +382,17 @@ export default function SynthDataDetail() { ID: {item.id} - {/* 删除该 Chunk 按钮 */} - { - e?.stopPropagation(); - setSelectedChunkId(item.id); - handleDeleteCurrentChunk(); +
@@ -397,37 +452,31 @@ export default function SynthDataDetail() { >
记录 {index + 1} - ID:{item.id} -
- - {/* 顶部操作按钮:编辑 & 删除,改为仅图标按钮 */} -
- {!isEditing && ( - <> -
{/* key-value 展示区域:不再截断,完整展示 */}