diff --git a/frontend/package.json b/frontend/package.json index 17b069cb2..53c95e0f1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,7 +7,7 @@ "dev": "vite", "mock": "cd src/mock && nodemon --config nodemon.json --inspect=0.0.0.0:9229 mock.cjs --env=development --port=8002", "build": "vite build", - "test": "node --test tests/basic-functionality.test.cjs tests/bootstrap-auth.test.cjs", + "test": "node --test tests/basic-functionality.test.cjs tests/bootstrap-auth.test.cjs tests/cleaning-logs.test.cjs", "lint": "eslint .", "preview": "vite preview" }, diff --git a/frontend/src/i18n/locales/en/common.json b/frontend/src/i18n/locales/en/common.json index af81d1063..c5576e9d3 100644 --- a/frontend/src/i18n/locales/en/common.json +++ b/frontend/src/i18n/locales/en/common.json @@ -2338,11 +2338,13 @@ "evalTitle": "Create Evaluation Task", "evalName": "Evaluation Task Name", "evalMethod": { + "label": "Evaluation Method", "auto": "Auto Evaluation", "cot": "CoT Evaluation", "qa": "QA Evaluation" }, "evalModel": "Evaluation Model", + "evalObject": "Evaluation Object", "evalSource": "Source: Synthesis Task (SYNTHESIS)", "sourceName": "Source Name: {{name}}", "startEval": "Start Evaluation", diff --git a/frontend/src/i18n/locales/zh/common.json b/frontend/src/i18n/locales/zh/common.json index 643278dbc..0a68a89f3 100644 --- a/frontend/src/i18n/locales/zh/common.json +++ b/frontend/src/i18n/locales/zh/common.json @@ -2337,11 +2337,13 @@ "evalTitle": "创建评估任务", "evalName": "评估任务名称", "evalMethod": { + "label": "评估方式", "auto": "自动评估", "cot": "CoT 评估", "qa": "QA 评估" }, "evalModel": "评估模型", + "evalObject": "评估对象", "evalSource": "来源:合成任务(SYNTHESIS)", "sourceName": "源名称:{{name}}", "startEval": "开始评估", diff --git a/frontend/src/pages/DataCleansing/Detail/components/LogsTable.tsx b/frontend/src/pages/DataCleansing/Detail/components/LogsTable.tsx index b8efa8bba..a16c1f942 100644 --- a/frontend/src/pages/DataCleansing/Detail/components/LogsTable.tsx +++ b/frontend/src/pages/DataCleansing/Detail/components/LogsTable.tsx @@ -34,7 +34,7 @@ export default function LogsTable({ const [selectedLog, setSelectedLog] = useState(retryCount + 1); const [streamingLogs, setStreamingLogs] = useState([]); const [isStreaming, setIsStreaming] = useState(false); - const eventSourceRef = useRef(null); + const eventSourceRef = useRef | null>(null); const { message } = App.useApp(); // Only stream when task is RUNNING and viewing the latest run @@ -123,7 +123,7 @@ export default function LogsTable({ const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; - a.download = `${taskName}_${t("dataCleansing.logTable.nthRun", { num: selectedLog })}.log`; + a.download = `${taskName}_${t("dataCleansing.detail.logTable.nthRun", { num: selectedLog })}.log`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); diff --git a/frontend/src/pages/DataCleansing/cleansing.api.ts b/frontend/src/pages/DataCleansing/cleansing.api.ts index a8bf7ab52..75b3a93d3 100644 --- a/frontend/src/pages/DataCleansing/cleansing.api.ts +++ b/frontend/src/pages/DataCleansing/cleansing.api.ts @@ -31,18 +31,71 @@ export function queryCleaningTaskLogByIdUsingGet(taskId: string | number, retryC return get(`/api/cleaning/tasks/${taskId}/log/${retryCount}`); } -export function streamCleaningTaskLog(taskId: string | number, retryCount: number = 0): EventSource { +export interface CleaningTaskLogStream { + onmessage: ((event: { data: string }) => void) | null; + onerror: ((error: unknown) => void) | null; + close: () => void; +} + +export function streamCleaningTaskLog(taskId: string | number, retryCount: number = 0): CleaningTaskLogStream { const url = `/api/cleaning/tasks/${taskId}/log/stream?retry_count=${retryCount}`; - return new EventSource(url); + const controller = new AbortController(); + const stream: CleaningTaskLogStream = { + onmessage: null, + onerror: null, + close: () => controller.abort(), + }; + + // Fetch supports the Authorization header supplied by the request interceptor. + void (async () => { + try { + const response = await get(url, null, { + responseType: "stream", + headers: { Accept: "text/event-stream" }, + signal: controller.signal, + }); + if (!response.headers.get("content-type")?.includes("text/event-stream") || !response.body) { + throw new Error("Invalid log stream response"); + } + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; + let data: string[] = []; + try { + while (!controller.signal.aborted) { + const { value, done } = await reader.read(); + if (done) break; + buffer += decoder.decode(value, { stream: true }); + let end: number; + while (!controller.signal.aborted && (end = buffer.indexOf("\n")) !== -1) { + const line = buffer.slice(0, end).replace(/\r$/, ""); + buffer = buffer.slice(end + 1); + if (line === "") { + if (data.length) stream.onmessage?.({ data: data.join("\n") }); + data = []; + } else if (line === "data" || line.startsWith("data:")) { + data.push(line.slice(5).replace(/^ /, "")); + } + } + } + } finally { + await reader.cancel(); + reader.releaseLock(); + } + if (!controller.signal.aborted) { + throw new Error("Log stream ended"); + } + } catch (error) { + if (!controller.signal.aborted) stream.onerror?.(error); + } + })(); + + return stream; } export async function downloadCleaningTaskLog(taskId: string | number, retryCount: number = 0): Promise { const url = `/api/cleaning/tasks/${taskId}/log/${retryCount}/download`; - const response = await fetch(url); - if (!response.ok) { - throw new Error('Failed to download log file'); - } - return response.blob(); + return get(url, null, { responseType: "blob" }); } export function updateCleaningTaskByIdUsingPut(taskId: string | number, data: any) { diff --git a/frontend/src/pages/DataEvaluation/evaluation.const.tsx b/frontend/src/pages/DataEvaluation/evaluation.const.tsx index 5371b56ca..f81b23c94 100644 --- a/frontend/src/pages/DataEvaluation/evaluation.const.tsx +++ b/frontend/src/pages/DataEvaluation/evaluation.const.tsx @@ -23,7 +23,6 @@ export function getTaskTypes(t: TFunction) { export function getEvalMethods(t: TFunction) { return [ { label: t("dataEvaluation.create.evalMethods.auto"), value: "AUTO" }, - { label: t("dataEvaluation.create.evalMethods.manual"), value: "MANUAL" }, ]; } @@ -33,6 +32,9 @@ export const getEvalType = (type: string, t?: TFunction) => { }; export const getEvalMethod = (type: string, t?: TFunction) => { + if (type === "MANUAL") { + return t ? t("dataEvaluation.create.evalMethods.manual") : "人工评估"; + } if (t) return getEvalMethods(t).find((item) => item.value === type)?.label; return EVAL_METHODS.find((item) => item.value === type)?.label; }; diff --git a/frontend/src/pages/SynthesisTask/DataSynthesis.tsx b/frontend/src/pages/SynthesisTask/DataSynthesis.tsx index c2fca0051..9e886f7df 100644 --- a/frontend/src/pages/SynthesisTask/DataSynthesis.tsx +++ b/frontend/src/pages/SynthesisTask/DataSynthesis.tsx @@ -4,7 +4,6 @@ import { PlusOutlined } from "@ant-design/icons"; import { Plus, ArrowRight } from "lucide-react"; import { useNavigate } from "react-router"; import { useTranslation } from "react-i18next"; -import InstructionTemplateTab from "./components/InstructionTemplateTab"; import SynthesisTaskTab from "./components/SynthesisTaskTab"; export default function DataSynthesisPage() { @@ -36,14 +35,6 @@ export default function DataSynthesisPage() {

{t('synthesisTask.title')}

-