From 6d4b22c63dc5d993d02cfa7ec8a515bde9e6244d Mon Sep 17 00:00:00 2001 From: ZihanChen821 <130351655+ZihanChen821@users.noreply.github.com> Date: Tue, 26 Mar 2024 20:53:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(ai):=20=E5=88=9B=E5=BB=BA=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E5=92=8C=E8=AE=AD=E7=BB=83=E9=A1=B5=E9=9D=A2UI?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E4=BC=98=E5=8C=96=20(#1169)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## UI和交互改动 原UI ![image](https://github.com/PKUHPC/SCOW/assets/130351655/d4f5fc77-cce0-459b-b182-dbde08b562eb) 改动后UI ![image](https://github.com/PKUHPC/SCOW/assets/130351655/c753b483-c0bc-4501-8d57-418b3fcf389d) 改动点: 1. 整体布局分为三块, 应用配置,添加算法/数据集/模型,资源 2. 展示创建应用的镜像,用户可使用自定义镜像,同时需要 选择启动命令 ![image](https://github.com/PKUHPC/SCOW/assets/130351655/05fe09a3-147e-40c7-b3e8-0415c87319ea) 3. 指定工作目录和挂载点放在应用配置内 4. 算法数据集模型的添加,用户先check后再进行选择 ![image](https://github.com/PKUHPC/SCOW/assets/130351655/61786982-ee47-470f-86c7-70c4dce48784) --- .changeset/slow-dogs-march.md | 5 + .../(auth)/jobs/[clusterId]/LaunchAppForm.tsx | 448 ++++++++++-------- .../trpc/route/algorithm/algorithmVersion.ts | 2 +- .../trpc/route/dataset/datasetVersion.ts | 2 +- apps/ai/src/server/trpc/route/file.ts | 2 +- apps/ai/src/server/trpc/route/jobs/apps.ts | 23 +- .../server/trpc/route/model/modelVersion.ts | 2 +- 7 files changed, 280 insertions(+), 204 deletions(-) create mode 100644 .changeset/slow-dogs-march.md diff --git a/.changeset/slow-dogs-march.md b/.changeset/slow-dogs-march.md new file mode 100644 index 0000000000..d96f264103 --- /dev/null +++ b/.changeset/slow-dogs-march.md @@ -0,0 +1,5 @@ +--- +"@scow/ai": patch +--- + +AI 系统创建应用和训练页面 UI 交互优化 diff --git a/apps/ai/src/app/(auth)/jobs/[clusterId]/LaunchAppForm.tsx b/apps/ai/src/app/(auth)/jobs/[clusterId]/LaunchAppForm.tsx index 4134021be1..0f064abc2a 100644 --- a/apps/ai/src/app/(auth)/jobs/[clusterId]/LaunchAppForm.tsx +++ b/apps/ai/src/app/(auth)/jobs/[clusterId]/LaunchAppForm.tsx @@ -14,7 +14,8 @@ import { I18nStringType } from "@scow/config/build/i18n"; import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/systemLanguage"; -import { App, Button, Col, Divider, Form, Input, InputNumber, Row, Select, Space, Spin } from "antd"; +import { App, Button, Checkbox, Col, + Divider, Form, Input, InputNumber, Row, Select, Space, Spin, Typography } from "antd"; import { Rule } from "antd/es/form"; import dayjs from "dayjs"; import { useRouter } from "next/navigation"; @@ -22,6 +23,7 @@ import { useEffect, useMemo, useState } from "react"; import { AccountSelector } from "src/components/AccountSelector"; import { FileSelectModal } from "src/components/FileSelectModal"; import { AlgorithmInterface, AlgorithmVersionInterface } from "src/models/Algorithm"; +import { Status } from "src/models/Image"; import { ModelInterface, ModelVersionInterface } from "src/models/Model"; import { DatasetInterface } from "src/server/trpc/route/dataset/dataset"; import { DatasetVersionInterface } from "src/server/trpc/route/dataset/datasetVersion"; @@ -104,7 +106,7 @@ const inputNumberFloorConfig = { export const LaunchAppForm = (props: Props) => { - const { clusterId, appName, clusterInfo, isTraining = false, appId, attributes = []} = props; + const { clusterId, appName, clusterInfo, isTraining = false, appId, attributes = [], appImage } = props; const { message } = App.useApp(); @@ -114,6 +116,12 @@ export const LaunchAppForm = (props: Props) => { const [currentPartitionInfo, setCurrentPartitionInfo] = useState(); + const [useCustomImage, setUseCustomImage] = useState(false); + + const [showAlgorithm, setShowAlgorithm] = useState(false); + const [showDataset, setShowDataset] = useState(false); + const [showModel, setShowModel] = useState(false); + const { dataOptions: datasetOptions, isDataLoading: isDatasetsLoading } = useDataOptions( form, "dataset", @@ -176,7 +184,9 @@ export const LaunchAppForm = (props: Props) => { }); const imageOptions = useMemo(() => { - return images?.items.map((x) => ({ label: `${x.name}:${x.tag}`, value: x.id })); + return images?.items + .filter((x) => x.status === Status.CREATED) + .map((x) => ({ label: `${x.name}:${x.tag}`, value: x.id })); }, [images]); // 暂时写死为1 @@ -229,7 +239,7 @@ export const LaunchAppForm = (props: Props) => { inputItem = ( { await trainJobMutation.mutateAsync({ clusterId, trainJobName: appJobName, - algorithm: algorithm.version, - imageId: image.name, - dataset: dataset.version, - model: model.version, + algorithm: algorithm?.version, + imageId: image?.name, + dataset: dataset?.version, + model: model?.version, mountPoint: mountPoint, account: account, partition: partition, @@ -359,11 +369,11 @@ export const LaunchAppForm = (props: Props) => { clusterId, appId: appId!, appJobName, - algorithm: algorithm.version, - image: image.name, + algorithm: algorithm?.version, + image: image?.name, startCommand, - dataset: dataset.version, - model: model.version, + dataset: dataset?.version, + model: model?.version, mountPoint, account: account, partition: partition, @@ -385,189 +395,83 @@ export const LaunchAppForm = (props: Props) => { - - - - { - form.setFieldValue("startCommand", undefined); - }} - loading={isImagesLoading && isImagePublic !== undefined} - options={imageOptions} - /> - - - - {(selectedImage && !isTraining) ? ( + {isTraining ? "训练配置" : "应用配置"} + {!isTraining && ( {`请选择安装了${appName}应用的镜像,并指定启动命令`} + } > - + + + {selectedImage + ? imageOptions?.find((x) => x.value === selectedImage)?.label + : appImage ? `${appImage?.name}:${appImage?.tag}` : "-"} + + setUseCustomImage(e.target.checked)}>使用自定义镜像 + - ) : null } - - - - { - form.setFieldValue(["algorithm", "version"], undefined); - }} - loading={isAlgorithmLoading} - options={algorithmOptions} - /> - - - { - form.setFieldsValue({ dataset: { name: undefined, version: undefined } }); - }} - options={ - [ - { - value: AccessibilityType.PRIVATE, - label: "我的数据集", - }, - { - value: AccessibilityType.PUBLIC, - label: "公共数据集", - }, + )} + { + (isTraining || useCustomImage) && ( + <> + + + + { + form.setFieldValue("startCommand", undefined); + }} + loading={isImagesLoading && isImagePublic !== undefined} + options={imageOptions} + /> + + + + {(selectedImage && !isTraining) ? ( + + + + ) : null } + + ) + } - ] - } - /> - - - - - - - - - - { - form.setFieldValue(["model", "version"], undefined); - }} - loading={isModelsLoading } - options={modelOptions} - /> - - - { } /> - 资源 + 添加算法/数据集/模型 + + setShowAlgorithm(e.target.checked)}> + 算法 + + setShowDataset(e.target.checked)}> + 数据集 + + setShowModel(e.target.checked)}> + 模型 + + + { + showAlgorithm ? ( + + + + { + form.setFieldValue(["algorithm", "version"], undefined); + }} + loading={isAlgorithmLoading} + options={algorithmOptions} + /> + + + { + form.setFieldsValue({ dataset: { name: undefined, version: undefined } }); + }} + options={ + [ + { + value: AccessibilityType.PRIVATE, + label: "我的数据集", + }, + { + value: AccessibilityType.PUBLIC, + label: "公共数据集", + }, + + ] + } + /> + + + + + + + ) : null + } + + { + showModel ? ( + + + + { + form.setFieldValue(["model", "version"], undefined); + }} + loading={isModelsLoading } + options={modelOptions} + /> + + +